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')) {
3868 ce.removeClass('collapse');
3869 ce.addClass('collapsing');
3870 var h = ce.getHeight();
3872 ce.setHeight(0); // resize it ...
3873 ce.on('transitionend', function() {
3875 ce.removeClass('collapsing');
3876 ce.addClass('show');
3877 ce.removeClass('collapse');
3879 ce.dom.style.height = '';
3880 }, this, { single: true} );
3884 ce.addClass('collapsing');
3885 ce.removeClass('show');
3887 ce.removeClass('collapsing');
3888 ce.addClass('collapse');
3902 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3904 var size = this.el.getSize();
3905 this.maskEl.setSize(size.width, size.height);
3906 this.maskEl.enableDisplayMode("block");
3915 getChildContainer : function()
3917 if (this.el.select('.collapse').getCount()) {
3918 return this.el.select('.collapse',true).first();
3951 * @class Roo.bootstrap.NavSimplebar
3952 * @extends Roo.bootstrap.Navbar
3953 * Bootstrap Sidebar class
3955 * @cfg {Boolean} inverse is inverted color
3957 * @cfg {String} type (nav | pills | tabs)
3958 * @cfg {Boolean} arrangement stacked | justified
3959 * @cfg {String} align (left | right) alignment
3961 * @cfg {Boolean} main (true|false) main nav bar? default false
3962 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3964 * @cfg {String} tag (header|footer|nav|div) default is nav
3966 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3970 * Create a new Sidebar
3971 * @param {Object} config The config object
3975 Roo.bootstrap.NavSimplebar = function(config){
3976 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3979 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3995 getAutoCreate : function(){
3999 tag : this.tag || 'div',
4000 cls : 'navbar navbar-expand-lg'
4002 if (['light','white'].indexOf(this.weight) > -1) {
4003 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4005 cfg.cls += ' bg-' + this.weight;
4017 this.type = this.type || 'nav';
4018 if (['tabs','pills'].indexOf(this.type)!==-1) {
4019 cfg.cn[0].cls += ' nav-' + this.type
4023 if (this.type!=='nav') {
4024 Roo.log('nav type must be nav/tabs/pills')
4026 cfg.cn[0].cls += ' navbar-nav'
4032 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4033 cfg.cn[0].cls += ' nav-' + this.arrangement;
4037 if (this.align === 'right') {
4038 cfg.cn[0].cls += ' navbar-right';
4042 cfg.cls += ' navbar-inverse';
4066 * navbar-expand-md fixed-top
4070 * @class Roo.bootstrap.NavHeaderbar
4071 * @extends Roo.bootstrap.NavSimplebar
4072 * Bootstrap Sidebar class
4074 * @cfg {String} brand what is brand
4075 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4076 * @cfg {String} brand_href href of the brand
4077 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
4078 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4079 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4080 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4083 * Create a new Sidebar
4084 * @param {Object} config The config object
4088 Roo.bootstrap.NavHeaderbar = function(config){
4089 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4093 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
4100 desktopCenter : false,
4103 getAutoCreate : function(){
4106 tag: this.nav || 'nav',
4107 cls: 'navbar navbar-expand-md',
4113 if (this.desktopCenter) {
4114 cn.push({cls : 'container', cn : []});
4122 cls: 'navbar-toggle navbar-toggler',
4123 'data-toggle': 'collapse',
4128 html: 'Toggle navigation'
4132 cls: 'icon-bar navbar-toggler-icon'
4145 cn.push( Roo.bootstrap.version == 4 ? btn : {
4147 cls: 'navbar-header',
4156 cls: 'collapse navbar-collapse',
4160 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4162 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4163 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4165 // tag can override this..
4167 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4170 if (this.brand !== '') {
4171 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4172 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4174 href: this.brand_href ? this.brand_href : '#',
4175 cls: 'navbar-brand',
4183 cfg.cls += ' main-nav';
4191 getHeaderChildContainer : function()
4193 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4194 return this.el.select('.navbar-header',true).first();
4197 return this.getChildContainer();
4201 initEvents : function()
4203 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4205 if (this.autohide) {
4210 Roo.get(document).on('scroll',function(e) {
4211 var ns = Roo.get(document).getScroll().top;
4212 var os = prevScroll;
4216 ft.removeClass('slideDown');
4217 ft.addClass('slideUp');
4220 ft.removeClass('slideUp');
4221 ft.addClass('slideDown');
4242 * @class Roo.bootstrap.NavSidebar
4243 * @extends Roo.bootstrap.Navbar
4244 * Bootstrap Sidebar class
4247 * Create a new Sidebar
4248 * @param {Object} config The config object
4252 Roo.bootstrap.NavSidebar = function(config){
4253 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4256 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4258 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4260 getAutoCreate : function(){
4265 cls: 'sidebar sidebar-nav'
4287 * @class Roo.bootstrap.NavGroup
4288 * @extends Roo.bootstrap.Component
4289 * Bootstrap NavGroup class
4290 * @cfg {String} align (left|right)
4291 * @cfg {Boolean} inverse
4292 * @cfg {String} type (nav|pills|tab) default nav
4293 * @cfg {String} navId - reference Id for navbar.
4297 * Create a new nav group
4298 * @param {Object} config The config object
4301 Roo.bootstrap.NavGroup = function(config){
4302 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4305 Roo.bootstrap.NavGroup.register(this);
4309 * Fires when the active item changes
4310 * @param {Roo.bootstrap.NavGroup} this
4311 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4312 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4319 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4330 getAutoCreate : function()
4332 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4339 if (['tabs','pills'].indexOf(this.type)!==-1) {
4340 cfg.cls += ' nav-' + this.type
4342 if (this.type!=='nav') {
4343 Roo.log('nav type must be nav/tabs/pills')
4345 cfg.cls += ' navbar-nav'
4348 if (this.parent() && this.parent().sidebar) {
4351 cls: 'dashboard-menu sidebar-menu'
4357 if (this.form === true) {
4363 if (this.align === 'right') {
4364 cfg.cls += ' navbar-right ml-md-auto';
4366 cfg.cls += ' navbar-left';
4370 if (this.align === 'right') {
4371 cfg.cls += ' navbar-right ml-md-auto';
4373 cfg.cls += ' mr-auto';
4377 cfg.cls += ' navbar-inverse';
4385 * sets the active Navigation item
4386 * @param {Roo.bootstrap.NavItem} the new current navitem
4388 setActiveItem : function(item)
4391 Roo.each(this.navItems, function(v){
4396 v.setActive(false, true);
4403 item.setActive(true, true);
4404 this.fireEvent('changed', this, item, prev);
4409 * gets the active Navigation item
4410 * @return {Roo.bootstrap.NavItem} the current navitem
4412 getActive : function()
4416 Roo.each(this.navItems, function(v){
4427 indexOfNav : function()
4431 Roo.each(this.navItems, function(v,i){
4442 * adds a Navigation item
4443 * @param {Roo.bootstrap.NavItem} the navitem to add
4445 addItem : function(cfg)
4447 var cn = new Roo.bootstrap.NavItem(cfg);
4449 cn.parentId = this.id;
4450 cn.onRender(this.el, null);
4454 * register a Navigation item
4455 * @param {Roo.bootstrap.NavItem} the navitem to add
4457 register : function(item)
4459 this.navItems.push( item);
4460 item.navId = this.navId;
4465 * clear all the Navigation item
4468 clearAll : function()
4471 this.el.dom.innerHTML = '';
4474 getNavItem: function(tabId)
4477 Roo.each(this.navItems, function(e) {
4478 if (e.tabId == tabId) {
4488 setActiveNext : function()
4490 var i = this.indexOfNav(this.getActive());
4491 if (i > this.navItems.length) {
4494 this.setActiveItem(this.navItems[i+1]);
4496 setActivePrev : function()
4498 var i = this.indexOfNav(this.getActive());
4502 this.setActiveItem(this.navItems[i-1]);
4504 clearWasActive : function(except) {
4505 Roo.each(this.navItems, function(e) {
4506 if (e.tabId != except.tabId && e.was_active) {
4507 e.was_active = false;
4514 getWasActive : function ()
4517 Roo.each(this.navItems, function(e) {
4532 Roo.apply(Roo.bootstrap.NavGroup, {
4536 * register a Navigation Group
4537 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4539 register : function(navgrp)
4541 this.groups[navgrp.navId] = navgrp;
4545 * fetch a Navigation Group based on the navigation ID
4546 * @param {string} the navgroup to add
4547 * @returns {Roo.bootstrap.NavGroup} the navgroup
4549 get: function(navId) {
4550 if (typeof(this.groups[navId]) == 'undefined') {
4552 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4554 return this.groups[navId] ;
4569 * @class Roo.bootstrap.NavItem
4570 * @extends Roo.bootstrap.Component
4571 * Bootstrap Navbar.NavItem class
4572 * @cfg {String} href link to
4573 * @cfg {String} html content of button
4574 * @cfg {String} badge text inside badge
4575 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4576 * @cfg {String} glyphicon DEPRICATED - use fa
4577 * @cfg {String} icon DEPRICATED - use fa
4578 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4579 * @cfg {Boolean} active Is item active
4580 * @cfg {Boolean} disabled Is item disabled
4582 * @cfg {Boolean} preventDefault (true | false) default false
4583 * @cfg {String} tabId the tab that this item activates.
4584 * @cfg {String} tagtype (a|span) render as a href or span?
4585 * @cfg {Boolean} animateRef (true|false) link to element default false
4588 * Create a new Navbar Item
4589 * @param {Object} config The config object
4591 Roo.bootstrap.NavItem = function(config){
4592 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4597 * The raw click event for the entire grid.
4598 * @param {Roo.EventObject} e
4603 * Fires when the active item active state changes
4604 * @param {Roo.bootstrap.NavItem} this
4605 * @param {boolean} state the new state
4611 * Fires when scroll to element
4612 * @param {Roo.bootstrap.NavItem} this
4613 * @param {Object} options
4614 * @param {Roo.EventObject} e
4622 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4631 preventDefault : false,
4638 getAutoCreate : function(){
4647 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4649 if (this.disabled) {
4650 cfg.cls += ' disabled';
4653 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4657 href : this.href || "#",
4658 html: this.html || ''
4661 if (this.tagtype == 'a') {
4662 cfg.cn[0].cls = 'nav-link';
4665 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4668 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4670 if(this.glyphicon) {
4671 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4676 cfg.cn[0].html += " <span class='caret'></span>";
4680 if (this.badge !== '') {
4682 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4690 initEvents: function()
4692 if (typeof (this.menu) != 'undefined') {
4693 this.menu.parentType = this.xtype;
4694 this.menu.triggerEl = this.el;
4695 this.menu = this.addxtype(Roo.apply({}, this.menu));
4698 this.el.select('a',true).on('click', this.onClick, this);
4700 if(this.tagtype == 'span'){
4701 this.el.select('span',true).on('click', this.onClick, this);
4704 // at this point parent should be available..
4705 this.parent().register(this);
4708 onClick : function(e)
4710 if (e.getTarget('.dropdown-menu-item')) {
4711 // did you click on a menu itemm.... - then don't trigger onclick..
4716 this.preventDefault ||
4719 Roo.log("NavItem - prevent Default?");
4723 if (this.disabled) {
4727 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4728 if (tg && tg.transition) {
4729 Roo.log("waiting for the transitionend");
4735 //Roo.log("fire event clicked");
4736 if(this.fireEvent('click', this, e) === false){
4740 if(this.tagtype == 'span'){
4744 //Roo.log(this.href);
4745 var ael = this.el.select('a',true).first();
4748 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4749 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4750 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4751 return; // ignore... - it's a 'hash' to another page.
4753 Roo.log("NavItem - prevent Default?");
4755 this.scrollToElement(e);
4759 var p = this.parent();
4761 if (['tabs','pills'].indexOf(p.type)!==-1) {
4762 if (typeof(p.setActiveItem) !== 'undefined') {
4763 p.setActiveItem(this);
4767 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4768 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4769 // remove the collapsed menu expand...
4770 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4774 isActive: function () {
4777 setActive : function(state, fire, is_was_active)
4779 if (this.active && !state && this.navId) {
4780 this.was_active = true;
4781 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4783 nv.clearWasActive(this);
4787 this.active = state;
4790 this.el.removeClass('active');
4791 } else if (!this.el.hasClass('active')) {
4792 this.el.addClass('active');
4795 this.fireEvent('changed', this, state);
4798 // show a panel if it's registered and related..
4800 if (!this.navId || !this.tabId || !state || is_was_active) {
4804 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4808 var pan = tg.getPanelByName(this.tabId);
4812 // if we can not flip to new panel - go back to old nav highlight..
4813 if (false == tg.showPanel(pan)) {
4814 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4816 var onav = nv.getWasActive();
4818 onav.setActive(true, false, true);
4827 // this should not be here...
4828 setDisabled : function(state)
4830 this.disabled = state;
4832 this.el.removeClass('disabled');
4833 } else if (!this.el.hasClass('disabled')) {
4834 this.el.addClass('disabled');
4840 * Fetch the element to display the tooltip on.
4841 * @return {Roo.Element} defaults to this.el
4843 tooltipEl : function()
4845 return this.el.select('' + this.tagtype + '', true).first();
4848 scrollToElement : function(e)
4850 var c = document.body;
4853 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4855 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4856 c = document.documentElement;
4859 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4865 var o = target.calcOffsetsTo(c);
4872 this.fireEvent('scrollto', this, options, e);
4874 Roo.get(c).scrollTo('top', options.value, true);
4887 * <span> icon </span>
4888 * <span> text </span>
4889 * <span>badge </span>
4893 * @class Roo.bootstrap.NavSidebarItem
4894 * @extends Roo.bootstrap.NavItem
4895 * Bootstrap Navbar.NavSidebarItem class
4896 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4897 * {Boolean} open is the menu open
4898 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4899 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4900 * {String} buttonSize (sm|md|lg)the extra classes for the button
4901 * {Boolean} showArrow show arrow next to the text (default true)
4903 * Create a new Navbar Button
4904 * @param {Object} config The config object
4906 Roo.bootstrap.NavSidebarItem = function(config){
4907 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4912 * The raw click event for the entire grid.
4913 * @param {Roo.EventObject} e
4918 * Fires when the active item active state changes
4919 * @param {Roo.bootstrap.NavSidebarItem} this
4920 * @param {boolean} state the new state
4928 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4930 badgeWeight : 'default',
4936 buttonWeight : 'default',
4942 getAutoCreate : function(){
4947 href : this.href || '#',
4953 if(this.buttonView){
4956 href : this.href || '#',
4957 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4970 cfg.cls += ' active';
4973 if (this.disabled) {
4974 cfg.cls += ' disabled';
4977 cfg.cls += ' open x-open';
4980 if (this.glyphicon || this.icon) {
4981 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4982 a.cn.push({ tag : 'i', cls : c }) ;
4985 if(!this.buttonView){
4988 html : this.html || ''
4995 if (this.badge !== '') {
4996 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
5002 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5005 a.cls += ' dropdown-toggle treeview' ;
5011 initEvents : function()
5013 if (typeof (this.menu) != 'undefined') {
5014 this.menu.parentType = this.xtype;
5015 this.menu.triggerEl = this.el;
5016 this.menu = this.addxtype(Roo.apply({}, this.menu));
5019 this.el.on('click', this.onClick, this);
5021 if(this.badge !== ''){
5022 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5027 onClick : function(e)
5034 if(this.preventDefault){
5038 this.fireEvent('click', this);
5041 disable : function()
5043 this.setDisabled(true);
5048 this.setDisabled(false);
5051 setDisabled : function(state)
5053 if(this.disabled == state){
5057 this.disabled = state;
5060 this.el.addClass('disabled');
5064 this.el.removeClass('disabled');
5069 setActive : function(state)
5071 if(this.active == state){
5075 this.active = state;
5078 this.el.addClass('active');
5082 this.el.removeClass('active');
5087 isActive: function ()
5092 setBadge : function(str)
5098 this.badgeEl.dom.innerHTML = str;
5115 * @class Roo.bootstrap.Row
5116 * @extends Roo.bootstrap.Component
5117 * Bootstrap Row class (contains columns...)
5121 * @param {Object} config The config object
5124 Roo.bootstrap.Row = function(config){
5125 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5128 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5130 getAutoCreate : function(){
5149 * @class Roo.bootstrap.Element
5150 * @extends Roo.bootstrap.Component
5151 * Bootstrap Element class
5152 * @cfg {String} html contents of the element
5153 * @cfg {String} tag tag of the element
5154 * @cfg {String} cls class of the element
5155 * @cfg {Boolean} preventDefault (true|false) default false
5156 * @cfg {Boolean} clickable (true|false) default false
5159 * Create a new Element
5160 * @param {Object} config The config object
5163 Roo.bootstrap.Element = function(config){
5164 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5170 * When a element is chick
5171 * @param {Roo.bootstrap.Element} this
5172 * @param {Roo.EventObject} e
5178 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5183 preventDefault: false,
5186 getAutoCreate : function(){
5190 // cls: this.cls, double assign in parent class Component.js :: onRender
5197 initEvents: function()
5199 Roo.bootstrap.Element.superclass.initEvents.call(this);
5202 this.el.on('click', this.onClick, this);
5207 onClick : function(e)
5209 if(this.preventDefault){
5213 this.fireEvent('click', this, e);
5216 getValue : function()
5218 return this.el.dom.innerHTML;
5221 setValue : function(value)
5223 this.el.dom.innerHTML = value;
5238 * @class Roo.bootstrap.Pagination
5239 * @extends Roo.bootstrap.Component
5240 * Bootstrap Pagination class
5241 * @cfg {String} size xs | sm | md | lg
5242 * @cfg {Boolean} inverse false | true
5245 * Create a new Pagination
5246 * @param {Object} config The config object
5249 Roo.bootstrap.Pagination = function(config){
5250 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5253 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5259 getAutoCreate : function(){
5265 cfg.cls += ' inverse';
5271 cfg.cls += " " + this.cls;
5289 * @class Roo.bootstrap.PaginationItem
5290 * @extends Roo.bootstrap.Component
5291 * Bootstrap PaginationItem class
5292 * @cfg {String} html text
5293 * @cfg {String} href the link
5294 * @cfg {Boolean} preventDefault (true | false) default true
5295 * @cfg {Boolean} active (true | false) default false
5296 * @cfg {Boolean} disabled default false
5300 * Create a new PaginationItem
5301 * @param {Object} config The config object
5305 Roo.bootstrap.PaginationItem = function(config){
5306 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5311 * The raw click event for the entire grid.
5312 * @param {Roo.EventObject} e
5318 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5322 preventDefault: true,
5327 getAutoCreate : function(){
5333 href : this.href ? this.href : '#',
5334 html : this.html ? this.html : ''
5344 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5348 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5354 initEvents: function() {
5356 this.el.on('click', this.onClick, this);
5359 onClick : function(e)
5361 Roo.log('PaginationItem on click ');
5362 if(this.preventDefault){
5370 this.fireEvent('click', this, e);
5386 * @class Roo.bootstrap.Slider
5387 * @extends Roo.bootstrap.Component
5388 * Bootstrap Slider class
5391 * Create a new Slider
5392 * @param {Object} config The config object
5395 Roo.bootstrap.Slider = function(config){
5396 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5399 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5401 getAutoCreate : function(){
5405 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5409 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5421 * Ext JS Library 1.1.1
5422 * Copyright(c) 2006-2007, Ext JS, LLC.
5424 * Originally Released Under LGPL - original licence link has changed is not relivant.
5427 * <script type="text/javascript">
5432 * @class Roo.grid.ColumnModel
5433 * @extends Roo.util.Observable
5434 * This is the default implementation of a ColumnModel used by the Grid. It defines
5435 * the columns in the grid.
5438 var colModel = new Roo.grid.ColumnModel([
5439 {header: "Ticker", width: 60, sortable: true, locked: true},
5440 {header: "Company Name", width: 150, sortable: true},
5441 {header: "Market Cap.", width: 100, sortable: true},
5442 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5443 {header: "Employees", width: 100, sortable: true, resizable: false}
5448 * The config options listed for this class are options which may appear in each
5449 * individual column definition.
5450 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5452 * @param {Object} config An Array of column config objects. See this class's
5453 * config objects for details.
5455 Roo.grid.ColumnModel = function(config){
5457 * The config passed into the constructor
5459 this.config = config;
5462 // if no id, create one
5463 // if the column does not have a dataIndex mapping,
5464 // map it to the order it is in the config
5465 for(var i = 0, len = config.length; i < len; i++){
5467 if(typeof c.dataIndex == "undefined"){
5470 if(typeof c.renderer == "string"){
5471 c.renderer = Roo.util.Format[c.renderer];
5473 if(typeof c.id == "undefined"){
5476 if(c.editor && c.editor.xtype){
5477 c.editor = Roo.factory(c.editor, Roo.grid);
5479 if(c.editor && c.editor.isFormField){
5480 c.editor = new Roo.grid.GridEditor(c.editor);
5482 this.lookup[c.id] = c;
5486 * The width of columns which have no width specified (defaults to 100)
5489 this.defaultWidth = 100;
5492 * Default sortable of columns which have no sortable specified (defaults to false)
5495 this.defaultSortable = false;
5499 * @event widthchange
5500 * Fires when the width of a column changes.
5501 * @param {ColumnModel} this
5502 * @param {Number} columnIndex The column index
5503 * @param {Number} newWidth The new width
5505 "widthchange": true,
5507 * @event headerchange
5508 * Fires when the text of a header changes.
5509 * @param {ColumnModel} this
5510 * @param {Number} columnIndex The column index
5511 * @param {Number} newText The new header text
5513 "headerchange": true,
5515 * @event hiddenchange
5516 * Fires when a column is hidden or "unhidden".
5517 * @param {ColumnModel} this
5518 * @param {Number} columnIndex The column index
5519 * @param {Boolean} hidden true if hidden, false otherwise
5521 "hiddenchange": true,
5523 * @event columnmoved
5524 * Fires when a column is moved.
5525 * @param {ColumnModel} this
5526 * @param {Number} oldIndex
5527 * @param {Number} newIndex
5529 "columnmoved" : true,
5531 * @event columlockchange
5532 * Fires when a column's locked state is changed
5533 * @param {ColumnModel} this
5534 * @param {Number} colIndex
5535 * @param {Boolean} locked true if locked
5537 "columnlockchange" : true
5539 Roo.grid.ColumnModel.superclass.constructor.call(this);
5541 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5543 * @cfg {String} header The header text to display in the Grid view.
5546 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5547 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5548 * specified, the column's index is used as an index into the Record's data Array.
5551 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5552 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5555 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5556 * Defaults to the value of the {@link #defaultSortable} property.
5557 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5560 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5563 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5566 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5569 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5572 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5573 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5574 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5575 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5578 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5581 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5584 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5587 * @cfg {String} cursor (Optional)
5590 * @cfg {String} tooltip (Optional)
5593 * @cfg {Number} xs (Optional)
5596 * @cfg {Number} sm (Optional)
5599 * @cfg {Number} md (Optional)
5602 * @cfg {Number} lg (Optional)
5605 * Returns the id of the column at the specified index.
5606 * @param {Number} index The column index
5607 * @return {String} the id
5609 getColumnId : function(index){
5610 return this.config[index].id;
5614 * Returns the column for a specified id.
5615 * @param {String} id The column id
5616 * @return {Object} the column
5618 getColumnById : function(id){
5619 return this.lookup[id];
5624 * Returns the column for a specified dataIndex.
5625 * @param {String} dataIndex The column dataIndex
5626 * @return {Object|Boolean} the column or false if not found
5628 getColumnByDataIndex: function(dataIndex){
5629 var index = this.findColumnIndex(dataIndex);
5630 return index > -1 ? this.config[index] : false;
5634 * Returns the index for a specified column id.
5635 * @param {String} id The column id
5636 * @return {Number} the index, or -1 if not found
5638 getIndexById : function(id){
5639 for(var i = 0, len = this.config.length; i < len; i++){
5640 if(this.config[i].id == id){
5648 * Returns the index for a specified column dataIndex.
5649 * @param {String} dataIndex The column dataIndex
5650 * @return {Number} the index, or -1 if not found
5653 findColumnIndex : function(dataIndex){
5654 for(var i = 0, len = this.config.length; i < len; i++){
5655 if(this.config[i].dataIndex == dataIndex){
5663 moveColumn : function(oldIndex, newIndex){
5664 var c = this.config[oldIndex];
5665 this.config.splice(oldIndex, 1);
5666 this.config.splice(newIndex, 0, c);
5667 this.dataMap = null;
5668 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5671 isLocked : function(colIndex){
5672 return this.config[colIndex].locked === true;
5675 setLocked : function(colIndex, value, suppressEvent){
5676 if(this.isLocked(colIndex) == value){
5679 this.config[colIndex].locked = value;
5681 this.fireEvent("columnlockchange", this, colIndex, value);
5685 getTotalLockedWidth : function(){
5687 for(var i = 0; i < this.config.length; i++){
5688 if(this.isLocked(i) && !this.isHidden(i)){
5689 this.totalWidth += this.getColumnWidth(i);
5695 getLockedCount : function(){
5696 for(var i = 0, len = this.config.length; i < len; i++){
5697 if(!this.isLocked(i)){
5702 return this.config.length;
5706 * Returns the number of columns.
5709 getColumnCount : function(visibleOnly){
5710 if(visibleOnly === true){
5712 for(var i = 0, len = this.config.length; i < len; i++){
5713 if(!this.isHidden(i)){
5719 return this.config.length;
5723 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5724 * @param {Function} fn
5725 * @param {Object} scope (optional)
5726 * @return {Array} result
5728 getColumnsBy : function(fn, scope){
5730 for(var i = 0, len = this.config.length; i < len; i++){
5731 var c = this.config[i];
5732 if(fn.call(scope||this, c, i) === true){
5740 * Returns true if the specified column is sortable.
5741 * @param {Number} col The column index
5744 isSortable : function(col){
5745 if(typeof this.config[col].sortable == "undefined"){
5746 return this.defaultSortable;
5748 return this.config[col].sortable;
5752 * Returns the rendering (formatting) function defined for the column.
5753 * @param {Number} col The column index.
5754 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5756 getRenderer : function(col){
5757 if(!this.config[col].renderer){
5758 return Roo.grid.ColumnModel.defaultRenderer;
5760 return this.config[col].renderer;
5764 * Sets the rendering (formatting) function for a column.
5765 * @param {Number} col The column index
5766 * @param {Function} fn The function to use to process the cell's raw data
5767 * to return HTML markup for the grid view. The render function is called with
5768 * the following parameters:<ul>
5769 * <li>Data value.</li>
5770 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5771 * <li>css A CSS style string to apply to the table cell.</li>
5772 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5773 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5774 * <li>Row index</li>
5775 * <li>Column index</li>
5776 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5778 setRenderer : function(col, fn){
5779 this.config[col].renderer = fn;
5783 * Returns the width for the specified column.
5784 * @param {Number} col The column index
5787 getColumnWidth : function(col){
5788 return this.config[col].width * 1 || this.defaultWidth;
5792 * Sets the width for a column.
5793 * @param {Number} col The column index
5794 * @param {Number} width The new width
5796 setColumnWidth : function(col, width, suppressEvent){
5797 this.config[col].width = width;
5798 this.totalWidth = null;
5800 this.fireEvent("widthchange", this, col, width);
5805 * Returns the total width of all columns.
5806 * @param {Boolean} includeHidden True to include hidden column widths
5809 getTotalWidth : function(includeHidden){
5810 if(!this.totalWidth){
5811 this.totalWidth = 0;
5812 for(var i = 0, len = this.config.length; i < len; i++){
5813 if(includeHidden || !this.isHidden(i)){
5814 this.totalWidth += this.getColumnWidth(i);
5818 return this.totalWidth;
5822 * Returns the header for the specified column.
5823 * @param {Number} col The column index
5826 getColumnHeader : function(col){
5827 return this.config[col].header;
5831 * Sets the header for a column.
5832 * @param {Number} col The column index
5833 * @param {String} header The new header
5835 setColumnHeader : function(col, header){
5836 this.config[col].header = header;
5837 this.fireEvent("headerchange", this, col, header);
5841 * Returns the tooltip for the specified column.
5842 * @param {Number} col The column index
5845 getColumnTooltip : function(col){
5846 return this.config[col].tooltip;
5849 * Sets the tooltip for a column.
5850 * @param {Number} col The column index
5851 * @param {String} tooltip The new tooltip
5853 setColumnTooltip : function(col, tooltip){
5854 this.config[col].tooltip = tooltip;
5858 * Returns the dataIndex for the specified column.
5859 * @param {Number} col The column index
5862 getDataIndex : function(col){
5863 return this.config[col].dataIndex;
5867 * Sets the dataIndex for a column.
5868 * @param {Number} col The column index
5869 * @param {Number} dataIndex The new dataIndex
5871 setDataIndex : function(col, dataIndex){
5872 this.config[col].dataIndex = dataIndex;
5878 * Returns true if the cell is editable.
5879 * @param {Number} colIndex The column index
5880 * @param {Number} rowIndex The row index - this is nto actually used..?
5883 isCellEditable : function(colIndex, rowIndex){
5884 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5888 * Returns the editor defined for the cell/column.
5889 * return false or null to disable editing.
5890 * @param {Number} colIndex The column index
5891 * @param {Number} rowIndex The row index
5894 getCellEditor : function(colIndex, rowIndex){
5895 return this.config[colIndex].editor;
5899 * Sets if a column is editable.
5900 * @param {Number} col The column index
5901 * @param {Boolean} editable True if the column is editable
5903 setEditable : function(col, editable){
5904 this.config[col].editable = editable;
5909 * Returns true if the column is hidden.
5910 * @param {Number} colIndex The column index
5913 isHidden : function(colIndex){
5914 return this.config[colIndex].hidden;
5919 * Returns true if the column width cannot be changed
5921 isFixed : function(colIndex){
5922 return this.config[colIndex].fixed;
5926 * Returns true if the column can be resized
5929 isResizable : function(colIndex){
5930 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5933 * Sets if a column is hidden.
5934 * @param {Number} colIndex The column index
5935 * @param {Boolean} hidden True if the column is hidden
5937 setHidden : function(colIndex, hidden){
5938 this.config[colIndex].hidden = hidden;
5939 this.totalWidth = null;
5940 this.fireEvent("hiddenchange", this, colIndex, hidden);
5944 * Sets the editor for a column.
5945 * @param {Number} col The column index
5946 * @param {Object} editor The editor object
5948 setEditor : function(col, editor){
5949 this.config[col].editor = editor;
5953 Roo.grid.ColumnModel.defaultRenderer = function(value)
5955 if(typeof value == "object") {
5958 if(typeof value == "string" && value.length < 1){
5962 return String.format("{0}", value);
5965 // Alias for backwards compatibility
5966 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5969 * Ext JS Library 1.1.1
5970 * Copyright(c) 2006-2007, Ext JS, LLC.
5972 * Originally Released Under LGPL - original licence link has changed is not relivant.
5975 * <script type="text/javascript">
5979 * @class Roo.LoadMask
5980 * A simple utility class for generically masking elements while loading data. If the element being masked has
5981 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5982 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5983 * element's UpdateManager load indicator and will be destroyed after the initial load.
5985 * Create a new LoadMask
5986 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5987 * @param {Object} config The config object
5989 Roo.LoadMask = function(el, config){
5990 this.el = Roo.get(el);
5991 Roo.apply(this, config);
5993 this.store.on('beforeload', this.onBeforeLoad, this);
5994 this.store.on('load', this.onLoad, this);
5995 this.store.on('loadexception', this.onLoadException, this);
5996 this.removeMask = false;
5998 var um = this.el.getUpdateManager();
5999 um.showLoadIndicator = false; // disable the default indicator
6000 um.on('beforeupdate', this.onBeforeLoad, this);
6001 um.on('update', this.onLoad, this);
6002 um.on('failure', this.onLoad, this);
6003 this.removeMask = true;
6007 Roo.LoadMask.prototype = {
6009 * @cfg {Boolean} removeMask
6010 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6011 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
6015 * The text to display in a centered loading message box (defaults to 'Loading...')
6019 * @cfg {String} msgCls
6020 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6022 msgCls : 'x-mask-loading',
6025 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6031 * Disables the mask to prevent it from being displayed
6033 disable : function(){
6034 this.disabled = true;
6038 * Enables the mask so that it can be displayed
6040 enable : function(){
6041 this.disabled = false;
6044 onLoadException : function()
6048 if (typeof(arguments[3]) != 'undefined') {
6049 Roo.MessageBox.alert("Error loading",arguments[3]);
6053 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6054 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6061 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6066 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6070 onBeforeLoad : function(){
6072 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6077 destroy : function(){
6079 this.store.un('beforeload', this.onBeforeLoad, this);
6080 this.store.un('load', this.onLoad, this);
6081 this.store.un('loadexception', this.onLoadException, this);
6083 var um = this.el.getUpdateManager();
6084 um.un('beforeupdate', this.onBeforeLoad, this);
6085 um.un('update', this.onLoad, this);
6086 um.un('failure', this.onLoad, this);
6097 * @class Roo.bootstrap.Table
6098 * @extends Roo.bootstrap.Component
6099 * Bootstrap Table class
6100 * @cfg {String} cls table class
6101 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6102 * @cfg {String} bgcolor Specifies the background color for a table
6103 * @cfg {Number} border Specifies whether the table cells should have borders or not
6104 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6105 * @cfg {Number} cellspacing Specifies the space between cells
6106 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6107 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6108 * @cfg {String} sortable Specifies that the table should be sortable
6109 * @cfg {String} summary Specifies a summary of the content of a table
6110 * @cfg {Number} width Specifies the width of a table
6111 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6113 * @cfg {boolean} striped Should the rows be alternative striped
6114 * @cfg {boolean} bordered Add borders to the table
6115 * @cfg {boolean} hover Add hover highlighting
6116 * @cfg {boolean} condensed Format condensed
6117 * @cfg {boolean} responsive Format condensed
6118 * @cfg {Boolean} loadMask (true|false) default false
6119 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6120 * @cfg {Boolean} headerShow (true|false) generate thead, default true
6121 * @cfg {Boolean} rowSelection (true|false) default false
6122 * @cfg {Boolean} cellSelection (true|false) default false
6123 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6124 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
6125 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
6126 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6130 * Create a new Table
6131 * @param {Object} config The config object
6134 Roo.bootstrap.Table = function(config){
6135 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6140 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6141 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6142 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6143 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6145 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6147 this.sm.grid = this;
6148 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6149 this.sm = this.selModel;
6150 this.sm.xmodule = this.xmodule || false;
6153 if (this.cm && typeof(this.cm.config) == 'undefined') {
6154 this.colModel = new Roo.grid.ColumnModel(this.cm);
6155 this.cm = this.colModel;
6156 this.cm.xmodule = this.xmodule || false;
6159 this.store= Roo.factory(this.store, Roo.data);
6160 this.ds = this.store;
6161 this.ds.xmodule = this.xmodule || false;
6164 if (this.footer && this.store) {
6165 this.footer.dataSource = this.ds;
6166 this.footer = Roo.factory(this.footer);
6173 * Fires when a cell is clicked
6174 * @param {Roo.bootstrap.Table} this
6175 * @param {Roo.Element} el
6176 * @param {Number} rowIndex
6177 * @param {Number} columnIndex
6178 * @param {Roo.EventObject} e
6182 * @event celldblclick
6183 * Fires when a cell is double clicked
6184 * @param {Roo.bootstrap.Table} this
6185 * @param {Roo.Element} el
6186 * @param {Number} rowIndex
6187 * @param {Number} columnIndex
6188 * @param {Roo.EventObject} e
6190 "celldblclick" : true,
6193 * Fires when a row is clicked
6194 * @param {Roo.bootstrap.Table} this
6195 * @param {Roo.Element} el
6196 * @param {Number} rowIndex
6197 * @param {Roo.EventObject} e
6201 * @event rowdblclick
6202 * Fires when a row is double clicked
6203 * @param {Roo.bootstrap.Table} this
6204 * @param {Roo.Element} el
6205 * @param {Number} rowIndex
6206 * @param {Roo.EventObject} e
6208 "rowdblclick" : true,
6211 * Fires when a mouseover occur
6212 * @param {Roo.bootstrap.Table} this
6213 * @param {Roo.Element} el
6214 * @param {Number} rowIndex
6215 * @param {Number} columnIndex
6216 * @param {Roo.EventObject} e
6221 * Fires when a mouseout occur
6222 * @param {Roo.bootstrap.Table} this
6223 * @param {Roo.Element} el
6224 * @param {Number} rowIndex
6225 * @param {Number} columnIndex
6226 * @param {Roo.EventObject} e
6231 * Fires when a row is rendered, so you can change add a style to it.
6232 * @param {Roo.bootstrap.Table} this
6233 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6237 * @event rowsrendered
6238 * Fires when all the rows have been rendered
6239 * @param {Roo.bootstrap.Table} this
6241 'rowsrendered' : true,
6243 * @event contextmenu
6244 * The raw contextmenu event for the entire grid.
6245 * @param {Roo.EventObject} e
6247 "contextmenu" : true,
6249 * @event rowcontextmenu
6250 * Fires when a row is right clicked
6251 * @param {Roo.bootstrap.Table} this
6252 * @param {Number} rowIndex
6253 * @param {Roo.EventObject} e
6255 "rowcontextmenu" : true,
6257 * @event cellcontextmenu
6258 * Fires when a cell is right clicked
6259 * @param {Roo.bootstrap.Table} this
6260 * @param {Number} rowIndex
6261 * @param {Number} cellIndex
6262 * @param {Roo.EventObject} e
6264 "cellcontextmenu" : true,
6266 * @event headercontextmenu
6267 * Fires when a header is right clicked
6268 * @param {Roo.bootstrap.Table} this
6269 * @param {Number} columnIndex
6270 * @param {Roo.EventObject} e
6272 "headercontextmenu" : true
6276 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6302 rowSelection : false,
6303 cellSelection : false,
6306 // Roo.Element - the tbody
6308 // Roo.Element - thead element
6311 container: false, // used by gridpanel...
6317 auto_hide_footer : false,
6319 getAutoCreate : function()
6321 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6328 if (this.scrollBody) {
6329 cfg.cls += ' table-body-fixed';
6332 cfg.cls += ' table-striped';
6336 cfg.cls += ' table-hover';
6338 if (this.bordered) {
6339 cfg.cls += ' table-bordered';
6341 if (this.condensed) {
6342 cfg.cls += ' table-condensed';
6344 if (this.responsive) {
6345 cfg.cls += ' table-responsive';
6349 cfg.cls+= ' ' +this.cls;
6352 // this lot should be simplifed...
6365 ].forEach(function(k) {
6373 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6376 if(this.store || this.cm){
6377 if(this.headerShow){
6378 cfg.cn.push(this.renderHeader());
6381 cfg.cn.push(this.renderBody());
6383 if(this.footerShow){
6384 cfg.cn.push(this.renderFooter());
6386 // where does this come from?
6387 //cfg.cls+= ' TableGrid';
6390 return { cn : [ cfg ] };
6393 initEvents : function()
6395 if(!this.store || !this.cm){
6398 if (this.selModel) {
6399 this.selModel.initEvents();
6403 //Roo.log('initEvents with ds!!!!');
6405 this.mainBody = this.el.select('tbody', true).first();
6406 this.mainHead = this.el.select('thead', true).first();
6407 this.mainFoot = this.el.select('tfoot', true).first();
6413 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6414 e.on('click', _this.sort, _this);
6417 this.mainBody.on("click", this.onClick, this);
6418 this.mainBody.on("dblclick", this.onDblClick, this);
6420 // why is this done????? = it breaks dialogs??
6421 //this.parent().el.setStyle('position', 'relative');
6425 this.footer.parentId = this.id;
6426 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6429 this.el.select('tfoot tr td').first().addClass('hide');
6434 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6437 this.store.on('load', this.onLoad, this);
6438 this.store.on('beforeload', this.onBeforeLoad, this);
6439 this.store.on('update', this.onUpdate, this);
6440 this.store.on('add', this.onAdd, this);
6441 this.store.on("clear", this.clear, this);
6443 this.el.on("contextmenu", this.onContextMenu, this);
6445 this.mainBody.on('scroll', this.onBodyScroll, this);
6447 this.cm.on("headerchange", this.onHeaderChange, this);
6449 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6453 onContextMenu : function(e, t)
6455 this.processEvent("contextmenu", e);
6458 processEvent : function(name, e)
6460 if (name != 'touchstart' ) {
6461 this.fireEvent(name, e);
6464 var t = e.getTarget();
6466 var cell = Roo.get(t);
6472 if(cell.findParent('tfoot', false, true)){
6476 if(cell.findParent('thead', false, true)){
6478 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6479 cell = Roo.get(t).findParent('th', false, true);
6481 Roo.log("failed to find th in thead?");
6482 Roo.log(e.getTarget());
6487 var cellIndex = cell.dom.cellIndex;
6489 var ename = name == 'touchstart' ? 'click' : name;
6490 this.fireEvent("header" + ename, this, cellIndex, e);
6495 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6496 cell = Roo.get(t).findParent('td', false, true);
6498 Roo.log("failed to find th in tbody?");
6499 Roo.log(e.getTarget());
6504 var row = cell.findParent('tr', false, true);
6505 var cellIndex = cell.dom.cellIndex;
6506 var rowIndex = row.dom.rowIndex - 1;
6510 this.fireEvent("row" + name, this, rowIndex, e);
6514 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6520 onMouseover : function(e, el)
6522 var cell = Roo.get(el);
6528 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6529 cell = cell.findParent('td', false, true);
6532 var row = cell.findParent('tr', false, true);
6533 var cellIndex = cell.dom.cellIndex;
6534 var rowIndex = row.dom.rowIndex - 1; // start from 0
6536 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6540 onMouseout : function(e, el)
6542 var cell = Roo.get(el);
6548 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6549 cell = cell.findParent('td', false, true);
6552 var row = cell.findParent('tr', false, true);
6553 var cellIndex = cell.dom.cellIndex;
6554 var rowIndex = row.dom.rowIndex - 1; // start from 0
6556 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6560 onClick : function(e, el)
6562 var cell = Roo.get(el);
6564 if(!cell || (!this.cellSelection && !this.rowSelection)){
6568 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6569 cell = cell.findParent('td', false, true);
6572 if(!cell || typeof(cell) == 'undefined'){
6576 var row = cell.findParent('tr', false, true);
6578 if(!row || typeof(row) == 'undefined'){
6582 var cellIndex = cell.dom.cellIndex;
6583 var rowIndex = this.getRowIndex(row);
6585 // why??? - should these not be based on SelectionModel?
6586 if(this.cellSelection){
6587 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6590 if(this.rowSelection){
6591 this.fireEvent('rowclick', this, row, rowIndex, e);
6597 onDblClick : function(e,el)
6599 var cell = Roo.get(el);
6601 if(!cell || (!this.cellSelection && !this.rowSelection)){
6605 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6606 cell = cell.findParent('td', false, true);
6609 if(!cell || typeof(cell) == 'undefined'){
6613 var row = cell.findParent('tr', false, true);
6615 if(!row || typeof(row) == 'undefined'){
6619 var cellIndex = cell.dom.cellIndex;
6620 var rowIndex = this.getRowIndex(row);
6622 if(this.cellSelection){
6623 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6626 if(this.rowSelection){
6627 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6631 sort : function(e,el)
6633 var col = Roo.get(el);
6635 if(!col.hasClass('sortable')){
6639 var sort = col.attr('sort');
6642 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6646 this.store.sortInfo = {field : sort, direction : dir};
6649 Roo.log("calling footer first");
6650 this.footer.onClick('first');
6653 this.store.load({ params : { start : 0 } });
6657 renderHeader : function()
6665 this.totalWidth = 0;
6667 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6669 var config = cm.config[i];
6673 cls : 'x-hcol-' + i,
6675 html: cm.getColumnHeader(i)
6680 if(typeof(config.sortable) != 'undefined' && config.sortable){
6682 c.html = '<i class="glyphicon"></i>' + c.html;
6685 if(typeof(config.lgHeader) != 'undefined'){
6686 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6689 if(typeof(config.mdHeader) != 'undefined'){
6690 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6693 if(typeof(config.smHeader) != 'undefined'){
6694 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6697 if(typeof(config.xsHeader) != 'undefined'){
6698 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6705 if(typeof(config.tooltip) != 'undefined'){
6706 c.tooltip = config.tooltip;
6709 if(typeof(config.colspan) != 'undefined'){
6710 c.colspan = config.colspan;
6713 if(typeof(config.hidden) != 'undefined' && config.hidden){
6714 c.style += ' display:none;';
6717 if(typeof(config.dataIndex) != 'undefined'){
6718 c.sort = config.dataIndex;
6723 if(typeof(config.align) != 'undefined' && config.align.length){
6724 c.style += ' text-align:' + config.align + ';';
6727 if(typeof(config.width) != 'undefined'){
6728 c.style += ' width:' + config.width + 'px;';
6729 this.totalWidth += config.width;
6731 this.totalWidth += 100; // assume minimum of 100 per column?
6734 if(typeof(config.cls) != 'undefined'){
6735 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6738 ['xs','sm','md','lg'].map(function(size){
6740 if(typeof(config[size]) == 'undefined'){
6744 if (!config[size]) { // 0 = hidden
6745 c.cls += ' hidden-' + size;
6749 c.cls += ' col-' + size + '-' + config[size];
6759 renderBody : function()
6769 colspan : this.cm.getColumnCount()
6779 renderFooter : function()
6789 colspan : this.cm.getColumnCount()
6803 // Roo.log('ds onload');
6808 var ds = this.store;
6810 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6811 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6812 if (_this.store.sortInfo) {
6814 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6815 e.select('i', true).addClass(['glyphicon-arrow-up']);
6818 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6819 e.select('i', true).addClass(['glyphicon-arrow-down']);
6824 var tbody = this.mainBody;
6826 if(ds.getCount() > 0){
6827 ds.data.each(function(d,rowIndex){
6828 var row = this.renderRow(cm, ds, rowIndex);
6830 tbody.createChild(row);
6834 if(row.cellObjects.length){
6835 Roo.each(row.cellObjects, function(r){
6836 _this.renderCellObject(r);
6843 var tfoot = this.el.select('tfoot', true).first();
6845 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6847 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6849 var total = this.ds.getTotalCount();
6851 if(this.footer.pageSize < total){
6852 this.mainFoot.show();
6856 Roo.each(this.el.select('tbody td', true).elements, function(e){
6857 e.on('mouseover', _this.onMouseover, _this);
6860 Roo.each(this.el.select('tbody td', true).elements, function(e){
6861 e.on('mouseout', _this.onMouseout, _this);
6863 this.fireEvent('rowsrendered', this);
6869 onUpdate : function(ds,record)
6871 this.refreshRow(record);
6875 onRemove : function(ds, record, index, isUpdate){
6876 if(isUpdate !== true){
6877 this.fireEvent("beforerowremoved", this, index, record);
6879 var bt = this.mainBody.dom;
6881 var rows = this.el.select('tbody > tr', true).elements;
6883 if(typeof(rows[index]) != 'undefined'){
6884 bt.removeChild(rows[index].dom);
6887 // if(bt.rows[index]){
6888 // bt.removeChild(bt.rows[index]);
6891 if(isUpdate !== true){
6892 //this.stripeRows(index);
6893 //this.syncRowHeights(index, index);
6895 this.fireEvent("rowremoved", this, index, record);
6899 onAdd : function(ds, records, rowIndex)
6901 //Roo.log('on Add called');
6902 // - note this does not handle multiple adding very well..
6903 var bt = this.mainBody.dom;
6904 for (var i =0 ; i < records.length;i++) {
6905 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6906 //Roo.log(records[i]);
6907 //Roo.log(this.store.getAt(rowIndex+i));
6908 this.insertRow(this.store, rowIndex + i, false);
6915 refreshRow : function(record){
6916 var ds = this.store, index;
6917 if(typeof record == 'number'){
6919 record = ds.getAt(index);
6921 index = ds.indexOf(record);
6923 this.insertRow(ds, index, true);
6925 this.onRemove(ds, record, index+1, true);
6927 //this.syncRowHeights(index, index);
6929 this.fireEvent("rowupdated", this, index, record);
6932 insertRow : function(dm, rowIndex, isUpdate){
6935 this.fireEvent("beforerowsinserted", this, rowIndex);
6937 //var s = this.getScrollState();
6938 var row = this.renderRow(this.cm, this.store, rowIndex);
6939 // insert before rowIndex..
6940 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6944 if(row.cellObjects.length){
6945 Roo.each(row.cellObjects, function(r){
6946 _this.renderCellObject(r);
6951 this.fireEvent("rowsinserted", this, rowIndex);
6952 //this.syncRowHeights(firstRow, lastRow);
6953 //this.stripeRows(firstRow);
6960 getRowDom : function(rowIndex)
6962 var rows = this.el.select('tbody > tr', true).elements;
6964 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6967 // returns the object tree for a tr..
6970 renderRow : function(cm, ds, rowIndex)
6972 var d = ds.getAt(rowIndex);
6976 cls : 'x-row-' + rowIndex,
6980 var cellObjects = [];
6982 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6983 var config = cm.config[i];
6985 var renderer = cm.getRenderer(i);
6989 if(typeof(renderer) !== 'undefined'){
6990 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6992 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6993 // and are rendered into the cells after the row is rendered - using the id for the element.
6995 if(typeof(value) === 'object'){
7005 rowIndex : rowIndex,
7010 this.fireEvent('rowclass', this, rowcfg);
7014 cls : rowcfg.rowClass + ' x-col-' + i,
7016 html: (typeof(value) === 'object') ? '' : value
7023 if(typeof(config.colspan) != 'undefined'){
7024 td.colspan = config.colspan;
7027 if(typeof(config.hidden) != 'undefined' && config.hidden){
7028 td.style += ' display:none;';
7031 if(typeof(config.align) != 'undefined' && config.align.length){
7032 td.style += ' text-align:' + config.align + ';';
7034 if(typeof(config.valign) != 'undefined' && config.valign.length){
7035 td.style += ' vertical-align:' + config.valign + ';';
7038 if(typeof(config.width) != 'undefined'){
7039 td.style += ' width:' + config.width + 'px;';
7042 if(typeof(config.cursor) != 'undefined'){
7043 td.style += ' cursor:' + config.cursor + ';';
7046 if(typeof(config.cls) != 'undefined'){
7047 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7050 ['xs','sm','md','lg'].map(function(size){
7052 if(typeof(config[size]) == 'undefined'){
7056 if (!config[size]) { // 0 = hidden
7057 td.cls += ' hidden-' + size;
7061 td.cls += ' col-' + size + '-' + config[size];
7069 row.cellObjects = cellObjects;
7077 onBeforeLoad : function()
7086 this.el.select('tbody', true).first().dom.innerHTML = '';
7089 * Show or hide a row.
7090 * @param {Number} rowIndex to show or hide
7091 * @param {Boolean} state hide
7093 setRowVisibility : function(rowIndex, state)
7095 var bt = this.mainBody.dom;
7097 var rows = this.el.select('tbody > tr', true).elements;
7099 if(typeof(rows[rowIndex]) == 'undefined'){
7102 rows[rowIndex].dom.style.display = state ? '' : 'none';
7106 getSelectionModel : function(){
7108 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7110 return this.selModel;
7113 * Render the Roo.bootstrap object from renderder
7115 renderCellObject : function(r)
7119 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7121 var t = r.cfg.render(r.container);
7124 Roo.each(r.cfg.cn, function(c){
7126 container: t.getChildContainer(),
7129 _this.renderCellObject(child);
7134 getRowIndex : function(row)
7138 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7149 * Returns the grid's underlying element = used by panel.Grid
7150 * @return {Element} The element
7152 getGridEl : function(){
7156 * Forces a resize - used by panel.Grid
7157 * @return {Element} The element
7159 autoSize : function()
7161 //var ctr = Roo.get(this.container.dom.parentElement);
7162 var ctr = Roo.get(this.el.dom);
7164 var thd = this.getGridEl().select('thead',true).first();
7165 var tbd = this.getGridEl().select('tbody', true).first();
7166 var tfd = this.getGridEl().select('tfoot', true).first();
7168 var cw = ctr.getWidth();
7172 tbd.setSize(ctr.getWidth(),
7173 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7175 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7178 cw = Math.max(cw, this.totalWidth);
7179 this.getGridEl().select('tr',true).setWidth(cw);
7180 // resize 'expandable coloumn?
7182 return; // we doe not have a view in this design..
7185 onBodyScroll: function()
7187 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7189 this.mainHead.setStyle({
7190 'position' : 'relative',
7191 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7197 var scrollHeight = this.mainBody.dom.scrollHeight;
7199 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7201 var height = this.mainBody.getHeight();
7203 if(scrollHeight - height == scrollTop) {
7205 var total = this.ds.getTotalCount();
7207 if(this.footer.cursor + this.footer.pageSize < total){
7209 this.footer.ds.load({
7211 start : this.footer.cursor + this.footer.pageSize,
7212 limit : this.footer.pageSize
7222 onHeaderChange : function()
7224 var header = this.renderHeader();
7225 var table = this.el.select('table', true).first();
7227 this.mainHead.remove();
7228 this.mainHead = table.createChild(header, this.mainBody, false);
7231 onHiddenChange : function(colModel, colIndex, hidden)
7233 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7234 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7236 this.CSS.updateRule(thSelector, "display", "");
7237 this.CSS.updateRule(tdSelector, "display", "");
7240 this.CSS.updateRule(thSelector, "display", "none");
7241 this.CSS.updateRule(tdSelector, "display", "none");
7244 this.onHeaderChange();
7248 setColumnWidth: function(col_index, width)
7250 // width = "md-2 xs-2..."
7251 if(!this.colModel.config[col_index]) {
7255 var w = width.split(" ");
7257 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7259 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7262 for(var j = 0; j < w.length; j++) {
7268 var size_cls = w[j].split("-");
7270 if(!Number.isInteger(size_cls[1] * 1)) {
7274 if(!this.colModel.config[col_index][size_cls[0]]) {
7278 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7282 h_row[0].classList.replace(
7283 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7284 "col-"+size_cls[0]+"-"+size_cls[1]
7287 for(var i = 0; i < rows.length; i++) {
7289 var size_cls = w[j].split("-");
7291 if(!Number.isInteger(size_cls[1] * 1)) {
7295 if(!this.colModel.config[col_index][size_cls[0]]) {
7299 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7303 rows[i].classList.replace(
7304 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7305 "col-"+size_cls[0]+"-"+size_cls[1]
7309 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7324 * @class Roo.bootstrap.TableCell
7325 * @extends Roo.bootstrap.Component
7326 * Bootstrap TableCell class
7327 * @cfg {String} html cell contain text
7328 * @cfg {String} cls cell class
7329 * @cfg {String} tag cell tag (td|th) default td
7330 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7331 * @cfg {String} align Aligns the content in a cell
7332 * @cfg {String} axis Categorizes cells
7333 * @cfg {String} bgcolor Specifies the background color of a cell
7334 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7335 * @cfg {Number} colspan Specifies the number of columns a cell should span
7336 * @cfg {String} headers Specifies one or more header cells a cell is related to
7337 * @cfg {Number} height Sets the height of a cell
7338 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7339 * @cfg {Number} rowspan Sets the number of rows a cell should span
7340 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7341 * @cfg {String} valign Vertical aligns the content in a cell
7342 * @cfg {Number} width Specifies the width of a cell
7345 * Create a new TableCell
7346 * @param {Object} config The config object
7349 Roo.bootstrap.TableCell = function(config){
7350 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7353 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7373 getAutoCreate : function(){
7374 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7394 cfg.align=this.align
7400 cfg.bgcolor=this.bgcolor
7403 cfg.charoff=this.charoff
7406 cfg.colspan=this.colspan
7409 cfg.headers=this.headers
7412 cfg.height=this.height
7415 cfg.nowrap=this.nowrap
7418 cfg.rowspan=this.rowspan
7421 cfg.scope=this.scope
7424 cfg.valign=this.valign
7427 cfg.width=this.width
7446 * @class Roo.bootstrap.TableRow
7447 * @extends Roo.bootstrap.Component
7448 * Bootstrap TableRow class
7449 * @cfg {String} cls row class
7450 * @cfg {String} align Aligns the content in a table row
7451 * @cfg {String} bgcolor Specifies a background color for a table row
7452 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7453 * @cfg {String} valign Vertical aligns the content in a table row
7456 * Create a new TableRow
7457 * @param {Object} config The config object
7460 Roo.bootstrap.TableRow = function(config){
7461 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7464 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7472 getAutoCreate : function(){
7473 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7483 cfg.align = this.align;
7486 cfg.bgcolor = this.bgcolor;
7489 cfg.charoff = this.charoff;
7492 cfg.valign = this.valign;
7510 * @class Roo.bootstrap.TableBody
7511 * @extends Roo.bootstrap.Component
7512 * Bootstrap TableBody class
7513 * @cfg {String} cls element class
7514 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7515 * @cfg {String} align Aligns the content inside the element
7516 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7517 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7520 * Create a new TableBody
7521 * @param {Object} config The config object
7524 Roo.bootstrap.TableBody = function(config){
7525 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7528 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7536 getAutoCreate : function(){
7537 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7551 cfg.align = this.align;
7554 cfg.charoff = this.charoff;
7557 cfg.valign = this.valign;
7564 // initEvents : function()
7571 // this.store = Roo.factory(this.store, Roo.data);
7572 // this.store.on('load', this.onLoad, this);
7574 // this.store.load();
7578 // onLoad: function ()
7580 // this.fireEvent('load', this);
7590 * Ext JS Library 1.1.1
7591 * Copyright(c) 2006-2007, Ext JS, LLC.
7593 * Originally Released Under LGPL - original licence link has changed is not relivant.
7596 * <script type="text/javascript">
7599 // as we use this in bootstrap.
7600 Roo.namespace('Roo.form');
7602 * @class Roo.form.Action
7603 * Internal Class used to handle form actions
7605 * @param {Roo.form.BasicForm} el The form element or its id
7606 * @param {Object} config Configuration options
7611 // define the action interface
7612 Roo.form.Action = function(form, options){
7614 this.options = options || {};
7617 * Client Validation Failed
7620 Roo.form.Action.CLIENT_INVALID = 'client';
7622 * Server Validation Failed
7625 Roo.form.Action.SERVER_INVALID = 'server';
7627 * Connect to Server Failed
7630 Roo.form.Action.CONNECT_FAILURE = 'connect';
7632 * Reading Data from Server Failed
7635 Roo.form.Action.LOAD_FAILURE = 'load';
7637 Roo.form.Action.prototype = {
7639 failureType : undefined,
7640 response : undefined,
7644 run : function(options){
7649 success : function(response){
7654 handleResponse : function(response){
7658 // default connection failure
7659 failure : function(response){
7661 this.response = response;
7662 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7663 this.form.afterAction(this, false);
7666 processResponse : function(response){
7667 this.response = response;
7668 if(!response.responseText){
7671 this.result = this.handleResponse(response);
7675 // utility functions used internally
7676 getUrl : function(appendParams){
7677 var url = this.options.url || this.form.url || this.form.el.dom.action;
7679 var p = this.getParams();
7681 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7687 getMethod : function(){
7688 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7691 getParams : function(){
7692 var bp = this.form.baseParams;
7693 var p = this.options.params;
7695 if(typeof p == "object"){
7696 p = Roo.urlEncode(Roo.applyIf(p, bp));
7697 }else if(typeof p == 'string' && bp){
7698 p += '&' + Roo.urlEncode(bp);
7701 p = Roo.urlEncode(bp);
7706 createCallback : function(){
7708 success: this.success,
7709 failure: this.failure,
7711 timeout: (this.form.timeout*1000),
7712 upload: this.form.fileUpload ? this.success : undefined
7717 Roo.form.Action.Submit = function(form, options){
7718 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7721 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7724 haveProgress : false,
7725 uploadComplete : false,
7727 // uploadProgress indicator.
7728 uploadProgress : function()
7730 if (!this.form.progressUrl) {
7734 if (!this.haveProgress) {
7735 Roo.MessageBox.progress("Uploading", "Uploading");
7737 if (this.uploadComplete) {
7738 Roo.MessageBox.hide();
7742 this.haveProgress = true;
7744 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7746 var c = new Roo.data.Connection();
7748 url : this.form.progressUrl,
7753 success : function(req){
7754 //console.log(data);
7758 rdata = Roo.decode(req.responseText)
7760 Roo.log("Invalid data from server..");
7764 if (!rdata || !rdata.success) {
7766 Roo.MessageBox.alert(Roo.encode(rdata));
7769 var data = rdata.data;
7771 if (this.uploadComplete) {
7772 Roo.MessageBox.hide();
7777 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7778 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7781 this.uploadProgress.defer(2000,this);
7784 failure: function(data) {
7785 Roo.log('progress url failed ');
7796 // run get Values on the form, so it syncs any secondary forms.
7797 this.form.getValues();
7799 var o = this.options;
7800 var method = this.getMethod();
7801 var isPost = method == 'POST';
7802 if(o.clientValidation === false || this.form.isValid()){
7804 if (this.form.progressUrl) {
7805 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7806 (new Date() * 1) + '' + Math.random());
7811 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7812 form:this.form.el.dom,
7813 url:this.getUrl(!isPost),
7815 params:isPost ? this.getParams() : null,
7816 isUpload: this.form.fileUpload
7819 this.uploadProgress();
7821 }else if (o.clientValidation !== false){ // client validation failed
7822 this.failureType = Roo.form.Action.CLIENT_INVALID;
7823 this.form.afterAction(this, false);
7827 success : function(response)
7829 this.uploadComplete= true;
7830 if (this.haveProgress) {
7831 Roo.MessageBox.hide();
7835 var result = this.processResponse(response);
7836 if(result === true || result.success){
7837 this.form.afterAction(this, true);
7841 this.form.markInvalid(result.errors);
7842 this.failureType = Roo.form.Action.SERVER_INVALID;
7844 this.form.afterAction(this, false);
7846 failure : function(response)
7848 this.uploadComplete= true;
7849 if (this.haveProgress) {
7850 Roo.MessageBox.hide();
7853 this.response = response;
7854 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7855 this.form.afterAction(this, false);
7858 handleResponse : function(response){
7859 if(this.form.errorReader){
7860 var rs = this.form.errorReader.read(response);
7863 for(var i = 0, len = rs.records.length; i < len; i++) {
7864 var r = rs.records[i];
7868 if(errors.length < 1){
7872 success : rs.success,
7878 ret = Roo.decode(response.responseText);
7882 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7892 Roo.form.Action.Load = function(form, options){
7893 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7894 this.reader = this.form.reader;
7897 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7902 Roo.Ajax.request(Roo.apply(
7903 this.createCallback(), {
7904 method:this.getMethod(),
7905 url:this.getUrl(false),
7906 params:this.getParams()
7910 success : function(response){
7912 var result = this.processResponse(response);
7913 if(result === true || !result.success || !result.data){
7914 this.failureType = Roo.form.Action.LOAD_FAILURE;
7915 this.form.afterAction(this, false);
7918 this.form.clearInvalid();
7919 this.form.setValues(result.data);
7920 this.form.afterAction(this, true);
7923 handleResponse : function(response){
7924 if(this.form.reader){
7925 var rs = this.form.reader.read(response);
7926 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7928 success : rs.success,
7932 return Roo.decode(response.responseText);
7936 Roo.form.Action.ACTION_TYPES = {
7937 'load' : Roo.form.Action.Load,
7938 'submit' : Roo.form.Action.Submit
7947 * @class Roo.bootstrap.Form
7948 * @extends Roo.bootstrap.Component
7949 * Bootstrap Form class
7950 * @cfg {String} method GET | POST (default POST)
7951 * @cfg {String} labelAlign top | left (default top)
7952 * @cfg {String} align left | right - for navbars
7953 * @cfg {Boolean} loadMask load mask when submit (default true)
7958 * @param {Object} config The config object
7962 Roo.bootstrap.Form = function(config){
7964 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7966 Roo.bootstrap.Form.popover.apply();
7970 * @event clientvalidation
7971 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7972 * @param {Form} this
7973 * @param {Boolean} valid true if the form has passed client-side validation
7975 clientvalidation: true,
7977 * @event beforeaction
7978 * Fires before any action is performed. Return false to cancel the action.
7979 * @param {Form} this
7980 * @param {Action} action The action to be performed
7984 * @event actionfailed
7985 * Fires when an action fails.
7986 * @param {Form} this
7987 * @param {Action} action The action that failed
7989 actionfailed : true,
7991 * @event actioncomplete
7992 * Fires when an action is completed.
7993 * @param {Form} this
7994 * @param {Action} action The action that completed
7996 actioncomplete : true
8000 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
8003 * @cfg {String} method
8004 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8009 * The URL to use for form actions if one isn't supplied in the action options.
8012 * @cfg {Boolean} fileUpload
8013 * Set to true if this form is a file upload.
8017 * @cfg {Object} baseParams
8018 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8022 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8026 * @cfg {Sting} align (left|right) for navbar forms
8031 activeAction : null,
8034 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8035 * element by passing it or its id or mask the form itself by passing in true.
8038 waitMsgTarget : false,
8043 * @cfg {Boolean} errorMask (true|false) default false
8048 * @cfg {Number} maskOffset Default 100
8053 * @cfg {Boolean} maskBody
8057 getAutoCreate : function(){
8061 method : this.method || 'POST',
8062 id : this.id || Roo.id(),
8065 if (this.parent().xtype.match(/^Nav/)) {
8066 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8070 if (this.labelAlign == 'left' ) {
8071 cfg.cls += ' form-horizontal';
8077 initEvents : function()
8079 this.el.on('submit', this.onSubmit, this);
8080 // this was added as random key presses on the form where triggering form submit.
8081 this.el.on('keypress', function(e) {
8082 if (e.getCharCode() != 13) {
8085 // we might need to allow it for textareas.. and some other items.
8086 // check e.getTarget().
8088 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8092 Roo.log("keypress blocked");
8100 onSubmit : function(e){
8105 * Returns true if client-side validation on the form is successful.
8108 isValid : function(){
8109 var items = this.getItems();
8113 items.each(function(f){
8119 Roo.log('invalid field: ' + f.name);
8123 if(!target && f.el.isVisible(true)){
8129 if(this.errorMask && !valid){
8130 Roo.bootstrap.Form.popover.mask(this, target);
8137 * Returns true if any fields in this form have changed since their original load.
8140 isDirty : function(){
8142 var items = this.getItems();
8143 items.each(function(f){
8153 * Performs a predefined action (submit or load) or custom actions you define on this form.
8154 * @param {String} actionName The name of the action type
8155 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8156 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8157 * accept other config options):
8159 Property Type Description
8160 ---------------- --------------- ----------------------------------------------------------------------------------
8161 url String The url for the action (defaults to the form's url)
8162 method String The form method to use (defaults to the form's method, or POST if not defined)
8163 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8164 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8165 validate the form on the client (defaults to false)
8167 * @return {BasicForm} this
8169 doAction : function(action, options){
8170 if(typeof action == 'string'){
8171 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8173 if(this.fireEvent('beforeaction', this, action) !== false){
8174 this.beforeAction(action);
8175 action.run.defer(100, action);
8181 beforeAction : function(action){
8182 var o = action.options;
8187 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8189 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8192 // not really supported yet.. ??
8194 //if(this.waitMsgTarget === true){
8195 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8196 //}else if(this.waitMsgTarget){
8197 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8198 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8200 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8206 afterAction : function(action, success){
8207 this.activeAction = null;
8208 var o = action.options;
8213 Roo.get(document.body).unmask();
8219 //if(this.waitMsgTarget === true){
8220 // this.el.unmask();
8221 //}else if(this.waitMsgTarget){
8222 // this.waitMsgTarget.unmask();
8224 // Roo.MessageBox.updateProgress(1);
8225 // Roo.MessageBox.hide();
8232 Roo.callback(o.success, o.scope, [this, action]);
8233 this.fireEvent('actioncomplete', this, action);
8237 // failure condition..
8238 // we have a scenario where updates need confirming.
8239 // eg. if a locking scenario exists..
8240 // we look for { errors : { needs_confirm : true }} in the response.
8242 (typeof(action.result) != 'undefined') &&
8243 (typeof(action.result.errors) != 'undefined') &&
8244 (typeof(action.result.errors.needs_confirm) != 'undefined')
8247 Roo.log("not supported yet");
8250 Roo.MessageBox.confirm(
8251 "Change requires confirmation",
8252 action.result.errorMsg,
8257 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8267 Roo.callback(o.failure, o.scope, [this, action]);
8268 // show an error message if no failed handler is set..
8269 if (!this.hasListener('actionfailed')) {
8270 Roo.log("need to add dialog support");
8272 Roo.MessageBox.alert("Error",
8273 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8274 action.result.errorMsg :
8275 "Saving Failed, please check your entries or try again"
8280 this.fireEvent('actionfailed', this, action);
8285 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8286 * @param {String} id The value to search for
8289 findField : function(id){
8290 var items = this.getItems();
8291 var field = items.get(id);
8293 items.each(function(f){
8294 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8301 return field || null;
8304 * Mark fields in this form invalid in bulk.
8305 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8306 * @return {BasicForm} this
8308 markInvalid : function(errors){
8309 if(errors instanceof Array){
8310 for(var i = 0, len = errors.length; i < len; i++){
8311 var fieldError = errors[i];
8312 var f = this.findField(fieldError.id);
8314 f.markInvalid(fieldError.msg);
8320 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8321 field.markInvalid(errors[id]);
8325 //Roo.each(this.childForms || [], function (f) {
8326 // f.markInvalid(errors);
8333 * Set values for fields in this form in bulk.
8334 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8335 * @return {BasicForm} this
8337 setValues : function(values){
8338 if(values instanceof Array){ // array of objects
8339 for(var i = 0, len = values.length; i < len; i++){
8341 var f = this.findField(v.id);
8343 f.setValue(v.value);
8344 if(this.trackResetOnLoad){
8345 f.originalValue = f.getValue();
8349 }else{ // object hash
8352 if(typeof values[id] != 'function' && (field = this.findField(id))){
8354 if (field.setFromData &&
8356 field.displayField &&
8357 // combos' with local stores can
8358 // be queried via setValue()
8359 // to set their value..
8360 (field.store && !field.store.isLocal)
8364 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8365 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8366 field.setFromData(sd);
8368 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8370 field.setFromData(values);
8373 field.setValue(values[id]);
8377 if(this.trackResetOnLoad){
8378 field.originalValue = field.getValue();
8384 //Roo.each(this.childForms || [], function (f) {
8385 // f.setValues(values);
8392 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8393 * they are returned as an array.
8394 * @param {Boolean} asString
8397 getValues : function(asString){
8398 //if (this.childForms) {
8399 // copy values from the child forms
8400 // Roo.each(this.childForms, function (f) {
8401 // this.setValues(f.getValues());
8407 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8408 if(asString === true){
8411 return Roo.urlDecode(fs);
8415 * Returns the fields in this form as an object with key/value pairs.
8416 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8419 getFieldValues : function(with_hidden)
8421 var items = this.getItems();
8423 items.each(function(f){
8429 var v = f.getValue();
8431 if (f.inputType =='radio') {
8432 if (typeof(ret[f.getName()]) == 'undefined') {
8433 ret[f.getName()] = ''; // empty..
8436 if (!f.el.dom.checked) {
8444 if(f.xtype == 'MoneyField'){
8445 ret[f.currencyName] = f.getCurrency();
8448 // not sure if this supported any more..
8449 if ((typeof(v) == 'object') && f.getRawValue) {
8450 v = f.getRawValue() ; // dates..
8452 // combo boxes where name != hiddenName...
8453 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8454 ret[f.name] = f.getRawValue();
8456 ret[f.getName()] = v;
8463 * Clears all invalid messages in this form.
8464 * @return {BasicForm} this
8466 clearInvalid : function(){
8467 var items = this.getItems();
8469 items.each(function(f){
8478 * @return {BasicForm} this
8481 var items = this.getItems();
8482 items.each(function(f){
8486 Roo.each(this.childForms || [], function (f) {
8494 getItems : function()
8496 var r=new Roo.util.MixedCollection(false, function(o){
8497 return o.id || (o.id = Roo.id());
8499 var iter = function(el) {
8506 Roo.each(el.items,function(e) {
8515 hideFields : function(items)
8517 Roo.each(items, function(i){
8519 var f = this.findField(i);
8530 showFields : function(items)
8532 Roo.each(items, function(i){
8534 var f = this.findField(i);
8547 Roo.apply(Roo.bootstrap.Form, {
8574 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8575 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8576 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8577 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8580 this.maskEl.top.enableDisplayMode("block");
8581 this.maskEl.left.enableDisplayMode("block");
8582 this.maskEl.bottom.enableDisplayMode("block");
8583 this.maskEl.right.enableDisplayMode("block");
8585 this.toolTip = new Roo.bootstrap.Tooltip({
8586 cls : 'roo-form-error-popover',
8588 'left' : ['r-l', [-2,0], 'right'],
8589 'right' : ['l-r', [2,0], 'left'],
8590 'bottom' : ['tl-bl', [0,2], 'top'],
8591 'top' : [ 'bl-tl', [0,-2], 'bottom']
8595 this.toolTip.render(Roo.get(document.body));
8597 this.toolTip.el.enableDisplayMode("block");
8599 Roo.get(document.body).on('click', function(){
8603 Roo.get(document.body).on('touchstart', function(){
8607 this.isApplied = true
8610 mask : function(form, target)
8614 this.target = target;
8616 if(!this.form.errorMask || !target.el){
8620 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8622 Roo.log(scrollable);
8624 var ot = this.target.el.calcOffsetsTo(scrollable);
8626 var scrollTo = ot[1] - this.form.maskOffset;
8628 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8630 scrollable.scrollTo('top', scrollTo);
8632 var box = this.target.el.getBox();
8634 var zIndex = Roo.bootstrap.Modal.zIndex++;
8637 this.maskEl.top.setStyle('position', 'absolute');
8638 this.maskEl.top.setStyle('z-index', zIndex);
8639 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8640 this.maskEl.top.setLeft(0);
8641 this.maskEl.top.setTop(0);
8642 this.maskEl.top.show();
8644 this.maskEl.left.setStyle('position', 'absolute');
8645 this.maskEl.left.setStyle('z-index', zIndex);
8646 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8647 this.maskEl.left.setLeft(0);
8648 this.maskEl.left.setTop(box.y - this.padding);
8649 this.maskEl.left.show();
8651 this.maskEl.bottom.setStyle('position', 'absolute');
8652 this.maskEl.bottom.setStyle('z-index', zIndex);
8653 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8654 this.maskEl.bottom.setLeft(0);
8655 this.maskEl.bottom.setTop(box.bottom + this.padding);
8656 this.maskEl.bottom.show();
8658 this.maskEl.right.setStyle('position', 'absolute');
8659 this.maskEl.right.setStyle('z-index', zIndex);
8660 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8661 this.maskEl.right.setLeft(box.right + this.padding);
8662 this.maskEl.right.setTop(box.y - this.padding);
8663 this.maskEl.right.show();
8665 this.toolTip.bindEl = this.target.el;
8667 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8669 var tip = this.target.blankText;
8671 if(this.target.getValue() !== '' ) {
8673 if (this.target.invalidText.length) {
8674 tip = this.target.invalidText;
8675 } else if (this.target.regexText.length){
8676 tip = this.target.regexText;
8680 this.toolTip.show(tip);
8682 this.intervalID = window.setInterval(function() {
8683 Roo.bootstrap.Form.popover.unmask();
8686 window.onwheel = function(){ return false;};
8688 (function(){ this.isMasked = true; }).defer(500, this);
8694 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8698 this.maskEl.top.setStyle('position', 'absolute');
8699 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8700 this.maskEl.top.hide();
8702 this.maskEl.left.setStyle('position', 'absolute');
8703 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8704 this.maskEl.left.hide();
8706 this.maskEl.bottom.setStyle('position', 'absolute');
8707 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8708 this.maskEl.bottom.hide();
8710 this.maskEl.right.setStyle('position', 'absolute');
8711 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8712 this.maskEl.right.hide();
8714 this.toolTip.hide();
8716 this.toolTip.el.hide();
8718 window.onwheel = function(){ return true;};
8720 if(this.intervalID){
8721 window.clearInterval(this.intervalID);
8722 this.intervalID = false;
8725 this.isMasked = false;
8735 * Ext JS Library 1.1.1
8736 * Copyright(c) 2006-2007, Ext JS, LLC.
8738 * Originally Released Under LGPL - original licence link has changed is not relivant.
8741 * <script type="text/javascript">
8744 * @class Roo.form.VTypes
8745 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8748 Roo.form.VTypes = function(){
8749 // closure these in so they are only created once.
8750 var alpha = /^[a-zA-Z_]+$/;
8751 var alphanum = /^[a-zA-Z0-9_]+$/;
8752 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8753 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8755 // All these messages and functions are configurable
8758 * The function used to validate email addresses
8759 * @param {String} value The email address
8761 'email' : function(v){
8762 return email.test(v);
8765 * The error text to display when the email validation function returns false
8768 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8770 * The keystroke filter mask to be applied on email input
8773 'emailMask' : /[a-z0-9_\.\-@]/i,
8776 * The function used to validate URLs
8777 * @param {String} value The URL
8779 'url' : function(v){
8783 * The error text to display when the url validation function returns false
8786 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8789 * The function used to validate alpha values
8790 * @param {String} value The value
8792 'alpha' : function(v){
8793 return alpha.test(v);
8796 * The error text to display when the alpha validation function returns false
8799 'alphaText' : 'This field should only contain letters and _',
8801 * The keystroke filter mask to be applied on alpha input
8804 'alphaMask' : /[a-z_]/i,
8807 * The function used to validate alphanumeric values
8808 * @param {String} value The value
8810 'alphanum' : function(v){
8811 return alphanum.test(v);
8814 * The error text to display when the alphanumeric validation function returns false
8817 'alphanumText' : 'This field should only contain letters, numbers and _',
8819 * The keystroke filter mask to be applied on alphanumeric input
8822 'alphanumMask' : /[a-z0-9_]/i
8832 * @class Roo.bootstrap.Input
8833 * @extends Roo.bootstrap.Component
8834 * Bootstrap Input class
8835 * @cfg {Boolean} disabled is it disabled
8836 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8837 * @cfg {String} name name of the input
8838 * @cfg {string} fieldLabel - the label associated
8839 * @cfg {string} placeholder - placeholder to put in text.
8840 * @cfg {string} before - input group add on before
8841 * @cfg {string} after - input group add on after
8842 * @cfg {string} size - (lg|sm) or leave empty..
8843 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8844 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8845 * @cfg {Number} md colspan out of 12 for computer-sized screens
8846 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8847 * @cfg {string} value default value of the input
8848 * @cfg {Number} labelWidth set the width of label
8849 * @cfg {Number} labellg set the width of label (1-12)
8850 * @cfg {Number} labelmd set the width of label (1-12)
8851 * @cfg {Number} labelsm set the width of label (1-12)
8852 * @cfg {Number} labelxs set the width of label (1-12)
8853 * @cfg {String} labelAlign (top|left)
8854 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8855 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8856 * @cfg {String} indicatorpos (left|right) default left
8857 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8858 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8860 * @cfg {String} align (left|center|right) Default left
8861 * @cfg {Boolean} forceFeedback (true|false) Default false
8864 * Create a new Input
8865 * @param {Object} config The config object
8868 Roo.bootstrap.Input = function(config){
8870 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8875 * Fires when this field receives input focus.
8876 * @param {Roo.form.Field} this
8881 * Fires when this field loses input focus.
8882 * @param {Roo.form.Field} this
8887 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8888 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8889 * @param {Roo.form.Field} this
8890 * @param {Roo.EventObject} e The event object
8895 * Fires just before the field blurs if the field value has changed.
8896 * @param {Roo.form.Field} this
8897 * @param {Mixed} newValue The new value
8898 * @param {Mixed} oldValue The original value
8903 * Fires after the field has been marked as invalid.
8904 * @param {Roo.form.Field} this
8905 * @param {String} msg The validation message
8910 * Fires after the field has been validated with no errors.
8911 * @param {Roo.form.Field} this
8916 * Fires after the key up
8917 * @param {Roo.form.Field} this
8918 * @param {Roo.EventObject} e The event Object
8924 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8926 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8927 automatic validation (defaults to "keyup").
8929 validationEvent : "keyup",
8931 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8933 validateOnBlur : true,
8935 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8937 validationDelay : 250,
8939 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8941 focusClass : "x-form-focus", // not needed???
8945 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8947 invalidClass : "has-warning",
8950 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8952 validClass : "has-success",
8955 * @cfg {Boolean} hasFeedback (true|false) default true
8960 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8962 invalidFeedbackClass : "glyphicon-warning-sign",
8965 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8967 validFeedbackClass : "glyphicon-ok",
8970 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8972 selectOnFocus : false,
8975 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8979 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8984 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8986 disableKeyFilter : false,
8989 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8993 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8997 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8999 blankText : "Please complete this mandatory field",
9002 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9006 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9008 maxLength : Number.MAX_VALUE,
9010 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9012 minLengthText : "The minimum length for this field is {0}",
9014 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9016 maxLengthText : "The maximum length for this field is {0}",
9020 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9021 * If available, this function will be called only after the basic validators all return true, and will be passed the
9022 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9026 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9027 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9028 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
9032 * @cfg {String} regexText -- Depricated - use Invalid Text
9037 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9043 autocomplete: false,
9062 formatedValue : false,
9063 forceFeedback : false,
9065 indicatorpos : 'left',
9075 parentLabelAlign : function()
9078 while (parent.parent()) {
9079 parent = parent.parent();
9080 if (typeof(parent.labelAlign) !='undefined') {
9081 return parent.labelAlign;
9088 getAutoCreate : function()
9090 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9096 if(this.inputType != 'hidden'){
9097 cfg.cls = 'form-group' //input-group
9103 type : this.inputType,
9105 cls : 'form-control',
9106 placeholder : this.placeholder || '',
9107 autocomplete : this.autocomplete || 'new-password'
9110 if(this.capture.length){
9111 input.capture = this.capture;
9114 if(this.accept.length){
9115 input.accept = this.accept + "/*";
9119 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9122 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9123 input.maxLength = this.maxLength;
9126 if (this.disabled) {
9127 input.disabled=true;
9130 if (this.readOnly) {
9131 input.readonly=true;
9135 input.name = this.name;
9139 input.cls += ' input-' + this.size;
9143 ['xs','sm','md','lg'].map(function(size){
9144 if (settings[size]) {
9145 cfg.cls += ' col-' + size + '-' + settings[size];
9149 var inputblock = input;
9153 cls: 'glyphicon form-control-feedback'
9156 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9159 cls : 'has-feedback',
9167 if (this.before || this.after) {
9170 cls : 'input-group',
9174 if (this.before && typeof(this.before) == 'string') {
9176 inputblock.cn.push({
9178 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9182 if (this.before && typeof(this.before) == 'object') {
9183 this.before = Roo.factory(this.before);
9185 inputblock.cn.push({
9187 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9188 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9192 inputblock.cn.push(input);
9194 if (this.after && typeof(this.after) == 'string') {
9195 inputblock.cn.push({
9197 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9201 if (this.after && typeof(this.after) == 'object') {
9202 this.after = Roo.factory(this.after);
9204 inputblock.cn.push({
9206 cls : 'roo-input-after input-group-append input-group-text input-group-' +
9207 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9211 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9212 inputblock.cls += ' has-feedback';
9213 inputblock.cn.push(feedback);
9218 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9219 tooltip : 'This field is required'
9221 if (Roo.bootstrap.version == 4) {
9224 style : 'display-none'
9227 if (align ==='left' && this.fieldLabel.length) {
9229 cfg.cls += ' roo-form-group-label-left row';
9236 cls : 'control-label col-form-label',
9237 html : this.fieldLabel
9248 var labelCfg = cfg.cn[1];
9249 var contentCfg = cfg.cn[2];
9251 if(this.indicatorpos == 'right'){
9256 cls : 'control-label col-form-label',
9260 html : this.fieldLabel
9274 labelCfg = cfg.cn[0];
9275 contentCfg = cfg.cn[1];
9279 if(this.labelWidth > 12){
9280 labelCfg.style = "width: " + this.labelWidth + 'px';
9283 if(this.labelWidth < 13 && this.labelmd == 0){
9284 this.labelmd = this.labelWidth;
9287 if(this.labellg > 0){
9288 labelCfg.cls += ' col-lg-' + this.labellg;
9289 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9292 if(this.labelmd > 0){
9293 labelCfg.cls += ' col-md-' + this.labelmd;
9294 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9297 if(this.labelsm > 0){
9298 labelCfg.cls += ' col-sm-' + this.labelsm;
9299 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9302 if(this.labelxs > 0){
9303 labelCfg.cls += ' col-xs-' + this.labelxs;
9304 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9308 } else if ( this.fieldLabel.length) {
9313 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9314 tooltip : 'This field is required'
9318 //cls : 'input-group-addon',
9319 html : this.fieldLabel
9327 if(this.indicatorpos == 'right'){
9332 //cls : 'input-group-addon',
9333 html : this.fieldLabel
9338 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9339 tooltip : 'This field is required'
9359 if (this.parentType === 'Navbar' && this.parent().bar) {
9360 cfg.cls += ' navbar-form';
9363 if (this.parentType === 'NavGroup') {
9364 cfg.cls += ' navbar-form';
9372 * return the real input element.
9374 inputEl: function ()
9376 return this.el.select('input.form-control',true).first();
9379 tooltipEl : function()
9381 return this.inputEl();
9384 indicatorEl : function()
9386 if (Roo.bootstrap.version == 4) {
9387 return false; // not enabled in v4 yet.
9390 var indicator = this.el.select('i.roo-required-indicator',true).first();
9400 setDisabled : function(v)
9402 var i = this.inputEl().dom;
9404 i.removeAttribute('disabled');
9408 i.setAttribute('disabled','true');
9410 initEvents : function()
9413 this.inputEl().on("keydown" , this.fireKey, this);
9414 this.inputEl().on("focus", this.onFocus, this);
9415 this.inputEl().on("blur", this.onBlur, this);
9417 this.inputEl().relayEvent('keyup', this);
9419 this.indicator = this.indicatorEl();
9422 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9425 // reference to original value for reset
9426 this.originalValue = this.getValue();
9427 //Roo.form.TextField.superclass.initEvents.call(this);
9428 if(this.validationEvent == 'keyup'){
9429 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9430 this.inputEl().on('keyup', this.filterValidation, this);
9432 else if(this.validationEvent !== false){
9433 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9436 if(this.selectOnFocus){
9437 this.on("focus", this.preFocus, this);
9440 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9441 this.inputEl().on("keypress", this.filterKeys, this);
9443 this.inputEl().relayEvent('keypress', this);
9446 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9447 this.el.on("click", this.autoSize, this);
9450 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9451 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9454 if (typeof(this.before) == 'object') {
9455 this.before.render(this.el.select('.roo-input-before',true).first());
9457 if (typeof(this.after) == 'object') {
9458 this.after.render(this.el.select('.roo-input-after',true).first());
9461 this.inputEl().on('change', this.onChange, this);
9464 filterValidation : function(e){
9465 if(!e.isNavKeyPress()){
9466 this.validationTask.delay(this.validationDelay);
9470 * Validates the field value
9471 * @return {Boolean} True if the value is valid, else false
9473 validate : function(){
9474 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9475 if(this.disabled || this.validateValue(this.getRawValue())){
9486 * Validates a value according to the field's validation rules and marks the field as invalid
9487 * if the validation fails
9488 * @param {Mixed} value The value to validate
9489 * @return {Boolean} True if the value is valid, else false
9491 validateValue : function(value)
9493 if(this.getVisibilityEl().hasClass('hidden')){
9497 if(value.length < 1) { // if it's blank
9498 if(this.allowBlank){
9504 if(value.length < this.minLength){
9507 if(value.length > this.maxLength){
9511 var vt = Roo.form.VTypes;
9512 if(!vt[this.vtype](value, this)){
9516 if(typeof this.validator == "function"){
9517 var msg = this.validator(value);
9521 if (typeof(msg) == 'string') {
9522 this.invalidText = msg;
9526 if(this.regex && !this.regex.test(value)){
9534 fireKey : function(e){
9535 //Roo.log('field ' + e.getKey());
9536 if(e.isNavKeyPress()){
9537 this.fireEvent("specialkey", this, e);
9540 focus : function (selectText){
9542 this.inputEl().focus();
9543 if(selectText === true){
9544 this.inputEl().dom.select();
9550 onFocus : function(){
9551 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9552 // this.el.addClass(this.focusClass);
9555 this.hasFocus = true;
9556 this.startValue = this.getValue();
9557 this.fireEvent("focus", this);
9561 beforeBlur : Roo.emptyFn,
9565 onBlur : function(){
9567 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9568 //this.el.removeClass(this.focusClass);
9570 this.hasFocus = false;
9571 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9574 var v = this.getValue();
9575 if(String(v) !== String(this.startValue)){
9576 this.fireEvent('change', this, v, this.startValue);
9578 this.fireEvent("blur", this);
9581 onChange : function(e)
9583 var v = this.getValue();
9584 if(String(v) !== String(this.startValue)){
9585 this.fireEvent('change', this, v, this.startValue);
9591 * Resets the current field value to the originally loaded value and clears any validation messages
9594 this.setValue(this.originalValue);
9598 * Returns the name of the field
9599 * @return {Mixed} name The name field
9601 getName: function(){
9605 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9606 * @return {Mixed} value The field value
9608 getValue : function(){
9610 var v = this.inputEl().getValue();
9615 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9616 * @return {Mixed} value The field value
9618 getRawValue : function(){
9619 var v = this.inputEl().getValue();
9625 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9626 * @param {Mixed} value The value to set
9628 setRawValue : function(v){
9629 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9632 selectText : function(start, end){
9633 var v = this.getRawValue();
9635 start = start === undefined ? 0 : start;
9636 end = end === undefined ? v.length : end;
9637 var d = this.inputEl().dom;
9638 if(d.setSelectionRange){
9639 d.setSelectionRange(start, end);
9640 }else if(d.createTextRange){
9641 var range = d.createTextRange();
9642 range.moveStart("character", start);
9643 range.moveEnd("character", v.length-end);
9650 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9651 * @param {Mixed} value The value to set
9653 setValue : function(v){
9656 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9662 processValue : function(value){
9663 if(this.stripCharsRe){
9664 var newValue = value.replace(this.stripCharsRe, '');
9665 if(newValue !== value){
9666 this.setRawValue(newValue);
9673 preFocus : function(){
9675 if(this.selectOnFocus){
9676 this.inputEl().dom.select();
9679 filterKeys : function(e){
9681 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9684 var c = e.getCharCode(), cc = String.fromCharCode(c);
9685 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9688 if(!this.maskRe.test(cc)){
9693 * Clear any invalid styles/messages for this field
9695 clearInvalid : function(){
9697 if(!this.el || this.preventMark){ // not rendered
9702 this.el.removeClass(this.invalidClass);
9704 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9706 var feedback = this.el.select('.form-control-feedback', true).first();
9709 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9715 this.indicator.removeClass('visible');
9716 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9719 this.fireEvent('valid', this);
9723 * Mark this field as valid
9725 markValid : function()
9727 if(!this.el || this.preventMark){ // not rendered...
9731 this.el.removeClass([this.invalidClass, this.validClass]);
9733 var feedback = this.el.select('.form-control-feedback', true).first();
9736 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9740 this.indicator.removeClass('visible');
9741 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9748 if(this.allowBlank && !this.getRawValue().length){
9752 this.el.addClass(this.validClass);
9754 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9756 var feedback = this.el.select('.form-control-feedback', true).first();
9759 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9760 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9765 this.fireEvent('valid', this);
9769 * Mark this field as invalid
9770 * @param {String} msg The validation message
9772 markInvalid : function(msg)
9774 if(!this.el || this.preventMark){ // not rendered
9778 this.el.removeClass([this.invalidClass, this.validClass]);
9780 var feedback = this.el.select('.form-control-feedback', true).first();
9783 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9790 if(this.allowBlank && !this.getRawValue().length){
9795 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9796 this.indicator.addClass('visible');
9799 this.el.addClass(this.invalidClass);
9801 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9803 var feedback = this.el.select('.form-control-feedback', true).first();
9806 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9808 if(this.getValue().length || this.forceFeedback){
9809 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9816 this.fireEvent('invalid', this, msg);
9819 SafariOnKeyDown : function(event)
9821 // this is a workaround for a password hang bug on chrome/ webkit.
9822 if (this.inputEl().dom.type != 'password') {
9826 var isSelectAll = false;
9828 if(this.inputEl().dom.selectionEnd > 0){
9829 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9831 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9832 event.preventDefault();
9837 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9839 event.preventDefault();
9840 // this is very hacky as keydown always get's upper case.
9842 var cc = String.fromCharCode(event.getCharCode());
9843 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9847 adjustWidth : function(tag, w){
9848 tag = tag.toLowerCase();
9849 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9850 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9854 if(tag == 'textarea'){
9857 }else if(Roo.isOpera){
9861 if(tag == 'textarea'){
9869 setFieldLabel : function(v)
9875 if(this.indicatorEl()){
9876 var ar = this.el.select('label > span',true);
9878 if (ar.elements.length) {
9879 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9880 this.fieldLabel = v;
9884 var br = this.el.select('label',true);
9886 if(br.elements.length) {
9887 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9888 this.fieldLabel = v;
9892 Roo.log('Cannot Found any of label > span || label in input');
9896 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9897 this.fieldLabel = v;
9912 * @class Roo.bootstrap.TextArea
9913 * @extends Roo.bootstrap.Input
9914 * Bootstrap TextArea class
9915 * @cfg {Number} cols Specifies the visible width of a text area
9916 * @cfg {Number} rows Specifies the visible number of lines in a text area
9917 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9918 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9919 * @cfg {string} html text
9922 * Create a new TextArea
9923 * @param {Object} config The config object
9926 Roo.bootstrap.TextArea = function(config){
9927 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9931 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9941 getAutoCreate : function(){
9943 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9949 if(this.inputType != 'hidden'){
9950 cfg.cls = 'form-group' //input-group
9958 value : this.value || '',
9959 html: this.html || '',
9960 cls : 'form-control',
9961 placeholder : this.placeholder || ''
9965 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9966 input.maxLength = this.maxLength;
9970 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9974 input.cols = this.cols;
9977 if (this.readOnly) {
9978 input.readonly = true;
9982 input.name = this.name;
9986 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9990 ['xs','sm','md','lg'].map(function(size){
9991 if (settings[size]) {
9992 cfg.cls += ' col-' + size + '-' + settings[size];
9996 var inputblock = input;
9998 if(this.hasFeedback && !this.allowBlank){
10002 cls: 'glyphicon form-control-feedback'
10006 cls : 'has-feedback',
10015 if (this.before || this.after) {
10018 cls : 'input-group',
10022 inputblock.cn.push({
10024 cls : 'input-group-addon',
10029 inputblock.cn.push(input);
10031 if(this.hasFeedback && !this.allowBlank){
10032 inputblock.cls += ' has-feedback';
10033 inputblock.cn.push(feedback);
10037 inputblock.cn.push({
10039 cls : 'input-group-addon',
10046 if (align ==='left' && this.fieldLabel.length) {
10051 cls : 'control-label',
10052 html : this.fieldLabel
10063 if(this.labelWidth > 12){
10064 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10067 if(this.labelWidth < 13 && this.labelmd == 0){
10068 this.labelmd = this.labelWidth;
10071 if(this.labellg > 0){
10072 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10073 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10076 if(this.labelmd > 0){
10077 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10078 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10081 if(this.labelsm > 0){
10082 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10083 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10086 if(this.labelxs > 0){
10087 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10088 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10091 } else if ( this.fieldLabel.length) {
10096 //cls : 'input-group-addon',
10097 html : this.fieldLabel
10115 if (this.disabled) {
10116 input.disabled=true;
10123 * return the real textarea element.
10125 inputEl: function ()
10127 return this.el.select('textarea.form-control',true).first();
10131 * Clear any invalid styles/messages for this field
10133 clearInvalid : function()
10136 if(!this.el || this.preventMark){ // not rendered
10140 var label = this.el.select('label', true).first();
10141 var icon = this.el.select('i.fa-star', true).first();
10147 this.el.removeClass(this.invalidClass);
10149 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10151 var feedback = this.el.select('.form-control-feedback', true).first();
10154 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10159 this.fireEvent('valid', this);
10163 * Mark this field as valid
10165 markValid : function()
10167 if(!this.el || this.preventMark){ // not rendered
10171 this.el.removeClass([this.invalidClass, this.validClass]);
10173 var feedback = this.el.select('.form-control-feedback', true).first();
10176 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10179 if(this.disabled || this.allowBlank){
10183 var label = this.el.select('label', true).first();
10184 var icon = this.el.select('i.fa-star', true).first();
10190 this.el.addClass(this.validClass);
10192 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10194 var feedback = this.el.select('.form-control-feedback', true).first();
10197 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10198 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10203 this.fireEvent('valid', this);
10207 * Mark this field as invalid
10208 * @param {String} msg The validation message
10210 markInvalid : function(msg)
10212 if(!this.el || this.preventMark){ // not rendered
10216 this.el.removeClass([this.invalidClass, this.validClass]);
10218 var feedback = this.el.select('.form-control-feedback', true).first();
10221 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10224 if(this.disabled || this.allowBlank){
10228 var label = this.el.select('label', true).first();
10229 var icon = this.el.select('i.fa-star', true).first();
10231 if(!this.getValue().length && label && !icon){
10232 this.el.createChild({
10234 cls : 'text-danger fa fa-lg fa-star',
10235 tooltip : 'This field is required',
10236 style : 'margin-right:5px;'
10240 this.el.addClass(this.invalidClass);
10242 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10244 var feedback = this.el.select('.form-control-feedback', true).first();
10247 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10249 if(this.getValue().length || this.forceFeedback){
10250 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10257 this.fireEvent('invalid', this, msg);
10265 * trigger field - base class for combo..
10270 * @class Roo.bootstrap.TriggerField
10271 * @extends Roo.bootstrap.Input
10272 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10273 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10274 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10275 * for which you can provide a custom implementation. For example:
10277 var trigger = new Roo.bootstrap.TriggerField();
10278 trigger.onTriggerClick = myTriggerFn;
10279 trigger.applyTo('my-field');
10282 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10283 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10284 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10285 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10286 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10289 * Create a new TriggerField.
10290 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10291 * to the base TextField)
10293 Roo.bootstrap.TriggerField = function(config){
10294 this.mimicing = false;
10295 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10298 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10300 * @cfg {String} triggerClass A CSS class to apply to the trigger
10303 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10308 * @cfg {Boolean} removable (true|false) special filter default false
10312 /** @cfg {Boolean} grow @hide */
10313 /** @cfg {Number} growMin @hide */
10314 /** @cfg {Number} growMax @hide */
10320 autoSize: Roo.emptyFn,
10324 deferHeight : true,
10327 actionMode : 'wrap',
10332 getAutoCreate : function(){
10334 var align = this.labelAlign || this.parentLabelAlign();
10339 cls: 'form-group' //input-group
10346 type : this.inputType,
10347 cls : 'form-control',
10348 autocomplete: 'new-password',
10349 placeholder : this.placeholder || ''
10353 input.name = this.name;
10356 input.cls += ' input-' + this.size;
10359 if (this.disabled) {
10360 input.disabled=true;
10363 var inputblock = input;
10365 if(this.hasFeedback && !this.allowBlank){
10369 cls: 'glyphicon form-control-feedback'
10372 if(this.removable && !this.editable && !this.tickable){
10374 cls : 'has-feedback',
10380 cls : 'roo-combo-removable-btn close'
10387 cls : 'has-feedback',
10396 if(this.removable && !this.editable && !this.tickable){
10398 cls : 'roo-removable',
10404 cls : 'roo-combo-removable-btn close'
10411 if (this.before || this.after) {
10414 cls : 'input-group',
10418 inputblock.cn.push({
10420 cls : 'input-group-addon input-group-prepend input-group-text',
10425 inputblock.cn.push(input);
10427 if(this.hasFeedback && !this.allowBlank){
10428 inputblock.cls += ' has-feedback';
10429 inputblock.cn.push(feedback);
10433 inputblock.cn.push({
10435 cls : 'input-group-addon input-group-append input-group-text',
10444 var ibwrap = inputblock;
10449 cls: 'roo-select2-choices',
10453 cls: 'roo-select2-search-field',
10465 cls: 'roo-select2-container input-group',
10470 cls: 'form-hidden-field'
10476 if(!this.multiple && this.showToggleBtn){
10482 if (this.caret != false) {
10485 cls: 'fa fa-' + this.caret
10492 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10497 cls: 'combobox-clear',
10511 combobox.cls += ' roo-select2-container-multi';
10515 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10516 tooltip : 'This field is required'
10518 if (Roo.bootstrap.version == 4) {
10521 style : 'display:none'
10526 if (align ==='left' && this.fieldLabel.length) {
10528 cfg.cls += ' roo-form-group-label-left row';
10535 cls : 'control-label',
10536 html : this.fieldLabel
10548 var labelCfg = cfg.cn[1];
10549 var contentCfg = cfg.cn[2];
10551 if(this.indicatorpos == 'right'){
10556 cls : 'control-label',
10560 html : this.fieldLabel
10574 labelCfg = cfg.cn[0];
10575 contentCfg = cfg.cn[1];
10578 if(this.labelWidth > 12){
10579 labelCfg.style = "width: " + this.labelWidth + 'px';
10582 if(this.labelWidth < 13 && this.labelmd == 0){
10583 this.labelmd = this.labelWidth;
10586 if(this.labellg > 0){
10587 labelCfg.cls += ' col-lg-' + this.labellg;
10588 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10591 if(this.labelmd > 0){
10592 labelCfg.cls += ' col-md-' + this.labelmd;
10593 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10596 if(this.labelsm > 0){
10597 labelCfg.cls += ' col-sm-' + this.labelsm;
10598 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10601 if(this.labelxs > 0){
10602 labelCfg.cls += ' col-xs-' + this.labelxs;
10603 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10606 } else if ( this.fieldLabel.length) {
10607 // Roo.log(" label");
10612 //cls : 'input-group-addon',
10613 html : this.fieldLabel
10621 if(this.indicatorpos == 'right'){
10629 html : this.fieldLabel
10643 // Roo.log(" no label && no align");
10650 ['xs','sm','md','lg'].map(function(size){
10651 if (settings[size]) {
10652 cfg.cls += ' col-' + size + '-' + settings[size];
10663 onResize : function(w, h){
10664 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10665 // if(typeof w == 'number'){
10666 // var x = w - this.trigger.getWidth();
10667 // this.inputEl().setWidth(this.adjustWidth('input', x));
10668 // this.trigger.setStyle('left', x+'px');
10673 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10676 getResizeEl : function(){
10677 return this.inputEl();
10681 getPositionEl : function(){
10682 return this.inputEl();
10686 alignErrorIcon : function(){
10687 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10691 initEvents : function(){
10695 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10696 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10697 if(!this.multiple && this.showToggleBtn){
10698 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10699 if(this.hideTrigger){
10700 this.trigger.setDisplayed(false);
10702 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10706 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10709 if(this.removable && !this.editable && !this.tickable){
10710 var close = this.closeTriggerEl();
10713 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10714 close.on('click', this.removeBtnClick, this, close);
10718 //this.trigger.addClassOnOver('x-form-trigger-over');
10719 //this.trigger.addClassOnClick('x-form-trigger-click');
10722 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10726 closeTriggerEl : function()
10728 var close = this.el.select('.roo-combo-removable-btn', true).first();
10729 return close ? close : false;
10732 removeBtnClick : function(e, h, el)
10734 e.preventDefault();
10736 if(this.fireEvent("remove", this) !== false){
10738 this.fireEvent("afterremove", this)
10742 createList : function()
10744 this.list = Roo.get(document.body).createChild({
10745 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10746 cls: 'typeahead typeahead-long dropdown-menu',
10747 style: 'display:none'
10750 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10755 initTrigger : function(){
10760 onDestroy : function(){
10762 this.trigger.removeAllListeners();
10763 // this.trigger.remove();
10766 // this.wrap.remove();
10768 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10772 onFocus : function(){
10773 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10775 if(!this.mimicing){
10776 this.wrap.addClass('x-trigger-wrap-focus');
10777 this.mimicing = true;
10778 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10779 if(this.monitorTab){
10780 this.el.on("keydown", this.checkTab, this);
10787 checkTab : function(e){
10788 if(e.getKey() == e.TAB){
10789 this.triggerBlur();
10794 onBlur : function(){
10799 mimicBlur : function(e, t){
10801 if(!this.wrap.contains(t) && this.validateBlur()){
10802 this.triggerBlur();
10808 triggerBlur : function(){
10809 this.mimicing = false;
10810 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10811 if(this.monitorTab){
10812 this.el.un("keydown", this.checkTab, this);
10814 //this.wrap.removeClass('x-trigger-wrap-focus');
10815 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10819 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10820 validateBlur : function(e, t){
10825 onDisable : function(){
10826 this.inputEl().dom.disabled = true;
10827 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10829 // this.wrap.addClass('x-item-disabled');
10834 onEnable : function(){
10835 this.inputEl().dom.disabled = false;
10836 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10838 // this.el.removeClass('x-item-disabled');
10843 onShow : function(){
10844 var ae = this.getActionEl();
10847 ae.dom.style.display = '';
10848 ae.dom.style.visibility = 'visible';
10854 onHide : function(){
10855 var ae = this.getActionEl();
10856 ae.dom.style.display = 'none';
10860 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10861 * by an implementing function.
10863 * @param {EventObject} e
10865 onTriggerClick : Roo.emptyFn
10869 * Ext JS Library 1.1.1
10870 * Copyright(c) 2006-2007, Ext JS, LLC.
10872 * Originally Released Under LGPL - original licence link has changed is not relivant.
10875 * <script type="text/javascript">
10880 * @class Roo.data.SortTypes
10882 * Defines the default sorting (casting?) comparison functions used when sorting data.
10884 Roo.data.SortTypes = {
10886 * Default sort that does nothing
10887 * @param {Mixed} s The value being converted
10888 * @return {Mixed} The comparison value
10890 none : function(s){
10895 * The regular expression used to strip tags
10899 stripTagsRE : /<\/?[^>]+>/gi,
10902 * Strips all HTML tags to sort on text only
10903 * @param {Mixed} s The value being converted
10904 * @return {String} The comparison value
10906 asText : function(s){
10907 return String(s).replace(this.stripTagsRE, "");
10911 * Strips all HTML tags to sort on text only - Case insensitive
10912 * @param {Mixed} s The value being converted
10913 * @return {String} The comparison value
10915 asUCText : function(s){
10916 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10920 * Case insensitive string
10921 * @param {Mixed} s The value being converted
10922 * @return {String} The comparison value
10924 asUCString : function(s) {
10925 return String(s).toUpperCase();
10930 * @param {Mixed} s The value being converted
10931 * @return {Number} The comparison value
10933 asDate : function(s) {
10937 if(s instanceof Date){
10938 return s.getTime();
10940 return Date.parse(String(s));
10945 * @param {Mixed} s The value being converted
10946 * @return {Float} The comparison value
10948 asFloat : function(s) {
10949 var val = parseFloat(String(s).replace(/,/g, ""));
10958 * @param {Mixed} s The value being converted
10959 * @return {Number} The comparison value
10961 asInt : function(s) {
10962 var val = parseInt(String(s).replace(/,/g, ""));
10970 * Ext JS Library 1.1.1
10971 * Copyright(c) 2006-2007, Ext JS, LLC.
10973 * Originally Released Under LGPL - original licence link has changed is not relivant.
10976 * <script type="text/javascript">
10980 * @class Roo.data.Record
10981 * Instances of this class encapsulate both record <em>definition</em> information, and record
10982 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10983 * to access Records cached in an {@link Roo.data.Store} object.<br>
10985 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10986 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10989 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10991 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10992 * {@link #create}. The parameters are the same.
10993 * @param {Array} data An associative Array of data values keyed by the field name.
10994 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10995 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10996 * not specified an integer id is generated.
10998 Roo.data.Record = function(data, id){
10999 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11004 * Generate a constructor for a specific record layout.
11005 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11006 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11007 * Each field definition object may contain the following properties: <ul>
11008 * <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,
11009 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11010 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11011 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11012 * is being used, then this is a string containing the javascript expression to reference the data relative to
11013 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11014 * to the data item relative to the record element. If the mapping expression is the same as the field name,
11015 * this may be omitted.</p></li>
11016 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11017 * <ul><li>auto (Default, implies no conversion)</li>
11022 * <li>date</li></ul></p></li>
11023 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11024 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11025 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11026 * by the Reader into an object that will be stored in the Record. It is passed the
11027 * following parameters:<ul>
11028 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11030 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11032 * <br>usage:<br><pre><code>
11033 var TopicRecord = Roo.data.Record.create(
11034 {name: 'title', mapping: 'topic_title'},
11035 {name: 'author', mapping: 'username'},
11036 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11037 {name: 'lastPost', mapping: 'post_time', type: 'date'},
11038 {name: 'lastPoster', mapping: 'user2'},
11039 {name: 'excerpt', mapping: 'post_text'}
11042 var myNewRecord = new TopicRecord({
11043 title: 'Do my job please',
11046 lastPost: new Date(),
11047 lastPoster: 'Animal',
11048 excerpt: 'No way dude!'
11050 myStore.add(myNewRecord);
11055 Roo.data.Record.create = function(o){
11056 var f = function(){
11057 f.superclass.constructor.apply(this, arguments);
11059 Roo.extend(f, Roo.data.Record);
11060 var p = f.prototype;
11061 p.fields = new Roo.util.MixedCollection(false, function(field){
11064 for(var i = 0, len = o.length; i < len; i++){
11065 p.fields.add(new Roo.data.Field(o[i]));
11067 f.getField = function(name){
11068 return p.fields.get(name);
11073 Roo.data.Record.AUTO_ID = 1000;
11074 Roo.data.Record.EDIT = 'edit';
11075 Roo.data.Record.REJECT = 'reject';
11076 Roo.data.Record.COMMIT = 'commit';
11078 Roo.data.Record.prototype = {
11080 * Readonly flag - true if this record has been modified.
11089 join : function(store){
11090 this.store = store;
11094 * Set the named field to the specified value.
11095 * @param {String} name The name of the field to set.
11096 * @param {Object} value The value to set the field to.
11098 set : function(name, value){
11099 if(this.data[name] == value){
11103 if(!this.modified){
11104 this.modified = {};
11106 if(typeof this.modified[name] == 'undefined'){
11107 this.modified[name] = this.data[name];
11109 this.data[name] = value;
11110 if(!this.editing && this.store){
11111 this.store.afterEdit(this);
11116 * Get the value of the named field.
11117 * @param {String} name The name of the field to get the value of.
11118 * @return {Object} The value of the field.
11120 get : function(name){
11121 return this.data[name];
11125 beginEdit : function(){
11126 this.editing = true;
11127 this.modified = {};
11131 cancelEdit : function(){
11132 this.editing = false;
11133 delete this.modified;
11137 endEdit : function(){
11138 this.editing = false;
11139 if(this.dirty && this.store){
11140 this.store.afterEdit(this);
11145 * Usually called by the {@link Roo.data.Store} which owns the Record.
11146 * Rejects all changes made to the Record since either creation, or the last commit operation.
11147 * Modified fields are reverted to their original values.
11149 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11150 * of reject operations.
11152 reject : function(){
11153 var m = this.modified;
11155 if(typeof m[n] != "function"){
11156 this.data[n] = m[n];
11159 this.dirty = false;
11160 delete this.modified;
11161 this.editing = false;
11163 this.store.afterReject(this);
11168 * Usually called by the {@link Roo.data.Store} which owns the Record.
11169 * Commits all changes made to the Record since either creation, or the last commit operation.
11171 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11172 * of commit operations.
11174 commit : function(){
11175 this.dirty = false;
11176 delete this.modified;
11177 this.editing = false;
11179 this.store.afterCommit(this);
11184 hasError : function(){
11185 return this.error != null;
11189 clearError : function(){
11194 * Creates a copy of this record.
11195 * @param {String} id (optional) A new record id if you don't want to use this record's id
11198 copy : function(newId) {
11199 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11203 * Ext JS Library 1.1.1
11204 * Copyright(c) 2006-2007, Ext JS, LLC.
11206 * Originally Released Under LGPL - original licence link has changed is not relivant.
11209 * <script type="text/javascript">
11215 * @class Roo.data.Store
11216 * @extends Roo.util.Observable
11217 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11218 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11220 * 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
11221 * has no knowledge of the format of the data returned by the Proxy.<br>
11223 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11224 * instances from the data object. These records are cached and made available through accessor functions.
11226 * Creates a new Store.
11227 * @param {Object} config A config object containing the objects needed for the Store to access data,
11228 * and read the data into Records.
11230 Roo.data.Store = function(config){
11231 this.data = new Roo.util.MixedCollection(false);
11232 this.data.getKey = function(o){
11235 this.baseParams = {};
11237 this.paramNames = {
11242 "multisort" : "_multisort"
11245 if(config && config.data){
11246 this.inlineData = config.data;
11247 delete config.data;
11250 Roo.apply(this, config);
11252 if(this.reader){ // reader passed
11253 this.reader = Roo.factory(this.reader, Roo.data);
11254 this.reader.xmodule = this.xmodule || false;
11255 if(!this.recordType){
11256 this.recordType = this.reader.recordType;
11258 if(this.reader.onMetaChange){
11259 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11263 if(this.recordType){
11264 this.fields = this.recordType.prototype.fields;
11266 this.modified = [];
11270 * @event datachanged
11271 * Fires when the data cache has changed, and a widget which is using this Store
11272 * as a Record cache should refresh its view.
11273 * @param {Store} this
11275 datachanged : true,
11277 * @event metachange
11278 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11279 * @param {Store} this
11280 * @param {Object} meta The JSON metadata
11285 * Fires when Records have been added to the Store
11286 * @param {Store} this
11287 * @param {Roo.data.Record[]} records The array of Records added
11288 * @param {Number} index The index at which the record(s) were added
11293 * Fires when a Record has been removed from the Store
11294 * @param {Store} this
11295 * @param {Roo.data.Record} record The Record that was removed
11296 * @param {Number} index The index at which the record was removed
11301 * Fires when a Record has been updated
11302 * @param {Store} this
11303 * @param {Roo.data.Record} record The Record that was updated
11304 * @param {String} operation The update operation being performed. Value may be one of:
11306 Roo.data.Record.EDIT
11307 Roo.data.Record.REJECT
11308 Roo.data.Record.COMMIT
11314 * Fires when the data cache has been cleared.
11315 * @param {Store} this
11319 * @event beforeload
11320 * Fires before a request is made for a new data object. If the beforeload handler returns false
11321 * the load action will be canceled.
11322 * @param {Store} this
11323 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11327 * @event beforeloadadd
11328 * Fires after a new set of Records has been loaded.
11329 * @param {Store} this
11330 * @param {Roo.data.Record[]} records The Records that were loaded
11331 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11333 beforeloadadd : true,
11336 * Fires after a new set of Records has been loaded, before they are added to the store.
11337 * @param {Store} this
11338 * @param {Roo.data.Record[]} records The Records that were loaded
11339 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11340 * @params {Object} return from reader
11344 * @event loadexception
11345 * Fires if an exception occurs in the Proxy during loading.
11346 * Called with the signature of the Proxy's "loadexception" event.
11347 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11350 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11351 * @param {Object} load options
11352 * @param {Object} jsonData from your request (normally this contains the Exception)
11354 loadexception : true
11358 this.proxy = Roo.factory(this.proxy, Roo.data);
11359 this.proxy.xmodule = this.xmodule || false;
11360 this.relayEvents(this.proxy, ["loadexception"]);
11362 this.sortToggle = {};
11363 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11365 Roo.data.Store.superclass.constructor.call(this);
11367 if(this.inlineData){
11368 this.loadData(this.inlineData);
11369 delete this.inlineData;
11373 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11375 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11376 * without a remote query - used by combo/forms at present.
11380 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11383 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11386 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11387 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11390 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11391 * on any HTTP request
11394 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11397 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11401 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11402 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11404 remoteSort : false,
11407 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11408 * loaded or when a record is removed. (defaults to false).
11410 pruneModifiedRecords : false,
11413 lastOptions : null,
11416 * Add Records to the Store and fires the add event.
11417 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11419 add : function(records){
11420 records = [].concat(records);
11421 for(var i = 0, len = records.length; i < len; i++){
11422 records[i].join(this);
11424 var index = this.data.length;
11425 this.data.addAll(records);
11426 this.fireEvent("add", this, records, index);
11430 * Remove a Record from the Store and fires the remove event.
11431 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11433 remove : function(record){
11434 var index = this.data.indexOf(record);
11435 this.data.removeAt(index);
11437 if(this.pruneModifiedRecords){
11438 this.modified.remove(record);
11440 this.fireEvent("remove", this, record, index);
11444 * Remove all Records from the Store and fires the clear event.
11446 removeAll : function(){
11448 if(this.pruneModifiedRecords){
11449 this.modified = [];
11451 this.fireEvent("clear", this);
11455 * Inserts Records to the Store at the given index and fires the add event.
11456 * @param {Number} index The start index at which to insert the passed Records.
11457 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11459 insert : function(index, records){
11460 records = [].concat(records);
11461 for(var i = 0, len = records.length; i < len; i++){
11462 this.data.insert(index, records[i]);
11463 records[i].join(this);
11465 this.fireEvent("add", this, records, index);
11469 * Get the index within the cache of the passed Record.
11470 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11471 * @return {Number} The index of the passed Record. Returns -1 if not found.
11473 indexOf : function(record){
11474 return this.data.indexOf(record);
11478 * Get the index within the cache of the Record with the passed id.
11479 * @param {String} id The id of the Record to find.
11480 * @return {Number} The index of the Record. Returns -1 if not found.
11482 indexOfId : function(id){
11483 return this.data.indexOfKey(id);
11487 * Get the Record with the specified id.
11488 * @param {String} id The id of the Record to find.
11489 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11491 getById : function(id){
11492 return this.data.key(id);
11496 * Get the Record at the specified index.
11497 * @param {Number} index The index of the Record to find.
11498 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11500 getAt : function(index){
11501 return this.data.itemAt(index);
11505 * Returns a range of Records between specified indices.
11506 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11507 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11508 * @return {Roo.data.Record[]} An array of Records
11510 getRange : function(start, end){
11511 return this.data.getRange(start, end);
11515 storeOptions : function(o){
11516 o = Roo.apply({}, o);
11519 this.lastOptions = o;
11523 * Loads the Record cache from the configured Proxy using the configured Reader.
11525 * If using remote paging, then the first load call must specify the <em>start</em>
11526 * and <em>limit</em> properties in the options.params property to establish the initial
11527 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11529 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11530 * and this call will return before the new data has been loaded. Perform any post-processing
11531 * in a callback function, or in a "load" event handler.</strong>
11533 * @param {Object} options An object containing properties which control loading options:<ul>
11534 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11535 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11536 * passed the following arguments:<ul>
11537 * <li>r : Roo.data.Record[]</li>
11538 * <li>options: Options object from the load call</li>
11539 * <li>success: Boolean success indicator</li></ul></li>
11540 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11541 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11544 load : function(options){
11545 options = options || {};
11546 if(this.fireEvent("beforeload", this, options) !== false){
11547 this.storeOptions(options);
11548 var p = Roo.apply(options.params || {}, this.baseParams);
11549 // if meta was not loaded from remote source.. try requesting it.
11550 if (!this.reader.metaFromRemote) {
11551 p._requestMeta = 1;
11553 if(this.sortInfo && this.remoteSort){
11554 var pn = this.paramNames;
11555 p[pn["sort"]] = this.sortInfo.field;
11556 p[pn["dir"]] = this.sortInfo.direction;
11558 if (this.multiSort) {
11559 var pn = this.paramNames;
11560 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11563 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11568 * Reloads the Record cache from the configured Proxy using the configured Reader and
11569 * the options from the last load operation performed.
11570 * @param {Object} options (optional) An object containing properties which may override the options
11571 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11572 * the most recently used options are reused).
11574 reload : function(options){
11575 this.load(Roo.applyIf(options||{}, this.lastOptions));
11579 // Called as a callback by the Reader during a load operation.
11580 loadRecords : function(o, options, success){
11581 if(!o || success === false){
11582 if(success !== false){
11583 this.fireEvent("load", this, [], options, o);
11585 if(options.callback){
11586 options.callback.call(options.scope || this, [], options, false);
11590 // if data returned failure - throw an exception.
11591 if (o.success === false) {
11592 // show a message if no listener is registered.
11593 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11594 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11596 // loadmask wil be hooked into this..
11597 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11600 var r = o.records, t = o.totalRecords || r.length;
11602 this.fireEvent("beforeloadadd", this, r, options, o);
11604 if(!options || options.add !== true){
11605 if(this.pruneModifiedRecords){
11606 this.modified = [];
11608 for(var i = 0, len = r.length; i < len; i++){
11612 this.data = this.snapshot;
11613 delete this.snapshot;
11616 this.data.addAll(r);
11617 this.totalLength = t;
11619 this.fireEvent("datachanged", this);
11621 this.totalLength = Math.max(t, this.data.length+r.length);
11625 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11627 var e = new Roo.data.Record({});
11629 e.set(this.parent.displayField, this.parent.emptyTitle);
11630 e.set(this.parent.valueField, '');
11635 this.fireEvent("load", this, r, options, o);
11636 if(options.callback){
11637 options.callback.call(options.scope || this, r, options, true);
11643 * Loads data from a passed data block. A Reader which understands the format of the data
11644 * must have been configured in the constructor.
11645 * @param {Object} data The data block from which to read the Records. The format of the data expected
11646 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11647 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11649 loadData : function(o, append){
11650 var r = this.reader.readRecords(o);
11651 this.loadRecords(r, {add: append}, true);
11655 * Gets the number of cached records.
11657 * <em>If using paging, this may not be the total size of the dataset. If the data object
11658 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11659 * the data set size</em>
11661 getCount : function(){
11662 return this.data.length || 0;
11666 * Gets the total number of records in the dataset as returned by the server.
11668 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11669 * the dataset size</em>
11671 getTotalCount : function(){
11672 return this.totalLength || 0;
11676 * Returns the sort state of the Store as an object with two properties:
11678 field {String} The name of the field by which the Records are sorted
11679 direction {String} The sort order, "ASC" or "DESC"
11682 getSortState : function(){
11683 return this.sortInfo;
11687 applySort : function(){
11688 if(this.sortInfo && !this.remoteSort){
11689 var s = this.sortInfo, f = s.field;
11690 var st = this.fields.get(f).sortType;
11691 var fn = function(r1, r2){
11692 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11693 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11695 this.data.sort(s.direction, fn);
11696 if(this.snapshot && this.snapshot != this.data){
11697 this.snapshot.sort(s.direction, fn);
11703 * Sets the default sort column and order to be used by the next load operation.
11704 * @param {String} fieldName The name of the field to sort by.
11705 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11707 setDefaultSort : function(field, dir){
11708 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11712 * Sort the Records.
11713 * If remote sorting is used, the sort is performed on the server, and the cache is
11714 * reloaded. If local sorting is used, the cache is sorted internally.
11715 * @param {String} fieldName The name of the field to sort by.
11716 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11718 sort : function(fieldName, dir){
11719 var f = this.fields.get(fieldName);
11721 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11723 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11724 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11729 this.sortToggle[f.name] = dir;
11730 this.sortInfo = {field: f.name, direction: dir};
11731 if(!this.remoteSort){
11733 this.fireEvent("datachanged", this);
11735 this.load(this.lastOptions);
11740 * Calls the specified function for each of the Records in the cache.
11741 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11742 * Returning <em>false</em> aborts and exits the iteration.
11743 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11745 each : function(fn, scope){
11746 this.data.each(fn, scope);
11750 * Gets all records modified since the last commit. Modified records are persisted across load operations
11751 * (e.g., during paging).
11752 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11754 getModifiedRecords : function(){
11755 return this.modified;
11759 createFilterFn : function(property, value, anyMatch){
11760 if(!value.exec){ // not a regex
11761 value = String(value);
11762 if(value.length == 0){
11765 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11767 return function(r){
11768 return value.test(r.data[property]);
11773 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11774 * @param {String} property A field on your records
11775 * @param {Number} start The record index to start at (defaults to 0)
11776 * @param {Number} end The last record index to include (defaults to length - 1)
11777 * @return {Number} The sum
11779 sum : function(property, start, end){
11780 var rs = this.data.items, v = 0;
11781 start = start || 0;
11782 end = (end || end === 0) ? end : rs.length-1;
11784 for(var i = start; i <= end; i++){
11785 v += (rs[i].data[property] || 0);
11791 * Filter the records by a specified property.
11792 * @param {String} field A field on your records
11793 * @param {String/RegExp} value Either a string that the field
11794 * should start with or a RegExp to test against the field
11795 * @param {Boolean} anyMatch True to match any part not just the beginning
11797 filter : function(property, value, anyMatch){
11798 var fn = this.createFilterFn(property, value, anyMatch);
11799 return fn ? this.filterBy(fn) : this.clearFilter();
11803 * Filter by a function. The specified function will be called with each
11804 * record in this data source. If the function returns true the record is included,
11805 * otherwise it is filtered.
11806 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11807 * @param {Object} scope (optional) The scope of the function (defaults to this)
11809 filterBy : function(fn, scope){
11810 this.snapshot = this.snapshot || this.data;
11811 this.data = this.queryBy(fn, scope||this);
11812 this.fireEvent("datachanged", this);
11816 * Query the records by a specified property.
11817 * @param {String} field A field on your records
11818 * @param {String/RegExp} value Either a string that the field
11819 * should start with or a RegExp to test against the field
11820 * @param {Boolean} anyMatch True to match any part not just the beginning
11821 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11823 query : function(property, value, anyMatch){
11824 var fn = this.createFilterFn(property, value, anyMatch);
11825 return fn ? this.queryBy(fn) : this.data.clone();
11829 * Query by a function. The specified function will be called with each
11830 * record in this data source. If the function returns true the record is included
11832 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11833 * @param {Object} scope (optional) The scope of the function (defaults to this)
11834 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11836 queryBy : function(fn, scope){
11837 var data = this.snapshot || this.data;
11838 return data.filterBy(fn, scope||this);
11842 * Collects unique values for a particular dataIndex from this store.
11843 * @param {String} dataIndex The property to collect
11844 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11845 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11846 * @return {Array} An array of the unique values
11848 collect : function(dataIndex, allowNull, bypassFilter){
11849 var d = (bypassFilter === true && this.snapshot) ?
11850 this.snapshot.items : this.data.items;
11851 var v, sv, r = [], l = {};
11852 for(var i = 0, len = d.length; i < len; i++){
11853 v = d[i].data[dataIndex];
11855 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11864 * Revert to a view of the Record cache with no filtering applied.
11865 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11867 clearFilter : function(suppressEvent){
11868 if(this.snapshot && this.snapshot != this.data){
11869 this.data = this.snapshot;
11870 delete this.snapshot;
11871 if(suppressEvent !== true){
11872 this.fireEvent("datachanged", this);
11878 afterEdit : function(record){
11879 if(this.modified.indexOf(record) == -1){
11880 this.modified.push(record);
11882 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11886 afterReject : function(record){
11887 this.modified.remove(record);
11888 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11892 afterCommit : function(record){
11893 this.modified.remove(record);
11894 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11898 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11899 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11901 commitChanges : function(){
11902 var m = this.modified.slice(0);
11903 this.modified = [];
11904 for(var i = 0, len = m.length; i < len; i++){
11910 * Cancel outstanding changes on all changed records.
11912 rejectChanges : function(){
11913 var m = this.modified.slice(0);
11914 this.modified = [];
11915 for(var i = 0, len = m.length; i < len; i++){
11920 onMetaChange : function(meta, rtype, o){
11921 this.recordType = rtype;
11922 this.fields = rtype.prototype.fields;
11923 delete this.snapshot;
11924 this.sortInfo = meta.sortInfo || this.sortInfo;
11925 this.modified = [];
11926 this.fireEvent('metachange', this, this.reader.meta);
11929 moveIndex : function(data, type)
11931 var index = this.indexOf(data);
11933 var newIndex = index + type;
11937 this.insert(newIndex, data);
11942 * Ext JS Library 1.1.1
11943 * Copyright(c) 2006-2007, Ext JS, LLC.
11945 * Originally Released Under LGPL - original licence link has changed is not relivant.
11948 * <script type="text/javascript">
11952 * @class Roo.data.SimpleStore
11953 * @extends Roo.data.Store
11954 * Small helper class to make creating Stores from Array data easier.
11955 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11956 * @cfg {Array} fields An array of field definition objects, or field name strings.
11957 * @cfg {Array} data The multi-dimensional array of data
11959 * @param {Object} config
11961 Roo.data.SimpleStore = function(config){
11962 Roo.data.SimpleStore.superclass.constructor.call(this, {
11964 reader: new Roo.data.ArrayReader({
11967 Roo.data.Record.create(config.fields)
11969 proxy : new Roo.data.MemoryProxy(config.data)
11973 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11975 * Ext JS Library 1.1.1
11976 * Copyright(c) 2006-2007, Ext JS, LLC.
11978 * Originally Released Under LGPL - original licence link has changed is not relivant.
11981 * <script type="text/javascript">
11986 * @extends Roo.data.Store
11987 * @class Roo.data.JsonStore
11988 * Small helper class to make creating Stores for JSON data easier. <br/>
11990 var store = new Roo.data.JsonStore({
11991 url: 'get-images.php',
11993 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11996 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11997 * JsonReader and HttpProxy (unless inline data is provided).</b>
11998 * @cfg {Array} fields An array of field definition objects, or field name strings.
12000 * @param {Object} config
12002 Roo.data.JsonStore = function(c){
12003 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12004 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12005 reader: new Roo.data.JsonReader(c, c.fields)
12008 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12010 * Ext JS Library 1.1.1
12011 * Copyright(c) 2006-2007, Ext JS, LLC.
12013 * Originally Released Under LGPL - original licence link has changed is not relivant.
12016 * <script type="text/javascript">
12020 Roo.data.Field = function(config){
12021 if(typeof config == "string"){
12022 config = {name: config};
12024 Roo.apply(this, config);
12027 this.type = "auto";
12030 var st = Roo.data.SortTypes;
12031 // named sortTypes are supported, here we look them up
12032 if(typeof this.sortType == "string"){
12033 this.sortType = st[this.sortType];
12036 // set default sortType for strings and dates
12037 if(!this.sortType){
12040 this.sortType = st.asUCString;
12043 this.sortType = st.asDate;
12046 this.sortType = st.none;
12051 var stripRe = /[\$,%]/g;
12053 // prebuilt conversion function for this field, instead of
12054 // switching every time we're reading a value
12056 var cv, dateFormat = this.dateFormat;
12061 cv = function(v){ return v; };
12064 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12068 return v !== undefined && v !== null && v !== '' ?
12069 parseInt(String(v).replace(stripRe, ""), 10) : '';
12074 return v !== undefined && v !== null && v !== '' ?
12075 parseFloat(String(v).replace(stripRe, ""), 10) : '';
12080 cv = function(v){ return v === true || v === "true" || v == 1; };
12087 if(v instanceof Date){
12091 if(dateFormat == "timestamp"){
12092 return new Date(v*1000);
12094 return Date.parseDate(v, dateFormat);
12096 var parsed = Date.parse(v);
12097 return parsed ? new Date(parsed) : null;
12106 Roo.data.Field.prototype = {
12114 * Ext JS Library 1.1.1
12115 * Copyright(c) 2006-2007, Ext JS, LLC.
12117 * Originally Released Under LGPL - original licence link has changed is not relivant.
12120 * <script type="text/javascript">
12123 // Base class for reading structured data from a data source. This class is intended to be
12124 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12127 * @class Roo.data.DataReader
12128 * Base class for reading structured data from a data source. This class is intended to be
12129 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12132 Roo.data.DataReader = function(meta, recordType){
12136 this.recordType = recordType instanceof Array ?
12137 Roo.data.Record.create(recordType) : recordType;
12140 Roo.data.DataReader.prototype = {
12142 * Create an empty record
12143 * @param {Object} data (optional) - overlay some values
12144 * @return {Roo.data.Record} record created.
12146 newRow : function(d) {
12148 this.recordType.prototype.fields.each(function(c) {
12150 case 'int' : da[c.name] = 0; break;
12151 case 'date' : da[c.name] = new Date(); break;
12152 case 'float' : da[c.name] = 0.0; break;
12153 case 'boolean' : da[c.name] = false; break;
12154 default : da[c.name] = ""; break;
12158 return new this.recordType(Roo.apply(da, d));
12163 * Ext JS Library 1.1.1
12164 * Copyright(c) 2006-2007, Ext JS, LLC.
12166 * Originally Released Under LGPL - original licence link has changed is not relivant.
12169 * <script type="text/javascript">
12173 * @class Roo.data.DataProxy
12174 * @extends Roo.data.Observable
12175 * This class is an abstract base class for implementations which provide retrieval of
12176 * unformatted data objects.<br>
12178 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12179 * (of the appropriate type which knows how to parse the data object) to provide a block of
12180 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12182 * Custom implementations must implement the load method as described in
12183 * {@link Roo.data.HttpProxy#load}.
12185 Roo.data.DataProxy = function(){
12188 * @event beforeload
12189 * Fires before a network request is made to retrieve a data object.
12190 * @param {Object} This DataProxy object.
12191 * @param {Object} params The params parameter to the load function.
12196 * Fires before the load method's callback is called.
12197 * @param {Object} This DataProxy object.
12198 * @param {Object} o The data object.
12199 * @param {Object} arg The callback argument object passed to the load function.
12203 * @event loadexception
12204 * Fires if an Exception occurs during data retrieval.
12205 * @param {Object} This DataProxy object.
12206 * @param {Object} o The data object.
12207 * @param {Object} arg The callback argument object passed to the load function.
12208 * @param {Object} e The Exception.
12210 loadexception : true
12212 Roo.data.DataProxy.superclass.constructor.call(this);
12215 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12218 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12222 * Ext JS Library 1.1.1
12223 * Copyright(c) 2006-2007, Ext JS, LLC.
12225 * Originally Released Under LGPL - original licence link has changed is not relivant.
12228 * <script type="text/javascript">
12231 * @class Roo.data.MemoryProxy
12232 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12233 * to the Reader when its load method is called.
12235 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12237 Roo.data.MemoryProxy = function(data){
12241 Roo.data.MemoryProxy.superclass.constructor.call(this);
12245 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12248 * Load data from the requested source (in this case an in-memory
12249 * data object passed to the constructor), read the data object into
12250 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12251 * process that block using the passed callback.
12252 * @param {Object} params This parameter is not used by the MemoryProxy class.
12253 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12254 * object into a block of Roo.data.Records.
12255 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12256 * The function must be passed <ul>
12257 * <li>The Record block object</li>
12258 * <li>The "arg" argument from the load function</li>
12259 * <li>A boolean success indicator</li>
12261 * @param {Object} scope The scope in which to call the callback
12262 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12264 load : function(params, reader, callback, scope, arg){
12265 params = params || {};
12268 result = reader.readRecords(this.data);
12270 this.fireEvent("loadexception", this, arg, null, e);
12271 callback.call(scope, null, arg, false);
12274 callback.call(scope, result, arg, true);
12278 update : function(params, records){
12283 * Ext JS Library 1.1.1
12284 * Copyright(c) 2006-2007, Ext JS, LLC.
12286 * Originally Released Under LGPL - original licence link has changed is not relivant.
12289 * <script type="text/javascript">
12292 * @class Roo.data.HttpProxy
12293 * @extends Roo.data.DataProxy
12294 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12295 * configured to reference a certain URL.<br><br>
12297 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12298 * from which the running page was served.<br><br>
12300 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12302 * Be aware that to enable the browser to parse an XML document, the server must set
12303 * the Content-Type header in the HTTP response to "text/xml".
12305 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12306 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12307 * will be used to make the request.
12309 Roo.data.HttpProxy = function(conn){
12310 Roo.data.HttpProxy.superclass.constructor.call(this);
12311 // is conn a conn config or a real conn?
12313 this.useAjax = !conn || !conn.events;
12317 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12318 // thse are take from connection...
12321 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12324 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12325 * extra parameters to each request made by this object. (defaults to undefined)
12328 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12329 * to each request made by this object. (defaults to undefined)
12332 * @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)
12335 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12338 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12344 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12348 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12349 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12350 * a finer-grained basis than the DataProxy events.
12352 getConnection : function(){
12353 return this.useAjax ? Roo.Ajax : this.conn;
12357 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12358 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12359 * process that block using the passed callback.
12360 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12361 * for the request to the remote server.
12362 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12363 * object into a block of Roo.data.Records.
12364 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12365 * The function must be passed <ul>
12366 * <li>The Record block object</li>
12367 * <li>The "arg" argument from the load function</li>
12368 * <li>A boolean success indicator</li>
12370 * @param {Object} scope The scope in which to call the callback
12371 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12373 load : function(params, reader, callback, scope, arg){
12374 if(this.fireEvent("beforeload", this, params) !== false){
12376 params : params || {},
12378 callback : callback,
12383 callback : this.loadResponse,
12387 Roo.applyIf(o, this.conn);
12388 if(this.activeRequest){
12389 Roo.Ajax.abort(this.activeRequest);
12391 this.activeRequest = Roo.Ajax.request(o);
12393 this.conn.request(o);
12396 callback.call(scope||this, null, arg, false);
12401 loadResponse : function(o, success, response){
12402 delete this.activeRequest;
12404 this.fireEvent("loadexception", this, o, response);
12405 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12410 result = o.reader.read(response);
12412 this.fireEvent("loadexception", this, o, response, e);
12413 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12417 this.fireEvent("load", this, o, o.request.arg);
12418 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12422 update : function(dataSet){
12427 updateResponse : function(dataSet){
12432 * Ext JS Library 1.1.1
12433 * Copyright(c) 2006-2007, Ext JS, LLC.
12435 * Originally Released Under LGPL - original licence link has changed is not relivant.
12438 * <script type="text/javascript">
12442 * @class Roo.data.ScriptTagProxy
12443 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12444 * other than the originating domain of the running page.<br><br>
12446 * <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
12447 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12449 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12450 * source code that is used as the source inside a <script> tag.<br><br>
12452 * In order for the browser to process the returned data, the server must wrap the data object
12453 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12454 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12455 * depending on whether the callback name was passed:
12458 boolean scriptTag = false;
12459 String cb = request.getParameter("callback");
12462 response.setContentType("text/javascript");
12464 response.setContentType("application/x-json");
12466 Writer out = response.getWriter();
12468 out.write(cb + "(");
12470 out.print(dataBlock.toJsonString());
12477 * @param {Object} config A configuration object.
12479 Roo.data.ScriptTagProxy = function(config){
12480 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12481 Roo.apply(this, config);
12482 this.head = document.getElementsByTagName("head")[0];
12485 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12487 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12489 * @cfg {String} url The URL from which to request the data object.
12492 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12496 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12497 * the server the name of the callback function set up by the load call to process the returned data object.
12498 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12499 * javascript output which calls this named function passing the data object as its only parameter.
12501 callbackParam : "callback",
12503 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12504 * name to the request.
12509 * Load data from the configured URL, read the data object into
12510 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12511 * process that block using the passed callback.
12512 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12513 * for the request to the remote server.
12514 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12515 * object into a block of Roo.data.Records.
12516 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12517 * The function must be passed <ul>
12518 * <li>The Record block object</li>
12519 * <li>The "arg" argument from the load function</li>
12520 * <li>A boolean success indicator</li>
12522 * @param {Object} scope The scope in which to call the callback
12523 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12525 load : function(params, reader, callback, scope, arg){
12526 if(this.fireEvent("beforeload", this, params) !== false){
12528 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12530 var url = this.url;
12531 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12533 url += "&_dc=" + (new Date().getTime());
12535 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12538 cb : "stcCallback"+transId,
12539 scriptId : "stcScript"+transId,
12543 callback : callback,
12549 window[trans.cb] = function(o){
12550 conn.handleResponse(o, trans);
12553 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12555 if(this.autoAbort !== false){
12559 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12561 var script = document.createElement("script");
12562 script.setAttribute("src", url);
12563 script.setAttribute("type", "text/javascript");
12564 script.setAttribute("id", trans.scriptId);
12565 this.head.appendChild(script);
12567 this.trans = trans;
12569 callback.call(scope||this, null, arg, false);
12574 isLoading : function(){
12575 return this.trans ? true : false;
12579 * Abort the current server request.
12581 abort : function(){
12582 if(this.isLoading()){
12583 this.destroyTrans(this.trans);
12588 destroyTrans : function(trans, isLoaded){
12589 this.head.removeChild(document.getElementById(trans.scriptId));
12590 clearTimeout(trans.timeoutId);
12592 window[trans.cb] = undefined;
12594 delete window[trans.cb];
12597 // if hasn't been loaded, wait for load to remove it to prevent script error
12598 window[trans.cb] = function(){
12599 window[trans.cb] = undefined;
12601 delete window[trans.cb];
12608 handleResponse : function(o, trans){
12609 this.trans = false;
12610 this.destroyTrans(trans, true);
12613 result = trans.reader.readRecords(o);
12615 this.fireEvent("loadexception", this, o, trans.arg, e);
12616 trans.callback.call(trans.scope||window, null, trans.arg, false);
12619 this.fireEvent("load", this, o, trans.arg);
12620 trans.callback.call(trans.scope||window, result, trans.arg, true);
12624 handleFailure : function(trans){
12625 this.trans = false;
12626 this.destroyTrans(trans, false);
12627 this.fireEvent("loadexception", this, null, trans.arg);
12628 trans.callback.call(trans.scope||window, null, trans.arg, false);
12632 * Ext JS Library 1.1.1
12633 * Copyright(c) 2006-2007, Ext JS, LLC.
12635 * Originally Released Under LGPL - original licence link has changed is not relivant.
12638 * <script type="text/javascript">
12642 * @class Roo.data.JsonReader
12643 * @extends Roo.data.DataReader
12644 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12645 * based on mappings in a provided Roo.data.Record constructor.
12647 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12648 * in the reply previously.
12653 var RecordDef = Roo.data.Record.create([
12654 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12655 {name: 'occupation'} // This field will use "occupation" as the mapping.
12657 var myReader = new Roo.data.JsonReader({
12658 totalProperty: "results", // The property which contains the total dataset size (optional)
12659 root: "rows", // The property which contains an Array of row objects
12660 id: "id" // The property within each row object that provides an ID for the record (optional)
12664 * This would consume a JSON file like this:
12666 { 'results': 2, 'rows': [
12667 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12668 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12671 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12672 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12673 * paged from the remote server.
12674 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12675 * @cfg {String} root name of the property which contains the Array of row objects.
12676 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12677 * @cfg {Array} fields Array of field definition objects
12679 * Create a new JsonReader
12680 * @param {Object} meta Metadata configuration options
12681 * @param {Object} recordType Either an Array of field definition objects,
12682 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12684 Roo.data.JsonReader = function(meta, recordType){
12687 // set some defaults:
12688 Roo.applyIf(meta, {
12689 totalProperty: 'total',
12690 successProperty : 'success',
12695 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12697 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12700 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12701 * Used by Store query builder to append _requestMeta to params.
12704 metaFromRemote : false,
12706 * This method is only used by a DataProxy which has retrieved data from a remote server.
12707 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12708 * @return {Object} data A data block which is used by an Roo.data.Store object as
12709 * a cache of Roo.data.Records.
12711 read : function(response){
12712 var json = response.responseText;
12714 var o = /* eval:var:o */ eval("("+json+")");
12716 throw {message: "JsonReader.read: Json object not found"};
12722 this.metaFromRemote = true;
12723 this.meta = o.metaData;
12724 this.recordType = Roo.data.Record.create(o.metaData.fields);
12725 this.onMetaChange(this.meta, this.recordType, o);
12727 return this.readRecords(o);
12730 // private function a store will implement
12731 onMetaChange : function(meta, recordType, o){
12738 simpleAccess: function(obj, subsc) {
12745 getJsonAccessor: function(){
12747 return function(expr) {
12749 return(re.test(expr))
12750 ? new Function("obj", "return obj." + expr)
12755 return Roo.emptyFn;
12760 * Create a data block containing Roo.data.Records from an XML document.
12761 * @param {Object} o An object which contains an Array of row objects in the property specified
12762 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12763 * which contains the total size of the dataset.
12764 * @return {Object} data A data block which is used by an Roo.data.Store object as
12765 * a cache of Roo.data.Records.
12767 readRecords : function(o){
12769 * After any data loads, the raw JSON data is available for further custom processing.
12773 var s = this.meta, Record = this.recordType,
12774 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12776 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12778 if(s.totalProperty) {
12779 this.getTotal = this.getJsonAccessor(s.totalProperty);
12781 if(s.successProperty) {
12782 this.getSuccess = this.getJsonAccessor(s.successProperty);
12784 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12786 var g = this.getJsonAccessor(s.id);
12787 this.getId = function(rec) {
12789 return (r === undefined || r === "") ? null : r;
12792 this.getId = function(){return null;};
12795 for(var jj = 0; jj < fl; jj++){
12797 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12798 this.ef[jj] = this.getJsonAccessor(map);
12802 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12803 if(s.totalProperty){
12804 var vt = parseInt(this.getTotal(o), 10);
12809 if(s.successProperty){
12810 var vs = this.getSuccess(o);
12811 if(vs === false || vs === 'false'){
12816 for(var i = 0; i < c; i++){
12819 var id = this.getId(n);
12820 for(var j = 0; j < fl; j++){
12822 var v = this.ef[j](n);
12824 Roo.log('missing convert for ' + f.name);
12828 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12830 var record = new Record(values, id);
12832 records[i] = record;
12838 totalRecords : totalRecords
12843 * Ext JS Library 1.1.1
12844 * Copyright(c) 2006-2007, Ext JS, LLC.
12846 * Originally Released Under LGPL - original licence link has changed is not relivant.
12849 * <script type="text/javascript">
12853 * @class Roo.data.ArrayReader
12854 * @extends Roo.data.DataReader
12855 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12856 * Each element of that Array represents a row of data fields. The
12857 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12858 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12862 var RecordDef = Roo.data.Record.create([
12863 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12864 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12866 var myReader = new Roo.data.ArrayReader({
12867 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12871 * This would consume an Array like this:
12873 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12875 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12877 * Create a new JsonReader
12878 * @param {Object} meta Metadata configuration options.
12879 * @param {Object} recordType Either an Array of field definition objects
12880 * as specified to {@link Roo.data.Record#create},
12881 * or an {@link Roo.data.Record} object
12882 * created using {@link Roo.data.Record#create}.
12884 Roo.data.ArrayReader = function(meta, recordType){
12885 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12888 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12890 * Create a data block containing Roo.data.Records from an XML document.
12891 * @param {Object} o An Array of row objects which represents the dataset.
12892 * @return {Object} data A data block which is used by an Roo.data.Store object as
12893 * a cache of Roo.data.Records.
12895 readRecords : function(o){
12896 var sid = this.meta ? this.meta.id : null;
12897 var recordType = this.recordType, fields = recordType.prototype.fields;
12900 for(var i = 0; i < root.length; i++){
12903 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12904 for(var j = 0, jlen = fields.length; j < jlen; j++){
12905 var f = fields.items[j];
12906 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12907 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12909 values[f.name] = v;
12911 var record = new recordType(values, id);
12913 records[records.length] = record;
12917 totalRecords : records.length
12926 * @class Roo.bootstrap.ComboBox
12927 * @extends Roo.bootstrap.TriggerField
12928 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12929 * @cfg {Boolean} append (true|false) default false
12930 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12931 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12932 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12933 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12934 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12935 * @cfg {Boolean} animate default true
12936 * @cfg {Boolean} emptyResultText only for touch device
12937 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12938 * @cfg {String} emptyTitle default ''
12940 * Create a new ComboBox.
12941 * @param {Object} config Configuration options
12943 Roo.bootstrap.ComboBox = function(config){
12944 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12948 * Fires when the dropdown list is expanded
12949 * @param {Roo.bootstrap.ComboBox} combo This combo box
12954 * Fires when the dropdown list is collapsed
12955 * @param {Roo.bootstrap.ComboBox} combo This combo box
12959 * @event beforeselect
12960 * Fires before a list item is selected. Return false to cancel the selection.
12961 * @param {Roo.bootstrap.ComboBox} combo This combo box
12962 * @param {Roo.data.Record} record The data record returned from the underlying store
12963 * @param {Number} index The index of the selected item in the dropdown list
12965 'beforeselect' : true,
12968 * Fires when a list item is selected
12969 * @param {Roo.bootstrap.ComboBox} combo This combo box
12970 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12971 * @param {Number} index The index of the selected item in the dropdown list
12975 * @event beforequery
12976 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12977 * The event object passed has these properties:
12978 * @param {Roo.bootstrap.ComboBox} combo This combo box
12979 * @param {String} query The query
12980 * @param {Boolean} forceAll true to force "all" query
12981 * @param {Boolean} cancel true to cancel the query
12982 * @param {Object} e The query event object
12984 'beforequery': true,
12987 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12988 * @param {Roo.bootstrap.ComboBox} combo This combo box
12993 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12994 * @param {Roo.bootstrap.ComboBox} combo This combo box
12995 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13000 * Fires when the remove value from the combobox array
13001 * @param {Roo.bootstrap.ComboBox} combo This combo box
13005 * @event afterremove
13006 * Fires when the remove value from the combobox array
13007 * @param {Roo.bootstrap.ComboBox} combo This combo box
13009 'afterremove' : true,
13011 * @event specialfilter
13012 * Fires when specialfilter
13013 * @param {Roo.bootstrap.ComboBox} combo This combo box
13015 'specialfilter' : true,
13018 * Fires when tick the element
13019 * @param {Roo.bootstrap.ComboBox} combo This combo box
13023 * @event touchviewdisplay
13024 * Fires when touch view require special display (default is using displayField)
13025 * @param {Roo.bootstrap.ComboBox} combo This combo box
13026 * @param {Object} cfg set html .
13028 'touchviewdisplay' : true
13033 this.tickItems = [];
13035 this.selectedIndex = -1;
13036 if(this.mode == 'local'){
13037 if(config.queryDelay === undefined){
13038 this.queryDelay = 10;
13040 if(config.minChars === undefined){
13046 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13049 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13050 * rendering into an Roo.Editor, defaults to false)
13053 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13054 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13057 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13060 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13061 * the dropdown list (defaults to undefined, with no header element)
13065 * @cfg {String/Roo.Template} tpl The template to use to render the output
13069 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13071 listWidth: undefined,
13073 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13074 * mode = 'remote' or 'text' if mode = 'local')
13076 displayField: undefined,
13079 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13080 * mode = 'remote' or 'value' if mode = 'local').
13081 * Note: use of a valueField requires the user make a selection
13082 * in order for a value to be mapped.
13084 valueField: undefined,
13086 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13091 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13092 * field's data value (defaults to the underlying DOM element's name)
13094 hiddenName: undefined,
13096 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13100 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13102 selectedClass: 'active',
13105 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13109 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13110 * anchor positions (defaults to 'tl-bl')
13112 listAlign: 'tl-bl?',
13114 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13118 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13119 * query specified by the allQuery config option (defaults to 'query')
13121 triggerAction: 'query',
13123 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13124 * (defaults to 4, does not apply if editable = false)
13128 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13129 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13133 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13134 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13138 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13139 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13143 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13144 * when editable = true (defaults to false)
13146 selectOnFocus:false,
13148 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13150 queryParam: 'query',
13152 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13153 * when mode = 'remote' (defaults to 'Loading...')
13155 loadingText: 'Loading...',
13157 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13161 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13165 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13166 * traditional select (defaults to true)
13170 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13174 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13178 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13179 * listWidth has a higher value)
13183 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13184 * allow the user to set arbitrary text into the field (defaults to false)
13186 forceSelection:false,
13188 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13189 * if typeAhead = true (defaults to 250)
13191 typeAheadDelay : 250,
13193 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13194 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13196 valueNotFoundText : undefined,
13198 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13200 blockFocus : false,
13203 * @cfg {Boolean} disableClear Disable showing of clear button.
13205 disableClear : false,
13207 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13209 alwaysQuery : false,
13212 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13217 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13219 invalidClass : "has-warning",
13222 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13224 validClass : "has-success",
13227 * @cfg {Boolean} specialFilter (true|false) special filter default false
13229 specialFilter : false,
13232 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13234 mobileTouchView : true,
13237 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13239 useNativeIOS : false,
13242 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13244 mobile_restrict_height : false,
13246 ios_options : false,
13258 btnPosition : 'right',
13259 triggerList : true,
13260 showToggleBtn : true,
13262 emptyResultText: 'Empty',
13263 triggerText : 'Select',
13266 // element that contains real text value.. (when hidden is used..)
13268 getAutoCreate : function()
13273 * Render classic select for iso
13276 if(Roo.isIOS && this.useNativeIOS){
13277 cfg = this.getAutoCreateNativeIOS();
13285 if(Roo.isTouch && this.mobileTouchView){
13286 cfg = this.getAutoCreateTouchView();
13293 if(!this.tickable){
13294 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13299 * ComboBox with tickable selections
13302 var align = this.labelAlign || this.parentLabelAlign();
13305 cls : 'form-group roo-combobox-tickable' //input-group
13308 var btn_text_select = '';
13309 var btn_text_done = '';
13310 var btn_text_cancel = '';
13312 if (this.btn_text_show) {
13313 btn_text_select = 'Select';
13314 btn_text_done = 'Done';
13315 btn_text_cancel = 'Cancel';
13320 cls : 'tickable-buttons',
13325 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13326 //html : this.triggerText
13327 html: btn_text_select
13333 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13335 html: btn_text_done
13341 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13343 html: btn_text_cancel
13349 buttons.cn.unshift({
13351 cls: 'roo-select2-search-field-input'
13357 Roo.each(buttons.cn, function(c){
13359 c.cls += ' btn-' + _this.size;
13362 if (_this.disabled) {
13373 cls: 'form-hidden-field'
13377 cls: 'roo-select2-choices',
13381 cls: 'roo-select2-search-field',
13392 cls: 'roo-select2-container input-group roo-select2-container-multi',
13398 // cls: 'typeahead typeahead-long dropdown-menu',
13399 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13404 if(this.hasFeedback && !this.allowBlank){
13408 cls: 'glyphicon form-control-feedback'
13411 combobox.cn.push(feedback);
13416 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13417 tooltip : 'This field is required'
13419 if (Roo.bootstrap.version == 4) {
13422 style : 'display:none'
13425 if (align ==='left' && this.fieldLabel.length) {
13427 cfg.cls += ' roo-form-group-label-left row';
13434 cls : 'control-label col-form-label',
13435 html : this.fieldLabel
13447 var labelCfg = cfg.cn[1];
13448 var contentCfg = cfg.cn[2];
13451 if(this.indicatorpos == 'right'){
13457 cls : 'control-label col-form-label',
13461 html : this.fieldLabel
13477 labelCfg = cfg.cn[0];
13478 contentCfg = cfg.cn[1];
13482 if(this.labelWidth > 12){
13483 labelCfg.style = "width: " + this.labelWidth + 'px';
13486 if(this.labelWidth < 13 && this.labelmd == 0){
13487 this.labelmd = this.labelWidth;
13490 if(this.labellg > 0){
13491 labelCfg.cls += ' col-lg-' + this.labellg;
13492 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13495 if(this.labelmd > 0){
13496 labelCfg.cls += ' col-md-' + this.labelmd;
13497 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13500 if(this.labelsm > 0){
13501 labelCfg.cls += ' col-sm-' + this.labelsm;
13502 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13505 if(this.labelxs > 0){
13506 labelCfg.cls += ' col-xs-' + this.labelxs;
13507 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13511 } else if ( this.fieldLabel.length) {
13512 // Roo.log(" label");
13517 //cls : 'input-group-addon',
13518 html : this.fieldLabel
13523 if(this.indicatorpos == 'right'){
13527 //cls : 'input-group-addon',
13528 html : this.fieldLabel
13538 // Roo.log(" no label && no align");
13545 ['xs','sm','md','lg'].map(function(size){
13546 if (settings[size]) {
13547 cfg.cls += ' col-' + size + '-' + settings[size];
13555 _initEventsCalled : false,
13558 initEvents: function()
13560 if (this._initEventsCalled) { // as we call render... prevent looping...
13563 this._initEventsCalled = true;
13566 throw "can not find store for combo";
13569 this.indicator = this.indicatorEl();
13571 this.store = Roo.factory(this.store, Roo.data);
13572 this.store.parent = this;
13574 // if we are building from html. then this element is so complex, that we can not really
13575 // use the rendered HTML.
13576 // so we have to trash and replace the previous code.
13577 if (Roo.XComponent.build_from_html) {
13578 // remove this element....
13579 var e = this.el.dom, k=0;
13580 while (e ) { e = e.previousSibling; ++k;}
13585 this.rendered = false;
13587 this.render(this.parent().getChildContainer(true), k);
13590 if(Roo.isIOS && this.useNativeIOS){
13591 this.initIOSView();
13599 if(Roo.isTouch && this.mobileTouchView){
13600 this.initTouchView();
13605 this.initTickableEvents();
13609 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13611 if(this.hiddenName){
13613 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13615 this.hiddenField.dom.value =
13616 this.hiddenValue !== undefined ? this.hiddenValue :
13617 this.value !== undefined ? this.value : '';
13619 // prevent input submission
13620 this.el.dom.removeAttribute('name');
13621 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13626 // this.el.dom.setAttribute('autocomplete', 'off');
13629 var cls = 'x-combo-list';
13631 //this.list = new Roo.Layer({
13632 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13638 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13639 _this.list.setWidth(lw);
13642 this.list.on('mouseover', this.onViewOver, this);
13643 this.list.on('mousemove', this.onViewMove, this);
13644 this.list.on('scroll', this.onViewScroll, this);
13647 this.list.swallowEvent('mousewheel');
13648 this.assetHeight = 0;
13651 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13652 this.assetHeight += this.header.getHeight();
13655 this.innerList = this.list.createChild({cls:cls+'-inner'});
13656 this.innerList.on('mouseover', this.onViewOver, this);
13657 this.innerList.on('mousemove', this.onViewMove, this);
13658 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13660 if(this.allowBlank && !this.pageSize && !this.disableClear){
13661 this.footer = this.list.createChild({cls:cls+'-ft'});
13662 this.pageTb = new Roo.Toolbar(this.footer);
13666 this.footer = this.list.createChild({cls:cls+'-ft'});
13667 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13668 {pageSize: this.pageSize});
13672 if (this.pageTb && this.allowBlank && !this.disableClear) {
13674 this.pageTb.add(new Roo.Toolbar.Fill(), {
13675 cls: 'x-btn-icon x-btn-clear',
13677 handler: function()
13680 _this.clearValue();
13681 _this.onSelect(false, -1);
13686 this.assetHeight += this.footer.getHeight();
13691 this.tpl = Roo.bootstrap.version == 4 ?
13692 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
13693 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13696 this.view = new Roo.View(this.list, this.tpl, {
13697 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13699 //this.view.wrapEl.setDisplayed(false);
13700 this.view.on('click', this.onViewClick, this);
13703 this.store.on('beforeload', this.onBeforeLoad, this);
13704 this.store.on('load', this.onLoad, this);
13705 this.store.on('loadexception', this.onLoadException, this);
13707 if(this.resizable){
13708 this.resizer = new Roo.Resizable(this.list, {
13709 pinned:true, handles:'se'
13711 this.resizer.on('resize', function(r, w, h){
13712 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13713 this.listWidth = w;
13714 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13715 this.restrictHeight();
13717 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13720 if(!this.editable){
13721 this.editable = true;
13722 this.setEditable(false);
13727 if (typeof(this.events.add.listeners) != 'undefined') {
13729 this.addicon = this.wrap.createChild(
13730 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13732 this.addicon.on('click', function(e) {
13733 this.fireEvent('add', this);
13736 if (typeof(this.events.edit.listeners) != 'undefined') {
13738 this.editicon = this.wrap.createChild(
13739 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13740 if (this.addicon) {
13741 this.editicon.setStyle('margin-left', '40px');
13743 this.editicon.on('click', function(e) {
13745 // we fire even if inothing is selected..
13746 this.fireEvent('edit', this, this.lastData );
13752 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13753 "up" : function(e){
13754 this.inKeyMode = true;
13758 "down" : function(e){
13759 if(!this.isExpanded()){
13760 this.onTriggerClick();
13762 this.inKeyMode = true;
13767 "enter" : function(e){
13768 // this.onViewClick();
13772 if(this.fireEvent("specialkey", this, e)){
13773 this.onViewClick(false);
13779 "esc" : function(e){
13783 "tab" : function(e){
13786 if(this.fireEvent("specialkey", this, e)){
13787 this.onViewClick(false);
13795 doRelay : function(foo, bar, hname){
13796 if(hname == 'down' || this.scope.isExpanded()){
13797 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13806 this.queryDelay = Math.max(this.queryDelay || 10,
13807 this.mode == 'local' ? 10 : 250);
13810 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13812 if(this.typeAhead){
13813 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13815 if(this.editable !== false){
13816 this.inputEl().on("keyup", this.onKeyUp, this);
13818 if(this.forceSelection){
13819 this.inputEl().on('blur', this.doForce, this);
13823 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13824 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13828 initTickableEvents: function()
13832 if(this.hiddenName){
13834 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13836 this.hiddenField.dom.value =
13837 this.hiddenValue !== undefined ? this.hiddenValue :
13838 this.value !== undefined ? this.value : '';
13840 // prevent input submission
13841 this.el.dom.removeAttribute('name');
13842 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13847 // this.list = this.el.select('ul.dropdown-menu',true).first();
13849 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13850 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13851 if(this.triggerList){
13852 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13855 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13856 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13858 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13859 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13861 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13862 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13864 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13865 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13866 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13869 this.cancelBtn.hide();
13874 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13875 _this.list.setWidth(lw);
13878 this.list.on('mouseover', this.onViewOver, this);
13879 this.list.on('mousemove', this.onViewMove, this);
13881 this.list.on('scroll', this.onViewScroll, this);
13884 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13885 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13888 this.view = new Roo.View(this.list, this.tpl, {
13893 selectedClass: this.selectedClass
13896 //this.view.wrapEl.setDisplayed(false);
13897 this.view.on('click', this.onViewClick, this);
13901 this.store.on('beforeload', this.onBeforeLoad, this);
13902 this.store.on('load', this.onLoad, this);
13903 this.store.on('loadexception', this.onLoadException, this);
13906 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13907 "up" : function(e){
13908 this.inKeyMode = true;
13912 "down" : function(e){
13913 this.inKeyMode = true;
13917 "enter" : function(e){
13918 if(this.fireEvent("specialkey", this, e)){
13919 this.onViewClick(false);
13925 "esc" : function(e){
13926 this.onTickableFooterButtonClick(e, false, false);
13929 "tab" : function(e){
13930 this.fireEvent("specialkey", this, e);
13932 this.onTickableFooterButtonClick(e, false, false);
13939 doRelay : function(e, fn, key){
13940 if(this.scope.isExpanded()){
13941 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13950 this.queryDelay = Math.max(this.queryDelay || 10,
13951 this.mode == 'local' ? 10 : 250);
13954 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13956 if(this.typeAhead){
13957 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13960 if(this.editable !== false){
13961 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13964 this.indicator = this.indicatorEl();
13966 if(this.indicator){
13967 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13968 this.indicator.hide();
13973 onDestroy : function(){
13975 this.view.setStore(null);
13976 this.view.el.removeAllListeners();
13977 this.view.el.remove();
13978 this.view.purgeListeners();
13981 this.list.dom.innerHTML = '';
13985 this.store.un('beforeload', this.onBeforeLoad, this);
13986 this.store.un('load', this.onLoad, this);
13987 this.store.un('loadexception', this.onLoadException, this);
13989 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13993 fireKey : function(e){
13994 if(e.isNavKeyPress() && !this.list.isVisible()){
13995 this.fireEvent("specialkey", this, e);
14000 onResize: function(w, h){
14001 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14003 // if(typeof w != 'number'){
14004 // // we do not handle it!?!?
14007 // var tw = this.trigger.getWidth();
14008 // // tw += this.addicon ? this.addicon.getWidth() : 0;
14009 // // tw += this.editicon ? this.editicon.getWidth() : 0;
14011 // this.inputEl().setWidth( this.adjustWidth('input', x));
14013 // //this.trigger.setStyle('left', x+'px');
14015 // if(this.list && this.listWidth === undefined){
14016 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14017 // this.list.setWidth(lw);
14018 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14026 * Allow or prevent the user from directly editing the field text. If false is passed,
14027 * the user will only be able to select from the items defined in the dropdown list. This method
14028 * is the runtime equivalent of setting the 'editable' config option at config time.
14029 * @param {Boolean} value True to allow the user to directly edit the field text
14031 setEditable : function(value){
14032 if(value == this.editable){
14035 this.editable = value;
14037 this.inputEl().dom.setAttribute('readOnly', true);
14038 this.inputEl().on('mousedown', this.onTriggerClick, this);
14039 this.inputEl().addClass('x-combo-noedit');
14041 this.inputEl().dom.setAttribute('readOnly', false);
14042 this.inputEl().un('mousedown', this.onTriggerClick, this);
14043 this.inputEl().removeClass('x-combo-noedit');
14049 onBeforeLoad : function(combo,opts){
14050 if(!this.hasFocus){
14054 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14056 this.restrictHeight();
14057 this.selectedIndex = -1;
14061 onLoad : function(){
14063 this.hasQuery = false;
14065 if(!this.hasFocus){
14069 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14070 this.loading.hide();
14073 if(this.store.getCount() > 0){
14076 this.restrictHeight();
14077 if(this.lastQuery == this.allQuery){
14078 if(this.editable && !this.tickable){
14079 this.inputEl().dom.select();
14083 !this.selectByValue(this.value, true) &&
14086 !this.store.lastOptions ||
14087 typeof(this.store.lastOptions.add) == 'undefined' ||
14088 this.store.lastOptions.add != true
14091 this.select(0, true);
14094 if(this.autoFocus){
14097 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14098 this.taTask.delay(this.typeAheadDelay);
14102 this.onEmptyResults();
14108 onLoadException : function()
14110 this.hasQuery = false;
14112 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14113 this.loading.hide();
14116 if(this.tickable && this.editable){
14121 // only causes errors at present
14122 //Roo.log(this.store.reader.jsonData);
14123 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14125 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14131 onTypeAhead : function(){
14132 if(this.store.getCount() > 0){
14133 var r = this.store.getAt(0);
14134 var newValue = r.data[this.displayField];
14135 var len = newValue.length;
14136 var selStart = this.getRawValue().length;
14138 if(selStart != len){
14139 this.setRawValue(newValue);
14140 this.selectText(selStart, newValue.length);
14146 onSelect : function(record, index){
14148 if(this.fireEvent('beforeselect', this, record, index) !== false){
14150 this.setFromData(index > -1 ? record.data : false);
14153 this.fireEvent('select', this, record, index);
14158 * Returns the currently selected field value or empty string if no value is set.
14159 * @return {String} value The selected value
14161 getValue : function()
14163 if(Roo.isIOS && this.useNativeIOS){
14164 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14168 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14171 if(this.valueField){
14172 return typeof this.value != 'undefined' ? this.value : '';
14174 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14178 getRawValue : function()
14180 if(Roo.isIOS && this.useNativeIOS){
14181 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14184 var v = this.inputEl().getValue();
14190 * Clears any text/value currently set in the field
14192 clearValue : function(){
14194 if(this.hiddenField){
14195 this.hiddenField.dom.value = '';
14198 this.setRawValue('');
14199 this.lastSelectionText = '';
14200 this.lastData = false;
14202 var close = this.closeTriggerEl();
14213 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14214 * will be displayed in the field. If the value does not match the data value of an existing item,
14215 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14216 * Otherwise the field will be blank (although the value will still be set).
14217 * @param {String} value The value to match
14219 setValue : function(v)
14221 if(Roo.isIOS && this.useNativeIOS){
14222 this.setIOSValue(v);
14232 if(this.valueField){
14233 var r = this.findRecord(this.valueField, v);
14235 text = r.data[this.displayField];
14236 }else if(this.valueNotFoundText !== undefined){
14237 text = this.valueNotFoundText;
14240 this.lastSelectionText = text;
14241 if(this.hiddenField){
14242 this.hiddenField.dom.value = v;
14244 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14247 var close = this.closeTriggerEl();
14250 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14256 * @property {Object} the last set data for the element
14261 * Sets the value of the field based on a object which is related to the record format for the store.
14262 * @param {Object} value the value to set as. or false on reset?
14264 setFromData : function(o){
14271 var dv = ''; // display value
14272 var vv = ''; // value value..
14274 if (this.displayField) {
14275 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14277 // this is an error condition!!!
14278 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14281 if(this.valueField){
14282 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14285 var close = this.closeTriggerEl();
14288 if(dv.length || vv * 1 > 0){
14290 this.blockFocus=true;
14296 if(this.hiddenField){
14297 this.hiddenField.dom.value = vv;
14299 this.lastSelectionText = dv;
14300 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14304 // no hidden field.. - we store the value in 'value', but still display
14305 // display field!!!!
14306 this.lastSelectionText = dv;
14307 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14314 reset : function(){
14315 // overridden so that last data is reset..
14322 this.setValue(this.originalValue);
14323 //this.clearInvalid();
14324 this.lastData = false;
14326 this.view.clearSelections();
14332 findRecord : function(prop, value){
14334 if(this.store.getCount() > 0){
14335 this.store.each(function(r){
14336 if(r.data[prop] == value){
14346 getName: function()
14348 // returns hidden if it's set..
14349 if (!this.rendered) {return ''};
14350 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14354 onViewMove : function(e, t){
14355 this.inKeyMode = false;
14359 onViewOver : function(e, t){
14360 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14363 var item = this.view.findItemFromChild(t);
14366 var index = this.view.indexOf(item);
14367 this.select(index, false);
14372 onViewClick : function(view, doFocus, el, e)
14374 var index = this.view.getSelectedIndexes()[0];
14376 var r = this.store.getAt(index);
14380 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14387 Roo.each(this.tickItems, function(v,k){
14389 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14391 _this.tickItems.splice(k, 1);
14393 if(typeof(e) == 'undefined' && view == false){
14394 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14406 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14407 this.tickItems.push(r.data);
14410 if(typeof(e) == 'undefined' && view == false){
14411 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14418 this.onSelect(r, index);
14420 if(doFocus !== false && !this.blockFocus){
14421 this.inputEl().focus();
14426 restrictHeight : function(){
14427 //this.innerList.dom.style.height = '';
14428 //var inner = this.innerList.dom;
14429 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14430 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14431 //this.list.beginUpdate();
14432 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14433 this.list.alignTo(this.inputEl(), this.listAlign);
14434 this.list.alignTo(this.inputEl(), this.listAlign);
14435 //this.list.endUpdate();
14439 onEmptyResults : function(){
14441 if(this.tickable && this.editable){
14442 this.hasFocus = false;
14443 this.restrictHeight();
14451 * Returns true if the dropdown list is expanded, else false.
14453 isExpanded : function(){
14454 return this.list.isVisible();
14458 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14459 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14460 * @param {String} value The data value of the item to select
14461 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14462 * selected item if it is not currently in view (defaults to true)
14463 * @return {Boolean} True if the value matched an item in the list, else false
14465 selectByValue : function(v, scrollIntoView){
14466 if(v !== undefined && v !== null){
14467 var r = this.findRecord(this.valueField || this.displayField, v);
14469 this.select(this.store.indexOf(r), scrollIntoView);
14477 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14478 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14479 * @param {Number} index The zero-based index of the list item to select
14480 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14481 * selected item if it is not currently in view (defaults to true)
14483 select : function(index, scrollIntoView){
14484 this.selectedIndex = index;
14485 this.view.select(index);
14486 if(scrollIntoView !== false){
14487 var el = this.view.getNode(index);
14489 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14492 this.list.scrollChildIntoView(el, false);
14498 selectNext : function(){
14499 var ct = this.store.getCount();
14501 if(this.selectedIndex == -1){
14503 }else if(this.selectedIndex < ct-1){
14504 this.select(this.selectedIndex+1);
14510 selectPrev : function(){
14511 var ct = this.store.getCount();
14513 if(this.selectedIndex == -1){
14515 }else if(this.selectedIndex != 0){
14516 this.select(this.selectedIndex-1);
14522 onKeyUp : function(e){
14523 if(this.editable !== false && !e.isSpecialKey()){
14524 this.lastKey = e.getKey();
14525 this.dqTask.delay(this.queryDelay);
14530 validateBlur : function(){
14531 return !this.list || !this.list.isVisible();
14535 initQuery : function(){
14537 var v = this.getRawValue();
14539 if(this.tickable && this.editable){
14540 v = this.tickableInputEl().getValue();
14547 doForce : function(){
14548 if(this.inputEl().dom.value.length > 0){
14549 this.inputEl().dom.value =
14550 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14556 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14557 * query allowing the query action to be canceled if needed.
14558 * @param {String} query The SQL query to execute
14559 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14560 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14561 * saved in the current store (defaults to false)
14563 doQuery : function(q, forceAll){
14565 if(q === undefined || q === null){
14570 forceAll: forceAll,
14574 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14579 forceAll = qe.forceAll;
14580 if(forceAll === true || (q.length >= this.minChars)){
14582 this.hasQuery = true;
14584 if(this.lastQuery != q || this.alwaysQuery){
14585 this.lastQuery = q;
14586 if(this.mode == 'local'){
14587 this.selectedIndex = -1;
14589 this.store.clearFilter();
14592 if(this.specialFilter){
14593 this.fireEvent('specialfilter', this);
14598 this.store.filter(this.displayField, q);
14601 this.store.fireEvent("datachanged", this.store);
14608 this.store.baseParams[this.queryParam] = q;
14610 var options = {params : this.getParams(q)};
14613 options.add = true;
14614 options.params.start = this.page * this.pageSize;
14617 this.store.load(options);
14620 * this code will make the page width larger, at the beginning, the list not align correctly,
14621 * we should expand the list on onLoad
14622 * so command out it
14627 this.selectedIndex = -1;
14632 this.loadNext = false;
14636 getParams : function(q){
14638 //p[this.queryParam] = q;
14642 p.limit = this.pageSize;
14648 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14650 collapse : function(){
14651 if(!this.isExpanded()){
14657 this.hasFocus = false;
14661 this.cancelBtn.hide();
14662 this.trigger.show();
14665 this.tickableInputEl().dom.value = '';
14666 this.tickableInputEl().blur();
14671 Roo.get(document).un('mousedown', this.collapseIf, this);
14672 Roo.get(document).un('mousewheel', this.collapseIf, this);
14673 if (!this.editable) {
14674 Roo.get(document).un('keydown', this.listKeyPress, this);
14676 this.fireEvent('collapse', this);
14682 collapseIf : function(e){
14683 var in_combo = e.within(this.el);
14684 var in_list = e.within(this.list);
14685 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14687 if (in_combo || in_list || is_list) {
14688 //e.stopPropagation();
14693 this.onTickableFooterButtonClick(e, false, false);
14701 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14703 expand : function(){
14705 if(this.isExpanded() || !this.hasFocus){
14709 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14710 this.list.setWidth(lw);
14716 this.restrictHeight();
14720 this.tickItems = Roo.apply([], this.item);
14723 this.cancelBtn.show();
14724 this.trigger.hide();
14727 this.tickableInputEl().focus();
14732 Roo.get(document).on('mousedown', this.collapseIf, this);
14733 Roo.get(document).on('mousewheel', this.collapseIf, this);
14734 if (!this.editable) {
14735 Roo.get(document).on('keydown', this.listKeyPress, this);
14738 this.fireEvent('expand', this);
14742 // Implements the default empty TriggerField.onTriggerClick function
14743 onTriggerClick : function(e)
14745 Roo.log('trigger click');
14747 if(this.disabled || !this.triggerList){
14752 this.loadNext = false;
14754 if(this.isExpanded()){
14756 if (!this.blockFocus) {
14757 this.inputEl().focus();
14761 this.hasFocus = true;
14762 if(this.triggerAction == 'all') {
14763 this.doQuery(this.allQuery, true);
14765 this.doQuery(this.getRawValue());
14767 if (!this.blockFocus) {
14768 this.inputEl().focus();
14773 onTickableTriggerClick : function(e)
14780 this.loadNext = false;
14781 this.hasFocus = true;
14783 if(this.triggerAction == 'all') {
14784 this.doQuery(this.allQuery, true);
14786 this.doQuery(this.getRawValue());
14790 onSearchFieldClick : function(e)
14792 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14793 this.onTickableFooterButtonClick(e, false, false);
14797 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14802 this.loadNext = false;
14803 this.hasFocus = true;
14805 if(this.triggerAction == 'all') {
14806 this.doQuery(this.allQuery, true);
14808 this.doQuery(this.getRawValue());
14812 listKeyPress : function(e)
14814 //Roo.log('listkeypress');
14815 // scroll to first matching element based on key pres..
14816 if (e.isSpecialKey()) {
14819 var k = String.fromCharCode(e.getKey()).toUpperCase();
14822 var csel = this.view.getSelectedNodes();
14823 var cselitem = false;
14825 var ix = this.view.indexOf(csel[0]);
14826 cselitem = this.store.getAt(ix);
14827 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14833 this.store.each(function(v) {
14835 // start at existing selection.
14836 if (cselitem.id == v.id) {
14842 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14843 match = this.store.indexOf(v);
14849 if (match === false) {
14850 return true; // no more action?
14853 this.view.select(match);
14854 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14855 sn.scrollIntoView(sn.dom.parentNode, false);
14858 onViewScroll : function(e, t){
14860 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){
14864 this.hasQuery = true;
14866 this.loading = this.list.select('.loading', true).first();
14868 if(this.loading === null){
14869 this.list.createChild({
14871 cls: 'loading roo-select2-more-results roo-select2-active',
14872 html: 'Loading more results...'
14875 this.loading = this.list.select('.loading', true).first();
14877 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14879 this.loading.hide();
14882 this.loading.show();
14887 this.loadNext = true;
14889 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14894 addItem : function(o)
14896 var dv = ''; // display value
14898 if (this.displayField) {
14899 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14901 // this is an error condition!!!
14902 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14909 var choice = this.choices.createChild({
14911 cls: 'roo-select2-search-choice',
14920 cls: 'roo-select2-search-choice-close fa fa-times',
14925 }, this.searchField);
14927 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14929 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14937 this.inputEl().dom.value = '';
14942 onRemoveItem : function(e, _self, o)
14944 e.preventDefault();
14946 this.lastItem = Roo.apply([], this.item);
14948 var index = this.item.indexOf(o.data) * 1;
14951 Roo.log('not this item?!');
14955 this.item.splice(index, 1);
14960 this.fireEvent('remove', this, e);
14966 syncValue : function()
14968 if(!this.item.length){
14975 Roo.each(this.item, function(i){
14976 if(_this.valueField){
14977 value.push(i[_this.valueField]);
14984 this.value = value.join(',');
14986 if(this.hiddenField){
14987 this.hiddenField.dom.value = this.value;
14990 this.store.fireEvent("datachanged", this.store);
14995 clearItem : function()
14997 if(!this.multiple){
15003 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15011 if(this.tickable && !Roo.isTouch){
15012 this.view.refresh();
15016 inputEl: function ()
15018 if(Roo.isIOS && this.useNativeIOS){
15019 return this.el.select('select.roo-ios-select', true).first();
15022 if(Roo.isTouch && this.mobileTouchView){
15023 return this.el.select('input.form-control',true).first();
15027 return this.searchField;
15030 return this.el.select('input.form-control',true).first();
15033 onTickableFooterButtonClick : function(e, btn, el)
15035 e.preventDefault();
15037 this.lastItem = Roo.apply([], this.item);
15039 if(btn && btn.name == 'cancel'){
15040 this.tickItems = Roo.apply([], this.item);
15049 Roo.each(this.tickItems, function(o){
15057 validate : function()
15059 if(this.getVisibilityEl().hasClass('hidden')){
15063 var v = this.getRawValue();
15066 v = this.getValue();
15069 if(this.disabled || this.allowBlank || v.length){
15074 this.markInvalid();
15078 tickableInputEl : function()
15080 if(!this.tickable || !this.editable){
15081 return this.inputEl();
15084 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15088 getAutoCreateTouchView : function()
15093 cls: 'form-group' //input-group
15099 type : this.inputType,
15100 cls : 'form-control x-combo-noedit',
15101 autocomplete: 'new-password',
15102 placeholder : this.placeholder || '',
15107 input.name = this.name;
15111 input.cls += ' input-' + this.size;
15114 if (this.disabled) {
15115 input.disabled = true;
15126 inputblock.cls += ' input-group';
15128 inputblock.cn.unshift({
15130 cls : 'input-group-addon input-group-prepend input-group-text',
15135 if(this.removable && !this.multiple){
15136 inputblock.cls += ' roo-removable';
15138 inputblock.cn.push({
15141 cls : 'roo-combo-removable-btn close'
15145 if(this.hasFeedback && !this.allowBlank){
15147 inputblock.cls += ' has-feedback';
15149 inputblock.cn.push({
15151 cls: 'glyphicon form-control-feedback'
15158 inputblock.cls += (this.before) ? '' : ' input-group';
15160 inputblock.cn.push({
15162 cls : 'input-group-addon input-group-append input-group-text',
15168 var ibwrap = inputblock;
15173 cls: 'roo-select2-choices',
15177 cls: 'roo-select2-search-field',
15190 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15195 cls: 'form-hidden-field'
15201 if(!this.multiple && this.showToggleBtn){
15208 if (this.caret != false) {
15211 cls: 'fa fa-' + this.caret
15218 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15223 cls: 'combobox-clear',
15237 combobox.cls += ' roo-select2-container-multi';
15240 var align = this.labelAlign || this.parentLabelAlign();
15242 if (align ==='left' && this.fieldLabel.length) {
15247 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15248 tooltip : 'This field is required'
15252 cls : 'control-label col-form-label',
15253 html : this.fieldLabel
15264 var labelCfg = cfg.cn[1];
15265 var contentCfg = cfg.cn[2];
15268 if(this.indicatorpos == 'right'){
15273 cls : 'control-label col-form-label',
15277 html : this.fieldLabel
15281 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15282 tooltip : 'This field is required'
15295 labelCfg = cfg.cn[0];
15296 contentCfg = cfg.cn[1];
15301 if(this.labelWidth > 12){
15302 labelCfg.style = "width: " + this.labelWidth + 'px';
15305 if(this.labelWidth < 13 && this.labelmd == 0){
15306 this.labelmd = this.labelWidth;
15309 if(this.labellg > 0){
15310 labelCfg.cls += ' col-lg-' + this.labellg;
15311 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15314 if(this.labelmd > 0){
15315 labelCfg.cls += ' col-md-' + this.labelmd;
15316 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15319 if(this.labelsm > 0){
15320 labelCfg.cls += ' col-sm-' + this.labelsm;
15321 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15324 if(this.labelxs > 0){
15325 labelCfg.cls += ' col-xs-' + this.labelxs;
15326 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15330 } else if ( this.fieldLabel.length) {
15334 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15335 tooltip : 'This field is required'
15339 cls : 'control-label',
15340 html : this.fieldLabel
15351 if(this.indicatorpos == 'right'){
15355 cls : 'control-label',
15356 html : this.fieldLabel,
15360 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15361 tooltip : 'This field is required'
15378 var settings = this;
15380 ['xs','sm','md','lg'].map(function(size){
15381 if (settings[size]) {
15382 cfg.cls += ' col-' + size + '-' + settings[size];
15389 initTouchView : function()
15391 this.renderTouchView();
15393 this.touchViewEl.on('scroll', function(){
15394 this.el.dom.scrollTop = 0;
15397 this.originalValue = this.getValue();
15399 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15401 this.inputEl().on("click", this.showTouchView, this);
15402 if (this.triggerEl) {
15403 this.triggerEl.on("click", this.showTouchView, this);
15407 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15408 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15410 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15412 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15413 this.store.on('load', this.onTouchViewLoad, this);
15414 this.store.on('loadexception', this.onTouchViewLoadException, this);
15416 if(this.hiddenName){
15418 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15420 this.hiddenField.dom.value =
15421 this.hiddenValue !== undefined ? this.hiddenValue :
15422 this.value !== undefined ? this.value : '';
15424 this.el.dom.removeAttribute('name');
15425 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15429 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15430 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15433 if(this.removable && !this.multiple){
15434 var close = this.closeTriggerEl();
15436 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15437 close.on('click', this.removeBtnClick, this, close);
15441 * fix the bug in Safari iOS8
15443 this.inputEl().on("focus", function(e){
15444 document.activeElement.blur();
15447 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15454 renderTouchView : function()
15456 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15457 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15459 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15460 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15462 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15463 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15464 this.touchViewBodyEl.setStyle('overflow', 'auto');
15466 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15467 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15469 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15470 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15474 showTouchView : function()
15480 this.touchViewHeaderEl.hide();
15482 if(this.modalTitle.length){
15483 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15484 this.touchViewHeaderEl.show();
15487 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15488 this.touchViewEl.show();
15490 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15492 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15493 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15495 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15497 if(this.modalTitle.length){
15498 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15501 this.touchViewBodyEl.setHeight(bodyHeight);
15505 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15507 this.touchViewEl.addClass('in');
15510 if(this._touchViewMask){
15511 Roo.get(document.body).addClass("x-body-masked");
15512 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15513 this._touchViewMask.setStyle('z-index', 10000);
15514 this._touchViewMask.addClass('show');
15517 this.doTouchViewQuery();
15521 hideTouchView : function()
15523 this.touchViewEl.removeClass('in');
15527 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15529 this.touchViewEl.setStyle('display', 'none');
15532 if(this._touchViewMask){
15533 this._touchViewMask.removeClass('show');
15534 Roo.get(document.body).removeClass("x-body-masked");
15538 setTouchViewValue : function()
15545 Roo.each(this.tickItems, function(o){
15550 this.hideTouchView();
15553 doTouchViewQuery : function()
15562 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15566 if(!this.alwaysQuery || this.mode == 'local'){
15567 this.onTouchViewLoad();
15574 onTouchViewBeforeLoad : function(combo,opts)
15580 onTouchViewLoad : function()
15582 if(this.store.getCount() < 1){
15583 this.onTouchViewEmptyResults();
15587 this.clearTouchView();
15589 var rawValue = this.getRawValue();
15591 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15593 this.tickItems = [];
15595 this.store.data.each(function(d, rowIndex){
15596 var row = this.touchViewListGroup.createChild(template);
15598 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15599 row.addClass(d.data.cls);
15602 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15605 html : d.data[this.displayField]
15608 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15609 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15612 row.removeClass('selected');
15613 if(!this.multiple && this.valueField &&
15614 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15617 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15618 row.addClass('selected');
15621 if(this.multiple && this.valueField &&
15622 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15626 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15627 this.tickItems.push(d.data);
15630 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15634 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15636 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15638 if(this.modalTitle.length){
15639 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15642 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15644 if(this.mobile_restrict_height && listHeight < bodyHeight){
15645 this.touchViewBodyEl.setHeight(listHeight);
15650 if(firstChecked && listHeight > bodyHeight){
15651 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15656 onTouchViewLoadException : function()
15658 this.hideTouchView();
15661 onTouchViewEmptyResults : function()
15663 this.clearTouchView();
15665 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15667 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15671 clearTouchView : function()
15673 this.touchViewListGroup.dom.innerHTML = '';
15676 onTouchViewClick : function(e, el, o)
15678 e.preventDefault();
15681 var rowIndex = o.rowIndex;
15683 var r = this.store.getAt(rowIndex);
15685 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15687 if(!this.multiple){
15688 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15689 c.dom.removeAttribute('checked');
15692 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15694 this.setFromData(r.data);
15696 var close = this.closeTriggerEl();
15702 this.hideTouchView();
15704 this.fireEvent('select', this, r, rowIndex);
15709 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15710 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15711 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15715 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15716 this.addItem(r.data);
15717 this.tickItems.push(r.data);
15721 getAutoCreateNativeIOS : function()
15724 cls: 'form-group' //input-group,
15729 cls : 'roo-ios-select'
15733 combobox.name = this.name;
15736 if (this.disabled) {
15737 combobox.disabled = true;
15740 var settings = this;
15742 ['xs','sm','md','lg'].map(function(size){
15743 if (settings[size]) {
15744 cfg.cls += ' col-' + size + '-' + settings[size];
15754 initIOSView : function()
15756 this.store.on('load', this.onIOSViewLoad, this);
15761 onIOSViewLoad : function()
15763 if(this.store.getCount() < 1){
15767 this.clearIOSView();
15769 if(this.allowBlank) {
15771 var default_text = '-- SELECT --';
15773 if(this.placeholder.length){
15774 default_text = this.placeholder;
15777 if(this.emptyTitle.length){
15778 default_text += ' - ' + this.emptyTitle + ' -';
15781 var opt = this.inputEl().createChild({
15784 html : default_text
15788 o[this.valueField] = 0;
15789 o[this.displayField] = default_text;
15791 this.ios_options.push({
15798 this.store.data.each(function(d, rowIndex){
15802 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15803 html = d.data[this.displayField];
15808 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15809 value = d.data[this.valueField];
15818 if(this.value == d.data[this.valueField]){
15819 option['selected'] = true;
15822 var opt = this.inputEl().createChild(option);
15824 this.ios_options.push({
15831 this.inputEl().on('change', function(){
15832 this.fireEvent('select', this);
15837 clearIOSView: function()
15839 this.inputEl().dom.innerHTML = '';
15841 this.ios_options = [];
15844 setIOSValue: function(v)
15848 if(!this.ios_options){
15852 Roo.each(this.ios_options, function(opts){
15854 opts.el.dom.removeAttribute('selected');
15856 if(opts.data[this.valueField] != v){
15860 opts.el.dom.setAttribute('selected', true);
15866 * @cfg {Boolean} grow
15870 * @cfg {Number} growMin
15874 * @cfg {Number} growMax
15883 Roo.apply(Roo.bootstrap.ComboBox, {
15887 cls: 'modal-header',
15909 cls: 'list-group-item',
15913 cls: 'roo-combobox-list-group-item-value'
15917 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15931 listItemCheckbox : {
15933 cls: 'list-group-item',
15937 cls: 'roo-combobox-list-group-item-value'
15941 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15957 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15962 cls: 'modal-footer',
15970 cls: 'col-xs-6 text-left',
15973 cls: 'btn btn-danger roo-touch-view-cancel',
15979 cls: 'col-xs-6 text-right',
15982 cls: 'btn btn-success roo-touch-view-ok',
15993 Roo.apply(Roo.bootstrap.ComboBox, {
15995 touchViewTemplate : {
15997 cls: 'modal fade roo-combobox-touch-view',
16001 cls: 'modal-dialog',
16002 style : 'position:fixed', // we have to fix position....
16006 cls: 'modal-content',
16008 Roo.bootstrap.ComboBox.header,
16009 Roo.bootstrap.ComboBox.body,
16010 Roo.bootstrap.ComboBox.footer
16019 * Ext JS Library 1.1.1
16020 * Copyright(c) 2006-2007, Ext JS, LLC.
16022 * Originally Released Under LGPL - original licence link has changed is not relivant.
16025 * <script type="text/javascript">
16030 * @extends Roo.util.Observable
16031 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
16032 * This class also supports single and multi selection modes. <br>
16033 * Create a data model bound view:
16035 var store = new Roo.data.Store(...);
16037 var view = new Roo.View({
16039 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
16041 singleSelect: true,
16042 selectedClass: "ydataview-selected",
16046 // listen for node click?
16047 view.on("click", function(vw, index, node, e){
16048 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16052 dataModel.load("foobar.xml");
16054 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16056 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16057 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16059 * Note: old style constructor is still suported (container, template, config)
16062 * Create a new View
16063 * @param {Object} config The config object
16066 Roo.View = function(config, depreciated_tpl, depreciated_config){
16068 this.parent = false;
16070 if (typeof(depreciated_tpl) == 'undefined') {
16071 // new way.. - universal constructor.
16072 Roo.apply(this, config);
16073 this.el = Roo.get(this.el);
16076 this.el = Roo.get(config);
16077 this.tpl = depreciated_tpl;
16078 Roo.apply(this, depreciated_config);
16080 this.wrapEl = this.el.wrap().wrap();
16081 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16084 if(typeof(this.tpl) == "string"){
16085 this.tpl = new Roo.Template(this.tpl);
16087 // support xtype ctors..
16088 this.tpl = new Roo.factory(this.tpl, Roo);
16092 this.tpl.compile();
16097 * @event beforeclick
16098 * Fires before a click is processed. Returns false to cancel the default action.
16099 * @param {Roo.View} this
16100 * @param {Number} index The index of the target node
16101 * @param {HTMLElement} node The target node
16102 * @param {Roo.EventObject} e The raw event object
16104 "beforeclick" : true,
16107 * Fires when a template node is clicked.
16108 * @param {Roo.View} this
16109 * @param {Number} index The index of the target node
16110 * @param {HTMLElement} node The target node
16111 * @param {Roo.EventObject} e The raw event object
16116 * Fires when a template node is double clicked.
16117 * @param {Roo.View} this
16118 * @param {Number} index The index of the target node
16119 * @param {HTMLElement} node The target node
16120 * @param {Roo.EventObject} e The raw event object
16124 * @event contextmenu
16125 * Fires when a template node is right clicked.
16126 * @param {Roo.View} this
16127 * @param {Number} index The index of the target node
16128 * @param {HTMLElement} node The target node
16129 * @param {Roo.EventObject} e The raw event object
16131 "contextmenu" : true,
16133 * @event selectionchange
16134 * Fires when the selected nodes change.
16135 * @param {Roo.View} this
16136 * @param {Array} selections Array of the selected nodes
16138 "selectionchange" : true,
16141 * @event beforeselect
16142 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16143 * @param {Roo.View} this
16144 * @param {HTMLElement} node The node to be selected
16145 * @param {Array} selections Array of currently selected nodes
16147 "beforeselect" : true,
16149 * @event preparedata
16150 * Fires on every row to render, to allow you to change the data.
16151 * @param {Roo.View} this
16152 * @param {Object} data to be rendered (change this)
16154 "preparedata" : true
16162 "click": this.onClick,
16163 "dblclick": this.onDblClick,
16164 "contextmenu": this.onContextMenu,
16168 this.selections = [];
16170 this.cmp = new Roo.CompositeElementLite([]);
16172 this.store = Roo.factory(this.store, Roo.data);
16173 this.setStore(this.store, true);
16176 if ( this.footer && this.footer.xtype) {
16178 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16180 this.footer.dataSource = this.store;
16181 this.footer.container = fctr;
16182 this.footer = Roo.factory(this.footer, Roo);
16183 fctr.insertFirst(this.el);
16185 // this is a bit insane - as the paging toolbar seems to detach the el..
16186 // dom.parentNode.parentNode.parentNode
16187 // they get detached?
16191 Roo.View.superclass.constructor.call(this);
16196 Roo.extend(Roo.View, Roo.util.Observable, {
16199 * @cfg {Roo.data.Store} store Data store to load data from.
16204 * @cfg {String|Roo.Element} el The container element.
16209 * @cfg {String|Roo.Template} tpl The template used by this View
16213 * @cfg {String} dataName the named area of the template to use as the data area
16214 * Works with domtemplates roo-name="name"
16218 * @cfg {String} selectedClass The css class to add to selected nodes
16220 selectedClass : "x-view-selected",
16222 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16227 * @cfg {String} text to display on mask (default Loading)
16231 * @cfg {Boolean} multiSelect Allow multiple selection
16233 multiSelect : false,
16235 * @cfg {Boolean} singleSelect Allow single selection
16237 singleSelect: false,
16240 * @cfg {Boolean} toggleSelect - selecting
16242 toggleSelect : false,
16245 * @cfg {Boolean} tickable - selecting
16250 * Returns the element this view is bound to.
16251 * @return {Roo.Element}
16253 getEl : function(){
16254 return this.wrapEl;
16260 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16262 refresh : function(){
16263 //Roo.log('refresh');
16266 // if we are using something like 'domtemplate', then
16267 // the what gets used is:
16268 // t.applySubtemplate(NAME, data, wrapping data..)
16269 // the outer template then get' applied with
16270 // the store 'extra data'
16271 // and the body get's added to the
16272 // roo-name="data" node?
16273 // <span class='roo-tpl-{name}'></span> ?????
16277 this.clearSelections();
16278 this.el.update("");
16280 var records = this.store.getRange();
16281 if(records.length < 1) {
16283 // is this valid?? = should it render a template??
16285 this.el.update(this.emptyText);
16289 if (this.dataName) {
16290 this.el.update(t.apply(this.store.meta)); //????
16291 el = this.el.child('.roo-tpl-' + this.dataName);
16294 for(var i = 0, len = records.length; i < len; i++){
16295 var data = this.prepareData(records[i].data, i, records[i]);
16296 this.fireEvent("preparedata", this, data, i, records[i]);
16298 var d = Roo.apply({}, data);
16301 Roo.apply(d, {'roo-id' : Roo.id()});
16305 Roo.each(this.parent.item, function(item){
16306 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16309 Roo.apply(d, {'roo-data-checked' : 'checked'});
16313 html[html.length] = Roo.util.Format.trim(
16315 t.applySubtemplate(this.dataName, d, this.store.meta) :
16322 el.update(html.join(""));
16323 this.nodes = el.dom.childNodes;
16324 this.updateIndexes(0);
16329 * Function to override to reformat the data that is sent to
16330 * the template for each node.
16331 * DEPRICATED - use the preparedata event handler.
16332 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16333 * a JSON object for an UpdateManager bound view).
16335 prepareData : function(data, index, record)
16337 this.fireEvent("preparedata", this, data, index, record);
16341 onUpdate : function(ds, record){
16342 // Roo.log('on update');
16343 this.clearSelections();
16344 var index = this.store.indexOf(record);
16345 var n = this.nodes[index];
16346 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16347 n.parentNode.removeChild(n);
16348 this.updateIndexes(index, index);
16354 onAdd : function(ds, records, index)
16356 //Roo.log(['on Add', ds, records, index] );
16357 this.clearSelections();
16358 if(this.nodes.length == 0){
16362 var n = this.nodes[index];
16363 for(var i = 0, len = records.length; i < len; i++){
16364 var d = this.prepareData(records[i].data, i, records[i]);
16366 this.tpl.insertBefore(n, d);
16369 this.tpl.append(this.el, d);
16372 this.updateIndexes(index);
16375 onRemove : function(ds, record, index){
16376 // Roo.log('onRemove');
16377 this.clearSelections();
16378 var el = this.dataName ?
16379 this.el.child('.roo-tpl-' + this.dataName) :
16382 el.dom.removeChild(this.nodes[index]);
16383 this.updateIndexes(index);
16387 * Refresh an individual node.
16388 * @param {Number} index
16390 refreshNode : function(index){
16391 this.onUpdate(this.store, this.store.getAt(index));
16394 updateIndexes : function(startIndex, endIndex){
16395 var ns = this.nodes;
16396 startIndex = startIndex || 0;
16397 endIndex = endIndex || ns.length - 1;
16398 for(var i = startIndex; i <= endIndex; i++){
16399 ns[i].nodeIndex = i;
16404 * Changes the data store this view uses and refresh the view.
16405 * @param {Store} store
16407 setStore : function(store, initial){
16408 if(!initial && this.store){
16409 this.store.un("datachanged", this.refresh);
16410 this.store.un("add", this.onAdd);
16411 this.store.un("remove", this.onRemove);
16412 this.store.un("update", this.onUpdate);
16413 this.store.un("clear", this.refresh);
16414 this.store.un("beforeload", this.onBeforeLoad);
16415 this.store.un("load", this.onLoad);
16416 this.store.un("loadexception", this.onLoad);
16420 store.on("datachanged", this.refresh, this);
16421 store.on("add", this.onAdd, this);
16422 store.on("remove", this.onRemove, this);
16423 store.on("update", this.onUpdate, this);
16424 store.on("clear", this.refresh, this);
16425 store.on("beforeload", this.onBeforeLoad, this);
16426 store.on("load", this.onLoad, this);
16427 store.on("loadexception", this.onLoad, this);
16435 * onbeforeLoad - masks the loading area.
16438 onBeforeLoad : function(store,opts)
16440 //Roo.log('onBeforeLoad');
16442 this.el.update("");
16444 this.el.mask(this.mask ? this.mask : "Loading" );
16446 onLoad : function ()
16453 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16454 * @param {HTMLElement} node
16455 * @return {HTMLElement} The template node
16457 findItemFromChild : function(node){
16458 var el = this.dataName ?
16459 this.el.child('.roo-tpl-' + this.dataName,true) :
16462 if(!node || node.parentNode == el){
16465 var p = node.parentNode;
16466 while(p && p != el){
16467 if(p.parentNode == el){
16476 onClick : function(e){
16477 var item = this.findItemFromChild(e.getTarget());
16479 var index = this.indexOf(item);
16480 if(this.onItemClick(item, index, e) !== false){
16481 this.fireEvent("click", this, index, item, e);
16484 this.clearSelections();
16489 onContextMenu : function(e){
16490 var item = this.findItemFromChild(e.getTarget());
16492 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16497 onDblClick : function(e){
16498 var item = this.findItemFromChild(e.getTarget());
16500 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16504 onItemClick : function(item, index, e)
16506 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16509 if (this.toggleSelect) {
16510 var m = this.isSelected(item) ? 'unselect' : 'select';
16513 _t[m](item, true, false);
16516 if(this.multiSelect || this.singleSelect){
16517 if(this.multiSelect && e.shiftKey && this.lastSelection){
16518 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16520 this.select(item, this.multiSelect && e.ctrlKey);
16521 this.lastSelection = item;
16524 if(!this.tickable){
16525 e.preventDefault();
16533 * Get the number of selected nodes.
16536 getSelectionCount : function(){
16537 return this.selections.length;
16541 * Get the currently selected nodes.
16542 * @return {Array} An array of HTMLElements
16544 getSelectedNodes : function(){
16545 return this.selections;
16549 * Get the indexes of the selected nodes.
16552 getSelectedIndexes : function(){
16553 var indexes = [], s = this.selections;
16554 for(var i = 0, len = s.length; i < len; i++){
16555 indexes.push(s[i].nodeIndex);
16561 * Clear all selections
16562 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16564 clearSelections : function(suppressEvent){
16565 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16566 this.cmp.elements = this.selections;
16567 this.cmp.removeClass(this.selectedClass);
16568 this.selections = [];
16569 if(!suppressEvent){
16570 this.fireEvent("selectionchange", this, this.selections);
16576 * Returns true if the passed node is selected
16577 * @param {HTMLElement/Number} node The node or node index
16578 * @return {Boolean}
16580 isSelected : function(node){
16581 var s = this.selections;
16585 node = this.getNode(node);
16586 return s.indexOf(node) !== -1;
16591 * @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
16592 * @param {Boolean} keepExisting (optional) true to keep existing selections
16593 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16595 select : function(nodeInfo, keepExisting, suppressEvent){
16596 if(nodeInfo instanceof Array){
16598 this.clearSelections(true);
16600 for(var i = 0, len = nodeInfo.length; i < len; i++){
16601 this.select(nodeInfo[i], true, true);
16605 var node = this.getNode(nodeInfo);
16606 if(!node || this.isSelected(node)){
16607 return; // already selected.
16610 this.clearSelections(true);
16613 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16614 Roo.fly(node).addClass(this.selectedClass);
16615 this.selections.push(node);
16616 if(!suppressEvent){
16617 this.fireEvent("selectionchange", this, this.selections);
16625 * @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
16626 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16627 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16629 unselect : function(nodeInfo, keepExisting, suppressEvent)
16631 if(nodeInfo instanceof Array){
16632 Roo.each(this.selections, function(s) {
16633 this.unselect(s, nodeInfo);
16637 var node = this.getNode(nodeInfo);
16638 if(!node || !this.isSelected(node)){
16639 //Roo.log("not selected");
16640 return; // not selected.
16644 Roo.each(this.selections, function(s) {
16646 Roo.fly(node).removeClass(this.selectedClass);
16653 this.selections= ns;
16654 this.fireEvent("selectionchange", this, this.selections);
16658 * Gets a template node.
16659 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16660 * @return {HTMLElement} The node or null if it wasn't found
16662 getNode : function(nodeInfo){
16663 if(typeof nodeInfo == "string"){
16664 return document.getElementById(nodeInfo);
16665 }else if(typeof nodeInfo == "number"){
16666 return this.nodes[nodeInfo];
16672 * Gets a range template nodes.
16673 * @param {Number} startIndex
16674 * @param {Number} endIndex
16675 * @return {Array} An array of nodes
16677 getNodes : function(start, end){
16678 var ns = this.nodes;
16679 start = start || 0;
16680 end = typeof end == "undefined" ? ns.length - 1 : end;
16683 for(var i = start; i <= end; i++){
16687 for(var i = start; i >= end; i--){
16695 * Finds the index of the passed node
16696 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16697 * @return {Number} The index of the node or -1
16699 indexOf : function(node){
16700 node = this.getNode(node);
16701 if(typeof node.nodeIndex == "number"){
16702 return node.nodeIndex;
16704 var ns = this.nodes;
16705 for(var i = 0, len = ns.length; i < len; i++){
16716 * based on jquery fullcalendar
16720 Roo.bootstrap = Roo.bootstrap || {};
16722 * @class Roo.bootstrap.Calendar
16723 * @extends Roo.bootstrap.Component
16724 * Bootstrap Calendar class
16725 * @cfg {Boolean} loadMask (true|false) default false
16726 * @cfg {Object} header generate the user specific header of the calendar, default false
16729 * Create a new Container
16730 * @param {Object} config The config object
16735 Roo.bootstrap.Calendar = function(config){
16736 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16740 * Fires when a date is selected
16741 * @param {DatePicker} this
16742 * @param {Date} date The selected date
16746 * @event monthchange
16747 * Fires when the displayed month changes
16748 * @param {DatePicker} this
16749 * @param {Date} date The selected month
16751 'monthchange': true,
16753 * @event evententer
16754 * Fires when mouse over an event
16755 * @param {Calendar} this
16756 * @param {event} Event
16758 'evententer': true,
16760 * @event eventleave
16761 * Fires when the mouse leaves an
16762 * @param {Calendar} this
16765 'eventleave': true,
16767 * @event eventclick
16768 * Fires when the mouse click an
16769 * @param {Calendar} this
16778 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16781 * @cfg {Number} startDay
16782 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16790 getAutoCreate : function(){
16793 var fc_button = function(name, corner, style, content ) {
16794 return Roo.apply({},{
16796 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16798 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16801 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16812 style : 'width:100%',
16819 cls : 'fc-header-left',
16821 fc_button('prev', 'left', 'arrow', '‹' ),
16822 fc_button('next', 'right', 'arrow', '›' ),
16823 { tag: 'span', cls: 'fc-header-space' },
16824 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16832 cls : 'fc-header-center',
16836 cls: 'fc-header-title',
16839 html : 'month / year'
16847 cls : 'fc-header-right',
16849 /* fc_button('month', 'left', '', 'month' ),
16850 fc_button('week', '', '', 'week' ),
16851 fc_button('day', 'right', '', 'day' )
16863 header = this.header;
16866 var cal_heads = function() {
16868 // fixme - handle this.
16870 for (var i =0; i < Date.dayNames.length; i++) {
16871 var d = Date.dayNames[i];
16874 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16875 html : d.substring(0,3)
16879 ret[0].cls += ' fc-first';
16880 ret[6].cls += ' fc-last';
16883 var cal_cell = function(n) {
16886 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16891 cls: 'fc-day-number',
16895 cls: 'fc-day-content',
16899 style: 'position: relative;' // height: 17px;
16911 var cal_rows = function() {
16914 for (var r = 0; r < 6; r++) {
16921 for (var i =0; i < Date.dayNames.length; i++) {
16922 var d = Date.dayNames[i];
16923 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16926 row.cn[0].cls+=' fc-first';
16927 row.cn[0].cn[0].style = 'min-height:90px';
16928 row.cn[6].cls+=' fc-last';
16932 ret[0].cls += ' fc-first';
16933 ret[4].cls += ' fc-prev-last';
16934 ret[5].cls += ' fc-last';
16941 cls: 'fc-border-separate',
16942 style : 'width:100%',
16950 cls : 'fc-first fc-last',
16968 cls : 'fc-content',
16969 style : "position: relative;",
16972 cls : 'fc-view fc-view-month fc-grid',
16973 style : 'position: relative',
16974 unselectable : 'on',
16977 cls : 'fc-event-container',
16978 style : 'position:absolute;z-index:8;top:0;left:0;'
16996 initEvents : function()
16999 throw "can not find store for calendar";
17005 style: "text-align:center",
17009 style: "background-color:white;width:50%;margin:250 auto",
17013 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
17024 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17026 var size = this.el.select('.fc-content', true).first().getSize();
17027 this.maskEl.setSize(size.width, size.height);
17028 this.maskEl.enableDisplayMode("block");
17029 if(!this.loadMask){
17030 this.maskEl.hide();
17033 this.store = Roo.factory(this.store, Roo.data);
17034 this.store.on('load', this.onLoad, this);
17035 this.store.on('beforeload', this.onBeforeLoad, this);
17039 this.cells = this.el.select('.fc-day',true);
17040 //Roo.log(this.cells);
17041 this.textNodes = this.el.query('.fc-day-number');
17042 this.cells.addClassOnOver('fc-state-hover');
17044 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17045 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17046 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17047 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17049 this.on('monthchange', this.onMonthChange, this);
17051 this.update(new Date().clearTime());
17054 resize : function() {
17055 var sz = this.el.getSize();
17057 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17058 this.el.select('.fc-day-content div',true).setHeight(34);
17063 showPrevMonth : function(e){
17064 this.update(this.activeDate.add("mo", -1));
17066 showToday : function(e){
17067 this.update(new Date().clearTime());
17070 showNextMonth : function(e){
17071 this.update(this.activeDate.add("mo", 1));
17075 showPrevYear : function(){
17076 this.update(this.activeDate.add("y", -1));
17080 showNextYear : function(){
17081 this.update(this.activeDate.add("y", 1));
17086 update : function(date)
17088 var vd = this.activeDate;
17089 this.activeDate = date;
17090 // if(vd && this.el){
17091 // var t = date.getTime();
17092 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17093 // Roo.log('using add remove');
17095 // this.fireEvent('monthchange', this, date);
17097 // this.cells.removeClass("fc-state-highlight");
17098 // this.cells.each(function(c){
17099 // if(c.dateValue == t){
17100 // c.addClass("fc-state-highlight");
17101 // setTimeout(function(){
17102 // try{c.dom.firstChild.focus();}catch(e){}
17112 var days = date.getDaysInMonth();
17114 var firstOfMonth = date.getFirstDateOfMonth();
17115 var startingPos = firstOfMonth.getDay()-this.startDay;
17117 if(startingPos < this.startDay){
17121 var pm = date.add(Date.MONTH, -1);
17122 var prevStart = pm.getDaysInMonth()-startingPos;
17124 this.cells = this.el.select('.fc-day',true);
17125 this.textNodes = this.el.query('.fc-day-number');
17126 this.cells.addClassOnOver('fc-state-hover');
17128 var cells = this.cells.elements;
17129 var textEls = this.textNodes;
17131 Roo.each(cells, function(cell){
17132 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17135 days += startingPos;
17137 // convert everything to numbers so it's fast
17138 var day = 86400000;
17139 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17142 //Roo.log(prevStart);
17144 var today = new Date().clearTime().getTime();
17145 var sel = date.clearTime().getTime();
17146 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17147 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17148 var ddMatch = this.disabledDatesRE;
17149 var ddText = this.disabledDatesText;
17150 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17151 var ddaysText = this.disabledDaysText;
17152 var format = this.format;
17154 var setCellClass = function(cal, cell){
17158 //Roo.log('set Cell Class');
17160 var t = d.getTime();
17164 cell.dateValue = t;
17166 cell.className += " fc-today";
17167 cell.className += " fc-state-highlight";
17168 cell.title = cal.todayText;
17171 // disable highlight in other month..
17172 //cell.className += " fc-state-highlight";
17177 cell.className = " fc-state-disabled";
17178 cell.title = cal.minText;
17182 cell.className = " fc-state-disabled";
17183 cell.title = cal.maxText;
17187 if(ddays.indexOf(d.getDay()) != -1){
17188 cell.title = ddaysText;
17189 cell.className = " fc-state-disabled";
17192 if(ddMatch && format){
17193 var fvalue = d.dateFormat(format);
17194 if(ddMatch.test(fvalue)){
17195 cell.title = ddText.replace("%0", fvalue);
17196 cell.className = " fc-state-disabled";
17200 if (!cell.initialClassName) {
17201 cell.initialClassName = cell.dom.className;
17204 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17209 for(; i < startingPos; i++) {
17210 textEls[i].innerHTML = (++prevStart);
17211 d.setDate(d.getDate()+1);
17213 cells[i].className = "fc-past fc-other-month";
17214 setCellClass(this, cells[i]);
17219 for(; i < days; i++){
17220 intDay = i - startingPos + 1;
17221 textEls[i].innerHTML = (intDay);
17222 d.setDate(d.getDate()+1);
17224 cells[i].className = ''; // "x-date-active";
17225 setCellClass(this, cells[i]);
17229 for(; i < 42; i++) {
17230 textEls[i].innerHTML = (++extraDays);
17231 d.setDate(d.getDate()+1);
17233 cells[i].className = "fc-future fc-other-month";
17234 setCellClass(this, cells[i]);
17237 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17239 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17241 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17242 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17244 if(totalRows != 6){
17245 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17246 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17249 this.fireEvent('monthchange', this, date);
17253 if(!this.internalRender){
17254 var main = this.el.dom.firstChild;
17255 var w = main.offsetWidth;
17256 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17257 Roo.fly(main).setWidth(w);
17258 this.internalRender = true;
17259 // opera does not respect the auto grow header center column
17260 // then, after it gets a width opera refuses to recalculate
17261 // without a second pass
17262 if(Roo.isOpera && !this.secondPass){
17263 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17264 this.secondPass = true;
17265 this.update.defer(10, this, [date]);
17272 findCell : function(dt) {
17273 dt = dt.clearTime().getTime();
17275 this.cells.each(function(c){
17276 //Roo.log("check " +c.dateValue + '?=' + dt);
17277 if(c.dateValue == dt){
17287 findCells : function(ev) {
17288 var s = ev.start.clone().clearTime().getTime();
17290 var e= ev.end.clone().clearTime().getTime();
17293 this.cells.each(function(c){
17294 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17296 if(c.dateValue > e){
17299 if(c.dateValue < s){
17308 // findBestRow: function(cells)
17312 // for (var i =0 ; i < cells.length;i++) {
17313 // ret = Math.max(cells[i].rows || 0,ret);
17320 addItem : function(ev)
17322 // look for vertical location slot in
17323 var cells = this.findCells(ev);
17325 // ev.row = this.findBestRow(cells);
17327 // work out the location.
17331 for(var i =0; i < cells.length; i++) {
17333 cells[i].row = cells[0].row;
17336 cells[i].row = cells[i].row + 1;
17346 if (crow.start.getY() == cells[i].getY()) {
17348 crow.end = cells[i];
17365 cells[0].events.push(ev);
17367 this.calevents.push(ev);
17370 clearEvents: function() {
17372 if(!this.calevents){
17376 Roo.each(this.cells.elements, function(c){
17382 Roo.each(this.calevents, function(e) {
17383 Roo.each(e.els, function(el) {
17384 el.un('mouseenter' ,this.onEventEnter, this);
17385 el.un('mouseleave' ,this.onEventLeave, this);
17390 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17396 renderEvents: function()
17400 this.cells.each(function(c) {
17409 if(c.row != c.events.length){
17410 r = 4 - (4 - (c.row - c.events.length));
17413 c.events = ev.slice(0, r);
17414 c.more = ev.slice(r);
17416 if(c.more.length && c.more.length == 1){
17417 c.events.push(c.more.pop());
17420 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17424 this.cells.each(function(c) {
17426 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17429 for (var e = 0; e < c.events.length; e++){
17430 var ev = c.events[e];
17431 var rows = ev.rows;
17433 for(var i = 0; i < rows.length; i++) {
17435 // how many rows should it span..
17438 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17439 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17441 unselectable : "on",
17444 cls: 'fc-event-inner',
17448 // cls: 'fc-event-time',
17449 // html : cells.length > 1 ? '' : ev.time
17453 cls: 'fc-event-title',
17454 html : String.format('{0}', ev.title)
17461 cls: 'ui-resizable-handle ui-resizable-e',
17462 html : '  '
17469 cfg.cls += ' fc-event-start';
17471 if ((i+1) == rows.length) {
17472 cfg.cls += ' fc-event-end';
17475 var ctr = _this.el.select('.fc-event-container',true).first();
17476 var cg = ctr.createChild(cfg);
17478 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17479 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17481 var r = (c.more.length) ? 1 : 0;
17482 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17483 cg.setWidth(ebox.right - sbox.x -2);
17485 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17486 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17487 cg.on('click', _this.onEventClick, _this, ev);
17498 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17499 style : 'position: absolute',
17500 unselectable : "on",
17503 cls: 'fc-event-inner',
17507 cls: 'fc-event-title',
17515 cls: 'ui-resizable-handle ui-resizable-e',
17516 html : '  '
17522 var ctr = _this.el.select('.fc-event-container',true).first();
17523 var cg = ctr.createChild(cfg);
17525 var sbox = c.select('.fc-day-content',true).first().getBox();
17526 var ebox = c.select('.fc-day-content',true).first().getBox();
17528 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17529 cg.setWidth(ebox.right - sbox.x -2);
17531 cg.on('click', _this.onMoreEventClick, _this, c.more);
17541 onEventEnter: function (e, el,event,d) {
17542 this.fireEvent('evententer', this, el, event);
17545 onEventLeave: function (e, el,event,d) {
17546 this.fireEvent('eventleave', this, el, event);
17549 onEventClick: function (e, el,event,d) {
17550 this.fireEvent('eventclick', this, el, event);
17553 onMonthChange: function () {
17557 onMoreEventClick: function(e, el, more)
17561 this.calpopover.placement = 'right';
17562 this.calpopover.setTitle('More');
17564 this.calpopover.setContent('');
17566 var ctr = this.calpopover.el.select('.popover-content', true).first();
17568 Roo.each(more, function(m){
17570 cls : 'fc-event-hori fc-event-draggable',
17573 var cg = ctr.createChild(cfg);
17575 cg.on('click', _this.onEventClick, _this, m);
17578 this.calpopover.show(el);
17583 onLoad: function ()
17585 this.calevents = [];
17588 if(this.store.getCount() > 0){
17589 this.store.data.each(function(d){
17592 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17593 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17594 time : d.data.start_time,
17595 title : d.data.title,
17596 description : d.data.description,
17597 venue : d.data.venue
17602 this.renderEvents();
17604 if(this.calevents.length && this.loadMask){
17605 this.maskEl.hide();
17609 onBeforeLoad: function()
17611 this.clearEvents();
17613 this.maskEl.show();
17627 * @class Roo.bootstrap.Popover
17628 * @extends Roo.bootstrap.Component
17629 * Bootstrap Popover class
17630 * @cfg {String} html contents of the popover (or false to use children..)
17631 * @cfg {String} title of popover (or false to hide)
17632 * @cfg {String} placement how it is placed
17633 * @cfg {String} trigger click || hover (or false to trigger manually)
17634 * @cfg {String} over what (parent or false to trigger manually.)
17635 * @cfg {Number} delay - delay before showing
17638 * Create a new Popover
17639 * @param {Object} config The config object
17642 Roo.bootstrap.Popover = function(config){
17643 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17649 * After the popover show
17651 * @param {Roo.bootstrap.Popover} this
17656 * After the popover hide
17658 * @param {Roo.bootstrap.Popover} this
17664 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17666 title: 'Fill in a title',
17669 placement : 'right',
17670 trigger : 'hover', // hover
17676 can_build_overlaid : false,
17678 getChildContainer : function()
17680 return this.el.select('.popover-content',true).first();
17683 getAutoCreate : function(){
17686 cls : 'popover roo-dynamic',
17687 style: 'display:block',
17693 cls : 'popover-inner',
17697 cls: 'popover-title popover-header',
17701 cls : 'popover-content popover-body',
17712 setTitle: function(str)
17715 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17717 setContent: function(str)
17720 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17722 // as it get's added to the bottom of the page.
17723 onRender : function(ct, position)
17725 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17727 var cfg = Roo.apply({}, this.getAutoCreate());
17731 cfg.cls += ' ' + this.cls;
17734 cfg.style = this.style;
17736 //Roo.log("adding to ");
17737 this.el = Roo.get(document.body).createChild(cfg, position);
17738 // Roo.log(this.el);
17743 initEvents : function()
17745 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17746 this.el.enableDisplayMode('block');
17748 if (this.over === false) {
17751 if (this.triggers === false) {
17754 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17755 var triggers = this.trigger ? this.trigger.split(' ') : [];
17756 Roo.each(triggers, function(trigger) {
17758 if (trigger == 'click') {
17759 on_el.on('click', this.toggle, this);
17760 } else if (trigger != 'manual') {
17761 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17762 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17764 on_el.on(eventIn ,this.enter, this);
17765 on_el.on(eventOut, this.leave, this);
17776 toggle : function () {
17777 this.hoverState == 'in' ? this.leave() : this.enter();
17780 enter : function () {
17782 clearTimeout(this.timeout);
17784 this.hoverState = 'in';
17786 if (!this.delay || !this.delay.show) {
17791 this.timeout = setTimeout(function () {
17792 if (_t.hoverState == 'in') {
17795 }, this.delay.show)
17798 leave : function() {
17799 clearTimeout(this.timeout);
17801 this.hoverState = 'out';
17803 if (!this.delay || !this.delay.hide) {
17808 this.timeout = setTimeout(function () {
17809 if (_t.hoverState == 'out') {
17812 }, this.delay.hide)
17815 show : function (on_el)
17818 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17822 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17823 if (this.html !== false) {
17824 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17826 this.el.removeClass([
17827 'fade','top','bottom', 'left', 'right','in',
17828 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17830 if (!this.title.length) {
17831 this.el.select('.popover-title',true).hide();
17834 var placement = typeof this.placement == 'function' ?
17835 this.placement.call(this, this.el, on_el) :
17838 var autoToken = /\s?auto?\s?/i;
17839 var autoPlace = autoToken.test(placement);
17841 placement = placement.replace(autoToken, '') || 'top';
17845 //this.el.setXY([0,0]);
17847 this.el.dom.style.display='block';
17848 this.el.addClass(placement);
17850 //this.el.appendTo(on_el);
17852 var p = this.getPosition();
17853 var box = this.el.getBox();
17858 var align = Roo.bootstrap.Popover.alignment[placement];
17861 this.el.alignTo(on_el, align[0],align[1]);
17862 //var arrow = this.el.select('.arrow',true).first();
17863 //arrow.set(align[2],
17865 this.el.addClass('in');
17868 if (this.el.hasClass('fade')) {
17872 this.hoverState = 'in';
17874 this.fireEvent('show', this);
17879 this.el.setXY([0,0]);
17880 this.el.removeClass('in');
17882 this.hoverState = null;
17884 this.fireEvent('hide', this);
17889 Roo.bootstrap.Popover.alignment = {
17890 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17891 'right' : ['l-r', [10,0], 'left bs-popover-left'],
17892 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17893 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17904 * @class Roo.bootstrap.Progress
17905 * @extends Roo.bootstrap.Component
17906 * Bootstrap Progress class
17907 * @cfg {Boolean} striped striped of the progress bar
17908 * @cfg {Boolean} active animated of the progress bar
17912 * Create a new Progress
17913 * @param {Object} config The config object
17916 Roo.bootstrap.Progress = function(config){
17917 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17920 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17925 getAutoCreate : function(){
17933 cfg.cls += ' progress-striped';
17937 cfg.cls += ' active';
17956 * @class Roo.bootstrap.ProgressBar
17957 * @extends Roo.bootstrap.Component
17958 * Bootstrap ProgressBar class
17959 * @cfg {Number} aria_valuenow aria-value now
17960 * @cfg {Number} aria_valuemin aria-value min
17961 * @cfg {Number} aria_valuemax aria-value max
17962 * @cfg {String} label label for the progress bar
17963 * @cfg {String} panel (success | info | warning | danger )
17964 * @cfg {String} role role of the progress bar
17965 * @cfg {String} sr_only text
17969 * Create a new ProgressBar
17970 * @param {Object} config The config object
17973 Roo.bootstrap.ProgressBar = function(config){
17974 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17977 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17981 aria_valuemax : 100,
17987 getAutoCreate : function()
17992 cls: 'progress-bar',
17993 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18005 cfg.role = this.role;
18008 if(this.aria_valuenow){
18009 cfg['aria-valuenow'] = this.aria_valuenow;
18012 if(this.aria_valuemin){
18013 cfg['aria-valuemin'] = this.aria_valuemin;
18016 if(this.aria_valuemax){
18017 cfg['aria-valuemax'] = this.aria_valuemax;
18020 if(this.label && !this.sr_only){
18021 cfg.html = this.label;
18025 cfg.cls += ' progress-bar-' + this.panel;
18031 update : function(aria_valuenow)
18033 this.aria_valuenow = aria_valuenow;
18035 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18050 * @class Roo.bootstrap.TabGroup
18051 * @extends Roo.bootstrap.Column
18052 * Bootstrap Column class
18053 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18054 * @cfg {Boolean} carousel true to make the group behave like a carousel
18055 * @cfg {Boolean} bullets show bullets for the panels
18056 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18057 * @cfg {Number} timer auto slide timer .. default 0 millisecond
18058 * @cfg {Boolean} showarrow (true|false) show arrow default true
18061 * Create a new TabGroup
18062 * @param {Object} config The config object
18065 Roo.bootstrap.TabGroup = function(config){
18066 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18068 this.navId = Roo.id();
18071 Roo.bootstrap.TabGroup.register(this);
18075 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18078 transition : false,
18083 slideOnTouch : false,
18086 getAutoCreate : function()
18088 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18090 cfg.cls += ' tab-content';
18092 if (this.carousel) {
18093 cfg.cls += ' carousel slide';
18096 cls : 'carousel-inner',
18100 if(this.bullets && !Roo.isTouch){
18103 cls : 'carousel-bullets',
18107 if(this.bullets_cls){
18108 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18115 cfg.cn[0].cn.push(bullets);
18118 if(this.showarrow){
18119 cfg.cn[0].cn.push({
18121 class : 'carousel-arrow',
18125 class : 'carousel-prev',
18129 class : 'fa fa-chevron-left'
18135 class : 'carousel-next',
18139 class : 'fa fa-chevron-right'
18152 initEvents: function()
18154 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18155 // this.el.on("touchstart", this.onTouchStart, this);
18158 if(this.autoslide){
18161 this.slideFn = window.setInterval(function() {
18162 _this.showPanelNext();
18166 if(this.showarrow){
18167 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18168 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18174 // onTouchStart : function(e, el, o)
18176 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18180 // this.showPanelNext();
18184 getChildContainer : function()
18186 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18190 * register a Navigation item
18191 * @param {Roo.bootstrap.NavItem} the navitem to add
18193 register : function(item)
18195 this.tabs.push( item);
18196 item.navId = this.navId; // not really needed..
18201 getActivePanel : function()
18204 Roo.each(this.tabs, function(t) {
18214 getPanelByName : function(n)
18217 Roo.each(this.tabs, function(t) {
18218 if (t.tabId == n) {
18226 indexOfPanel : function(p)
18229 Roo.each(this.tabs, function(t,i) {
18230 if (t.tabId == p.tabId) {
18239 * show a specific panel
18240 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18241 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18243 showPanel : function (pan)
18245 if(this.transition || typeof(pan) == 'undefined'){
18246 Roo.log("waiting for the transitionend");
18250 if (typeof(pan) == 'number') {
18251 pan = this.tabs[pan];
18254 if (typeof(pan) == 'string') {
18255 pan = this.getPanelByName(pan);
18258 var cur = this.getActivePanel();
18261 Roo.log('pan or acitve pan is undefined');
18265 if (pan.tabId == this.getActivePanel().tabId) {
18269 if (false === cur.fireEvent('beforedeactivate')) {
18273 if(this.bullets > 0 && !Roo.isTouch){
18274 this.setActiveBullet(this.indexOfPanel(pan));
18277 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18279 this.transition = true;
18280 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18281 var lr = dir == 'next' ? 'left' : 'right';
18282 pan.el.addClass(dir); // or prev
18283 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18284 cur.el.addClass(lr); // or right
18285 pan.el.addClass(lr);
18288 cur.el.on('transitionend', function() {
18289 Roo.log("trans end?");
18291 pan.el.removeClass([lr,dir]);
18292 pan.setActive(true);
18294 cur.el.removeClass([lr]);
18295 cur.setActive(false);
18297 _this.transition = false;
18299 }, this, { single: true } );
18304 cur.setActive(false);
18305 pan.setActive(true);
18310 showPanelNext : function()
18312 var i = this.indexOfPanel(this.getActivePanel());
18314 if (i >= this.tabs.length - 1 && !this.autoslide) {
18318 if (i >= this.tabs.length - 1 && this.autoslide) {
18322 this.showPanel(this.tabs[i+1]);
18325 showPanelPrev : function()
18327 var i = this.indexOfPanel(this.getActivePanel());
18329 if (i < 1 && !this.autoslide) {
18333 if (i < 1 && this.autoslide) {
18334 i = this.tabs.length;
18337 this.showPanel(this.tabs[i-1]);
18341 addBullet: function()
18343 if(!this.bullets || Roo.isTouch){
18346 var ctr = this.el.select('.carousel-bullets',true).first();
18347 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18348 var bullet = ctr.createChild({
18349 cls : 'bullet bullet-' + i
18350 },ctr.dom.lastChild);
18355 bullet.on('click', (function(e, el, o, ii, t){
18357 e.preventDefault();
18359 this.showPanel(ii);
18361 if(this.autoslide && this.slideFn){
18362 clearInterval(this.slideFn);
18363 this.slideFn = window.setInterval(function() {
18364 _this.showPanelNext();
18368 }).createDelegate(this, [i, bullet], true));
18373 setActiveBullet : function(i)
18379 Roo.each(this.el.select('.bullet', true).elements, function(el){
18380 el.removeClass('selected');
18383 var bullet = this.el.select('.bullet-' + i, true).first();
18389 bullet.addClass('selected');
18400 Roo.apply(Roo.bootstrap.TabGroup, {
18404 * register a Navigation Group
18405 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18407 register : function(navgrp)
18409 this.groups[navgrp.navId] = navgrp;
18413 * fetch a Navigation Group based on the navigation ID
18414 * if one does not exist , it will get created.
18415 * @param {string} the navgroup to add
18416 * @returns {Roo.bootstrap.NavGroup} the navgroup
18418 get: function(navId) {
18419 if (typeof(this.groups[navId]) == 'undefined') {
18420 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18422 return this.groups[navId] ;
18437 * @class Roo.bootstrap.TabPanel
18438 * @extends Roo.bootstrap.Component
18439 * Bootstrap TabPanel class
18440 * @cfg {Boolean} active panel active
18441 * @cfg {String} html panel content
18442 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18443 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18444 * @cfg {String} href click to link..
18448 * Create a new TabPanel
18449 * @param {Object} config The config object
18452 Roo.bootstrap.TabPanel = function(config){
18453 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18457 * Fires when the active status changes
18458 * @param {Roo.bootstrap.TabPanel} this
18459 * @param {Boolean} state the new state
18464 * @event beforedeactivate
18465 * Fires before a tab is de-activated - can be used to do validation on a form.
18466 * @param {Roo.bootstrap.TabPanel} this
18467 * @return {Boolean} false if there is an error
18470 'beforedeactivate': true
18473 this.tabId = this.tabId || Roo.id();
18477 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18485 getAutoCreate : function(){
18488 // item is needed for carousel - not sure if it has any effect otherwise
18489 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18490 html: this.html || ''
18494 cfg.cls += ' active';
18498 cfg.tabId = this.tabId;
18505 initEvents: function()
18507 var p = this.parent();
18509 this.navId = this.navId || p.navId;
18511 if (typeof(this.navId) != 'undefined') {
18512 // not really needed.. but just in case.. parent should be a NavGroup.
18513 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18517 var i = tg.tabs.length - 1;
18519 if(this.active && tg.bullets > 0 && i < tg.bullets){
18520 tg.setActiveBullet(i);
18524 this.el.on('click', this.onClick, this);
18527 this.el.on("touchstart", this.onTouchStart, this);
18528 this.el.on("touchmove", this.onTouchMove, this);
18529 this.el.on("touchend", this.onTouchEnd, this);
18534 onRender : function(ct, position)
18536 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18539 setActive : function(state)
18541 Roo.log("panel - set active " + this.tabId + "=" + state);
18543 this.active = state;
18545 this.el.removeClass('active');
18547 } else if (!this.el.hasClass('active')) {
18548 this.el.addClass('active');
18551 this.fireEvent('changed', this, state);
18554 onClick : function(e)
18556 e.preventDefault();
18558 if(!this.href.length){
18562 window.location.href = this.href;
18571 onTouchStart : function(e)
18573 this.swiping = false;
18575 this.startX = e.browserEvent.touches[0].clientX;
18576 this.startY = e.browserEvent.touches[0].clientY;
18579 onTouchMove : function(e)
18581 this.swiping = true;
18583 this.endX = e.browserEvent.touches[0].clientX;
18584 this.endY = e.browserEvent.touches[0].clientY;
18587 onTouchEnd : function(e)
18594 var tabGroup = this.parent();
18596 if(this.endX > this.startX){ // swiping right
18597 tabGroup.showPanelPrev();
18601 if(this.startX > this.endX){ // swiping left
18602 tabGroup.showPanelNext();
18621 * @class Roo.bootstrap.DateField
18622 * @extends Roo.bootstrap.Input
18623 * Bootstrap DateField class
18624 * @cfg {Number} weekStart default 0
18625 * @cfg {String} viewMode default empty, (months|years)
18626 * @cfg {String} minViewMode default empty, (months|years)
18627 * @cfg {Number} startDate default -Infinity
18628 * @cfg {Number} endDate default Infinity
18629 * @cfg {Boolean} todayHighlight default false
18630 * @cfg {Boolean} todayBtn default false
18631 * @cfg {Boolean} calendarWeeks default false
18632 * @cfg {Object} daysOfWeekDisabled default empty
18633 * @cfg {Boolean} singleMode default false (true | false)
18635 * @cfg {Boolean} keyboardNavigation default true
18636 * @cfg {String} language default en
18639 * Create a new DateField
18640 * @param {Object} config The config object
18643 Roo.bootstrap.DateField = function(config){
18644 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18648 * Fires when this field show.
18649 * @param {Roo.bootstrap.DateField} this
18650 * @param {Mixed} date The date value
18655 * Fires when this field hide.
18656 * @param {Roo.bootstrap.DateField} this
18657 * @param {Mixed} date The date value
18662 * Fires when select a date.
18663 * @param {Roo.bootstrap.DateField} this
18664 * @param {Mixed} date The date value
18668 * @event beforeselect
18669 * Fires when before select a date.
18670 * @param {Roo.bootstrap.DateField} this
18671 * @param {Mixed} date The date value
18673 beforeselect : true
18677 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18680 * @cfg {String} format
18681 * The default date format string which can be overriden for localization support. The format must be
18682 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18686 * @cfg {String} altFormats
18687 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18688 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18690 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18698 todayHighlight : false,
18704 keyboardNavigation: true,
18706 calendarWeeks: false,
18708 startDate: -Infinity,
18712 daysOfWeekDisabled: [],
18716 singleMode : false,
18718 UTCDate: function()
18720 return new Date(Date.UTC.apply(Date, arguments));
18723 UTCToday: function()
18725 var today = new Date();
18726 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18729 getDate: function() {
18730 var d = this.getUTCDate();
18731 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18734 getUTCDate: function() {
18738 setDate: function(d) {
18739 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18742 setUTCDate: function(d) {
18744 this.setValue(this.formatDate(this.date));
18747 onRender: function(ct, position)
18750 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18752 this.language = this.language || 'en';
18753 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18754 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18756 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18757 this.format = this.format || 'm/d/y';
18758 this.isInline = false;
18759 this.isInput = true;
18760 this.component = this.el.select('.add-on', true).first() || false;
18761 this.component = (this.component && this.component.length === 0) ? false : this.component;
18762 this.hasInput = this.component && this.inputEl().length;
18764 if (typeof(this.minViewMode === 'string')) {
18765 switch (this.minViewMode) {
18767 this.minViewMode = 1;
18770 this.minViewMode = 2;
18773 this.minViewMode = 0;
18778 if (typeof(this.viewMode === 'string')) {
18779 switch (this.viewMode) {
18792 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18794 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18796 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18798 this.picker().on('mousedown', this.onMousedown, this);
18799 this.picker().on('click', this.onClick, this);
18801 this.picker().addClass('datepicker-dropdown');
18803 this.startViewMode = this.viewMode;
18805 if(this.singleMode){
18806 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18807 v.setVisibilityMode(Roo.Element.DISPLAY);
18811 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18812 v.setStyle('width', '189px');
18816 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18817 if(!this.calendarWeeks){
18822 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18823 v.attr('colspan', function(i, val){
18824 return parseInt(val) + 1;
18829 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18831 this.setStartDate(this.startDate);
18832 this.setEndDate(this.endDate);
18834 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18841 if(this.isInline) {
18846 picker : function()
18848 return this.pickerEl;
18849 // return this.el.select('.datepicker', true).first();
18852 fillDow: function()
18854 var dowCnt = this.weekStart;
18863 if(this.calendarWeeks){
18871 while (dowCnt < this.weekStart + 7) {
18875 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18879 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18882 fillMonths: function()
18885 var months = this.picker().select('>.datepicker-months td', true).first();
18887 months.dom.innerHTML = '';
18893 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18896 months.createChild(month);
18903 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;
18905 if (this.date < this.startDate) {
18906 this.viewDate = new Date(this.startDate);
18907 } else if (this.date > this.endDate) {
18908 this.viewDate = new Date(this.endDate);
18910 this.viewDate = new Date(this.date);
18918 var d = new Date(this.viewDate),
18919 year = d.getUTCFullYear(),
18920 month = d.getUTCMonth(),
18921 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18922 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18923 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18924 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18925 currentDate = this.date && this.date.valueOf(),
18926 today = this.UTCToday();
18928 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18930 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18932 // this.picker.select('>tfoot th.today').
18933 // .text(dates[this.language].today)
18934 // .toggle(this.todayBtn !== false);
18936 this.updateNavArrows();
18939 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18941 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18943 prevMonth.setUTCDate(day);
18945 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18947 var nextMonth = new Date(prevMonth);
18949 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18951 nextMonth = nextMonth.valueOf();
18953 var fillMonths = false;
18955 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18957 while(prevMonth.valueOf() <= nextMonth) {
18960 if (prevMonth.getUTCDay() === this.weekStart) {
18962 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18970 if(this.calendarWeeks){
18971 // ISO 8601: First week contains first thursday.
18972 // ISO also states week starts on Monday, but we can be more abstract here.
18974 // Start of current week: based on weekstart/current date
18975 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18976 // Thursday of this week
18977 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18978 // First Thursday of year, year from thursday
18979 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18980 // Calendar week: ms between thursdays, div ms per day, div 7 days
18981 calWeek = (th - yth) / 864e5 / 7 + 1;
18983 fillMonths.cn.push({
18991 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18993 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18996 if (this.todayHighlight &&
18997 prevMonth.getUTCFullYear() == today.getFullYear() &&
18998 prevMonth.getUTCMonth() == today.getMonth() &&
18999 prevMonth.getUTCDate() == today.getDate()) {
19000 clsName += ' today';
19003 if (currentDate && prevMonth.valueOf() === currentDate) {
19004 clsName += ' active';
19007 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19008 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19009 clsName += ' disabled';
19012 fillMonths.cn.push({
19014 cls: 'day ' + clsName,
19015 html: prevMonth.getDate()
19018 prevMonth.setDate(prevMonth.getDate()+1);
19021 var currentYear = this.date && this.date.getUTCFullYear();
19022 var currentMonth = this.date && this.date.getUTCMonth();
19024 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19026 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19027 v.removeClass('active');
19029 if(currentYear === year && k === currentMonth){
19030 v.addClass('active');
19033 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19034 v.addClass('disabled');
19040 year = parseInt(year/10, 10) * 10;
19042 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19044 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19047 for (var i = -1; i < 11; i++) {
19048 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19050 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19058 showMode: function(dir)
19061 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19064 Roo.each(this.picker().select('>div',true).elements, function(v){
19065 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19068 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19073 if(this.isInline) {
19077 this.picker().removeClass(['bottom', 'top']);
19079 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19081 * place to the top of element!
19085 this.picker().addClass('top');
19086 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19091 this.picker().addClass('bottom');
19093 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19096 parseDate : function(value)
19098 if(!value || value instanceof Date){
19101 var v = Date.parseDate(value, this.format);
19102 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19103 v = Date.parseDate(value, 'Y-m-d');
19105 if(!v && this.altFormats){
19106 if(!this.altFormatsArray){
19107 this.altFormatsArray = this.altFormats.split("|");
19109 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19110 v = Date.parseDate(value, this.altFormatsArray[i]);
19116 formatDate : function(date, fmt)
19118 return (!date || !(date instanceof Date)) ?
19119 date : date.dateFormat(fmt || this.format);
19122 onFocus : function()
19124 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19128 onBlur : function()
19130 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19132 var d = this.inputEl().getValue();
19139 showPopup : function()
19141 this.picker().show();
19145 this.fireEvent('showpopup', this, this.date);
19148 hidePopup : function()
19150 if(this.isInline) {
19153 this.picker().hide();
19154 this.viewMode = this.startViewMode;
19157 this.fireEvent('hidepopup', this, this.date);
19161 onMousedown: function(e)
19163 e.stopPropagation();
19164 e.preventDefault();
19169 Roo.bootstrap.DateField.superclass.keyup.call(this);
19173 setValue: function(v)
19175 if(this.fireEvent('beforeselect', this, v) !== false){
19176 var d = new Date(this.parseDate(v) ).clearTime();
19178 if(isNaN(d.getTime())){
19179 this.date = this.viewDate = '';
19180 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19184 v = this.formatDate(d);
19186 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19188 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19192 this.fireEvent('select', this, this.date);
19196 getValue: function()
19198 return this.formatDate(this.date);
19201 fireKey: function(e)
19203 if (!this.picker().isVisible()){
19204 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19210 var dateChanged = false,
19212 newDate, newViewDate;
19217 e.preventDefault();
19221 if (!this.keyboardNavigation) {
19224 dir = e.keyCode == 37 ? -1 : 1;
19227 newDate = this.moveYear(this.date, dir);
19228 newViewDate = this.moveYear(this.viewDate, dir);
19229 } else if (e.shiftKey){
19230 newDate = this.moveMonth(this.date, dir);
19231 newViewDate = this.moveMonth(this.viewDate, dir);
19233 newDate = new Date(this.date);
19234 newDate.setUTCDate(this.date.getUTCDate() + dir);
19235 newViewDate = new Date(this.viewDate);
19236 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19238 if (this.dateWithinRange(newDate)){
19239 this.date = newDate;
19240 this.viewDate = newViewDate;
19241 this.setValue(this.formatDate(this.date));
19243 e.preventDefault();
19244 dateChanged = true;
19249 if (!this.keyboardNavigation) {
19252 dir = e.keyCode == 38 ? -1 : 1;
19254 newDate = this.moveYear(this.date, dir);
19255 newViewDate = this.moveYear(this.viewDate, dir);
19256 } else if (e.shiftKey){
19257 newDate = this.moveMonth(this.date, dir);
19258 newViewDate = this.moveMonth(this.viewDate, dir);
19260 newDate = new Date(this.date);
19261 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19262 newViewDate = new Date(this.viewDate);
19263 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19265 if (this.dateWithinRange(newDate)){
19266 this.date = newDate;
19267 this.viewDate = newViewDate;
19268 this.setValue(this.formatDate(this.date));
19270 e.preventDefault();
19271 dateChanged = true;
19275 this.setValue(this.formatDate(this.date));
19277 e.preventDefault();
19280 this.setValue(this.formatDate(this.date));
19294 onClick: function(e)
19296 e.stopPropagation();
19297 e.preventDefault();
19299 var target = e.getTarget();
19301 if(target.nodeName.toLowerCase() === 'i'){
19302 target = Roo.get(target).dom.parentNode;
19305 var nodeName = target.nodeName;
19306 var className = target.className;
19307 var html = target.innerHTML;
19308 //Roo.log(nodeName);
19310 switch(nodeName.toLowerCase()) {
19312 switch(className) {
19318 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19319 switch(this.viewMode){
19321 this.viewDate = this.moveMonth(this.viewDate, dir);
19325 this.viewDate = this.moveYear(this.viewDate, dir);
19331 var date = new Date();
19332 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19334 this.setValue(this.formatDate(this.date));
19341 if (className.indexOf('disabled') < 0) {
19342 this.viewDate.setUTCDate(1);
19343 if (className.indexOf('month') > -1) {
19344 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19346 var year = parseInt(html, 10) || 0;
19347 this.viewDate.setUTCFullYear(year);
19351 if(this.singleMode){
19352 this.setValue(this.formatDate(this.viewDate));
19363 //Roo.log(className);
19364 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19365 var day = parseInt(html, 10) || 1;
19366 var year = this.viewDate.getUTCFullYear(),
19367 month = this.viewDate.getUTCMonth();
19369 if (className.indexOf('old') > -1) {
19376 } else if (className.indexOf('new') > -1) {
19384 //Roo.log([year,month,day]);
19385 this.date = this.UTCDate(year, month, day,0,0,0,0);
19386 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19388 //Roo.log(this.formatDate(this.date));
19389 this.setValue(this.formatDate(this.date));
19396 setStartDate: function(startDate)
19398 this.startDate = startDate || -Infinity;
19399 if (this.startDate !== -Infinity) {
19400 this.startDate = this.parseDate(this.startDate);
19403 this.updateNavArrows();
19406 setEndDate: function(endDate)
19408 this.endDate = endDate || Infinity;
19409 if (this.endDate !== Infinity) {
19410 this.endDate = this.parseDate(this.endDate);
19413 this.updateNavArrows();
19416 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19418 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19419 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19420 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19422 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19423 return parseInt(d, 10);
19426 this.updateNavArrows();
19429 updateNavArrows: function()
19431 if(this.singleMode){
19435 var d = new Date(this.viewDate),
19436 year = d.getUTCFullYear(),
19437 month = d.getUTCMonth();
19439 Roo.each(this.picker().select('.prev', true).elements, function(v){
19441 switch (this.viewMode) {
19444 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19450 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19457 Roo.each(this.picker().select('.next', true).elements, function(v){
19459 switch (this.viewMode) {
19462 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19468 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19476 moveMonth: function(date, dir)
19481 var new_date = new Date(date.valueOf()),
19482 day = new_date.getUTCDate(),
19483 month = new_date.getUTCMonth(),
19484 mag = Math.abs(dir),
19486 dir = dir > 0 ? 1 : -1;
19489 // If going back one month, make sure month is not current month
19490 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19492 return new_date.getUTCMonth() == month;
19494 // If going forward one month, make sure month is as expected
19495 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19497 return new_date.getUTCMonth() != new_month;
19499 new_month = month + dir;
19500 new_date.setUTCMonth(new_month);
19501 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19502 if (new_month < 0 || new_month > 11) {
19503 new_month = (new_month + 12) % 12;
19506 // For magnitudes >1, move one month at a time...
19507 for (var i=0; i<mag; i++) {
19508 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19509 new_date = this.moveMonth(new_date, dir);
19511 // ...then reset the day, keeping it in the new month
19512 new_month = new_date.getUTCMonth();
19513 new_date.setUTCDate(day);
19515 return new_month != new_date.getUTCMonth();
19518 // Common date-resetting loop -- if date is beyond end of month, make it
19521 new_date.setUTCDate(--day);
19522 new_date.setUTCMonth(new_month);
19527 moveYear: function(date, dir)
19529 return this.moveMonth(date, dir*12);
19532 dateWithinRange: function(date)
19534 return date >= this.startDate && date <= this.endDate;
19540 this.picker().remove();
19543 validateValue : function(value)
19545 if(this.getVisibilityEl().hasClass('hidden')){
19549 if(value.length < 1) {
19550 if(this.allowBlank){
19556 if(value.length < this.minLength){
19559 if(value.length > this.maxLength){
19563 var vt = Roo.form.VTypes;
19564 if(!vt[this.vtype](value, this)){
19568 if(typeof this.validator == "function"){
19569 var msg = this.validator(value);
19575 if(this.regex && !this.regex.test(value)){
19579 if(typeof(this.parseDate(value)) == 'undefined'){
19583 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19587 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19597 this.date = this.viewDate = '';
19599 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19604 Roo.apply(Roo.bootstrap.DateField, {
19615 html: '<i class="fa fa-arrow-left"/>'
19625 html: '<i class="fa fa-arrow-right"/>'
19667 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19668 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19669 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19670 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19671 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19684 navFnc: 'FullYear',
19689 navFnc: 'FullYear',
19694 Roo.apply(Roo.bootstrap.DateField, {
19698 cls: 'datepicker dropdown-menu roo-dynamic',
19702 cls: 'datepicker-days',
19706 cls: 'table-condensed',
19708 Roo.bootstrap.DateField.head,
19712 Roo.bootstrap.DateField.footer
19719 cls: 'datepicker-months',
19723 cls: 'table-condensed',
19725 Roo.bootstrap.DateField.head,
19726 Roo.bootstrap.DateField.content,
19727 Roo.bootstrap.DateField.footer
19734 cls: 'datepicker-years',
19738 cls: 'table-condensed',
19740 Roo.bootstrap.DateField.head,
19741 Roo.bootstrap.DateField.content,
19742 Roo.bootstrap.DateField.footer
19761 * @class Roo.bootstrap.TimeField
19762 * @extends Roo.bootstrap.Input
19763 * Bootstrap DateField class
19767 * Create a new TimeField
19768 * @param {Object} config The config object
19771 Roo.bootstrap.TimeField = function(config){
19772 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19776 * Fires when this field show.
19777 * @param {Roo.bootstrap.DateField} thisthis
19778 * @param {Mixed} date The date value
19783 * Fires when this field hide.
19784 * @param {Roo.bootstrap.DateField} this
19785 * @param {Mixed} date The date value
19790 * Fires when select a date.
19791 * @param {Roo.bootstrap.DateField} this
19792 * @param {Mixed} date The date value
19798 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19801 * @cfg {String} format
19802 * The default time format string which can be overriden for localization support. The format must be
19803 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19807 onRender: function(ct, position)
19810 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19812 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19814 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19816 this.pop = this.picker().select('>.datepicker-time',true).first();
19817 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19819 this.picker().on('mousedown', this.onMousedown, this);
19820 this.picker().on('click', this.onClick, this);
19822 this.picker().addClass('datepicker-dropdown');
19827 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19828 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19829 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19830 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19831 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19832 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19836 fireKey: function(e){
19837 if (!this.picker().isVisible()){
19838 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19844 e.preventDefault();
19852 this.onTogglePeriod();
19855 this.onIncrementMinutes();
19858 this.onDecrementMinutes();
19867 onClick: function(e) {
19868 e.stopPropagation();
19869 e.preventDefault();
19872 picker : function()
19874 return this.el.select('.datepicker', true).first();
19877 fillTime: function()
19879 var time = this.pop.select('tbody', true).first();
19881 time.dom.innerHTML = '';
19896 cls: 'hours-up glyphicon glyphicon-chevron-up'
19916 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19937 cls: 'timepicker-hour',
19952 cls: 'timepicker-minute',
19967 cls: 'btn btn-primary period',
19989 cls: 'hours-down glyphicon glyphicon-chevron-down'
20009 cls: 'minutes-down glyphicon glyphicon-chevron-down'
20027 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20034 var hours = this.time.getHours();
20035 var minutes = this.time.getMinutes();
20048 hours = hours - 12;
20052 hours = '0' + hours;
20056 minutes = '0' + minutes;
20059 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20060 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20061 this.pop.select('button', true).first().dom.innerHTML = period;
20067 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20069 var cls = ['bottom'];
20071 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20078 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20083 this.picker().addClass(cls.join('-'));
20087 Roo.each(cls, function(c){
20089 _this.picker().setTop(_this.inputEl().getHeight());
20093 _this.picker().setTop(0 - _this.picker().getHeight());
20098 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20102 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20109 onFocus : function()
20111 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20115 onBlur : function()
20117 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20123 this.picker().show();
20128 this.fireEvent('show', this, this.date);
20133 this.picker().hide();
20136 this.fireEvent('hide', this, this.date);
20139 setTime : function()
20142 this.setValue(this.time.format(this.format));
20144 this.fireEvent('select', this, this.date);
20149 onMousedown: function(e){
20150 e.stopPropagation();
20151 e.preventDefault();
20154 onIncrementHours: function()
20156 Roo.log('onIncrementHours');
20157 this.time = this.time.add(Date.HOUR, 1);
20162 onDecrementHours: function()
20164 Roo.log('onDecrementHours');
20165 this.time = this.time.add(Date.HOUR, -1);
20169 onIncrementMinutes: function()
20171 Roo.log('onIncrementMinutes');
20172 this.time = this.time.add(Date.MINUTE, 1);
20176 onDecrementMinutes: function()
20178 Roo.log('onDecrementMinutes');
20179 this.time = this.time.add(Date.MINUTE, -1);
20183 onTogglePeriod: function()
20185 Roo.log('onTogglePeriod');
20186 this.time = this.time.add(Date.HOUR, 12);
20193 Roo.apply(Roo.bootstrap.TimeField, {
20223 cls: 'btn btn-info ok',
20235 Roo.apply(Roo.bootstrap.TimeField, {
20239 cls: 'datepicker dropdown-menu',
20243 cls: 'datepicker-time',
20247 cls: 'table-condensed',
20249 Roo.bootstrap.TimeField.content,
20250 Roo.bootstrap.TimeField.footer
20269 * @class Roo.bootstrap.MonthField
20270 * @extends Roo.bootstrap.Input
20271 * Bootstrap MonthField class
20273 * @cfg {String} language default en
20276 * Create a new MonthField
20277 * @param {Object} config The config object
20280 Roo.bootstrap.MonthField = function(config){
20281 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20286 * Fires when this field show.
20287 * @param {Roo.bootstrap.MonthField} this
20288 * @param {Mixed} date The date value
20293 * Fires when this field hide.
20294 * @param {Roo.bootstrap.MonthField} this
20295 * @param {Mixed} date The date value
20300 * Fires when select a date.
20301 * @param {Roo.bootstrap.MonthField} this
20302 * @param {String} oldvalue The old value
20303 * @param {String} newvalue The new value
20309 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20311 onRender: function(ct, position)
20314 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20316 this.language = this.language || 'en';
20317 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20318 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20320 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20321 this.isInline = false;
20322 this.isInput = true;
20323 this.component = this.el.select('.add-on', true).first() || false;
20324 this.component = (this.component && this.component.length === 0) ? false : this.component;
20325 this.hasInput = this.component && this.inputEL().length;
20327 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20329 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20331 this.picker().on('mousedown', this.onMousedown, this);
20332 this.picker().on('click', this.onClick, this);
20334 this.picker().addClass('datepicker-dropdown');
20336 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20337 v.setStyle('width', '189px');
20344 if(this.isInline) {
20350 setValue: function(v, suppressEvent)
20352 var o = this.getValue();
20354 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20358 if(suppressEvent !== true){
20359 this.fireEvent('select', this, o, v);
20364 getValue: function()
20369 onClick: function(e)
20371 e.stopPropagation();
20372 e.preventDefault();
20374 var target = e.getTarget();
20376 if(target.nodeName.toLowerCase() === 'i'){
20377 target = Roo.get(target).dom.parentNode;
20380 var nodeName = target.nodeName;
20381 var className = target.className;
20382 var html = target.innerHTML;
20384 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20388 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20390 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20396 picker : function()
20398 return this.pickerEl;
20401 fillMonths: function()
20404 var months = this.picker().select('>.datepicker-months td', true).first();
20406 months.dom.innerHTML = '';
20412 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20415 months.createChild(month);
20424 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20425 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20428 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20429 e.removeClass('active');
20431 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20432 e.addClass('active');
20439 if(this.isInline) {
20443 this.picker().removeClass(['bottom', 'top']);
20445 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20447 * place to the top of element!
20451 this.picker().addClass('top');
20452 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20457 this.picker().addClass('bottom');
20459 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20462 onFocus : function()
20464 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20468 onBlur : function()
20470 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20472 var d = this.inputEl().getValue();
20481 this.picker().show();
20482 this.picker().select('>.datepicker-months', true).first().show();
20486 this.fireEvent('show', this, this.date);
20491 if(this.isInline) {
20494 this.picker().hide();
20495 this.fireEvent('hide', this, this.date);
20499 onMousedown: function(e)
20501 e.stopPropagation();
20502 e.preventDefault();
20507 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20511 fireKey: function(e)
20513 if (!this.picker().isVisible()){
20514 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20525 e.preventDefault();
20529 dir = e.keyCode == 37 ? -1 : 1;
20531 this.vIndex = this.vIndex + dir;
20533 if(this.vIndex < 0){
20537 if(this.vIndex > 11){
20541 if(isNaN(this.vIndex)){
20545 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20551 dir = e.keyCode == 38 ? -1 : 1;
20553 this.vIndex = this.vIndex + dir * 4;
20555 if(this.vIndex < 0){
20559 if(this.vIndex > 11){
20563 if(isNaN(this.vIndex)){
20567 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20572 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20573 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20577 e.preventDefault();
20580 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20581 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20597 this.picker().remove();
20602 Roo.apply(Roo.bootstrap.MonthField, {
20621 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20622 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20627 Roo.apply(Roo.bootstrap.MonthField, {
20631 cls: 'datepicker dropdown-menu roo-dynamic',
20635 cls: 'datepicker-months',
20639 cls: 'table-condensed',
20641 Roo.bootstrap.DateField.content
20661 * @class Roo.bootstrap.CheckBox
20662 * @extends Roo.bootstrap.Input
20663 * Bootstrap CheckBox class
20665 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20666 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20667 * @cfg {String} boxLabel The text that appears beside the checkbox
20668 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20669 * @cfg {Boolean} checked initnal the element
20670 * @cfg {Boolean} inline inline the element (default false)
20671 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20672 * @cfg {String} tooltip label tooltip
20675 * Create a new CheckBox
20676 * @param {Object} config The config object
20679 Roo.bootstrap.CheckBox = function(config){
20680 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20685 * Fires when the element is checked or unchecked.
20686 * @param {Roo.bootstrap.CheckBox} this This input
20687 * @param {Boolean} checked The new checked value
20692 * Fires when the element is click.
20693 * @param {Roo.bootstrap.CheckBox} this This input
20700 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20702 inputType: 'checkbox',
20711 getAutoCreate : function()
20713 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20719 cfg.cls = 'form-group ' + this.inputType; //input-group
20722 cfg.cls += ' ' + this.inputType + '-inline';
20728 type : this.inputType,
20729 value : this.inputValue,
20730 cls : 'roo-' + this.inputType, //'form-box',
20731 placeholder : this.placeholder || ''
20735 if(this.inputType != 'radio'){
20739 cls : 'roo-hidden-value',
20740 value : this.checked ? this.inputValue : this.valueOff
20745 if (this.weight) { // Validity check?
20746 cfg.cls += " " + this.inputType + "-" + this.weight;
20749 if (this.disabled) {
20750 input.disabled=true;
20754 input.checked = this.checked;
20759 input.name = this.name;
20761 if(this.inputType != 'radio'){
20762 hidden.name = this.name;
20763 input.name = '_hidden_' + this.name;
20768 input.cls += ' input-' + this.size;
20773 ['xs','sm','md','lg'].map(function(size){
20774 if (settings[size]) {
20775 cfg.cls += ' col-' + size + '-' + settings[size];
20779 var inputblock = input;
20781 if (this.before || this.after) {
20784 cls : 'input-group',
20789 inputblock.cn.push({
20791 cls : 'input-group-addon',
20796 inputblock.cn.push(input);
20798 if(this.inputType != 'radio'){
20799 inputblock.cn.push(hidden);
20803 inputblock.cn.push({
20805 cls : 'input-group-addon',
20812 if (align ==='left' && this.fieldLabel.length) {
20813 // Roo.log("left and has label");
20818 cls : 'control-label',
20819 html : this.fieldLabel
20829 if(this.labelWidth > 12){
20830 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20833 if(this.labelWidth < 13 && this.labelmd == 0){
20834 this.labelmd = this.labelWidth;
20837 if(this.labellg > 0){
20838 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20839 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20842 if(this.labelmd > 0){
20843 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20844 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20847 if(this.labelsm > 0){
20848 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20849 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20852 if(this.labelxs > 0){
20853 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20854 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20857 } else if ( this.fieldLabel.length) {
20858 // Roo.log(" label");
20862 tag: this.boxLabel ? 'span' : 'label',
20864 cls: 'control-label box-input-label',
20865 //cls : 'input-group-addon',
20866 html : this.fieldLabel
20875 // Roo.log(" no label && no align");
20876 cfg.cn = [ inputblock ] ;
20882 var boxLabelCfg = {
20884 //'for': id, // box label is handled by onclick - so no for...
20886 html: this.boxLabel
20890 boxLabelCfg.tooltip = this.tooltip;
20893 cfg.cn.push(boxLabelCfg);
20896 if(this.inputType != 'radio'){
20897 cfg.cn.push(hidden);
20905 * return the real input element.
20907 inputEl: function ()
20909 return this.el.select('input.roo-' + this.inputType,true).first();
20911 hiddenEl: function ()
20913 return this.el.select('input.roo-hidden-value',true).first();
20916 labelEl: function()
20918 return this.el.select('label.control-label',true).first();
20920 /* depricated... */
20924 return this.labelEl();
20927 boxLabelEl: function()
20929 return this.el.select('label.box-label',true).first();
20932 initEvents : function()
20934 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20936 this.inputEl().on('click', this.onClick, this);
20938 if (this.boxLabel) {
20939 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20942 this.startValue = this.getValue();
20945 Roo.bootstrap.CheckBox.register(this);
20949 onClick : function(e)
20951 if(this.fireEvent('click', this, e) !== false){
20952 this.setChecked(!this.checked);
20957 setChecked : function(state,suppressEvent)
20959 this.startValue = this.getValue();
20961 if(this.inputType == 'radio'){
20963 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20964 e.dom.checked = false;
20967 this.inputEl().dom.checked = true;
20969 this.inputEl().dom.value = this.inputValue;
20971 if(suppressEvent !== true){
20972 this.fireEvent('check', this, true);
20980 this.checked = state;
20982 this.inputEl().dom.checked = state;
20985 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20987 if(suppressEvent !== true){
20988 this.fireEvent('check', this, state);
20994 getValue : function()
20996 if(this.inputType == 'radio'){
20997 return this.getGroupValue();
21000 return this.hiddenEl().dom.value;
21004 getGroupValue : function()
21006 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21010 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21013 setValue : function(v,suppressEvent)
21015 if(this.inputType == 'radio'){
21016 this.setGroupValue(v, suppressEvent);
21020 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21025 setGroupValue : function(v, suppressEvent)
21027 this.startValue = this.getValue();
21029 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21030 e.dom.checked = false;
21032 if(e.dom.value == v){
21033 e.dom.checked = true;
21037 if(suppressEvent !== true){
21038 this.fireEvent('check', this, true);
21046 validate : function()
21048 if(this.getVisibilityEl().hasClass('hidden')){
21054 (this.inputType == 'radio' && this.validateRadio()) ||
21055 (this.inputType == 'checkbox' && this.validateCheckbox())
21061 this.markInvalid();
21065 validateRadio : function()
21067 if(this.getVisibilityEl().hasClass('hidden')){
21071 if(this.allowBlank){
21077 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21078 if(!e.dom.checked){
21090 validateCheckbox : function()
21093 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21094 //return (this.getValue() == this.inputValue) ? true : false;
21097 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21105 for(var i in group){
21106 if(group[i].el.isVisible(true)){
21114 for(var i in group){
21119 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21126 * Mark this field as valid
21128 markValid : function()
21132 this.fireEvent('valid', this);
21134 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21137 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21144 if(this.inputType == 'radio'){
21145 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21146 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21147 e.findParent('.form-group', false, true).addClass(_this.validClass);
21154 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21155 this.el.findParent('.form-group', false, true).addClass(this.validClass);
21159 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21165 for(var i in group){
21166 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21167 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21172 * Mark this field as invalid
21173 * @param {String} msg The validation message
21175 markInvalid : function(msg)
21177 if(this.allowBlank){
21183 this.fireEvent('invalid', this, msg);
21185 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21188 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21192 label.markInvalid();
21195 if(this.inputType == 'radio'){
21196 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21197 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21198 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21205 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21206 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21210 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21216 for(var i in group){
21217 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21218 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21223 clearInvalid : function()
21225 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21227 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21229 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21231 if (label && label.iconEl) {
21232 label.iconEl.removeClass(label.validClass);
21233 label.iconEl.removeClass(label.invalidClass);
21237 disable : function()
21239 if(this.inputType != 'radio'){
21240 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21247 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21248 _this.getActionEl().addClass(this.disabledClass);
21249 e.dom.disabled = true;
21253 this.disabled = true;
21254 this.fireEvent("disable", this);
21258 enable : function()
21260 if(this.inputType != 'radio'){
21261 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21268 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21269 _this.getActionEl().removeClass(this.disabledClass);
21270 e.dom.disabled = false;
21274 this.disabled = false;
21275 this.fireEvent("enable", this);
21279 setBoxLabel : function(v)
21284 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21290 Roo.apply(Roo.bootstrap.CheckBox, {
21295 * register a CheckBox Group
21296 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21298 register : function(checkbox)
21300 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21301 this.groups[checkbox.groupId] = {};
21304 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21308 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21312 * fetch a CheckBox Group based on the group ID
21313 * @param {string} the group ID
21314 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21316 get: function(groupId) {
21317 if (typeof(this.groups[groupId]) == 'undefined') {
21321 return this.groups[groupId] ;
21334 * @class Roo.bootstrap.Radio
21335 * @extends Roo.bootstrap.Component
21336 * Bootstrap Radio class
21337 * @cfg {String} boxLabel - the label associated
21338 * @cfg {String} value - the value of radio
21341 * Create a new Radio
21342 * @param {Object} config The config object
21344 Roo.bootstrap.Radio = function(config){
21345 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21349 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21355 getAutoCreate : function()
21359 cls : 'form-group radio',
21364 html : this.boxLabel
21372 initEvents : function()
21374 this.parent().register(this);
21376 this.el.on('click', this.onClick, this);
21380 onClick : function(e)
21382 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21383 this.setChecked(true);
21387 setChecked : function(state, suppressEvent)
21389 this.parent().setValue(this.value, suppressEvent);
21393 setBoxLabel : function(v)
21398 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21413 * @class Roo.bootstrap.SecurePass
21414 * @extends Roo.bootstrap.Input
21415 * Bootstrap SecurePass class
21419 * Create a new SecurePass
21420 * @param {Object} config The config object
21423 Roo.bootstrap.SecurePass = function (config) {
21424 // these go here, so the translation tool can replace them..
21426 PwdEmpty: "Please type a password, and then retype it to confirm.",
21427 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21428 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21429 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21430 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21431 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21432 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21433 TooWeak: "Your password is Too Weak."
21435 this.meterLabel = "Password strength:";
21436 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21437 this.meterClass = [
21438 "roo-password-meter-tooweak",
21439 "roo-password-meter-weak",
21440 "roo-password-meter-medium",
21441 "roo-password-meter-strong",
21442 "roo-password-meter-grey"
21447 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21450 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21452 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21454 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21455 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21456 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21457 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21458 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21459 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21460 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21470 * @cfg {String/Object} Label for the strength meter (defaults to
21471 * 'Password strength:')
21476 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21477 * ['Weak', 'Medium', 'Strong'])
21480 pwdStrengths: false,
21493 initEvents: function ()
21495 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21497 if (this.el.is('input[type=password]') && Roo.isSafari) {
21498 this.el.on('keydown', this.SafariOnKeyDown, this);
21501 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21504 onRender: function (ct, position)
21506 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21507 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21508 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21510 this.trigger.createChild({
21515 cls: 'roo-password-meter-grey col-xs-12',
21518 //width: this.meterWidth + 'px'
21522 cls: 'roo-password-meter-text'
21528 if (this.hideTrigger) {
21529 this.trigger.setDisplayed(false);
21531 this.setSize(this.width || '', this.height || '');
21534 onDestroy: function ()
21536 if (this.trigger) {
21537 this.trigger.removeAllListeners();
21538 this.trigger.remove();
21541 this.wrap.remove();
21543 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21546 checkStrength: function ()
21548 var pwd = this.inputEl().getValue();
21549 if (pwd == this._lastPwd) {
21554 if (this.ClientSideStrongPassword(pwd)) {
21556 } else if (this.ClientSideMediumPassword(pwd)) {
21558 } else if (this.ClientSideWeakPassword(pwd)) {
21564 Roo.log('strength1: ' + strength);
21566 //var pm = this.trigger.child('div/div/div').dom;
21567 var pm = this.trigger.child('div/div');
21568 pm.removeClass(this.meterClass);
21569 pm.addClass(this.meterClass[strength]);
21572 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21574 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21576 this._lastPwd = pwd;
21580 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21582 this._lastPwd = '';
21584 var pm = this.trigger.child('div/div');
21585 pm.removeClass(this.meterClass);
21586 pm.addClass('roo-password-meter-grey');
21589 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21592 this.inputEl().dom.type='password';
21595 validateValue: function (value)
21598 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21601 if (value.length == 0) {
21602 if (this.allowBlank) {
21603 this.clearInvalid();
21607 this.markInvalid(this.errors.PwdEmpty);
21608 this.errorMsg = this.errors.PwdEmpty;
21616 if ('[\x21-\x7e]*'.match(value)) {
21617 this.markInvalid(this.errors.PwdBadChar);
21618 this.errorMsg = this.errors.PwdBadChar;
21621 if (value.length < 6) {
21622 this.markInvalid(this.errors.PwdShort);
21623 this.errorMsg = this.errors.PwdShort;
21626 if (value.length > 16) {
21627 this.markInvalid(this.errors.PwdLong);
21628 this.errorMsg = this.errors.PwdLong;
21632 if (this.ClientSideStrongPassword(value)) {
21634 } else if (this.ClientSideMediumPassword(value)) {
21636 } else if (this.ClientSideWeakPassword(value)) {
21643 if (strength < 2) {
21644 //this.markInvalid(this.errors.TooWeak);
21645 this.errorMsg = this.errors.TooWeak;
21650 console.log('strength2: ' + strength);
21652 //var pm = this.trigger.child('div/div/div').dom;
21654 var pm = this.trigger.child('div/div');
21655 pm.removeClass(this.meterClass);
21656 pm.addClass(this.meterClass[strength]);
21658 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21660 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21662 this.errorMsg = '';
21666 CharacterSetChecks: function (type)
21669 this.fResult = false;
21672 isctype: function (character, type)
21675 case this.kCapitalLetter:
21676 if (character >= 'A' && character <= 'Z') {
21681 case this.kSmallLetter:
21682 if (character >= 'a' && character <= 'z') {
21688 if (character >= '0' && character <= '9') {
21693 case this.kPunctuation:
21694 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21705 IsLongEnough: function (pwd, size)
21707 return !(pwd == null || isNaN(size) || pwd.length < size);
21710 SpansEnoughCharacterSets: function (word, nb)
21712 if (!this.IsLongEnough(word, nb))
21717 var characterSetChecks = new Array(
21718 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21719 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21722 for (var index = 0; index < word.length; ++index) {
21723 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21724 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21725 characterSetChecks[nCharSet].fResult = true;
21732 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21733 if (characterSetChecks[nCharSet].fResult) {
21738 if (nCharSets < nb) {
21744 ClientSideStrongPassword: function (pwd)
21746 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21749 ClientSideMediumPassword: function (pwd)
21751 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21754 ClientSideWeakPassword: function (pwd)
21756 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21759 })//<script type="text/javascript">
21762 * Based Ext JS Library 1.1.1
21763 * Copyright(c) 2006-2007, Ext JS, LLC.
21769 * @class Roo.HtmlEditorCore
21770 * @extends Roo.Component
21771 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21773 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21776 Roo.HtmlEditorCore = function(config){
21779 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21784 * @event initialize
21785 * Fires when the editor is fully initialized (including the iframe)
21786 * @param {Roo.HtmlEditorCore} this
21791 * Fires when the editor is first receives the focus. Any insertion must wait
21792 * until after this event.
21793 * @param {Roo.HtmlEditorCore} this
21797 * @event beforesync
21798 * Fires before the textarea is updated with content from the editor iframe. Return false
21799 * to cancel the sync.
21800 * @param {Roo.HtmlEditorCore} this
21801 * @param {String} html
21805 * @event beforepush
21806 * Fires before the iframe editor is updated with content from the textarea. Return false
21807 * to cancel the push.
21808 * @param {Roo.HtmlEditorCore} this
21809 * @param {String} html
21814 * Fires when the textarea is updated with content from the editor iframe.
21815 * @param {Roo.HtmlEditorCore} this
21816 * @param {String} html
21821 * Fires when the iframe editor is updated with content from the textarea.
21822 * @param {Roo.HtmlEditorCore} this
21823 * @param {String} html
21828 * @event editorevent
21829 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21830 * @param {Roo.HtmlEditorCore} this
21836 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21838 // defaults : white / black...
21839 this.applyBlacklists();
21846 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21850 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21856 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21861 * @cfg {Number} height (in pixels)
21865 * @cfg {Number} width (in pixels)
21870 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21873 stylesheets: false,
21878 // private properties
21879 validationEvent : false,
21881 initialized : false,
21883 sourceEditMode : false,
21884 onFocus : Roo.emptyFn,
21886 hideMode:'offsets',
21890 // blacklist + whitelisted elements..
21897 * Protected method that will not generally be called directly. It
21898 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21899 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21901 getDocMarkup : function(){
21905 // inherit styels from page...??
21906 if (this.stylesheets === false) {
21908 Roo.get(document.head).select('style').each(function(node) {
21909 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21912 Roo.get(document.head).select('link').each(function(node) {
21913 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21916 } else if (!this.stylesheets.length) {
21918 st = '<style type="text/css">' +
21919 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21922 st = '<style type="text/css">' +
21927 st += '<style type="text/css">' +
21928 'IMG { cursor: pointer } ' +
21931 var cls = 'roo-htmleditor-body';
21933 if(this.bodyCls.length){
21934 cls += ' ' + this.bodyCls;
21937 return '<html><head>' + st +
21938 //<style type="text/css">' +
21939 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21941 ' </head><body class="' + cls + '"></body></html>';
21945 onRender : function(ct, position)
21948 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21949 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21952 this.el.dom.style.border = '0 none';
21953 this.el.dom.setAttribute('tabIndex', -1);
21954 this.el.addClass('x-hidden hide');
21958 if(Roo.isIE){ // fix IE 1px bogus margin
21959 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21963 this.frameId = Roo.id();
21967 var iframe = this.owner.wrap.createChild({
21969 cls: 'form-control', // bootstrap..
21971 name: this.frameId,
21972 frameBorder : 'no',
21973 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21978 this.iframe = iframe.dom;
21980 this.assignDocWin();
21982 this.doc.designMode = 'on';
21985 this.doc.write(this.getDocMarkup());
21989 var task = { // must defer to wait for browser to be ready
21991 //console.log("run task?" + this.doc.readyState);
21992 this.assignDocWin();
21993 if(this.doc.body || this.doc.readyState == 'complete'){
21995 this.doc.designMode="on";
21999 Roo.TaskMgr.stop(task);
22000 this.initEditor.defer(10, this);
22007 Roo.TaskMgr.start(task);
22012 onResize : function(w, h)
22014 Roo.log('resize: ' +w + ',' + h );
22015 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22019 if(typeof w == 'number'){
22021 this.iframe.style.width = w + 'px';
22023 if(typeof h == 'number'){
22025 this.iframe.style.height = h + 'px';
22027 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22034 * Toggles the editor between standard and source edit mode.
22035 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22037 toggleSourceEdit : function(sourceEditMode){
22039 this.sourceEditMode = sourceEditMode === true;
22041 if(this.sourceEditMode){
22043 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
22046 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22047 //this.iframe.className = '';
22050 //this.setSize(this.owner.wrap.getSize());
22051 //this.fireEvent('editmodechange', this, this.sourceEditMode);
22058 * Protected method that will not generally be called directly. If you need/want
22059 * custom HTML cleanup, this is the method you should override.
22060 * @param {String} html The HTML to be cleaned
22061 * return {String} The cleaned HTML
22063 cleanHtml : function(html){
22064 html = String(html);
22065 if(html.length > 5){
22066 if(Roo.isSafari){ // strip safari nonsense
22067 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22070 if(html == ' '){
22077 * HTML Editor -> Textarea
22078 * Protected method that will not generally be called directly. Syncs the contents
22079 * of the editor iframe with the textarea.
22081 syncValue : function(){
22082 if(this.initialized){
22083 var bd = (this.doc.body || this.doc.documentElement);
22084 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22085 var html = bd.innerHTML;
22087 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22088 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22090 html = '<div style="'+m[0]+'">' + html + '</div>';
22093 html = this.cleanHtml(html);
22094 // fix up the special chars.. normaly like back quotes in word...
22095 // however we do not want to do this with chinese..
22096 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22097 var cc = b.charCodeAt();
22099 (cc >= 0x4E00 && cc < 0xA000 ) ||
22100 (cc >= 0x3400 && cc < 0x4E00 ) ||
22101 (cc >= 0xf900 && cc < 0xfb00 )
22107 if(this.owner.fireEvent('beforesync', this, html) !== false){
22108 this.el.dom.value = html;
22109 this.owner.fireEvent('sync', this, html);
22115 * Protected method that will not generally be called directly. Pushes the value of the textarea
22116 * into the iframe editor.
22118 pushValue : function(){
22119 if(this.initialized){
22120 var v = this.el.dom.value.trim();
22122 // if(v.length < 1){
22126 if(this.owner.fireEvent('beforepush', this, v) !== false){
22127 var d = (this.doc.body || this.doc.documentElement);
22129 this.cleanUpPaste();
22130 this.el.dom.value = d.innerHTML;
22131 this.owner.fireEvent('push', this, v);
22137 deferFocus : function(){
22138 this.focus.defer(10, this);
22142 focus : function(){
22143 if(this.win && !this.sourceEditMode){
22150 assignDocWin: function()
22152 var iframe = this.iframe;
22155 this.doc = iframe.contentWindow.document;
22156 this.win = iframe.contentWindow;
22158 // if (!Roo.get(this.frameId)) {
22161 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22162 // this.win = Roo.get(this.frameId).dom.contentWindow;
22164 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22168 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22169 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22174 initEditor : function(){
22175 //console.log("INIT EDITOR");
22176 this.assignDocWin();
22180 this.doc.designMode="on";
22182 this.doc.write(this.getDocMarkup());
22185 var dbody = (this.doc.body || this.doc.documentElement);
22186 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22187 // this copies styles from the containing element into thsi one..
22188 // not sure why we need all of this..
22189 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22191 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22192 //ss['background-attachment'] = 'fixed'; // w3c
22193 dbody.bgProperties = 'fixed'; // ie
22194 //Roo.DomHelper.applyStyles(dbody, ss);
22195 Roo.EventManager.on(this.doc, {
22196 //'mousedown': this.onEditorEvent,
22197 'mouseup': this.onEditorEvent,
22198 'dblclick': this.onEditorEvent,
22199 'click': this.onEditorEvent,
22200 'keyup': this.onEditorEvent,
22205 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22207 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22208 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22210 this.initialized = true;
22212 this.owner.fireEvent('initialize', this);
22217 onDestroy : function(){
22223 //for (var i =0; i < this.toolbars.length;i++) {
22224 // // fixme - ask toolbars for heights?
22225 // this.toolbars[i].onDestroy();
22228 //this.wrap.dom.innerHTML = '';
22229 //this.wrap.remove();
22234 onFirstFocus : function(){
22236 this.assignDocWin();
22239 this.activated = true;
22242 if(Roo.isGecko){ // prevent silly gecko errors
22244 var s = this.win.getSelection();
22245 if(!s.focusNode || s.focusNode.nodeType != 3){
22246 var r = s.getRangeAt(0);
22247 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22252 this.execCmd('useCSS', true);
22253 this.execCmd('styleWithCSS', false);
22256 this.owner.fireEvent('activate', this);
22260 adjustFont: function(btn){
22261 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22262 //if(Roo.isSafari){ // safari
22265 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22266 if(Roo.isSafari){ // safari
22267 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22268 v = (v < 10) ? 10 : v;
22269 v = (v > 48) ? 48 : v;
22270 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22275 v = Math.max(1, v+adjust);
22277 this.execCmd('FontSize', v );
22280 onEditorEvent : function(e)
22282 this.owner.fireEvent('editorevent', this, e);
22283 // this.updateToolbar();
22284 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22287 insertTag : function(tg)
22289 // could be a bit smarter... -> wrap the current selected tRoo..
22290 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22292 range = this.createRange(this.getSelection());
22293 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22294 wrappingNode.appendChild(range.extractContents());
22295 range.insertNode(wrappingNode);
22302 this.execCmd("formatblock", tg);
22306 insertText : function(txt)
22310 var range = this.createRange();
22311 range.deleteContents();
22312 //alert(Sender.getAttribute('label'));
22314 range.insertNode(this.doc.createTextNode(txt));
22320 * Executes a Midas editor command on the editor document and performs necessary focus and
22321 * toolbar updates. <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 relayCmd : function(cmd, value){
22327 this.execCmd(cmd, value);
22328 this.owner.fireEvent('editorevent', this);
22329 //this.updateToolbar();
22330 this.owner.deferFocus();
22334 * Executes a Midas editor command directly on the editor document.
22335 * For visual commands, you should use {@link #relayCmd} instead.
22336 * <b>This should only be called after the editor is initialized.</b>
22337 * @param {String} cmd The Midas command
22338 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22340 execCmd : function(cmd, value){
22341 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22348 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22350 * @param {String} text | dom node..
22352 insertAtCursor : function(text)
22355 if(!this.activated){
22361 var r = this.doc.selection.createRange();
22372 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22376 // from jquery ui (MIT licenced)
22378 var win = this.win;
22380 if (win.getSelection && win.getSelection().getRangeAt) {
22381 range = win.getSelection().getRangeAt(0);
22382 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22383 range.insertNode(node);
22384 } else if (win.document.selection && win.document.selection.createRange) {
22385 // no firefox support
22386 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22387 win.document.selection.createRange().pasteHTML(txt);
22389 // no firefox support
22390 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22391 this.execCmd('InsertHTML', txt);
22400 mozKeyPress : function(e){
22402 var c = e.getCharCode(), cmd;
22405 c = String.fromCharCode(c).toLowerCase();
22419 this.cleanUpPaste.defer(100, this);
22427 e.preventDefault();
22435 fixKeys : function(){ // load time branching for fastest keydown performance
22437 return function(e){
22438 var k = e.getKey(), r;
22441 r = this.doc.selection.createRange();
22444 r.pasteHTML('    ');
22451 r = this.doc.selection.createRange();
22453 var target = r.parentElement();
22454 if(!target || target.tagName.toLowerCase() != 'li'){
22456 r.pasteHTML('<br />');
22462 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22463 this.cleanUpPaste.defer(100, this);
22469 }else if(Roo.isOpera){
22470 return function(e){
22471 var k = e.getKey();
22475 this.execCmd('InsertHTML','    ');
22478 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22479 this.cleanUpPaste.defer(100, this);
22484 }else if(Roo.isSafari){
22485 return function(e){
22486 var k = e.getKey();
22490 this.execCmd('InsertText','\t');
22494 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22495 this.cleanUpPaste.defer(100, this);
22503 getAllAncestors: function()
22505 var p = this.getSelectedNode();
22508 a.push(p); // push blank onto stack..
22509 p = this.getParentElement();
22513 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22517 a.push(this.doc.body);
22521 lastSelNode : false,
22524 getSelection : function()
22526 this.assignDocWin();
22527 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22530 getSelectedNode: function()
22532 // this may only work on Gecko!!!
22534 // should we cache this!!!!
22539 var range = this.createRange(this.getSelection()).cloneRange();
22542 var parent = range.parentElement();
22544 var testRange = range.duplicate();
22545 testRange.moveToElementText(parent);
22546 if (testRange.inRange(range)) {
22549 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22552 parent = parent.parentElement;
22557 // is ancestor a text element.
22558 var ac = range.commonAncestorContainer;
22559 if (ac.nodeType == 3) {
22560 ac = ac.parentNode;
22563 var ar = ac.childNodes;
22566 var other_nodes = [];
22567 var has_other_nodes = false;
22568 for (var i=0;i<ar.length;i++) {
22569 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22572 // fullly contained node.
22574 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22579 // probably selected..
22580 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22581 other_nodes.push(ar[i]);
22585 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22590 has_other_nodes = true;
22592 if (!nodes.length && other_nodes.length) {
22593 nodes= other_nodes;
22595 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22601 createRange: function(sel)
22603 // this has strange effects when using with
22604 // top toolbar - not sure if it's a great idea.
22605 //this.editor.contentWindow.focus();
22606 if (typeof sel != "undefined") {
22608 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22610 return this.doc.createRange();
22613 return this.doc.createRange();
22616 getParentElement: function()
22619 this.assignDocWin();
22620 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22622 var range = this.createRange(sel);
22625 var p = range.commonAncestorContainer;
22626 while (p.nodeType == 3) { // text node
22637 * Range intersection.. the hard stuff...
22641 * [ -- selected range --- ]
22645 * if end is before start or hits it. fail.
22646 * if start is after end or hits it fail.
22648 * if either hits (but other is outside. - then it's not
22654 // @see http://www.thismuchiknow.co.uk/?p=64.
22655 rangeIntersectsNode : function(range, node)
22657 var nodeRange = node.ownerDocument.createRange();
22659 nodeRange.selectNode(node);
22661 nodeRange.selectNodeContents(node);
22664 var rangeStartRange = range.cloneRange();
22665 rangeStartRange.collapse(true);
22667 var rangeEndRange = range.cloneRange();
22668 rangeEndRange.collapse(false);
22670 var nodeStartRange = nodeRange.cloneRange();
22671 nodeStartRange.collapse(true);
22673 var nodeEndRange = nodeRange.cloneRange();
22674 nodeEndRange.collapse(false);
22676 return rangeStartRange.compareBoundaryPoints(
22677 Range.START_TO_START, nodeEndRange) == -1 &&
22678 rangeEndRange.compareBoundaryPoints(
22679 Range.START_TO_START, nodeStartRange) == 1;
22683 rangeCompareNode : function(range, node)
22685 var nodeRange = node.ownerDocument.createRange();
22687 nodeRange.selectNode(node);
22689 nodeRange.selectNodeContents(node);
22693 range.collapse(true);
22695 nodeRange.collapse(true);
22697 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22698 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22700 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22702 var nodeIsBefore = ss == 1;
22703 var nodeIsAfter = ee == -1;
22705 if (nodeIsBefore && nodeIsAfter) {
22708 if (!nodeIsBefore && nodeIsAfter) {
22709 return 1; //right trailed.
22712 if (nodeIsBefore && !nodeIsAfter) {
22713 return 2; // left trailed.
22719 // private? - in a new class?
22720 cleanUpPaste : function()
22722 // cleans up the whole document..
22723 Roo.log('cleanuppaste');
22725 this.cleanUpChildren(this.doc.body);
22726 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22727 if (clean != this.doc.body.innerHTML) {
22728 this.doc.body.innerHTML = clean;
22733 cleanWordChars : function(input) {// change the chars to hex code
22734 var he = Roo.HtmlEditorCore;
22736 var output = input;
22737 Roo.each(he.swapCodes, function(sw) {
22738 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22740 output = output.replace(swapper, sw[1]);
22747 cleanUpChildren : function (n)
22749 if (!n.childNodes.length) {
22752 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22753 this.cleanUpChild(n.childNodes[i]);
22760 cleanUpChild : function (node)
22763 //console.log(node);
22764 if (node.nodeName == "#text") {
22765 // clean up silly Windows -- stuff?
22768 if (node.nodeName == "#comment") {
22769 node.parentNode.removeChild(node);
22770 // clean up silly Windows -- stuff?
22773 var lcname = node.tagName.toLowerCase();
22774 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22775 // whitelist of tags..
22777 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22779 node.parentNode.removeChild(node);
22784 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22786 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22787 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22789 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22790 // remove_keep_children = true;
22793 if (remove_keep_children) {
22794 this.cleanUpChildren(node);
22795 // inserts everything just before this node...
22796 while (node.childNodes.length) {
22797 var cn = node.childNodes[0];
22798 node.removeChild(cn);
22799 node.parentNode.insertBefore(cn, node);
22801 node.parentNode.removeChild(node);
22805 if (!node.attributes || !node.attributes.length) {
22806 this.cleanUpChildren(node);
22810 function cleanAttr(n,v)
22813 if (v.match(/^\./) || v.match(/^\//)) {
22816 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22819 if (v.match(/^#/)) {
22822 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22823 node.removeAttribute(n);
22827 var cwhite = this.cwhite;
22828 var cblack = this.cblack;
22830 function cleanStyle(n,v)
22832 if (v.match(/expression/)) { //XSS?? should we even bother..
22833 node.removeAttribute(n);
22837 var parts = v.split(/;/);
22840 Roo.each(parts, function(p) {
22841 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22845 var l = p.split(':').shift().replace(/\s+/g,'');
22846 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22848 if ( cwhite.length && cblack.indexOf(l) > -1) {
22849 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22850 //node.removeAttribute(n);
22854 // only allow 'c whitelisted system attributes'
22855 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22856 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22857 //node.removeAttribute(n);
22867 if (clean.length) {
22868 node.setAttribute(n, clean.join(';'));
22870 node.removeAttribute(n);
22876 for (var i = node.attributes.length-1; i > -1 ; i--) {
22877 var a = node.attributes[i];
22880 if (a.name.toLowerCase().substr(0,2)=='on') {
22881 node.removeAttribute(a.name);
22884 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22885 node.removeAttribute(a.name);
22888 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22889 cleanAttr(a.name,a.value); // fixme..
22892 if (a.name == 'style') {
22893 cleanStyle(a.name,a.value);
22896 /// clean up MS crap..
22897 // tecnically this should be a list of valid class'es..
22900 if (a.name == 'class') {
22901 if (a.value.match(/^Mso/)) {
22902 node.className = '';
22905 if (a.value.match(/^body$/)) {
22906 node.className = '';
22917 this.cleanUpChildren(node);
22923 * Clean up MS wordisms...
22925 cleanWord : function(node)
22930 this.cleanWord(this.doc.body);
22933 if (node.nodeName == "#text") {
22934 // clean up silly Windows -- stuff?
22937 if (node.nodeName == "#comment") {
22938 node.parentNode.removeChild(node);
22939 // clean up silly Windows -- stuff?
22943 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22944 node.parentNode.removeChild(node);
22948 // remove - but keep children..
22949 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22950 while (node.childNodes.length) {
22951 var cn = node.childNodes[0];
22952 node.removeChild(cn);
22953 node.parentNode.insertBefore(cn, node);
22955 node.parentNode.removeChild(node);
22956 this.iterateChildren(node, this.cleanWord);
22960 if (node.className.length) {
22962 var cn = node.className.split(/\W+/);
22964 Roo.each(cn, function(cls) {
22965 if (cls.match(/Mso[a-zA-Z]+/)) {
22970 node.className = cna.length ? cna.join(' ') : '';
22972 node.removeAttribute("class");
22976 if (node.hasAttribute("lang")) {
22977 node.removeAttribute("lang");
22980 if (node.hasAttribute("style")) {
22982 var styles = node.getAttribute("style").split(";");
22984 Roo.each(styles, function(s) {
22985 if (!s.match(/:/)) {
22988 var kv = s.split(":");
22989 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22992 // what ever is left... we allow.
22995 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22996 if (!nstyle.length) {
22997 node.removeAttribute('style');
23000 this.iterateChildren(node, this.cleanWord);
23006 * iterateChildren of a Node, calling fn each time, using this as the scole..
23007 * @param {DomNode} node node to iterate children of.
23008 * @param {Function} fn method of this class to call on each item.
23010 iterateChildren : function(node, fn)
23012 if (!node.childNodes.length) {
23015 for (var i = node.childNodes.length-1; i > -1 ; i--) {
23016 fn.call(this, node.childNodes[i])
23022 * cleanTableWidths.
23024 * Quite often pasting from word etc.. results in tables with column and widths.
23025 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23028 cleanTableWidths : function(node)
23033 this.cleanTableWidths(this.doc.body);
23038 if (node.nodeName == "#text" || node.nodeName == "#comment") {
23041 Roo.log(node.tagName);
23042 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23043 this.iterateChildren(node, this.cleanTableWidths);
23046 if (node.hasAttribute('width')) {
23047 node.removeAttribute('width');
23051 if (node.hasAttribute("style")) {
23054 var styles = node.getAttribute("style").split(";");
23056 Roo.each(styles, function(s) {
23057 if (!s.match(/:/)) {
23060 var kv = s.split(":");
23061 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23064 // what ever is left... we allow.
23067 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23068 if (!nstyle.length) {
23069 node.removeAttribute('style');
23073 this.iterateChildren(node, this.cleanTableWidths);
23081 domToHTML : function(currentElement, depth, nopadtext) {
23083 depth = depth || 0;
23084 nopadtext = nopadtext || false;
23086 if (!currentElement) {
23087 return this.domToHTML(this.doc.body);
23090 //Roo.log(currentElement);
23092 var allText = false;
23093 var nodeName = currentElement.nodeName;
23094 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23096 if (nodeName == '#text') {
23098 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23103 if (nodeName != 'BODY') {
23106 // Prints the node tagName, such as <A>, <IMG>, etc
23109 for(i = 0; i < currentElement.attributes.length;i++) {
23111 var aname = currentElement.attributes.item(i).name;
23112 if (!currentElement.attributes.item(i).value.length) {
23115 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23118 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23127 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23130 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23135 // Traverse the tree
23137 var currentElementChild = currentElement.childNodes.item(i);
23138 var allText = true;
23139 var innerHTML = '';
23141 while (currentElementChild) {
23142 // Formatting code (indent the tree so it looks nice on the screen)
23143 var nopad = nopadtext;
23144 if (lastnode == 'SPAN') {
23148 if (currentElementChild.nodeName == '#text') {
23149 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23150 toadd = nopadtext ? toadd : toadd.trim();
23151 if (!nopad && toadd.length > 80) {
23152 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23154 innerHTML += toadd;
23157 currentElementChild = currentElement.childNodes.item(i);
23163 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23165 // Recursively traverse the tree structure of the child node
23166 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23167 lastnode = currentElementChild.nodeName;
23169 currentElementChild=currentElement.childNodes.item(i);
23175 // The remaining code is mostly for formatting the tree
23176 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23181 ret+= "</"+tagName+">";
23187 applyBlacklists : function()
23189 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23190 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23194 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23195 if (b.indexOf(tag) > -1) {
23198 this.white.push(tag);
23202 Roo.each(w, function(tag) {
23203 if (b.indexOf(tag) > -1) {
23206 if (this.white.indexOf(tag) > -1) {
23209 this.white.push(tag);
23214 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23215 if (w.indexOf(tag) > -1) {
23218 this.black.push(tag);
23222 Roo.each(b, function(tag) {
23223 if (w.indexOf(tag) > -1) {
23226 if (this.black.indexOf(tag) > -1) {
23229 this.black.push(tag);
23234 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23235 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23239 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23240 if (b.indexOf(tag) > -1) {
23243 this.cwhite.push(tag);
23247 Roo.each(w, function(tag) {
23248 if (b.indexOf(tag) > -1) {
23251 if (this.cwhite.indexOf(tag) > -1) {
23254 this.cwhite.push(tag);
23259 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23260 if (w.indexOf(tag) > -1) {
23263 this.cblack.push(tag);
23267 Roo.each(b, function(tag) {
23268 if (w.indexOf(tag) > -1) {
23271 if (this.cblack.indexOf(tag) > -1) {
23274 this.cblack.push(tag);
23279 setStylesheets : function(stylesheets)
23281 if(typeof(stylesheets) == 'string'){
23282 Roo.get(this.iframe.contentDocument.head).createChild({
23284 rel : 'stylesheet',
23293 Roo.each(stylesheets, function(s) {
23298 Roo.get(_this.iframe.contentDocument.head).createChild({
23300 rel : 'stylesheet',
23309 removeStylesheets : function()
23313 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23318 setStyle : function(style)
23320 Roo.get(this.iframe.contentDocument.head).createChild({
23329 // hide stuff that is not compatible
23343 * @event specialkey
23347 * @cfg {String} fieldClass @hide
23350 * @cfg {String} focusClass @hide
23353 * @cfg {String} autoCreate @hide
23356 * @cfg {String} inputType @hide
23359 * @cfg {String} invalidClass @hide
23362 * @cfg {String} invalidText @hide
23365 * @cfg {String} msgFx @hide
23368 * @cfg {String} validateOnBlur @hide
23372 Roo.HtmlEditorCore.white = [
23373 'area', 'br', 'img', 'input', 'hr', 'wbr',
23375 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23376 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23377 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23378 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23379 'table', 'ul', 'xmp',
23381 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23384 'dir', 'menu', 'ol', 'ul', 'dl',
23390 Roo.HtmlEditorCore.black = [
23391 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23393 'base', 'basefont', 'bgsound', 'blink', 'body',
23394 'frame', 'frameset', 'head', 'html', 'ilayer',
23395 'iframe', 'layer', 'link', 'meta', 'object',
23396 'script', 'style' ,'title', 'xml' // clean later..
23398 Roo.HtmlEditorCore.clean = [
23399 'script', 'style', 'title', 'xml'
23401 Roo.HtmlEditorCore.remove = [
23406 Roo.HtmlEditorCore.ablack = [
23410 Roo.HtmlEditorCore.aclean = [
23411 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23415 Roo.HtmlEditorCore.pwhite= [
23416 'http', 'https', 'mailto'
23419 // white listed style attributes.
23420 Roo.HtmlEditorCore.cwhite= [
23421 // 'text-align', /// default is to allow most things..
23427 // black listed style attributes.
23428 Roo.HtmlEditorCore.cblack= [
23429 // 'font-size' -- this can be set by the project
23433 Roo.HtmlEditorCore.swapCodes =[
23452 * @class Roo.bootstrap.HtmlEditor
23453 * @extends Roo.bootstrap.TextArea
23454 * Bootstrap HtmlEditor class
23457 * Create a new HtmlEditor
23458 * @param {Object} config The config object
23461 Roo.bootstrap.HtmlEditor = function(config){
23462 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23463 if (!this.toolbars) {
23464 this.toolbars = [];
23467 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23470 * @event initialize
23471 * Fires when the editor is fully initialized (including the iframe)
23472 * @param {HtmlEditor} this
23477 * Fires when the editor is first receives the focus. Any insertion must wait
23478 * until after this event.
23479 * @param {HtmlEditor} this
23483 * @event beforesync
23484 * Fires before the textarea is updated with content from the editor iframe. Return false
23485 * to cancel the sync.
23486 * @param {HtmlEditor} this
23487 * @param {String} html
23491 * @event beforepush
23492 * Fires before the iframe editor is updated with content from the textarea. Return false
23493 * to cancel the push.
23494 * @param {HtmlEditor} this
23495 * @param {String} html
23500 * Fires when the textarea is updated with content from the editor iframe.
23501 * @param {HtmlEditor} this
23502 * @param {String} html
23507 * Fires when the iframe editor is updated with content from the textarea.
23508 * @param {HtmlEditor} this
23509 * @param {String} html
23513 * @event editmodechange
23514 * Fires when the editor switches edit modes
23515 * @param {HtmlEditor} this
23516 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23518 editmodechange: true,
23520 * @event editorevent
23521 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23522 * @param {HtmlEditor} this
23526 * @event firstfocus
23527 * Fires when on first focus - needed by toolbars..
23528 * @param {HtmlEditor} this
23533 * Auto save the htmlEditor value as a file into Events
23534 * @param {HtmlEditor} this
23538 * @event savedpreview
23539 * preview the saved version of htmlEditor
23540 * @param {HtmlEditor} this
23547 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23551 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23556 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23561 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23566 * @cfg {Number} height (in pixels)
23570 * @cfg {Number} width (in pixels)
23575 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23578 stylesheets: false,
23583 // private properties
23584 validationEvent : false,
23586 initialized : false,
23589 onFocus : Roo.emptyFn,
23591 hideMode:'offsets',
23593 tbContainer : false,
23597 toolbarContainer :function() {
23598 return this.wrap.select('.x-html-editor-tb',true).first();
23602 * Protected method that will not generally be called directly. It
23603 * is called when the editor creates its toolbar. Override this method if you need to
23604 * add custom toolbar buttons.
23605 * @param {HtmlEditor} editor
23607 createToolbar : function(){
23608 Roo.log('renewing');
23609 Roo.log("create toolbars");
23611 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23612 this.toolbars[0].render(this.toolbarContainer());
23616 // if (!editor.toolbars || !editor.toolbars.length) {
23617 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23620 // for (var i =0 ; i < editor.toolbars.length;i++) {
23621 // editor.toolbars[i] = Roo.factory(
23622 // typeof(editor.toolbars[i]) == 'string' ?
23623 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23624 // Roo.bootstrap.HtmlEditor);
23625 // editor.toolbars[i].init(editor);
23631 onRender : function(ct, position)
23633 // Roo.log("Call onRender: " + this.xtype);
23635 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23637 this.wrap = this.inputEl().wrap({
23638 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23641 this.editorcore.onRender(ct, position);
23643 if (this.resizable) {
23644 this.resizeEl = new Roo.Resizable(this.wrap, {
23648 minHeight : this.height,
23649 height: this.height,
23650 handles : this.resizable,
23653 resize : function(r, w, h) {
23654 _t.onResize(w,h); // -something
23660 this.createToolbar(this);
23663 if(!this.width && this.resizable){
23664 this.setSize(this.wrap.getSize());
23666 if (this.resizeEl) {
23667 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23668 // should trigger onReize..
23674 onResize : function(w, h)
23676 Roo.log('resize: ' +w + ',' + h );
23677 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23681 if(this.inputEl() ){
23682 if(typeof w == 'number'){
23683 var aw = w - this.wrap.getFrameWidth('lr');
23684 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23687 if(typeof h == 'number'){
23688 var tbh = -11; // fixme it needs to tool bar size!
23689 for (var i =0; i < this.toolbars.length;i++) {
23690 // fixme - ask toolbars for heights?
23691 tbh += this.toolbars[i].el.getHeight();
23692 //if (this.toolbars[i].footer) {
23693 // tbh += this.toolbars[i].footer.el.getHeight();
23701 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23702 ah -= 5; // knock a few pixes off for look..
23703 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23707 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23708 this.editorcore.onResize(ew,eh);
23713 * Toggles the editor between standard and source edit mode.
23714 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23716 toggleSourceEdit : function(sourceEditMode)
23718 this.editorcore.toggleSourceEdit(sourceEditMode);
23720 if(this.editorcore.sourceEditMode){
23721 Roo.log('editor - showing textarea');
23724 // Roo.log(this.syncValue());
23726 this.inputEl().removeClass(['hide', 'x-hidden']);
23727 this.inputEl().dom.removeAttribute('tabIndex');
23728 this.inputEl().focus();
23730 Roo.log('editor - hiding textarea');
23732 // Roo.log(this.pushValue());
23735 this.inputEl().addClass(['hide', 'x-hidden']);
23736 this.inputEl().dom.setAttribute('tabIndex', -1);
23737 //this.deferFocus();
23740 if(this.resizable){
23741 this.setSize(this.wrap.getSize());
23744 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23747 // private (for BoxComponent)
23748 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23750 // private (for BoxComponent)
23751 getResizeEl : function(){
23755 // private (for BoxComponent)
23756 getPositionEl : function(){
23761 initEvents : function(){
23762 this.originalValue = this.getValue();
23766 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23769 // markInvalid : Roo.emptyFn,
23771 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23774 // clearInvalid : Roo.emptyFn,
23776 setValue : function(v){
23777 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23778 this.editorcore.pushValue();
23783 deferFocus : function(){
23784 this.focus.defer(10, this);
23788 focus : function(){
23789 this.editorcore.focus();
23795 onDestroy : function(){
23801 for (var i =0; i < this.toolbars.length;i++) {
23802 // fixme - ask toolbars for heights?
23803 this.toolbars[i].onDestroy();
23806 this.wrap.dom.innerHTML = '';
23807 this.wrap.remove();
23812 onFirstFocus : function(){
23813 //Roo.log("onFirstFocus");
23814 this.editorcore.onFirstFocus();
23815 for (var i =0; i < this.toolbars.length;i++) {
23816 this.toolbars[i].onFirstFocus();
23822 syncValue : function()
23824 this.editorcore.syncValue();
23827 pushValue : function()
23829 this.editorcore.pushValue();
23833 // hide stuff that is not compatible
23847 * @event specialkey
23851 * @cfg {String} fieldClass @hide
23854 * @cfg {String} focusClass @hide
23857 * @cfg {String} autoCreate @hide
23860 * @cfg {String} inputType @hide
23863 * @cfg {String} invalidClass @hide
23866 * @cfg {String} invalidText @hide
23869 * @cfg {String} msgFx @hide
23872 * @cfg {String} validateOnBlur @hide
23881 Roo.namespace('Roo.bootstrap.htmleditor');
23883 * @class Roo.bootstrap.HtmlEditorToolbar1
23888 new Roo.bootstrap.HtmlEditor({
23891 new Roo.bootstrap.HtmlEditorToolbar1({
23892 disable : { fonts: 1 , format: 1, ..., ... , ...],
23898 * @cfg {Object} disable List of elements to disable..
23899 * @cfg {Array} btns List of additional buttons.
23903 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23906 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23909 Roo.apply(this, config);
23911 // default disabled, based on 'good practice'..
23912 this.disable = this.disable || {};
23913 Roo.applyIf(this.disable, {
23916 specialElements : true
23918 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23920 this.editor = config.editor;
23921 this.editorcore = config.editor.editorcore;
23923 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23925 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23926 // dont call parent... till later.
23928 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23933 editorcore : false,
23938 "h1","h2","h3","h4","h5","h6",
23940 "abbr", "acronym", "address", "cite", "samp", "var",
23944 onRender : function(ct, position)
23946 // Roo.log("Call onRender: " + this.xtype);
23948 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23950 this.el.dom.style.marginBottom = '0';
23952 var editorcore = this.editorcore;
23953 var editor= this.editor;
23956 var btn = function(id,cmd , toggle, handler, html){
23958 var event = toggle ? 'toggle' : 'click';
23963 xns: Roo.bootstrap,
23966 enableToggle:toggle !== false,
23968 pressed : toggle ? false : null,
23971 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23972 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23978 // var cb_box = function...
23983 xns: Roo.bootstrap,
23984 glyphicon : 'font',
23988 xns: Roo.bootstrap,
23992 Roo.each(this.formats, function(f) {
23993 style.menu.items.push({
23995 xns: Roo.bootstrap,
23996 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24001 editorcore.insertTag(this.tagname);
24008 children.push(style);
24010 btn('bold',false,true);
24011 btn('italic',false,true);
24012 btn('align-left', 'justifyleft',true);
24013 btn('align-center', 'justifycenter',true);
24014 btn('align-right' , 'justifyright',true);
24015 btn('link', false, false, function(btn) {
24016 //Roo.log("create link?");
24017 var url = prompt(this.createLinkText, this.defaultLinkValue);
24018 if(url && url != 'http:/'+'/'){
24019 this.editorcore.relayCmd('createlink', url);
24022 btn('list','insertunorderedlist',true);
24023 btn('pencil', false,true, function(btn){
24025 this.toggleSourceEdit(btn.pressed);
24028 if (this.editor.btns.length > 0) {
24029 for (var i = 0; i<this.editor.btns.length; i++) {
24030 children.push(this.editor.btns[i]);
24038 xns: Roo.bootstrap,
24043 xns: Roo.bootstrap,
24048 cog.menu.items.push({
24050 xns: Roo.bootstrap,
24051 html : Clean styles,
24056 editorcore.insertTag(this.tagname);
24065 this.xtype = 'NavSimplebar';
24067 for(var i=0;i< children.length;i++) {
24069 this.buttons.add(this.addxtypeChild(children[i]));
24073 editor.on('editorevent', this.updateToolbar, this);
24075 onBtnClick : function(id)
24077 this.editorcore.relayCmd(id);
24078 this.editorcore.focus();
24082 * Protected method that will not generally be called directly. It triggers
24083 * a toolbar update by reading the markup state of the current selection in the editor.
24085 updateToolbar: function(){
24087 if(!this.editorcore.activated){
24088 this.editor.onFirstFocus(); // is this neeed?
24092 var btns = this.buttons;
24093 var doc = this.editorcore.doc;
24094 btns.get('bold').setActive(doc.queryCommandState('bold'));
24095 btns.get('italic').setActive(doc.queryCommandState('italic'));
24096 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24098 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24099 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24100 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24102 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24103 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24106 var ans = this.editorcore.getAllAncestors();
24107 if (this.formatCombo) {
24110 var store = this.formatCombo.store;
24111 this.formatCombo.setValue("");
24112 for (var i =0; i < ans.length;i++) {
24113 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24115 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24123 // hides menus... - so this cant be on a menu...
24124 Roo.bootstrap.MenuMgr.hideAll();
24126 Roo.bootstrap.MenuMgr.hideAll();
24127 //this.editorsyncValue();
24129 onFirstFocus: function() {
24130 this.buttons.each(function(item){
24134 toggleSourceEdit : function(sourceEditMode){
24137 if(sourceEditMode){
24138 Roo.log("disabling buttons");
24139 this.buttons.each( function(item){
24140 if(item.cmd != 'pencil'){
24146 Roo.log("enabling buttons");
24147 if(this.editorcore.initialized){
24148 this.buttons.each( function(item){
24154 Roo.log("calling toggole on editor");
24155 // tell the editor that it's been pressed..
24156 this.editor.toggleSourceEdit(sourceEditMode);
24166 * @class Roo.bootstrap.Table.AbstractSelectionModel
24167 * @extends Roo.util.Observable
24168 * Abstract base class for grid SelectionModels. It provides the interface that should be
24169 * implemented by descendant classes. This class should not be directly instantiated.
24172 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24173 this.locked = false;
24174 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24178 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24179 /** @ignore Called by the grid automatically. Do not call directly. */
24180 init : function(grid){
24186 * Locks the selections.
24189 this.locked = true;
24193 * Unlocks the selections.
24195 unlock : function(){
24196 this.locked = false;
24200 * Returns true if the selections are locked.
24201 * @return {Boolean}
24203 isLocked : function(){
24204 return this.locked;
24208 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24209 * @class Roo.bootstrap.Table.RowSelectionModel
24210 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24211 * It supports multiple selections and keyboard selection/navigation.
24213 * @param {Object} config
24216 Roo.bootstrap.Table.RowSelectionModel = function(config){
24217 Roo.apply(this, config);
24218 this.selections = new Roo.util.MixedCollection(false, function(o){
24223 this.lastActive = false;
24227 * @event selectionchange
24228 * Fires when the selection changes
24229 * @param {SelectionModel} this
24231 "selectionchange" : true,
24233 * @event afterselectionchange
24234 * Fires after the selection changes (eg. by key press or clicking)
24235 * @param {SelectionModel} this
24237 "afterselectionchange" : true,
24239 * @event beforerowselect
24240 * Fires when a row is selected being selected, return false to cancel.
24241 * @param {SelectionModel} this
24242 * @param {Number} rowIndex The selected index
24243 * @param {Boolean} keepExisting False if other selections will be cleared
24245 "beforerowselect" : true,
24248 * Fires when a row is selected.
24249 * @param {SelectionModel} this
24250 * @param {Number} rowIndex The selected index
24251 * @param {Roo.data.Record} r The record
24253 "rowselect" : true,
24255 * @event rowdeselect
24256 * Fires when a row is deselected.
24257 * @param {SelectionModel} this
24258 * @param {Number} rowIndex The selected index
24260 "rowdeselect" : true
24262 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24263 this.locked = false;
24266 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24268 * @cfg {Boolean} singleSelect
24269 * True to allow selection of only one row at a time (defaults to false)
24271 singleSelect : false,
24274 initEvents : function()
24277 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24278 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24279 //}else{ // allow click to work like normal
24280 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24282 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24283 this.grid.on("rowclick", this.handleMouseDown, this);
24285 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24286 "up" : function(e){
24288 this.selectPrevious(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);
24301 "down" : function(e){
24303 this.selectNext(e.shiftKey);
24304 }else if(this.last !== false && this.lastActive !== false){
24305 var last = this.last;
24306 this.selectRange(this.last, this.lastActive+1);
24307 this.grid.getView().focusRow(this.lastActive);
24308 if(last !== false){
24312 this.selectFirstRow();
24314 this.fireEvent("afterselectionchange", this);
24318 this.grid.store.on('load', function(){
24319 this.selections.clear();
24322 var view = this.grid.view;
24323 view.on("refresh", this.onRefresh, this);
24324 view.on("rowupdated", this.onRowUpdated, this);
24325 view.on("rowremoved", this.onRemove, this);
24330 onRefresh : function()
24332 var ds = this.grid.store, i, v = this.grid.view;
24333 var s = this.selections;
24334 s.each(function(r){
24335 if((i = ds.indexOfId(r.id)) != -1){
24344 onRemove : function(v, index, r){
24345 this.selections.remove(r);
24349 onRowUpdated : function(v, index, r){
24350 if(this.isSelected(r)){
24351 v.onRowSelect(index);
24357 * @param {Array} records The records to select
24358 * @param {Boolean} keepExisting (optional) True to keep existing selections
24360 selectRecords : function(records, keepExisting)
24363 this.clearSelections();
24365 var ds = this.grid.store;
24366 for(var i = 0, len = records.length; i < len; i++){
24367 this.selectRow(ds.indexOf(records[i]), true);
24372 * Gets the number of selected rows.
24375 getCount : function(){
24376 return this.selections.length;
24380 * Selects the first row in the grid.
24382 selectFirstRow : function(){
24387 * Select the last row.
24388 * @param {Boolean} keepExisting (optional) True to keep existing selections
24390 selectLastRow : function(keepExisting){
24391 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24392 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24396 * Selects the row immediately following the last selected row.
24397 * @param {Boolean} keepExisting (optional) True to keep existing selections
24399 selectNext : function(keepExisting)
24401 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24402 this.selectRow(this.last+1, keepExisting);
24403 this.grid.getView().focusRow(this.last);
24408 * Selects the row that precedes the last selected row.
24409 * @param {Boolean} keepExisting (optional) True to keep existing selections
24411 selectPrevious : function(keepExisting){
24413 this.selectRow(this.last-1, keepExisting);
24414 this.grid.getView().focusRow(this.last);
24419 * Returns the selected records
24420 * @return {Array} Array of selected records
24422 getSelections : function(){
24423 return [].concat(this.selections.items);
24427 * Returns the first selected record.
24430 getSelected : function(){
24431 return this.selections.itemAt(0);
24436 * Clears all selections.
24438 clearSelections : function(fast)
24444 var ds = this.grid.store;
24445 var s = this.selections;
24446 s.each(function(r){
24447 this.deselectRow(ds.indexOfId(r.id));
24451 this.selections.clear();
24458 * Selects all rows.
24460 selectAll : function(){
24464 this.selections.clear();
24465 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24466 this.selectRow(i, true);
24471 * Returns True if there is a selection.
24472 * @return {Boolean}
24474 hasSelection : function(){
24475 return this.selections.length > 0;
24479 * Returns True if the specified row is selected.
24480 * @param {Number/Record} record The record or index of the record to check
24481 * @return {Boolean}
24483 isSelected : function(index){
24484 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24485 return (r && this.selections.key(r.id) ? true : false);
24489 * Returns True if the specified record id is selected.
24490 * @param {String} id The id of record to check
24491 * @return {Boolean}
24493 isIdSelected : function(id){
24494 return (this.selections.key(id) ? true : false);
24499 handleMouseDBClick : function(e, t){
24503 handleMouseDown : function(e, t)
24505 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24506 if(this.isLocked() || rowIndex < 0 ){
24509 if(e.shiftKey && this.last !== false){
24510 var last = this.last;
24511 this.selectRange(last, rowIndex, e.ctrlKey);
24512 this.last = last; // reset the last
24516 var isSelected = this.isSelected(rowIndex);
24517 //Roo.log("select row:" + rowIndex);
24519 this.deselectRow(rowIndex);
24521 this.selectRow(rowIndex, true);
24525 if(e.button !== 0 && isSelected){
24526 alert('rowIndex 2: ' + rowIndex);
24527 view.focusRow(rowIndex);
24528 }else if(e.ctrlKey && isSelected){
24529 this.deselectRow(rowIndex);
24530 }else if(!isSelected){
24531 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24532 view.focusRow(rowIndex);
24536 this.fireEvent("afterselectionchange", this);
24539 handleDragableRowClick : function(grid, rowIndex, e)
24541 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24542 this.selectRow(rowIndex, false);
24543 grid.view.focusRow(rowIndex);
24544 this.fireEvent("afterselectionchange", this);
24549 * Selects multiple rows.
24550 * @param {Array} rows Array of the indexes of the row to select
24551 * @param {Boolean} keepExisting (optional) True to keep existing selections
24553 selectRows : function(rows, keepExisting){
24555 this.clearSelections();
24557 for(var i = 0, len = rows.length; i < len; i++){
24558 this.selectRow(rows[i], true);
24563 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24564 * @param {Number} startRow The index of the first row in the range
24565 * @param {Number} endRow The index of the last row in the range
24566 * @param {Boolean} keepExisting (optional) True to retain existing selections
24568 selectRange : function(startRow, endRow, keepExisting){
24573 this.clearSelections();
24575 if(startRow <= endRow){
24576 for(var i = startRow; i <= endRow; i++){
24577 this.selectRow(i, true);
24580 for(var i = startRow; i >= endRow; i--){
24581 this.selectRow(i, true);
24587 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24588 * @param {Number} startRow The index of the first row in the range
24589 * @param {Number} endRow The index of the last row in the range
24591 deselectRange : function(startRow, endRow, preventViewNotify){
24595 for(var i = startRow; i <= endRow; i++){
24596 this.deselectRow(i, preventViewNotify);
24602 * @param {Number} row The index of the row to select
24603 * @param {Boolean} keepExisting (optional) True to keep existing selections
24605 selectRow : function(index, keepExisting, preventViewNotify)
24607 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24610 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24611 if(!keepExisting || this.singleSelect){
24612 this.clearSelections();
24615 var r = this.grid.store.getAt(index);
24616 //console.log('selectRow - record id :' + r.id);
24618 this.selections.add(r);
24619 this.last = this.lastActive = index;
24620 if(!preventViewNotify){
24621 var proxy = new Roo.Element(
24622 this.grid.getRowDom(index)
24624 proxy.addClass('bg-info info');
24626 this.fireEvent("rowselect", this, index, r);
24627 this.fireEvent("selectionchange", this);
24633 * @param {Number} row The index of the row to deselect
24635 deselectRow : function(index, preventViewNotify)
24640 if(this.last == index){
24643 if(this.lastActive == index){
24644 this.lastActive = false;
24647 var r = this.grid.store.getAt(index);
24652 this.selections.remove(r);
24653 //.console.log('deselectRow - record id :' + r.id);
24654 if(!preventViewNotify){
24656 var proxy = new Roo.Element(
24657 this.grid.getRowDom(index)
24659 proxy.removeClass('bg-info info');
24661 this.fireEvent("rowdeselect", this, index);
24662 this.fireEvent("selectionchange", this);
24666 restoreLast : function(){
24668 this.last = this._last;
24673 acceptsNav : function(row, col, cm){
24674 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24678 onEditorKey : function(field, e){
24679 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24684 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24686 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24688 }else if(k == e.ENTER && !e.ctrlKey){
24692 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24694 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24696 }else if(k == e.ESC){
24700 g.startEditing(newCell[0], newCell[1]);
24706 * Ext JS Library 1.1.1
24707 * Copyright(c) 2006-2007, Ext JS, LLC.
24709 * Originally Released Under LGPL - original licence link has changed is not relivant.
24712 * <script type="text/javascript">
24716 * @class Roo.bootstrap.PagingToolbar
24717 * @extends Roo.bootstrap.NavSimplebar
24718 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24720 * Create a new PagingToolbar
24721 * @param {Object} config The config object
24722 * @param {Roo.data.Store} store
24724 Roo.bootstrap.PagingToolbar = function(config)
24726 // old args format still supported... - xtype is prefered..
24727 // created from xtype...
24729 this.ds = config.dataSource;
24731 if (config.store && !this.ds) {
24732 this.store= Roo.factory(config.store, Roo.data);
24733 this.ds = this.store;
24734 this.ds.xmodule = this.xmodule || false;
24737 this.toolbarItems = [];
24738 if (config.items) {
24739 this.toolbarItems = config.items;
24742 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24747 this.bind(this.ds);
24750 if (Roo.bootstrap.version == 4) {
24751 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24753 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24758 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24760 * @cfg {Roo.data.Store} dataSource
24761 * The underlying data store providing the paged data
24764 * @cfg {String/HTMLElement/Element} container
24765 * container The id or element that will contain the toolbar
24768 * @cfg {Boolean} displayInfo
24769 * True to display the displayMsg (defaults to false)
24772 * @cfg {Number} pageSize
24773 * The number of records to display per page (defaults to 20)
24777 * @cfg {String} displayMsg
24778 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24780 displayMsg : 'Displaying {0} - {1} of {2}',
24782 * @cfg {String} emptyMsg
24783 * The message to display when no records are found (defaults to "No data to display")
24785 emptyMsg : 'No data to display',
24787 * Customizable piece of the default paging text (defaults to "Page")
24790 beforePageText : "Page",
24792 * Customizable piece of the default paging text (defaults to "of %0")
24795 afterPageText : "of {0}",
24797 * Customizable piece of the default paging text (defaults to "First Page")
24800 firstText : "First Page",
24802 * Customizable piece of the default paging text (defaults to "Previous Page")
24805 prevText : "Previous Page",
24807 * Customizable piece of the default paging text (defaults to "Next Page")
24810 nextText : "Next Page",
24812 * Customizable piece of the default paging text (defaults to "Last Page")
24815 lastText : "Last Page",
24817 * Customizable piece of the default paging text (defaults to "Refresh")
24820 refreshText : "Refresh",
24824 onRender : function(ct, position)
24826 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24827 this.navgroup.parentId = this.id;
24828 this.navgroup.onRender(this.el, null);
24829 // add the buttons to the navgroup
24831 if(this.displayInfo){
24832 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24833 this.displayEl = this.el.select('.x-paging-info', true).first();
24834 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24835 // this.displayEl = navel.el.select('span',true).first();
24841 Roo.each(_this.buttons, function(e){ // this might need to use render????
24842 Roo.factory(e).render(_this.el);
24846 Roo.each(_this.toolbarItems, function(e) {
24847 _this.navgroup.addItem(e);
24851 this.first = this.navgroup.addItem({
24852 tooltip: this.firstText,
24853 cls: "prev btn-outline-secondary",
24854 html : ' <i class="fa fa-step-backward"></i>',
24856 preventDefault: true,
24857 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24860 this.prev = this.navgroup.addItem({
24861 tooltip: this.prevText,
24862 cls: "prev btn-outline-secondary",
24863 html : ' <i class="fa fa-backward"></i>',
24865 preventDefault: true,
24866 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24868 //this.addSeparator();
24871 var field = this.navgroup.addItem( {
24873 cls : 'x-paging-position btn-outline-secondary',
24875 html : this.beforePageText +
24876 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24877 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24880 this.field = field.el.select('input', true).first();
24881 this.field.on("keydown", this.onPagingKeydown, this);
24882 this.field.on("focus", function(){this.dom.select();});
24885 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24886 //this.field.setHeight(18);
24887 //this.addSeparator();
24888 this.next = this.navgroup.addItem({
24889 tooltip: this.nextText,
24890 cls: "next btn-outline-secondary",
24891 html : ' <i class="fa fa-forward"></i>',
24893 preventDefault: true,
24894 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24896 this.last = this.navgroup.addItem({
24897 tooltip: this.lastText,
24898 html : ' <i class="fa fa-step-forward"></i>',
24899 cls: "next btn-outline-secondary",
24901 preventDefault: true,
24902 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24904 //this.addSeparator();
24905 this.loading = this.navgroup.addItem({
24906 tooltip: this.refreshText,
24907 cls: "btn-outline-secondary",
24908 html : ' <i class="fa fa-refresh"></i>',
24909 preventDefault: true,
24910 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24916 updateInfo : function(){
24917 if(this.displayEl){
24918 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24919 var msg = count == 0 ?
24923 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24925 this.displayEl.update(msg);
24930 onLoad : function(ds, r, o)
24932 this.cursor = o.params.start ? o.params.start : 0;
24934 var d = this.getPageData(),
24939 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24940 this.field.dom.value = ap;
24941 this.first.setDisabled(ap == 1);
24942 this.prev.setDisabled(ap == 1);
24943 this.next.setDisabled(ap == ps);
24944 this.last.setDisabled(ap == ps);
24945 this.loading.enable();
24950 getPageData : function(){
24951 var total = this.ds.getTotalCount();
24954 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24955 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24960 onLoadError : function(){
24961 this.loading.enable();
24965 onPagingKeydown : function(e){
24966 var k = e.getKey();
24967 var d = this.getPageData();
24969 var v = this.field.dom.value, pageNum;
24970 if(!v || isNaN(pageNum = parseInt(v, 10))){
24971 this.field.dom.value = d.activePage;
24974 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24975 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24978 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))
24980 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24981 this.field.dom.value = pageNum;
24982 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24985 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24987 var v = this.field.dom.value, pageNum;
24988 var increment = (e.shiftKey) ? 10 : 1;
24989 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24992 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24993 this.field.dom.value = d.activePage;
24996 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24998 this.field.dom.value = parseInt(v, 10) + increment;
24999 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25000 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25007 beforeLoad : function(){
25009 this.loading.disable();
25014 onClick : function(which){
25023 ds.load({params:{start: 0, limit: this.pageSize}});
25026 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25029 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25032 var total = ds.getTotalCount();
25033 var extra = total % this.pageSize;
25034 var lastStart = extra ? (total - extra) : total-this.pageSize;
25035 ds.load({params:{start: lastStart, limit: this.pageSize}});
25038 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25044 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25045 * @param {Roo.data.Store} store The data store to unbind
25047 unbind : function(ds){
25048 ds.un("beforeload", this.beforeLoad, this);
25049 ds.un("load", this.onLoad, this);
25050 ds.un("loadexception", this.onLoadError, this);
25051 ds.un("remove", this.updateInfo, this);
25052 ds.un("add", this.updateInfo, this);
25053 this.ds = undefined;
25057 * Binds the paging toolbar to the specified {@link Roo.data.Store}
25058 * @param {Roo.data.Store} store The data store to bind
25060 bind : function(ds){
25061 ds.on("beforeload", this.beforeLoad, this);
25062 ds.on("load", this.onLoad, this);
25063 ds.on("loadexception", this.onLoadError, this);
25064 ds.on("remove", this.updateInfo, this);
25065 ds.on("add", this.updateInfo, this);
25076 * @class Roo.bootstrap.MessageBar
25077 * @extends Roo.bootstrap.Component
25078 * Bootstrap MessageBar class
25079 * @cfg {String} html contents of the MessageBar
25080 * @cfg {String} weight (info | success | warning | danger) default info
25081 * @cfg {String} beforeClass insert the bar before the given class
25082 * @cfg {Boolean} closable (true | false) default false
25083 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25086 * Create a new Element
25087 * @param {Object} config The config object
25090 Roo.bootstrap.MessageBar = function(config){
25091 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25094 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25100 beforeClass: 'bootstrap-sticky-wrap',
25102 getAutoCreate : function(){
25106 cls: 'alert alert-dismissable alert-' + this.weight,
25111 html: this.html || ''
25117 cfg.cls += ' alert-messages-fixed';
25131 onRender : function(ct, position)
25133 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25136 var cfg = Roo.apply({}, this.getAutoCreate());
25140 cfg.cls += ' ' + this.cls;
25143 cfg.style = this.style;
25145 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25147 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25150 this.el.select('>button.close').on('click', this.hide, this);
25156 if (!this.rendered) {
25162 this.fireEvent('show', this);
25168 if (!this.rendered) {
25174 this.fireEvent('hide', this);
25177 update : function()
25179 // var e = this.el.dom.firstChild;
25181 // if(this.closable){
25182 // e = e.nextSibling;
25185 // e.data = this.html || '';
25187 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25203 * @class Roo.bootstrap.Graph
25204 * @extends Roo.bootstrap.Component
25205 * Bootstrap Graph class
25209 @cfg {String} graphtype bar | vbar | pie
25210 @cfg {number} g_x coodinator | centre x (pie)
25211 @cfg {number} g_y coodinator | centre y (pie)
25212 @cfg {number} g_r radius (pie)
25213 @cfg {number} g_height height of the chart (respected by all elements in the set)
25214 @cfg {number} g_width width of the chart (respected by all elements in the set)
25215 @cfg {Object} title The title of the chart
25218 -opts (object) options for the chart
25220 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25221 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25223 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.
25224 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25226 o stretch (boolean)
25228 -opts (object) options for the pie
25231 o startAngle (number)
25232 o endAngle (number)
25236 * Create a new Input
25237 * @param {Object} config The config object
25240 Roo.bootstrap.Graph = function(config){
25241 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25247 * The img click event for the img.
25248 * @param {Roo.EventObject} e
25254 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25265 //g_colors: this.colors,
25272 getAutoCreate : function(){
25283 onRender : function(ct,position){
25286 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25288 if (typeof(Raphael) == 'undefined') {
25289 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25293 this.raphael = Raphael(this.el.dom);
25295 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25296 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25297 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25298 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25300 r.text(160, 10, "Single Series Chart").attr(txtattr);
25301 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25302 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25303 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25305 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25306 r.barchart(330, 10, 300, 220, data1);
25307 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25308 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25311 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25312 // r.barchart(30, 30, 560, 250, xdata, {
25313 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25314 // axis : "0 0 1 1",
25315 // axisxlabels : xdata
25316 // //yvalues : cols,
25319 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25321 // this.load(null,xdata,{
25322 // axis : "0 0 1 1",
25323 // axisxlabels : xdata
25328 load : function(graphtype,xdata,opts)
25330 this.raphael.clear();
25332 graphtype = this.graphtype;
25337 var r = this.raphael,
25338 fin = function () {
25339 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25341 fout = function () {
25342 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25344 pfin = function() {
25345 this.sector.stop();
25346 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25349 this.label[0].stop();
25350 this.label[0].attr({ r: 7.5 });
25351 this.label[1].attr({ "font-weight": 800 });
25354 pfout = function() {
25355 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25358 this.label[0].animate({ r: 5 }, 500, "bounce");
25359 this.label[1].attr({ "font-weight": 400 });
25365 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25368 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25371 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25372 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25374 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25381 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25386 setTitle: function(o)
25391 initEvents: function() {
25394 this.el.on('click', this.onClick, this);
25398 onClick : function(e)
25400 Roo.log('img onclick');
25401 this.fireEvent('click', this, e);
25413 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25416 * @class Roo.bootstrap.dash.NumberBox
25417 * @extends Roo.bootstrap.Component
25418 * Bootstrap NumberBox class
25419 * @cfg {String} headline Box headline
25420 * @cfg {String} content Box content
25421 * @cfg {String} icon Box icon
25422 * @cfg {String} footer Footer text
25423 * @cfg {String} fhref Footer href
25426 * Create a new NumberBox
25427 * @param {Object} config The config object
25431 Roo.bootstrap.dash.NumberBox = function(config){
25432 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25436 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25445 getAutoCreate : function(){
25449 cls : 'small-box ',
25457 cls : 'roo-headline',
25458 html : this.headline
25462 cls : 'roo-content',
25463 html : this.content
25477 cls : 'ion ' + this.icon
25486 cls : 'small-box-footer',
25487 href : this.fhref || '#',
25491 cfg.cn.push(footer);
25498 onRender : function(ct,position){
25499 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25506 setHeadline: function (value)
25508 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25511 setFooter: function (value, href)
25513 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25516 this.el.select('a.small-box-footer',true).first().attr('href', href);
25521 setContent: function (value)
25523 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25526 initEvents: function()
25540 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25543 * @class Roo.bootstrap.dash.TabBox
25544 * @extends Roo.bootstrap.Component
25545 * Bootstrap TabBox class
25546 * @cfg {String} title Title of the TabBox
25547 * @cfg {String} icon Icon of the TabBox
25548 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25549 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25552 * Create a new TabBox
25553 * @param {Object} config The config object
25557 Roo.bootstrap.dash.TabBox = function(config){
25558 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25563 * When a pane is added
25564 * @param {Roo.bootstrap.dash.TabPane} pane
25568 * @event activatepane
25569 * When a pane is activated
25570 * @param {Roo.bootstrap.dash.TabPane} pane
25572 "activatepane" : true
25580 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25585 tabScrollable : false,
25587 getChildContainer : function()
25589 return this.el.select('.tab-content', true).first();
25592 getAutoCreate : function(){
25596 cls: 'pull-left header',
25604 cls: 'fa ' + this.icon
25610 cls: 'nav nav-tabs pull-right',
25616 if(this.tabScrollable){
25623 cls: 'nav nav-tabs pull-right',
25634 cls: 'nav-tabs-custom',
25639 cls: 'tab-content no-padding',
25647 initEvents : function()
25649 //Roo.log('add add pane handler');
25650 this.on('addpane', this.onAddPane, this);
25653 * Updates the box title
25654 * @param {String} html to set the title to.
25656 setTitle : function(value)
25658 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25660 onAddPane : function(pane)
25662 this.panes.push(pane);
25663 //Roo.log('addpane');
25665 // tabs are rendere left to right..
25666 if(!this.showtabs){
25670 var ctr = this.el.select('.nav-tabs', true).first();
25673 var existing = ctr.select('.nav-tab',true);
25674 var qty = existing.getCount();;
25677 var tab = ctr.createChild({
25679 cls : 'nav-tab' + (qty ? '' : ' active'),
25687 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25690 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25692 pane.el.addClass('active');
25697 onTabClick : function(ev,un,ob,pane)
25699 //Roo.log('tab - prev default');
25700 ev.preventDefault();
25703 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25704 pane.tab.addClass('active');
25705 //Roo.log(pane.title);
25706 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25707 // technically we should have a deactivate event.. but maybe add later.
25708 // and it should not de-activate the selected tab...
25709 this.fireEvent('activatepane', pane);
25710 pane.el.addClass('active');
25711 pane.fireEvent('activate');
25716 getActivePane : function()
25719 Roo.each(this.panes, function(p) {
25720 if(p.el.hasClass('active')){
25741 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25743 * @class Roo.bootstrap.TabPane
25744 * @extends Roo.bootstrap.Component
25745 * Bootstrap TabPane class
25746 * @cfg {Boolean} active (false | true) Default false
25747 * @cfg {String} title title of panel
25751 * Create a new TabPane
25752 * @param {Object} config The config object
25755 Roo.bootstrap.dash.TabPane = function(config){
25756 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25762 * When a pane is activated
25763 * @param {Roo.bootstrap.dash.TabPane} pane
25770 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25775 // the tabBox that this is attached to.
25778 getAutoCreate : function()
25786 cfg.cls += ' active';
25791 initEvents : function()
25793 //Roo.log('trigger add pane handler');
25794 this.parent().fireEvent('addpane', this)
25798 * Updates the tab title
25799 * @param {String} html to set the title to.
25801 setTitle: function(str)
25807 this.tab.select('a', true).first().dom.innerHTML = str;
25824 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25827 * @class Roo.bootstrap.menu.Menu
25828 * @extends Roo.bootstrap.Component
25829 * Bootstrap Menu class - container for Menu
25830 * @cfg {String} html Text of the menu
25831 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25832 * @cfg {String} icon Font awesome icon
25833 * @cfg {String} pos Menu align to (top | bottom) default bottom
25837 * Create a new Menu
25838 * @param {Object} config The config object
25842 Roo.bootstrap.menu.Menu = function(config){
25843 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25847 * @event beforeshow
25848 * Fires before this menu is displayed
25849 * @param {Roo.bootstrap.menu.Menu} this
25853 * @event beforehide
25854 * Fires before this menu is hidden
25855 * @param {Roo.bootstrap.menu.Menu} this
25860 * Fires after this menu is displayed
25861 * @param {Roo.bootstrap.menu.Menu} this
25866 * Fires after this menu is hidden
25867 * @param {Roo.bootstrap.menu.Menu} this
25872 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25873 * @param {Roo.bootstrap.menu.Menu} this
25874 * @param {Roo.EventObject} e
25881 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25885 weight : 'default',
25890 getChildContainer : function() {
25891 if(this.isSubMenu){
25895 return this.el.select('ul.dropdown-menu', true).first();
25898 getAutoCreate : function()
25903 cls : 'roo-menu-text',
25911 cls : 'fa ' + this.icon
25922 cls : 'dropdown-button btn btn-' + this.weight,
25927 cls : 'dropdown-toggle btn btn-' + this.weight,
25937 cls : 'dropdown-menu'
25943 if(this.pos == 'top'){
25944 cfg.cls += ' dropup';
25947 if(this.isSubMenu){
25950 cls : 'dropdown-menu'
25957 onRender : function(ct, position)
25959 this.isSubMenu = ct.hasClass('dropdown-submenu');
25961 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25964 initEvents : function()
25966 if(this.isSubMenu){
25970 this.hidden = true;
25972 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25973 this.triggerEl.on('click', this.onTriggerPress, this);
25975 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25976 this.buttonEl.on('click', this.onClick, this);
25982 if(this.isSubMenu){
25986 return this.el.select('ul.dropdown-menu', true).first();
25989 onClick : function(e)
25991 this.fireEvent("click", this, e);
25994 onTriggerPress : function(e)
25996 if (this.isVisible()) {
26003 isVisible : function(){
26004 return !this.hidden;
26009 this.fireEvent("beforeshow", this);
26011 this.hidden = false;
26012 this.el.addClass('open');
26014 Roo.get(document).on("mouseup", this.onMouseUp, this);
26016 this.fireEvent("show", this);
26023 this.fireEvent("beforehide", this);
26025 this.hidden = true;
26026 this.el.removeClass('open');
26028 Roo.get(document).un("mouseup", this.onMouseUp);
26030 this.fireEvent("hide", this);
26033 onMouseUp : function()
26047 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26050 * @class Roo.bootstrap.menu.Item
26051 * @extends Roo.bootstrap.Component
26052 * Bootstrap MenuItem class
26053 * @cfg {Boolean} submenu (true | false) default false
26054 * @cfg {String} html text of the item
26055 * @cfg {String} href the link
26056 * @cfg {Boolean} disable (true | false) default false
26057 * @cfg {Boolean} preventDefault (true | false) default true
26058 * @cfg {String} icon Font awesome icon
26059 * @cfg {String} pos Submenu align to (left | right) default right
26063 * Create a new Item
26064 * @param {Object} config The config object
26068 Roo.bootstrap.menu.Item = function(config){
26069 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26073 * Fires when the mouse is hovering over this menu
26074 * @param {Roo.bootstrap.menu.Item} this
26075 * @param {Roo.EventObject} e
26080 * Fires when the mouse exits this menu
26081 * @param {Roo.bootstrap.menu.Item} this
26082 * @param {Roo.EventObject} e
26088 * The raw click event for the entire grid.
26089 * @param {Roo.EventObject} e
26095 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26100 preventDefault: true,
26105 getAutoCreate : function()
26110 cls : 'roo-menu-item-text',
26118 cls : 'fa ' + this.icon
26127 href : this.href || '#',
26134 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26138 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26140 if(this.pos == 'left'){
26141 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26148 initEvents : function()
26150 this.el.on('mouseover', this.onMouseOver, this);
26151 this.el.on('mouseout', this.onMouseOut, this);
26153 this.el.select('a', true).first().on('click', this.onClick, this);
26157 onClick : function(e)
26159 if(this.preventDefault){
26160 e.preventDefault();
26163 this.fireEvent("click", this, e);
26166 onMouseOver : function(e)
26168 if(this.submenu && this.pos == 'left'){
26169 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26172 this.fireEvent("mouseover", this, e);
26175 onMouseOut : function(e)
26177 this.fireEvent("mouseout", this, e);
26189 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26192 * @class Roo.bootstrap.menu.Separator
26193 * @extends Roo.bootstrap.Component
26194 * Bootstrap Separator class
26197 * Create a new Separator
26198 * @param {Object} config The config object
26202 Roo.bootstrap.menu.Separator = function(config){
26203 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26206 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26208 getAutoCreate : function(){
26229 * @class Roo.bootstrap.Tooltip
26230 * Bootstrap Tooltip class
26231 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26232 * to determine which dom element triggers the tooltip.
26234 * It needs to add support for additional attributes like tooltip-position
26237 * Create a new Toolti
26238 * @param {Object} config The config object
26241 Roo.bootstrap.Tooltip = function(config){
26242 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26244 this.alignment = Roo.bootstrap.Tooltip.alignment;
26246 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26247 this.alignment = config.alignment;
26252 Roo.apply(Roo.bootstrap.Tooltip, {
26254 * @function init initialize tooltip monitoring.
26258 currentTip : false,
26259 currentRegion : false,
26265 Roo.get(document).on('mouseover', this.enter ,this);
26266 Roo.get(document).on('mouseout', this.leave, this);
26269 this.currentTip = new Roo.bootstrap.Tooltip();
26272 enter : function(ev)
26274 var dom = ev.getTarget();
26276 //Roo.log(['enter',dom]);
26277 var el = Roo.fly(dom);
26278 if (this.currentEl) {
26280 //Roo.log(this.currentEl);
26281 //Roo.log(this.currentEl.contains(dom));
26282 if (this.currentEl == el) {
26285 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26291 if (this.currentTip.el) {
26292 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26296 if(!el || el.dom == document){
26302 // you can not look for children, as if el is the body.. then everythign is the child..
26303 if (!el.attr('tooltip')) { //
26304 if (!el.select("[tooltip]").elements.length) {
26307 // is the mouse over this child...?
26308 bindEl = el.select("[tooltip]").first();
26309 var xy = ev.getXY();
26310 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26311 //Roo.log("not in region.");
26314 //Roo.log("child element over..");
26317 this.currentEl = bindEl;
26318 this.currentTip.bind(bindEl);
26319 this.currentRegion = Roo.lib.Region.getRegion(dom);
26320 this.currentTip.enter();
26323 leave : function(ev)
26325 var dom = ev.getTarget();
26326 //Roo.log(['leave',dom]);
26327 if (!this.currentEl) {
26332 if (dom != this.currentEl.dom) {
26335 var xy = ev.getXY();
26336 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26339 // only activate leave if mouse cursor is outside... bounding box..
26344 if (this.currentTip) {
26345 this.currentTip.leave();
26347 //Roo.log('clear currentEl');
26348 this.currentEl = false;
26353 'left' : ['r-l', [-2,0], 'right'],
26354 'right' : ['l-r', [2,0], 'left'],
26355 'bottom' : ['t-b', [0,2], 'top'],
26356 'top' : [ 'b-t', [0,-2], 'bottom']
26362 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26367 delay : null, // can be { show : 300 , hide: 500}
26371 hoverState : null, //???
26373 placement : 'bottom',
26377 getAutoCreate : function(){
26384 cls : 'tooltip-arrow'
26387 cls : 'tooltip-inner'
26394 bind : function(el)
26400 enter : function () {
26402 if (this.timeout != null) {
26403 clearTimeout(this.timeout);
26406 this.hoverState = 'in';
26407 //Roo.log("enter - show");
26408 if (!this.delay || !this.delay.show) {
26413 this.timeout = setTimeout(function () {
26414 if (_t.hoverState == 'in') {
26417 }, this.delay.show);
26421 clearTimeout(this.timeout);
26423 this.hoverState = 'out';
26424 if (!this.delay || !this.delay.hide) {
26430 this.timeout = setTimeout(function () {
26431 //Roo.log("leave - timeout");
26433 if (_t.hoverState == 'out') {
26435 Roo.bootstrap.Tooltip.currentEl = false;
26440 show : function (msg)
26443 this.render(document.body);
26446 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26448 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26450 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26452 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26454 var placement = typeof this.placement == 'function' ?
26455 this.placement.call(this, this.el, on_el) :
26458 var autoToken = /\s?auto?\s?/i;
26459 var autoPlace = autoToken.test(placement);
26461 placement = placement.replace(autoToken, '') || 'top';
26465 //this.el.setXY([0,0]);
26467 //this.el.dom.style.display='block';
26469 //this.el.appendTo(on_el);
26471 var p = this.getPosition();
26472 var box = this.el.getBox();
26478 var align = this.alignment[placement];
26480 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26482 if(placement == 'top' || placement == 'bottom'){
26484 placement = 'right';
26487 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26488 placement = 'left';
26491 var scroll = Roo.select('body', true).first().getScroll();
26493 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26497 align = this.alignment[placement];
26500 this.el.alignTo(this.bindEl, align[0],align[1]);
26501 //var arrow = this.el.select('.arrow',true).first();
26502 //arrow.set(align[2],
26504 this.el.addClass(placement);
26506 this.el.addClass('in fade');
26508 this.hoverState = null;
26510 if (this.el.hasClass('fade')) {
26521 //this.el.setXY([0,0]);
26522 this.el.removeClass('in');
26538 * @class Roo.bootstrap.LocationPicker
26539 * @extends Roo.bootstrap.Component
26540 * Bootstrap LocationPicker class
26541 * @cfg {Number} latitude Position when init default 0
26542 * @cfg {Number} longitude Position when init default 0
26543 * @cfg {Number} zoom default 15
26544 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26545 * @cfg {Boolean} mapTypeControl default false
26546 * @cfg {Boolean} disableDoubleClickZoom default false
26547 * @cfg {Boolean} scrollwheel default true
26548 * @cfg {Boolean} streetViewControl default false
26549 * @cfg {Number} radius default 0
26550 * @cfg {String} locationName
26551 * @cfg {Boolean} draggable default true
26552 * @cfg {Boolean} enableAutocomplete default false
26553 * @cfg {Boolean} enableReverseGeocode default true
26554 * @cfg {String} markerTitle
26557 * Create a new LocationPicker
26558 * @param {Object} config The config object
26562 Roo.bootstrap.LocationPicker = function(config){
26564 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26569 * Fires when the picker initialized.
26570 * @param {Roo.bootstrap.LocationPicker} this
26571 * @param {Google Location} location
26575 * @event positionchanged
26576 * Fires when the picker position changed.
26577 * @param {Roo.bootstrap.LocationPicker} this
26578 * @param {Google Location} location
26580 positionchanged : true,
26583 * Fires when the map resize.
26584 * @param {Roo.bootstrap.LocationPicker} this
26589 * Fires when the map show.
26590 * @param {Roo.bootstrap.LocationPicker} this
26595 * Fires when the map hide.
26596 * @param {Roo.bootstrap.LocationPicker} this
26601 * Fires when click the map.
26602 * @param {Roo.bootstrap.LocationPicker} this
26603 * @param {Map event} e
26607 * @event mapRightClick
26608 * Fires when right click the map.
26609 * @param {Roo.bootstrap.LocationPicker} this
26610 * @param {Map event} e
26612 mapRightClick : true,
26614 * @event markerClick
26615 * Fires when click the marker.
26616 * @param {Roo.bootstrap.LocationPicker} this
26617 * @param {Map event} e
26619 markerClick : true,
26621 * @event markerRightClick
26622 * Fires when right click the marker.
26623 * @param {Roo.bootstrap.LocationPicker} this
26624 * @param {Map event} e
26626 markerRightClick : true,
26628 * @event OverlayViewDraw
26629 * Fires when OverlayView Draw
26630 * @param {Roo.bootstrap.LocationPicker} this
26632 OverlayViewDraw : true,
26634 * @event OverlayViewOnAdd
26635 * Fires when OverlayView Draw
26636 * @param {Roo.bootstrap.LocationPicker} this
26638 OverlayViewOnAdd : true,
26640 * @event OverlayViewOnRemove
26641 * Fires when OverlayView Draw
26642 * @param {Roo.bootstrap.LocationPicker} this
26644 OverlayViewOnRemove : true,
26646 * @event OverlayViewShow
26647 * Fires when OverlayView Draw
26648 * @param {Roo.bootstrap.LocationPicker} this
26649 * @param {Pixel} cpx
26651 OverlayViewShow : true,
26653 * @event OverlayViewHide
26654 * Fires when OverlayView Draw
26655 * @param {Roo.bootstrap.LocationPicker} this
26657 OverlayViewHide : true,
26659 * @event loadexception
26660 * Fires when load google lib failed.
26661 * @param {Roo.bootstrap.LocationPicker} this
26663 loadexception : true
26668 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26670 gMapContext: false,
26676 mapTypeControl: false,
26677 disableDoubleClickZoom: false,
26679 streetViewControl: false,
26683 enableAutocomplete: false,
26684 enableReverseGeocode: true,
26687 getAutoCreate: function()
26692 cls: 'roo-location-picker'
26698 initEvents: function(ct, position)
26700 if(!this.el.getWidth() || this.isApplied()){
26704 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26709 initial: function()
26711 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26712 this.fireEvent('loadexception', this);
26716 if(!this.mapTypeId){
26717 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26720 this.gMapContext = this.GMapContext();
26722 this.initOverlayView();
26724 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26728 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26729 _this.setPosition(_this.gMapContext.marker.position);
26732 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26733 _this.fireEvent('mapClick', this, event);
26737 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26738 _this.fireEvent('mapRightClick', this, event);
26742 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26743 _this.fireEvent('markerClick', this, event);
26747 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26748 _this.fireEvent('markerRightClick', this, event);
26752 this.setPosition(this.gMapContext.location);
26754 this.fireEvent('initial', this, this.gMapContext.location);
26757 initOverlayView: function()
26761 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26765 _this.fireEvent('OverlayViewDraw', _this);
26770 _this.fireEvent('OverlayViewOnAdd', _this);
26773 onRemove: function()
26775 _this.fireEvent('OverlayViewOnRemove', _this);
26778 show: function(cpx)
26780 _this.fireEvent('OverlayViewShow', _this, cpx);
26785 _this.fireEvent('OverlayViewHide', _this);
26791 fromLatLngToContainerPixel: function(event)
26793 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26796 isApplied: function()
26798 return this.getGmapContext() == false ? false : true;
26801 getGmapContext: function()
26803 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26806 GMapContext: function()
26808 var position = new google.maps.LatLng(this.latitude, this.longitude);
26810 var _map = new google.maps.Map(this.el.dom, {
26813 mapTypeId: this.mapTypeId,
26814 mapTypeControl: this.mapTypeControl,
26815 disableDoubleClickZoom: this.disableDoubleClickZoom,
26816 scrollwheel: this.scrollwheel,
26817 streetViewControl: this.streetViewControl,
26818 locationName: this.locationName,
26819 draggable: this.draggable,
26820 enableAutocomplete: this.enableAutocomplete,
26821 enableReverseGeocode: this.enableReverseGeocode
26824 var _marker = new google.maps.Marker({
26825 position: position,
26827 title: this.markerTitle,
26828 draggable: this.draggable
26835 location: position,
26836 radius: this.radius,
26837 locationName: this.locationName,
26838 addressComponents: {
26839 formatted_address: null,
26840 addressLine1: null,
26841 addressLine2: null,
26843 streetNumber: null,
26847 stateOrProvince: null
26850 domContainer: this.el.dom,
26851 geodecoder: new google.maps.Geocoder()
26855 drawCircle: function(center, radius, options)
26857 if (this.gMapContext.circle != null) {
26858 this.gMapContext.circle.setMap(null);
26862 options = Roo.apply({}, options, {
26863 strokeColor: "#0000FF",
26864 strokeOpacity: .35,
26866 fillColor: "#0000FF",
26870 options.map = this.gMapContext.map;
26871 options.radius = radius;
26872 options.center = center;
26873 this.gMapContext.circle = new google.maps.Circle(options);
26874 return this.gMapContext.circle;
26880 setPosition: function(location)
26882 this.gMapContext.location = location;
26883 this.gMapContext.marker.setPosition(location);
26884 this.gMapContext.map.panTo(location);
26885 this.drawCircle(location, this.gMapContext.radius, {});
26889 if (this.gMapContext.settings.enableReverseGeocode) {
26890 this.gMapContext.geodecoder.geocode({
26891 latLng: this.gMapContext.location
26892 }, function(results, status) {
26894 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26895 _this.gMapContext.locationName = results[0].formatted_address;
26896 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26898 _this.fireEvent('positionchanged', this, location);
26905 this.fireEvent('positionchanged', this, location);
26910 google.maps.event.trigger(this.gMapContext.map, "resize");
26912 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26914 this.fireEvent('resize', this);
26917 setPositionByLatLng: function(latitude, longitude)
26919 this.setPosition(new google.maps.LatLng(latitude, longitude));
26922 getCurrentPosition: function()
26925 latitude: this.gMapContext.location.lat(),
26926 longitude: this.gMapContext.location.lng()
26930 getAddressName: function()
26932 return this.gMapContext.locationName;
26935 getAddressComponents: function()
26937 return this.gMapContext.addressComponents;
26940 address_component_from_google_geocode: function(address_components)
26944 for (var i = 0; i < address_components.length; i++) {
26945 var component = address_components[i];
26946 if (component.types.indexOf("postal_code") >= 0) {
26947 result.postalCode = component.short_name;
26948 } else if (component.types.indexOf("street_number") >= 0) {
26949 result.streetNumber = component.short_name;
26950 } else if (component.types.indexOf("route") >= 0) {
26951 result.streetName = component.short_name;
26952 } else if (component.types.indexOf("neighborhood") >= 0) {
26953 result.city = component.short_name;
26954 } else if (component.types.indexOf("locality") >= 0) {
26955 result.city = component.short_name;
26956 } else if (component.types.indexOf("sublocality") >= 0) {
26957 result.district = component.short_name;
26958 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26959 result.stateOrProvince = component.short_name;
26960 } else if (component.types.indexOf("country") >= 0) {
26961 result.country = component.short_name;
26965 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26966 result.addressLine2 = "";
26970 setZoomLevel: function(zoom)
26972 this.gMapContext.map.setZoom(zoom);
26985 this.fireEvent('show', this);
26996 this.fireEvent('hide', this);
27001 Roo.apply(Roo.bootstrap.LocationPicker, {
27003 OverlayView : function(map, options)
27005 options = options || {};
27019 * @class Roo.bootstrap.Alert
27020 * @extends Roo.bootstrap.Component
27021 * Bootstrap Alert class
27022 * @cfg {String} title The title of alert
27023 * @cfg {String} html The content of alert
27024 * @cfg {String} weight ( success | info | warning | danger )
27025 * @cfg {String} faicon font-awesomeicon
27028 * Create a new alert
27029 * @param {Object} config The config object
27033 Roo.bootstrap.Alert = function(config){
27034 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27038 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
27045 getAutoCreate : function()
27054 cls : 'roo-alert-icon'
27059 cls : 'roo-alert-title',
27064 cls : 'roo-alert-text',
27071 cfg.cn[0].cls += ' fa ' + this.faicon;
27075 cfg.cls += ' alert-' + this.weight;
27081 initEvents: function()
27083 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27086 setTitle : function(str)
27088 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27091 setText : function(str)
27093 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27096 setWeight : function(weight)
27099 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27102 this.weight = weight;
27104 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27107 setIcon : function(icon)
27110 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27113 this.faicon = icon;
27115 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27136 * @class Roo.bootstrap.UploadCropbox
27137 * @extends Roo.bootstrap.Component
27138 * Bootstrap UploadCropbox class
27139 * @cfg {String} emptyText show when image has been loaded
27140 * @cfg {String} rotateNotify show when image too small to rotate
27141 * @cfg {Number} errorTimeout default 3000
27142 * @cfg {Number} minWidth default 300
27143 * @cfg {Number} minHeight default 300
27144 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27145 * @cfg {Boolean} isDocument (true|false) default false
27146 * @cfg {String} url action url
27147 * @cfg {String} paramName default 'imageUpload'
27148 * @cfg {String} method default POST
27149 * @cfg {Boolean} loadMask (true|false) default true
27150 * @cfg {Boolean} loadingText default 'Loading...'
27153 * Create a new UploadCropbox
27154 * @param {Object} config The config object
27157 Roo.bootstrap.UploadCropbox = function(config){
27158 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27162 * @event beforeselectfile
27163 * Fire before select file
27164 * @param {Roo.bootstrap.UploadCropbox} this
27166 "beforeselectfile" : true,
27169 * Fire after initEvent
27170 * @param {Roo.bootstrap.UploadCropbox} this
27175 * Fire after initEvent
27176 * @param {Roo.bootstrap.UploadCropbox} this
27177 * @param {String} data
27182 * Fire when preparing the file data
27183 * @param {Roo.bootstrap.UploadCropbox} this
27184 * @param {Object} file
27189 * Fire when get exception
27190 * @param {Roo.bootstrap.UploadCropbox} this
27191 * @param {XMLHttpRequest} xhr
27193 "exception" : true,
27195 * @event beforeloadcanvas
27196 * Fire before load the canvas
27197 * @param {Roo.bootstrap.UploadCropbox} this
27198 * @param {String} src
27200 "beforeloadcanvas" : true,
27203 * Fire when trash image
27204 * @param {Roo.bootstrap.UploadCropbox} this
27209 * Fire when download the image
27210 * @param {Roo.bootstrap.UploadCropbox} this
27214 * @event footerbuttonclick
27215 * Fire when footerbuttonclick
27216 * @param {Roo.bootstrap.UploadCropbox} this
27217 * @param {String} type
27219 "footerbuttonclick" : true,
27223 * @param {Roo.bootstrap.UploadCropbox} this
27228 * Fire when rotate the image
27229 * @param {Roo.bootstrap.UploadCropbox} this
27230 * @param {String} pos
27235 * Fire when inspect the file
27236 * @param {Roo.bootstrap.UploadCropbox} this
27237 * @param {Object} file
27242 * Fire when xhr upload the file
27243 * @param {Roo.bootstrap.UploadCropbox} this
27244 * @param {Object} data
27249 * Fire when arrange the file data
27250 * @param {Roo.bootstrap.UploadCropbox} this
27251 * @param {Object} formData
27256 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27259 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27261 emptyText : 'Click to upload image',
27262 rotateNotify : 'Image is too small to rotate',
27263 errorTimeout : 3000,
27277 cropType : 'image/jpeg',
27279 canvasLoaded : false,
27280 isDocument : false,
27282 paramName : 'imageUpload',
27284 loadingText : 'Loading...',
27287 getAutoCreate : function()
27291 cls : 'roo-upload-cropbox',
27295 cls : 'roo-upload-cropbox-selector',
27300 cls : 'roo-upload-cropbox-body',
27301 style : 'cursor:pointer',
27305 cls : 'roo-upload-cropbox-preview'
27309 cls : 'roo-upload-cropbox-thumb'
27313 cls : 'roo-upload-cropbox-empty-notify',
27314 html : this.emptyText
27318 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27319 html : this.rotateNotify
27325 cls : 'roo-upload-cropbox-footer',
27328 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27338 onRender : function(ct, position)
27340 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27342 if (this.buttons.length) {
27344 Roo.each(this.buttons, function(bb) {
27346 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27348 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27354 this.maskEl = this.el;
27358 initEvents : function()
27360 this.urlAPI = (window.createObjectURL && window) ||
27361 (window.URL && URL.revokeObjectURL && URL) ||
27362 (window.webkitURL && webkitURL);
27364 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27365 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27367 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27368 this.selectorEl.hide();
27370 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27371 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27373 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27374 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27375 this.thumbEl.hide();
27377 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27378 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27380 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27381 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27382 this.errorEl.hide();
27384 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27385 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27386 this.footerEl.hide();
27388 this.setThumbBoxSize();
27394 this.fireEvent('initial', this);
27401 window.addEventListener("resize", function() { _this.resize(); } );
27403 this.bodyEl.on('click', this.beforeSelectFile, this);
27406 this.bodyEl.on('touchstart', this.onTouchStart, this);
27407 this.bodyEl.on('touchmove', this.onTouchMove, this);
27408 this.bodyEl.on('touchend', this.onTouchEnd, this);
27412 this.bodyEl.on('mousedown', this.onMouseDown, this);
27413 this.bodyEl.on('mousemove', this.onMouseMove, this);
27414 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27415 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27416 Roo.get(document).on('mouseup', this.onMouseUp, this);
27419 this.selectorEl.on('change', this.onFileSelected, this);
27425 this.baseScale = 1;
27427 this.baseRotate = 1;
27428 this.dragable = false;
27429 this.pinching = false;
27432 this.cropData = false;
27433 this.notifyEl.dom.innerHTML = this.emptyText;
27435 this.selectorEl.dom.value = '';
27439 resize : function()
27441 if(this.fireEvent('resize', this) != false){
27442 this.setThumbBoxPosition();
27443 this.setCanvasPosition();
27447 onFooterButtonClick : function(e, el, o, type)
27450 case 'rotate-left' :
27451 this.onRotateLeft(e);
27453 case 'rotate-right' :
27454 this.onRotateRight(e);
27457 this.beforeSelectFile(e);
27472 this.fireEvent('footerbuttonclick', this, type);
27475 beforeSelectFile : function(e)
27477 e.preventDefault();
27479 if(this.fireEvent('beforeselectfile', this) != false){
27480 this.selectorEl.dom.click();
27484 onFileSelected : function(e)
27486 e.preventDefault();
27488 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27492 var file = this.selectorEl.dom.files[0];
27494 if(this.fireEvent('inspect', this, file) != false){
27495 this.prepare(file);
27500 trash : function(e)
27502 this.fireEvent('trash', this);
27505 download : function(e)
27507 this.fireEvent('download', this);
27510 loadCanvas : function(src)
27512 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27516 this.imageEl = document.createElement('img');
27520 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27522 this.imageEl.src = src;
27526 onLoadCanvas : function()
27528 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27529 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27531 this.bodyEl.un('click', this.beforeSelectFile, this);
27533 this.notifyEl.hide();
27534 this.thumbEl.show();
27535 this.footerEl.show();
27537 this.baseRotateLevel();
27539 if(this.isDocument){
27540 this.setThumbBoxSize();
27543 this.setThumbBoxPosition();
27545 this.baseScaleLevel();
27551 this.canvasLoaded = true;
27554 this.maskEl.unmask();
27559 setCanvasPosition : function()
27561 if(!this.canvasEl){
27565 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27566 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27568 this.previewEl.setLeft(pw);
27569 this.previewEl.setTop(ph);
27573 onMouseDown : function(e)
27577 this.dragable = true;
27578 this.pinching = false;
27580 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27581 this.dragable = false;
27585 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27586 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27590 onMouseMove : function(e)
27594 if(!this.canvasLoaded){
27598 if (!this.dragable){
27602 var minX = Math.ceil(this.thumbEl.getLeft(true));
27603 var minY = Math.ceil(this.thumbEl.getTop(true));
27605 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27606 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27608 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27609 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27611 x = x - this.mouseX;
27612 y = y - this.mouseY;
27614 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27615 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27617 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27618 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27620 this.previewEl.setLeft(bgX);
27621 this.previewEl.setTop(bgY);
27623 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27624 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27627 onMouseUp : function(e)
27631 this.dragable = false;
27634 onMouseWheel : function(e)
27638 this.startScale = this.scale;
27640 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27642 if(!this.zoomable()){
27643 this.scale = this.startScale;
27652 zoomable : function()
27654 var minScale = this.thumbEl.getWidth() / this.minWidth;
27656 if(this.minWidth < this.minHeight){
27657 minScale = this.thumbEl.getHeight() / this.minHeight;
27660 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27661 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27665 (this.rotate == 0 || this.rotate == 180) &&
27667 width > this.imageEl.OriginWidth ||
27668 height > this.imageEl.OriginHeight ||
27669 (width < this.minWidth && height < this.minHeight)
27677 (this.rotate == 90 || this.rotate == 270) &&
27679 width > this.imageEl.OriginWidth ||
27680 height > this.imageEl.OriginHeight ||
27681 (width < this.minHeight && height < this.minWidth)
27688 !this.isDocument &&
27689 (this.rotate == 0 || this.rotate == 180) &&
27691 width < this.minWidth ||
27692 width > this.imageEl.OriginWidth ||
27693 height < this.minHeight ||
27694 height > this.imageEl.OriginHeight
27701 !this.isDocument &&
27702 (this.rotate == 90 || this.rotate == 270) &&
27704 width < this.minHeight ||
27705 width > this.imageEl.OriginWidth ||
27706 height < this.minWidth ||
27707 height > this.imageEl.OriginHeight
27717 onRotateLeft : function(e)
27719 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27721 var minScale = this.thumbEl.getWidth() / this.minWidth;
27723 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27724 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27726 this.startScale = this.scale;
27728 while (this.getScaleLevel() < minScale){
27730 this.scale = this.scale + 1;
27732 if(!this.zoomable()){
27737 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27738 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27743 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27750 this.scale = this.startScale;
27752 this.onRotateFail();
27757 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27759 if(this.isDocument){
27760 this.setThumbBoxSize();
27761 this.setThumbBoxPosition();
27762 this.setCanvasPosition();
27767 this.fireEvent('rotate', this, 'left');
27771 onRotateRight : function(e)
27773 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27775 var minScale = this.thumbEl.getWidth() / this.minWidth;
27777 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27778 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27780 this.startScale = this.scale;
27782 while (this.getScaleLevel() < minScale){
27784 this.scale = this.scale + 1;
27786 if(!this.zoomable()){
27791 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27792 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27797 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27804 this.scale = this.startScale;
27806 this.onRotateFail();
27811 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27813 if(this.isDocument){
27814 this.setThumbBoxSize();
27815 this.setThumbBoxPosition();
27816 this.setCanvasPosition();
27821 this.fireEvent('rotate', this, 'right');
27824 onRotateFail : function()
27826 this.errorEl.show(true);
27830 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27835 this.previewEl.dom.innerHTML = '';
27837 var canvasEl = document.createElement("canvas");
27839 var contextEl = canvasEl.getContext("2d");
27841 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27842 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27843 var center = this.imageEl.OriginWidth / 2;
27845 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27846 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27847 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27848 center = this.imageEl.OriginHeight / 2;
27851 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27853 contextEl.translate(center, center);
27854 contextEl.rotate(this.rotate * Math.PI / 180);
27856 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27858 this.canvasEl = document.createElement("canvas");
27860 this.contextEl = this.canvasEl.getContext("2d");
27862 switch (this.rotate) {
27865 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27866 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27868 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27873 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27874 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27876 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27877 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);
27881 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27886 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27887 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27889 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27890 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27894 this.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);
27899 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27900 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27902 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27903 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27907 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);
27914 this.previewEl.appendChild(this.canvasEl);
27916 this.setCanvasPosition();
27921 if(!this.canvasLoaded){
27925 var imageCanvas = document.createElement("canvas");
27927 var imageContext = imageCanvas.getContext("2d");
27929 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27930 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27932 var center = imageCanvas.width / 2;
27934 imageContext.translate(center, center);
27936 imageContext.rotate(this.rotate * Math.PI / 180);
27938 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27940 var canvas = document.createElement("canvas");
27942 var context = canvas.getContext("2d");
27944 canvas.width = this.minWidth;
27945 canvas.height = this.minHeight;
27947 switch (this.rotate) {
27950 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27951 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27953 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27954 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27956 var targetWidth = this.minWidth - 2 * x;
27957 var targetHeight = this.minHeight - 2 * y;
27961 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27962 scale = targetWidth / width;
27965 if(x > 0 && y == 0){
27966 scale = targetHeight / height;
27969 if(x > 0 && y > 0){
27970 scale = targetWidth / width;
27972 if(width < height){
27973 scale = targetHeight / height;
27977 context.scale(scale, scale);
27979 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27980 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27982 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27983 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27985 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27990 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27991 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27993 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27994 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27996 var targetWidth = this.minWidth - 2 * x;
27997 var targetHeight = this.minHeight - 2 * y;
28001 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28002 scale = targetWidth / width;
28005 if(x > 0 && y == 0){
28006 scale = targetHeight / height;
28009 if(x > 0 && y > 0){
28010 scale = targetWidth / width;
28012 if(width < height){
28013 scale = targetHeight / height;
28017 context.scale(scale, scale);
28019 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28020 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28022 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28023 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28025 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28027 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28032 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28033 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28035 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28036 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28038 var targetWidth = this.minWidth - 2 * x;
28039 var targetHeight = this.minHeight - 2 * y;
28043 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28044 scale = targetWidth / width;
28047 if(x > 0 && y == 0){
28048 scale = targetHeight / height;
28051 if(x > 0 && y > 0){
28052 scale = targetWidth / width;
28054 if(width < height){
28055 scale = targetHeight / height;
28059 context.scale(scale, scale);
28061 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28062 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28064 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28065 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28067 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28068 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28070 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28075 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28076 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28078 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28079 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28081 var targetWidth = this.minWidth - 2 * x;
28082 var targetHeight = this.minHeight - 2 * y;
28086 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28087 scale = targetWidth / width;
28090 if(x > 0 && y == 0){
28091 scale = targetHeight / height;
28094 if(x > 0 && y > 0){
28095 scale = targetWidth / width;
28097 if(width < height){
28098 scale = targetHeight / height;
28102 context.scale(scale, scale);
28104 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28105 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28107 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28108 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28110 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28112 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28119 this.cropData = canvas.toDataURL(this.cropType);
28121 if(this.fireEvent('crop', this, this.cropData) !== false){
28122 this.process(this.file, this.cropData);
28129 setThumbBoxSize : function()
28133 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28134 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28135 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28137 this.minWidth = width;
28138 this.minHeight = height;
28140 if(this.rotate == 90 || this.rotate == 270){
28141 this.minWidth = height;
28142 this.minHeight = width;
28147 width = Math.ceil(this.minWidth * height / this.minHeight);
28149 if(this.minWidth > this.minHeight){
28151 height = Math.ceil(this.minHeight * width / this.minWidth);
28154 this.thumbEl.setStyle({
28155 width : width + 'px',
28156 height : height + 'px'
28163 setThumbBoxPosition : function()
28165 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28166 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28168 this.thumbEl.setLeft(x);
28169 this.thumbEl.setTop(y);
28173 baseRotateLevel : function()
28175 this.baseRotate = 1;
28178 typeof(this.exif) != 'undefined' &&
28179 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28180 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28182 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28185 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28189 baseScaleLevel : function()
28193 if(this.isDocument){
28195 if(this.baseRotate == 6 || this.baseRotate == 8){
28197 height = this.thumbEl.getHeight();
28198 this.baseScale = height / this.imageEl.OriginWidth;
28200 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28201 width = this.thumbEl.getWidth();
28202 this.baseScale = width / this.imageEl.OriginHeight;
28208 height = this.thumbEl.getHeight();
28209 this.baseScale = height / this.imageEl.OriginHeight;
28211 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28212 width = this.thumbEl.getWidth();
28213 this.baseScale = width / this.imageEl.OriginWidth;
28219 if(this.baseRotate == 6 || this.baseRotate == 8){
28221 width = this.thumbEl.getHeight();
28222 this.baseScale = width / this.imageEl.OriginHeight;
28224 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28225 height = this.thumbEl.getWidth();
28226 this.baseScale = height / this.imageEl.OriginHeight;
28229 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28230 height = this.thumbEl.getWidth();
28231 this.baseScale = height / this.imageEl.OriginHeight;
28233 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28234 width = this.thumbEl.getHeight();
28235 this.baseScale = width / this.imageEl.OriginWidth;
28242 width = this.thumbEl.getWidth();
28243 this.baseScale = width / this.imageEl.OriginWidth;
28245 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28246 height = this.thumbEl.getHeight();
28247 this.baseScale = height / this.imageEl.OriginHeight;
28250 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28252 height = this.thumbEl.getHeight();
28253 this.baseScale = height / this.imageEl.OriginHeight;
28255 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28256 width = this.thumbEl.getWidth();
28257 this.baseScale = width / this.imageEl.OriginWidth;
28265 getScaleLevel : function()
28267 return this.baseScale * Math.pow(1.1, this.scale);
28270 onTouchStart : function(e)
28272 if(!this.canvasLoaded){
28273 this.beforeSelectFile(e);
28277 var touches = e.browserEvent.touches;
28283 if(touches.length == 1){
28284 this.onMouseDown(e);
28288 if(touches.length != 2){
28294 for(var i = 0, finger; finger = touches[i]; i++){
28295 coords.push(finger.pageX, finger.pageY);
28298 var x = Math.pow(coords[0] - coords[2], 2);
28299 var y = Math.pow(coords[1] - coords[3], 2);
28301 this.startDistance = Math.sqrt(x + y);
28303 this.startScale = this.scale;
28305 this.pinching = true;
28306 this.dragable = false;
28310 onTouchMove : function(e)
28312 if(!this.pinching && !this.dragable){
28316 var touches = e.browserEvent.touches;
28323 this.onMouseMove(e);
28329 for(var i = 0, finger; finger = touches[i]; i++){
28330 coords.push(finger.pageX, finger.pageY);
28333 var x = Math.pow(coords[0] - coords[2], 2);
28334 var y = Math.pow(coords[1] - coords[3], 2);
28336 this.endDistance = Math.sqrt(x + y);
28338 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28340 if(!this.zoomable()){
28341 this.scale = this.startScale;
28349 onTouchEnd : function(e)
28351 this.pinching = false;
28352 this.dragable = false;
28356 process : function(file, crop)
28359 this.maskEl.mask(this.loadingText);
28362 this.xhr = new XMLHttpRequest();
28364 file.xhr = this.xhr;
28366 this.xhr.open(this.method, this.url, true);
28369 "Accept": "application/json",
28370 "Cache-Control": "no-cache",
28371 "X-Requested-With": "XMLHttpRequest"
28374 for (var headerName in headers) {
28375 var headerValue = headers[headerName];
28377 this.xhr.setRequestHeader(headerName, headerValue);
28383 this.xhr.onload = function()
28385 _this.xhrOnLoad(_this.xhr);
28388 this.xhr.onerror = function()
28390 _this.xhrOnError(_this.xhr);
28393 var formData = new FormData();
28395 formData.append('returnHTML', 'NO');
28398 formData.append('crop', crop);
28401 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28402 formData.append(this.paramName, file, file.name);
28405 if(typeof(file.filename) != 'undefined'){
28406 formData.append('filename', file.filename);
28409 if(typeof(file.mimetype) != 'undefined'){
28410 formData.append('mimetype', file.mimetype);
28413 if(this.fireEvent('arrange', this, formData) != false){
28414 this.xhr.send(formData);
28418 xhrOnLoad : function(xhr)
28421 this.maskEl.unmask();
28424 if (xhr.readyState !== 4) {
28425 this.fireEvent('exception', this, xhr);
28429 var response = Roo.decode(xhr.responseText);
28431 if(!response.success){
28432 this.fireEvent('exception', this, xhr);
28436 var response = Roo.decode(xhr.responseText);
28438 this.fireEvent('upload', this, response);
28442 xhrOnError : function()
28445 this.maskEl.unmask();
28448 Roo.log('xhr on error');
28450 var response = Roo.decode(xhr.responseText);
28456 prepare : function(file)
28459 this.maskEl.mask(this.loadingText);
28465 if(typeof(file) === 'string'){
28466 this.loadCanvas(file);
28470 if(!file || !this.urlAPI){
28475 this.cropType = file.type;
28479 if(this.fireEvent('prepare', this, this.file) != false){
28481 var reader = new FileReader();
28483 reader.onload = function (e) {
28484 if (e.target.error) {
28485 Roo.log(e.target.error);
28489 var buffer = e.target.result,
28490 dataView = new DataView(buffer),
28492 maxOffset = dataView.byteLength - 4,
28496 if (dataView.getUint16(0) === 0xffd8) {
28497 while (offset < maxOffset) {
28498 markerBytes = dataView.getUint16(offset);
28500 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28501 markerLength = dataView.getUint16(offset + 2) + 2;
28502 if (offset + markerLength > dataView.byteLength) {
28503 Roo.log('Invalid meta data: Invalid segment size.');
28507 if(markerBytes == 0xffe1){
28508 _this.parseExifData(
28515 offset += markerLength;
28525 var url = _this.urlAPI.createObjectURL(_this.file);
28527 _this.loadCanvas(url);
28532 reader.readAsArrayBuffer(this.file);
28538 parseExifData : function(dataView, offset, length)
28540 var tiffOffset = offset + 10,
28544 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28545 // No Exif data, might be XMP data instead
28549 // Check for the ASCII code for "Exif" (0x45786966):
28550 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28551 // No Exif data, might be XMP data instead
28554 if (tiffOffset + 8 > dataView.byteLength) {
28555 Roo.log('Invalid Exif data: Invalid segment size.');
28558 // Check for the two null bytes:
28559 if (dataView.getUint16(offset + 8) !== 0x0000) {
28560 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28563 // Check the byte alignment:
28564 switch (dataView.getUint16(tiffOffset)) {
28566 littleEndian = true;
28569 littleEndian = false;
28572 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28575 // Check for the TIFF tag marker (0x002A):
28576 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28577 Roo.log('Invalid Exif data: Missing TIFF marker.');
28580 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28581 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28583 this.parseExifTags(
28586 tiffOffset + dirOffset,
28591 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28596 if (dirOffset + 6 > dataView.byteLength) {
28597 Roo.log('Invalid Exif data: Invalid directory offset.');
28600 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28601 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28602 if (dirEndOffset + 4 > dataView.byteLength) {
28603 Roo.log('Invalid Exif data: Invalid directory size.');
28606 for (i = 0; i < tagsNumber; i += 1) {
28610 dirOffset + 2 + 12 * i, // tag offset
28614 // Return the offset to the next directory:
28615 return dataView.getUint32(dirEndOffset, littleEndian);
28618 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28620 var tag = dataView.getUint16(offset, littleEndian);
28622 this.exif[tag] = this.getExifValue(
28626 dataView.getUint16(offset + 2, littleEndian), // tag type
28627 dataView.getUint32(offset + 4, littleEndian), // tag length
28632 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28634 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28643 Roo.log('Invalid Exif data: Invalid tag type.');
28647 tagSize = tagType.size * length;
28648 // Determine if the value is contained in the dataOffset bytes,
28649 // or if the value at the dataOffset is a pointer to the actual data:
28650 dataOffset = tagSize > 4 ?
28651 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28652 if (dataOffset + tagSize > dataView.byteLength) {
28653 Roo.log('Invalid Exif data: Invalid data offset.');
28656 if (length === 1) {
28657 return tagType.getValue(dataView, dataOffset, littleEndian);
28660 for (i = 0; i < length; i += 1) {
28661 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28664 if (tagType.ascii) {
28666 // Concatenate the chars:
28667 for (i = 0; i < values.length; i += 1) {
28669 // Ignore the terminating NULL byte(s):
28670 if (c === '\u0000') {
28682 Roo.apply(Roo.bootstrap.UploadCropbox, {
28684 'Orientation': 0x0112
28688 1: 0, //'top-left',
28690 3: 180, //'bottom-right',
28691 // 4: 'bottom-left',
28693 6: 90, //'right-top',
28694 // 7: 'right-bottom',
28695 8: 270 //'left-bottom'
28699 // byte, 8-bit unsigned int:
28701 getValue: function (dataView, dataOffset) {
28702 return dataView.getUint8(dataOffset);
28706 // ascii, 8-bit byte:
28708 getValue: function (dataView, dataOffset) {
28709 return String.fromCharCode(dataView.getUint8(dataOffset));
28714 // short, 16 bit int:
28716 getValue: function (dataView, dataOffset, littleEndian) {
28717 return dataView.getUint16(dataOffset, littleEndian);
28721 // long, 32 bit int:
28723 getValue: function (dataView, dataOffset, littleEndian) {
28724 return dataView.getUint32(dataOffset, littleEndian);
28728 // rational = two long values, first is numerator, second is denominator:
28730 getValue: function (dataView, dataOffset, littleEndian) {
28731 return dataView.getUint32(dataOffset, littleEndian) /
28732 dataView.getUint32(dataOffset + 4, littleEndian);
28736 // slong, 32 bit signed int:
28738 getValue: function (dataView, dataOffset, littleEndian) {
28739 return dataView.getInt32(dataOffset, littleEndian);
28743 // srational, two slongs, first is numerator, second is denominator:
28745 getValue: function (dataView, dataOffset, littleEndian) {
28746 return dataView.getInt32(dataOffset, littleEndian) /
28747 dataView.getInt32(dataOffset + 4, littleEndian);
28757 cls : 'btn-group roo-upload-cropbox-rotate-left',
28758 action : 'rotate-left',
28762 cls : 'btn btn-default',
28763 html : '<i class="fa fa-undo"></i>'
28769 cls : 'btn-group roo-upload-cropbox-picture',
28770 action : 'picture',
28774 cls : 'btn btn-default',
28775 html : '<i class="fa fa-picture-o"></i>'
28781 cls : 'btn-group roo-upload-cropbox-rotate-right',
28782 action : 'rotate-right',
28786 cls : 'btn btn-default',
28787 html : '<i class="fa fa-repeat"></i>'
28795 cls : 'btn-group roo-upload-cropbox-rotate-left',
28796 action : 'rotate-left',
28800 cls : 'btn btn-default',
28801 html : '<i class="fa fa-undo"></i>'
28807 cls : 'btn-group roo-upload-cropbox-download',
28808 action : 'download',
28812 cls : 'btn btn-default',
28813 html : '<i class="fa fa-download"></i>'
28819 cls : 'btn-group roo-upload-cropbox-crop',
28824 cls : 'btn btn-default',
28825 html : '<i class="fa fa-crop"></i>'
28831 cls : 'btn-group roo-upload-cropbox-trash',
28836 cls : 'btn btn-default',
28837 html : '<i class="fa fa-trash"></i>'
28843 cls : 'btn-group roo-upload-cropbox-rotate-right',
28844 action : 'rotate-right',
28848 cls : 'btn btn-default',
28849 html : '<i class="fa fa-repeat"></i>'
28857 cls : 'btn-group roo-upload-cropbox-rotate-left',
28858 action : 'rotate-left',
28862 cls : 'btn btn-default',
28863 html : '<i class="fa fa-undo"></i>'
28869 cls : 'btn-group roo-upload-cropbox-rotate-right',
28870 action : 'rotate-right',
28874 cls : 'btn btn-default',
28875 html : '<i class="fa fa-repeat"></i>'
28888 * @class Roo.bootstrap.DocumentManager
28889 * @extends Roo.bootstrap.Component
28890 * Bootstrap DocumentManager class
28891 * @cfg {String} paramName default 'imageUpload'
28892 * @cfg {String} toolTipName default 'filename'
28893 * @cfg {String} method default POST
28894 * @cfg {String} url action url
28895 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28896 * @cfg {Boolean} multiple multiple upload default true
28897 * @cfg {Number} thumbSize default 300
28898 * @cfg {String} fieldLabel
28899 * @cfg {Number} labelWidth default 4
28900 * @cfg {String} labelAlign (left|top) default left
28901 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28902 * @cfg {Number} labellg set the width of label (1-12)
28903 * @cfg {Number} labelmd set the width of label (1-12)
28904 * @cfg {Number} labelsm set the width of label (1-12)
28905 * @cfg {Number} labelxs set the width of label (1-12)
28908 * Create a new DocumentManager
28909 * @param {Object} config The config object
28912 Roo.bootstrap.DocumentManager = function(config){
28913 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28916 this.delegates = [];
28921 * Fire when initial the DocumentManager
28922 * @param {Roo.bootstrap.DocumentManager} this
28927 * inspect selected file
28928 * @param {Roo.bootstrap.DocumentManager} this
28929 * @param {File} file
28934 * Fire when xhr load exception
28935 * @param {Roo.bootstrap.DocumentManager} this
28936 * @param {XMLHttpRequest} xhr
28938 "exception" : true,
28940 * @event afterupload
28941 * Fire when xhr load exception
28942 * @param {Roo.bootstrap.DocumentManager} this
28943 * @param {XMLHttpRequest} xhr
28945 "afterupload" : true,
28948 * prepare the form data
28949 * @param {Roo.bootstrap.DocumentManager} this
28950 * @param {Object} formData
28955 * Fire when remove the file
28956 * @param {Roo.bootstrap.DocumentManager} this
28957 * @param {Object} file
28962 * Fire after refresh the file
28963 * @param {Roo.bootstrap.DocumentManager} this
28968 * Fire after click the image
28969 * @param {Roo.bootstrap.DocumentManager} this
28970 * @param {Object} file
28975 * Fire when upload a image and editable set to true
28976 * @param {Roo.bootstrap.DocumentManager} this
28977 * @param {Object} file
28981 * @event beforeselectfile
28982 * Fire before select file
28983 * @param {Roo.bootstrap.DocumentManager} this
28985 "beforeselectfile" : true,
28988 * Fire before process file
28989 * @param {Roo.bootstrap.DocumentManager} this
28990 * @param {Object} file
28994 * @event previewrendered
28995 * Fire when preview rendered
28996 * @param {Roo.bootstrap.DocumentManager} this
28997 * @param {Object} file
28999 "previewrendered" : true,
29002 "previewResize" : true
29007 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
29016 paramName : 'imageUpload',
29017 toolTipName : 'filename',
29020 labelAlign : 'left',
29030 getAutoCreate : function()
29032 var managerWidget = {
29034 cls : 'roo-document-manager',
29038 cls : 'roo-document-manager-selector',
29043 cls : 'roo-document-manager-uploader',
29047 cls : 'roo-document-manager-upload-btn',
29048 html : '<i class="fa fa-plus"></i>'
29059 cls : 'column col-md-12',
29064 if(this.fieldLabel.length){
29069 cls : 'column col-md-12',
29070 html : this.fieldLabel
29074 cls : 'column col-md-12',
29079 if(this.labelAlign == 'left'){
29084 html : this.fieldLabel
29093 if(this.labelWidth > 12){
29094 content[0].style = "width: " + this.labelWidth + 'px';
29097 if(this.labelWidth < 13 && this.labelmd == 0){
29098 this.labelmd = this.labelWidth;
29101 if(this.labellg > 0){
29102 content[0].cls += ' col-lg-' + this.labellg;
29103 content[1].cls += ' col-lg-' + (12 - this.labellg);
29106 if(this.labelmd > 0){
29107 content[0].cls += ' col-md-' + this.labelmd;
29108 content[1].cls += ' col-md-' + (12 - this.labelmd);
29111 if(this.labelsm > 0){
29112 content[0].cls += ' col-sm-' + this.labelsm;
29113 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29116 if(this.labelxs > 0){
29117 content[0].cls += ' col-xs-' + this.labelxs;
29118 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29126 cls : 'row clearfix',
29134 initEvents : function()
29136 this.managerEl = this.el.select('.roo-document-manager', true).first();
29137 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29139 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29140 this.selectorEl.hide();
29143 this.selectorEl.attr('multiple', 'multiple');
29146 this.selectorEl.on('change', this.onFileSelected, this);
29148 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29149 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29151 this.uploader.on('click', this.onUploaderClick, this);
29153 this.renderProgressDialog();
29157 window.addEventListener("resize", function() { _this.refresh(); } );
29159 this.fireEvent('initial', this);
29162 renderProgressDialog : function()
29166 this.progressDialog = new Roo.bootstrap.Modal({
29167 cls : 'roo-document-manager-progress-dialog',
29168 allow_close : false,
29178 btnclick : function() {
29179 _this.uploadCancel();
29185 this.progressDialog.render(Roo.get(document.body));
29187 this.progress = new Roo.bootstrap.Progress({
29188 cls : 'roo-document-manager-progress',
29193 this.progress.render(this.progressDialog.getChildContainer());
29195 this.progressBar = new Roo.bootstrap.ProgressBar({
29196 cls : 'roo-document-manager-progress-bar',
29199 aria_valuemax : 12,
29203 this.progressBar.render(this.progress.getChildContainer());
29206 onUploaderClick : function(e)
29208 e.preventDefault();
29210 if(this.fireEvent('beforeselectfile', this) != false){
29211 this.selectorEl.dom.click();
29216 onFileSelected : function(e)
29218 e.preventDefault();
29220 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29224 Roo.each(this.selectorEl.dom.files, function(file){
29225 if(this.fireEvent('inspect', this, file) != false){
29226 this.files.push(file);
29236 this.selectorEl.dom.value = '';
29238 if(!this.files || !this.files.length){
29242 if(this.boxes > 0 && this.files.length > this.boxes){
29243 this.files = this.files.slice(0, this.boxes);
29246 this.uploader.show();
29248 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29249 this.uploader.hide();
29258 Roo.each(this.files, function(file){
29260 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29261 var f = this.renderPreview(file);
29266 if(file.type.indexOf('image') != -1){
29267 this.delegates.push(
29269 _this.process(file);
29270 }).createDelegate(this)
29278 _this.process(file);
29279 }).createDelegate(this)
29284 this.files = files;
29286 this.delegates = this.delegates.concat(docs);
29288 if(!this.delegates.length){
29293 this.progressBar.aria_valuemax = this.delegates.length;
29300 arrange : function()
29302 if(!this.delegates.length){
29303 this.progressDialog.hide();
29308 var delegate = this.delegates.shift();
29310 this.progressDialog.show();
29312 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29314 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29319 refresh : function()
29321 this.uploader.show();
29323 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29324 this.uploader.hide();
29327 Roo.isTouch ? this.closable(false) : this.closable(true);
29329 this.fireEvent('refresh', this);
29332 onRemove : function(e, el, o)
29334 e.preventDefault();
29336 this.fireEvent('remove', this, o);
29340 remove : function(o)
29344 Roo.each(this.files, function(file){
29345 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29354 this.files = files;
29361 Roo.each(this.files, function(file){
29366 file.target.remove();
29375 onClick : function(e, el, o)
29377 e.preventDefault();
29379 this.fireEvent('click', this, o);
29383 closable : function(closable)
29385 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29387 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29399 xhrOnLoad : function(xhr)
29401 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29405 if (xhr.readyState !== 4) {
29407 this.fireEvent('exception', this, xhr);
29411 var response = Roo.decode(xhr.responseText);
29413 if(!response.success){
29415 this.fireEvent('exception', this, xhr);
29419 var file = this.renderPreview(response.data);
29421 this.files.push(file);
29425 this.fireEvent('afterupload', this, xhr);
29429 xhrOnError : function(xhr)
29431 Roo.log('xhr on error');
29433 var response = Roo.decode(xhr.responseText);
29440 process : function(file)
29442 if(this.fireEvent('process', this, file) !== false){
29443 if(this.editable && file.type.indexOf('image') != -1){
29444 this.fireEvent('edit', this, file);
29448 this.uploadStart(file, false);
29455 uploadStart : function(file, crop)
29457 this.xhr = new XMLHttpRequest();
29459 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29464 file.xhr = this.xhr;
29466 this.managerEl.createChild({
29468 cls : 'roo-document-manager-loading',
29472 tooltip : file.name,
29473 cls : 'roo-document-manager-thumb',
29474 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29480 this.xhr.open(this.method, this.url, true);
29483 "Accept": "application/json",
29484 "Cache-Control": "no-cache",
29485 "X-Requested-With": "XMLHttpRequest"
29488 for (var headerName in headers) {
29489 var headerValue = headers[headerName];
29491 this.xhr.setRequestHeader(headerName, headerValue);
29497 this.xhr.onload = function()
29499 _this.xhrOnLoad(_this.xhr);
29502 this.xhr.onerror = function()
29504 _this.xhrOnError(_this.xhr);
29507 var formData = new FormData();
29509 formData.append('returnHTML', 'NO');
29512 formData.append('crop', crop);
29515 formData.append(this.paramName, file, file.name);
29522 if(this.fireEvent('prepare', this, formData, options) != false){
29524 if(options.manually){
29528 this.xhr.send(formData);
29532 this.uploadCancel();
29535 uploadCancel : function()
29541 this.delegates = [];
29543 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29550 renderPreview : function(file)
29552 if(typeof(file.target) != 'undefined' && file.target){
29556 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29558 var previewEl = this.managerEl.createChild({
29560 cls : 'roo-document-manager-preview',
29564 tooltip : file[this.toolTipName],
29565 cls : 'roo-document-manager-thumb',
29566 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29571 html : '<i class="fa fa-times-circle"></i>'
29576 var close = previewEl.select('button.close', true).first();
29578 close.on('click', this.onRemove, this, file);
29580 file.target = previewEl;
29582 var image = previewEl.select('img', true).first();
29586 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29588 image.on('click', this.onClick, this, file);
29590 this.fireEvent('previewrendered', this, file);
29596 onPreviewLoad : function(file, image)
29598 if(typeof(file.target) == 'undefined' || !file.target){
29602 var width = image.dom.naturalWidth || image.dom.width;
29603 var height = image.dom.naturalHeight || image.dom.height;
29605 if(!this.previewResize) {
29609 if(width > height){
29610 file.target.addClass('wide');
29614 file.target.addClass('tall');
29619 uploadFromSource : function(file, crop)
29621 this.xhr = new XMLHttpRequest();
29623 this.managerEl.createChild({
29625 cls : 'roo-document-manager-loading',
29629 tooltip : file.name,
29630 cls : 'roo-document-manager-thumb',
29631 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29637 this.xhr.open(this.method, this.url, true);
29640 "Accept": "application/json",
29641 "Cache-Control": "no-cache",
29642 "X-Requested-With": "XMLHttpRequest"
29645 for (var headerName in headers) {
29646 var headerValue = headers[headerName];
29648 this.xhr.setRequestHeader(headerName, headerValue);
29654 this.xhr.onload = function()
29656 _this.xhrOnLoad(_this.xhr);
29659 this.xhr.onerror = function()
29661 _this.xhrOnError(_this.xhr);
29664 var formData = new FormData();
29666 formData.append('returnHTML', 'NO');
29668 formData.append('crop', crop);
29670 if(typeof(file.filename) != 'undefined'){
29671 formData.append('filename', file.filename);
29674 if(typeof(file.mimetype) != 'undefined'){
29675 formData.append('mimetype', file.mimetype);
29680 if(this.fireEvent('prepare', this, formData) != false){
29681 this.xhr.send(formData);
29691 * @class Roo.bootstrap.DocumentViewer
29692 * @extends Roo.bootstrap.Component
29693 * Bootstrap DocumentViewer class
29694 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29695 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29698 * Create a new DocumentViewer
29699 * @param {Object} config The config object
29702 Roo.bootstrap.DocumentViewer = function(config){
29703 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29708 * Fire after initEvent
29709 * @param {Roo.bootstrap.DocumentViewer} this
29715 * @param {Roo.bootstrap.DocumentViewer} this
29720 * Fire after download button
29721 * @param {Roo.bootstrap.DocumentViewer} this
29726 * Fire after trash button
29727 * @param {Roo.bootstrap.DocumentViewer} this
29734 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29736 showDownload : true,
29740 getAutoCreate : function()
29744 cls : 'roo-document-viewer',
29748 cls : 'roo-document-viewer-body',
29752 cls : 'roo-document-viewer-thumb',
29756 cls : 'roo-document-viewer-image'
29764 cls : 'roo-document-viewer-footer',
29767 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29771 cls : 'btn-group roo-document-viewer-download',
29775 cls : 'btn btn-default',
29776 html : '<i class="fa fa-download"></i>'
29782 cls : 'btn-group roo-document-viewer-trash',
29786 cls : 'btn btn-default',
29787 html : '<i class="fa fa-trash"></i>'
29800 initEvents : function()
29802 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29803 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29805 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29806 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29808 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29809 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29811 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29812 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29814 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29815 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29817 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29818 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29820 this.bodyEl.on('click', this.onClick, this);
29821 this.downloadBtn.on('click', this.onDownload, this);
29822 this.trashBtn.on('click', this.onTrash, this);
29824 this.downloadBtn.hide();
29825 this.trashBtn.hide();
29827 if(this.showDownload){
29828 this.downloadBtn.show();
29831 if(this.showTrash){
29832 this.trashBtn.show();
29835 if(!this.showDownload && !this.showTrash) {
29836 this.footerEl.hide();
29841 initial : function()
29843 this.fireEvent('initial', this);
29847 onClick : function(e)
29849 e.preventDefault();
29851 this.fireEvent('click', this);
29854 onDownload : function(e)
29856 e.preventDefault();
29858 this.fireEvent('download', this);
29861 onTrash : function(e)
29863 e.preventDefault();
29865 this.fireEvent('trash', this);
29877 * @class Roo.bootstrap.NavProgressBar
29878 * @extends Roo.bootstrap.Component
29879 * Bootstrap NavProgressBar class
29882 * Create a new nav progress bar
29883 * @param {Object} config The config object
29886 Roo.bootstrap.NavProgressBar = function(config){
29887 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29889 this.bullets = this.bullets || [];
29891 // Roo.bootstrap.NavProgressBar.register(this);
29895 * Fires when the active item changes
29896 * @param {Roo.bootstrap.NavProgressBar} this
29897 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29898 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29905 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29910 getAutoCreate : function()
29912 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29916 cls : 'roo-navigation-bar-group',
29920 cls : 'roo-navigation-top-bar'
29924 cls : 'roo-navigation-bullets-bar',
29928 cls : 'roo-navigation-bar'
29935 cls : 'roo-navigation-bottom-bar'
29945 initEvents: function()
29950 onRender : function(ct, position)
29952 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29954 if(this.bullets.length){
29955 Roo.each(this.bullets, function(b){
29964 addItem : function(cfg)
29966 var item = new Roo.bootstrap.NavProgressItem(cfg);
29968 item.parentId = this.id;
29969 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29972 var top = new Roo.bootstrap.Element({
29974 cls : 'roo-navigation-bar-text'
29977 var bottom = new Roo.bootstrap.Element({
29979 cls : 'roo-navigation-bar-text'
29982 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29983 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29985 var topText = new Roo.bootstrap.Element({
29987 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29990 var bottomText = new Roo.bootstrap.Element({
29992 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29995 topText.onRender(top.el, null);
29996 bottomText.onRender(bottom.el, null);
29999 item.bottomEl = bottom;
30002 this.barItems.push(item);
30007 getActive : function()
30009 var active = false;
30011 Roo.each(this.barItems, function(v){
30013 if (!v.isActive()) {
30025 setActiveItem : function(item)
30029 Roo.each(this.barItems, function(v){
30030 if (v.rid == item.rid) {
30034 if (v.isActive()) {
30035 v.setActive(false);
30040 item.setActive(true);
30042 this.fireEvent('changed', this, item, prev);
30045 getBarItem: function(rid)
30049 Roo.each(this.barItems, function(e) {
30050 if (e.rid != rid) {
30061 indexOfItem : function(item)
30065 Roo.each(this.barItems, function(v, i){
30067 if (v.rid != item.rid) {
30078 setActiveNext : function()
30080 var i = this.indexOfItem(this.getActive());
30082 if (i > this.barItems.length) {
30086 this.setActiveItem(this.barItems[i+1]);
30089 setActivePrev : function()
30091 var i = this.indexOfItem(this.getActive());
30097 this.setActiveItem(this.barItems[i-1]);
30100 format : function()
30102 if(!this.barItems.length){
30106 var width = 100 / this.barItems.length;
30108 Roo.each(this.barItems, function(i){
30109 i.el.setStyle('width', width + '%');
30110 i.topEl.el.setStyle('width', width + '%');
30111 i.bottomEl.el.setStyle('width', width + '%');
30120 * Nav Progress Item
30125 * @class Roo.bootstrap.NavProgressItem
30126 * @extends Roo.bootstrap.Component
30127 * Bootstrap NavProgressItem class
30128 * @cfg {String} rid the reference id
30129 * @cfg {Boolean} active (true|false) Is item active default false
30130 * @cfg {Boolean} disabled (true|false) Is item active default false
30131 * @cfg {String} html
30132 * @cfg {String} position (top|bottom) text position default bottom
30133 * @cfg {String} icon show icon instead of number
30136 * Create a new NavProgressItem
30137 * @param {Object} config The config object
30139 Roo.bootstrap.NavProgressItem = function(config){
30140 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30145 * The raw click event for the entire grid.
30146 * @param {Roo.bootstrap.NavProgressItem} this
30147 * @param {Roo.EventObject} e
30154 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30160 position : 'bottom',
30163 getAutoCreate : function()
30165 var iconCls = 'roo-navigation-bar-item-icon';
30167 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30171 cls: 'roo-navigation-bar-item',
30181 cfg.cls += ' active';
30184 cfg.cls += ' disabled';
30190 disable : function()
30192 this.setDisabled(true);
30195 enable : function()
30197 this.setDisabled(false);
30200 initEvents: function()
30202 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30204 this.iconEl.on('click', this.onClick, this);
30207 onClick : function(e)
30209 e.preventDefault();
30215 if(this.fireEvent('click', this, e) === false){
30219 this.parent().setActiveItem(this);
30222 isActive: function ()
30224 return this.active;
30227 setActive : function(state)
30229 if(this.active == state){
30233 this.active = state;
30236 this.el.addClass('active');
30240 this.el.removeClass('active');
30245 setDisabled : function(state)
30247 if(this.disabled == state){
30251 this.disabled = state;
30254 this.el.addClass('disabled');
30258 this.el.removeClass('disabled');
30261 tooltipEl : function()
30263 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30276 * @class Roo.bootstrap.FieldLabel
30277 * @extends Roo.bootstrap.Component
30278 * Bootstrap FieldLabel class
30279 * @cfg {String} html contents of the element
30280 * @cfg {String} tag tag of the element default label
30281 * @cfg {String} cls class of the element
30282 * @cfg {String} target label target
30283 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30284 * @cfg {String} invalidClass default "text-warning"
30285 * @cfg {String} validClass default "text-success"
30286 * @cfg {String} iconTooltip default "This field is required"
30287 * @cfg {String} indicatorpos (left|right) default left
30290 * Create a new FieldLabel
30291 * @param {Object} config The config object
30294 Roo.bootstrap.FieldLabel = function(config){
30295 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30300 * Fires after the field has been marked as invalid.
30301 * @param {Roo.form.FieldLabel} this
30302 * @param {String} msg The validation message
30307 * Fires after the field has been validated with no errors.
30308 * @param {Roo.form.FieldLabel} this
30314 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30321 invalidClass : 'has-warning',
30322 validClass : 'has-success',
30323 iconTooltip : 'This field is required',
30324 indicatorpos : 'left',
30326 getAutoCreate : function(){
30329 if (!this.allowBlank) {
30335 cls : 'roo-bootstrap-field-label ' + this.cls,
30340 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30341 tooltip : this.iconTooltip
30350 if(this.indicatorpos == 'right'){
30353 cls : 'roo-bootstrap-field-label ' + this.cls,
30362 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30363 tooltip : this.iconTooltip
30372 initEvents: function()
30374 Roo.bootstrap.Element.superclass.initEvents.call(this);
30376 this.indicator = this.indicatorEl();
30378 if(this.indicator){
30379 this.indicator.removeClass('visible');
30380 this.indicator.addClass('invisible');
30383 Roo.bootstrap.FieldLabel.register(this);
30386 indicatorEl : function()
30388 var indicator = this.el.select('i.roo-required-indicator',true).first();
30399 * Mark this field as valid
30401 markValid : function()
30403 if(this.indicator){
30404 this.indicator.removeClass('visible');
30405 this.indicator.addClass('invisible');
30408 this.el.removeClass(this.invalidClass);
30410 this.el.addClass(this.validClass);
30412 this.fireEvent('valid', this);
30416 * Mark this field as invalid
30417 * @param {String} msg The validation message
30419 markInvalid : function(msg)
30421 if(this.indicator){
30422 this.indicator.removeClass('invisible');
30423 this.indicator.addClass('visible');
30426 this.el.removeClass(this.validClass);
30428 this.el.addClass(this.invalidClass);
30430 this.fireEvent('invalid', this, msg);
30436 Roo.apply(Roo.bootstrap.FieldLabel, {
30441 * register a FieldLabel Group
30442 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30444 register : function(label)
30446 if(this.groups.hasOwnProperty(label.target)){
30450 this.groups[label.target] = label;
30454 * fetch a FieldLabel Group based on the target
30455 * @param {string} target
30456 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30458 get: function(target) {
30459 if (typeof(this.groups[target]) == 'undefined') {
30463 return this.groups[target] ;
30472 * page DateSplitField.
30478 * @class Roo.bootstrap.DateSplitField
30479 * @extends Roo.bootstrap.Component
30480 * Bootstrap DateSplitField class
30481 * @cfg {string} fieldLabel - the label associated
30482 * @cfg {Number} labelWidth set the width of label (0-12)
30483 * @cfg {String} labelAlign (top|left)
30484 * @cfg {Boolean} dayAllowBlank (true|false) default false
30485 * @cfg {Boolean} monthAllowBlank (true|false) default false
30486 * @cfg {Boolean} yearAllowBlank (true|false) default false
30487 * @cfg {string} dayPlaceholder
30488 * @cfg {string} monthPlaceholder
30489 * @cfg {string} yearPlaceholder
30490 * @cfg {string} dayFormat default 'd'
30491 * @cfg {string} monthFormat default 'm'
30492 * @cfg {string} yearFormat default 'Y'
30493 * @cfg {Number} labellg set the width of label (1-12)
30494 * @cfg {Number} labelmd set the width of label (1-12)
30495 * @cfg {Number} labelsm set the width of label (1-12)
30496 * @cfg {Number} labelxs set the width of label (1-12)
30500 * Create a new DateSplitField
30501 * @param {Object} config The config object
30504 Roo.bootstrap.DateSplitField = function(config){
30505 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30511 * getting the data of years
30512 * @param {Roo.bootstrap.DateSplitField} this
30513 * @param {Object} years
30518 * getting the data of days
30519 * @param {Roo.bootstrap.DateSplitField} this
30520 * @param {Object} days
30525 * Fires after the field has been marked as invalid.
30526 * @param {Roo.form.Field} this
30527 * @param {String} msg The validation message
30532 * Fires after the field has been validated with no errors.
30533 * @param {Roo.form.Field} this
30539 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30542 labelAlign : 'top',
30544 dayAllowBlank : false,
30545 monthAllowBlank : false,
30546 yearAllowBlank : false,
30547 dayPlaceholder : '',
30548 monthPlaceholder : '',
30549 yearPlaceholder : '',
30553 isFormField : true,
30559 getAutoCreate : function()
30563 cls : 'row roo-date-split-field-group',
30568 cls : 'form-hidden-field roo-date-split-field-group-value',
30574 var labelCls = 'col-md-12';
30575 var contentCls = 'col-md-4';
30577 if(this.fieldLabel){
30581 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30585 html : this.fieldLabel
30590 if(this.labelAlign == 'left'){
30592 if(this.labelWidth > 12){
30593 label.style = "width: " + this.labelWidth + 'px';
30596 if(this.labelWidth < 13 && this.labelmd == 0){
30597 this.labelmd = this.labelWidth;
30600 if(this.labellg > 0){
30601 labelCls = ' col-lg-' + this.labellg;
30602 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30605 if(this.labelmd > 0){
30606 labelCls = ' col-md-' + this.labelmd;
30607 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30610 if(this.labelsm > 0){
30611 labelCls = ' col-sm-' + this.labelsm;
30612 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30615 if(this.labelxs > 0){
30616 labelCls = ' col-xs-' + this.labelxs;
30617 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30621 label.cls += ' ' + labelCls;
30623 cfg.cn.push(label);
30626 Roo.each(['day', 'month', 'year'], function(t){
30629 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30636 inputEl: function ()
30638 return this.el.select('.roo-date-split-field-group-value', true).first();
30641 onRender : function(ct, position)
30645 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30647 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30649 this.dayField = new Roo.bootstrap.ComboBox({
30650 allowBlank : this.dayAllowBlank,
30651 alwaysQuery : true,
30652 displayField : 'value',
30655 forceSelection : true,
30657 placeholder : this.dayPlaceholder,
30658 selectOnFocus : true,
30659 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30660 triggerAction : 'all',
30662 valueField : 'value',
30663 store : new Roo.data.SimpleStore({
30664 data : (function() {
30666 _this.fireEvent('days', _this, days);
30669 fields : [ 'value' ]
30672 select : function (_self, record, index)
30674 _this.setValue(_this.getValue());
30679 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30681 this.monthField = new Roo.bootstrap.MonthField({
30682 after : '<i class=\"fa fa-calendar\"></i>',
30683 allowBlank : this.monthAllowBlank,
30684 placeholder : this.monthPlaceholder,
30687 render : function (_self)
30689 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30690 e.preventDefault();
30694 select : function (_self, oldvalue, newvalue)
30696 _this.setValue(_this.getValue());
30701 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30703 this.yearField = new Roo.bootstrap.ComboBox({
30704 allowBlank : this.yearAllowBlank,
30705 alwaysQuery : true,
30706 displayField : 'value',
30709 forceSelection : true,
30711 placeholder : this.yearPlaceholder,
30712 selectOnFocus : true,
30713 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30714 triggerAction : 'all',
30716 valueField : 'value',
30717 store : new Roo.data.SimpleStore({
30718 data : (function() {
30720 _this.fireEvent('years', _this, years);
30723 fields : [ 'value' ]
30726 select : function (_self, record, index)
30728 _this.setValue(_this.getValue());
30733 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30736 setValue : function(v, format)
30738 this.inputEl.dom.value = v;
30740 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30742 var d = Date.parseDate(v, f);
30749 this.setDay(d.format(this.dayFormat));
30750 this.setMonth(d.format(this.monthFormat));
30751 this.setYear(d.format(this.yearFormat));
30758 setDay : function(v)
30760 this.dayField.setValue(v);
30761 this.inputEl.dom.value = this.getValue();
30766 setMonth : function(v)
30768 this.monthField.setValue(v, true);
30769 this.inputEl.dom.value = this.getValue();
30774 setYear : function(v)
30776 this.yearField.setValue(v);
30777 this.inputEl.dom.value = this.getValue();
30782 getDay : function()
30784 return this.dayField.getValue();
30787 getMonth : function()
30789 return this.monthField.getValue();
30792 getYear : function()
30794 return this.yearField.getValue();
30797 getValue : function()
30799 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30801 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30811 this.inputEl.dom.value = '';
30816 validate : function()
30818 var d = this.dayField.validate();
30819 var m = this.monthField.validate();
30820 var y = this.yearField.validate();
30825 (!this.dayAllowBlank && !d) ||
30826 (!this.monthAllowBlank && !m) ||
30827 (!this.yearAllowBlank && !y)
30832 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30841 this.markInvalid();
30846 markValid : function()
30849 var label = this.el.select('label', true).first();
30850 var icon = this.el.select('i.fa-star', true).first();
30856 this.fireEvent('valid', this);
30860 * Mark this field as invalid
30861 * @param {String} msg The validation message
30863 markInvalid : function(msg)
30866 var label = this.el.select('label', true).first();
30867 var icon = this.el.select('i.fa-star', true).first();
30869 if(label && !icon){
30870 this.el.select('.roo-date-split-field-label', true).createChild({
30872 cls : 'text-danger fa fa-lg fa-star',
30873 tooltip : 'This field is required',
30874 style : 'margin-right:5px;'
30878 this.fireEvent('invalid', this, msg);
30881 clearInvalid : function()
30883 var label = this.el.select('label', true).first();
30884 var icon = this.el.select('i.fa-star', true).first();
30890 this.fireEvent('valid', this);
30893 getName: function()
30903 * http://masonry.desandro.com
30905 * The idea is to render all the bricks based on vertical width...
30907 * The original code extends 'outlayer' - we might need to use that....
30913 * @class Roo.bootstrap.LayoutMasonry
30914 * @extends Roo.bootstrap.Component
30915 * Bootstrap Layout Masonry class
30918 * Create a new Element
30919 * @param {Object} config The config object
30922 Roo.bootstrap.LayoutMasonry = function(config){
30924 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30928 Roo.bootstrap.LayoutMasonry.register(this);
30934 * Fire after layout the items
30935 * @param {Roo.bootstrap.LayoutMasonry} this
30936 * @param {Roo.EventObject} e
30943 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30946 * @cfg {Boolean} isLayoutInstant = no animation?
30948 isLayoutInstant : false, // needed?
30951 * @cfg {Number} boxWidth width of the columns
30956 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30961 * @cfg {Number} padWidth padding below box..
30966 * @cfg {Number} gutter gutter width..
30971 * @cfg {Number} maxCols maximum number of columns
30977 * @cfg {Boolean} isAutoInitial defalut true
30979 isAutoInitial : true,
30984 * @cfg {Boolean} isHorizontal defalut false
30986 isHorizontal : false,
30988 currentSize : null,
30994 bricks: null, //CompositeElement
30998 _isLayoutInited : false,
31000 // isAlternative : false, // only use for vertical layout...
31003 * @cfg {Number} alternativePadWidth padding below box..
31005 alternativePadWidth : 50,
31007 selectedBrick : [],
31009 getAutoCreate : function(){
31011 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31015 cls: 'blog-masonary-wrapper ' + this.cls,
31017 cls : 'mas-boxes masonary'
31024 getChildContainer: function( )
31026 if (this.boxesEl) {
31027 return this.boxesEl;
31030 this.boxesEl = this.el.select('.mas-boxes').first();
31032 return this.boxesEl;
31036 initEvents : function()
31040 if(this.isAutoInitial){
31041 Roo.log('hook children rendered');
31042 this.on('childrenrendered', function() {
31043 Roo.log('children rendered');
31049 initial : function()
31051 this.selectedBrick = [];
31053 this.currentSize = this.el.getBox(true);
31055 Roo.EventManager.onWindowResize(this.resize, this);
31057 if(!this.isAutoInitial){
31065 //this.layout.defer(500,this);
31069 resize : function()
31071 var cs = this.el.getBox(true);
31074 this.currentSize.width == cs.width &&
31075 this.currentSize.x == cs.x &&
31076 this.currentSize.height == cs.height &&
31077 this.currentSize.y == cs.y
31079 Roo.log("no change in with or X or Y");
31083 this.currentSize = cs;
31089 layout : function()
31091 this._resetLayout();
31093 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31095 this.layoutItems( isInstant );
31097 this._isLayoutInited = true;
31099 this.fireEvent('layout', this);
31103 _resetLayout : function()
31105 if(this.isHorizontal){
31106 this.horizontalMeasureColumns();
31110 this.verticalMeasureColumns();
31114 verticalMeasureColumns : function()
31116 this.getContainerWidth();
31118 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31119 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31123 var boxWidth = this.boxWidth + this.padWidth;
31125 if(this.containerWidth < this.boxWidth){
31126 boxWidth = this.containerWidth
31129 var containerWidth = this.containerWidth;
31131 var cols = Math.floor(containerWidth / boxWidth);
31133 this.cols = Math.max( cols, 1 );
31135 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31137 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31139 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31141 this.colWidth = boxWidth + avail - this.padWidth;
31143 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31144 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31147 horizontalMeasureColumns : function()
31149 this.getContainerWidth();
31151 var boxWidth = this.boxWidth;
31153 if(this.containerWidth < boxWidth){
31154 boxWidth = this.containerWidth;
31157 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31159 this.el.setHeight(boxWidth);
31163 getContainerWidth : function()
31165 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31168 layoutItems : function( isInstant )
31170 Roo.log(this.bricks);
31172 var items = Roo.apply([], this.bricks);
31174 if(this.isHorizontal){
31175 this._horizontalLayoutItems( items , isInstant );
31179 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31180 // this._verticalAlternativeLayoutItems( items , isInstant );
31184 this._verticalLayoutItems( items , isInstant );
31188 _verticalLayoutItems : function ( items , isInstant)
31190 if ( !items || !items.length ) {
31195 ['xs', 'xs', 'xs', 'tall'],
31196 ['xs', 'xs', 'tall'],
31197 ['xs', 'xs', 'sm'],
31198 ['xs', 'xs', 'xs'],
31204 ['sm', 'xs', 'xs'],
31208 ['tall', 'xs', 'xs', 'xs'],
31209 ['tall', 'xs', 'xs'],
31221 Roo.each(items, function(item, k){
31223 switch (item.size) {
31224 // these layouts take up a full box,
31235 boxes.push([item]);
31258 var filterPattern = function(box, length)
31266 var pattern = box.slice(0, length);
31270 Roo.each(pattern, function(i){
31271 format.push(i.size);
31274 Roo.each(standard, function(s){
31276 if(String(s) != String(format)){
31285 if(!match && length == 1){
31290 filterPattern(box, length - 1);
31294 queue.push(pattern);
31296 box = box.slice(length, box.length);
31298 filterPattern(box, 4);
31304 Roo.each(boxes, function(box, k){
31310 if(box.length == 1){
31315 filterPattern(box, 4);
31319 this._processVerticalLayoutQueue( queue, isInstant );
31323 // _verticalAlternativeLayoutItems : function( items , isInstant )
31325 // if ( !items || !items.length ) {
31329 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31333 _horizontalLayoutItems : function ( items , isInstant)
31335 if ( !items || !items.length || items.length < 3) {
31341 var eItems = items.slice(0, 3);
31343 items = items.slice(3, items.length);
31346 ['xs', 'xs', 'xs', 'wide'],
31347 ['xs', 'xs', 'wide'],
31348 ['xs', 'xs', 'sm'],
31349 ['xs', 'xs', 'xs'],
31355 ['sm', 'xs', 'xs'],
31359 ['wide', 'xs', 'xs', 'xs'],
31360 ['wide', 'xs', 'xs'],
31373 Roo.each(items, function(item, k){
31375 switch (item.size) {
31386 boxes.push([item]);
31410 var filterPattern = function(box, length)
31418 var pattern = box.slice(0, length);
31422 Roo.each(pattern, function(i){
31423 format.push(i.size);
31426 Roo.each(standard, function(s){
31428 if(String(s) != String(format)){
31437 if(!match && length == 1){
31442 filterPattern(box, length - 1);
31446 queue.push(pattern);
31448 box = box.slice(length, box.length);
31450 filterPattern(box, 4);
31456 Roo.each(boxes, function(box, k){
31462 if(box.length == 1){
31467 filterPattern(box, 4);
31474 var pos = this.el.getBox(true);
31478 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31480 var hit_end = false;
31482 Roo.each(queue, function(box){
31486 Roo.each(box, function(b){
31488 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31498 Roo.each(box, function(b){
31500 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31503 mx = Math.max(mx, b.x);
31507 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31511 Roo.each(box, function(b){
31513 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31527 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31530 /** Sets position of item in DOM
31531 * @param {Element} item
31532 * @param {Number} x - horizontal position
31533 * @param {Number} y - vertical position
31534 * @param {Boolean} isInstant - disables transitions
31536 _processVerticalLayoutQueue : function( queue, isInstant )
31538 var pos = this.el.getBox(true);
31543 for (var i = 0; i < this.cols; i++){
31547 Roo.each(queue, function(box, k){
31549 var col = k % this.cols;
31551 Roo.each(box, function(b,kk){
31553 b.el.position('absolute');
31555 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31556 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31558 if(b.size == 'md-left' || b.size == 'md-right'){
31559 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31560 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31563 b.el.setWidth(width);
31564 b.el.setHeight(height);
31566 b.el.select('iframe',true).setSize(width,height);
31570 for (var i = 0; i < this.cols; i++){
31572 if(maxY[i] < maxY[col]){
31577 col = Math.min(col, i);
31581 x = pos.x + col * (this.colWidth + this.padWidth);
31585 var positions = [];
31587 switch (box.length){
31589 positions = this.getVerticalOneBoxColPositions(x, y, box);
31592 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31595 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31598 positions = this.getVerticalFourBoxColPositions(x, y, box);
31604 Roo.each(box, function(b,kk){
31606 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31608 var sz = b.el.getSize();
31610 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31618 for (var i = 0; i < this.cols; i++){
31619 mY = Math.max(mY, maxY[i]);
31622 this.el.setHeight(mY - pos.y);
31626 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31628 // var pos = this.el.getBox(true);
31631 // var maxX = pos.right;
31633 // var maxHeight = 0;
31635 // Roo.each(items, function(item, k){
31639 // item.el.position('absolute');
31641 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31643 // item.el.setWidth(width);
31645 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31647 // item.el.setHeight(height);
31650 // item.el.setXY([x, y], isInstant ? false : true);
31652 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31655 // y = y + height + this.alternativePadWidth;
31657 // maxHeight = maxHeight + height + this.alternativePadWidth;
31661 // this.el.setHeight(maxHeight);
31665 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31667 var pos = this.el.getBox(true);
31672 var maxX = pos.right;
31674 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31676 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31678 Roo.each(queue, function(box, k){
31680 Roo.each(box, function(b, kk){
31682 b.el.position('absolute');
31684 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31685 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31687 if(b.size == 'md-left' || b.size == 'md-right'){
31688 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31689 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31692 b.el.setWidth(width);
31693 b.el.setHeight(height);
31701 var positions = [];
31703 switch (box.length){
31705 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31708 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31711 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31714 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31720 Roo.each(box, function(b,kk){
31722 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31724 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31732 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31734 Roo.each(eItems, function(b,k){
31736 b.size = (k == 0) ? 'sm' : 'xs';
31737 b.x = (k == 0) ? 2 : 1;
31738 b.y = (k == 0) ? 2 : 1;
31740 b.el.position('absolute');
31742 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31744 b.el.setWidth(width);
31746 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31748 b.el.setHeight(height);
31752 var positions = [];
31755 x : maxX - this.unitWidth * 2 - this.gutter,
31760 x : maxX - this.unitWidth,
31761 y : minY + (this.unitWidth + this.gutter) * 2
31765 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31769 Roo.each(eItems, function(b,k){
31771 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31777 getVerticalOneBoxColPositions : function(x, y, box)
31781 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31783 if(box[0].size == 'md-left'){
31787 if(box[0].size == 'md-right'){
31792 x : x + (this.unitWidth + this.gutter) * rand,
31799 getVerticalTwoBoxColPositions : function(x, y, box)
31803 if(box[0].size == 'xs'){
31807 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31811 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31825 x : x + (this.unitWidth + this.gutter) * 2,
31826 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31833 getVerticalThreeBoxColPositions : function(x, y, box)
31837 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31845 x : x + (this.unitWidth + this.gutter) * 1,
31850 x : x + (this.unitWidth + this.gutter) * 2,
31858 if(box[0].size == 'xs' && box[1].size == 'xs'){
31867 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31871 x : x + (this.unitWidth + this.gutter) * 1,
31885 x : x + (this.unitWidth + this.gutter) * 2,
31890 x : x + (this.unitWidth + this.gutter) * 2,
31891 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31898 getVerticalFourBoxColPositions : function(x, y, box)
31902 if(box[0].size == 'xs'){
31911 y : y + (this.unitHeight + this.gutter) * 1
31916 y : y + (this.unitHeight + this.gutter) * 2
31920 x : x + (this.unitWidth + this.gutter) * 1,
31934 x : x + (this.unitWidth + this.gutter) * 2,
31939 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31940 y : y + (this.unitHeight + this.gutter) * 1
31944 x : x + (this.unitWidth + this.gutter) * 2,
31945 y : y + (this.unitWidth + this.gutter) * 2
31952 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31956 if(box[0].size == 'md-left'){
31958 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31965 if(box[0].size == 'md-right'){
31967 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31968 y : minY + (this.unitWidth + this.gutter) * 1
31974 var rand = Math.floor(Math.random() * (4 - box[0].y));
31977 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31978 y : minY + (this.unitWidth + this.gutter) * rand
31985 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31989 if(box[0].size == 'xs'){
31992 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31997 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31998 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32006 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32011 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32012 y : minY + (this.unitWidth + this.gutter) * 2
32019 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32023 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32026 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32031 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32032 y : minY + (this.unitWidth + this.gutter) * 1
32036 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32037 y : minY + (this.unitWidth + this.gutter) * 2
32044 if(box[0].size == 'xs' && box[1].size == 'xs'){
32047 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32052 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32057 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32058 y : minY + (this.unitWidth + this.gutter) * 1
32066 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32071 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32072 y : minY + (this.unitWidth + this.gutter) * 2
32076 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32077 y : minY + (this.unitWidth + this.gutter) * 2
32084 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32088 if(box[0].size == 'xs'){
32091 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32096 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32101 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),
32106 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32107 y : minY + (this.unitWidth + this.gutter) * 1
32115 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32120 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32121 y : minY + (this.unitWidth + this.gutter) * 2
32125 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32126 y : minY + (this.unitWidth + this.gutter) * 2
32130 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),
32131 y : minY + (this.unitWidth + this.gutter) * 2
32139 * remove a Masonry Brick
32140 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32142 removeBrick : function(brick_id)
32148 for (var i = 0; i<this.bricks.length; i++) {
32149 if (this.bricks[i].id == brick_id) {
32150 this.bricks.splice(i,1);
32151 this.el.dom.removeChild(Roo.get(brick_id).dom);
32158 * adds a Masonry Brick
32159 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32161 addBrick : function(cfg)
32163 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32164 //this.register(cn);
32165 cn.parentId = this.id;
32166 cn.render(this.el);
32171 * register a Masonry Brick
32172 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32175 register : function(brick)
32177 this.bricks.push(brick);
32178 brick.masonryId = this.id;
32182 * clear all the Masonry Brick
32184 clearAll : function()
32187 //this.getChildContainer().dom.innerHTML = "";
32188 this.el.dom.innerHTML = '';
32191 getSelected : function()
32193 if (!this.selectedBrick) {
32197 return this.selectedBrick;
32201 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32205 * register a Masonry Layout
32206 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32209 register : function(layout)
32211 this.groups[layout.id] = layout;
32214 * fetch a Masonry Layout based on the masonry layout ID
32215 * @param {string} the masonry layout to add
32216 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32219 get: function(layout_id) {
32220 if (typeof(this.groups[layout_id]) == 'undefined') {
32223 return this.groups[layout_id] ;
32235 * http://masonry.desandro.com
32237 * The idea is to render all the bricks based on vertical width...
32239 * The original code extends 'outlayer' - we might need to use that....
32245 * @class Roo.bootstrap.LayoutMasonryAuto
32246 * @extends Roo.bootstrap.Component
32247 * Bootstrap Layout Masonry class
32250 * Create a new Element
32251 * @param {Object} config The config object
32254 Roo.bootstrap.LayoutMasonryAuto = function(config){
32255 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32258 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32261 * @cfg {Boolean} isFitWidth - resize the width..
32263 isFitWidth : false, // options..
32265 * @cfg {Boolean} isOriginLeft = left align?
32267 isOriginLeft : true,
32269 * @cfg {Boolean} isOriginTop = top align?
32271 isOriginTop : false,
32273 * @cfg {Boolean} isLayoutInstant = no animation?
32275 isLayoutInstant : false, // needed?
32277 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32279 isResizingContainer : true,
32281 * @cfg {Number} columnWidth width of the columns
32287 * @cfg {Number} maxCols maximum number of columns
32292 * @cfg {Number} padHeight padding below box..
32298 * @cfg {Boolean} isAutoInitial defalut true
32301 isAutoInitial : true,
32307 initialColumnWidth : 0,
32308 currentSize : null,
32310 colYs : null, // array.
32317 bricks: null, //CompositeElement
32318 cols : 0, // array?
32319 // element : null, // wrapped now this.el
32320 _isLayoutInited : null,
32323 getAutoCreate : function(){
32327 cls: 'blog-masonary-wrapper ' + this.cls,
32329 cls : 'mas-boxes masonary'
32336 getChildContainer: function( )
32338 if (this.boxesEl) {
32339 return this.boxesEl;
32342 this.boxesEl = this.el.select('.mas-boxes').first();
32344 return this.boxesEl;
32348 initEvents : function()
32352 if(this.isAutoInitial){
32353 Roo.log('hook children rendered');
32354 this.on('childrenrendered', function() {
32355 Roo.log('children rendered');
32362 initial : function()
32364 this.reloadItems();
32366 this.currentSize = this.el.getBox(true);
32368 /// was window resize... - let's see if this works..
32369 Roo.EventManager.onWindowResize(this.resize, this);
32371 if(!this.isAutoInitial){
32376 this.layout.defer(500,this);
32379 reloadItems: function()
32381 this.bricks = this.el.select('.masonry-brick', true);
32383 this.bricks.each(function(b) {
32384 //Roo.log(b.getSize());
32385 if (!b.attr('originalwidth')) {
32386 b.attr('originalwidth', b.getSize().width);
32391 Roo.log(this.bricks.elements.length);
32394 resize : function()
32397 var cs = this.el.getBox(true);
32399 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32400 Roo.log("no change in with or X");
32403 this.currentSize = cs;
32407 layout : function()
32410 this._resetLayout();
32411 //this._manageStamps();
32413 // don't animate first layout
32414 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32415 this.layoutItems( isInstant );
32417 // flag for initalized
32418 this._isLayoutInited = true;
32421 layoutItems : function( isInstant )
32423 //var items = this._getItemsForLayout( this.items );
32424 // original code supports filtering layout items.. we just ignore it..
32426 this._layoutItems( this.bricks , isInstant );
32428 this._postLayout();
32430 _layoutItems : function ( items , isInstant)
32432 //this.fireEvent( 'layout', this, items );
32435 if ( !items || !items.elements.length ) {
32436 // no items, emit event with empty array
32441 items.each(function(item) {
32442 Roo.log("layout item");
32444 // get x/y object from method
32445 var position = this._getItemLayoutPosition( item );
32447 position.item = item;
32448 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32449 queue.push( position );
32452 this._processLayoutQueue( queue );
32454 /** Sets position of item in DOM
32455 * @param {Element} item
32456 * @param {Number} x - horizontal position
32457 * @param {Number} y - vertical position
32458 * @param {Boolean} isInstant - disables transitions
32460 _processLayoutQueue : function( queue )
32462 for ( var i=0, len = queue.length; i < len; i++ ) {
32463 var obj = queue[i];
32464 obj.item.position('absolute');
32465 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32471 * Any logic you want to do after each layout,
32472 * i.e. size the container
32474 _postLayout : function()
32476 this.resizeContainer();
32479 resizeContainer : function()
32481 if ( !this.isResizingContainer ) {
32484 var size = this._getContainerSize();
32486 this.el.setSize(size.width,size.height);
32487 this.boxesEl.setSize(size.width,size.height);
32493 _resetLayout : function()
32495 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32496 this.colWidth = this.el.getWidth();
32497 //this.gutter = this.el.getWidth();
32499 this.measureColumns();
32505 this.colYs.push( 0 );
32511 measureColumns : function()
32513 this.getContainerWidth();
32514 // if columnWidth is 0, default to outerWidth of first item
32515 if ( !this.columnWidth ) {
32516 var firstItem = this.bricks.first();
32517 Roo.log(firstItem);
32518 this.columnWidth = this.containerWidth;
32519 if (firstItem && firstItem.attr('originalwidth') ) {
32520 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32522 // columnWidth fall back to item of first element
32523 Roo.log("set column width?");
32524 this.initialColumnWidth = this.columnWidth ;
32526 // if first elem has no width, default to size of container
32531 if (this.initialColumnWidth) {
32532 this.columnWidth = this.initialColumnWidth;
32537 // column width is fixed at the top - however if container width get's smaller we should
32540 // this bit calcs how man columns..
32542 var columnWidth = this.columnWidth += this.gutter;
32544 // calculate columns
32545 var containerWidth = this.containerWidth + this.gutter;
32547 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32548 // fix rounding errors, typically with gutters
32549 var excess = columnWidth - containerWidth % columnWidth;
32552 // if overshoot is less than a pixel, round up, otherwise floor it
32553 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32554 cols = Math[ mathMethod ]( cols );
32555 this.cols = Math.max( cols, 1 );
32556 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32558 // padding positioning..
32559 var totalColWidth = this.cols * this.columnWidth;
32560 var padavail = this.containerWidth - totalColWidth;
32561 // so for 2 columns - we need 3 'pads'
32563 var padNeeded = (1+this.cols) * this.padWidth;
32565 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32567 this.columnWidth += padExtra
32568 //this.padWidth = Math.floor(padavail / ( this.cols));
32570 // adjust colum width so that padding is fixed??
32572 // we have 3 columns ... total = width * 3
32573 // we have X left over... that should be used by
32575 //if (this.expandC) {
32583 getContainerWidth : function()
32585 /* // container is parent if fit width
32586 var container = this.isFitWidth ? this.element.parentNode : this.element;
32587 // check that this.size and size are there
32588 // IE8 triggers resize on body size change, so they might not be
32590 var size = getSize( container ); //FIXME
32591 this.containerWidth = size && size.innerWidth; //FIXME
32594 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32598 _getItemLayoutPosition : function( item ) // what is item?
32600 // we resize the item to our columnWidth..
32602 item.setWidth(this.columnWidth);
32603 item.autoBoxAdjust = false;
32605 var sz = item.getSize();
32607 // how many columns does this brick span
32608 var remainder = this.containerWidth % this.columnWidth;
32610 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32611 // round if off by 1 pixel, otherwise use ceil
32612 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32613 colSpan = Math.min( colSpan, this.cols );
32615 // normally this should be '1' as we dont' currently allow multi width columns..
32617 var colGroup = this._getColGroup( colSpan );
32618 // get the minimum Y value from the columns
32619 var minimumY = Math.min.apply( Math, colGroup );
32620 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32622 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32624 // position the brick
32626 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32627 y: this.currentSize.y + minimumY + this.padHeight
32631 // apply setHeight to necessary columns
32632 var setHeight = minimumY + sz.height + this.padHeight;
32633 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32635 var setSpan = this.cols + 1 - colGroup.length;
32636 for ( var i = 0; i < setSpan; i++ ) {
32637 this.colYs[ shortColIndex + i ] = setHeight ;
32644 * @param {Number} colSpan - number of columns the element spans
32645 * @returns {Array} colGroup
32647 _getColGroup : function( colSpan )
32649 if ( colSpan < 2 ) {
32650 // if brick spans only one column, use all the column Ys
32655 // how many different places could this brick fit horizontally
32656 var groupCount = this.cols + 1 - colSpan;
32657 // for each group potential horizontal position
32658 for ( var i = 0; i < groupCount; i++ ) {
32659 // make an array of colY values for that one group
32660 var groupColYs = this.colYs.slice( i, i + colSpan );
32661 // and get the max value of the array
32662 colGroup[i] = Math.max.apply( Math, groupColYs );
32667 _manageStamp : function( stamp )
32669 var stampSize = stamp.getSize();
32670 var offset = stamp.getBox();
32671 // get the columns that this stamp affects
32672 var firstX = this.isOriginLeft ? offset.x : offset.right;
32673 var lastX = firstX + stampSize.width;
32674 var firstCol = Math.floor( firstX / this.columnWidth );
32675 firstCol = Math.max( 0, firstCol );
32677 var lastCol = Math.floor( lastX / this.columnWidth );
32678 // lastCol should not go over if multiple of columnWidth #425
32679 lastCol -= lastX % this.columnWidth ? 0 : 1;
32680 lastCol = Math.min( this.cols - 1, lastCol );
32682 // set colYs to bottom of the stamp
32683 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32686 for ( var i = firstCol; i <= lastCol; i++ ) {
32687 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32692 _getContainerSize : function()
32694 this.maxY = Math.max.apply( Math, this.colYs );
32699 if ( this.isFitWidth ) {
32700 size.width = this._getContainerFitWidth();
32706 _getContainerFitWidth : function()
32708 var unusedCols = 0;
32709 // count unused columns
32712 if ( this.colYs[i] !== 0 ) {
32717 // fit container to columns that have been used
32718 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32721 needsResizeLayout : function()
32723 var previousWidth = this.containerWidth;
32724 this.getContainerWidth();
32725 return previousWidth !== this.containerWidth;
32740 * @class Roo.bootstrap.MasonryBrick
32741 * @extends Roo.bootstrap.Component
32742 * Bootstrap MasonryBrick class
32745 * Create a new MasonryBrick
32746 * @param {Object} config The config object
32749 Roo.bootstrap.MasonryBrick = function(config){
32751 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32753 Roo.bootstrap.MasonryBrick.register(this);
32759 * When a MasonryBrick is clcik
32760 * @param {Roo.bootstrap.MasonryBrick} this
32761 * @param {Roo.EventObject} e
32767 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32770 * @cfg {String} title
32774 * @cfg {String} html
32778 * @cfg {String} bgimage
32782 * @cfg {String} videourl
32786 * @cfg {String} cls
32790 * @cfg {String} href
32794 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32799 * @cfg {String} placetitle (center|bottom)
32804 * @cfg {Boolean} isFitContainer defalut true
32806 isFitContainer : true,
32809 * @cfg {Boolean} preventDefault defalut false
32811 preventDefault : false,
32814 * @cfg {Boolean} inverse defalut false
32816 maskInverse : false,
32818 getAutoCreate : function()
32820 if(!this.isFitContainer){
32821 return this.getSplitAutoCreate();
32824 var cls = 'masonry-brick masonry-brick-full';
32826 if(this.href.length){
32827 cls += ' masonry-brick-link';
32830 if(this.bgimage.length){
32831 cls += ' masonry-brick-image';
32834 if(this.maskInverse){
32835 cls += ' mask-inverse';
32838 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32839 cls += ' enable-mask';
32843 cls += ' masonry-' + this.size + '-brick';
32846 if(this.placetitle.length){
32848 switch (this.placetitle) {
32850 cls += ' masonry-center-title';
32853 cls += ' masonry-bottom-title';
32860 if(!this.html.length && !this.bgimage.length){
32861 cls += ' masonry-center-title';
32864 if(!this.html.length && this.bgimage.length){
32865 cls += ' masonry-bottom-title';
32870 cls += ' ' + this.cls;
32874 tag: (this.href.length) ? 'a' : 'div',
32879 cls: 'masonry-brick-mask'
32883 cls: 'masonry-brick-paragraph',
32889 if(this.href.length){
32890 cfg.href = this.href;
32893 var cn = cfg.cn[1].cn;
32895 if(this.title.length){
32898 cls: 'masonry-brick-title',
32903 if(this.html.length){
32906 cls: 'masonry-brick-text',
32911 if (!this.title.length && !this.html.length) {
32912 cfg.cn[1].cls += ' hide';
32915 if(this.bgimage.length){
32918 cls: 'masonry-brick-image-view',
32923 if(this.videourl.length){
32924 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32925 // youtube support only?
32928 cls: 'masonry-brick-image-view',
32931 allowfullscreen : true
32939 getSplitAutoCreate : function()
32941 var cls = 'masonry-brick masonry-brick-split';
32943 if(this.href.length){
32944 cls += ' masonry-brick-link';
32947 if(this.bgimage.length){
32948 cls += ' masonry-brick-image';
32952 cls += ' masonry-' + this.size + '-brick';
32955 switch (this.placetitle) {
32957 cls += ' masonry-center-title';
32960 cls += ' masonry-bottom-title';
32963 if(!this.bgimage.length){
32964 cls += ' masonry-center-title';
32967 if(this.bgimage.length){
32968 cls += ' masonry-bottom-title';
32974 cls += ' ' + this.cls;
32978 tag: (this.href.length) ? 'a' : 'div',
32983 cls: 'masonry-brick-split-head',
32987 cls: 'masonry-brick-paragraph',
32994 cls: 'masonry-brick-split-body',
33000 if(this.href.length){
33001 cfg.href = this.href;
33004 if(this.title.length){
33005 cfg.cn[0].cn[0].cn.push({
33007 cls: 'masonry-brick-title',
33012 if(this.html.length){
33013 cfg.cn[1].cn.push({
33015 cls: 'masonry-brick-text',
33020 if(this.bgimage.length){
33021 cfg.cn[0].cn.push({
33023 cls: 'masonry-brick-image-view',
33028 if(this.videourl.length){
33029 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33030 // youtube support only?
33031 cfg.cn[0].cn.cn.push({
33033 cls: 'masonry-brick-image-view',
33036 allowfullscreen : true
33043 initEvents: function()
33045 switch (this.size) {
33078 this.el.on('touchstart', this.onTouchStart, this);
33079 this.el.on('touchmove', this.onTouchMove, this);
33080 this.el.on('touchend', this.onTouchEnd, this);
33081 this.el.on('contextmenu', this.onContextMenu, this);
33083 this.el.on('mouseenter' ,this.enter, this);
33084 this.el.on('mouseleave', this.leave, this);
33085 this.el.on('click', this.onClick, this);
33088 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33089 this.parent().bricks.push(this);
33094 onClick: function(e, el)
33096 var time = this.endTimer - this.startTimer;
33097 // Roo.log(e.preventDefault());
33100 e.preventDefault();
33105 if(!this.preventDefault){
33109 e.preventDefault();
33111 if (this.activeClass != '') {
33112 this.selectBrick();
33115 this.fireEvent('click', this, e);
33118 enter: function(e, el)
33120 e.preventDefault();
33122 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33126 if(this.bgimage.length && this.html.length){
33127 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33131 leave: function(e, el)
33133 e.preventDefault();
33135 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33139 if(this.bgimage.length && this.html.length){
33140 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33144 onTouchStart: function(e, el)
33146 // e.preventDefault();
33148 this.touchmoved = false;
33150 if(!this.isFitContainer){
33154 if(!this.bgimage.length || !this.html.length){
33158 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33160 this.timer = new Date().getTime();
33164 onTouchMove: function(e, el)
33166 this.touchmoved = true;
33169 onContextMenu : function(e,el)
33171 e.preventDefault();
33172 e.stopPropagation();
33176 onTouchEnd: function(e, el)
33178 // e.preventDefault();
33180 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33187 if(!this.bgimage.length || !this.html.length){
33189 if(this.href.length){
33190 window.location.href = this.href;
33196 if(!this.isFitContainer){
33200 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33202 window.location.href = this.href;
33205 //selection on single brick only
33206 selectBrick : function() {
33208 if (!this.parentId) {
33212 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33213 var index = m.selectedBrick.indexOf(this.id);
33216 m.selectedBrick.splice(index,1);
33217 this.el.removeClass(this.activeClass);
33221 for(var i = 0; i < m.selectedBrick.length; i++) {
33222 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33223 b.el.removeClass(b.activeClass);
33226 m.selectedBrick = [];
33228 m.selectedBrick.push(this.id);
33229 this.el.addClass(this.activeClass);
33233 isSelected : function(){
33234 return this.el.hasClass(this.activeClass);
33239 Roo.apply(Roo.bootstrap.MasonryBrick, {
33242 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33244 * register a Masonry Brick
33245 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33248 register : function(brick)
33250 //this.groups[brick.id] = brick;
33251 this.groups.add(brick.id, brick);
33254 * fetch a masonry brick based on the masonry brick ID
33255 * @param {string} the masonry brick to add
33256 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33259 get: function(brick_id)
33261 // if (typeof(this.groups[brick_id]) == 'undefined') {
33264 // return this.groups[brick_id] ;
33266 if(this.groups.key(brick_id)) {
33267 return this.groups.key(brick_id);
33285 * @class Roo.bootstrap.Brick
33286 * @extends Roo.bootstrap.Component
33287 * Bootstrap Brick class
33290 * Create a new Brick
33291 * @param {Object} config The config object
33294 Roo.bootstrap.Brick = function(config){
33295 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33301 * When a Brick is click
33302 * @param {Roo.bootstrap.Brick} this
33303 * @param {Roo.EventObject} e
33309 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33312 * @cfg {String} title
33316 * @cfg {String} html
33320 * @cfg {String} bgimage
33324 * @cfg {String} cls
33328 * @cfg {String} href
33332 * @cfg {String} video
33336 * @cfg {Boolean} square
33340 getAutoCreate : function()
33342 var cls = 'roo-brick';
33344 if(this.href.length){
33345 cls += ' roo-brick-link';
33348 if(this.bgimage.length){
33349 cls += ' roo-brick-image';
33352 if(!this.html.length && !this.bgimage.length){
33353 cls += ' roo-brick-center-title';
33356 if(!this.html.length && this.bgimage.length){
33357 cls += ' roo-brick-bottom-title';
33361 cls += ' ' + this.cls;
33365 tag: (this.href.length) ? 'a' : 'div',
33370 cls: 'roo-brick-paragraph',
33376 if(this.href.length){
33377 cfg.href = this.href;
33380 var cn = cfg.cn[0].cn;
33382 if(this.title.length){
33385 cls: 'roo-brick-title',
33390 if(this.html.length){
33393 cls: 'roo-brick-text',
33400 if(this.bgimage.length){
33403 cls: 'roo-brick-image-view',
33411 initEvents: function()
33413 if(this.title.length || this.html.length){
33414 this.el.on('mouseenter' ,this.enter, this);
33415 this.el.on('mouseleave', this.leave, this);
33418 Roo.EventManager.onWindowResize(this.resize, this);
33420 if(this.bgimage.length){
33421 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33422 this.imageEl.on('load', this.onImageLoad, this);
33429 onImageLoad : function()
33434 resize : function()
33436 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33438 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33440 if(this.bgimage.length){
33441 var image = this.el.select('.roo-brick-image-view', true).first();
33443 image.setWidth(paragraph.getWidth());
33446 image.setHeight(paragraph.getWidth());
33449 this.el.setHeight(image.getHeight());
33450 paragraph.setHeight(image.getHeight());
33456 enter: function(e, el)
33458 e.preventDefault();
33460 if(this.bgimage.length){
33461 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33462 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33466 leave: function(e, el)
33468 e.preventDefault();
33470 if(this.bgimage.length){
33471 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33472 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33487 * @class Roo.bootstrap.NumberField
33488 * @extends Roo.bootstrap.Input
33489 * Bootstrap NumberField class
33495 * Create a new NumberField
33496 * @param {Object} config The config object
33499 Roo.bootstrap.NumberField = function(config){
33500 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33503 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33506 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33508 allowDecimals : true,
33510 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33512 decimalSeparator : ".",
33514 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33516 decimalPrecision : 2,
33518 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33520 allowNegative : true,
33523 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33527 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33529 minValue : Number.NEGATIVE_INFINITY,
33531 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33533 maxValue : Number.MAX_VALUE,
33535 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33537 minText : "The minimum value for this field is {0}",
33539 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33541 maxText : "The maximum value for this field is {0}",
33543 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33544 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33546 nanText : "{0} is not a valid number",
33548 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33550 thousandsDelimiter : false,
33552 * @cfg {String} valueAlign alignment of value
33554 valueAlign : "left",
33556 getAutoCreate : function()
33558 var hiddenInput = {
33562 cls: 'hidden-number-input'
33566 hiddenInput.name = this.name;
33571 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33573 this.name = hiddenInput.name;
33575 if(cfg.cn.length > 0) {
33576 cfg.cn.push(hiddenInput);
33583 initEvents : function()
33585 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33587 var allowed = "0123456789";
33589 if(this.allowDecimals){
33590 allowed += this.decimalSeparator;
33593 if(this.allowNegative){
33597 if(this.thousandsDelimiter) {
33601 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33603 var keyPress = function(e){
33605 var k = e.getKey();
33607 var c = e.getCharCode();
33610 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33611 allowed.indexOf(String.fromCharCode(c)) === -1
33617 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33621 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33626 this.el.on("keypress", keyPress, this);
33629 validateValue : function(value)
33632 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33636 var num = this.parseValue(value);
33639 this.markInvalid(String.format(this.nanText, value));
33643 if(num < this.minValue){
33644 this.markInvalid(String.format(this.minText, this.minValue));
33648 if(num > this.maxValue){
33649 this.markInvalid(String.format(this.maxText, this.maxValue));
33656 getValue : function()
33658 var v = this.hiddenEl().getValue();
33660 return this.fixPrecision(this.parseValue(v));
33663 parseValue : function(value)
33665 if(this.thousandsDelimiter) {
33667 r = new RegExp(",", "g");
33668 value = value.replace(r, "");
33671 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33672 return isNaN(value) ? '' : value;
33675 fixPrecision : function(value)
33677 if(this.thousandsDelimiter) {
33679 r = new RegExp(",", "g");
33680 value = value.replace(r, "");
33683 var nan = isNaN(value);
33685 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33686 return nan ? '' : value;
33688 return parseFloat(value).toFixed(this.decimalPrecision);
33691 setValue : function(v)
33693 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33699 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33701 this.inputEl().dom.value = (v == '') ? '' :
33702 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33704 if(!this.allowZero && v === '0') {
33705 this.hiddenEl().dom.value = '';
33706 this.inputEl().dom.value = '';
33713 decimalPrecisionFcn : function(v)
33715 return Math.floor(v);
33718 beforeBlur : function()
33720 var v = this.parseValue(this.getRawValue());
33722 if(v || v === 0 || v === ''){
33727 hiddenEl : function()
33729 return this.el.select('input.hidden-number-input',true).first();
33741 * @class Roo.bootstrap.DocumentSlider
33742 * @extends Roo.bootstrap.Component
33743 * Bootstrap DocumentSlider class
33746 * Create a new DocumentViewer
33747 * @param {Object} config The config object
33750 Roo.bootstrap.DocumentSlider = function(config){
33751 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33758 * Fire after initEvent
33759 * @param {Roo.bootstrap.DocumentSlider} this
33764 * Fire after update
33765 * @param {Roo.bootstrap.DocumentSlider} this
33771 * @param {Roo.bootstrap.DocumentSlider} this
33777 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33783 getAutoCreate : function()
33787 cls : 'roo-document-slider',
33791 cls : 'roo-document-slider-header',
33795 cls : 'roo-document-slider-header-title'
33801 cls : 'roo-document-slider-body',
33805 cls : 'roo-document-slider-prev',
33809 cls : 'fa fa-chevron-left'
33815 cls : 'roo-document-slider-thumb',
33819 cls : 'roo-document-slider-image'
33825 cls : 'roo-document-slider-next',
33829 cls : 'fa fa-chevron-right'
33841 initEvents : function()
33843 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33844 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33846 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33847 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33849 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33850 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33852 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33853 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33855 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33856 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33858 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33859 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33861 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33862 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33864 this.thumbEl.on('click', this.onClick, this);
33866 this.prevIndicator.on('click', this.prev, this);
33868 this.nextIndicator.on('click', this.next, this);
33872 initial : function()
33874 if(this.files.length){
33875 this.indicator = 1;
33879 this.fireEvent('initial', this);
33882 update : function()
33884 this.imageEl.attr('src', this.files[this.indicator - 1]);
33886 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33888 this.prevIndicator.show();
33890 if(this.indicator == 1){
33891 this.prevIndicator.hide();
33894 this.nextIndicator.show();
33896 if(this.indicator == this.files.length){
33897 this.nextIndicator.hide();
33900 this.thumbEl.scrollTo('top');
33902 this.fireEvent('update', this);
33905 onClick : function(e)
33907 e.preventDefault();
33909 this.fireEvent('click', this);
33914 e.preventDefault();
33916 this.indicator = Math.max(1, this.indicator - 1);
33923 e.preventDefault();
33925 this.indicator = Math.min(this.files.length, this.indicator + 1);
33939 * @class Roo.bootstrap.RadioSet
33940 * @extends Roo.bootstrap.Input
33941 * Bootstrap RadioSet class
33942 * @cfg {String} indicatorpos (left|right) default left
33943 * @cfg {Boolean} inline (true|false) inline the element (default true)
33944 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33946 * Create a new RadioSet
33947 * @param {Object} config The config object
33950 Roo.bootstrap.RadioSet = function(config){
33952 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33956 Roo.bootstrap.RadioSet.register(this);
33961 * Fires when the element is checked or unchecked.
33962 * @param {Roo.bootstrap.RadioSet} this This radio
33963 * @param {Roo.bootstrap.Radio} item The checked item
33968 * Fires when the element is click.
33969 * @param {Roo.bootstrap.RadioSet} this This radio set
33970 * @param {Roo.bootstrap.Radio} item The checked item
33971 * @param {Roo.EventObject} e The event object
33978 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33986 indicatorpos : 'left',
33988 getAutoCreate : function()
33992 cls : 'roo-radio-set-label',
33996 html : this.fieldLabel
34001 if(this.indicatorpos == 'left'){
34004 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34005 tooltip : 'This field is required'
34010 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34011 tooltip : 'This field is required'
34017 cls : 'roo-radio-set-items'
34020 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34022 if (align === 'left' && this.fieldLabel.length) {
34025 cls : "roo-radio-set-right",
34031 if(this.labelWidth > 12){
34032 label.style = "width: " + this.labelWidth + 'px';
34035 if(this.labelWidth < 13 && this.labelmd == 0){
34036 this.labelmd = this.labelWidth;
34039 if(this.labellg > 0){
34040 label.cls += ' col-lg-' + this.labellg;
34041 items.cls += ' col-lg-' + (12 - this.labellg);
34044 if(this.labelmd > 0){
34045 label.cls += ' col-md-' + this.labelmd;
34046 items.cls += ' col-md-' + (12 - this.labelmd);
34049 if(this.labelsm > 0){
34050 label.cls += ' col-sm-' + this.labelsm;
34051 items.cls += ' col-sm-' + (12 - this.labelsm);
34054 if(this.labelxs > 0){
34055 label.cls += ' col-xs-' + this.labelxs;
34056 items.cls += ' col-xs-' + (12 - this.labelxs);
34062 cls : 'roo-radio-set',
34066 cls : 'roo-radio-set-input',
34069 value : this.value ? this.value : ''
34076 if(this.weight.length){
34077 cfg.cls += ' roo-radio-' + this.weight;
34081 cfg.cls += ' roo-radio-set-inline';
34085 ['xs','sm','md','lg'].map(function(size){
34086 if (settings[size]) {
34087 cfg.cls += ' col-' + size + '-' + settings[size];
34095 initEvents : function()
34097 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34098 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34100 if(!this.fieldLabel.length){
34101 this.labelEl.hide();
34104 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34105 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34107 this.indicator = this.indicatorEl();
34109 if(this.indicator){
34110 this.indicator.addClass('invisible');
34113 this.originalValue = this.getValue();
34117 inputEl: function ()
34119 return this.el.select('.roo-radio-set-input', true).first();
34122 getChildContainer : function()
34124 return this.itemsEl;
34127 register : function(item)
34129 this.radioes.push(item);
34133 validate : function()
34135 if(this.getVisibilityEl().hasClass('hidden')){
34141 Roo.each(this.radioes, function(i){
34150 if(this.allowBlank) {
34154 if(this.disabled || valid){
34159 this.markInvalid();
34164 markValid : function()
34166 if(this.labelEl.isVisible(true)){
34167 this.indicatorEl().removeClass('visible');
34168 this.indicatorEl().addClass('invisible');
34171 this.el.removeClass([this.invalidClass, this.validClass]);
34172 this.el.addClass(this.validClass);
34174 this.fireEvent('valid', this);
34177 markInvalid : function(msg)
34179 if(this.allowBlank || this.disabled){
34183 if(this.labelEl.isVisible(true)){
34184 this.indicatorEl().removeClass('invisible');
34185 this.indicatorEl().addClass('visible');
34188 this.el.removeClass([this.invalidClass, this.validClass]);
34189 this.el.addClass(this.invalidClass);
34191 this.fireEvent('invalid', this, msg);
34195 setValue : function(v, suppressEvent)
34197 if(this.value === v){
34204 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34207 Roo.each(this.radioes, function(i){
34209 i.el.removeClass('checked');
34212 Roo.each(this.radioes, function(i){
34214 if(i.value === v || i.value.toString() === v.toString()){
34216 i.el.addClass('checked');
34218 if(suppressEvent !== true){
34219 this.fireEvent('check', this, i);
34230 clearInvalid : function(){
34232 if(!this.el || this.preventMark){
34236 this.el.removeClass([this.invalidClass]);
34238 this.fireEvent('valid', this);
34243 Roo.apply(Roo.bootstrap.RadioSet, {
34247 register : function(set)
34249 this.groups[set.name] = set;
34252 get: function(name)
34254 if (typeof(this.groups[name]) == 'undefined') {
34258 return this.groups[name] ;
34264 * Ext JS Library 1.1.1
34265 * Copyright(c) 2006-2007, Ext JS, LLC.
34267 * Originally Released Under LGPL - original licence link has changed is not relivant.
34270 * <script type="text/javascript">
34275 * @class Roo.bootstrap.SplitBar
34276 * @extends Roo.util.Observable
34277 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34281 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34282 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34283 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34284 split.minSize = 100;
34285 split.maxSize = 600;
34286 split.animate = true;
34287 split.on('moved', splitterMoved);
34290 * Create a new SplitBar
34291 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34292 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34293 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34294 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34295 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34296 position of the SplitBar).
34298 Roo.bootstrap.SplitBar = function(cfg){
34303 // dragElement : elm
34304 // resizingElement: el,
34306 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34307 // placement : Roo.bootstrap.SplitBar.LEFT ,
34308 // existingProxy ???
34311 this.el = Roo.get(cfg.dragElement, true);
34312 this.el.dom.unselectable = "on";
34314 this.resizingEl = Roo.get(cfg.resizingElement, true);
34318 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34319 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34322 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34325 * The minimum size of the resizing element. (Defaults to 0)
34331 * The maximum size of the resizing element. (Defaults to 2000)
34334 this.maxSize = 2000;
34337 * Whether to animate the transition to the new size
34340 this.animate = false;
34343 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34346 this.useShim = false;
34351 if(!cfg.existingProxy){
34353 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34355 this.proxy = Roo.get(cfg.existingProxy).dom;
34358 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34361 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34364 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34367 this.dragSpecs = {};
34370 * @private The adapter to use to positon and resize elements
34372 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34373 this.adapter.init(this);
34375 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34377 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34378 this.el.addClass("roo-splitbar-h");
34381 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34382 this.el.addClass("roo-splitbar-v");
34388 * Fires when the splitter is moved (alias for {@link #event-moved})
34389 * @param {Roo.bootstrap.SplitBar} this
34390 * @param {Number} newSize the new width or height
34395 * Fires when the splitter is moved
34396 * @param {Roo.bootstrap.SplitBar} this
34397 * @param {Number} newSize the new width or height
34401 * @event beforeresize
34402 * Fires before the splitter is dragged
34403 * @param {Roo.bootstrap.SplitBar} this
34405 "beforeresize" : true,
34407 "beforeapply" : true
34410 Roo.util.Observable.call(this);
34413 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34414 onStartProxyDrag : function(x, y){
34415 this.fireEvent("beforeresize", this);
34417 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34419 o.enableDisplayMode("block");
34420 // all splitbars share the same overlay
34421 Roo.bootstrap.SplitBar.prototype.overlay = o;
34423 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34424 this.overlay.show();
34425 Roo.get(this.proxy).setDisplayed("block");
34426 var size = this.adapter.getElementSize(this);
34427 this.activeMinSize = this.getMinimumSize();;
34428 this.activeMaxSize = this.getMaximumSize();;
34429 var c1 = size - this.activeMinSize;
34430 var c2 = Math.max(this.activeMaxSize - size, 0);
34431 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34432 this.dd.resetConstraints();
34433 this.dd.setXConstraint(
34434 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34435 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34437 this.dd.setYConstraint(0, 0);
34439 this.dd.resetConstraints();
34440 this.dd.setXConstraint(0, 0);
34441 this.dd.setYConstraint(
34442 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34443 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34446 this.dragSpecs.startSize = size;
34447 this.dragSpecs.startPoint = [x, y];
34448 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34452 * @private Called after the drag operation by the DDProxy
34454 onEndProxyDrag : function(e){
34455 Roo.get(this.proxy).setDisplayed(false);
34456 var endPoint = Roo.lib.Event.getXY(e);
34458 this.overlay.hide();
34461 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34462 newSize = this.dragSpecs.startSize +
34463 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34464 endPoint[0] - this.dragSpecs.startPoint[0] :
34465 this.dragSpecs.startPoint[0] - endPoint[0]
34468 newSize = this.dragSpecs.startSize +
34469 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34470 endPoint[1] - this.dragSpecs.startPoint[1] :
34471 this.dragSpecs.startPoint[1] - endPoint[1]
34474 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34475 if(newSize != this.dragSpecs.startSize){
34476 if(this.fireEvent('beforeapply', this, newSize) !== false){
34477 this.adapter.setElementSize(this, newSize);
34478 this.fireEvent("moved", this, newSize);
34479 this.fireEvent("resize", this, newSize);
34485 * Get the adapter this SplitBar uses
34486 * @return The adapter object
34488 getAdapter : function(){
34489 return this.adapter;
34493 * Set the adapter this SplitBar uses
34494 * @param {Object} adapter A SplitBar adapter object
34496 setAdapter : function(adapter){
34497 this.adapter = adapter;
34498 this.adapter.init(this);
34502 * Gets the minimum size for the resizing element
34503 * @return {Number} The minimum size
34505 getMinimumSize : function(){
34506 return this.minSize;
34510 * Sets the minimum size for the resizing element
34511 * @param {Number} minSize The minimum size
34513 setMinimumSize : function(minSize){
34514 this.minSize = minSize;
34518 * Gets the maximum size for the resizing element
34519 * @return {Number} The maximum size
34521 getMaximumSize : function(){
34522 return this.maxSize;
34526 * Sets the maximum size for the resizing element
34527 * @param {Number} maxSize The maximum size
34529 setMaximumSize : function(maxSize){
34530 this.maxSize = maxSize;
34534 * Sets the initialize size for the resizing element
34535 * @param {Number} size The initial size
34537 setCurrentSize : function(size){
34538 var oldAnimate = this.animate;
34539 this.animate = false;
34540 this.adapter.setElementSize(this, size);
34541 this.animate = oldAnimate;
34545 * Destroy this splitbar.
34546 * @param {Boolean} removeEl True to remove the element
34548 destroy : function(removeEl){
34550 this.shim.remove();
34553 this.proxy.parentNode.removeChild(this.proxy);
34561 * @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.
34563 Roo.bootstrap.SplitBar.createProxy = function(dir){
34564 var proxy = new Roo.Element(document.createElement("div"));
34565 proxy.unselectable();
34566 var cls = 'roo-splitbar-proxy';
34567 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34568 document.body.appendChild(proxy.dom);
34573 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34574 * Default Adapter. It assumes the splitter and resizing element are not positioned
34575 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34577 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34580 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34581 // do nothing for now
34582 init : function(s){
34586 * Called before drag operations to get the current size of the resizing element.
34587 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34589 getElementSize : function(s){
34590 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34591 return s.resizingEl.getWidth();
34593 return s.resizingEl.getHeight();
34598 * Called after drag operations to set the size of the resizing element.
34599 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34600 * @param {Number} newSize The new size to set
34601 * @param {Function} onComplete A function to be invoked when resizing is complete
34603 setElementSize : function(s, newSize, onComplete){
34604 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34606 s.resizingEl.setWidth(newSize);
34608 onComplete(s, newSize);
34611 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34616 s.resizingEl.setHeight(newSize);
34618 onComplete(s, newSize);
34621 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34628 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34629 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34630 * Adapter that moves the splitter element to align with the resized sizing element.
34631 * Used with an absolute positioned SplitBar.
34632 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34633 * document.body, make sure you assign an id to the body element.
34635 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34636 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34637 this.container = Roo.get(container);
34640 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34641 init : function(s){
34642 this.basic.init(s);
34645 getElementSize : function(s){
34646 return this.basic.getElementSize(s);
34649 setElementSize : function(s, newSize, onComplete){
34650 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34653 moveSplitter : function(s){
34654 var yes = Roo.bootstrap.SplitBar;
34655 switch(s.placement){
34657 s.el.setX(s.resizingEl.getRight());
34660 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34663 s.el.setY(s.resizingEl.getBottom());
34666 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34673 * Orientation constant - Create a vertical SplitBar
34677 Roo.bootstrap.SplitBar.VERTICAL = 1;
34680 * Orientation constant - Create a horizontal SplitBar
34684 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34687 * Placement constant - The resizing element is to the left of the splitter element
34691 Roo.bootstrap.SplitBar.LEFT = 1;
34694 * Placement constant - The resizing element is to the right of the splitter element
34698 Roo.bootstrap.SplitBar.RIGHT = 2;
34701 * Placement constant - The resizing element is positioned above the splitter element
34705 Roo.bootstrap.SplitBar.TOP = 3;
34708 * Placement constant - The resizing element is positioned under splitter element
34712 Roo.bootstrap.SplitBar.BOTTOM = 4;
34713 Roo.namespace("Roo.bootstrap.layout");/*
34715 * Ext JS Library 1.1.1
34716 * Copyright(c) 2006-2007, Ext JS, LLC.
34718 * Originally Released Under LGPL - original licence link has changed is not relivant.
34721 * <script type="text/javascript">
34725 * @class Roo.bootstrap.layout.Manager
34726 * @extends Roo.bootstrap.Component
34727 * Base class for layout managers.
34729 Roo.bootstrap.layout.Manager = function(config)
34731 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34737 /** false to disable window resize monitoring @type Boolean */
34738 this.monitorWindowResize = true;
34743 * Fires when a layout is performed.
34744 * @param {Roo.LayoutManager} this
34748 * @event regionresized
34749 * Fires when the user resizes a region.
34750 * @param {Roo.LayoutRegion} region The resized region
34751 * @param {Number} newSize The new size (width for east/west, height for north/south)
34753 "regionresized" : true,
34755 * @event regioncollapsed
34756 * Fires when a region is collapsed.
34757 * @param {Roo.LayoutRegion} region The collapsed region
34759 "regioncollapsed" : true,
34761 * @event regionexpanded
34762 * Fires when a region is expanded.
34763 * @param {Roo.LayoutRegion} region The expanded region
34765 "regionexpanded" : true
34767 this.updating = false;
34770 this.el = Roo.get(config.el);
34776 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34781 monitorWindowResize : true,
34787 onRender : function(ct, position)
34790 this.el = Roo.get(ct);
34793 //this.fireEvent('render',this);
34797 initEvents: function()
34801 // ie scrollbar fix
34802 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34803 document.body.scroll = "no";
34804 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34805 this.el.position('relative');
34807 this.id = this.el.id;
34808 this.el.addClass("roo-layout-container");
34809 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34810 if(this.el.dom != document.body ) {
34811 this.el.on('resize', this.layout,this);
34812 this.el.on('show', this.layout,this);
34818 * Returns true if this layout is currently being updated
34819 * @return {Boolean}
34821 isUpdating : function(){
34822 return this.updating;
34826 * Suspend the LayoutManager from doing auto-layouts while
34827 * making multiple add or remove calls
34829 beginUpdate : function(){
34830 this.updating = true;
34834 * Restore auto-layouts and optionally disable the manager from performing a layout
34835 * @param {Boolean} noLayout true to disable a layout update
34837 endUpdate : function(noLayout){
34838 this.updating = false;
34844 layout: function(){
34848 onRegionResized : function(region, newSize){
34849 this.fireEvent("regionresized", region, newSize);
34853 onRegionCollapsed : function(region){
34854 this.fireEvent("regioncollapsed", region);
34857 onRegionExpanded : function(region){
34858 this.fireEvent("regionexpanded", region);
34862 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34863 * performs box-model adjustments.
34864 * @return {Object} The size as an object {width: (the width), height: (the height)}
34866 getViewSize : function()
34869 if(this.el.dom != document.body){
34870 size = this.el.getSize();
34872 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34874 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34875 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34880 * Returns the Element this layout is bound to.
34881 * @return {Roo.Element}
34883 getEl : function(){
34888 * Returns the specified region.
34889 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34890 * @return {Roo.LayoutRegion}
34892 getRegion : function(target){
34893 return this.regions[target.toLowerCase()];
34896 onWindowResize : function(){
34897 if(this.monitorWindowResize){
34904 * Ext JS Library 1.1.1
34905 * Copyright(c) 2006-2007, Ext JS, LLC.
34907 * Originally Released Under LGPL - original licence link has changed is not relivant.
34910 * <script type="text/javascript">
34913 * @class Roo.bootstrap.layout.Border
34914 * @extends Roo.bootstrap.layout.Manager
34915 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34916 * please see: examples/bootstrap/nested.html<br><br>
34918 <b>The container the layout is rendered into can be either the body element or any other element.
34919 If it is not the body element, the container needs to either be an absolute positioned element,
34920 or you will need to add "position:relative" to the css of the container. You will also need to specify
34921 the container size if it is not the body element.</b>
34924 * Create a new Border
34925 * @param {Object} config Configuration options
34927 Roo.bootstrap.layout.Border = function(config){
34928 config = config || {};
34929 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34933 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34934 if(config[region]){
34935 config[region].region = region;
34936 this.addRegion(config[region]);
34942 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34944 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34946 * Creates and adds a new region if it doesn't already exist.
34947 * @param {String} target The target region key (north, south, east, west or center).
34948 * @param {Object} config The regions config object
34949 * @return {BorderLayoutRegion} The new region
34951 addRegion : function(config)
34953 if(!this.regions[config.region]){
34954 var r = this.factory(config);
34955 this.bindRegion(r);
34957 return this.regions[config.region];
34961 bindRegion : function(r){
34962 this.regions[r.config.region] = r;
34964 r.on("visibilitychange", this.layout, this);
34965 r.on("paneladded", this.layout, this);
34966 r.on("panelremoved", this.layout, this);
34967 r.on("invalidated", this.layout, this);
34968 r.on("resized", this.onRegionResized, this);
34969 r.on("collapsed", this.onRegionCollapsed, this);
34970 r.on("expanded", this.onRegionExpanded, this);
34974 * Performs a layout update.
34976 layout : function()
34978 if(this.updating) {
34982 // render all the rebions if they have not been done alreayd?
34983 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34984 if(this.regions[region] && !this.regions[region].bodyEl){
34985 this.regions[region].onRender(this.el)
34989 var size = this.getViewSize();
34990 var w = size.width;
34991 var h = size.height;
34996 //var x = 0, y = 0;
34998 var rs = this.regions;
34999 var north = rs["north"];
35000 var south = rs["south"];
35001 var west = rs["west"];
35002 var east = rs["east"];
35003 var center = rs["center"];
35004 //if(this.hideOnLayout){ // not supported anymore
35005 //c.el.setStyle("display", "none");
35007 if(north && north.isVisible()){
35008 var b = north.getBox();
35009 var m = north.getMargins();
35010 b.width = w - (m.left+m.right);
35013 centerY = b.height + b.y + m.bottom;
35014 centerH -= centerY;
35015 north.updateBox(this.safeBox(b));
35017 if(south && south.isVisible()){
35018 var b = south.getBox();
35019 var m = south.getMargins();
35020 b.width = w - (m.left+m.right);
35022 var totalHeight = (b.height + m.top + m.bottom);
35023 b.y = h - totalHeight + m.top;
35024 centerH -= totalHeight;
35025 south.updateBox(this.safeBox(b));
35027 if(west && west.isVisible()){
35028 var b = west.getBox();
35029 var m = west.getMargins();
35030 b.height = centerH - (m.top+m.bottom);
35032 b.y = centerY + m.top;
35033 var totalWidth = (b.width + m.left + m.right);
35034 centerX += totalWidth;
35035 centerW -= totalWidth;
35036 west.updateBox(this.safeBox(b));
35038 if(east && east.isVisible()){
35039 var b = east.getBox();
35040 var m = east.getMargins();
35041 b.height = centerH - (m.top+m.bottom);
35042 var totalWidth = (b.width + m.left + m.right);
35043 b.x = w - totalWidth + m.left;
35044 b.y = centerY + m.top;
35045 centerW -= totalWidth;
35046 east.updateBox(this.safeBox(b));
35049 var m = center.getMargins();
35051 x: centerX + m.left,
35052 y: centerY + m.top,
35053 width: centerW - (m.left+m.right),
35054 height: centerH - (m.top+m.bottom)
35056 //if(this.hideOnLayout){
35057 //center.el.setStyle("display", "block");
35059 center.updateBox(this.safeBox(centerBox));
35062 this.fireEvent("layout", this);
35066 safeBox : function(box){
35067 box.width = Math.max(0, box.width);
35068 box.height = Math.max(0, box.height);
35073 * Adds a ContentPanel (or subclass) to this layout.
35074 * @param {String} target The target region key (north, south, east, west or center).
35075 * @param {Roo.ContentPanel} panel The panel to add
35076 * @return {Roo.ContentPanel} The added panel
35078 add : function(target, panel){
35080 target = target.toLowerCase();
35081 return this.regions[target].add(panel);
35085 * Remove a ContentPanel (or subclass) to this layout.
35086 * @param {String} target The target region key (north, south, east, west or center).
35087 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35088 * @return {Roo.ContentPanel} The removed panel
35090 remove : function(target, panel){
35091 target = target.toLowerCase();
35092 return this.regions[target].remove(panel);
35096 * Searches all regions for a panel with the specified id
35097 * @param {String} panelId
35098 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35100 findPanel : function(panelId){
35101 var rs = this.regions;
35102 for(var target in rs){
35103 if(typeof rs[target] != "function"){
35104 var p = rs[target].getPanel(panelId);
35114 * Searches all regions for a panel with the specified id and activates (shows) it.
35115 * @param {String/ContentPanel} panelId The panels id or the panel itself
35116 * @return {Roo.ContentPanel} The shown panel or null
35118 showPanel : function(panelId) {
35119 var rs = this.regions;
35120 for(var target in rs){
35121 var r = rs[target];
35122 if(typeof r != "function"){
35123 if(r.hasPanel(panelId)){
35124 return r.showPanel(panelId);
35132 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35133 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35136 restoreState : function(provider){
35138 provider = Roo.state.Manager;
35140 var sm = new Roo.LayoutStateManager();
35141 sm.init(this, provider);
35147 * Adds a xtype elements to the layout.
35151 xtype : 'ContentPanel',
35158 xtype : 'NestedLayoutPanel',
35164 items : [ ... list of content panels or nested layout panels.. ]
35168 * @param {Object} cfg Xtype definition of item to add.
35170 addxtype : function(cfg)
35172 // basically accepts a pannel...
35173 // can accept a layout region..!?!?
35174 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35177 // theory? children can only be panels??
35179 //if (!cfg.xtype.match(/Panel$/)) {
35184 if (typeof(cfg.region) == 'undefined') {
35185 Roo.log("Failed to add Panel, region was not set");
35189 var region = cfg.region;
35195 xitems = cfg.items;
35202 case 'Content': // ContentPanel (el, cfg)
35203 case 'Scroll': // ContentPanel (el, cfg)
35205 cfg.autoCreate = true;
35206 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35208 // var el = this.el.createChild();
35209 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35212 this.add(region, ret);
35216 case 'TreePanel': // our new panel!
35217 cfg.el = this.el.createChild();
35218 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35219 this.add(region, ret);
35224 // create a new Layout (which is a Border Layout...
35226 var clayout = cfg.layout;
35227 clayout.el = this.el.createChild();
35228 clayout.items = clayout.items || [];
35232 // replace this exitems with the clayout ones..
35233 xitems = clayout.items;
35235 // force background off if it's in center...
35236 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35237 cfg.background = false;
35239 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35242 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35243 //console.log('adding nested layout panel ' + cfg.toSource());
35244 this.add(region, ret);
35245 nb = {}; /// find first...
35250 // needs grid and region
35252 //var el = this.getRegion(region).el.createChild();
35254 *var el = this.el.createChild();
35255 // create the grid first...
35256 cfg.grid.container = el;
35257 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35260 if (region == 'center' && this.active ) {
35261 cfg.background = false;
35264 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35266 this.add(region, ret);
35268 if (cfg.background) {
35269 // render grid on panel activation (if panel background)
35270 ret.on('activate', function(gp) {
35271 if (!gp.grid.rendered) {
35272 // gp.grid.render(el);
35276 // cfg.grid.render(el);
35282 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35283 // it was the old xcomponent building that caused this before.
35284 // espeically if border is the top element in the tree.
35294 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35296 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35297 this.add(region, ret);
35301 throw "Can not add '" + cfg.xtype + "' to Border";
35307 this.beginUpdate();
35311 Roo.each(xitems, function(i) {
35312 region = nb && i.region ? i.region : false;
35314 var add = ret.addxtype(i);
35317 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35318 if (!i.background) {
35319 abn[region] = nb[region] ;
35326 // make the last non-background panel active..
35327 //if (nb) { Roo.log(abn); }
35330 for(var r in abn) {
35331 region = this.getRegion(r);
35333 // tried using nb[r], but it does not work..
35335 region.showPanel(abn[r]);
35346 factory : function(cfg)
35349 var validRegions = Roo.bootstrap.layout.Border.regions;
35351 var target = cfg.region;
35354 var r = Roo.bootstrap.layout;
35358 return new r.North(cfg);
35360 return new r.South(cfg);
35362 return new r.East(cfg);
35364 return new r.West(cfg);
35366 return new r.Center(cfg);
35368 throw 'Layout region "'+target+'" not supported.';
35375 * Ext JS Library 1.1.1
35376 * Copyright(c) 2006-2007, Ext JS, LLC.
35378 * Originally Released Under LGPL - original licence link has changed is not relivant.
35381 * <script type="text/javascript">
35385 * @class Roo.bootstrap.layout.Basic
35386 * @extends Roo.util.Observable
35387 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35388 * and does not have a titlebar, tabs or any other features. All it does is size and position
35389 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35390 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35391 * @cfg {string} region the region that it inhabits..
35392 * @cfg {bool} skipConfig skip config?
35396 Roo.bootstrap.layout.Basic = function(config){
35398 this.mgr = config.mgr;
35400 this.position = config.region;
35402 var skipConfig = config.skipConfig;
35406 * @scope Roo.BasicLayoutRegion
35410 * @event beforeremove
35411 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35412 * @param {Roo.LayoutRegion} this
35413 * @param {Roo.ContentPanel} panel The panel
35414 * @param {Object} e The cancel event object
35416 "beforeremove" : true,
35418 * @event invalidated
35419 * Fires when the layout for this region is changed.
35420 * @param {Roo.LayoutRegion} this
35422 "invalidated" : true,
35424 * @event visibilitychange
35425 * Fires when this region is shown or hidden
35426 * @param {Roo.LayoutRegion} this
35427 * @param {Boolean} visibility true or false
35429 "visibilitychange" : true,
35431 * @event paneladded
35432 * Fires when a panel is added.
35433 * @param {Roo.LayoutRegion} this
35434 * @param {Roo.ContentPanel} panel The panel
35436 "paneladded" : true,
35438 * @event panelremoved
35439 * Fires when a panel is removed.
35440 * @param {Roo.LayoutRegion} this
35441 * @param {Roo.ContentPanel} panel The panel
35443 "panelremoved" : true,
35445 * @event beforecollapse
35446 * Fires when this region before collapse.
35447 * @param {Roo.LayoutRegion} this
35449 "beforecollapse" : true,
35452 * Fires when this region is collapsed.
35453 * @param {Roo.LayoutRegion} this
35455 "collapsed" : true,
35458 * Fires when this region is expanded.
35459 * @param {Roo.LayoutRegion} this
35464 * Fires when this region is slid into view.
35465 * @param {Roo.LayoutRegion} this
35467 "slideshow" : true,
35470 * Fires when this region slides out of view.
35471 * @param {Roo.LayoutRegion} this
35473 "slidehide" : true,
35475 * @event panelactivated
35476 * Fires when a panel is activated.
35477 * @param {Roo.LayoutRegion} this
35478 * @param {Roo.ContentPanel} panel The activated panel
35480 "panelactivated" : true,
35483 * Fires when the user resizes this region.
35484 * @param {Roo.LayoutRegion} this
35485 * @param {Number} newSize The new size (width for east/west, height for north/south)
35489 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35490 this.panels = new Roo.util.MixedCollection();
35491 this.panels.getKey = this.getPanelId.createDelegate(this);
35493 this.activePanel = null;
35494 // ensure listeners are added...
35496 if (config.listeners || config.events) {
35497 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35498 listeners : config.listeners || {},
35499 events : config.events || {}
35503 if(skipConfig !== true){
35504 this.applyConfig(config);
35508 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35510 getPanelId : function(p){
35514 applyConfig : function(config){
35515 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35516 this.config = config;
35521 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35522 * the width, for horizontal (north, south) the height.
35523 * @param {Number} newSize The new width or height
35525 resizeTo : function(newSize){
35526 var el = this.el ? this.el :
35527 (this.activePanel ? this.activePanel.getEl() : null);
35529 switch(this.position){
35532 el.setWidth(newSize);
35533 this.fireEvent("resized", this, newSize);
35537 el.setHeight(newSize);
35538 this.fireEvent("resized", this, newSize);
35544 getBox : function(){
35545 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35548 getMargins : function(){
35549 return this.margins;
35552 updateBox : function(box){
35554 var el = this.activePanel.getEl();
35555 el.dom.style.left = box.x + "px";
35556 el.dom.style.top = box.y + "px";
35557 this.activePanel.setSize(box.width, box.height);
35561 * Returns the container element for this region.
35562 * @return {Roo.Element}
35564 getEl : function(){
35565 return this.activePanel;
35569 * Returns true if this region is currently visible.
35570 * @return {Boolean}
35572 isVisible : function(){
35573 return this.activePanel ? true : false;
35576 setActivePanel : function(panel){
35577 panel = this.getPanel(panel);
35578 if(this.activePanel && this.activePanel != panel){
35579 this.activePanel.setActiveState(false);
35580 this.activePanel.getEl().setLeftTop(-10000,-10000);
35582 this.activePanel = panel;
35583 panel.setActiveState(true);
35585 panel.setSize(this.box.width, this.box.height);
35587 this.fireEvent("panelactivated", this, panel);
35588 this.fireEvent("invalidated");
35592 * Show the specified panel.
35593 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35594 * @return {Roo.ContentPanel} The shown panel or null
35596 showPanel : function(panel){
35597 panel = this.getPanel(panel);
35599 this.setActivePanel(panel);
35605 * Get the active panel for this region.
35606 * @return {Roo.ContentPanel} The active panel or null
35608 getActivePanel : function(){
35609 return this.activePanel;
35613 * Add the passed ContentPanel(s)
35614 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35615 * @return {Roo.ContentPanel} The panel added (if only one was added)
35617 add : function(panel){
35618 if(arguments.length > 1){
35619 for(var i = 0, len = arguments.length; i < len; i++) {
35620 this.add(arguments[i]);
35624 if(this.hasPanel(panel)){
35625 this.showPanel(panel);
35628 var el = panel.getEl();
35629 if(el.dom.parentNode != this.mgr.el.dom){
35630 this.mgr.el.dom.appendChild(el.dom);
35632 if(panel.setRegion){
35633 panel.setRegion(this);
35635 this.panels.add(panel);
35636 el.setStyle("position", "absolute");
35637 if(!panel.background){
35638 this.setActivePanel(panel);
35639 if(this.config.initialSize && this.panels.getCount()==1){
35640 this.resizeTo(this.config.initialSize);
35643 this.fireEvent("paneladded", this, panel);
35648 * Returns true if the panel is in this region.
35649 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35650 * @return {Boolean}
35652 hasPanel : function(panel){
35653 if(typeof panel == "object"){ // must be panel obj
35654 panel = panel.getId();
35656 return this.getPanel(panel) ? true : false;
35660 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35661 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35662 * @param {Boolean} preservePanel Overrides the config preservePanel option
35663 * @return {Roo.ContentPanel} The panel that was removed
35665 remove : function(panel, preservePanel){
35666 panel = this.getPanel(panel);
35671 this.fireEvent("beforeremove", this, panel, e);
35672 if(e.cancel === true){
35675 var panelId = panel.getId();
35676 this.panels.removeKey(panelId);
35681 * Returns the panel specified or null if it's not in this region.
35682 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35683 * @return {Roo.ContentPanel}
35685 getPanel : function(id){
35686 if(typeof id == "object"){ // must be panel obj
35689 return this.panels.get(id);
35693 * Returns this regions position (north/south/east/west/center).
35696 getPosition: function(){
35697 return this.position;
35701 * Ext JS Library 1.1.1
35702 * Copyright(c) 2006-2007, Ext JS, LLC.
35704 * Originally Released Under LGPL - original licence link has changed is not relivant.
35707 * <script type="text/javascript">
35711 * @class Roo.bootstrap.layout.Region
35712 * @extends Roo.bootstrap.layout.Basic
35713 * This class represents a region in a layout manager.
35715 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35716 * @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})
35717 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35718 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35719 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35720 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35721 * @cfg {String} title The title for the region (overrides panel titles)
35722 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35723 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35724 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35725 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35726 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35727 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35728 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35729 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35730 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35731 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35733 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35734 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35735 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35736 * @cfg {Number} width For East/West panels
35737 * @cfg {Number} height For North/South panels
35738 * @cfg {Boolean} split To show the splitter
35739 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35741 * @cfg {string} cls Extra CSS classes to add to region
35743 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35744 * @cfg {string} region the region that it inhabits..
35747 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35748 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35750 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35751 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35752 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35754 Roo.bootstrap.layout.Region = function(config)
35756 this.applyConfig(config);
35758 var mgr = config.mgr;
35759 var pos = config.region;
35760 config.skipConfig = true;
35761 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35764 this.onRender(mgr.el);
35767 this.visible = true;
35768 this.collapsed = false;
35769 this.unrendered_panels = [];
35772 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35774 position: '', // set by wrapper (eg. north/south etc..)
35775 unrendered_panels : null, // unrendered panels.
35776 createBody : function(){
35777 /** This region's body element
35778 * @type Roo.Element */
35779 this.bodyEl = this.el.createChild({
35781 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35785 onRender: function(ctr, pos)
35787 var dh = Roo.DomHelper;
35788 /** This region's container element
35789 * @type Roo.Element */
35790 this.el = dh.append(ctr.dom, {
35792 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35794 /** This region's title element
35795 * @type Roo.Element */
35797 this.titleEl = dh.append(this.el.dom,
35800 unselectable: "on",
35801 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35803 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35804 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35807 this.titleEl.enableDisplayMode();
35808 /** This region's title text element
35809 * @type HTMLElement */
35810 this.titleTextEl = this.titleEl.dom.firstChild;
35811 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35813 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35814 this.closeBtn.enableDisplayMode();
35815 this.closeBtn.on("click", this.closeClicked, this);
35816 this.closeBtn.hide();
35818 this.createBody(this.config);
35819 if(this.config.hideWhenEmpty){
35821 this.on("paneladded", this.validateVisibility, this);
35822 this.on("panelremoved", this.validateVisibility, this);
35824 if(this.autoScroll){
35825 this.bodyEl.setStyle("overflow", "auto");
35827 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35829 //if(c.titlebar !== false){
35830 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35831 this.titleEl.hide();
35833 this.titleEl.show();
35834 if(this.config.title){
35835 this.titleTextEl.innerHTML = this.config.title;
35839 if(this.config.collapsed){
35840 this.collapse(true);
35842 if(this.config.hidden){
35846 if (this.unrendered_panels && this.unrendered_panels.length) {
35847 for (var i =0;i< this.unrendered_panels.length; i++) {
35848 this.add(this.unrendered_panels[i]);
35850 this.unrendered_panels = null;
35856 applyConfig : function(c)
35859 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35860 var dh = Roo.DomHelper;
35861 if(c.titlebar !== false){
35862 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35863 this.collapseBtn.on("click", this.collapse, this);
35864 this.collapseBtn.enableDisplayMode();
35866 if(c.showPin === true || this.showPin){
35867 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35868 this.stickBtn.enableDisplayMode();
35869 this.stickBtn.on("click", this.expand, this);
35870 this.stickBtn.hide();
35875 /** This region's collapsed element
35876 * @type Roo.Element */
35879 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35880 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35883 if(c.floatable !== false){
35884 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35885 this.collapsedEl.on("click", this.collapseClick, this);
35888 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35889 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35890 id: "message", unselectable: "on", style:{"float":"left"}});
35891 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35893 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35894 this.expandBtn.on("click", this.expand, this);
35898 if(this.collapseBtn){
35899 this.collapseBtn.setVisible(c.collapsible == true);
35902 this.cmargins = c.cmargins || this.cmargins ||
35903 (this.position == "west" || this.position == "east" ?
35904 {top: 0, left: 2, right:2, bottom: 0} :
35905 {top: 2, left: 0, right:0, bottom: 2});
35907 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35910 this.bottomTabs = c.tabPosition != "top";
35912 this.autoScroll = c.autoScroll || false;
35917 this.duration = c.duration || .30;
35918 this.slideDuration = c.slideDuration || .45;
35923 * Returns true if this region is currently visible.
35924 * @return {Boolean}
35926 isVisible : function(){
35927 return this.visible;
35931 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35932 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35934 //setCollapsedTitle : function(title){
35935 // title = title || " ";
35936 // if(this.collapsedTitleTextEl){
35937 // this.collapsedTitleTextEl.innerHTML = title;
35941 getBox : function(){
35943 // if(!this.collapsed){
35944 b = this.el.getBox(false, true);
35946 // b = this.collapsedEl.getBox(false, true);
35951 getMargins : function(){
35952 return this.margins;
35953 //return this.collapsed ? this.cmargins : this.margins;
35956 highlight : function(){
35957 this.el.addClass("x-layout-panel-dragover");
35960 unhighlight : function(){
35961 this.el.removeClass("x-layout-panel-dragover");
35964 updateBox : function(box)
35966 if (!this.bodyEl) {
35967 return; // not rendered yet..
35971 if(!this.collapsed){
35972 this.el.dom.style.left = box.x + "px";
35973 this.el.dom.style.top = box.y + "px";
35974 this.updateBody(box.width, box.height);
35976 this.collapsedEl.dom.style.left = box.x + "px";
35977 this.collapsedEl.dom.style.top = box.y + "px";
35978 this.collapsedEl.setSize(box.width, box.height);
35981 this.tabs.autoSizeTabs();
35985 updateBody : function(w, h)
35988 this.el.setWidth(w);
35989 w -= this.el.getBorderWidth("rl");
35990 if(this.config.adjustments){
35991 w += this.config.adjustments[0];
35994 if(h !== null && h > 0){
35995 this.el.setHeight(h);
35996 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35997 h -= this.el.getBorderWidth("tb");
35998 if(this.config.adjustments){
35999 h += this.config.adjustments[1];
36001 this.bodyEl.setHeight(h);
36003 h = this.tabs.syncHeight(h);
36006 if(this.panelSize){
36007 w = w !== null ? w : this.panelSize.width;
36008 h = h !== null ? h : this.panelSize.height;
36010 if(this.activePanel){
36011 var el = this.activePanel.getEl();
36012 w = w !== null ? w : el.getWidth();
36013 h = h !== null ? h : el.getHeight();
36014 this.panelSize = {width: w, height: h};
36015 this.activePanel.setSize(w, h);
36017 if(Roo.isIE && this.tabs){
36018 this.tabs.el.repaint();
36023 * Returns the container element for this region.
36024 * @return {Roo.Element}
36026 getEl : function(){
36031 * Hides this region.
36034 //if(!this.collapsed){
36035 this.el.dom.style.left = "-2000px";
36038 // this.collapsedEl.dom.style.left = "-2000px";
36039 // this.collapsedEl.hide();
36041 this.visible = false;
36042 this.fireEvent("visibilitychange", this, false);
36046 * Shows this region if it was previously hidden.
36049 //if(!this.collapsed){
36052 // this.collapsedEl.show();
36054 this.visible = true;
36055 this.fireEvent("visibilitychange", this, true);
36058 closeClicked : function(){
36059 if(this.activePanel){
36060 this.remove(this.activePanel);
36064 collapseClick : function(e){
36066 e.stopPropagation();
36069 e.stopPropagation();
36075 * Collapses this region.
36076 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36079 collapse : function(skipAnim, skipCheck = false){
36080 if(this.collapsed) {
36084 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36086 this.collapsed = true;
36088 this.split.el.hide();
36090 if(this.config.animate && skipAnim !== true){
36091 this.fireEvent("invalidated", this);
36092 this.animateCollapse();
36094 this.el.setLocation(-20000,-20000);
36096 this.collapsedEl.show();
36097 this.fireEvent("collapsed", this);
36098 this.fireEvent("invalidated", this);
36104 animateCollapse : function(){
36109 * Expands this region if it was previously collapsed.
36110 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36111 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36114 expand : function(e, skipAnim){
36116 e.stopPropagation();
36118 if(!this.collapsed || this.el.hasActiveFx()) {
36122 this.afterSlideIn();
36125 this.collapsed = false;
36126 if(this.config.animate && skipAnim !== true){
36127 this.animateExpand();
36131 this.split.el.show();
36133 this.collapsedEl.setLocation(-2000,-2000);
36134 this.collapsedEl.hide();
36135 this.fireEvent("invalidated", this);
36136 this.fireEvent("expanded", this);
36140 animateExpand : function(){
36144 initTabs : function()
36146 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36148 var ts = new Roo.bootstrap.panel.Tabs({
36149 el: this.bodyEl.dom,
36150 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36151 disableTooltips: this.config.disableTabTips,
36152 toolbar : this.config.toolbar
36155 if(this.config.hideTabs){
36156 ts.stripWrap.setDisplayed(false);
36159 ts.resizeTabs = this.config.resizeTabs === true;
36160 ts.minTabWidth = this.config.minTabWidth || 40;
36161 ts.maxTabWidth = this.config.maxTabWidth || 250;
36162 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36163 ts.monitorResize = false;
36164 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36165 ts.bodyEl.addClass('roo-layout-tabs-body');
36166 this.panels.each(this.initPanelAsTab, this);
36169 initPanelAsTab : function(panel){
36170 var ti = this.tabs.addTab(
36174 this.config.closeOnTab && panel.isClosable(),
36177 if(panel.tabTip !== undefined){
36178 ti.setTooltip(panel.tabTip);
36180 ti.on("activate", function(){
36181 this.setActivePanel(panel);
36184 if(this.config.closeOnTab){
36185 ti.on("beforeclose", function(t, e){
36187 this.remove(panel);
36191 panel.tabItem = ti;
36196 updatePanelTitle : function(panel, title)
36198 if(this.activePanel == panel){
36199 this.updateTitle(title);
36202 var ti = this.tabs.getTab(panel.getEl().id);
36204 if(panel.tabTip !== undefined){
36205 ti.setTooltip(panel.tabTip);
36210 updateTitle : function(title){
36211 if(this.titleTextEl && !this.config.title){
36212 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36216 setActivePanel : function(panel)
36218 panel = this.getPanel(panel);
36219 if(this.activePanel && this.activePanel != panel){
36220 if(this.activePanel.setActiveState(false) === false){
36224 this.activePanel = panel;
36225 panel.setActiveState(true);
36226 if(this.panelSize){
36227 panel.setSize(this.panelSize.width, this.panelSize.height);
36230 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36232 this.updateTitle(panel.getTitle());
36234 this.fireEvent("invalidated", this);
36236 this.fireEvent("panelactivated", this, panel);
36240 * Shows the specified panel.
36241 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36242 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36244 showPanel : function(panel)
36246 panel = this.getPanel(panel);
36249 var tab = this.tabs.getTab(panel.getEl().id);
36250 if(tab.isHidden()){
36251 this.tabs.unhideTab(tab.id);
36255 this.setActivePanel(panel);
36262 * Get the active panel for this region.
36263 * @return {Roo.ContentPanel} The active panel or null
36265 getActivePanel : function(){
36266 return this.activePanel;
36269 validateVisibility : function(){
36270 if(this.panels.getCount() < 1){
36271 this.updateTitle(" ");
36272 this.closeBtn.hide();
36275 if(!this.isVisible()){
36282 * Adds the passed ContentPanel(s) to this region.
36283 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36284 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36286 add : function(panel)
36288 if(arguments.length > 1){
36289 for(var i = 0, len = arguments.length; i < len; i++) {
36290 this.add(arguments[i]);
36295 // if we have not been rendered yet, then we can not really do much of this..
36296 if (!this.bodyEl) {
36297 this.unrendered_panels.push(panel);
36304 if(this.hasPanel(panel)){
36305 this.showPanel(panel);
36308 panel.setRegion(this);
36309 this.panels.add(panel);
36310 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36311 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36312 // and hide them... ???
36313 this.bodyEl.dom.appendChild(panel.getEl().dom);
36314 if(panel.background !== true){
36315 this.setActivePanel(panel);
36317 this.fireEvent("paneladded", this, panel);
36324 this.initPanelAsTab(panel);
36328 if(panel.background !== true){
36329 this.tabs.activate(panel.getEl().id);
36331 this.fireEvent("paneladded", this, panel);
36336 * Hides the tab for the specified panel.
36337 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36339 hidePanel : function(panel){
36340 if(this.tabs && (panel = this.getPanel(panel))){
36341 this.tabs.hideTab(panel.getEl().id);
36346 * Unhides the tab for a previously hidden panel.
36347 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36349 unhidePanel : function(panel){
36350 if(this.tabs && (panel = this.getPanel(panel))){
36351 this.tabs.unhideTab(panel.getEl().id);
36355 clearPanels : function(){
36356 while(this.panels.getCount() > 0){
36357 this.remove(this.panels.first());
36362 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36363 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36364 * @param {Boolean} preservePanel Overrides the config preservePanel option
36365 * @return {Roo.ContentPanel} The panel that was removed
36367 remove : function(panel, preservePanel)
36369 panel = this.getPanel(panel);
36374 this.fireEvent("beforeremove", this, panel, e);
36375 if(e.cancel === true){
36378 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36379 var panelId = panel.getId();
36380 this.panels.removeKey(panelId);
36382 document.body.appendChild(panel.getEl().dom);
36385 this.tabs.removeTab(panel.getEl().id);
36386 }else if (!preservePanel){
36387 this.bodyEl.dom.removeChild(panel.getEl().dom);
36389 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36390 var p = this.panels.first();
36391 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36392 tempEl.appendChild(p.getEl().dom);
36393 this.bodyEl.update("");
36394 this.bodyEl.dom.appendChild(p.getEl().dom);
36396 this.updateTitle(p.getTitle());
36398 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36399 this.setActivePanel(p);
36401 panel.setRegion(null);
36402 if(this.activePanel == panel){
36403 this.activePanel = null;
36405 if(this.config.autoDestroy !== false && preservePanel !== true){
36406 try{panel.destroy();}catch(e){}
36408 this.fireEvent("panelremoved", this, panel);
36413 * Returns the TabPanel component used by this region
36414 * @return {Roo.TabPanel}
36416 getTabs : function(){
36420 createTool : function(parentEl, className){
36421 var btn = Roo.DomHelper.append(parentEl, {
36423 cls: "x-layout-tools-button",
36426 cls: "roo-layout-tools-button-inner " + className,
36430 btn.addClassOnOver("roo-layout-tools-button-over");
36435 * Ext JS Library 1.1.1
36436 * Copyright(c) 2006-2007, Ext JS, LLC.
36438 * Originally Released Under LGPL - original licence link has changed is not relivant.
36441 * <script type="text/javascript">
36447 * @class Roo.SplitLayoutRegion
36448 * @extends Roo.LayoutRegion
36449 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36451 Roo.bootstrap.layout.Split = function(config){
36452 this.cursor = config.cursor;
36453 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36456 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36458 splitTip : "Drag to resize.",
36459 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36460 useSplitTips : false,
36462 applyConfig : function(config){
36463 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36466 onRender : function(ctr,pos) {
36468 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36469 if(!this.config.split){
36474 var splitEl = Roo.DomHelper.append(ctr.dom, {
36476 id: this.el.id + "-split",
36477 cls: "roo-layout-split roo-layout-split-"+this.position,
36480 /** The SplitBar for this region
36481 * @type Roo.SplitBar */
36482 // does not exist yet...
36483 Roo.log([this.position, this.orientation]);
36485 this.split = new Roo.bootstrap.SplitBar({
36486 dragElement : splitEl,
36487 resizingElement: this.el,
36488 orientation : this.orientation
36491 this.split.on("moved", this.onSplitMove, this);
36492 this.split.useShim = this.config.useShim === true;
36493 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36494 if(this.useSplitTips){
36495 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36497 //if(config.collapsible){
36498 // this.split.el.on("dblclick", this.collapse, this);
36501 if(typeof this.config.minSize != "undefined"){
36502 this.split.minSize = this.config.minSize;
36504 if(typeof this.config.maxSize != "undefined"){
36505 this.split.maxSize = this.config.maxSize;
36507 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36508 this.hideSplitter();
36513 getHMaxSize : function(){
36514 var cmax = this.config.maxSize || 10000;
36515 var center = this.mgr.getRegion("center");
36516 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36519 getVMaxSize : function(){
36520 var cmax = this.config.maxSize || 10000;
36521 var center = this.mgr.getRegion("center");
36522 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36525 onSplitMove : function(split, newSize){
36526 this.fireEvent("resized", this, newSize);
36530 * Returns the {@link Roo.SplitBar} for this region.
36531 * @return {Roo.SplitBar}
36533 getSplitBar : function(){
36538 this.hideSplitter();
36539 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36542 hideSplitter : function(){
36544 this.split.el.setLocation(-2000,-2000);
36545 this.split.el.hide();
36551 this.split.el.show();
36553 Roo.bootstrap.layout.Split.superclass.show.call(this);
36556 beforeSlide: function(){
36557 if(Roo.isGecko){// firefox overflow auto bug workaround
36558 this.bodyEl.clip();
36560 this.tabs.bodyEl.clip();
36562 if(this.activePanel){
36563 this.activePanel.getEl().clip();
36565 if(this.activePanel.beforeSlide){
36566 this.activePanel.beforeSlide();
36572 afterSlide : function(){
36573 if(Roo.isGecko){// firefox overflow auto bug workaround
36574 this.bodyEl.unclip();
36576 this.tabs.bodyEl.unclip();
36578 if(this.activePanel){
36579 this.activePanel.getEl().unclip();
36580 if(this.activePanel.afterSlide){
36581 this.activePanel.afterSlide();
36587 initAutoHide : function(){
36588 if(this.autoHide !== false){
36589 if(!this.autoHideHd){
36590 var st = new Roo.util.DelayedTask(this.slideIn, this);
36591 this.autoHideHd = {
36592 "mouseout": function(e){
36593 if(!e.within(this.el, true)){
36597 "mouseover" : function(e){
36603 this.el.on(this.autoHideHd);
36607 clearAutoHide : function(){
36608 if(this.autoHide !== false){
36609 this.el.un("mouseout", this.autoHideHd.mouseout);
36610 this.el.un("mouseover", this.autoHideHd.mouseover);
36614 clearMonitor : function(){
36615 Roo.get(document).un("click", this.slideInIf, this);
36618 // these names are backwards but not changed for compat
36619 slideOut : function(){
36620 if(this.isSlid || this.el.hasActiveFx()){
36623 this.isSlid = true;
36624 if(this.collapseBtn){
36625 this.collapseBtn.hide();
36627 this.closeBtnState = this.closeBtn.getStyle('display');
36628 this.closeBtn.hide();
36630 this.stickBtn.show();
36633 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36634 this.beforeSlide();
36635 this.el.setStyle("z-index", 10001);
36636 this.el.slideIn(this.getSlideAnchor(), {
36637 callback: function(){
36639 this.initAutoHide();
36640 Roo.get(document).on("click", this.slideInIf, this);
36641 this.fireEvent("slideshow", this);
36648 afterSlideIn : function(){
36649 this.clearAutoHide();
36650 this.isSlid = false;
36651 this.clearMonitor();
36652 this.el.setStyle("z-index", "");
36653 if(this.collapseBtn){
36654 this.collapseBtn.show();
36656 this.closeBtn.setStyle('display', this.closeBtnState);
36658 this.stickBtn.hide();
36660 this.fireEvent("slidehide", this);
36663 slideIn : function(cb){
36664 if(!this.isSlid || this.el.hasActiveFx()){
36668 this.isSlid = false;
36669 this.beforeSlide();
36670 this.el.slideOut(this.getSlideAnchor(), {
36671 callback: function(){
36672 this.el.setLeftTop(-10000, -10000);
36674 this.afterSlideIn();
36682 slideInIf : function(e){
36683 if(!e.within(this.el)){
36688 animateCollapse : function(){
36689 this.beforeSlide();
36690 this.el.setStyle("z-index", 20000);
36691 var anchor = this.getSlideAnchor();
36692 this.el.slideOut(anchor, {
36693 callback : function(){
36694 this.el.setStyle("z-index", "");
36695 this.collapsedEl.slideIn(anchor, {duration:.3});
36697 this.el.setLocation(-10000,-10000);
36699 this.fireEvent("collapsed", this);
36706 animateExpand : function(){
36707 this.beforeSlide();
36708 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36709 this.el.setStyle("z-index", 20000);
36710 this.collapsedEl.hide({
36713 this.el.slideIn(this.getSlideAnchor(), {
36714 callback : function(){
36715 this.el.setStyle("z-index", "");
36718 this.split.el.show();
36720 this.fireEvent("invalidated", this);
36721 this.fireEvent("expanded", this);
36749 getAnchor : function(){
36750 return this.anchors[this.position];
36753 getCollapseAnchor : function(){
36754 return this.canchors[this.position];
36757 getSlideAnchor : function(){
36758 return this.sanchors[this.position];
36761 getAlignAdj : function(){
36762 var cm = this.cmargins;
36763 switch(this.position){
36779 getExpandAdj : function(){
36780 var c = this.collapsedEl, cm = this.cmargins;
36781 switch(this.position){
36783 return [-(cm.right+c.getWidth()+cm.left), 0];
36786 return [cm.right+c.getWidth()+cm.left, 0];
36789 return [0, -(cm.top+cm.bottom+c.getHeight())];
36792 return [0, cm.top+cm.bottom+c.getHeight()];
36798 * Ext JS Library 1.1.1
36799 * Copyright(c) 2006-2007, Ext JS, LLC.
36801 * Originally Released Under LGPL - original licence link has changed is not relivant.
36804 * <script type="text/javascript">
36807 * These classes are private internal classes
36809 Roo.bootstrap.layout.Center = function(config){
36810 config.region = "center";
36811 Roo.bootstrap.layout.Region.call(this, config);
36812 this.visible = true;
36813 this.minWidth = config.minWidth || 20;
36814 this.minHeight = config.minHeight || 20;
36817 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36819 // center panel can't be hidden
36823 // center panel can't be hidden
36826 getMinWidth: function(){
36827 return this.minWidth;
36830 getMinHeight: function(){
36831 return this.minHeight;
36844 Roo.bootstrap.layout.North = function(config)
36846 config.region = 'north';
36847 config.cursor = 'n-resize';
36849 Roo.bootstrap.layout.Split.call(this, config);
36853 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36854 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36855 this.split.el.addClass("roo-layout-split-v");
36857 var size = config.initialSize || config.height;
36858 if(typeof size != "undefined"){
36859 this.el.setHeight(size);
36862 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36864 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36868 getBox : function(){
36869 if(this.collapsed){
36870 return this.collapsedEl.getBox();
36872 var box = this.el.getBox();
36874 box.height += this.split.el.getHeight();
36879 updateBox : function(box){
36880 if(this.split && !this.collapsed){
36881 box.height -= this.split.el.getHeight();
36882 this.split.el.setLeft(box.x);
36883 this.split.el.setTop(box.y+box.height);
36884 this.split.el.setWidth(box.width);
36886 if(this.collapsed){
36887 this.updateBody(box.width, null);
36889 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36897 Roo.bootstrap.layout.South = function(config){
36898 config.region = 'south';
36899 config.cursor = 's-resize';
36900 Roo.bootstrap.layout.Split.call(this, config);
36902 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36903 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36904 this.split.el.addClass("roo-layout-split-v");
36906 var size = config.initialSize || config.height;
36907 if(typeof size != "undefined"){
36908 this.el.setHeight(size);
36912 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36913 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36914 getBox : function(){
36915 if(this.collapsed){
36916 return this.collapsedEl.getBox();
36918 var box = this.el.getBox();
36920 var sh = this.split.el.getHeight();
36927 updateBox : function(box){
36928 if(this.split && !this.collapsed){
36929 var sh = this.split.el.getHeight();
36932 this.split.el.setLeft(box.x);
36933 this.split.el.setTop(box.y-sh);
36934 this.split.el.setWidth(box.width);
36936 if(this.collapsed){
36937 this.updateBody(box.width, null);
36939 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36943 Roo.bootstrap.layout.East = function(config){
36944 config.region = "east";
36945 config.cursor = "e-resize";
36946 Roo.bootstrap.layout.Split.call(this, config);
36948 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36949 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36950 this.split.el.addClass("roo-layout-split-h");
36952 var size = config.initialSize || config.width;
36953 if(typeof size != "undefined"){
36954 this.el.setWidth(size);
36957 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36958 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36959 getBox : function(){
36960 if(this.collapsed){
36961 return this.collapsedEl.getBox();
36963 var box = this.el.getBox();
36965 var sw = this.split.el.getWidth();
36972 updateBox : function(box){
36973 if(this.split && !this.collapsed){
36974 var sw = this.split.el.getWidth();
36976 this.split.el.setLeft(box.x);
36977 this.split.el.setTop(box.y);
36978 this.split.el.setHeight(box.height);
36981 if(this.collapsed){
36982 this.updateBody(null, box.height);
36984 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36988 Roo.bootstrap.layout.West = function(config){
36989 config.region = "west";
36990 config.cursor = "w-resize";
36992 Roo.bootstrap.layout.Split.call(this, config);
36994 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36995 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36996 this.split.el.addClass("roo-layout-split-h");
37000 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37001 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37003 onRender: function(ctr, pos)
37005 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37006 var size = this.config.initialSize || this.config.width;
37007 if(typeof size != "undefined"){
37008 this.el.setWidth(size);
37012 getBox : function(){
37013 if(this.collapsed){
37014 return this.collapsedEl.getBox();
37016 var box = this.el.getBox();
37018 box.width += this.split.el.getWidth();
37023 updateBox : function(box){
37024 if(this.split && !this.collapsed){
37025 var sw = this.split.el.getWidth();
37027 this.split.el.setLeft(box.x+box.width);
37028 this.split.el.setTop(box.y);
37029 this.split.el.setHeight(box.height);
37031 if(this.collapsed){
37032 this.updateBody(null, box.height);
37034 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37037 Roo.namespace("Roo.bootstrap.panel");/*
37039 * Ext JS Library 1.1.1
37040 * Copyright(c) 2006-2007, Ext JS, LLC.
37042 * Originally Released Under LGPL - original licence link has changed is not relivant.
37045 * <script type="text/javascript">
37048 * @class Roo.ContentPanel
37049 * @extends Roo.util.Observable
37050 * A basic ContentPanel element.
37051 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
37052 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
37053 * @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
37054 * @cfg {Boolean} closable True if the panel can be closed/removed
37055 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
37056 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37057 * @cfg {Toolbar} toolbar A toolbar for this panel
37058 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37059 * @cfg {String} title The title for this panel
37060 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37061 * @cfg {String} url Calls {@link #setUrl} with this value
37062 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37063 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37064 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37065 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37066 * @cfg {Boolean} badges render the badges
37069 * Create a new ContentPanel.
37070 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37071 * @param {String/Object} config A string to set only the title or a config object
37072 * @param {String} content (optional) Set the HTML content for this panel
37073 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37075 Roo.bootstrap.panel.Content = function( config){
37077 this.tpl = config.tpl || false;
37079 var el = config.el;
37080 var content = config.content;
37082 if(config.autoCreate){ // xtype is available if this is called from factory
37085 this.el = Roo.get(el);
37086 if(!this.el && config && config.autoCreate){
37087 if(typeof config.autoCreate == "object"){
37088 if(!config.autoCreate.id){
37089 config.autoCreate.id = config.id||el;
37091 this.el = Roo.DomHelper.append(document.body,
37092 config.autoCreate, true);
37094 var elcfg = { tag: "div",
37095 cls: "roo-layout-inactive-content",
37099 elcfg.html = config.html;
37103 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37106 this.closable = false;
37107 this.loaded = false;
37108 this.active = false;
37111 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37113 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37115 this.wrapEl = this.el; //this.el.wrap();
37117 if (config.toolbar.items) {
37118 ti = config.toolbar.items ;
37119 delete config.toolbar.items ;
37123 this.toolbar.render(this.wrapEl, 'before');
37124 for(var i =0;i < ti.length;i++) {
37125 // Roo.log(['add child', items[i]]);
37126 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37128 this.toolbar.items = nitems;
37129 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37130 delete config.toolbar;
37134 // xtype created footer. - not sure if will work as we normally have to render first..
37135 if (this.footer && !this.footer.el && this.footer.xtype) {
37136 if (!this.wrapEl) {
37137 this.wrapEl = this.el.wrap();
37140 this.footer.container = this.wrapEl.createChild();
37142 this.footer = Roo.factory(this.footer, Roo);
37147 if(typeof config == "string"){
37148 this.title = config;
37150 Roo.apply(this, config);
37154 this.resizeEl = Roo.get(this.resizeEl, true);
37156 this.resizeEl = this.el;
37158 // handle view.xtype
37166 * Fires when this panel is activated.
37167 * @param {Roo.ContentPanel} this
37171 * @event deactivate
37172 * Fires when this panel is activated.
37173 * @param {Roo.ContentPanel} this
37175 "deactivate" : true,
37179 * Fires when this panel is resized if fitToFrame is true.
37180 * @param {Roo.ContentPanel} this
37181 * @param {Number} width The width after any component adjustments
37182 * @param {Number} height The height after any component adjustments
37188 * Fires when this tab is created
37189 * @param {Roo.ContentPanel} this
37200 if(this.autoScroll){
37201 this.resizeEl.setStyle("overflow", "auto");
37203 // fix randome scrolling
37204 //this.el.on('scroll', function() {
37205 // Roo.log('fix random scolling');
37206 // this.scrollTo('top',0);
37209 content = content || this.content;
37211 this.setContent(content);
37213 if(config && config.url){
37214 this.setUrl(this.url, this.params, this.loadOnce);
37219 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37221 if (this.view && typeof(this.view.xtype) != 'undefined') {
37222 this.view.el = this.el.appendChild(document.createElement("div"));
37223 this.view = Roo.factory(this.view);
37224 this.view.render && this.view.render(false, '');
37228 this.fireEvent('render', this);
37231 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37235 setRegion : function(region){
37236 this.region = region;
37237 this.setActiveClass(region && !this.background);
37241 setActiveClass: function(state)
37244 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37245 this.el.setStyle('position','relative');
37247 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37248 this.el.setStyle('position', 'absolute');
37253 * Returns the toolbar for this Panel if one was configured.
37254 * @return {Roo.Toolbar}
37256 getToolbar : function(){
37257 return this.toolbar;
37260 setActiveState : function(active)
37262 this.active = active;
37263 this.setActiveClass(active);
37265 if(this.fireEvent("deactivate", this) === false){
37270 this.fireEvent("activate", this);
37274 * Updates this panel's element
37275 * @param {String} content The new content
37276 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37278 setContent : function(content, loadScripts){
37279 this.el.update(content, loadScripts);
37282 ignoreResize : function(w, h){
37283 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37286 this.lastSize = {width: w, height: h};
37291 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37292 * @return {Roo.UpdateManager} The UpdateManager
37294 getUpdateManager : function(){
37295 return this.el.getUpdateManager();
37298 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37299 * @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:
37302 url: "your-url.php",
37303 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37304 callback: yourFunction,
37305 scope: yourObject, //(optional scope)
37308 text: "Loading...",
37313 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37314 * 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.
37315 * @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}
37316 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37317 * @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.
37318 * @return {Roo.ContentPanel} this
37321 var um = this.el.getUpdateManager();
37322 um.update.apply(um, arguments);
37328 * 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.
37329 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37330 * @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)
37331 * @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)
37332 * @return {Roo.UpdateManager} The UpdateManager
37334 setUrl : function(url, params, loadOnce){
37335 if(this.refreshDelegate){
37336 this.removeListener("activate", this.refreshDelegate);
37338 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37339 this.on("activate", this.refreshDelegate);
37340 return this.el.getUpdateManager();
37343 _handleRefresh : function(url, params, loadOnce){
37344 if(!loadOnce || !this.loaded){
37345 var updater = this.el.getUpdateManager();
37346 updater.update(url, params, this._setLoaded.createDelegate(this));
37350 _setLoaded : function(){
37351 this.loaded = true;
37355 * Returns this panel's id
37358 getId : function(){
37363 * Returns this panel's element - used by regiosn to add.
37364 * @return {Roo.Element}
37366 getEl : function(){
37367 return this.wrapEl || this.el;
37372 adjustForComponents : function(width, height)
37374 //Roo.log('adjustForComponents ');
37375 if(this.resizeEl != this.el){
37376 width -= this.el.getFrameWidth('lr');
37377 height -= this.el.getFrameWidth('tb');
37380 var te = this.toolbar.getEl();
37381 te.setWidth(width);
37382 height -= te.getHeight();
37385 var te = this.footer.getEl();
37386 te.setWidth(width);
37387 height -= te.getHeight();
37391 if(this.adjustments){
37392 width += this.adjustments[0];
37393 height += this.adjustments[1];
37395 return {"width": width, "height": height};
37398 setSize : function(width, height){
37399 if(this.fitToFrame && !this.ignoreResize(width, height)){
37400 if(this.fitContainer && this.resizeEl != this.el){
37401 this.el.setSize(width, height);
37403 var size = this.adjustForComponents(width, height);
37404 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37405 this.fireEvent('resize', this, size.width, size.height);
37410 * Returns this panel's title
37413 getTitle : function(){
37415 if (typeof(this.title) != 'object') {
37420 for (var k in this.title) {
37421 if (!this.title.hasOwnProperty(k)) {
37425 if (k.indexOf('-') >= 0) {
37426 var s = k.split('-');
37427 for (var i = 0; i<s.length; i++) {
37428 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37431 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37438 * Set this panel's title
37439 * @param {String} title
37441 setTitle : function(title){
37442 this.title = title;
37444 this.region.updatePanelTitle(this, title);
37449 * Returns true is this panel was configured to be closable
37450 * @return {Boolean}
37452 isClosable : function(){
37453 return this.closable;
37456 beforeSlide : function(){
37458 this.resizeEl.clip();
37461 afterSlide : function(){
37463 this.resizeEl.unclip();
37467 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37468 * Will fail silently if the {@link #setUrl} method has not been called.
37469 * This does not activate the panel, just updates its content.
37471 refresh : function(){
37472 if(this.refreshDelegate){
37473 this.loaded = false;
37474 this.refreshDelegate();
37479 * Destroys this panel
37481 destroy : function(){
37482 this.el.removeAllListeners();
37483 var tempEl = document.createElement("span");
37484 tempEl.appendChild(this.el.dom);
37485 tempEl.innerHTML = "";
37491 * form - if the content panel contains a form - this is a reference to it.
37492 * @type {Roo.form.Form}
37496 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37497 * This contains a reference to it.
37503 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37513 * @param {Object} cfg Xtype definition of item to add.
37517 getChildContainer: function () {
37518 return this.getEl();
37523 var ret = new Roo.factory(cfg);
37528 if (cfg.xtype.match(/^Form$/)) {
37531 //if (this.footer) {
37532 // el = this.footer.container.insertSibling(false, 'before');
37534 el = this.el.createChild();
37537 this.form = new Roo.form.Form(cfg);
37540 if ( this.form.allItems.length) {
37541 this.form.render(el.dom);
37545 // should only have one of theses..
37546 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37547 // views.. should not be just added - used named prop 'view''
37549 cfg.el = this.el.appendChild(document.createElement("div"));
37552 var ret = new Roo.factory(cfg);
37554 ret.render && ret.render(false, ''); // render blank..
37564 * @class Roo.bootstrap.panel.Grid
37565 * @extends Roo.bootstrap.panel.Content
37567 * Create a new GridPanel.
37568 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37569 * @param {Object} config A the config object
37575 Roo.bootstrap.panel.Grid = function(config)
37579 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37580 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37582 config.el = this.wrapper;
37583 //this.el = this.wrapper;
37585 if (config.container) {
37586 // ctor'ed from a Border/panel.grid
37589 this.wrapper.setStyle("overflow", "hidden");
37590 this.wrapper.addClass('roo-grid-container');
37595 if(config.toolbar){
37596 var tool_el = this.wrapper.createChild();
37597 this.toolbar = Roo.factory(config.toolbar);
37599 if (config.toolbar.items) {
37600 ti = config.toolbar.items ;
37601 delete config.toolbar.items ;
37605 this.toolbar.render(tool_el);
37606 for(var i =0;i < ti.length;i++) {
37607 // Roo.log(['add child', items[i]]);
37608 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37610 this.toolbar.items = nitems;
37612 delete config.toolbar;
37615 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37616 config.grid.scrollBody = true;;
37617 config.grid.monitorWindowResize = false; // turn off autosizing
37618 config.grid.autoHeight = false;
37619 config.grid.autoWidth = false;
37621 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37623 if (config.background) {
37624 // render grid on panel activation (if panel background)
37625 this.on('activate', function(gp) {
37626 if (!gp.grid.rendered) {
37627 gp.grid.render(this.wrapper);
37628 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37633 this.grid.render(this.wrapper);
37634 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37637 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37638 // ??? needed ??? config.el = this.wrapper;
37643 // xtype created footer. - not sure if will work as we normally have to render first..
37644 if (this.footer && !this.footer.el && this.footer.xtype) {
37646 var ctr = this.grid.getView().getFooterPanel(true);
37647 this.footer.dataSource = this.grid.dataSource;
37648 this.footer = Roo.factory(this.footer, Roo);
37649 this.footer.render(ctr);
37659 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37660 getId : function(){
37661 return this.grid.id;
37665 * Returns the grid for this panel
37666 * @return {Roo.bootstrap.Table}
37668 getGrid : function(){
37672 setSize : function(width, height){
37673 if(!this.ignoreResize(width, height)){
37674 var grid = this.grid;
37675 var size = this.adjustForComponents(width, height);
37676 var gridel = grid.getGridEl();
37677 gridel.setSize(size.width, size.height);
37679 var thd = grid.getGridEl().select('thead',true).first();
37680 var tbd = grid.getGridEl().select('tbody', true).first();
37682 tbd.setSize(width, height - thd.getHeight());
37691 beforeSlide : function(){
37692 this.grid.getView().scroller.clip();
37695 afterSlide : function(){
37696 this.grid.getView().scroller.unclip();
37699 destroy : function(){
37700 this.grid.destroy();
37702 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37707 * @class Roo.bootstrap.panel.Nest
37708 * @extends Roo.bootstrap.panel.Content
37710 * Create a new Panel, that can contain a layout.Border.
37713 * @param {Roo.BorderLayout} layout The layout for this panel
37714 * @param {String/Object} config A string to set only the title or a config object
37716 Roo.bootstrap.panel.Nest = function(config)
37718 // construct with only one argument..
37719 /* FIXME - implement nicer consturctors
37720 if (layout.layout) {
37722 layout = config.layout;
37723 delete config.layout;
37725 if (layout.xtype && !layout.getEl) {
37726 // then layout needs constructing..
37727 layout = Roo.factory(layout, Roo);
37731 config.el = config.layout.getEl();
37733 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37735 config.layout.monitorWindowResize = false; // turn off autosizing
37736 this.layout = config.layout;
37737 this.layout.getEl().addClass("roo-layout-nested-layout");
37744 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37746 setSize : function(width, height){
37747 if(!this.ignoreResize(width, height)){
37748 var size = this.adjustForComponents(width, height);
37749 var el = this.layout.getEl();
37750 if (size.height < 1) {
37751 el.setWidth(size.width);
37753 el.setSize(size.width, size.height);
37755 var touch = el.dom.offsetWidth;
37756 this.layout.layout();
37757 // ie requires a double layout on the first pass
37758 if(Roo.isIE && !this.initialized){
37759 this.initialized = true;
37760 this.layout.layout();
37765 // activate all subpanels if not currently active..
37767 setActiveState : function(active){
37768 this.active = active;
37769 this.setActiveClass(active);
37772 this.fireEvent("deactivate", this);
37776 this.fireEvent("activate", this);
37777 // not sure if this should happen before or after..
37778 if (!this.layout) {
37779 return; // should not happen..
37782 for (var r in this.layout.regions) {
37783 reg = this.layout.getRegion(r);
37784 if (reg.getActivePanel()) {
37785 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37786 reg.setActivePanel(reg.getActivePanel());
37789 if (!reg.panels.length) {
37792 reg.showPanel(reg.getPanel(0));
37801 * Returns the nested BorderLayout for this panel
37802 * @return {Roo.BorderLayout}
37804 getLayout : function(){
37805 return this.layout;
37809 * Adds a xtype elements to the layout of the nested panel
37813 xtype : 'ContentPanel',
37820 xtype : 'NestedLayoutPanel',
37826 items : [ ... list of content panels or nested layout panels.. ]
37830 * @param {Object} cfg Xtype definition of item to add.
37832 addxtype : function(cfg) {
37833 return this.layout.addxtype(cfg);
37838 * Ext JS Library 1.1.1
37839 * Copyright(c) 2006-2007, Ext JS, LLC.
37841 * Originally Released Under LGPL - original licence link has changed is not relivant.
37844 * <script type="text/javascript">
37847 * @class Roo.TabPanel
37848 * @extends Roo.util.Observable
37849 * A lightweight tab container.
37853 // basic tabs 1, built from existing content
37854 var tabs = new Roo.TabPanel("tabs1");
37855 tabs.addTab("script", "View Script");
37856 tabs.addTab("markup", "View Markup");
37857 tabs.activate("script");
37859 // more advanced tabs, built from javascript
37860 var jtabs = new Roo.TabPanel("jtabs");
37861 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37863 // set up the UpdateManager
37864 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37865 var updater = tab2.getUpdateManager();
37866 updater.setDefaultUrl("ajax1.htm");
37867 tab2.on('activate', updater.refresh, updater, true);
37869 // Use setUrl for Ajax loading
37870 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37871 tab3.setUrl("ajax2.htm", null, true);
37874 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37877 jtabs.activate("jtabs-1");
37880 * Create a new TabPanel.
37881 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37882 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37884 Roo.bootstrap.panel.Tabs = function(config){
37886 * The container element for this TabPanel.
37887 * @type Roo.Element
37889 this.el = Roo.get(config.el);
37892 if(typeof config == "boolean"){
37893 this.tabPosition = config ? "bottom" : "top";
37895 Roo.apply(this, config);
37899 if(this.tabPosition == "bottom"){
37900 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37901 this.el.addClass("roo-tabs-bottom");
37903 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37904 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37905 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37907 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37909 if(this.tabPosition != "bottom"){
37910 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37911 * @type Roo.Element
37913 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37914 this.el.addClass("roo-tabs-top");
37918 this.bodyEl.setStyle("position", "relative");
37920 this.active = null;
37921 this.activateDelegate = this.activate.createDelegate(this);
37926 * Fires when the active tab changes
37927 * @param {Roo.TabPanel} this
37928 * @param {Roo.TabPanelItem} activePanel The new active tab
37932 * @event beforetabchange
37933 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37934 * @param {Roo.TabPanel} this
37935 * @param {Object} e Set cancel to true on this object to cancel the tab change
37936 * @param {Roo.TabPanelItem} tab The tab being changed to
37938 "beforetabchange" : true
37941 Roo.EventManager.onWindowResize(this.onResize, this);
37942 this.cpad = this.el.getPadding("lr");
37943 this.hiddenCount = 0;
37946 // toolbar on the tabbar support...
37947 if (this.toolbar) {
37948 alert("no toolbar support yet");
37949 this.toolbar = false;
37951 var tcfg = this.toolbar;
37952 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37953 this.toolbar = new Roo.Toolbar(tcfg);
37954 if (Roo.isSafari) {
37955 var tbl = tcfg.container.child('table', true);
37956 tbl.setAttribute('width', '100%');
37964 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37967 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37969 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37971 tabPosition : "top",
37973 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37975 currentTabWidth : 0,
37977 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37981 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37985 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37987 preferredTabWidth : 175,
37989 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37991 resizeTabs : false,
37993 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37995 monitorResize : true,
37997 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
38002 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38003 * @param {String} id The id of the div to use <b>or create</b>
38004 * @param {String} text The text for the tab
38005 * @param {String} content (optional) Content to put in the TabPanelItem body
38006 * @param {Boolean} closable (optional) True to create a close icon on the tab
38007 * @return {Roo.TabPanelItem} The created TabPanelItem
38009 addTab : function(id, text, content, closable, tpl)
38011 var item = new Roo.bootstrap.panel.TabItem({
38015 closable : closable,
38018 this.addTabItem(item);
38020 item.setContent(content);
38026 * Returns the {@link Roo.TabPanelItem} with the specified id/index
38027 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38028 * @return {Roo.TabPanelItem}
38030 getTab : function(id){
38031 return this.items[id];
38035 * Hides the {@link Roo.TabPanelItem} with the specified id/index
38036 * @param {String/Number} id The id or index of the TabPanelItem to hide.
38038 hideTab : function(id){
38039 var t = this.items[id];
38042 this.hiddenCount++;
38043 this.autoSizeTabs();
38048 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38049 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38051 unhideTab : function(id){
38052 var t = this.items[id];
38054 t.setHidden(false);
38055 this.hiddenCount--;
38056 this.autoSizeTabs();
38061 * Adds an existing {@link Roo.TabPanelItem}.
38062 * @param {Roo.TabPanelItem} item The TabPanelItem to add
38064 addTabItem : function(item){
38065 this.items[item.id] = item;
38066 this.items.push(item);
38067 // if(this.resizeTabs){
38068 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38069 // this.autoSizeTabs();
38071 // item.autoSize();
38076 * Removes a {@link Roo.TabPanelItem}.
38077 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38079 removeTab : function(id){
38080 var items = this.items;
38081 var tab = items[id];
38082 if(!tab) { return; }
38083 var index = items.indexOf(tab);
38084 if(this.active == tab && items.length > 1){
38085 var newTab = this.getNextAvailable(index);
38090 this.stripEl.dom.removeChild(tab.pnode.dom);
38091 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38092 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38094 items.splice(index, 1);
38095 delete this.items[tab.id];
38096 tab.fireEvent("close", tab);
38097 tab.purgeListeners();
38098 this.autoSizeTabs();
38101 getNextAvailable : function(start){
38102 var items = this.items;
38104 // look for a next tab that will slide over to
38105 // replace the one being removed
38106 while(index < items.length){
38107 var item = items[++index];
38108 if(item && !item.isHidden()){
38112 // if one isn't found select the previous tab (on the left)
38115 var item = items[--index];
38116 if(item && !item.isHidden()){
38124 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38125 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38127 disableTab : function(id){
38128 var tab = this.items[id];
38129 if(tab && this.active != tab){
38135 * Enables a {@link Roo.TabPanelItem} that is disabled.
38136 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38138 enableTab : function(id){
38139 var tab = this.items[id];
38144 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38145 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38146 * @return {Roo.TabPanelItem} The TabPanelItem.
38148 activate : function(id){
38149 var tab = this.items[id];
38153 if(tab == this.active || tab.disabled){
38157 this.fireEvent("beforetabchange", this, e, tab);
38158 if(e.cancel !== true && !tab.disabled){
38160 this.active.hide();
38162 this.active = this.items[id];
38163 this.active.show();
38164 this.fireEvent("tabchange", this, this.active);
38170 * Gets the active {@link Roo.TabPanelItem}.
38171 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38173 getActiveTab : function(){
38174 return this.active;
38178 * Updates the tab body element to fit the height of the container element
38179 * for overflow scrolling
38180 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38182 syncHeight : function(targetHeight){
38183 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38184 var bm = this.bodyEl.getMargins();
38185 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38186 this.bodyEl.setHeight(newHeight);
38190 onResize : function(){
38191 if(this.monitorResize){
38192 this.autoSizeTabs();
38197 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38199 beginUpdate : function(){
38200 this.updating = true;
38204 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38206 endUpdate : function(){
38207 this.updating = false;
38208 this.autoSizeTabs();
38212 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38214 autoSizeTabs : function(){
38215 var count = this.items.length;
38216 var vcount = count - this.hiddenCount;
38217 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38220 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38221 var availWidth = Math.floor(w / vcount);
38222 var b = this.stripBody;
38223 if(b.getWidth() > w){
38224 var tabs = this.items;
38225 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38226 if(availWidth < this.minTabWidth){
38227 /*if(!this.sleft){ // incomplete scrolling code
38228 this.createScrollButtons();
38231 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38234 if(this.currentTabWidth < this.preferredTabWidth){
38235 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38241 * Returns the number of tabs in this TabPanel.
38244 getCount : function(){
38245 return this.items.length;
38249 * Resizes all the tabs to the passed width
38250 * @param {Number} The new width
38252 setTabWidth : function(width){
38253 this.currentTabWidth = width;
38254 for(var i = 0, len = this.items.length; i < len; i++) {
38255 if(!this.items[i].isHidden()) {
38256 this.items[i].setWidth(width);
38262 * Destroys this TabPanel
38263 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38265 destroy : function(removeEl){
38266 Roo.EventManager.removeResizeListener(this.onResize, this);
38267 for(var i = 0, len = this.items.length; i < len; i++){
38268 this.items[i].purgeListeners();
38270 if(removeEl === true){
38271 this.el.update("");
38276 createStrip : function(container)
38278 var strip = document.createElement("nav");
38279 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38280 container.appendChild(strip);
38284 createStripList : function(strip)
38286 // div wrapper for retard IE
38287 // returns the "tr" element.
38288 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38289 //'<div class="x-tabs-strip-wrap">'+
38290 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38291 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38292 return strip.firstChild; //.firstChild.firstChild.firstChild;
38294 createBody : function(container)
38296 var body = document.createElement("div");
38297 Roo.id(body, "tab-body");
38298 //Roo.fly(body).addClass("x-tabs-body");
38299 Roo.fly(body).addClass("tab-content");
38300 container.appendChild(body);
38303 createItemBody :function(bodyEl, id){
38304 var body = Roo.getDom(id);
38306 body = document.createElement("div");
38309 //Roo.fly(body).addClass("x-tabs-item-body");
38310 Roo.fly(body).addClass("tab-pane");
38311 bodyEl.insertBefore(body, bodyEl.firstChild);
38315 createStripElements : function(stripEl, text, closable, tpl)
38317 var td = document.createElement("li"); // was td..
38320 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38323 stripEl.appendChild(td);
38325 td.className = "x-tabs-closable";
38326 if(!this.closeTpl){
38327 this.closeTpl = new Roo.Template(
38328 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38329 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38330 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38333 var el = this.closeTpl.overwrite(td, {"text": text});
38334 var close = el.getElementsByTagName("div")[0];
38335 var inner = el.getElementsByTagName("em")[0];
38336 return {"el": el, "close": close, "inner": inner};
38339 // not sure what this is..
38340 // if(!this.tabTpl){
38341 //this.tabTpl = new Roo.Template(
38342 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38343 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38345 // this.tabTpl = new Roo.Template(
38346 // '<a href="#">' +
38347 // '<span unselectable="on"' +
38348 // (this.disableTooltips ? '' : ' title="{text}"') +
38349 // ' >{text}</span></a>'
38355 var template = tpl || this.tabTpl || false;
38359 template = new Roo.Template(
38361 '<span unselectable="on"' +
38362 (this.disableTooltips ? '' : ' title="{text}"') +
38363 ' >{text}</span></a>'
38367 switch (typeof(template)) {
38371 template = new Roo.Template(template);
38377 var el = template.overwrite(td, {"text": text});
38379 var inner = el.getElementsByTagName("span")[0];
38381 return {"el": el, "inner": inner};
38389 * @class Roo.TabPanelItem
38390 * @extends Roo.util.Observable
38391 * Represents an individual item (tab plus body) in a TabPanel.
38392 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38393 * @param {String} id The id of this TabPanelItem
38394 * @param {String} text The text for the tab of this TabPanelItem
38395 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38397 Roo.bootstrap.panel.TabItem = function(config){
38399 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38400 * @type Roo.TabPanel
38402 this.tabPanel = config.panel;
38404 * The id for this TabPanelItem
38407 this.id = config.id;
38409 this.disabled = false;
38411 this.text = config.text;
38413 this.loaded = false;
38414 this.closable = config.closable;
38417 * The body element for this TabPanelItem.
38418 * @type Roo.Element
38420 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38421 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38422 this.bodyEl.setStyle("display", "block");
38423 this.bodyEl.setStyle("zoom", "1");
38424 //this.hideAction();
38426 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38428 this.el = Roo.get(els.el);
38429 this.inner = Roo.get(els.inner, true);
38430 this.textEl = Roo.get(this.el.dom.firstChild, true);
38431 this.pnode = Roo.get(els.el.parentNode, true);
38432 // this.el.on("mousedown", this.onTabMouseDown, this);
38433 this.el.on("click", this.onTabClick, this);
38435 if(config.closable){
38436 var c = Roo.get(els.close, true);
38437 c.dom.title = this.closeText;
38438 c.addClassOnOver("close-over");
38439 c.on("click", this.closeClick, this);
38445 * Fires when this tab becomes the active tab.
38446 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38447 * @param {Roo.TabPanelItem} this
38451 * @event beforeclose
38452 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38453 * @param {Roo.TabPanelItem} this
38454 * @param {Object} e Set cancel to true on this object to cancel the close.
38456 "beforeclose": true,
38459 * Fires when this tab is closed.
38460 * @param {Roo.TabPanelItem} this
38464 * @event deactivate
38465 * Fires when this tab is no longer the active tab.
38466 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38467 * @param {Roo.TabPanelItem} this
38469 "deactivate" : true
38471 this.hidden = false;
38473 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38476 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38478 purgeListeners : function(){
38479 Roo.util.Observable.prototype.purgeListeners.call(this);
38480 this.el.removeAllListeners();
38483 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38486 this.pnode.addClass("active");
38489 this.tabPanel.stripWrap.repaint();
38491 this.fireEvent("activate", this.tabPanel, this);
38495 * Returns true if this tab is the active tab.
38496 * @return {Boolean}
38498 isActive : function(){
38499 return this.tabPanel.getActiveTab() == this;
38503 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38506 this.pnode.removeClass("active");
38508 this.fireEvent("deactivate", this.tabPanel, this);
38511 hideAction : function(){
38512 this.bodyEl.hide();
38513 this.bodyEl.setStyle("position", "absolute");
38514 this.bodyEl.setLeft("-20000px");
38515 this.bodyEl.setTop("-20000px");
38518 showAction : function(){
38519 this.bodyEl.setStyle("position", "relative");
38520 this.bodyEl.setTop("");
38521 this.bodyEl.setLeft("");
38522 this.bodyEl.show();
38526 * Set the tooltip for the tab.
38527 * @param {String} tooltip The tab's tooltip
38529 setTooltip : function(text){
38530 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38531 this.textEl.dom.qtip = text;
38532 this.textEl.dom.removeAttribute('title');
38534 this.textEl.dom.title = text;
38538 onTabClick : function(e){
38539 e.preventDefault();
38540 this.tabPanel.activate(this.id);
38543 onTabMouseDown : function(e){
38544 e.preventDefault();
38545 this.tabPanel.activate(this.id);
38548 getWidth : function(){
38549 return this.inner.getWidth();
38552 setWidth : function(width){
38553 var iwidth = width - this.pnode.getPadding("lr");
38554 this.inner.setWidth(iwidth);
38555 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38556 this.pnode.setWidth(width);
38560 * Show or hide the tab
38561 * @param {Boolean} hidden True to hide or false to show.
38563 setHidden : function(hidden){
38564 this.hidden = hidden;
38565 this.pnode.setStyle("display", hidden ? "none" : "");
38569 * Returns true if this tab is "hidden"
38570 * @return {Boolean}
38572 isHidden : function(){
38573 return this.hidden;
38577 * Returns the text for this tab
38580 getText : function(){
38584 autoSize : function(){
38585 //this.el.beginMeasure();
38586 this.textEl.setWidth(1);
38588 * #2804 [new] Tabs in Roojs
38589 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38591 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38592 //this.el.endMeasure();
38596 * Sets the text for the tab (Note: this also sets the tooltip text)
38597 * @param {String} text The tab's text and tooltip
38599 setText : function(text){
38601 this.textEl.update(text);
38602 this.setTooltip(text);
38603 //if(!this.tabPanel.resizeTabs){
38604 // this.autoSize();
38608 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38610 activate : function(){
38611 this.tabPanel.activate(this.id);
38615 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38617 disable : function(){
38618 if(this.tabPanel.active != this){
38619 this.disabled = true;
38620 this.pnode.addClass("disabled");
38625 * Enables this TabPanelItem if it was previously disabled.
38627 enable : function(){
38628 this.disabled = false;
38629 this.pnode.removeClass("disabled");
38633 * Sets the content for this TabPanelItem.
38634 * @param {String} content The content
38635 * @param {Boolean} loadScripts true to look for and load scripts
38637 setContent : function(content, loadScripts){
38638 this.bodyEl.update(content, loadScripts);
38642 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38643 * @return {Roo.UpdateManager} The UpdateManager
38645 getUpdateManager : function(){
38646 return this.bodyEl.getUpdateManager();
38650 * Set a URL to be used to load the content for this TabPanelItem.
38651 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38652 * @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)
38653 * @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)
38654 * @return {Roo.UpdateManager} The UpdateManager
38656 setUrl : function(url, params, loadOnce){
38657 if(this.refreshDelegate){
38658 this.un('activate', this.refreshDelegate);
38660 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38661 this.on("activate", this.refreshDelegate);
38662 return this.bodyEl.getUpdateManager();
38666 _handleRefresh : function(url, params, loadOnce){
38667 if(!loadOnce || !this.loaded){
38668 var updater = this.bodyEl.getUpdateManager();
38669 updater.update(url, params, this._setLoaded.createDelegate(this));
38674 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38675 * Will fail silently if the setUrl method has not been called.
38676 * This does not activate the panel, just updates its content.
38678 refresh : function(){
38679 if(this.refreshDelegate){
38680 this.loaded = false;
38681 this.refreshDelegate();
38686 _setLoaded : function(){
38687 this.loaded = true;
38691 closeClick : function(e){
38694 this.fireEvent("beforeclose", this, o);
38695 if(o.cancel !== true){
38696 this.tabPanel.removeTab(this.id);
38700 * The text displayed in the tooltip for the close icon.
38703 closeText : "Close this tab"
38706 * This script refer to:
38707 * Title: International Telephone Input
38708 * Author: Jack O'Connor
38709 * Code version: v12.1.12
38710 * Availability: https://github.com/jackocnr/intl-tel-input.git
38713 Roo.bootstrap.PhoneInputData = function() {
38716 "Afghanistan (افغانستان)",
38721 "Albania (Shqipëri)",
38726 "Algeria (الجزائر)",
38751 "Antigua and Barbuda",
38761 "Armenia (Հայաստան)",
38777 "Austria (Österreich)",
38782 "Azerbaijan (Azərbaycan)",
38792 "Bahrain (البحرين)",
38797 "Bangladesh (বাংলাদেশ)",
38807 "Belarus (Беларусь)",
38812 "Belgium (België)",
38842 "Bosnia and Herzegovina (Босна и Херцеговина)",
38857 "British Indian Ocean Territory",
38862 "British Virgin Islands",
38872 "Bulgaria (България)",
38882 "Burundi (Uburundi)",
38887 "Cambodia (កម្ពុជា)",
38892 "Cameroon (Cameroun)",
38901 ["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"]
38904 "Cape Verde (Kabu Verdi)",
38909 "Caribbean Netherlands",
38920 "Central African Republic (République centrafricaine)",
38940 "Christmas Island",
38946 "Cocos (Keeling) Islands",
38957 "Comoros (جزر القمر)",
38962 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38967 "Congo (Republic) (Congo-Brazzaville)",
38987 "Croatia (Hrvatska)",
39008 "Czech Republic (Česká republika)",
39013 "Denmark (Danmark)",
39028 "Dominican Republic (República Dominicana)",
39032 ["809", "829", "849"]
39050 "Equatorial Guinea (Guinea Ecuatorial)",
39070 "Falkland Islands (Islas Malvinas)",
39075 "Faroe Islands (Føroyar)",
39096 "French Guiana (Guyane française)",
39101 "French Polynesia (Polynésie française)",
39116 "Georgia (საქართველო)",
39121 "Germany (Deutschland)",
39141 "Greenland (Kalaallit Nunaat)",
39178 "Guinea-Bissau (Guiné Bissau)",
39203 "Hungary (Magyarország)",
39208 "Iceland (Ísland)",
39228 "Iraq (العراق)",
39244 "Israel (ישראל)",
39271 "Jordan (الأردن)",
39276 "Kazakhstan (Казахстан)",
39297 "Kuwait (الكويت)",
39302 "Kyrgyzstan (Кыргызстан)",
39312 "Latvia (Latvija)",
39317 "Lebanon (لبنان)",
39332 "Libya (ليبيا)",
39342 "Lithuania (Lietuva)",
39357 "Macedonia (FYROM) (Македонија)",
39362 "Madagascar (Madagasikara)",
39392 "Marshall Islands",
39402 "Mauritania (موريتانيا)",
39407 "Mauritius (Moris)",
39428 "Moldova (Republica Moldova)",
39438 "Mongolia (Монгол)",
39443 "Montenegro (Crna Gora)",
39453 "Morocco (المغرب)",
39459 "Mozambique (Moçambique)",
39464 "Myanmar (Burma) (မြန်မာ)",
39469 "Namibia (Namibië)",
39484 "Netherlands (Nederland)",
39489 "New Caledonia (Nouvelle-Calédonie)",
39524 "North Korea (조선 민주주의 인민 공화국)",
39529 "Northern Mariana Islands",
39545 "Pakistan (پاکستان)",
39555 "Palestine (فلسطين)",
39565 "Papua New Guinea",
39607 "Réunion (La Réunion)",
39613 "Romania (România)",
39629 "Saint Barthélemy",
39640 "Saint Kitts and Nevis",
39650 "Saint Martin (Saint-Martin (partie française))",
39656 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39661 "Saint Vincent and the Grenadines",
39676 "São Tomé and Príncipe (São Tomé e Príncipe)",
39681 "Saudi Arabia (المملكة العربية السعودية)",
39686 "Senegal (Sénégal)",
39716 "Slovakia (Slovensko)",
39721 "Slovenia (Slovenija)",
39731 "Somalia (Soomaaliya)",
39741 "South Korea (대한민국)",
39746 "South Sudan (جنوب السودان)",
39756 "Sri Lanka (ශ්රී ලංකාව)",
39761 "Sudan (السودان)",
39771 "Svalbard and Jan Mayen",
39782 "Sweden (Sverige)",
39787 "Switzerland (Schweiz)",
39792 "Syria (سوريا)",
39837 "Trinidad and Tobago",
39842 "Tunisia (تونس)",
39847 "Turkey (Türkiye)",
39857 "Turks and Caicos Islands",
39867 "U.S. Virgin Islands",
39877 "Ukraine (Україна)",
39882 "United Arab Emirates (الإمارات العربية المتحدة)",
39904 "Uzbekistan (Oʻzbekiston)",
39914 "Vatican City (Città del Vaticano)",
39925 "Vietnam (Việt Nam)",
39930 "Wallis and Futuna (Wallis-et-Futuna)",
39935 "Western Sahara (الصحراء الغربية)",
39941 "Yemen (اليمن)",
39965 * This script refer to:
39966 * Title: International Telephone Input
39967 * Author: Jack O'Connor
39968 * Code version: v12.1.12
39969 * Availability: https://github.com/jackocnr/intl-tel-input.git
39973 * @class Roo.bootstrap.PhoneInput
39974 * @extends Roo.bootstrap.TriggerField
39975 * An input with International dial-code selection
39977 * @cfg {String} defaultDialCode default '+852'
39978 * @cfg {Array} preferedCountries default []
39981 * Create a new PhoneInput.
39982 * @param {Object} config Configuration options
39985 Roo.bootstrap.PhoneInput = function(config) {
39986 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39989 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39991 listWidth: undefined,
39993 selectedClass: 'active',
39995 invalidClass : "has-warning",
39997 validClass: 'has-success',
39999 allowed: '0123456789',
40004 * @cfg {String} defaultDialCode The default dial code when initializing the input
40006 defaultDialCode: '+852',
40009 * @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
40011 preferedCountries: false,
40013 getAutoCreate : function()
40015 var data = Roo.bootstrap.PhoneInputData();
40016 var align = this.labelAlign || this.parentLabelAlign();
40019 this.allCountries = [];
40020 this.dialCodeMapping = [];
40022 for (var i = 0; i < data.length; i++) {
40024 this.allCountries[i] = {
40028 priority: c[3] || 0,
40029 areaCodes: c[4] || null
40031 this.dialCodeMapping[c[2]] = {
40034 priority: c[3] || 0,
40035 areaCodes: c[4] || null
40047 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40048 maxlength: this.max_length,
40049 cls : 'form-control tel-input',
40050 autocomplete: 'new-password'
40053 var hiddenInput = {
40056 cls: 'hidden-tel-input'
40060 hiddenInput.name = this.name;
40063 if (this.disabled) {
40064 input.disabled = true;
40067 var flag_container = {
40084 cls: this.hasFeedback ? 'has-feedback' : '',
40090 cls: 'dial-code-holder',
40097 cls: 'roo-select2-container input-group',
40104 if (this.fieldLabel.length) {
40107 tooltip: 'This field is required'
40113 cls: 'control-label',
40119 html: this.fieldLabel
40122 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40128 if(this.indicatorpos == 'right') {
40129 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40136 if(align == 'left') {
40144 if(this.labelWidth > 12){
40145 label.style = "width: " + this.labelWidth + 'px';
40147 if(this.labelWidth < 13 && this.labelmd == 0){
40148 this.labelmd = this.labelWidth;
40150 if(this.labellg > 0){
40151 label.cls += ' col-lg-' + this.labellg;
40152 input.cls += ' col-lg-' + (12 - this.labellg);
40154 if(this.labelmd > 0){
40155 label.cls += ' col-md-' + this.labelmd;
40156 container.cls += ' col-md-' + (12 - this.labelmd);
40158 if(this.labelsm > 0){
40159 label.cls += ' col-sm-' + this.labelsm;
40160 container.cls += ' col-sm-' + (12 - this.labelsm);
40162 if(this.labelxs > 0){
40163 label.cls += ' col-xs-' + this.labelxs;
40164 container.cls += ' col-xs-' + (12 - this.labelxs);
40174 var settings = this;
40176 ['xs','sm','md','lg'].map(function(size){
40177 if (settings[size]) {
40178 cfg.cls += ' col-' + size + '-' + settings[size];
40182 this.store = new Roo.data.Store({
40183 proxy : new Roo.data.MemoryProxy({}),
40184 reader : new Roo.data.JsonReader({
40195 'name' : 'dialCode',
40199 'name' : 'priority',
40203 'name' : 'areaCodes',
40210 if(!this.preferedCountries) {
40211 this.preferedCountries = [
40218 var p = this.preferedCountries.reverse();
40221 for (var i = 0; i < p.length; i++) {
40222 for (var j = 0; j < this.allCountries.length; j++) {
40223 if(this.allCountries[j].iso2 == p[i]) {
40224 var t = this.allCountries[j];
40225 this.allCountries.splice(j,1);
40226 this.allCountries.unshift(t);
40232 this.store.proxy.data = {
40234 data: this.allCountries
40240 initEvents : function()
40243 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40245 this.indicator = this.indicatorEl();
40246 this.flag = this.flagEl();
40247 this.dialCodeHolder = this.dialCodeHolderEl();
40249 this.trigger = this.el.select('div.flag-box',true).first();
40250 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40255 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40256 _this.list.setWidth(lw);
40259 this.list.on('mouseover', this.onViewOver, this);
40260 this.list.on('mousemove', this.onViewMove, this);
40261 this.inputEl().on("keyup", this.onKeyUp, this);
40262 this.inputEl().on("keypress", this.onKeyPress, this);
40264 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40266 this.view = new Roo.View(this.list, this.tpl, {
40267 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40270 this.view.on('click', this.onViewClick, this);
40271 this.setValue(this.defaultDialCode);
40274 onTriggerClick : function(e)
40276 Roo.log('trigger click');
40281 if(this.isExpanded()){
40283 this.hasFocus = false;
40285 this.store.load({});
40286 this.hasFocus = true;
40291 isExpanded : function()
40293 return this.list.isVisible();
40296 collapse : function()
40298 if(!this.isExpanded()){
40302 Roo.get(document).un('mousedown', this.collapseIf, this);
40303 Roo.get(document).un('mousewheel', this.collapseIf, this);
40304 this.fireEvent('collapse', this);
40308 expand : function()
40312 if(this.isExpanded() || !this.hasFocus){
40316 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40317 this.list.setWidth(lw);
40320 this.restrictHeight();
40322 Roo.get(document).on('mousedown', this.collapseIf, this);
40323 Roo.get(document).on('mousewheel', this.collapseIf, this);
40325 this.fireEvent('expand', this);
40328 restrictHeight : function()
40330 this.list.alignTo(this.inputEl(), this.listAlign);
40331 this.list.alignTo(this.inputEl(), this.listAlign);
40334 onViewOver : function(e, t)
40336 if(this.inKeyMode){
40339 var item = this.view.findItemFromChild(t);
40342 var index = this.view.indexOf(item);
40343 this.select(index, false);
40348 onViewClick : function(view, doFocus, el, e)
40350 var index = this.view.getSelectedIndexes()[0];
40352 var r = this.store.getAt(index);
40355 this.onSelect(r, index);
40357 if(doFocus !== false && !this.blockFocus){
40358 this.inputEl().focus();
40362 onViewMove : function(e, t)
40364 this.inKeyMode = false;
40367 select : function(index, scrollIntoView)
40369 this.selectedIndex = index;
40370 this.view.select(index);
40371 if(scrollIntoView !== false){
40372 var el = this.view.getNode(index);
40374 this.list.scrollChildIntoView(el, false);
40379 createList : function()
40381 this.list = Roo.get(document.body).createChild({
40383 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40384 style: 'display:none'
40387 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40390 collapseIf : function(e)
40392 var in_combo = e.within(this.el);
40393 var in_list = e.within(this.list);
40394 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40396 if (in_combo || in_list || is_list) {
40402 onSelect : function(record, index)
40404 if(this.fireEvent('beforeselect', this, record, index) !== false){
40406 this.setFlagClass(record.data.iso2);
40407 this.setDialCode(record.data.dialCode);
40408 this.hasFocus = false;
40410 this.fireEvent('select', this, record, index);
40414 flagEl : function()
40416 var flag = this.el.select('div.flag',true).first();
40423 dialCodeHolderEl : function()
40425 var d = this.el.select('input.dial-code-holder',true).first();
40432 setDialCode : function(v)
40434 this.dialCodeHolder.dom.value = '+'+v;
40437 setFlagClass : function(n)
40439 this.flag.dom.className = 'flag '+n;
40442 getValue : function()
40444 var v = this.inputEl().getValue();
40445 if(this.dialCodeHolder) {
40446 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40451 setValue : function(v)
40453 var d = this.getDialCode(v);
40455 //invalid dial code
40456 if(v.length == 0 || !d || d.length == 0) {
40458 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40459 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40465 this.setFlagClass(this.dialCodeMapping[d].iso2);
40466 this.setDialCode(d);
40467 this.inputEl().dom.value = v.replace('+'+d,'');
40468 this.hiddenEl().dom.value = this.getValue();
40473 getDialCode : function(v)
40477 if (v.length == 0) {
40478 return this.dialCodeHolder.dom.value;
40482 if (v.charAt(0) != "+") {
40485 var numericChars = "";
40486 for (var i = 1; i < v.length; i++) {
40487 var c = v.charAt(i);
40490 if (this.dialCodeMapping[numericChars]) {
40491 dialCode = v.substr(1, i);
40493 if (numericChars.length == 4) {
40503 this.setValue(this.defaultDialCode);
40507 hiddenEl : function()
40509 return this.el.select('input.hidden-tel-input',true).first();
40512 // after setting val
40513 onKeyUp : function(e){
40514 this.setValue(this.getValue());
40517 onKeyPress : function(e){
40518 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40525 * @class Roo.bootstrap.MoneyField
40526 * @extends Roo.bootstrap.ComboBox
40527 * Bootstrap MoneyField class
40530 * Create a new MoneyField.
40531 * @param {Object} config Configuration options
40534 Roo.bootstrap.MoneyField = function(config) {
40536 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40540 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40543 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40545 allowDecimals : true,
40547 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40549 decimalSeparator : ".",
40551 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40553 decimalPrecision : 0,
40555 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40557 allowNegative : true,
40559 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40563 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40565 minValue : Number.NEGATIVE_INFINITY,
40567 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40569 maxValue : Number.MAX_VALUE,
40571 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40573 minText : "The minimum value for this field is {0}",
40575 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40577 maxText : "The maximum value for this field is {0}",
40579 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40580 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40582 nanText : "{0} is not a valid number",
40584 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40588 * @cfg {String} defaults currency of the MoneyField
40589 * value should be in lkey
40591 defaultCurrency : false,
40593 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40595 thousandsDelimiter : false,
40597 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40608 getAutoCreate : function()
40610 var align = this.labelAlign || this.parentLabelAlign();
40622 cls : 'form-control roo-money-amount-input',
40623 autocomplete: 'new-password'
40626 var hiddenInput = {
40630 cls: 'hidden-number-input'
40633 if(this.max_length) {
40634 input.maxlength = this.max_length;
40638 hiddenInput.name = this.name;
40641 if (this.disabled) {
40642 input.disabled = true;
40645 var clg = 12 - this.inputlg;
40646 var cmd = 12 - this.inputmd;
40647 var csm = 12 - this.inputsm;
40648 var cxs = 12 - this.inputxs;
40652 cls : 'row roo-money-field',
40656 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40660 cls: 'roo-select2-container input-group',
40664 cls : 'form-control roo-money-currency-input',
40665 autocomplete: 'new-password',
40667 name : this.currencyName
40671 cls : 'input-group-addon',
40685 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40689 cls: this.hasFeedback ? 'has-feedback' : '',
40700 if (this.fieldLabel.length) {
40703 tooltip: 'This field is required'
40709 cls: 'control-label',
40715 html: this.fieldLabel
40718 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40724 if(this.indicatorpos == 'right') {
40725 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40732 if(align == 'left') {
40740 if(this.labelWidth > 12){
40741 label.style = "width: " + this.labelWidth + 'px';
40743 if(this.labelWidth < 13 && this.labelmd == 0){
40744 this.labelmd = this.labelWidth;
40746 if(this.labellg > 0){
40747 label.cls += ' col-lg-' + this.labellg;
40748 input.cls += ' col-lg-' + (12 - this.labellg);
40750 if(this.labelmd > 0){
40751 label.cls += ' col-md-' + this.labelmd;
40752 container.cls += ' col-md-' + (12 - this.labelmd);
40754 if(this.labelsm > 0){
40755 label.cls += ' col-sm-' + this.labelsm;
40756 container.cls += ' col-sm-' + (12 - this.labelsm);
40758 if(this.labelxs > 0){
40759 label.cls += ' col-xs-' + this.labelxs;
40760 container.cls += ' col-xs-' + (12 - this.labelxs);
40771 var settings = this;
40773 ['xs','sm','md','lg'].map(function(size){
40774 if (settings[size]) {
40775 cfg.cls += ' col-' + size + '-' + settings[size];
40782 initEvents : function()
40784 this.indicator = this.indicatorEl();
40786 this.initCurrencyEvent();
40788 this.initNumberEvent();
40791 initCurrencyEvent : function()
40794 throw "can not find store for combo";
40797 this.store = Roo.factory(this.store, Roo.data);
40798 this.store.parent = this;
40802 this.triggerEl = this.el.select('.input-group-addon', true).first();
40804 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40809 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40810 _this.list.setWidth(lw);
40813 this.list.on('mouseover', this.onViewOver, this);
40814 this.list.on('mousemove', this.onViewMove, this);
40815 this.list.on('scroll', this.onViewScroll, this);
40818 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40821 this.view = new Roo.View(this.list, this.tpl, {
40822 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40825 this.view.on('click', this.onViewClick, this);
40827 this.store.on('beforeload', this.onBeforeLoad, this);
40828 this.store.on('load', this.onLoad, this);
40829 this.store.on('loadexception', this.onLoadException, this);
40831 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40832 "up" : function(e){
40833 this.inKeyMode = true;
40837 "down" : function(e){
40838 if(!this.isExpanded()){
40839 this.onTriggerClick();
40841 this.inKeyMode = true;
40846 "enter" : function(e){
40849 if(this.fireEvent("specialkey", this, e)){
40850 this.onViewClick(false);
40856 "esc" : function(e){
40860 "tab" : function(e){
40863 if(this.fireEvent("specialkey", this, e)){
40864 this.onViewClick(false);
40872 doRelay : function(foo, bar, hname){
40873 if(hname == 'down' || this.scope.isExpanded()){
40874 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40882 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40886 initNumberEvent : function(e)
40888 this.inputEl().on("keydown" , this.fireKey, this);
40889 this.inputEl().on("focus", this.onFocus, this);
40890 this.inputEl().on("blur", this.onBlur, this);
40892 this.inputEl().relayEvent('keyup', this);
40894 if(this.indicator){
40895 this.indicator.addClass('invisible');
40898 this.originalValue = this.getValue();
40900 if(this.validationEvent == 'keyup'){
40901 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40902 this.inputEl().on('keyup', this.filterValidation, this);
40904 else if(this.validationEvent !== false){
40905 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40908 if(this.selectOnFocus){
40909 this.on("focus", this.preFocus, this);
40912 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40913 this.inputEl().on("keypress", this.filterKeys, this);
40915 this.inputEl().relayEvent('keypress', this);
40918 var allowed = "0123456789";
40920 if(this.allowDecimals){
40921 allowed += this.decimalSeparator;
40924 if(this.allowNegative){
40928 if(this.thousandsDelimiter) {
40932 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40934 var keyPress = function(e){
40936 var k = e.getKey();
40938 var c = e.getCharCode();
40941 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40942 allowed.indexOf(String.fromCharCode(c)) === -1
40948 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40952 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40957 this.inputEl().on("keypress", keyPress, this);
40961 onTriggerClick : function(e)
40968 this.loadNext = false;
40970 if(this.isExpanded()){
40975 this.hasFocus = true;
40977 if(this.triggerAction == 'all') {
40978 this.doQuery(this.allQuery, true);
40982 this.doQuery(this.getRawValue());
40985 getCurrency : function()
40987 var v = this.currencyEl().getValue();
40992 restrictHeight : function()
40994 this.list.alignTo(this.currencyEl(), this.listAlign);
40995 this.list.alignTo(this.currencyEl(), this.listAlign);
40998 onViewClick : function(view, doFocus, el, e)
41000 var index = this.view.getSelectedIndexes()[0];
41002 var r = this.store.getAt(index);
41005 this.onSelect(r, index);
41009 onSelect : function(record, index){
41011 if(this.fireEvent('beforeselect', this, record, index) !== false){
41013 this.setFromCurrencyData(index > -1 ? record.data : false);
41017 this.fireEvent('select', this, record, index);
41021 setFromCurrencyData : function(o)
41025 this.lastCurrency = o;
41027 if (this.currencyField) {
41028 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41030 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
41033 this.lastSelectionText = currency;
41035 //setting default currency
41036 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41037 this.setCurrency(this.defaultCurrency);
41041 this.setCurrency(currency);
41044 setFromData : function(o)
41048 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41050 this.setFromCurrencyData(c);
41055 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41057 Roo.log('no value set for '+ (this.name ? this.name : this.id));
41060 this.setValue(value);
41064 setCurrency : function(v)
41066 this.currencyValue = v;
41069 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41074 setValue : function(v)
41076 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41082 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41084 this.inputEl().dom.value = (v == '') ? '' :
41085 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41087 if(!this.allowZero && v === '0') {
41088 this.hiddenEl().dom.value = '';
41089 this.inputEl().dom.value = '';
41096 getRawValue : function()
41098 var v = this.inputEl().getValue();
41103 getValue : function()
41105 return this.fixPrecision(this.parseValue(this.getRawValue()));
41108 parseValue : function(value)
41110 if(this.thousandsDelimiter) {
41112 r = new RegExp(",", "g");
41113 value = value.replace(r, "");
41116 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41117 return isNaN(value) ? '' : value;
41121 fixPrecision : function(value)
41123 if(this.thousandsDelimiter) {
41125 r = new RegExp(",", "g");
41126 value = value.replace(r, "");
41129 var nan = isNaN(value);
41131 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41132 return nan ? '' : value;
41134 return parseFloat(value).toFixed(this.decimalPrecision);
41137 decimalPrecisionFcn : function(v)
41139 return Math.floor(v);
41142 validateValue : function(value)
41144 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41148 var num = this.parseValue(value);
41151 this.markInvalid(String.format(this.nanText, value));
41155 if(num < this.minValue){
41156 this.markInvalid(String.format(this.minText, this.minValue));
41160 if(num > this.maxValue){
41161 this.markInvalid(String.format(this.maxText, this.maxValue));
41168 validate : function()
41170 if(this.disabled || this.allowBlank){
41175 var currency = this.getCurrency();
41177 if(this.validateValue(this.getRawValue()) && currency.length){
41182 this.markInvalid();
41186 getName: function()
41191 beforeBlur : function()
41197 var v = this.parseValue(this.getRawValue());
41204 onBlur : function()
41208 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41209 //this.el.removeClass(this.focusClass);
41212 this.hasFocus = false;
41214 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41218 var v = this.getValue();
41220 if(String(v) !== String(this.startValue)){
41221 this.fireEvent('change', this, v, this.startValue);
41224 this.fireEvent("blur", this);
41227 inputEl : function()
41229 return this.el.select('.roo-money-amount-input', true).first();
41232 currencyEl : function()
41234 return this.el.select('.roo-money-currency-input', true).first();
41237 hiddenEl : function()
41239 return this.el.select('input.hidden-number-input',true).first();