2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = (
9 Roo.each(document.styleSheets[0], function(s) {
10 if (s.href.match(/css-bootstrap4/)) {
18 * base class for bootstrap elements.
22 Roo.bootstrap = Roo.bootstrap || {};
24 * @class Roo.bootstrap.Component
25 * @extends Roo.Component
26 * Bootstrap Component base class
27 * @cfg {String} cls css class
28 * @cfg {String} style any extra css
29 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
31 * @cfg {string} dataId cutomer id
32 * @cfg {string} name Specifies name attribute
33 * @cfg {string} tooltip Text for the tooltip
34 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
35 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
38 * Do not use directly - it does not do anything..
39 * @param {Object} config The config object
44 Roo.bootstrap.Component = function(config){
45 Roo.bootstrap.Component.superclass.constructor.call(this, config);
49 * @event childrenrendered
50 * Fires when the children have been rendered..
51 * @param {Roo.bootstrap.Component} this
53 "childrenrendered" : true
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
65 allowDomMove : false, // to stop relocations in parent onRender...
75 * Initialize Events for the element
77 initEvents : function() { },
83 can_build_overlaid : true,
85 container_method : false,
92 // returns the parent component..
93 return Roo.ComponentMgr.get(this.parentId)
99 onRender : function(ct, position)
101 // Roo.log("Call onRender: " + this.xtype);
103 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
106 if (this.el.attr('xtype')) {
107 this.el.attr('xtypex', this.el.attr('xtype'));
108 this.el.dom.removeAttribute('xtype');
118 var cfg = Roo.apply({}, this.getAutoCreate());
120 cfg.id = this.id || Roo.id();
122 // fill in the extra attributes
123 if (this.xattr && typeof(this.xattr) =='object') {
124 for (var i in this.xattr) {
125 cfg[i] = this.xattr[i];
130 cfg.dataId = this.dataId;
134 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
137 if (this.style) { // fixme needs to support more complex style data.
138 cfg.style = this.style;
142 cfg.name = this.name;
145 this.el = ct.createChild(cfg, position);
148 this.tooltipEl().attr('tooltip', this.tooltip);
151 if(this.tabIndex !== undefined){
152 this.el.dom.setAttribute('tabIndex', this.tabIndex);
159 * Fetch the element to add children to
160 * @return {Roo.Element} defaults to this.el
162 getChildContainer : function()
167 * Fetch the element to display the tooltip on.
168 * @return {Roo.Element} defaults to this.el
170 tooltipEl : function()
175 addxtype : function(tree,cntr)
179 cn = Roo.factory(tree);
180 //Roo.log(['addxtype', cn]);
182 cn.parentType = this.xtype; //??
183 cn.parentId = this.id;
185 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186 if (typeof(cn.container_method) == 'string') {
187 cntr = cn.container_method;
191 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
193 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
195 var build_from_html = Roo.XComponent.build_from_html;
197 var is_body = (tree.xtype == 'Body') ;
199 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
201 var self_cntr_el = Roo.get(this[cntr](false));
203 // do not try and build conditional elements
204 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
208 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210 return this.addxtypeChild(tree,cntr, is_body);
213 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
216 return this.addxtypeChild(Roo.apply({}, tree),cntr);
219 Roo.log('skipping render');
225 if (!build_from_html) {
229 // this i think handles overlaying multiple children of the same type
230 // with the sam eelement.. - which might be buggy..
232 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
238 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
242 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
249 addxtypeChild : function (tree, cntr, is_body)
251 Roo.debug && Roo.log('addxtypeChild:' + cntr);
253 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
256 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257 (typeof(tree['flexy:foreach']) != 'undefined');
261 skip_children = false;
262 // render the element if it's not BODY.
265 // if parent was disabled, then do not try and create the children..
266 if(!this[cntr](true)){
271 cn = Roo.factory(tree);
273 cn.parentType = this.xtype; //??
274 cn.parentId = this.id;
276 var build_from_html = Roo.XComponent.build_from_html;
279 // does the container contain child eleemnts with 'xtype' attributes.
280 // that match this xtype..
281 // note - when we render we create these as well..
282 // so we should check to see if body has xtype set.
283 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
285 var self_cntr_el = Roo.get(this[cntr](false));
286 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
288 //Roo.log(Roo.XComponent.build_from_html);
289 //Roo.log("got echild:");
292 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293 // and are not displayed -this causes this to use up the wrong element when matching.
294 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
297 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
304 //echild.dom.removeAttribute('xtype');
306 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307 Roo.debug && Roo.log(self_cntr_el);
308 Roo.debug && Roo.log(echild);
309 Roo.debug && Roo.log(cn);
315 // if object has flexy:if - then it may or may not be rendered.
316 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
317 // skip a flexy if element.
318 Roo.debug && Roo.log('skipping render');
319 Roo.debug && Roo.log(tree);
321 Roo.debug && Roo.log('skipping all children');
322 skip_children = true;
327 // actually if flexy:foreach is found, we really want to create
328 // multiple copies here...
330 //Roo.log(this[cntr]());
331 // some elements do not have render methods.. like the layouts...
333 if(this[cntr](true) === false){
338 cn.render && cn.render(this[cntr](true));
341 // then add the element..
348 if (typeof (tree.menu) != 'undefined') {
349 tree.menu.parentType = cn.xtype;
350 tree.menu.triggerEl = cn.el;
351 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
355 if (!tree.items || !tree.items.length) {
357 //Roo.log(["no children", this]);
362 var items = tree.items;
365 //Roo.log(items.length);
367 if (!skip_children) {
368 for(var i =0;i < items.length;i++) {
369 // Roo.log(['add child', items[i]]);
370 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
376 //Roo.log("fire childrenrendered");
378 cn.fireEvent('childrenrendered', this);
384 * Set the element that will be used to show or hide
386 setVisibilityEl : function(el)
388 this.visibilityEl = el;
392 * Get the element that will be used to show or hide
394 getVisibilityEl : function()
396 if (typeof(this.visibilityEl) == 'object') {
397 return this.visibilityEl;
400 if (typeof(this.visibilityEl) == 'string') {
401 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
408 * Show a component - removes 'hidden' class
412 if(!this.getVisibilityEl()){
416 this.getVisibilityEl().removeClass('hidden');
418 this.fireEvent('show', this);
423 * Hide a component - adds 'hidden' class
427 if(!this.getVisibilityEl()){
431 this.getVisibilityEl().addClass('hidden');
433 this.fireEvent('hide', this);
446 * @class Roo.bootstrap.Body
447 * @extends Roo.bootstrap.Component
448 * Bootstrap Body class
452 * @param {Object} config The config object
455 Roo.bootstrap.Body = function(config){
457 config = config || {};
459 Roo.bootstrap.Body.superclass.constructor.call(this, config);
460 this.el = Roo.get(config.el ? config.el : document.body );
461 if (this.cls && this.cls.length) {
462 Roo.get(document.body).addClass(this.cls);
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
468 is_body : true,// just to make sure it's constructed?
473 onRender : function(ct, position)
475 /* Roo.log("Roo.bootstrap.Body - onRender");
476 if (this.cls && this.cls.length) {
477 Roo.get(document.body).addClass(this.cls);
496 * @class Roo.bootstrap.ButtonGroup
497 * @extends Roo.bootstrap.Component
498 * Bootstrap ButtonGroup class
499 * @cfg {String} size lg | sm | xs (default empty normal)
500 * @cfg {String} align vertical | justified (default none)
501 * @cfg {String} direction up | down (default down)
502 * @cfg {Boolean} toolbar false | true
503 * @cfg {Boolean} btn true | false
508 * @param {Object} config The config object
511 Roo.bootstrap.ButtonGroup = function(config){
512 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
523 getAutoCreate : function(){
529 cfg.html = this.html || cfg.html;
540 if (['vertical','justified'].indexOf(this.align)!==-1) {
541 cfg.cls = 'btn-group-' + this.align;
543 if (this.align == 'justified') {
544 console.log(this.items);
548 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549 cfg.cls += ' btn-group-' + this.size;
552 if (this.direction == 'up') {
553 cfg.cls += ' dropup' ;
559 * Add a button to the group (similar to NavItem API.)
561 addItem : function(cfg)
563 var cn = new Roo.bootstrap.Button(cfg);
565 cn.parentId = this.id;
566 cn.onRender(this.el, null);
580 * @class Roo.bootstrap.Button
581 * @extends Roo.bootstrap.Component
582 * Bootstrap Button class
583 * @cfg {String} html The button content
584 * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585 * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587 * @cfg {String} size ( lg | sm | xs)
588 * @cfg {String} tag ( a | input | submit)
589 * @cfg {String} href empty or href
590 * @cfg {Boolean} disabled default false;
591 * @cfg {Boolean} isClose default false;
592 * @cfg {String} glyphicon depricated - use fs
593 * @cfg {String} badge text for badge
594 * @cfg {String} theme (default|glow)
595 * @cfg {Boolean} inverse dark themed version
596 * @cfg {Boolean} toggle is it a slidy toggle button
597 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
598 * @cfg {String} ontext text for on slidy toggle state
599 * @cfg {String} offtext text for off slidy toggle state
600 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
601 * @cfg {Boolean} removeClass remove the standard class..
602 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
605 * Create a new button
606 * @param {Object} config The config object
610 Roo.bootstrap.Button = function(config){
611 Roo.bootstrap.Button.superclass.constructor.call(this, config);
612 this.weightClass = ["btn-default btn-outline-secondary",
624 * When a butotn is pressed
625 * @param {Roo.bootstrap.Button} btn
626 * @param {Roo.EventObject} e
631 * After the button has been toggles
632 * @param {Roo.bootstrap.Button} btn
633 * @param {Roo.EventObject} e
634 * @param {boolean} pressed (also available as button.pressed)
640 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
661 preventDefault: true,
669 getAutoCreate : function(){
677 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
678 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
683 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
685 if (this.toggle == true) {
688 cls: 'slider-frame roo-button',
693 'data-off-text':'OFF',
694 cls: 'slider-button',
700 if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
701 cfg.cls += ' '+this.weight;
710 cfg["aria-hidden"] = true;
712 cfg.html = "×";
718 if (this.theme==='default') {
719 cfg.cls = 'btn roo-button';
721 //if (this.parentType != 'Navbar') {
722 this.weight = this.weight.length ? this.weight : 'default';
724 if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
726 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
727 var weight = this.weight == 'default' ? 'secondary' : this.weight;
728 cfg.cls += ' btn-' + outline + weight;
729 if (this.weight == 'default') {
731 cfg.cls += ' btn-' + this.weight;
734 } else if (this.theme==='glow') {
737 cfg.cls = 'btn-glow roo-button';
739 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
741 cfg.cls += ' ' + this.weight;
747 this.cls += ' inverse';
751 if (this.active || this.pressed === true) {
752 cfg.cls += ' active';
756 cfg.disabled = 'disabled';
760 Roo.log('changing to ul' );
762 this.glyphicon = 'caret';
763 if (Roo.bootstrap.version == 4) {
764 this.fa = 'caret-down';
769 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
771 //gsRoo.log(this.parentType);
772 if (this.parentType === 'Navbar' && !this.parent().bar) {
773 Roo.log('changing to li?');
782 href : this.href || '#'
785 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
786 cfg.cls += ' dropdown';
793 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
795 if (this.glyphicon) {
796 cfg.html = ' ' + cfg.html;
801 cls: 'glyphicon glyphicon-' + this.glyphicon
806 cfg.html = ' ' + cfg.html;
811 cls: 'fa fas fa-' + this.fa
821 // cfg.cls='btn roo-button';
825 var value = cfg.html;
830 cls: 'glyphicon glyphicon-' + this.glyphicon,
837 cls: 'fa fas fa-' + this.fa,
842 var bw = this.badge_weight.length ? this.badge_weight :
843 (this.weight.length ? this.weight : 'secondary');
844 bw = bw == 'default' ? 'secondary' : bw;
850 cls: 'badge badge-' + bw,
859 cfg.cls += ' dropdown';
860 cfg.html = typeof(cfg.html) != 'undefined' ?
861 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
864 if (cfg.tag !== 'a' && this.href !== '') {
865 throw "Tag must be a to set href.";
866 } else if (this.href.length > 0) {
867 cfg.href = this.href;
870 if(this.removeClass){
875 cfg.target = this.target;
880 initEvents: function() {
881 // Roo.log('init events?');
882 // Roo.log(this.el.dom);
885 if (typeof (this.menu) != 'undefined') {
886 this.menu.parentType = this.xtype;
887 this.menu.triggerEl = this.el;
888 this.addxtype(Roo.apply({}, this.menu));
892 if (this.el.hasClass('roo-button')) {
893 this.el.on('click', this.onClick, this);
895 this.el.select('.roo-button').on('click', this.onClick, this);
898 if(this.removeClass){
899 this.el.on('click', this.onClick, this);
902 this.el.enableDisplayMode();
905 onClick : function(e)
911 Roo.log('button on click ');
912 if(this.preventDefault){
916 if (this.pressed === true || this.pressed === false) {
917 this.toggleActive(e);
921 this.fireEvent('click', this, e);
925 * Enables this button
929 this.disabled = false;
930 this.el.removeClass('disabled');
934 * Disable this button
938 this.disabled = true;
939 this.el.addClass('disabled');
942 * sets the active state on/off,
943 * @param {Boolean} state (optional) Force a particular state
945 setActive : function(v) {
947 this.el[v ? 'addClass' : 'removeClass']('active');
951 * toggles the current active state
953 toggleActive : function(e)
955 this.setActive(!this.pressed);
956 this.fireEvent('toggle', this, e, !this.pressed);
959 * get the current active state
960 * @return {boolean} true if it's active
962 isActive : function()
964 return this.el.hasClass('active');
967 * set the text of the first selected button
969 setText : function(str)
971 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
974 * get the text of the first selected button
978 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
981 setWeight : function(str)
983 this.el.removeClass(this.weightClass);
985 var outline = this.outline ? 'outline-' : '';
986 if (str == 'default') {
987 this.el.addClass('btn-default btn-outline-secondary');
990 this.el.addClass('btn-' + outline + str);
1004 * @class Roo.bootstrap.Column
1005 * @extends Roo.bootstrap.Component
1006 * Bootstrap Column class
1007 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1008 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1009 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1010 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1011 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1012 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1013 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1014 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1017 * @cfg {Boolean} hidden (true|false) hide the element
1018 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1019 * @cfg {String} fa (ban|check|...) font awesome icon
1020 * @cfg {Number} fasize (1|2|....) font awsome size
1022 * @cfg {String} icon (info-sign|check|...) glyphicon name
1024 * @cfg {String} html content of column.
1027 * Create a new Column
1028 * @param {Object} config The config object
1031 Roo.bootstrap.Column = function(config){
1032 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1035 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1053 getAutoCreate : function(){
1054 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1062 ['xs','sm','md','lg'].map(function(size){
1063 //Roo.log( size + ':' + settings[size]);
1065 if (settings[size+'off'] !== false) {
1066 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1069 if (settings[size] === false) {
1073 if (!settings[size]) { // 0 = hidden
1074 cfg.cls += ' hidden-' + size;
1077 cfg.cls += ' col-' + size + '-' + settings[size];
1082 cfg.cls += ' hidden';
1085 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1086 cfg.cls +=' alert alert-' + this.alert;
1090 if (this.html.length) {
1091 cfg.html = this.html;
1095 if (this.fasize > 1) {
1096 fasize = ' fa-' + this.fasize + 'x';
1098 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1103 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1122 * @class Roo.bootstrap.Container
1123 * @extends Roo.bootstrap.Component
1124 * Bootstrap Container class
1125 * @cfg {Boolean} jumbotron is it a jumbotron element
1126 * @cfg {String} html content of element
1127 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1128 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1129 * @cfg {String} header content of header (for panel)
1130 * @cfg {String} footer content of footer (for panel)
1131 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1132 * @cfg {String} tag (header|aside|section) type of HTML tag.
1133 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1134 * @cfg {String} fa font awesome icon
1135 * @cfg {String} icon (info-sign|check|...) glyphicon name
1136 * @cfg {Boolean} hidden (true|false) hide the element
1137 * @cfg {Boolean} expandable (true|false) default false
1138 * @cfg {Boolean} expanded (true|false) default true
1139 * @cfg {String} rheader contet on the right of header
1140 * @cfg {Boolean} clickable (true|false) default false
1144 * Create a new Container
1145 * @param {Object} config The config object
1148 Roo.bootstrap.Container = function(config){
1149 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1155 * After the panel has been expand
1157 * @param {Roo.bootstrap.Container} this
1162 * After the panel has been collapsed
1164 * @param {Roo.bootstrap.Container} this
1169 * When a element is chick
1170 * @param {Roo.bootstrap.Container} this
1171 * @param {Roo.EventObject} e
1177 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1195 getChildContainer : function() {
1201 if (this.panel.length) {
1202 return this.el.select('.panel-body',true).first();
1209 getAutoCreate : function(){
1212 tag : this.tag || 'div',
1216 if (this.jumbotron) {
1217 cfg.cls = 'jumbotron';
1222 // - this is applied by the parent..
1224 // cfg.cls = this.cls + '';
1227 if (this.sticky.length) {
1229 var bd = Roo.get(document.body);
1230 if (!bd.hasClass('bootstrap-sticky')) {
1231 bd.addClass('bootstrap-sticky');
1232 Roo.select('html',true).setStyle('height', '100%');
1235 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1239 if (this.well.length) {
1240 switch (this.well) {
1243 cfg.cls +=' well well-' +this.well;
1252 cfg.cls += ' hidden';
1256 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1257 cfg.cls +=' alert alert-' + this.alert;
1262 if (this.panel.length) {
1263 cfg.cls += ' panel panel-' + this.panel;
1265 if (this.header.length) {
1269 if(this.expandable){
1271 cfg.cls = cfg.cls + ' expandable';
1275 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1283 cls : 'panel-title',
1284 html : (this.expandable ? ' ' : '') + this.header
1288 cls: 'panel-header-right',
1294 cls : 'panel-heading',
1295 style : this.expandable ? 'cursor: pointer' : '',
1303 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1308 if (this.footer.length) {
1310 cls : 'panel-footer',
1319 body.html = this.html || cfg.html;
1320 // prefix with the icons..
1322 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1325 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1330 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1331 cfg.cls = 'container';
1337 initEvents: function()
1339 if(this.expandable){
1340 var headerEl = this.headerEl();
1343 headerEl.on('click', this.onToggleClick, this);
1348 this.el.on('click', this.onClick, this);
1353 onToggleClick : function()
1355 var headerEl = this.headerEl();
1371 if(this.fireEvent('expand', this)) {
1373 this.expanded = true;
1375 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1377 this.el.select('.panel-body',true).first().removeClass('hide');
1379 var toggleEl = this.toggleEl();
1385 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1390 collapse : function()
1392 if(this.fireEvent('collapse', this)) {
1394 this.expanded = false;
1396 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1397 this.el.select('.panel-body',true).first().addClass('hide');
1399 var toggleEl = this.toggleEl();
1405 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1409 toggleEl : function()
1411 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1415 return this.el.select('.panel-heading .fa',true).first();
1418 headerEl : function()
1420 if(!this.el || !this.panel.length || !this.header.length){
1424 return this.el.select('.panel-heading',true).first()
1429 if(!this.el || !this.panel.length){
1433 return this.el.select('.panel-body',true).first()
1436 titleEl : function()
1438 if(!this.el || !this.panel.length || !this.header.length){
1442 return this.el.select('.panel-title',true).first();
1445 setTitle : function(v)
1447 var titleEl = this.titleEl();
1453 titleEl.dom.innerHTML = v;
1456 getTitle : function()
1459 var titleEl = this.titleEl();
1465 return titleEl.dom.innerHTML;
1468 setRightTitle : function(v)
1470 var t = this.el.select('.panel-header-right',true).first();
1476 t.dom.innerHTML = v;
1479 onClick : function(e)
1483 this.fireEvent('click', this, e);
1496 * @class Roo.bootstrap.Img
1497 * @extends Roo.bootstrap.Component
1498 * Bootstrap Img class
1499 * @cfg {Boolean} imgResponsive false | true
1500 * @cfg {String} border rounded | circle | thumbnail
1501 * @cfg {String} src image source
1502 * @cfg {String} alt image alternative text
1503 * @cfg {String} href a tag href
1504 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1505 * @cfg {String} xsUrl xs image source
1506 * @cfg {String} smUrl sm image source
1507 * @cfg {String} mdUrl md image source
1508 * @cfg {String} lgUrl lg image source
1511 * Create a new Input
1512 * @param {Object} config The config object
1515 Roo.bootstrap.Img = function(config){
1516 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1522 * The img click event for the img.
1523 * @param {Roo.EventObject} e
1529 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1531 imgResponsive: true,
1541 getAutoCreate : function()
1543 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1544 return this.createSingleImg();
1549 cls: 'roo-image-responsive-group',
1554 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1556 if(!_this[size + 'Url']){
1562 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1563 html: _this.html || cfg.html,
1564 src: _this[size + 'Url']
1567 img.cls += ' roo-image-responsive-' + size;
1569 var s = ['xs', 'sm', 'md', 'lg'];
1571 s.splice(s.indexOf(size), 1);
1573 Roo.each(s, function(ss){
1574 img.cls += ' hidden-' + ss;
1577 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1578 cfg.cls += ' img-' + _this.border;
1582 cfg.alt = _this.alt;
1595 a.target = _this.target;
1599 cfg.cn.push((_this.href) ? a : img);
1606 createSingleImg : function()
1610 cls: (this.imgResponsive) ? 'img-responsive' : '',
1612 src : 'about:blank' // just incase src get's set to undefined?!?
1615 cfg.html = this.html || cfg.html;
1617 cfg.src = this.src || cfg.src;
1619 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1620 cfg.cls += ' img-' + this.border;
1637 a.target = this.target;
1642 return (this.href) ? a : cfg;
1645 initEvents: function()
1648 this.el.on('click', this.onClick, this);
1653 onClick : function(e)
1655 Roo.log('img onclick');
1656 this.fireEvent('click', this, e);
1659 * Sets the url of the image - used to update it
1660 * @param {String} url the url of the image
1663 setSrc : function(url)
1667 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1668 this.el.dom.src = url;
1672 this.el.select('img', true).first().dom.src = url;
1688 * @class Roo.bootstrap.Link
1689 * @extends Roo.bootstrap.Component
1690 * Bootstrap Link Class
1691 * @cfg {String} alt image alternative text
1692 * @cfg {String} href a tag href
1693 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1694 * @cfg {String} html the content of the link.
1695 * @cfg {String} anchor name for the anchor link
1696 * @cfg {String} fa - favicon
1698 * @cfg {Boolean} preventDefault (true | false) default false
1702 * Create a new Input
1703 * @param {Object} config The config object
1706 Roo.bootstrap.Link = function(config){
1707 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1713 * The img click event for the img.
1714 * @param {Roo.EventObject} e
1720 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1724 preventDefault: false,
1730 getAutoCreate : function()
1732 var html = this.html || '';
1734 if (this.fa !== false) {
1735 html = '<i class="fa fa-' + this.fa + '"></i>';
1740 // anchor's do not require html/href...
1741 if (this.anchor === false) {
1743 cfg.href = this.href || '#';
1745 cfg.name = this.anchor;
1746 if (this.html !== false || this.fa !== false) {
1749 if (this.href !== false) {
1750 cfg.href = this.href;
1754 if(this.alt !== false){
1759 if(this.target !== false) {
1760 cfg.target = this.target;
1766 initEvents: function() {
1768 if(!this.href || this.preventDefault){
1769 this.el.on('click', this.onClick, this);
1773 onClick : function(e)
1775 if(this.preventDefault){
1778 //Roo.log('img onclick');
1779 this.fireEvent('click', this, e);
1792 * @class Roo.bootstrap.Header
1793 * @extends Roo.bootstrap.Component
1794 * Bootstrap Header class
1795 * @cfg {String} html content of header
1796 * @cfg {Number} level (1|2|3|4|5|6) default 1
1799 * Create a new Header
1800 * @param {Object} config The config object
1804 Roo.bootstrap.Header = function(config){
1805 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1808 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1816 getAutoCreate : function(){
1821 tag: 'h' + (1 *this.level),
1822 html: this.html || ''
1834 * Ext JS Library 1.1.1
1835 * Copyright(c) 2006-2007, Ext JS, LLC.
1837 * Originally Released Under LGPL - original licence link has changed is not relivant.
1840 * <script type="text/javascript">
1844 * @class Roo.bootstrap.MenuMgr
1845 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1848 Roo.bootstrap.MenuMgr = function(){
1849 var menus, active, groups = {}, attached = false, lastShow = new Date();
1851 // private - called when first menu is created
1854 active = new Roo.util.MixedCollection();
1855 Roo.get(document).addKeyListener(27, function(){
1856 if(active.length > 0){
1864 if(active && active.length > 0){
1865 var c = active.clone();
1875 if(active.length < 1){
1876 Roo.get(document).un("mouseup", onMouseDown);
1884 var last = active.last();
1885 lastShow = new Date();
1888 Roo.get(document).on("mouseup", onMouseDown);
1893 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1894 m.parentMenu.activeChild = m;
1895 }else if(last && last.isVisible()){
1896 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1901 function onBeforeHide(m){
1903 m.activeChild.hide();
1905 if(m.autoHideTimer){
1906 clearTimeout(m.autoHideTimer);
1907 delete m.autoHideTimer;
1912 function onBeforeShow(m){
1913 var pm = m.parentMenu;
1914 if(!pm && !m.allowOtherMenus){
1916 }else if(pm && pm.activeChild && active != m){
1917 pm.activeChild.hide();
1921 // private this should really trigger on mouseup..
1922 function onMouseDown(e){
1923 Roo.log("on Mouse Up");
1925 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1926 Roo.log("MenuManager hideAll");
1935 function onBeforeCheck(mi, state){
1937 var g = groups[mi.group];
1938 for(var i = 0, l = g.length; i < l; i++){
1940 g[i].setChecked(false);
1949 * Hides all menus that are currently visible
1951 hideAll : function(){
1956 register : function(menu){
1960 menus[menu.id] = menu;
1961 menu.on("beforehide", onBeforeHide);
1962 menu.on("hide", onHide);
1963 menu.on("beforeshow", onBeforeShow);
1964 menu.on("show", onShow);
1966 if(g && menu.events["checkchange"]){
1970 groups[g].push(menu);
1971 menu.on("checkchange", onCheck);
1976 * Returns a {@link Roo.menu.Menu} object
1977 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1978 * be used to generate and return a new Menu instance.
1980 get : function(menu){
1981 if(typeof menu == "string"){ // menu id
1983 }else if(menu.events){ // menu instance
1986 /*else if(typeof menu.length == 'number'){ // array of menu items?
1987 return new Roo.bootstrap.Menu({items:menu});
1988 }else{ // otherwise, must be a config
1989 return new Roo.bootstrap.Menu(menu);
1996 unregister : function(menu){
1997 delete menus[menu.id];
1998 menu.un("beforehide", onBeforeHide);
1999 menu.un("hide", onHide);
2000 menu.un("beforeshow", onBeforeShow);
2001 menu.un("show", onShow);
2003 if(g && menu.events["checkchange"]){
2004 groups[g].remove(menu);
2005 menu.un("checkchange", onCheck);
2010 registerCheckable : function(menuItem){
2011 var g = menuItem.group;
2016 groups[g].push(menuItem);
2017 menuItem.on("beforecheckchange", onBeforeCheck);
2022 unregisterCheckable : function(menuItem){
2023 var g = menuItem.group;
2025 groups[g].remove(menuItem);
2026 menuItem.un("beforecheckchange", onBeforeCheck);
2038 * @class Roo.bootstrap.Menu
2039 * @extends Roo.bootstrap.Component
2040 * Bootstrap Menu class - container for MenuItems
2041 * @cfg {String} type (dropdown|treeview|submenu) type of menu
2042 * @cfg {bool} hidden if the menu should be hidden when rendered.
2043 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
2044 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
2048 * @param {Object} config The config object
2052 Roo.bootstrap.Menu = function(config){
2053 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2054 if (this.registerMenu && this.type != 'treeview') {
2055 Roo.bootstrap.MenuMgr.register(this);
2062 * Fires before this menu is displayed
2063 * @param {Roo.menu.Menu} this
2068 * Fires before this menu is hidden
2069 * @param {Roo.menu.Menu} this
2074 * Fires after this menu is displayed
2075 * @param {Roo.menu.Menu} this
2080 * Fires after this menu is hidden
2081 * @param {Roo.menu.Menu} this
2086 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2087 * @param {Roo.menu.Menu} this
2088 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2089 * @param {Roo.EventObject} e
2094 * Fires when the mouse is hovering over this menu
2095 * @param {Roo.menu.Menu} this
2096 * @param {Roo.EventObject} e
2097 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2102 * Fires when the mouse exits this menu
2103 * @param {Roo.menu.Menu} this
2104 * @param {Roo.EventObject} e
2105 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2110 * Fires when a menu item contained in this menu is clicked
2111 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2112 * @param {Roo.EventObject} e
2116 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2119 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2123 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2126 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2128 registerMenu : true,
2130 menuItems :false, // stores the menu items..
2140 getChildContainer : function() {
2144 getAutoCreate : function(){
2146 //if (['right'].indexOf(this.align)!==-1) {
2147 // cfg.cn[1].cls += ' pull-right'
2153 cls : 'dropdown-menu' ,
2154 style : 'z-index:1000'
2158 if (this.type === 'submenu') {
2159 cfg.cls = 'submenu active';
2161 if (this.type === 'treeview') {
2162 cfg.cls = 'treeview-menu';
2167 initEvents : function() {
2169 // Roo.log("ADD event");
2170 // Roo.log(this.triggerEl.dom);
2172 this.triggerEl.on('click', this.onTriggerClick, this);
2174 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2177 if (this.triggerEl.hasClass('nav-item')) {
2178 // dropdown toggle on the 'a' in BS4?
2179 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2181 this.triggerEl.addClass('dropdown-toggle');
2184 this.el.on('touchstart' , this.onTouch, this);
2186 this.el.on('click' , this.onClick, this);
2188 this.el.on("mouseover", this.onMouseOver, this);
2189 this.el.on("mouseout", this.onMouseOut, this);
2193 findTargetItem : function(e)
2195 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2199 //Roo.log(t); Roo.log(t.id);
2201 //Roo.log(this.menuitems);
2202 return this.menuitems.get(t.id);
2204 //return this.items.get(t.menuItemId);
2210 onTouch : function(e)
2212 Roo.log("menu.onTouch");
2213 //e.stopEvent(); this make the user popdown broken
2217 onClick : function(e)
2219 Roo.log("menu.onClick");
2221 var t = this.findTargetItem(e);
2222 if(!t || t.isContainer){
2227 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2228 if(t == this.activeItem && t.shouldDeactivate(e)){
2229 this.activeItem.deactivate();
2230 delete this.activeItem;
2234 this.setActiveItem(t, true);
2242 Roo.log('pass click event');
2246 this.fireEvent("click", this, t, e);
2250 if(!t.href.length || t.href == '#'){
2251 (function() { _this.hide(); }).defer(100);
2256 onMouseOver : function(e){
2257 var t = this.findTargetItem(e);
2260 // if(t.canActivate && !t.disabled){
2261 // this.setActiveItem(t, true);
2265 this.fireEvent("mouseover", this, e, t);
2267 isVisible : function(){
2268 return !this.hidden;
2270 onMouseOut : function(e){
2271 var t = this.findTargetItem(e);
2274 // if(t == this.activeItem && t.shouldDeactivate(e)){
2275 // this.activeItem.deactivate();
2276 // delete this.activeItem;
2279 this.fireEvent("mouseout", this, e, t);
2284 * Displays this menu relative to another element
2285 * @param {String/HTMLElement/Roo.Element} element The element to align to
2286 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2287 * the element (defaults to this.defaultAlign)
2288 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2290 show : function(el, pos, parentMenu){
2291 this.parentMenu = parentMenu;
2295 this.fireEvent("beforeshow", this);
2296 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2299 * Displays this menu at a specific xy position
2300 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2301 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2303 showAt : function(xy, parentMenu, /* private: */_e){
2304 this.parentMenu = parentMenu;
2309 this.fireEvent("beforeshow", this);
2310 //xy = this.el.adjustForConstraints(xy);
2314 this.hideMenuItems();
2315 this.hidden = false;
2316 this.triggerEl.addClass('open');
2317 this.el.addClass('show');
2319 // reassign x when hitting right
2320 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2321 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2324 // reassign y when hitting bottom
2325 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2326 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2329 // but the list may align on trigger left or trigger top... should it be a properity?
2331 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2336 this.fireEvent("show", this);
2342 this.doFocus.defer(50, this);
2346 doFocus : function(){
2348 this.focusEl.focus();
2353 * Hides this menu and optionally all parent menus
2354 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2356 hide : function(deep)
2359 this.hideMenuItems();
2360 if(this.el && this.isVisible()){
2361 this.fireEvent("beforehide", this);
2362 if(this.activeItem){
2363 this.activeItem.deactivate();
2364 this.activeItem = null;
2366 this.triggerEl.removeClass('open');;
2367 this.el.removeClass('show');
2369 this.fireEvent("hide", this);
2371 if(deep === true && this.parentMenu){
2372 this.parentMenu.hide(true);
2376 onTriggerClick : function(e)
2378 Roo.log('trigger click');
2380 var target = e.getTarget();
2382 Roo.log(target.nodeName.toLowerCase());
2384 if(target.nodeName.toLowerCase() === 'i'){
2390 onTriggerPress : function(e)
2392 Roo.log('trigger press');
2393 //Roo.log(e.getTarget());
2394 // Roo.log(this.triggerEl.dom);
2396 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2397 var pel = Roo.get(e.getTarget());
2398 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2399 Roo.log('is treeview or dropdown?');
2403 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2407 if (this.isVisible()) {
2412 this.show(this.triggerEl, false, false);
2415 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2422 hideMenuItems : function()
2424 Roo.log("hide Menu Items");
2428 //$(backdrop).remove()
2429 this.el.select('.open',true).each(function(aa) {
2431 aa.removeClass('open');
2432 //var parent = getParent($(this))
2433 //var relatedTarget = { relatedTarget: this }
2435 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2436 //if (e.isDefaultPrevented()) return
2437 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2440 addxtypeChild : function (tree, cntr) {
2441 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2443 this.menuitems.add(comp);
2455 this.getEl().dom.innerHTML = '';
2456 this.menuitems.clear();
2470 * @class Roo.bootstrap.MenuItem
2471 * @extends Roo.bootstrap.Component
2472 * Bootstrap MenuItem class
2473 * @cfg {String} html the menu label
2474 * @cfg {String} href the link
2475 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2476 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2477 * @cfg {Boolean} active used on sidebars to highlight active itesm
2478 * @cfg {String} fa favicon to show on left of menu item.
2479 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2483 * Create a new MenuItem
2484 * @param {Object} config The config object
2488 Roo.bootstrap.MenuItem = function(config){
2489 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2494 * The raw click event for the entire grid.
2495 * @param {Roo.bootstrap.MenuItem} this
2496 * @param {Roo.EventObject} e
2502 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2506 preventDefault: false,
2507 isContainer : false,
2511 getAutoCreate : function(){
2513 if(this.isContainer){
2516 cls: 'dropdown-menu-item dropdown-item'
2530 if (this.fa !== false) {
2533 cls : 'fa fa-' + this.fa
2542 cls: 'dropdown-menu-item dropdown-item',
2545 if (this.parent().type == 'treeview') {
2546 cfg.cls = 'treeview-menu';
2549 cfg.cls += ' active';
2554 anc.href = this.href || cfg.cn[0].href ;
2555 ctag.html = this.html || cfg.cn[0].html ;
2559 initEvents: function()
2561 if (this.parent().type == 'treeview') {
2562 this.el.select('a').on('click', this.onClick, this);
2566 this.menu.parentType = this.xtype;
2567 this.menu.triggerEl = this.el;
2568 this.menu = this.addxtype(Roo.apply({}, this.menu));
2572 onClick : function(e)
2574 Roo.log('item on click ');
2576 if(this.preventDefault){
2579 //this.parent().hideMenuItems();
2581 this.fireEvent('click', this, e);
2600 * @class Roo.bootstrap.MenuSeparator
2601 * @extends Roo.bootstrap.Component
2602 * Bootstrap MenuSeparator class
2605 * Create a new MenuItem
2606 * @param {Object} config The config object
2610 Roo.bootstrap.MenuSeparator = function(config){
2611 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2614 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2616 getAutoCreate : function(){
2635 * @class Roo.bootstrap.Modal
2636 * @extends Roo.bootstrap.Component
2637 * Bootstrap Modal class
2638 * @cfg {String} title Title of dialog
2639 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2640 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2641 * @cfg {Boolean} specificTitle default false
2642 * @cfg {Array} buttons Array of buttons or standard button set..
2643 * @cfg {String} buttonPosition (left|right|center) default right
2644 * @cfg {Boolean} animate default true
2645 * @cfg {Boolean} allow_close default true
2646 * @cfg {Boolean} fitwindow default false
2647 * @cfg {String} size (sm|lg) default empty
2648 * @cfg {Number} max_width set the max width of modal
2652 * Create a new Modal Dialog
2653 * @param {Object} config The config object
2656 Roo.bootstrap.Modal = function(config){
2657 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2662 * The raw btnclick event for the button
2663 * @param {Roo.EventObject} e
2668 * Fire when dialog resize
2669 * @param {Roo.bootstrap.Modal} this
2670 * @param {Roo.EventObject} e
2674 this.buttons = this.buttons || [];
2677 this.tmpl = Roo.factory(this.tmpl);
2682 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2684 title : 'test dialog',
2694 specificTitle: false,
2696 buttonPosition: 'right',
2719 onRender : function(ct, position)
2721 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2724 var cfg = Roo.apply({}, this.getAutoCreate());
2727 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2729 //if (!cfg.name.length) {
2733 cfg.cls += ' ' + this.cls;
2736 cfg.style = this.style;
2738 this.el = Roo.get(document.body).createChild(cfg, position);
2740 //var type = this.el.dom.type;
2743 if(this.tabIndex !== undefined){
2744 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2747 this.dialogEl = this.el.select('.modal-dialog',true).first();
2748 this.bodyEl = this.el.select('.modal-body',true).first();
2749 this.closeEl = this.el.select('.modal-header .close', true).first();
2750 this.headerEl = this.el.select('.modal-header',true).first();
2751 this.titleEl = this.el.select('.modal-title',true).first();
2752 this.footerEl = this.el.select('.modal-footer',true).first();
2754 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2756 //this.el.addClass("x-dlg-modal");
2758 if (this.buttons.length) {
2759 Roo.each(this.buttons, function(bb) {
2760 var b = Roo.apply({}, bb);
2761 b.xns = b.xns || Roo.bootstrap;
2762 b.xtype = b.xtype || 'Button';
2763 if (typeof(b.listeners) == 'undefined') {
2764 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2767 var btn = Roo.factory(b);
2769 btn.render(this.el.select('.modal-footer div').first());
2773 // render the children.
2776 if(typeof(this.items) != 'undefined'){
2777 var items = this.items;
2780 for(var i =0;i < items.length;i++) {
2781 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2785 this.items = nitems;
2787 // where are these used - they used to be body/close/footer
2791 //this.el.addClass([this.fieldClass, this.cls]);
2795 getAutoCreate : function()
2799 html : this.html || ''
2804 cls : 'modal-title',
2808 if(this.specificTitle){
2814 if (this.allow_close && Roo.bootstrap.version == 3) {
2824 if (this.allow_close && Roo.bootstrap.version == 4) {
2834 if(this.size.length){
2835 size = 'modal-' + this.size;
2842 cls: "modal-dialog " + size,
2845 cls : "modal-content",
2848 cls : 'modal-header',
2853 cls : 'modal-footer',
2857 cls: 'btn-' + this.buttonPosition
2874 modal.cls += ' fade';
2880 getChildContainer : function() {
2885 getButtonContainer : function() {
2886 return this.el.select('.modal-footer div',true).first();
2889 initEvents : function()
2891 if (this.allow_close) {
2892 this.closeEl.on('click', this.hide, this);
2894 Roo.EventManager.onWindowResize(this.resize, this, true);
2901 this.maskEl.setSize(
2902 Roo.lib.Dom.getViewWidth(true),
2903 Roo.lib.Dom.getViewHeight(true)
2906 if (this.fitwindow) {
2908 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2909 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2914 if(this.max_width !== 0) {
2916 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2919 this.setSize(w, this.height);
2923 if(this.max_height) {
2924 this.setSize(w,Math.min(
2926 Roo.lib.Dom.getViewportHeight(true) - 60
2932 if(!this.fit_content) {
2933 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2937 this.setSize(w, Math.min(
2939 this.headerEl.getHeight() +
2940 this.footerEl.getHeight() +
2941 this.getChildHeight(this.bodyEl.dom.childNodes),
2942 Roo.lib.Dom.getViewportHeight(true) - 60)
2948 setSize : function(w,h)
2959 if (!this.rendered) {
2963 //this.el.setStyle('display', 'block');
2964 this.el.removeClass('hideing');
2965 this.el.dom.style.display='block';
2967 Roo.get(document.body).addClass('modal-open');
2969 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2972 this.el.addClass('show');
2973 this.el.addClass('in');
2976 this.el.addClass('show');
2977 this.el.addClass('in');
2980 // not sure how we can show data in here..
2982 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2985 Roo.get(document.body).addClass("x-body-masked");
2987 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2988 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2989 this.maskEl.dom.style.display = 'block';
2990 this.maskEl.addClass('show');
2995 this.fireEvent('show', this);
2997 // set zindex here - otherwise it appears to be ignored...
2998 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3001 this.items.forEach( function(e) {
3002 e.layout ? e.layout() : false;
3010 if(this.fireEvent("beforehide", this) !== false){
3012 this.maskEl.removeClass('show');
3014 this.maskEl.dom.style.display = '';
3015 Roo.get(document.body).removeClass("x-body-masked");
3016 this.el.removeClass('in');
3017 this.el.select('.modal-dialog', true).first().setStyle('transform','');
3019 if(this.animate){ // why
3020 this.el.addClass('hideing');
3021 this.el.removeClass('show');
3023 if (!this.el.hasClass('hideing')) {
3024 return; // it's been shown again...
3027 this.el.dom.style.display='';
3029 Roo.get(document.body).removeClass('modal-open');
3030 this.el.removeClass('hideing');
3034 this.el.removeClass('show');
3035 this.el.dom.style.display='';
3036 Roo.get(document.body).removeClass('modal-open');
3039 this.fireEvent('hide', this);
3042 isVisible : function()
3045 return this.el.hasClass('show') && !this.el.hasClass('hideing');
3049 addButton : function(str, cb)
3053 var b = Roo.apply({}, { html : str } );
3054 b.xns = b.xns || Roo.bootstrap;
3055 b.xtype = b.xtype || 'Button';
3056 if (typeof(b.listeners) == 'undefined') {
3057 b.listeners = { click : cb.createDelegate(this) };
3060 var btn = Roo.factory(b);
3062 btn.render(this.el.select('.modal-footer div').first());
3068 setDefaultButton : function(btn)
3070 //this.el.select('.modal-footer').()
3074 resizeTo: function(w,h)
3078 this.dialogEl.setWidth(w);
3079 if (this.diff === false) {
3080 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3083 this.bodyEl.setHeight(h - this.diff);
3085 this.fireEvent('resize', this);
3088 setContentSize : function(w, h)
3092 onButtonClick: function(btn,e)
3095 this.fireEvent('btnclick', btn.name, e);
3098 * Set the title of the Dialog
3099 * @param {String} str new Title
3101 setTitle: function(str) {
3102 this.titleEl.dom.innerHTML = str;
3105 * Set the body of the Dialog
3106 * @param {String} str new Title
3108 setBody: function(str) {
3109 this.bodyEl.dom.innerHTML = str;
3112 * Set the body of the Dialog using the template
3113 * @param {Obj} data - apply this data to the template and replace the body contents.
3115 applyBody: function(obj)
3118 Roo.log("Error - using apply Body without a template");
3121 this.tmpl.overwrite(this.bodyEl, obj);
3124 getChildHeight : function(child_nodes)
3128 child_nodes.length == 0
3133 var child_height = 0;
3135 for(var i = 0; i < child_nodes.length; i++) {
3138 * for modal with tabs...
3139 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3141 var layout_childs = child_nodes[i].childNodes;
3143 for(var j = 0; j < layout_childs.length; j++) {
3145 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3147 var layout_body_childs = layout_childs[j].childNodes;
3149 for(var k = 0; k < layout_body_childs.length; k++) {
3151 if(layout_body_childs[k].classList.contains('navbar')) {
3152 child_height += layout_body_childs[k].offsetHeight;
3156 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3158 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3160 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3162 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3163 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3178 child_height += child_nodes[i].offsetHeight;
3179 // Roo.log(child_nodes[i].offsetHeight);
3182 return child_height;
3188 Roo.apply(Roo.bootstrap.Modal, {
3190 * Button config that displays a single OK button
3199 * Button config that displays Yes and No buttons
3215 * Button config that displays OK and Cancel buttons
3230 * Button config that displays Yes, No and Cancel buttons
3254 * messagebox - can be used as a replace
3258 * @class Roo.MessageBox
3259 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3263 Roo.Msg.alert('Status', 'Changes saved successfully.');
3265 // Prompt for user data:
3266 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3268 // process text value...
3272 // Show a dialog using config options:
3274 title:'Save Changes?',
3275 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3276 buttons: Roo.Msg.YESNOCANCEL,
3283 Roo.bootstrap.MessageBox = function(){
3284 var dlg, opt, mask, waitTimer;
3285 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3286 var buttons, activeTextEl, bwidth;
3290 var handleButton = function(button){
3292 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3296 var handleHide = function(){
3298 dlg.el.removeClass(opt.cls);
3301 // Roo.TaskMgr.stop(waitTimer);
3302 // waitTimer = null;
3307 var updateButtons = function(b){
3310 buttons["ok"].hide();
3311 buttons["cancel"].hide();
3312 buttons["yes"].hide();
3313 buttons["no"].hide();
3314 //dlg.footer.dom.style.display = 'none';
3317 dlg.footerEl.dom.style.display = '';
3318 for(var k in buttons){
3319 if(typeof buttons[k] != "function"){
3322 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3323 width += buttons[k].el.getWidth()+15;
3333 var handleEsc = function(d, k, e){
3334 if(opt && opt.closable !== false){
3344 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3345 * @return {Roo.BasicDialog} The BasicDialog element
3347 getDialog : function(){
3349 dlg = new Roo.bootstrap.Modal( {
3352 //constraintoviewport:false,
3354 //collapsible : false,
3359 //buttonAlign:"center",
3360 closeClick : function(){
3361 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3364 handleButton("cancel");
3369 dlg.on("hide", handleHide);
3371 //dlg.addKeyListener(27, handleEsc);
3373 this.buttons = buttons;
3374 var bt = this.buttonText;
3375 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3376 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3377 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3378 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3380 bodyEl = dlg.bodyEl.createChild({
3382 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3383 '<textarea class="roo-mb-textarea"></textarea>' +
3384 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3386 msgEl = bodyEl.dom.firstChild;
3387 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3388 textboxEl.enableDisplayMode();
3389 textboxEl.addKeyListener([10,13], function(){
3390 if(dlg.isVisible() && opt && opt.buttons){
3393 }else if(opt.buttons.yes){
3394 handleButton("yes");
3398 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3399 textareaEl.enableDisplayMode();
3400 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3401 progressEl.enableDisplayMode();
3403 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3404 var pf = progressEl.dom.firstChild;
3406 pp = Roo.get(pf.firstChild);
3407 pp.setHeight(pf.offsetHeight);
3415 * Updates the message box body text
3416 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3417 * the XHTML-compliant non-breaking space character '&#160;')
3418 * @return {Roo.MessageBox} This message box
3420 updateText : function(text)
3422 if(!dlg.isVisible() && !opt.width){
3423 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3424 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3426 msgEl.innerHTML = text || ' ';
3428 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3429 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3431 Math.min(opt.width || cw , this.maxWidth),
3432 Math.max(opt.minWidth || this.minWidth, bwidth)
3435 activeTextEl.setWidth(w);
3437 if(dlg.isVisible()){
3438 dlg.fixedcenter = false;
3440 // to big, make it scroll. = But as usual stupid IE does not support
3443 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3444 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3445 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3447 bodyEl.dom.style.height = '';
3448 bodyEl.dom.style.overflowY = '';
3451 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3453 bodyEl.dom.style.overflowX = '';
3456 dlg.setContentSize(w, bodyEl.getHeight());
3457 if(dlg.isVisible()){
3458 dlg.fixedcenter = true;
3464 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3465 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3466 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3467 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3468 * @return {Roo.MessageBox} This message box
3470 updateProgress : function(value, text){
3472 this.updateText(text);
3475 if (pp) { // weird bug on my firefox - for some reason this is not defined
3476 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3477 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3483 * Returns true if the message box is currently displayed
3484 * @return {Boolean} True if the message box is visible, else false
3486 isVisible : function(){
3487 return dlg && dlg.isVisible();
3491 * Hides the message box if it is displayed
3494 if(this.isVisible()){
3500 * Displays a new message box, or reinitializes an existing message box, based on the config options
3501 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3502 * The following config object properties are supported:
3504 Property Type Description
3505 ---------- --------------- ------------------------------------------------------------------------------------
3506 animEl String/Element An id or Element from which the message box should animate as it opens and
3507 closes (defaults to undefined)
3508 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3509 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3510 closable Boolean False to hide the top-right close button (defaults to true). Note that
3511 progress and wait dialogs will ignore this property and always hide the
3512 close button as they can only be closed programmatically.
3513 cls String A custom CSS class to apply to the message box element
3514 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3515 displayed (defaults to 75)
3516 fn Function A callback function to execute after closing the dialog. The arguments to the
3517 function will be btn (the name of the button that was clicked, if applicable,
3518 e.g. "ok"), and text (the value of the active text field, if applicable).
3519 Progress and wait dialogs will ignore this option since they do not respond to
3520 user actions and can only be closed programmatically, so any required function
3521 should be called by the same code after it closes the dialog.
3522 icon String A CSS class that provides a background image to be used as an icon for
3523 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3524 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3525 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3526 modal Boolean False to allow user interaction with the page while the message box is
3527 displayed (defaults to true)
3528 msg String A string that will replace the existing message box body text (defaults
3529 to the XHTML-compliant non-breaking space character ' ')
3530 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3531 progress Boolean True to display a progress bar (defaults to false)
3532 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3533 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3534 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3535 title String The title text
3536 value String The string value to set into the active textbox element if displayed
3537 wait Boolean True to display a progress bar (defaults to false)
3538 width Number The width of the dialog in pixels
3545 msg: 'Please enter your address:',
3547 buttons: Roo.MessageBox.OKCANCEL,
3550 animEl: 'addAddressBtn'
3553 * @param {Object} config Configuration options
3554 * @return {Roo.MessageBox} This message box
3556 show : function(options)
3559 // this causes nightmares if you show one dialog after another
3560 // especially on callbacks..
3562 if(this.isVisible()){
3565 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3566 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3567 Roo.log("New Dialog Message:" + options.msg )
3568 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3569 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3572 var d = this.getDialog();
3574 d.setTitle(opt.title || " ");
3575 d.closeEl.setDisplayed(opt.closable !== false);
3576 activeTextEl = textboxEl;
3577 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3582 textareaEl.setHeight(typeof opt.multiline == "number" ?
3583 opt.multiline : this.defaultTextHeight);
3584 activeTextEl = textareaEl;
3593 progressEl.setDisplayed(opt.progress === true);
3594 this.updateProgress(0);
3595 activeTextEl.dom.value = opt.value || "";
3597 dlg.setDefaultButton(activeTextEl);
3599 var bs = opt.buttons;
3603 }else if(bs && bs.yes){
3604 db = buttons["yes"];
3606 dlg.setDefaultButton(db);
3608 bwidth = updateButtons(opt.buttons);
3609 this.updateText(opt.msg);
3611 d.el.addClass(opt.cls);
3613 d.proxyDrag = opt.proxyDrag === true;
3614 d.modal = opt.modal !== false;
3615 d.mask = opt.modal !== false ? mask : false;
3617 // force it to the end of the z-index stack so it gets a cursor in FF
3618 document.body.appendChild(dlg.el.dom);
3619 d.animateTarget = null;
3620 d.show(options.animEl);
3626 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3627 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3628 * and closing the message box when the process is complete.
3629 * @param {String} title The title bar text
3630 * @param {String} msg The message box body text
3631 * @return {Roo.MessageBox} This message box
3633 progress : function(title, msg){
3640 minWidth: this.minProgressWidth,
3647 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3648 * If a callback function is passed it will be called after the user clicks the button, and the
3649 * id of the button that was clicked will be passed as the only parameter to the callback
3650 * (could also be the top-right close button).
3651 * @param {String} title The title bar text
3652 * @param {String} msg The message box body text
3653 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3654 * @param {Object} scope (optional) The scope of the callback function
3655 * @return {Roo.MessageBox} This message box
3657 alert : function(title, msg, fn, scope)
3672 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3673 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3674 * You are responsible for closing the message box when the process is complete.
3675 * @param {String} msg The message box body text
3676 * @param {String} title (optional) The title bar text
3677 * @return {Roo.MessageBox} This message box
3679 wait : function(msg, title){
3690 waitTimer = Roo.TaskMgr.start({
3692 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3700 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3701 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3702 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3703 * @param {String} title The title bar text
3704 * @param {String} msg The message box body text
3705 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3706 * @param {Object} scope (optional) The scope of the callback function
3707 * @return {Roo.MessageBox} This message box
3709 confirm : function(title, msg, fn, scope){
3713 buttons: this.YESNO,
3722 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3723 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3724 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3725 * (could also be the top-right close button) and the text that was entered will be passed as the two
3726 * parameters to the callback.
3727 * @param {String} title The title bar text
3728 * @param {String} msg The message box body text
3729 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3730 * @param {Object} scope (optional) The scope of the callback function
3731 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3732 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3733 * @return {Roo.MessageBox} This message box
3735 prompt : function(title, msg, fn, scope, multiline){
3739 buttons: this.OKCANCEL,
3744 multiline: multiline,
3751 * Button config that displays a single OK button
3756 * Button config that displays Yes and No buttons
3759 YESNO : {yes:true, no:true},
3761 * Button config that displays OK and Cancel buttons
3764 OKCANCEL : {ok:true, cancel:true},
3766 * Button config that displays Yes, No and Cancel buttons
3769 YESNOCANCEL : {yes:true, no:true, cancel:true},
3772 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3775 defaultTextHeight : 75,
3777 * The maximum width in pixels of the message box (defaults to 600)
3782 * The minimum width in pixels of the message box (defaults to 100)
3787 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3788 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3791 minProgressWidth : 250,
3793 * An object containing the default button text strings that can be overriden for localized language support.
3794 * Supported properties are: ok, cancel, yes and no.
3795 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3808 * Shorthand for {@link Roo.MessageBox}
3810 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3811 Roo.Msg = Roo.Msg || Roo.MessageBox;
3820 * @class Roo.bootstrap.Navbar
3821 * @extends Roo.bootstrap.Component
3822 * Bootstrap Navbar class
3825 * Create a new Navbar
3826 * @param {Object} config The config object
3830 Roo.bootstrap.Navbar = function(config){
3831 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3835 * @event beforetoggle
3836 * Fire before toggle the menu
3837 * @param {Roo.EventObject} e
3839 "beforetoggle" : true
3843 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3852 getAutoCreate : function(){
3855 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3859 initEvents :function ()
3861 //Roo.log(this.el.select('.navbar-toggle',true));
3862 this.el.select('.navbar-toggle',true).on('click', function() {
3863 if(this.fireEvent('beforetoggle', this) !== false){
3864 this.el.select('.navbar-collapse',true).toggleClass('in collapse');
3874 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3876 var size = this.el.getSize();
3877 this.maskEl.setSize(size.width, size.height);
3878 this.maskEl.enableDisplayMode("block");
3887 getChildContainer : function()
3889 if (this.el.select('.collapse').getCount()) {
3890 return this.el.select('.collapse',true).first();
3923 * @class Roo.bootstrap.NavSimplebar
3924 * @extends Roo.bootstrap.Navbar
3925 * Bootstrap Sidebar class
3927 * @cfg {Boolean} inverse is inverted color
3929 * @cfg {String} type (nav | pills | tabs)
3930 * @cfg {Boolean} arrangement stacked | justified
3931 * @cfg {String} align (left | right) alignment
3933 * @cfg {Boolean} main (true|false) main nav bar? default false
3934 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3936 * @cfg {String} tag (header|footer|nav|div) default is nav
3938 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3942 * Create a new Sidebar
3943 * @param {Object} config The config object
3947 Roo.bootstrap.NavSimplebar = function(config){
3948 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3951 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3967 getAutoCreate : function(){
3971 tag : this.tag || 'div',
3972 cls : 'navbar navbar-expand-lg'
3974 if (['light','white'].indexOf(this.weight) > -1) {
3975 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
3977 cfg.cls += ' bg-' + this.weight;
3989 this.type = this.type || 'nav';
3990 if (['tabs','pills'].indexOf(this.type)!==-1) {
3991 cfg.cn[0].cls += ' nav-' + this.type
3995 if (this.type!=='nav') {
3996 Roo.log('nav type must be nav/tabs/pills')
3998 cfg.cn[0].cls += ' navbar-nav'
4004 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4005 cfg.cn[0].cls += ' nav-' + this.arrangement;
4009 if (this.align === 'right') {
4010 cfg.cn[0].cls += ' navbar-right';
4014 cfg.cls += ' navbar-inverse';
4038 * navbar-expand-md fixed-top
4042 * @class Roo.bootstrap.NavHeaderbar
4043 * @extends Roo.bootstrap.NavSimplebar
4044 * Bootstrap Sidebar class
4046 * @cfg {String} brand what is brand
4047 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4048 * @cfg {String} brand_href href of the brand
4049 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
4050 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4051 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4052 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4055 * Create a new Sidebar
4056 * @param {Object} config The config object
4060 Roo.bootstrap.NavHeaderbar = function(config){
4061 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4065 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
4072 desktopCenter : false,
4075 getAutoCreate : function(){
4078 tag: this.nav || 'nav',
4079 cls: 'navbar navbar-expand-md',
4085 if (this.desktopCenter) {
4086 cn.push({cls : 'container', cn : []});
4094 cls: 'navbar-toggle navbar-toggler',
4095 'data-toggle': 'collapse',
4100 html: 'Toggle navigation'
4104 cls: 'icon-bar navbar-toggler-icon'
4117 cn.push( Roo.bootstrap.version == 4 ? btn : {
4119 cls: 'navbar-header',
4128 cls: 'collapse navbar-collapse',
4132 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4134 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4135 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4137 // tag can override this..
4139 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4142 if (this.brand !== '') {
4143 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4144 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4146 href: this.brand_href ? this.brand_href : '#',
4147 cls: 'navbar-brand',
4155 cfg.cls += ' main-nav';
4163 getHeaderChildContainer : function()
4165 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4166 return this.el.select('.navbar-header',true).first();
4169 return this.getChildContainer();
4173 initEvents : function()
4175 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4177 if (this.autohide) {
4182 Roo.get(document).on('scroll',function(e) {
4183 var ns = Roo.get(document).getScroll().top;
4184 var os = prevScroll;
4188 ft.removeClass('slideDown');
4189 ft.addClass('slideUp');
4192 ft.removeClass('slideUp');
4193 ft.addClass('slideDown');
4214 * @class Roo.bootstrap.NavSidebar
4215 * @extends Roo.bootstrap.Navbar
4216 * Bootstrap Sidebar class
4219 * Create a new Sidebar
4220 * @param {Object} config The config object
4224 Roo.bootstrap.NavSidebar = function(config){
4225 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4228 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4230 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4232 getAutoCreate : function(){
4237 cls: 'sidebar sidebar-nav'
4259 * @class Roo.bootstrap.NavGroup
4260 * @extends Roo.bootstrap.Component
4261 * Bootstrap NavGroup class
4262 * @cfg {String} align (left|right)
4263 * @cfg {Boolean} inverse
4264 * @cfg {String} type (nav|pills|tab) default nav
4265 * @cfg {String} navId - reference Id for navbar.
4269 * Create a new nav group
4270 * @param {Object} config The config object
4273 Roo.bootstrap.NavGroup = function(config){
4274 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4277 Roo.bootstrap.NavGroup.register(this);
4281 * Fires when the active item changes
4282 * @param {Roo.bootstrap.NavGroup} this
4283 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4284 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4291 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4302 getAutoCreate : function()
4304 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4311 if (['tabs','pills'].indexOf(this.type)!==-1) {
4312 cfg.cls += ' nav-' + this.type
4314 if (this.type!=='nav') {
4315 Roo.log('nav type must be nav/tabs/pills')
4317 cfg.cls += ' navbar-nav'
4320 if (this.parent() && this.parent().sidebar) {
4323 cls: 'dashboard-menu sidebar-menu'
4329 if (this.form === true) {
4335 if (this.align === 'right') {
4336 cfg.cls += ' navbar-right ml-md-auto';
4338 cfg.cls += ' navbar-left';
4342 if (this.align === 'right') {
4343 cfg.cls += ' navbar-right ml-md-auto';
4345 cfg.cls += ' mr-auto';
4349 cfg.cls += ' navbar-inverse';
4357 * sets the active Navigation item
4358 * @param {Roo.bootstrap.NavItem} the new current navitem
4360 setActiveItem : function(item)
4363 Roo.each(this.navItems, function(v){
4368 v.setActive(false, true);
4375 item.setActive(true, true);
4376 this.fireEvent('changed', this, item, prev);
4381 * gets the active Navigation item
4382 * @return {Roo.bootstrap.NavItem} the current navitem
4384 getActive : function()
4388 Roo.each(this.navItems, function(v){
4399 indexOfNav : function()
4403 Roo.each(this.navItems, function(v,i){
4414 * adds a Navigation item
4415 * @param {Roo.bootstrap.NavItem} the navitem to add
4417 addItem : function(cfg)
4419 var cn = new Roo.bootstrap.NavItem(cfg);
4421 cn.parentId = this.id;
4422 cn.onRender(this.el, null);
4426 * register a Navigation item
4427 * @param {Roo.bootstrap.NavItem} the navitem to add
4429 register : function(item)
4431 this.navItems.push( item);
4432 item.navId = this.navId;
4437 * clear all the Navigation item
4440 clearAll : function()
4443 this.el.dom.innerHTML = '';
4446 getNavItem: function(tabId)
4449 Roo.each(this.navItems, function(e) {
4450 if (e.tabId == tabId) {
4460 setActiveNext : function()
4462 var i = this.indexOfNav(this.getActive());
4463 if (i > this.navItems.length) {
4466 this.setActiveItem(this.navItems[i+1]);
4468 setActivePrev : function()
4470 var i = this.indexOfNav(this.getActive());
4474 this.setActiveItem(this.navItems[i-1]);
4476 clearWasActive : function(except) {
4477 Roo.each(this.navItems, function(e) {
4478 if (e.tabId != except.tabId && e.was_active) {
4479 e.was_active = false;
4486 getWasActive : function ()
4489 Roo.each(this.navItems, function(e) {
4504 Roo.apply(Roo.bootstrap.NavGroup, {
4508 * register a Navigation Group
4509 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4511 register : function(navgrp)
4513 this.groups[navgrp.navId] = navgrp;
4517 * fetch a Navigation Group based on the navigation ID
4518 * @param {string} the navgroup to add
4519 * @returns {Roo.bootstrap.NavGroup} the navgroup
4521 get: function(navId) {
4522 if (typeof(this.groups[navId]) == 'undefined') {
4524 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4526 return this.groups[navId] ;
4541 * @class Roo.bootstrap.NavItem
4542 * @extends Roo.bootstrap.Component
4543 * Bootstrap Navbar.NavItem class
4544 * @cfg {String} href link to
4545 * @cfg {String} html content of button
4546 * @cfg {String} badge text inside badge
4547 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4548 * @cfg {String} glyphicon DEPRICATED - use fa
4549 * @cfg {String} icon DEPRICATED - use fa
4550 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4551 * @cfg {Boolean} active Is item active
4552 * @cfg {Boolean} disabled Is item disabled
4554 * @cfg {Boolean} preventDefault (true | false) default false
4555 * @cfg {String} tabId the tab that this item activates.
4556 * @cfg {String} tagtype (a|span) render as a href or span?
4557 * @cfg {Boolean} animateRef (true|false) link to element default false
4560 * Create a new Navbar Item
4561 * @param {Object} config The config object
4563 Roo.bootstrap.NavItem = function(config){
4564 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4569 * The raw click event for the entire grid.
4570 * @param {Roo.EventObject} e
4575 * Fires when the active item active state changes
4576 * @param {Roo.bootstrap.NavItem} this
4577 * @param {boolean} state the new state
4583 * Fires when scroll to element
4584 * @param {Roo.bootstrap.NavItem} this
4585 * @param {Object} options
4586 * @param {Roo.EventObject} e
4594 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4603 preventDefault : false,
4610 getAutoCreate : function(){
4619 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4621 if (this.disabled) {
4622 cfg.cls += ' disabled';
4625 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4629 href : this.href || "#",
4630 html: this.html || ''
4633 if (this.tagtype == 'a') {
4634 cfg.cn[0].cls = 'nav-link';
4637 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4640 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4642 if(this.glyphicon) {
4643 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4648 cfg.cn[0].html += " <span class='caret'></span>";
4652 if (this.badge !== '') {
4654 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4662 initEvents: function()
4664 if (typeof (this.menu) != 'undefined') {
4665 this.menu.parentType = this.xtype;
4666 this.menu.triggerEl = this.el;
4667 this.menu = this.addxtype(Roo.apply({}, this.menu));
4670 this.el.select('a',true).on('click', this.onClick, this);
4672 if(this.tagtype == 'span'){
4673 this.el.select('span',true).on('click', this.onClick, this);
4676 // at this point parent should be available..
4677 this.parent().register(this);
4680 onClick : function(e)
4682 if (e.getTarget('.dropdown-menu-item')) {
4683 // did you click on a menu itemm.... - then don't trigger onclick..
4688 this.preventDefault ||
4691 Roo.log("NavItem - prevent Default?");
4695 if (this.disabled) {
4699 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4700 if (tg && tg.transition) {
4701 Roo.log("waiting for the transitionend");
4707 //Roo.log("fire event clicked");
4708 if(this.fireEvent('click', this, e) === false){
4712 if(this.tagtype == 'span'){
4716 //Roo.log(this.href);
4717 var ael = this.el.select('a',true).first();
4720 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4721 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4722 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4723 return; // ignore... - it's a 'hash' to another page.
4725 Roo.log("NavItem - prevent Default?");
4727 this.scrollToElement(e);
4731 var p = this.parent();
4733 if (['tabs','pills'].indexOf(p.type)!==-1) {
4734 if (typeof(p.setActiveItem) !== 'undefined') {
4735 p.setActiveItem(this);
4739 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4740 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4741 // remove the collapsed menu expand...
4742 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4746 isActive: function () {
4749 setActive : function(state, fire, is_was_active)
4751 if (this.active && !state && this.navId) {
4752 this.was_active = true;
4753 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4755 nv.clearWasActive(this);
4759 this.active = state;
4762 this.el.removeClass('active');
4763 } else if (!this.el.hasClass('active')) {
4764 this.el.addClass('active');
4767 this.fireEvent('changed', this, state);
4770 // show a panel if it's registered and related..
4772 if (!this.navId || !this.tabId || !state || is_was_active) {
4776 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4780 var pan = tg.getPanelByName(this.tabId);
4784 // if we can not flip to new panel - go back to old nav highlight..
4785 if (false == tg.showPanel(pan)) {
4786 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4788 var onav = nv.getWasActive();
4790 onav.setActive(true, false, true);
4799 // this should not be here...
4800 setDisabled : function(state)
4802 this.disabled = state;
4804 this.el.removeClass('disabled');
4805 } else if (!this.el.hasClass('disabled')) {
4806 this.el.addClass('disabled');
4812 * Fetch the element to display the tooltip on.
4813 * @return {Roo.Element} defaults to this.el
4815 tooltipEl : function()
4817 return this.el.select('' + this.tagtype + '', true).first();
4820 scrollToElement : function(e)
4822 var c = document.body;
4825 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4827 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4828 c = document.documentElement;
4831 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4837 var o = target.calcOffsetsTo(c);
4844 this.fireEvent('scrollto', this, options, e);
4846 Roo.get(c).scrollTo('top', options.value, true);
4859 * <span> icon </span>
4860 * <span> text </span>
4861 * <span>badge </span>
4865 * @class Roo.bootstrap.NavSidebarItem
4866 * @extends Roo.bootstrap.NavItem
4867 * Bootstrap Navbar.NavSidebarItem class
4868 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4869 * {Boolean} open is the menu open
4870 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4871 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4872 * {String} buttonSize (sm|md|lg)the extra classes for the button
4873 * {Boolean} showArrow show arrow next to the text (default true)
4875 * Create a new Navbar Button
4876 * @param {Object} config The config object
4878 Roo.bootstrap.NavSidebarItem = function(config){
4879 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4884 * The raw click event for the entire grid.
4885 * @param {Roo.EventObject} e
4890 * Fires when the active item active state changes
4891 * @param {Roo.bootstrap.NavSidebarItem} this
4892 * @param {boolean} state the new state
4900 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4902 badgeWeight : 'default',
4908 buttonWeight : 'default',
4914 getAutoCreate : function(){
4919 href : this.href || '#',
4925 if(this.buttonView){
4928 href : this.href || '#',
4929 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4942 cfg.cls += ' active';
4945 if (this.disabled) {
4946 cfg.cls += ' disabled';
4949 cfg.cls += ' open x-open';
4952 if (this.glyphicon || this.icon) {
4953 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4954 a.cn.push({ tag : 'i', cls : c }) ;
4957 if(!this.buttonView){
4960 html : this.html || ''
4967 if (this.badge !== '') {
4968 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4974 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4977 a.cls += ' dropdown-toggle treeview' ;
4983 initEvents : function()
4985 if (typeof (this.menu) != 'undefined') {
4986 this.menu.parentType = this.xtype;
4987 this.menu.triggerEl = this.el;
4988 this.menu = this.addxtype(Roo.apply({}, this.menu));
4991 this.el.on('click', this.onClick, this);
4993 if(this.badge !== ''){
4994 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4999 onClick : function(e)
5006 if(this.preventDefault){
5010 this.fireEvent('click', this);
5013 disable : function()
5015 this.setDisabled(true);
5020 this.setDisabled(false);
5023 setDisabled : function(state)
5025 if(this.disabled == state){
5029 this.disabled = state;
5032 this.el.addClass('disabled');
5036 this.el.removeClass('disabled');
5041 setActive : function(state)
5043 if(this.active == state){
5047 this.active = state;
5050 this.el.addClass('active');
5054 this.el.removeClass('active');
5059 isActive: function ()
5064 setBadge : function(str)
5070 this.badgeEl.dom.innerHTML = str;
5087 * @class Roo.bootstrap.Row
5088 * @extends Roo.bootstrap.Component
5089 * Bootstrap Row class (contains columns...)
5093 * @param {Object} config The config object
5096 Roo.bootstrap.Row = function(config){
5097 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5100 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5102 getAutoCreate : function(){
5121 * @class Roo.bootstrap.Element
5122 * @extends Roo.bootstrap.Component
5123 * Bootstrap Element class
5124 * @cfg {String} html contents of the element
5125 * @cfg {String} tag tag of the element
5126 * @cfg {String} cls class of the element
5127 * @cfg {Boolean} preventDefault (true|false) default false
5128 * @cfg {Boolean} clickable (true|false) default false
5131 * Create a new Element
5132 * @param {Object} config The config object
5135 Roo.bootstrap.Element = function(config){
5136 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5142 * When a element is chick
5143 * @param {Roo.bootstrap.Element} this
5144 * @param {Roo.EventObject} e
5150 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5155 preventDefault: false,
5158 getAutoCreate : function(){
5162 // cls: this.cls, double assign in parent class Component.js :: onRender
5169 initEvents: function()
5171 Roo.bootstrap.Element.superclass.initEvents.call(this);
5174 this.el.on('click', this.onClick, this);
5179 onClick : function(e)
5181 if(this.preventDefault){
5185 this.fireEvent('click', this, e);
5188 getValue : function()
5190 return this.el.dom.innerHTML;
5193 setValue : function(value)
5195 this.el.dom.innerHTML = value;
5210 * @class Roo.bootstrap.Pagination
5211 * @extends Roo.bootstrap.Component
5212 * Bootstrap Pagination class
5213 * @cfg {String} size xs | sm | md | lg
5214 * @cfg {Boolean} inverse false | true
5217 * Create a new Pagination
5218 * @param {Object} config The config object
5221 Roo.bootstrap.Pagination = function(config){
5222 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5225 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5231 getAutoCreate : function(){
5237 cfg.cls += ' inverse';
5243 cfg.cls += " " + this.cls;
5261 * @class Roo.bootstrap.PaginationItem
5262 * @extends Roo.bootstrap.Component
5263 * Bootstrap PaginationItem class
5264 * @cfg {String} html text
5265 * @cfg {String} href the link
5266 * @cfg {Boolean} preventDefault (true | false) default true
5267 * @cfg {Boolean} active (true | false) default false
5268 * @cfg {Boolean} disabled default false
5272 * Create a new PaginationItem
5273 * @param {Object} config The config object
5277 Roo.bootstrap.PaginationItem = function(config){
5278 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5283 * The raw click event for the entire grid.
5284 * @param {Roo.EventObject} e
5290 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5294 preventDefault: true,
5299 getAutoCreate : function(){
5305 href : this.href ? this.href : '#',
5306 html : this.html ? this.html : ''
5316 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5320 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5326 initEvents: function() {
5328 this.el.on('click', this.onClick, this);
5331 onClick : function(e)
5333 Roo.log('PaginationItem on click ');
5334 if(this.preventDefault){
5342 this.fireEvent('click', this, e);
5358 * @class Roo.bootstrap.Slider
5359 * @extends Roo.bootstrap.Component
5360 * Bootstrap Slider class
5363 * Create a new Slider
5364 * @param {Object} config The config object
5367 Roo.bootstrap.Slider = function(config){
5368 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5371 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5373 getAutoCreate : function(){
5377 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5381 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5393 * Ext JS Library 1.1.1
5394 * Copyright(c) 2006-2007, Ext JS, LLC.
5396 * Originally Released Under LGPL - original licence link has changed is not relivant.
5399 * <script type="text/javascript">
5404 * @class Roo.grid.ColumnModel
5405 * @extends Roo.util.Observable
5406 * This is the default implementation of a ColumnModel used by the Grid. It defines
5407 * the columns in the grid.
5410 var colModel = new Roo.grid.ColumnModel([
5411 {header: "Ticker", width: 60, sortable: true, locked: true},
5412 {header: "Company Name", width: 150, sortable: true},
5413 {header: "Market Cap.", width: 100, sortable: true},
5414 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5415 {header: "Employees", width: 100, sortable: true, resizable: false}
5420 * The config options listed for this class are options which may appear in each
5421 * individual column definition.
5422 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5424 * @param {Object} config An Array of column config objects. See this class's
5425 * config objects for details.
5427 Roo.grid.ColumnModel = function(config){
5429 * The config passed into the constructor
5431 this.config = config;
5434 // if no id, create one
5435 // if the column does not have a dataIndex mapping,
5436 // map it to the order it is in the config
5437 for(var i = 0, len = config.length; i < len; i++){
5439 if(typeof c.dataIndex == "undefined"){
5442 if(typeof c.renderer == "string"){
5443 c.renderer = Roo.util.Format[c.renderer];
5445 if(typeof c.id == "undefined"){
5448 if(c.editor && c.editor.xtype){
5449 c.editor = Roo.factory(c.editor, Roo.grid);
5451 if(c.editor && c.editor.isFormField){
5452 c.editor = new Roo.grid.GridEditor(c.editor);
5454 this.lookup[c.id] = c;
5458 * The width of columns which have no width specified (defaults to 100)
5461 this.defaultWidth = 100;
5464 * Default sortable of columns which have no sortable specified (defaults to false)
5467 this.defaultSortable = false;
5471 * @event widthchange
5472 * Fires when the width of a column changes.
5473 * @param {ColumnModel} this
5474 * @param {Number} columnIndex The column index
5475 * @param {Number} newWidth The new width
5477 "widthchange": true,
5479 * @event headerchange
5480 * Fires when the text of a header changes.
5481 * @param {ColumnModel} this
5482 * @param {Number} columnIndex The column index
5483 * @param {Number} newText The new header text
5485 "headerchange": true,
5487 * @event hiddenchange
5488 * Fires when a column is hidden or "unhidden".
5489 * @param {ColumnModel} this
5490 * @param {Number} columnIndex The column index
5491 * @param {Boolean} hidden true if hidden, false otherwise
5493 "hiddenchange": true,
5495 * @event columnmoved
5496 * Fires when a column is moved.
5497 * @param {ColumnModel} this
5498 * @param {Number} oldIndex
5499 * @param {Number} newIndex
5501 "columnmoved" : true,
5503 * @event columlockchange
5504 * Fires when a column's locked state is changed
5505 * @param {ColumnModel} this
5506 * @param {Number} colIndex
5507 * @param {Boolean} locked true if locked
5509 "columnlockchange" : true
5511 Roo.grid.ColumnModel.superclass.constructor.call(this);
5513 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5515 * @cfg {String} header The header text to display in the Grid view.
5518 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5519 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5520 * specified, the column's index is used as an index into the Record's data Array.
5523 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5524 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5527 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5528 * Defaults to the value of the {@link #defaultSortable} property.
5529 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5532 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5535 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5538 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5541 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5544 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5545 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5546 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5547 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5550 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5553 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5556 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5559 * @cfg {String} cursor (Optional)
5562 * @cfg {String} tooltip (Optional)
5565 * @cfg {Number} xs (Optional)
5568 * @cfg {Number} sm (Optional)
5571 * @cfg {Number} md (Optional)
5574 * @cfg {Number} lg (Optional)
5577 * Returns the id of the column at the specified index.
5578 * @param {Number} index The column index
5579 * @return {String} the id
5581 getColumnId : function(index){
5582 return this.config[index].id;
5586 * Returns the column for a specified id.
5587 * @param {String} id The column id
5588 * @return {Object} the column
5590 getColumnById : function(id){
5591 return this.lookup[id];
5596 * Returns the column for a specified dataIndex.
5597 * @param {String} dataIndex The column dataIndex
5598 * @return {Object|Boolean} the column or false if not found
5600 getColumnByDataIndex: function(dataIndex){
5601 var index = this.findColumnIndex(dataIndex);
5602 return index > -1 ? this.config[index] : false;
5606 * Returns the index for a specified column id.
5607 * @param {String} id The column id
5608 * @return {Number} the index, or -1 if not found
5610 getIndexById : function(id){
5611 for(var i = 0, len = this.config.length; i < len; i++){
5612 if(this.config[i].id == id){
5620 * Returns the index for a specified column dataIndex.
5621 * @param {String} dataIndex The column dataIndex
5622 * @return {Number} the index, or -1 if not found
5625 findColumnIndex : function(dataIndex){
5626 for(var i = 0, len = this.config.length; i < len; i++){
5627 if(this.config[i].dataIndex == dataIndex){
5635 moveColumn : function(oldIndex, newIndex){
5636 var c = this.config[oldIndex];
5637 this.config.splice(oldIndex, 1);
5638 this.config.splice(newIndex, 0, c);
5639 this.dataMap = null;
5640 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5643 isLocked : function(colIndex){
5644 return this.config[colIndex].locked === true;
5647 setLocked : function(colIndex, value, suppressEvent){
5648 if(this.isLocked(colIndex) == value){
5651 this.config[colIndex].locked = value;
5653 this.fireEvent("columnlockchange", this, colIndex, value);
5657 getTotalLockedWidth : function(){
5659 for(var i = 0; i < this.config.length; i++){
5660 if(this.isLocked(i) && !this.isHidden(i)){
5661 this.totalWidth += this.getColumnWidth(i);
5667 getLockedCount : function(){
5668 for(var i = 0, len = this.config.length; i < len; i++){
5669 if(!this.isLocked(i)){
5674 return this.config.length;
5678 * Returns the number of columns.
5681 getColumnCount : function(visibleOnly){
5682 if(visibleOnly === true){
5684 for(var i = 0, len = this.config.length; i < len; i++){
5685 if(!this.isHidden(i)){
5691 return this.config.length;
5695 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5696 * @param {Function} fn
5697 * @param {Object} scope (optional)
5698 * @return {Array} result
5700 getColumnsBy : function(fn, scope){
5702 for(var i = 0, len = this.config.length; i < len; i++){
5703 var c = this.config[i];
5704 if(fn.call(scope||this, c, i) === true){
5712 * Returns true if the specified column is sortable.
5713 * @param {Number} col The column index
5716 isSortable : function(col){
5717 if(typeof this.config[col].sortable == "undefined"){
5718 return this.defaultSortable;
5720 return this.config[col].sortable;
5724 * Returns the rendering (formatting) function defined for the column.
5725 * @param {Number} col The column index.
5726 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5728 getRenderer : function(col){
5729 if(!this.config[col].renderer){
5730 return Roo.grid.ColumnModel.defaultRenderer;
5732 return this.config[col].renderer;
5736 * Sets the rendering (formatting) function for a column.
5737 * @param {Number} col The column index
5738 * @param {Function} fn The function to use to process the cell's raw data
5739 * to return HTML markup for the grid view. The render function is called with
5740 * the following parameters:<ul>
5741 * <li>Data value.</li>
5742 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5743 * <li>css A CSS style string to apply to the table cell.</li>
5744 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5745 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5746 * <li>Row index</li>
5747 * <li>Column index</li>
5748 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5750 setRenderer : function(col, fn){
5751 this.config[col].renderer = fn;
5755 * Returns the width for the specified column.
5756 * @param {Number} col The column index
5759 getColumnWidth : function(col){
5760 return this.config[col].width * 1 || this.defaultWidth;
5764 * Sets the width for a column.
5765 * @param {Number} col The column index
5766 * @param {Number} width The new width
5768 setColumnWidth : function(col, width, suppressEvent){
5769 this.config[col].width = width;
5770 this.totalWidth = null;
5772 this.fireEvent("widthchange", this, col, width);
5777 * Returns the total width of all columns.
5778 * @param {Boolean} includeHidden True to include hidden column widths
5781 getTotalWidth : function(includeHidden){
5782 if(!this.totalWidth){
5783 this.totalWidth = 0;
5784 for(var i = 0, len = this.config.length; i < len; i++){
5785 if(includeHidden || !this.isHidden(i)){
5786 this.totalWidth += this.getColumnWidth(i);
5790 return this.totalWidth;
5794 * Returns the header for the specified column.
5795 * @param {Number} col The column index
5798 getColumnHeader : function(col){
5799 return this.config[col].header;
5803 * Sets the header for a column.
5804 * @param {Number} col The column index
5805 * @param {String} header The new header
5807 setColumnHeader : function(col, header){
5808 this.config[col].header = header;
5809 this.fireEvent("headerchange", this, col, header);
5813 * Returns the tooltip for the specified column.
5814 * @param {Number} col The column index
5817 getColumnTooltip : function(col){
5818 return this.config[col].tooltip;
5821 * Sets the tooltip for a column.
5822 * @param {Number} col The column index
5823 * @param {String} tooltip The new tooltip
5825 setColumnTooltip : function(col, tooltip){
5826 this.config[col].tooltip = tooltip;
5830 * Returns the dataIndex for the specified column.
5831 * @param {Number} col The column index
5834 getDataIndex : function(col){
5835 return this.config[col].dataIndex;
5839 * Sets the dataIndex for a column.
5840 * @param {Number} col The column index
5841 * @param {Number} dataIndex The new dataIndex
5843 setDataIndex : function(col, dataIndex){
5844 this.config[col].dataIndex = dataIndex;
5850 * Returns true if the cell is editable.
5851 * @param {Number} colIndex The column index
5852 * @param {Number} rowIndex The row index - this is nto actually used..?
5855 isCellEditable : function(colIndex, rowIndex){
5856 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5860 * Returns the editor defined for the cell/column.
5861 * return false or null to disable editing.
5862 * @param {Number} colIndex The column index
5863 * @param {Number} rowIndex The row index
5866 getCellEditor : function(colIndex, rowIndex){
5867 return this.config[colIndex].editor;
5871 * Sets if a column is editable.
5872 * @param {Number} col The column index
5873 * @param {Boolean} editable True if the column is editable
5875 setEditable : function(col, editable){
5876 this.config[col].editable = editable;
5881 * Returns true if the column is hidden.
5882 * @param {Number} colIndex The column index
5885 isHidden : function(colIndex){
5886 return this.config[colIndex].hidden;
5891 * Returns true if the column width cannot be changed
5893 isFixed : function(colIndex){
5894 return this.config[colIndex].fixed;
5898 * Returns true if the column can be resized
5901 isResizable : function(colIndex){
5902 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5905 * Sets if a column is hidden.
5906 * @param {Number} colIndex The column index
5907 * @param {Boolean} hidden True if the column is hidden
5909 setHidden : function(colIndex, hidden){
5910 this.config[colIndex].hidden = hidden;
5911 this.totalWidth = null;
5912 this.fireEvent("hiddenchange", this, colIndex, hidden);
5916 * Sets the editor for a column.
5917 * @param {Number} col The column index
5918 * @param {Object} editor The editor object
5920 setEditor : function(col, editor){
5921 this.config[col].editor = editor;
5925 Roo.grid.ColumnModel.defaultRenderer = function(value)
5927 if(typeof value == "object") {
5930 if(typeof value == "string" && value.length < 1){
5934 return String.format("{0}", value);
5937 // Alias for backwards compatibility
5938 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5941 * Ext JS Library 1.1.1
5942 * Copyright(c) 2006-2007, Ext JS, LLC.
5944 * Originally Released Under LGPL - original licence link has changed is not relivant.
5947 * <script type="text/javascript">
5951 * @class Roo.LoadMask
5952 * A simple utility class for generically masking elements while loading data. If the element being masked has
5953 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5954 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5955 * element's UpdateManager load indicator and will be destroyed after the initial load.
5957 * Create a new LoadMask
5958 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5959 * @param {Object} config The config object
5961 Roo.LoadMask = function(el, config){
5962 this.el = Roo.get(el);
5963 Roo.apply(this, config);
5965 this.store.on('beforeload', this.onBeforeLoad, this);
5966 this.store.on('load', this.onLoad, this);
5967 this.store.on('loadexception', this.onLoadException, this);
5968 this.removeMask = false;
5970 var um = this.el.getUpdateManager();
5971 um.showLoadIndicator = false; // disable the default indicator
5972 um.on('beforeupdate', this.onBeforeLoad, this);
5973 um.on('update', this.onLoad, this);
5974 um.on('failure', this.onLoad, this);
5975 this.removeMask = true;
5979 Roo.LoadMask.prototype = {
5981 * @cfg {Boolean} removeMask
5982 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5983 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5987 * The text to display in a centered loading message box (defaults to 'Loading...')
5991 * @cfg {String} msgCls
5992 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5994 msgCls : 'x-mask-loading',
5997 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6003 * Disables the mask to prevent it from being displayed
6005 disable : function(){
6006 this.disabled = true;
6010 * Enables the mask so that it can be displayed
6012 enable : function(){
6013 this.disabled = false;
6016 onLoadException : function()
6020 if (typeof(arguments[3]) != 'undefined') {
6021 Roo.MessageBox.alert("Error loading",arguments[3]);
6025 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6026 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6033 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6038 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6042 onBeforeLoad : function(){
6044 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6049 destroy : function(){
6051 this.store.un('beforeload', this.onBeforeLoad, this);
6052 this.store.un('load', this.onLoad, this);
6053 this.store.un('loadexception', this.onLoadException, this);
6055 var um = this.el.getUpdateManager();
6056 um.un('beforeupdate', this.onBeforeLoad, this);
6057 um.un('update', this.onLoad, this);
6058 um.un('failure', this.onLoad, this);
6069 * @class Roo.bootstrap.Table
6070 * @extends Roo.bootstrap.Component
6071 * Bootstrap Table class
6072 * @cfg {String} cls table class
6073 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6074 * @cfg {String} bgcolor Specifies the background color for a table
6075 * @cfg {Number} border Specifies whether the table cells should have borders or not
6076 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6077 * @cfg {Number} cellspacing Specifies the space between cells
6078 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6079 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6080 * @cfg {String} sortable Specifies that the table should be sortable
6081 * @cfg {String} summary Specifies a summary of the content of a table
6082 * @cfg {Number} width Specifies the width of a table
6083 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6085 * @cfg {boolean} striped Should the rows be alternative striped
6086 * @cfg {boolean} bordered Add borders to the table
6087 * @cfg {boolean} hover Add hover highlighting
6088 * @cfg {boolean} condensed Format condensed
6089 * @cfg {boolean} responsive Format condensed
6090 * @cfg {Boolean} loadMask (true|false) default false
6091 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6092 * @cfg {Boolean} headerShow (true|false) generate thead, default true
6093 * @cfg {Boolean} rowSelection (true|false) default false
6094 * @cfg {Boolean} cellSelection (true|false) default false
6095 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6096 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
6097 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
6098 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6102 * Create a new Table
6103 * @param {Object} config The config object
6106 Roo.bootstrap.Table = function(config){
6107 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6112 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6113 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6114 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6115 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6117 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6119 this.sm.grid = this;
6120 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6121 this.sm = this.selModel;
6122 this.sm.xmodule = this.xmodule || false;
6125 if (this.cm && typeof(this.cm.config) == 'undefined') {
6126 this.colModel = new Roo.grid.ColumnModel(this.cm);
6127 this.cm = this.colModel;
6128 this.cm.xmodule = this.xmodule || false;
6131 this.store= Roo.factory(this.store, Roo.data);
6132 this.ds = this.store;
6133 this.ds.xmodule = this.xmodule || false;
6136 if (this.footer && this.store) {
6137 this.footer.dataSource = this.ds;
6138 this.footer = Roo.factory(this.footer);
6145 * Fires when a cell is clicked
6146 * @param {Roo.bootstrap.Table} this
6147 * @param {Roo.Element} el
6148 * @param {Number} rowIndex
6149 * @param {Number} columnIndex
6150 * @param {Roo.EventObject} e
6154 * @event celldblclick
6155 * Fires when a cell is double clicked
6156 * @param {Roo.bootstrap.Table} this
6157 * @param {Roo.Element} el
6158 * @param {Number} rowIndex
6159 * @param {Number} columnIndex
6160 * @param {Roo.EventObject} e
6162 "celldblclick" : true,
6165 * Fires when a row is clicked
6166 * @param {Roo.bootstrap.Table} this
6167 * @param {Roo.Element} el
6168 * @param {Number} rowIndex
6169 * @param {Roo.EventObject} e
6173 * @event rowdblclick
6174 * Fires when a row is double clicked
6175 * @param {Roo.bootstrap.Table} this
6176 * @param {Roo.Element} el
6177 * @param {Number} rowIndex
6178 * @param {Roo.EventObject} e
6180 "rowdblclick" : true,
6183 * Fires when a mouseover occur
6184 * @param {Roo.bootstrap.Table} this
6185 * @param {Roo.Element} el
6186 * @param {Number} rowIndex
6187 * @param {Number} columnIndex
6188 * @param {Roo.EventObject} e
6193 * Fires when a mouseout occur
6194 * @param {Roo.bootstrap.Table} this
6195 * @param {Roo.Element} el
6196 * @param {Number} rowIndex
6197 * @param {Number} columnIndex
6198 * @param {Roo.EventObject} e
6203 * Fires when a row is rendered, so you can change add a style to it.
6204 * @param {Roo.bootstrap.Table} this
6205 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6209 * @event rowsrendered
6210 * Fires when all the rows have been rendered
6211 * @param {Roo.bootstrap.Table} this
6213 'rowsrendered' : true,
6215 * @event contextmenu
6216 * The raw contextmenu event for the entire grid.
6217 * @param {Roo.EventObject} e
6219 "contextmenu" : true,
6221 * @event rowcontextmenu
6222 * Fires when a row is right clicked
6223 * @param {Roo.bootstrap.Table} this
6224 * @param {Number} rowIndex
6225 * @param {Roo.EventObject} e
6227 "rowcontextmenu" : true,
6229 * @event cellcontextmenu
6230 * Fires when a cell is right clicked
6231 * @param {Roo.bootstrap.Table} this
6232 * @param {Number} rowIndex
6233 * @param {Number} cellIndex
6234 * @param {Roo.EventObject} e
6236 "cellcontextmenu" : true,
6238 * @event headercontextmenu
6239 * Fires when a header is right clicked
6240 * @param {Roo.bootstrap.Table} this
6241 * @param {Number} columnIndex
6242 * @param {Roo.EventObject} e
6244 "headercontextmenu" : true
6248 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6274 rowSelection : false,
6275 cellSelection : false,
6278 // Roo.Element - the tbody
6280 // Roo.Element - thead element
6283 container: false, // used by gridpanel...
6289 auto_hide_footer : false,
6291 getAutoCreate : function()
6293 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6300 if (this.scrollBody) {
6301 cfg.cls += ' table-body-fixed';
6304 cfg.cls += ' table-striped';
6308 cfg.cls += ' table-hover';
6310 if (this.bordered) {
6311 cfg.cls += ' table-bordered';
6313 if (this.condensed) {
6314 cfg.cls += ' table-condensed';
6316 if (this.responsive) {
6317 cfg.cls += ' table-responsive';
6321 cfg.cls+= ' ' +this.cls;
6324 // this lot should be simplifed...
6337 ].forEach(function(k) {
6345 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6348 if(this.store || this.cm){
6349 if(this.headerShow){
6350 cfg.cn.push(this.renderHeader());
6353 cfg.cn.push(this.renderBody());
6355 if(this.footerShow){
6356 cfg.cn.push(this.renderFooter());
6358 // where does this come from?
6359 //cfg.cls+= ' TableGrid';
6362 return { cn : [ cfg ] };
6365 initEvents : function()
6367 if(!this.store || !this.cm){
6370 if (this.selModel) {
6371 this.selModel.initEvents();
6375 //Roo.log('initEvents with ds!!!!');
6377 this.mainBody = this.el.select('tbody', true).first();
6378 this.mainHead = this.el.select('thead', true).first();
6379 this.mainFoot = this.el.select('tfoot', true).first();
6385 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6386 e.on('click', _this.sort, _this);
6389 this.mainBody.on("click", this.onClick, this);
6390 this.mainBody.on("dblclick", this.onDblClick, this);
6392 // why is this done????? = it breaks dialogs??
6393 //this.parent().el.setStyle('position', 'relative');
6397 this.footer.parentId = this.id;
6398 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6401 this.el.select('tfoot tr td').first().addClass('hide');
6406 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6409 this.store.on('load', this.onLoad, this);
6410 this.store.on('beforeload', this.onBeforeLoad, this);
6411 this.store.on('update', this.onUpdate, this);
6412 this.store.on('add', this.onAdd, this);
6413 this.store.on("clear", this.clear, this);
6415 this.el.on("contextmenu", this.onContextMenu, this);
6417 this.mainBody.on('scroll', this.onBodyScroll, this);
6419 this.cm.on("headerchange", this.onHeaderChange, this);
6421 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6425 onContextMenu : function(e, t)
6427 this.processEvent("contextmenu", e);
6430 processEvent : function(name, e)
6432 if (name != 'touchstart' ) {
6433 this.fireEvent(name, e);
6436 var t = e.getTarget();
6438 var cell = Roo.get(t);
6444 if(cell.findParent('tfoot', false, true)){
6448 if(cell.findParent('thead', false, true)){
6450 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6451 cell = Roo.get(t).findParent('th', false, true);
6453 Roo.log("failed to find th in thead?");
6454 Roo.log(e.getTarget());
6459 var cellIndex = cell.dom.cellIndex;
6461 var ename = name == 'touchstart' ? 'click' : name;
6462 this.fireEvent("header" + ename, this, cellIndex, e);
6467 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6468 cell = Roo.get(t).findParent('td', false, true);
6470 Roo.log("failed to find th in tbody?");
6471 Roo.log(e.getTarget());
6476 var row = cell.findParent('tr', false, true);
6477 var cellIndex = cell.dom.cellIndex;
6478 var rowIndex = row.dom.rowIndex - 1;
6482 this.fireEvent("row" + name, this, rowIndex, e);
6486 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6492 onMouseover : function(e, el)
6494 var cell = Roo.get(el);
6500 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6501 cell = cell.findParent('td', false, true);
6504 var row = cell.findParent('tr', false, true);
6505 var cellIndex = cell.dom.cellIndex;
6506 var rowIndex = row.dom.rowIndex - 1; // start from 0
6508 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6512 onMouseout : function(e, el)
6514 var cell = Roo.get(el);
6520 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6521 cell = cell.findParent('td', false, true);
6524 var row = cell.findParent('tr', false, true);
6525 var cellIndex = cell.dom.cellIndex;
6526 var rowIndex = row.dom.rowIndex - 1; // start from 0
6528 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6532 onClick : function(e, el)
6534 var cell = Roo.get(el);
6536 if(!cell || (!this.cellSelection && !this.rowSelection)){
6540 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6541 cell = cell.findParent('td', false, true);
6544 if(!cell || typeof(cell) == 'undefined'){
6548 var row = cell.findParent('tr', false, true);
6550 if(!row || typeof(row) == 'undefined'){
6554 var cellIndex = cell.dom.cellIndex;
6555 var rowIndex = this.getRowIndex(row);
6557 // why??? - should these not be based on SelectionModel?
6558 if(this.cellSelection){
6559 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6562 if(this.rowSelection){
6563 this.fireEvent('rowclick', this, row, rowIndex, e);
6569 onDblClick : function(e,el)
6571 var cell = Roo.get(el);
6573 if(!cell || (!this.cellSelection && !this.rowSelection)){
6577 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6578 cell = cell.findParent('td', false, true);
6581 if(!cell || typeof(cell) == 'undefined'){
6585 var row = cell.findParent('tr', false, true);
6587 if(!row || typeof(row) == 'undefined'){
6591 var cellIndex = cell.dom.cellIndex;
6592 var rowIndex = this.getRowIndex(row);
6594 if(this.cellSelection){
6595 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6598 if(this.rowSelection){
6599 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6603 sort : function(e,el)
6605 var col = Roo.get(el);
6607 if(!col.hasClass('sortable')){
6611 var sort = col.attr('sort');
6614 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6618 this.store.sortInfo = {field : sort, direction : dir};
6621 Roo.log("calling footer first");
6622 this.footer.onClick('first');
6625 this.store.load({ params : { start : 0 } });
6629 renderHeader : function()
6637 this.totalWidth = 0;
6639 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6641 var config = cm.config[i];
6645 cls : 'x-hcol-' + i,
6647 html: cm.getColumnHeader(i)
6652 if(typeof(config.sortable) != 'undefined' && config.sortable){
6654 c.html = '<i class="glyphicon"></i>' + c.html;
6657 if(typeof(config.lgHeader) != 'undefined'){
6658 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6661 if(typeof(config.mdHeader) != 'undefined'){
6662 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6665 if(typeof(config.smHeader) != 'undefined'){
6666 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6669 if(typeof(config.xsHeader) != 'undefined'){
6670 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6677 if(typeof(config.tooltip) != 'undefined'){
6678 c.tooltip = config.tooltip;
6681 if(typeof(config.colspan) != 'undefined'){
6682 c.colspan = config.colspan;
6685 if(typeof(config.hidden) != 'undefined' && config.hidden){
6686 c.style += ' display:none;';
6689 if(typeof(config.dataIndex) != 'undefined'){
6690 c.sort = config.dataIndex;
6695 if(typeof(config.align) != 'undefined' && config.align.length){
6696 c.style += ' text-align:' + config.align + ';';
6699 if(typeof(config.width) != 'undefined'){
6700 c.style += ' width:' + config.width + 'px;';
6701 this.totalWidth += config.width;
6703 this.totalWidth += 100; // assume minimum of 100 per column?
6706 if(typeof(config.cls) != 'undefined'){
6707 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6710 ['xs','sm','md','lg'].map(function(size){
6712 if(typeof(config[size]) == 'undefined'){
6716 if (!config[size]) { // 0 = hidden
6717 c.cls += ' hidden-' + size;
6721 c.cls += ' col-' + size + '-' + config[size];
6731 renderBody : function()
6741 colspan : this.cm.getColumnCount()
6751 renderFooter : function()
6761 colspan : this.cm.getColumnCount()
6775 // Roo.log('ds onload');
6780 var ds = this.store;
6782 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6783 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6784 if (_this.store.sortInfo) {
6786 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6787 e.select('i', true).addClass(['glyphicon-arrow-up']);
6790 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6791 e.select('i', true).addClass(['glyphicon-arrow-down']);
6796 var tbody = this.mainBody;
6798 if(ds.getCount() > 0){
6799 ds.data.each(function(d,rowIndex){
6800 var row = this.renderRow(cm, ds, rowIndex);
6802 tbody.createChild(row);
6806 if(row.cellObjects.length){
6807 Roo.each(row.cellObjects, function(r){
6808 _this.renderCellObject(r);
6815 var tfoot = this.el.select('tfoot', true).first();
6817 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6819 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6821 var total = this.ds.getTotalCount();
6823 if(this.footer.pageSize < total){
6824 this.mainFoot.show();
6828 Roo.each(this.el.select('tbody td', true).elements, function(e){
6829 e.on('mouseover', _this.onMouseover, _this);
6832 Roo.each(this.el.select('tbody td', true).elements, function(e){
6833 e.on('mouseout', _this.onMouseout, _this);
6835 this.fireEvent('rowsrendered', this);
6841 onUpdate : function(ds,record)
6843 this.refreshRow(record);
6847 onRemove : function(ds, record, index, isUpdate){
6848 if(isUpdate !== true){
6849 this.fireEvent("beforerowremoved", this, index, record);
6851 var bt = this.mainBody.dom;
6853 var rows = this.el.select('tbody > tr', true).elements;
6855 if(typeof(rows[index]) != 'undefined'){
6856 bt.removeChild(rows[index].dom);
6859 // if(bt.rows[index]){
6860 // bt.removeChild(bt.rows[index]);
6863 if(isUpdate !== true){
6864 //this.stripeRows(index);
6865 //this.syncRowHeights(index, index);
6867 this.fireEvent("rowremoved", this, index, record);
6871 onAdd : function(ds, records, rowIndex)
6873 //Roo.log('on Add called');
6874 // - note this does not handle multiple adding very well..
6875 var bt = this.mainBody.dom;
6876 for (var i =0 ; i < records.length;i++) {
6877 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6878 //Roo.log(records[i]);
6879 //Roo.log(this.store.getAt(rowIndex+i));
6880 this.insertRow(this.store, rowIndex + i, false);
6887 refreshRow : function(record){
6888 var ds = this.store, index;
6889 if(typeof record == 'number'){
6891 record = ds.getAt(index);
6893 index = ds.indexOf(record);
6895 this.insertRow(ds, index, true);
6897 this.onRemove(ds, record, index+1, true);
6899 //this.syncRowHeights(index, index);
6901 this.fireEvent("rowupdated", this, index, record);
6904 insertRow : function(dm, rowIndex, isUpdate){
6907 this.fireEvent("beforerowsinserted", this, rowIndex);
6909 //var s = this.getScrollState();
6910 var row = this.renderRow(this.cm, this.store, rowIndex);
6911 // insert before rowIndex..
6912 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6916 if(row.cellObjects.length){
6917 Roo.each(row.cellObjects, function(r){
6918 _this.renderCellObject(r);
6923 this.fireEvent("rowsinserted", this, rowIndex);
6924 //this.syncRowHeights(firstRow, lastRow);
6925 //this.stripeRows(firstRow);
6932 getRowDom : function(rowIndex)
6934 var rows = this.el.select('tbody > tr', true).elements;
6936 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6939 // returns the object tree for a tr..
6942 renderRow : function(cm, ds, rowIndex)
6944 var d = ds.getAt(rowIndex);
6948 cls : 'x-row-' + rowIndex,
6952 var cellObjects = [];
6954 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6955 var config = cm.config[i];
6957 var renderer = cm.getRenderer(i);
6961 if(typeof(renderer) !== 'undefined'){
6962 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6964 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6965 // and are rendered into the cells after the row is rendered - using the id for the element.
6967 if(typeof(value) === 'object'){
6977 rowIndex : rowIndex,
6982 this.fireEvent('rowclass', this, rowcfg);
6986 cls : rowcfg.rowClass + ' x-col-' + i,
6988 html: (typeof(value) === 'object') ? '' : value
6995 if(typeof(config.colspan) != 'undefined'){
6996 td.colspan = config.colspan;
6999 if(typeof(config.hidden) != 'undefined' && config.hidden){
7000 td.style += ' display:none;';
7003 if(typeof(config.align) != 'undefined' && config.align.length){
7004 td.style += ' text-align:' + config.align + ';';
7006 if(typeof(config.valign) != 'undefined' && config.valign.length){
7007 td.style += ' vertical-align:' + config.valign + ';';
7010 if(typeof(config.width) != 'undefined'){
7011 td.style += ' width:' + config.width + 'px;';
7014 if(typeof(config.cursor) != 'undefined'){
7015 td.style += ' cursor:' + config.cursor + ';';
7018 if(typeof(config.cls) != 'undefined'){
7019 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7022 ['xs','sm','md','lg'].map(function(size){
7024 if(typeof(config[size]) == 'undefined'){
7028 if (!config[size]) { // 0 = hidden
7029 td.cls += ' hidden-' + size;
7033 td.cls += ' col-' + size + '-' + config[size];
7041 row.cellObjects = cellObjects;
7049 onBeforeLoad : function()
7058 this.el.select('tbody', true).first().dom.innerHTML = '';
7061 * Show or hide a row.
7062 * @param {Number} rowIndex to show or hide
7063 * @param {Boolean} state hide
7065 setRowVisibility : function(rowIndex, state)
7067 var bt = this.mainBody.dom;
7069 var rows = this.el.select('tbody > tr', true).elements;
7071 if(typeof(rows[rowIndex]) == 'undefined'){
7074 rows[rowIndex].dom.style.display = state ? '' : 'none';
7078 getSelectionModel : function(){
7080 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7082 return this.selModel;
7085 * Render the Roo.bootstrap object from renderder
7087 renderCellObject : function(r)
7091 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7093 var t = r.cfg.render(r.container);
7096 Roo.each(r.cfg.cn, function(c){
7098 container: t.getChildContainer(),
7101 _this.renderCellObject(child);
7106 getRowIndex : function(row)
7110 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7121 * Returns the grid's underlying element = used by panel.Grid
7122 * @return {Element} The element
7124 getGridEl : function(){
7128 * Forces a resize - used by panel.Grid
7129 * @return {Element} The element
7131 autoSize : function()
7133 //var ctr = Roo.get(this.container.dom.parentElement);
7134 var ctr = Roo.get(this.el.dom);
7136 var thd = this.getGridEl().select('thead',true).first();
7137 var tbd = this.getGridEl().select('tbody', true).first();
7138 var tfd = this.getGridEl().select('tfoot', true).first();
7140 var cw = ctr.getWidth();
7144 tbd.setSize(ctr.getWidth(),
7145 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7147 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7150 cw = Math.max(cw, this.totalWidth);
7151 this.getGridEl().select('tr',true).setWidth(cw);
7152 // resize 'expandable coloumn?
7154 return; // we doe not have a view in this design..
7157 onBodyScroll: function()
7159 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7161 this.mainHead.setStyle({
7162 'position' : 'relative',
7163 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7169 var scrollHeight = this.mainBody.dom.scrollHeight;
7171 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7173 var height = this.mainBody.getHeight();
7175 if(scrollHeight - height == scrollTop) {
7177 var total = this.ds.getTotalCount();
7179 if(this.footer.cursor + this.footer.pageSize < total){
7181 this.footer.ds.load({
7183 start : this.footer.cursor + this.footer.pageSize,
7184 limit : this.footer.pageSize
7194 onHeaderChange : function()
7196 var header = this.renderHeader();
7197 var table = this.el.select('table', true).first();
7199 this.mainHead.remove();
7200 this.mainHead = table.createChild(header, this.mainBody, false);
7203 onHiddenChange : function(colModel, colIndex, hidden)
7205 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7206 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7208 this.CSS.updateRule(thSelector, "display", "");
7209 this.CSS.updateRule(tdSelector, "display", "");
7212 this.CSS.updateRule(thSelector, "display", "none");
7213 this.CSS.updateRule(tdSelector, "display", "none");
7216 this.onHeaderChange();
7220 setColumnWidth: function(col_index, width)
7222 // width = "md-2 xs-2..."
7223 if(!this.colModel.config[col_index]) {
7227 var w = width.split(" ");
7229 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7231 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7234 for(var j = 0; j < w.length; j++) {
7240 var size_cls = w[j].split("-");
7242 if(!Number.isInteger(size_cls[1] * 1)) {
7246 if(!this.colModel.config[col_index][size_cls[0]]) {
7250 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7254 h_row[0].classList.replace(
7255 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7256 "col-"+size_cls[0]+"-"+size_cls[1]
7259 for(var i = 0; i < rows.length; i++) {
7261 var size_cls = w[j].split("-");
7263 if(!Number.isInteger(size_cls[1] * 1)) {
7267 if(!this.colModel.config[col_index][size_cls[0]]) {
7271 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7275 rows[i].classList.replace(
7276 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7277 "col-"+size_cls[0]+"-"+size_cls[1]
7281 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7296 * @class Roo.bootstrap.TableCell
7297 * @extends Roo.bootstrap.Component
7298 * Bootstrap TableCell class
7299 * @cfg {String} html cell contain text
7300 * @cfg {String} cls cell class
7301 * @cfg {String} tag cell tag (td|th) default td
7302 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7303 * @cfg {String} align Aligns the content in a cell
7304 * @cfg {String} axis Categorizes cells
7305 * @cfg {String} bgcolor Specifies the background color of a cell
7306 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7307 * @cfg {Number} colspan Specifies the number of columns a cell should span
7308 * @cfg {String} headers Specifies one or more header cells a cell is related to
7309 * @cfg {Number} height Sets the height of a cell
7310 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7311 * @cfg {Number} rowspan Sets the number of rows a cell should span
7312 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7313 * @cfg {String} valign Vertical aligns the content in a cell
7314 * @cfg {Number} width Specifies the width of a cell
7317 * Create a new TableCell
7318 * @param {Object} config The config object
7321 Roo.bootstrap.TableCell = function(config){
7322 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7325 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7345 getAutoCreate : function(){
7346 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7366 cfg.align=this.align
7372 cfg.bgcolor=this.bgcolor
7375 cfg.charoff=this.charoff
7378 cfg.colspan=this.colspan
7381 cfg.headers=this.headers
7384 cfg.height=this.height
7387 cfg.nowrap=this.nowrap
7390 cfg.rowspan=this.rowspan
7393 cfg.scope=this.scope
7396 cfg.valign=this.valign
7399 cfg.width=this.width
7418 * @class Roo.bootstrap.TableRow
7419 * @extends Roo.bootstrap.Component
7420 * Bootstrap TableRow class
7421 * @cfg {String} cls row class
7422 * @cfg {String} align Aligns the content in a table row
7423 * @cfg {String} bgcolor Specifies a background color for a table row
7424 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7425 * @cfg {String} valign Vertical aligns the content in a table row
7428 * Create a new TableRow
7429 * @param {Object} config The config object
7432 Roo.bootstrap.TableRow = function(config){
7433 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7436 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7444 getAutoCreate : function(){
7445 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7455 cfg.align = this.align;
7458 cfg.bgcolor = this.bgcolor;
7461 cfg.charoff = this.charoff;
7464 cfg.valign = this.valign;
7482 * @class Roo.bootstrap.TableBody
7483 * @extends Roo.bootstrap.Component
7484 * Bootstrap TableBody class
7485 * @cfg {String} cls element class
7486 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7487 * @cfg {String} align Aligns the content inside the element
7488 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7489 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7492 * Create a new TableBody
7493 * @param {Object} config The config object
7496 Roo.bootstrap.TableBody = function(config){
7497 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7500 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7508 getAutoCreate : function(){
7509 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7523 cfg.align = this.align;
7526 cfg.charoff = this.charoff;
7529 cfg.valign = this.valign;
7536 // initEvents : function()
7543 // this.store = Roo.factory(this.store, Roo.data);
7544 // this.store.on('load', this.onLoad, this);
7546 // this.store.load();
7550 // onLoad: function ()
7552 // this.fireEvent('load', this);
7562 * Ext JS Library 1.1.1
7563 * Copyright(c) 2006-2007, Ext JS, LLC.
7565 * Originally Released Under LGPL - original licence link has changed is not relivant.
7568 * <script type="text/javascript">
7571 // as we use this in bootstrap.
7572 Roo.namespace('Roo.form');
7574 * @class Roo.form.Action
7575 * Internal Class used to handle form actions
7577 * @param {Roo.form.BasicForm} el The form element or its id
7578 * @param {Object} config Configuration options
7583 // define the action interface
7584 Roo.form.Action = function(form, options){
7586 this.options = options || {};
7589 * Client Validation Failed
7592 Roo.form.Action.CLIENT_INVALID = 'client';
7594 * Server Validation Failed
7597 Roo.form.Action.SERVER_INVALID = 'server';
7599 * Connect to Server Failed
7602 Roo.form.Action.CONNECT_FAILURE = 'connect';
7604 * Reading Data from Server Failed
7607 Roo.form.Action.LOAD_FAILURE = 'load';
7609 Roo.form.Action.prototype = {
7611 failureType : undefined,
7612 response : undefined,
7616 run : function(options){
7621 success : function(response){
7626 handleResponse : function(response){
7630 // default connection failure
7631 failure : function(response){
7633 this.response = response;
7634 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7635 this.form.afterAction(this, false);
7638 processResponse : function(response){
7639 this.response = response;
7640 if(!response.responseText){
7643 this.result = this.handleResponse(response);
7647 // utility functions used internally
7648 getUrl : function(appendParams){
7649 var url = this.options.url || this.form.url || this.form.el.dom.action;
7651 var p = this.getParams();
7653 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7659 getMethod : function(){
7660 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7663 getParams : function(){
7664 var bp = this.form.baseParams;
7665 var p = this.options.params;
7667 if(typeof p == "object"){
7668 p = Roo.urlEncode(Roo.applyIf(p, bp));
7669 }else if(typeof p == 'string' && bp){
7670 p += '&' + Roo.urlEncode(bp);
7673 p = Roo.urlEncode(bp);
7678 createCallback : function(){
7680 success: this.success,
7681 failure: this.failure,
7683 timeout: (this.form.timeout*1000),
7684 upload: this.form.fileUpload ? this.success : undefined
7689 Roo.form.Action.Submit = function(form, options){
7690 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7693 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7696 haveProgress : false,
7697 uploadComplete : false,
7699 // uploadProgress indicator.
7700 uploadProgress : function()
7702 if (!this.form.progressUrl) {
7706 if (!this.haveProgress) {
7707 Roo.MessageBox.progress("Uploading", "Uploading");
7709 if (this.uploadComplete) {
7710 Roo.MessageBox.hide();
7714 this.haveProgress = true;
7716 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7718 var c = new Roo.data.Connection();
7720 url : this.form.progressUrl,
7725 success : function(req){
7726 //console.log(data);
7730 rdata = Roo.decode(req.responseText)
7732 Roo.log("Invalid data from server..");
7736 if (!rdata || !rdata.success) {
7738 Roo.MessageBox.alert(Roo.encode(rdata));
7741 var data = rdata.data;
7743 if (this.uploadComplete) {
7744 Roo.MessageBox.hide();
7749 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7750 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7753 this.uploadProgress.defer(2000,this);
7756 failure: function(data) {
7757 Roo.log('progress url failed ');
7768 // run get Values on the form, so it syncs any secondary forms.
7769 this.form.getValues();
7771 var o = this.options;
7772 var method = this.getMethod();
7773 var isPost = method == 'POST';
7774 if(o.clientValidation === false || this.form.isValid()){
7776 if (this.form.progressUrl) {
7777 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7778 (new Date() * 1) + '' + Math.random());
7783 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7784 form:this.form.el.dom,
7785 url:this.getUrl(!isPost),
7787 params:isPost ? this.getParams() : null,
7788 isUpload: this.form.fileUpload
7791 this.uploadProgress();
7793 }else if (o.clientValidation !== false){ // client validation failed
7794 this.failureType = Roo.form.Action.CLIENT_INVALID;
7795 this.form.afterAction(this, false);
7799 success : function(response)
7801 this.uploadComplete= true;
7802 if (this.haveProgress) {
7803 Roo.MessageBox.hide();
7807 var result = this.processResponse(response);
7808 if(result === true || result.success){
7809 this.form.afterAction(this, true);
7813 this.form.markInvalid(result.errors);
7814 this.failureType = Roo.form.Action.SERVER_INVALID;
7816 this.form.afterAction(this, false);
7818 failure : function(response)
7820 this.uploadComplete= true;
7821 if (this.haveProgress) {
7822 Roo.MessageBox.hide();
7825 this.response = response;
7826 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7827 this.form.afterAction(this, false);
7830 handleResponse : function(response){
7831 if(this.form.errorReader){
7832 var rs = this.form.errorReader.read(response);
7835 for(var i = 0, len = rs.records.length; i < len; i++) {
7836 var r = rs.records[i];
7840 if(errors.length < 1){
7844 success : rs.success,
7850 ret = Roo.decode(response.responseText);
7854 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7864 Roo.form.Action.Load = function(form, options){
7865 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7866 this.reader = this.form.reader;
7869 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7874 Roo.Ajax.request(Roo.apply(
7875 this.createCallback(), {
7876 method:this.getMethod(),
7877 url:this.getUrl(false),
7878 params:this.getParams()
7882 success : function(response){
7884 var result = this.processResponse(response);
7885 if(result === true || !result.success || !result.data){
7886 this.failureType = Roo.form.Action.LOAD_FAILURE;
7887 this.form.afterAction(this, false);
7890 this.form.clearInvalid();
7891 this.form.setValues(result.data);
7892 this.form.afterAction(this, true);
7895 handleResponse : function(response){
7896 if(this.form.reader){
7897 var rs = this.form.reader.read(response);
7898 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7900 success : rs.success,
7904 return Roo.decode(response.responseText);
7908 Roo.form.Action.ACTION_TYPES = {
7909 'load' : Roo.form.Action.Load,
7910 'submit' : Roo.form.Action.Submit
7919 * @class Roo.bootstrap.Form
7920 * @extends Roo.bootstrap.Component
7921 * Bootstrap Form class
7922 * @cfg {String} method GET | POST (default POST)
7923 * @cfg {String} labelAlign top | left (default top)
7924 * @cfg {String} align left | right - for navbars
7925 * @cfg {Boolean} loadMask load mask when submit (default true)
7930 * @param {Object} config The config object
7934 Roo.bootstrap.Form = function(config){
7936 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7938 Roo.bootstrap.Form.popover.apply();
7942 * @event clientvalidation
7943 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7944 * @param {Form} this
7945 * @param {Boolean} valid true if the form has passed client-side validation
7947 clientvalidation: true,
7949 * @event beforeaction
7950 * Fires before any action is performed. Return false to cancel the action.
7951 * @param {Form} this
7952 * @param {Action} action The action to be performed
7956 * @event actionfailed
7957 * Fires when an action fails.
7958 * @param {Form} this
7959 * @param {Action} action The action that failed
7961 actionfailed : true,
7963 * @event actioncomplete
7964 * Fires when an action is completed.
7965 * @param {Form} this
7966 * @param {Action} action The action that completed
7968 actioncomplete : true
7972 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7975 * @cfg {String} method
7976 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7981 * The URL to use for form actions if one isn't supplied in the action options.
7984 * @cfg {Boolean} fileUpload
7985 * Set to true if this form is a file upload.
7989 * @cfg {Object} baseParams
7990 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7994 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7998 * @cfg {Sting} align (left|right) for navbar forms
8003 activeAction : null,
8006 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8007 * element by passing it or its id or mask the form itself by passing in true.
8010 waitMsgTarget : false,
8015 * @cfg {Boolean} errorMask (true|false) default false
8020 * @cfg {Number} maskOffset Default 100
8025 * @cfg {Boolean} maskBody
8029 getAutoCreate : function(){
8033 method : this.method || 'POST',
8034 id : this.id || Roo.id(),
8037 if (this.parent().xtype.match(/^Nav/)) {
8038 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8042 if (this.labelAlign == 'left' ) {
8043 cfg.cls += ' form-horizontal';
8049 initEvents : function()
8051 this.el.on('submit', this.onSubmit, this);
8052 // this was added as random key presses on the form where triggering form submit.
8053 this.el.on('keypress', function(e) {
8054 if (e.getCharCode() != 13) {
8057 // we might need to allow it for textareas.. and some other items.
8058 // check e.getTarget().
8060 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8064 Roo.log("keypress blocked");
8072 onSubmit : function(e){
8077 * Returns true if client-side validation on the form is successful.
8080 isValid : function(){
8081 var items = this.getItems();
8085 items.each(function(f){
8091 Roo.log('invalid field: ' + f.name);
8095 if(!target && f.el.isVisible(true)){
8101 if(this.errorMask && !valid){
8102 Roo.bootstrap.Form.popover.mask(this, target);
8109 * Returns true if any fields in this form have changed since their original load.
8112 isDirty : function(){
8114 var items = this.getItems();
8115 items.each(function(f){
8125 * Performs a predefined action (submit or load) or custom actions you define on this form.
8126 * @param {String} actionName The name of the action type
8127 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8128 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8129 * accept other config options):
8131 Property Type Description
8132 ---------------- --------------- ----------------------------------------------------------------------------------
8133 url String The url for the action (defaults to the form's url)
8134 method String The form method to use (defaults to the form's method, or POST if not defined)
8135 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8136 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8137 validate the form on the client (defaults to false)
8139 * @return {BasicForm} this
8141 doAction : function(action, options){
8142 if(typeof action == 'string'){
8143 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8145 if(this.fireEvent('beforeaction', this, action) !== false){
8146 this.beforeAction(action);
8147 action.run.defer(100, action);
8153 beforeAction : function(action){
8154 var o = action.options;
8159 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8161 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8164 // not really supported yet.. ??
8166 //if(this.waitMsgTarget === true){
8167 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8168 //}else if(this.waitMsgTarget){
8169 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8170 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8172 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8178 afterAction : function(action, success){
8179 this.activeAction = null;
8180 var o = action.options;
8185 Roo.get(document.body).unmask();
8191 //if(this.waitMsgTarget === true){
8192 // this.el.unmask();
8193 //}else if(this.waitMsgTarget){
8194 // this.waitMsgTarget.unmask();
8196 // Roo.MessageBox.updateProgress(1);
8197 // Roo.MessageBox.hide();
8204 Roo.callback(o.success, o.scope, [this, action]);
8205 this.fireEvent('actioncomplete', this, action);
8209 // failure condition..
8210 // we have a scenario where updates need confirming.
8211 // eg. if a locking scenario exists..
8212 // we look for { errors : { needs_confirm : true }} in the response.
8214 (typeof(action.result) != 'undefined') &&
8215 (typeof(action.result.errors) != 'undefined') &&
8216 (typeof(action.result.errors.needs_confirm) != 'undefined')
8219 Roo.log("not supported yet");
8222 Roo.MessageBox.confirm(
8223 "Change requires confirmation",
8224 action.result.errorMsg,
8229 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8239 Roo.callback(o.failure, o.scope, [this, action]);
8240 // show an error message if no failed handler is set..
8241 if (!this.hasListener('actionfailed')) {
8242 Roo.log("need to add dialog support");
8244 Roo.MessageBox.alert("Error",
8245 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8246 action.result.errorMsg :
8247 "Saving Failed, please check your entries or try again"
8252 this.fireEvent('actionfailed', this, action);
8257 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8258 * @param {String} id The value to search for
8261 findField : function(id){
8262 var items = this.getItems();
8263 var field = items.get(id);
8265 items.each(function(f){
8266 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8273 return field || null;
8276 * Mark fields in this form invalid in bulk.
8277 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8278 * @return {BasicForm} this
8280 markInvalid : function(errors){
8281 if(errors instanceof Array){
8282 for(var i = 0, len = errors.length; i < len; i++){
8283 var fieldError = errors[i];
8284 var f = this.findField(fieldError.id);
8286 f.markInvalid(fieldError.msg);
8292 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8293 field.markInvalid(errors[id]);
8297 //Roo.each(this.childForms || [], function (f) {
8298 // f.markInvalid(errors);
8305 * Set values for fields in this form in bulk.
8306 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8307 * @return {BasicForm} this
8309 setValues : function(values){
8310 if(values instanceof Array){ // array of objects
8311 for(var i = 0, len = values.length; i < len; i++){
8313 var f = this.findField(v.id);
8315 f.setValue(v.value);
8316 if(this.trackResetOnLoad){
8317 f.originalValue = f.getValue();
8321 }else{ // object hash
8324 if(typeof values[id] != 'function' && (field = this.findField(id))){
8326 if (field.setFromData &&
8328 field.displayField &&
8329 // combos' with local stores can
8330 // be queried via setValue()
8331 // to set their value..
8332 (field.store && !field.store.isLocal)
8336 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8337 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8338 field.setFromData(sd);
8340 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8342 field.setFromData(values);
8345 field.setValue(values[id]);
8349 if(this.trackResetOnLoad){
8350 field.originalValue = field.getValue();
8356 //Roo.each(this.childForms || [], function (f) {
8357 // f.setValues(values);
8364 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8365 * they are returned as an array.
8366 * @param {Boolean} asString
8369 getValues : function(asString){
8370 //if (this.childForms) {
8371 // copy values from the child forms
8372 // Roo.each(this.childForms, function (f) {
8373 // this.setValues(f.getValues());
8379 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8380 if(asString === true){
8383 return Roo.urlDecode(fs);
8387 * Returns the fields in this form as an object with key/value pairs.
8388 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8391 getFieldValues : function(with_hidden)
8393 var items = this.getItems();
8395 items.each(function(f){
8401 var v = f.getValue();
8403 if (f.inputType =='radio') {
8404 if (typeof(ret[f.getName()]) == 'undefined') {
8405 ret[f.getName()] = ''; // empty..
8408 if (!f.el.dom.checked) {
8416 if(f.xtype == 'MoneyField'){
8417 ret[f.currencyName] = f.getCurrency();
8420 // not sure if this supported any more..
8421 if ((typeof(v) == 'object') && f.getRawValue) {
8422 v = f.getRawValue() ; // dates..
8424 // combo boxes where name != hiddenName...
8425 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8426 ret[f.name] = f.getRawValue();
8428 ret[f.getName()] = v;
8435 * Clears all invalid messages in this form.
8436 * @return {BasicForm} this
8438 clearInvalid : function(){
8439 var items = this.getItems();
8441 items.each(function(f){
8450 * @return {BasicForm} this
8453 var items = this.getItems();
8454 items.each(function(f){
8458 Roo.each(this.childForms || [], function (f) {
8466 getItems : function()
8468 var r=new Roo.util.MixedCollection(false, function(o){
8469 return o.id || (o.id = Roo.id());
8471 var iter = function(el) {
8478 Roo.each(el.items,function(e) {
8487 hideFields : function(items)
8489 Roo.each(items, function(i){
8491 var f = this.findField(i);
8502 showFields : function(items)
8504 Roo.each(items, function(i){
8506 var f = this.findField(i);
8519 Roo.apply(Roo.bootstrap.Form, {
8546 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8547 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8548 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8549 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8552 this.maskEl.top.enableDisplayMode("block");
8553 this.maskEl.left.enableDisplayMode("block");
8554 this.maskEl.bottom.enableDisplayMode("block");
8555 this.maskEl.right.enableDisplayMode("block");
8557 this.toolTip = new Roo.bootstrap.Tooltip({
8558 cls : 'roo-form-error-popover',
8560 'left' : ['r-l', [-2,0], 'right'],
8561 'right' : ['l-r', [2,0], 'left'],
8562 'bottom' : ['tl-bl', [0,2], 'top'],
8563 'top' : [ 'bl-tl', [0,-2], 'bottom']
8567 this.toolTip.render(Roo.get(document.body));
8569 this.toolTip.el.enableDisplayMode("block");
8571 Roo.get(document.body).on('click', function(){
8575 Roo.get(document.body).on('touchstart', function(){
8579 this.isApplied = true
8582 mask : function(form, target)
8586 this.target = target;
8588 if(!this.form.errorMask || !target.el){
8592 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8594 Roo.log(scrollable);
8596 var ot = this.target.el.calcOffsetsTo(scrollable);
8598 var scrollTo = ot[1] - this.form.maskOffset;
8600 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8602 scrollable.scrollTo('top', scrollTo);
8604 var box = this.target.el.getBox();
8606 var zIndex = Roo.bootstrap.Modal.zIndex++;
8609 this.maskEl.top.setStyle('position', 'absolute');
8610 this.maskEl.top.setStyle('z-index', zIndex);
8611 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8612 this.maskEl.top.setLeft(0);
8613 this.maskEl.top.setTop(0);
8614 this.maskEl.top.show();
8616 this.maskEl.left.setStyle('position', 'absolute');
8617 this.maskEl.left.setStyle('z-index', zIndex);
8618 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8619 this.maskEl.left.setLeft(0);
8620 this.maskEl.left.setTop(box.y - this.padding);
8621 this.maskEl.left.show();
8623 this.maskEl.bottom.setStyle('position', 'absolute');
8624 this.maskEl.bottom.setStyle('z-index', zIndex);
8625 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8626 this.maskEl.bottom.setLeft(0);
8627 this.maskEl.bottom.setTop(box.bottom + this.padding);
8628 this.maskEl.bottom.show();
8630 this.maskEl.right.setStyle('position', 'absolute');
8631 this.maskEl.right.setStyle('z-index', zIndex);
8632 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8633 this.maskEl.right.setLeft(box.right + this.padding);
8634 this.maskEl.right.setTop(box.y - this.padding);
8635 this.maskEl.right.show();
8637 this.toolTip.bindEl = this.target.el;
8639 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8641 var tip = this.target.blankText;
8643 if(this.target.getValue() !== '' ) {
8645 if (this.target.invalidText.length) {
8646 tip = this.target.invalidText;
8647 } else if (this.target.regexText.length){
8648 tip = this.target.regexText;
8652 this.toolTip.show(tip);
8654 this.intervalID = window.setInterval(function() {
8655 Roo.bootstrap.Form.popover.unmask();
8658 window.onwheel = function(){ return false;};
8660 (function(){ this.isMasked = true; }).defer(500, this);
8666 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8670 this.maskEl.top.setStyle('position', 'absolute');
8671 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8672 this.maskEl.top.hide();
8674 this.maskEl.left.setStyle('position', 'absolute');
8675 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8676 this.maskEl.left.hide();
8678 this.maskEl.bottom.setStyle('position', 'absolute');
8679 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8680 this.maskEl.bottom.hide();
8682 this.maskEl.right.setStyle('position', 'absolute');
8683 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8684 this.maskEl.right.hide();
8686 this.toolTip.hide();
8688 this.toolTip.el.hide();
8690 window.onwheel = function(){ return true;};
8692 if(this.intervalID){
8693 window.clearInterval(this.intervalID);
8694 this.intervalID = false;
8697 this.isMasked = false;
8707 * Ext JS Library 1.1.1
8708 * Copyright(c) 2006-2007, Ext JS, LLC.
8710 * Originally Released Under LGPL - original licence link has changed is not relivant.
8713 * <script type="text/javascript">
8716 * @class Roo.form.VTypes
8717 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8720 Roo.form.VTypes = function(){
8721 // closure these in so they are only created once.
8722 var alpha = /^[a-zA-Z_]+$/;
8723 var alphanum = /^[a-zA-Z0-9_]+$/;
8724 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8725 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8727 // All these messages and functions are configurable
8730 * The function used to validate email addresses
8731 * @param {String} value The email address
8733 'email' : function(v){
8734 return email.test(v);
8737 * The error text to display when the email validation function returns false
8740 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8742 * The keystroke filter mask to be applied on email input
8745 'emailMask' : /[a-z0-9_\.\-@]/i,
8748 * The function used to validate URLs
8749 * @param {String} value The URL
8751 'url' : function(v){
8755 * The error text to display when the url validation function returns false
8758 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8761 * The function used to validate alpha values
8762 * @param {String} value The value
8764 'alpha' : function(v){
8765 return alpha.test(v);
8768 * The error text to display when the alpha validation function returns false
8771 'alphaText' : 'This field should only contain letters and _',
8773 * The keystroke filter mask to be applied on alpha input
8776 'alphaMask' : /[a-z_]/i,
8779 * The function used to validate alphanumeric values
8780 * @param {String} value The value
8782 'alphanum' : function(v){
8783 return alphanum.test(v);
8786 * The error text to display when the alphanumeric validation function returns false
8789 'alphanumText' : 'This field should only contain letters, numbers and _',
8791 * The keystroke filter mask to be applied on alphanumeric input
8794 'alphanumMask' : /[a-z0-9_]/i
8804 * @class Roo.bootstrap.Input
8805 * @extends Roo.bootstrap.Component
8806 * Bootstrap Input class
8807 * @cfg {Boolean} disabled is it disabled
8808 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8809 * @cfg {String} name name of the input
8810 * @cfg {string} fieldLabel - the label associated
8811 * @cfg {string} placeholder - placeholder to put in text.
8812 * @cfg {string} before - input group add on before
8813 * @cfg {string} after - input group add on after
8814 * @cfg {string} size - (lg|sm) or leave empty..
8815 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8816 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8817 * @cfg {Number} md colspan out of 12 for computer-sized screens
8818 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8819 * @cfg {string} value default value of the input
8820 * @cfg {Number} labelWidth set the width of label
8821 * @cfg {Number} labellg set the width of label (1-12)
8822 * @cfg {Number} labelmd set the width of label (1-12)
8823 * @cfg {Number} labelsm set the width of label (1-12)
8824 * @cfg {Number} labelxs set the width of label (1-12)
8825 * @cfg {String} labelAlign (top|left)
8826 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8827 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8828 * @cfg {String} indicatorpos (left|right) default left
8829 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8830 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8832 * @cfg {String} align (left|center|right) Default left
8833 * @cfg {Boolean} forceFeedback (true|false) Default false
8836 * Create a new Input
8837 * @param {Object} config The config object
8840 Roo.bootstrap.Input = function(config){
8842 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8847 * Fires when this field receives input focus.
8848 * @param {Roo.form.Field} this
8853 * Fires when this field loses input focus.
8854 * @param {Roo.form.Field} this
8859 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8860 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8861 * @param {Roo.form.Field} this
8862 * @param {Roo.EventObject} e The event object
8867 * Fires just before the field blurs if the field value has changed.
8868 * @param {Roo.form.Field} this
8869 * @param {Mixed} newValue The new value
8870 * @param {Mixed} oldValue The original value
8875 * Fires after the field has been marked as invalid.
8876 * @param {Roo.form.Field} this
8877 * @param {String} msg The validation message
8882 * Fires after the field has been validated with no errors.
8883 * @param {Roo.form.Field} this
8888 * Fires after the key up
8889 * @param {Roo.form.Field} this
8890 * @param {Roo.EventObject} e The event Object
8896 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8898 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8899 automatic validation (defaults to "keyup").
8901 validationEvent : "keyup",
8903 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8905 validateOnBlur : true,
8907 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8909 validationDelay : 250,
8911 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8913 focusClass : "x-form-focus", // not needed???
8917 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8919 invalidClass : "has-warning",
8922 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8924 validClass : "has-success",
8927 * @cfg {Boolean} hasFeedback (true|false) default true
8932 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8934 invalidFeedbackClass : "glyphicon-warning-sign",
8937 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8939 validFeedbackClass : "glyphicon-ok",
8942 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8944 selectOnFocus : false,
8947 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8951 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8956 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8958 disableKeyFilter : false,
8961 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8965 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8969 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8971 blankText : "Please complete this mandatory field",
8974 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8978 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8980 maxLength : Number.MAX_VALUE,
8982 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8984 minLengthText : "The minimum length for this field is {0}",
8986 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8988 maxLengthText : "The maximum length for this field is {0}",
8992 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8993 * If available, this function will be called only after the basic validators all return true, and will be passed the
8994 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8998 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8999 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9000 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
9004 * @cfg {String} regexText -- Depricated - use Invalid Text
9009 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9015 autocomplete: false,
9034 formatedValue : false,
9035 forceFeedback : false,
9037 indicatorpos : 'left',
9047 parentLabelAlign : function()
9050 while (parent.parent()) {
9051 parent = parent.parent();
9052 if (typeof(parent.labelAlign) !='undefined') {
9053 return parent.labelAlign;
9060 getAutoCreate : function()
9062 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9068 if(this.inputType != 'hidden'){
9069 cfg.cls = 'form-group' //input-group
9075 type : this.inputType,
9077 cls : 'form-control',
9078 placeholder : this.placeholder || '',
9079 autocomplete : this.autocomplete || 'new-password'
9082 if(this.capture.length){
9083 input.capture = this.capture;
9086 if(this.accept.length){
9087 input.accept = this.accept + "/*";
9091 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9094 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9095 input.maxLength = this.maxLength;
9098 if (this.disabled) {
9099 input.disabled=true;
9102 if (this.readOnly) {
9103 input.readonly=true;
9107 input.name = this.name;
9111 input.cls += ' input-' + this.size;
9115 ['xs','sm','md','lg'].map(function(size){
9116 if (settings[size]) {
9117 cfg.cls += ' col-' + size + '-' + settings[size];
9121 var inputblock = input;
9125 cls: 'glyphicon form-control-feedback'
9128 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9131 cls : 'has-feedback',
9139 if (this.before || this.after) {
9142 cls : 'input-group',
9146 if (this.before && typeof(this.before) == 'string') {
9148 inputblock.cn.push({
9150 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9154 if (this.before && typeof(this.before) == 'object') {
9155 this.before = Roo.factory(this.before);
9157 inputblock.cn.push({
9159 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9160 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9164 inputblock.cn.push(input);
9166 if (this.after && typeof(this.after) == 'string') {
9167 inputblock.cn.push({
9169 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9173 if (this.after && typeof(this.after) == 'object') {
9174 this.after = Roo.factory(this.after);
9176 inputblock.cn.push({
9178 cls : 'roo-input-after input-group-append input-group-text input-group-' +
9179 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9183 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9184 inputblock.cls += ' has-feedback';
9185 inputblock.cn.push(feedback);
9190 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9191 tooltip : 'This field is required'
9193 if (Roo.bootstrap.version == 4) {
9196 style : 'display-none'
9199 if (align ==='left' && this.fieldLabel.length) {
9201 cfg.cls += ' roo-form-group-label-left row';
9208 cls : 'control-label col-form-label',
9209 html : this.fieldLabel
9220 var labelCfg = cfg.cn[1];
9221 var contentCfg = cfg.cn[2];
9223 if(this.indicatorpos == 'right'){
9228 cls : 'control-label col-form-label',
9232 html : this.fieldLabel
9246 labelCfg = cfg.cn[0];
9247 contentCfg = cfg.cn[1];
9251 if(this.labelWidth > 12){
9252 labelCfg.style = "width: " + this.labelWidth + 'px';
9255 if(this.labelWidth < 13 && this.labelmd == 0){
9256 this.labelmd = this.labelWidth;
9259 if(this.labellg > 0){
9260 labelCfg.cls += ' col-lg-' + this.labellg;
9261 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9264 if(this.labelmd > 0){
9265 labelCfg.cls += ' col-md-' + this.labelmd;
9266 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9269 if(this.labelsm > 0){
9270 labelCfg.cls += ' col-sm-' + this.labelsm;
9271 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9274 if(this.labelxs > 0){
9275 labelCfg.cls += ' col-xs-' + this.labelxs;
9276 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9280 } else if ( this.fieldLabel.length) {
9285 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9286 tooltip : 'This field is required'
9290 //cls : 'input-group-addon',
9291 html : this.fieldLabel
9299 if(this.indicatorpos == 'right'){
9304 //cls : 'input-group-addon',
9305 html : this.fieldLabel
9310 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9311 tooltip : 'This field is required'
9331 if (this.parentType === 'Navbar' && this.parent().bar) {
9332 cfg.cls += ' navbar-form';
9335 if (this.parentType === 'NavGroup') {
9336 cfg.cls += ' navbar-form';
9344 * return the real input element.
9346 inputEl: function ()
9348 return this.el.select('input.form-control',true).first();
9351 tooltipEl : function()
9353 return this.inputEl();
9356 indicatorEl : function()
9358 if (Roo.bootstrap.version == 4) {
9359 return false; // not enabled in v4 yet.
9362 var indicator = this.el.select('i.roo-required-indicator',true).first();
9372 setDisabled : function(v)
9374 var i = this.inputEl().dom;
9376 i.removeAttribute('disabled');
9380 i.setAttribute('disabled','true');
9382 initEvents : function()
9385 this.inputEl().on("keydown" , this.fireKey, this);
9386 this.inputEl().on("focus", this.onFocus, this);
9387 this.inputEl().on("blur", this.onBlur, this);
9389 this.inputEl().relayEvent('keyup', this);
9391 this.indicator = this.indicatorEl();
9394 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9397 // reference to original value for reset
9398 this.originalValue = this.getValue();
9399 //Roo.form.TextField.superclass.initEvents.call(this);
9400 if(this.validationEvent == 'keyup'){
9401 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9402 this.inputEl().on('keyup', this.filterValidation, this);
9404 else if(this.validationEvent !== false){
9405 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9408 if(this.selectOnFocus){
9409 this.on("focus", this.preFocus, this);
9412 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9413 this.inputEl().on("keypress", this.filterKeys, this);
9415 this.inputEl().relayEvent('keypress', this);
9418 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9419 this.el.on("click", this.autoSize, this);
9422 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9423 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9426 if (typeof(this.before) == 'object') {
9427 this.before.render(this.el.select('.roo-input-before',true).first());
9429 if (typeof(this.after) == 'object') {
9430 this.after.render(this.el.select('.roo-input-after',true).first());
9433 this.inputEl().on('change', this.onChange, this);
9436 filterValidation : function(e){
9437 if(!e.isNavKeyPress()){
9438 this.validationTask.delay(this.validationDelay);
9442 * Validates the field value
9443 * @return {Boolean} True if the value is valid, else false
9445 validate : function(){
9446 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9447 if(this.disabled || this.validateValue(this.getRawValue())){
9458 * Validates a value according to the field's validation rules and marks the field as invalid
9459 * if the validation fails
9460 * @param {Mixed} value The value to validate
9461 * @return {Boolean} True if the value is valid, else false
9463 validateValue : function(value)
9465 if(this.getVisibilityEl().hasClass('hidden')){
9469 if(value.length < 1) { // if it's blank
9470 if(this.allowBlank){
9476 if(value.length < this.minLength){
9479 if(value.length > this.maxLength){
9483 var vt = Roo.form.VTypes;
9484 if(!vt[this.vtype](value, this)){
9488 if(typeof this.validator == "function"){
9489 var msg = this.validator(value);
9493 if (typeof(msg) == 'string') {
9494 this.invalidText = msg;
9498 if(this.regex && !this.regex.test(value)){
9506 fireKey : function(e){
9507 //Roo.log('field ' + e.getKey());
9508 if(e.isNavKeyPress()){
9509 this.fireEvent("specialkey", this, e);
9512 focus : function (selectText){
9514 this.inputEl().focus();
9515 if(selectText === true){
9516 this.inputEl().dom.select();
9522 onFocus : function(){
9523 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9524 // this.el.addClass(this.focusClass);
9527 this.hasFocus = true;
9528 this.startValue = this.getValue();
9529 this.fireEvent("focus", this);
9533 beforeBlur : Roo.emptyFn,
9537 onBlur : function(){
9539 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9540 //this.el.removeClass(this.focusClass);
9542 this.hasFocus = false;
9543 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9546 var v = this.getValue();
9547 if(String(v) !== String(this.startValue)){
9548 this.fireEvent('change', this, v, this.startValue);
9550 this.fireEvent("blur", this);
9553 onChange : function(e)
9555 var v = this.getValue();
9556 if(String(v) !== String(this.startValue)){
9557 this.fireEvent('change', this, v, this.startValue);
9563 * Resets the current field value to the originally loaded value and clears any validation messages
9566 this.setValue(this.originalValue);
9570 * Returns the name of the field
9571 * @return {Mixed} name The name field
9573 getName: function(){
9577 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9578 * @return {Mixed} value The field value
9580 getValue : function(){
9582 var v = this.inputEl().getValue();
9587 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9588 * @return {Mixed} value The field value
9590 getRawValue : function(){
9591 var v = this.inputEl().getValue();
9597 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9598 * @param {Mixed} value The value to set
9600 setRawValue : function(v){
9601 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9604 selectText : function(start, end){
9605 var v = this.getRawValue();
9607 start = start === undefined ? 0 : start;
9608 end = end === undefined ? v.length : end;
9609 var d = this.inputEl().dom;
9610 if(d.setSelectionRange){
9611 d.setSelectionRange(start, end);
9612 }else if(d.createTextRange){
9613 var range = d.createTextRange();
9614 range.moveStart("character", start);
9615 range.moveEnd("character", v.length-end);
9622 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9623 * @param {Mixed} value The value to set
9625 setValue : function(v){
9628 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9634 processValue : function(value){
9635 if(this.stripCharsRe){
9636 var newValue = value.replace(this.stripCharsRe, '');
9637 if(newValue !== value){
9638 this.setRawValue(newValue);
9645 preFocus : function(){
9647 if(this.selectOnFocus){
9648 this.inputEl().dom.select();
9651 filterKeys : function(e){
9653 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9656 var c = e.getCharCode(), cc = String.fromCharCode(c);
9657 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9660 if(!this.maskRe.test(cc)){
9665 * Clear any invalid styles/messages for this field
9667 clearInvalid : function(){
9669 if(!this.el || this.preventMark){ // not rendered
9674 this.el.removeClass(this.invalidClass);
9676 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9678 var feedback = this.el.select('.form-control-feedback', true).first();
9681 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9687 this.indicator.removeClass('visible');
9688 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9691 this.fireEvent('valid', this);
9695 * Mark this field as valid
9697 markValid : function()
9699 if(!this.el || this.preventMark){ // not rendered...
9703 this.el.removeClass([this.invalidClass, this.validClass]);
9705 var feedback = this.el.select('.form-control-feedback', true).first();
9708 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9712 this.indicator.removeClass('visible');
9713 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9720 if(this.allowBlank && !this.getRawValue().length){
9724 this.el.addClass(this.validClass);
9726 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9728 var feedback = this.el.select('.form-control-feedback', true).first();
9731 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9732 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9737 this.fireEvent('valid', this);
9741 * Mark this field as invalid
9742 * @param {String} msg The validation message
9744 markInvalid : function(msg)
9746 if(!this.el || this.preventMark){ // not rendered
9750 this.el.removeClass([this.invalidClass, this.validClass]);
9752 var feedback = this.el.select('.form-control-feedback', true).first();
9755 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9762 if(this.allowBlank && !this.getRawValue().length){
9767 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9768 this.indicator.addClass('visible');
9771 this.el.addClass(this.invalidClass);
9773 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9775 var feedback = this.el.select('.form-control-feedback', true).first();
9778 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9780 if(this.getValue().length || this.forceFeedback){
9781 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9788 this.fireEvent('invalid', this, msg);
9791 SafariOnKeyDown : function(event)
9793 // this is a workaround for a password hang bug on chrome/ webkit.
9794 if (this.inputEl().dom.type != 'password') {
9798 var isSelectAll = false;
9800 if(this.inputEl().dom.selectionEnd > 0){
9801 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9803 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9804 event.preventDefault();
9809 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9811 event.preventDefault();
9812 // this is very hacky as keydown always get's upper case.
9814 var cc = String.fromCharCode(event.getCharCode());
9815 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9819 adjustWidth : function(tag, w){
9820 tag = tag.toLowerCase();
9821 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9822 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9826 if(tag == 'textarea'){
9829 }else if(Roo.isOpera){
9833 if(tag == 'textarea'){
9841 setFieldLabel : function(v)
9847 if(this.indicatorEl()){
9848 var ar = this.el.select('label > span',true);
9850 if (ar.elements.length) {
9851 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9852 this.fieldLabel = v;
9856 var br = this.el.select('label',true);
9858 if(br.elements.length) {
9859 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9860 this.fieldLabel = v;
9864 Roo.log('Cannot Found any of label > span || label in input');
9868 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9869 this.fieldLabel = v;
9884 * @class Roo.bootstrap.TextArea
9885 * @extends Roo.bootstrap.Input
9886 * Bootstrap TextArea class
9887 * @cfg {Number} cols Specifies the visible width of a text area
9888 * @cfg {Number} rows Specifies the visible number of lines in a text area
9889 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9890 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9891 * @cfg {string} html text
9894 * Create a new TextArea
9895 * @param {Object} config The config object
9898 Roo.bootstrap.TextArea = function(config){
9899 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9903 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9913 getAutoCreate : function(){
9915 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9921 if(this.inputType != 'hidden'){
9922 cfg.cls = 'form-group' //input-group
9930 value : this.value || '',
9931 html: this.html || '',
9932 cls : 'form-control',
9933 placeholder : this.placeholder || ''
9937 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9938 input.maxLength = this.maxLength;
9942 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9946 input.cols = this.cols;
9949 if (this.readOnly) {
9950 input.readonly = true;
9954 input.name = this.name;
9958 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9962 ['xs','sm','md','lg'].map(function(size){
9963 if (settings[size]) {
9964 cfg.cls += ' col-' + size + '-' + settings[size];
9968 var inputblock = input;
9970 if(this.hasFeedback && !this.allowBlank){
9974 cls: 'glyphicon form-control-feedback'
9978 cls : 'has-feedback',
9987 if (this.before || this.after) {
9990 cls : 'input-group',
9994 inputblock.cn.push({
9996 cls : 'input-group-addon',
10001 inputblock.cn.push(input);
10003 if(this.hasFeedback && !this.allowBlank){
10004 inputblock.cls += ' has-feedback';
10005 inputblock.cn.push(feedback);
10009 inputblock.cn.push({
10011 cls : 'input-group-addon',
10018 if (align ==='left' && this.fieldLabel.length) {
10023 cls : 'control-label',
10024 html : this.fieldLabel
10035 if(this.labelWidth > 12){
10036 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10039 if(this.labelWidth < 13 && this.labelmd == 0){
10040 this.labelmd = this.labelWidth;
10043 if(this.labellg > 0){
10044 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10045 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10048 if(this.labelmd > 0){
10049 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10050 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10053 if(this.labelsm > 0){
10054 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10055 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10058 if(this.labelxs > 0){
10059 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10060 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10063 } else if ( this.fieldLabel.length) {
10068 //cls : 'input-group-addon',
10069 html : this.fieldLabel
10087 if (this.disabled) {
10088 input.disabled=true;
10095 * return the real textarea element.
10097 inputEl: function ()
10099 return this.el.select('textarea.form-control',true).first();
10103 * Clear any invalid styles/messages for this field
10105 clearInvalid : function()
10108 if(!this.el || this.preventMark){ // not rendered
10112 var label = this.el.select('label', true).first();
10113 var icon = this.el.select('i.fa-star', true).first();
10119 this.el.removeClass(this.invalidClass);
10121 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10123 var feedback = this.el.select('.form-control-feedback', true).first();
10126 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10131 this.fireEvent('valid', this);
10135 * Mark this field as valid
10137 markValid : function()
10139 if(!this.el || this.preventMark){ // not rendered
10143 this.el.removeClass([this.invalidClass, this.validClass]);
10145 var feedback = this.el.select('.form-control-feedback', true).first();
10148 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10151 if(this.disabled || this.allowBlank){
10155 var label = this.el.select('label', true).first();
10156 var icon = this.el.select('i.fa-star', true).first();
10162 this.el.addClass(this.validClass);
10164 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10166 var feedback = this.el.select('.form-control-feedback', true).first();
10169 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10170 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10175 this.fireEvent('valid', this);
10179 * Mark this field as invalid
10180 * @param {String} msg The validation message
10182 markInvalid : function(msg)
10184 if(!this.el || this.preventMark){ // not rendered
10188 this.el.removeClass([this.invalidClass, this.validClass]);
10190 var feedback = this.el.select('.form-control-feedback', true).first();
10193 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10196 if(this.disabled || this.allowBlank){
10200 var label = this.el.select('label', true).first();
10201 var icon = this.el.select('i.fa-star', true).first();
10203 if(!this.getValue().length && label && !icon){
10204 this.el.createChild({
10206 cls : 'text-danger fa fa-lg fa-star',
10207 tooltip : 'This field is required',
10208 style : 'margin-right:5px;'
10212 this.el.addClass(this.invalidClass);
10214 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10216 var feedback = this.el.select('.form-control-feedback', true).first();
10219 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10221 if(this.getValue().length || this.forceFeedback){
10222 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10229 this.fireEvent('invalid', this, msg);
10237 * trigger field - base class for combo..
10242 * @class Roo.bootstrap.TriggerField
10243 * @extends Roo.bootstrap.Input
10244 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10245 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10246 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10247 * for which you can provide a custom implementation. For example:
10249 var trigger = new Roo.bootstrap.TriggerField();
10250 trigger.onTriggerClick = myTriggerFn;
10251 trigger.applyTo('my-field');
10254 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10255 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10256 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10257 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10258 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10261 * Create a new TriggerField.
10262 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10263 * to the base TextField)
10265 Roo.bootstrap.TriggerField = function(config){
10266 this.mimicing = false;
10267 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10270 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10272 * @cfg {String} triggerClass A CSS class to apply to the trigger
10275 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10280 * @cfg {Boolean} removable (true|false) special filter default false
10284 /** @cfg {Boolean} grow @hide */
10285 /** @cfg {Number} growMin @hide */
10286 /** @cfg {Number} growMax @hide */
10292 autoSize: Roo.emptyFn,
10296 deferHeight : true,
10299 actionMode : 'wrap',
10304 getAutoCreate : function(){
10306 var align = this.labelAlign || this.parentLabelAlign();
10311 cls: 'form-group' //input-group
10318 type : this.inputType,
10319 cls : 'form-control',
10320 autocomplete: 'new-password',
10321 placeholder : this.placeholder || ''
10325 input.name = this.name;
10328 input.cls += ' input-' + this.size;
10331 if (this.disabled) {
10332 input.disabled=true;
10335 var inputblock = input;
10337 if(this.hasFeedback && !this.allowBlank){
10341 cls: 'glyphicon form-control-feedback'
10344 if(this.removable && !this.editable && !this.tickable){
10346 cls : 'has-feedback',
10352 cls : 'roo-combo-removable-btn close'
10359 cls : 'has-feedback',
10368 if(this.removable && !this.editable && !this.tickable){
10370 cls : 'roo-removable',
10376 cls : 'roo-combo-removable-btn close'
10383 if (this.before || this.after) {
10386 cls : 'input-group',
10390 inputblock.cn.push({
10392 cls : 'input-group-addon input-group-prepend input-group-text',
10397 inputblock.cn.push(input);
10399 if(this.hasFeedback && !this.allowBlank){
10400 inputblock.cls += ' has-feedback';
10401 inputblock.cn.push(feedback);
10405 inputblock.cn.push({
10407 cls : 'input-group-addon input-group-append input-group-text',
10416 var ibwrap = inputblock;
10421 cls: 'roo-select2-choices',
10425 cls: 'roo-select2-search-field',
10437 cls: 'roo-select2-container input-group',
10442 cls: 'form-hidden-field'
10448 if(!this.multiple && this.showToggleBtn){
10454 if (this.caret != false) {
10457 cls: 'fa fa-' + this.caret
10464 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10469 cls: 'combobox-clear',
10483 combobox.cls += ' roo-select2-container-multi';
10487 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10488 tooltip : 'This field is required'
10490 if (Roo.bootstrap.version == 4) {
10493 style : 'display:none'
10498 if (align ==='left' && this.fieldLabel.length) {
10500 cfg.cls += ' roo-form-group-label-left row';
10507 cls : 'control-label',
10508 html : this.fieldLabel
10520 var labelCfg = cfg.cn[1];
10521 var contentCfg = cfg.cn[2];
10523 if(this.indicatorpos == 'right'){
10528 cls : 'control-label',
10532 html : this.fieldLabel
10546 labelCfg = cfg.cn[0];
10547 contentCfg = cfg.cn[1];
10550 if(this.labelWidth > 12){
10551 labelCfg.style = "width: " + this.labelWidth + 'px';
10554 if(this.labelWidth < 13 && this.labelmd == 0){
10555 this.labelmd = this.labelWidth;
10558 if(this.labellg > 0){
10559 labelCfg.cls += ' col-lg-' + this.labellg;
10560 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10563 if(this.labelmd > 0){
10564 labelCfg.cls += ' col-md-' + this.labelmd;
10565 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10568 if(this.labelsm > 0){
10569 labelCfg.cls += ' col-sm-' + this.labelsm;
10570 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10573 if(this.labelxs > 0){
10574 labelCfg.cls += ' col-xs-' + this.labelxs;
10575 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10578 } else if ( this.fieldLabel.length) {
10579 // Roo.log(" label");
10584 //cls : 'input-group-addon',
10585 html : this.fieldLabel
10593 if(this.indicatorpos == 'right'){
10601 html : this.fieldLabel
10615 // Roo.log(" no label && no align");
10622 ['xs','sm','md','lg'].map(function(size){
10623 if (settings[size]) {
10624 cfg.cls += ' col-' + size + '-' + settings[size];
10635 onResize : function(w, h){
10636 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10637 // if(typeof w == 'number'){
10638 // var x = w - this.trigger.getWidth();
10639 // this.inputEl().setWidth(this.adjustWidth('input', x));
10640 // this.trigger.setStyle('left', x+'px');
10645 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10648 getResizeEl : function(){
10649 return this.inputEl();
10653 getPositionEl : function(){
10654 return this.inputEl();
10658 alignErrorIcon : function(){
10659 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10663 initEvents : function(){
10667 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10668 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10669 if(!this.multiple && this.showToggleBtn){
10670 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10671 if(this.hideTrigger){
10672 this.trigger.setDisplayed(false);
10674 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10678 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10681 if(this.removable && !this.editable && !this.tickable){
10682 var close = this.closeTriggerEl();
10685 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10686 close.on('click', this.removeBtnClick, this, close);
10690 //this.trigger.addClassOnOver('x-form-trigger-over');
10691 //this.trigger.addClassOnClick('x-form-trigger-click');
10694 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10698 closeTriggerEl : function()
10700 var close = this.el.select('.roo-combo-removable-btn', true).first();
10701 return close ? close : false;
10704 removeBtnClick : function(e, h, el)
10706 e.preventDefault();
10708 if(this.fireEvent("remove", this) !== false){
10710 this.fireEvent("afterremove", this)
10714 createList : function()
10716 this.list = Roo.get(document.body).createChild({
10717 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10718 cls: 'typeahead typeahead-long dropdown-menu',
10719 style: 'display:none'
10722 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10727 initTrigger : function(){
10732 onDestroy : function(){
10734 this.trigger.removeAllListeners();
10735 // this.trigger.remove();
10738 // this.wrap.remove();
10740 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10744 onFocus : function(){
10745 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10747 if(!this.mimicing){
10748 this.wrap.addClass('x-trigger-wrap-focus');
10749 this.mimicing = true;
10750 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10751 if(this.monitorTab){
10752 this.el.on("keydown", this.checkTab, this);
10759 checkTab : function(e){
10760 if(e.getKey() == e.TAB){
10761 this.triggerBlur();
10766 onBlur : function(){
10771 mimicBlur : function(e, t){
10773 if(!this.wrap.contains(t) && this.validateBlur()){
10774 this.triggerBlur();
10780 triggerBlur : function(){
10781 this.mimicing = false;
10782 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10783 if(this.monitorTab){
10784 this.el.un("keydown", this.checkTab, this);
10786 //this.wrap.removeClass('x-trigger-wrap-focus');
10787 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10791 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10792 validateBlur : function(e, t){
10797 onDisable : function(){
10798 this.inputEl().dom.disabled = true;
10799 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10801 // this.wrap.addClass('x-item-disabled');
10806 onEnable : function(){
10807 this.inputEl().dom.disabled = false;
10808 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10810 // this.el.removeClass('x-item-disabled');
10815 onShow : function(){
10816 var ae = this.getActionEl();
10819 ae.dom.style.display = '';
10820 ae.dom.style.visibility = 'visible';
10826 onHide : function(){
10827 var ae = this.getActionEl();
10828 ae.dom.style.display = 'none';
10832 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10833 * by an implementing function.
10835 * @param {EventObject} e
10837 onTriggerClick : Roo.emptyFn
10841 * Ext JS Library 1.1.1
10842 * Copyright(c) 2006-2007, Ext JS, LLC.
10844 * Originally Released Under LGPL - original licence link has changed is not relivant.
10847 * <script type="text/javascript">
10852 * @class Roo.data.SortTypes
10854 * Defines the default sorting (casting?) comparison functions used when sorting data.
10856 Roo.data.SortTypes = {
10858 * Default sort that does nothing
10859 * @param {Mixed} s The value being converted
10860 * @return {Mixed} The comparison value
10862 none : function(s){
10867 * The regular expression used to strip tags
10871 stripTagsRE : /<\/?[^>]+>/gi,
10874 * Strips all HTML tags to sort on text only
10875 * @param {Mixed} s The value being converted
10876 * @return {String} The comparison value
10878 asText : function(s){
10879 return String(s).replace(this.stripTagsRE, "");
10883 * Strips all HTML tags to sort on text only - Case insensitive
10884 * @param {Mixed} s The value being converted
10885 * @return {String} The comparison value
10887 asUCText : function(s){
10888 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10892 * Case insensitive string
10893 * @param {Mixed} s The value being converted
10894 * @return {String} The comparison value
10896 asUCString : function(s) {
10897 return String(s).toUpperCase();
10902 * @param {Mixed} s The value being converted
10903 * @return {Number} The comparison value
10905 asDate : function(s) {
10909 if(s instanceof Date){
10910 return s.getTime();
10912 return Date.parse(String(s));
10917 * @param {Mixed} s The value being converted
10918 * @return {Float} The comparison value
10920 asFloat : function(s) {
10921 var val = parseFloat(String(s).replace(/,/g, ""));
10930 * @param {Mixed} s The value being converted
10931 * @return {Number} The comparison value
10933 asInt : function(s) {
10934 var val = parseInt(String(s).replace(/,/g, ""));
10942 * Ext JS Library 1.1.1
10943 * Copyright(c) 2006-2007, Ext JS, LLC.
10945 * Originally Released Under LGPL - original licence link has changed is not relivant.
10948 * <script type="text/javascript">
10952 * @class Roo.data.Record
10953 * Instances of this class encapsulate both record <em>definition</em> information, and record
10954 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10955 * to access Records cached in an {@link Roo.data.Store} object.<br>
10957 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10958 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10961 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10963 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10964 * {@link #create}. The parameters are the same.
10965 * @param {Array} data An associative Array of data values keyed by the field name.
10966 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10967 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10968 * not specified an integer id is generated.
10970 Roo.data.Record = function(data, id){
10971 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10976 * Generate a constructor for a specific record layout.
10977 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10978 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10979 * Each field definition object may contain the following properties: <ul>
10980 * <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,
10981 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10982 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10983 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10984 * is being used, then this is a string containing the javascript expression to reference the data relative to
10985 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10986 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10987 * this may be omitted.</p></li>
10988 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10989 * <ul><li>auto (Default, implies no conversion)</li>
10994 * <li>date</li></ul></p></li>
10995 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10996 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10997 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10998 * by the Reader into an object that will be stored in the Record. It is passed the
10999 * following parameters:<ul>
11000 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11002 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11004 * <br>usage:<br><pre><code>
11005 var TopicRecord = Roo.data.Record.create(
11006 {name: 'title', mapping: 'topic_title'},
11007 {name: 'author', mapping: 'username'},
11008 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11009 {name: 'lastPost', mapping: 'post_time', type: 'date'},
11010 {name: 'lastPoster', mapping: 'user2'},
11011 {name: 'excerpt', mapping: 'post_text'}
11014 var myNewRecord = new TopicRecord({
11015 title: 'Do my job please',
11018 lastPost: new Date(),
11019 lastPoster: 'Animal',
11020 excerpt: 'No way dude!'
11022 myStore.add(myNewRecord);
11027 Roo.data.Record.create = function(o){
11028 var f = function(){
11029 f.superclass.constructor.apply(this, arguments);
11031 Roo.extend(f, Roo.data.Record);
11032 var p = f.prototype;
11033 p.fields = new Roo.util.MixedCollection(false, function(field){
11036 for(var i = 0, len = o.length; i < len; i++){
11037 p.fields.add(new Roo.data.Field(o[i]));
11039 f.getField = function(name){
11040 return p.fields.get(name);
11045 Roo.data.Record.AUTO_ID = 1000;
11046 Roo.data.Record.EDIT = 'edit';
11047 Roo.data.Record.REJECT = 'reject';
11048 Roo.data.Record.COMMIT = 'commit';
11050 Roo.data.Record.prototype = {
11052 * Readonly flag - true if this record has been modified.
11061 join : function(store){
11062 this.store = store;
11066 * Set the named field to the specified value.
11067 * @param {String} name The name of the field to set.
11068 * @param {Object} value The value to set the field to.
11070 set : function(name, value){
11071 if(this.data[name] == value){
11075 if(!this.modified){
11076 this.modified = {};
11078 if(typeof this.modified[name] == 'undefined'){
11079 this.modified[name] = this.data[name];
11081 this.data[name] = value;
11082 if(!this.editing && this.store){
11083 this.store.afterEdit(this);
11088 * Get the value of the named field.
11089 * @param {String} name The name of the field to get the value of.
11090 * @return {Object} The value of the field.
11092 get : function(name){
11093 return this.data[name];
11097 beginEdit : function(){
11098 this.editing = true;
11099 this.modified = {};
11103 cancelEdit : function(){
11104 this.editing = false;
11105 delete this.modified;
11109 endEdit : function(){
11110 this.editing = false;
11111 if(this.dirty && this.store){
11112 this.store.afterEdit(this);
11117 * Usually called by the {@link Roo.data.Store} which owns the Record.
11118 * Rejects all changes made to the Record since either creation, or the last commit operation.
11119 * Modified fields are reverted to their original values.
11121 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11122 * of reject operations.
11124 reject : function(){
11125 var m = this.modified;
11127 if(typeof m[n] != "function"){
11128 this.data[n] = m[n];
11131 this.dirty = false;
11132 delete this.modified;
11133 this.editing = false;
11135 this.store.afterReject(this);
11140 * Usually called by the {@link Roo.data.Store} which owns the Record.
11141 * Commits all changes made to the Record since either creation, or the last commit operation.
11143 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11144 * of commit operations.
11146 commit : function(){
11147 this.dirty = false;
11148 delete this.modified;
11149 this.editing = false;
11151 this.store.afterCommit(this);
11156 hasError : function(){
11157 return this.error != null;
11161 clearError : function(){
11166 * Creates a copy of this record.
11167 * @param {String} id (optional) A new record id if you don't want to use this record's id
11170 copy : function(newId) {
11171 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11175 * Ext JS Library 1.1.1
11176 * Copyright(c) 2006-2007, Ext JS, LLC.
11178 * Originally Released Under LGPL - original licence link has changed is not relivant.
11181 * <script type="text/javascript">
11187 * @class Roo.data.Store
11188 * @extends Roo.util.Observable
11189 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11190 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11192 * 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
11193 * has no knowledge of the format of the data returned by the Proxy.<br>
11195 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11196 * instances from the data object. These records are cached and made available through accessor functions.
11198 * Creates a new Store.
11199 * @param {Object} config A config object containing the objects needed for the Store to access data,
11200 * and read the data into Records.
11202 Roo.data.Store = function(config){
11203 this.data = new Roo.util.MixedCollection(false);
11204 this.data.getKey = function(o){
11207 this.baseParams = {};
11209 this.paramNames = {
11214 "multisort" : "_multisort"
11217 if(config && config.data){
11218 this.inlineData = config.data;
11219 delete config.data;
11222 Roo.apply(this, config);
11224 if(this.reader){ // reader passed
11225 this.reader = Roo.factory(this.reader, Roo.data);
11226 this.reader.xmodule = this.xmodule || false;
11227 if(!this.recordType){
11228 this.recordType = this.reader.recordType;
11230 if(this.reader.onMetaChange){
11231 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11235 if(this.recordType){
11236 this.fields = this.recordType.prototype.fields;
11238 this.modified = [];
11242 * @event datachanged
11243 * Fires when the data cache has changed, and a widget which is using this Store
11244 * as a Record cache should refresh its view.
11245 * @param {Store} this
11247 datachanged : true,
11249 * @event metachange
11250 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11251 * @param {Store} this
11252 * @param {Object} meta The JSON metadata
11257 * Fires when Records have been added to the Store
11258 * @param {Store} this
11259 * @param {Roo.data.Record[]} records The array of Records added
11260 * @param {Number} index The index at which the record(s) were added
11265 * Fires when a Record has been removed from the Store
11266 * @param {Store} this
11267 * @param {Roo.data.Record} record The Record that was removed
11268 * @param {Number} index The index at which the record was removed
11273 * Fires when a Record has been updated
11274 * @param {Store} this
11275 * @param {Roo.data.Record} record The Record that was updated
11276 * @param {String} operation The update operation being performed. Value may be one of:
11278 Roo.data.Record.EDIT
11279 Roo.data.Record.REJECT
11280 Roo.data.Record.COMMIT
11286 * Fires when the data cache has been cleared.
11287 * @param {Store} this
11291 * @event beforeload
11292 * Fires before a request is made for a new data object. If the beforeload handler returns false
11293 * the load action will be canceled.
11294 * @param {Store} this
11295 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11299 * @event beforeloadadd
11300 * Fires after a new set of Records has been loaded.
11301 * @param {Store} this
11302 * @param {Roo.data.Record[]} records The Records that were loaded
11303 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11305 beforeloadadd : true,
11308 * Fires after a new set of Records has been loaded, before they are added to the store.
11309 * @param {Store} this
11310 * @param {Roo.data.Record[]} records The Records that were loaded
11311 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11312 * @params {Object} return from reader
11316 * @event loadexception
11317 * Fires if an exception occurs in the Proxy during loading.
11318 * Called with the signature of the Proxy's "loadexception" event.
11319 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11322 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11323 * @param {Object} load options
11324 * @param {Object} jsonData from your request (normally this contains the Exception)
11326 loadexception : true
11330 this.proxy = Roo.factory(this.proxy, Roo.data);
11331 this.proxy.xmodule = this.xmodule || false;
11332 this.relayEvents(this.proxy, ["loadexception"]);
11334 this.sortToggle = {};
11335 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11337 Roo.data.Store.superclass.constructor.call(this);
11339 if(this.inlineData){
11340 this.loadData(this.inlineData);
11341 delete this.inlineData;
11345 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11347 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11348 * without a remote query - used by combo/forms at present.
11352 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11355 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11358 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11359 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11362 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11363 * on any HTTP request
11366 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11369 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11373 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11374 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11376 remoteSort : false,
11379 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11380 * loaded or when a record is removed. (defaults to false).
11382 pruneModifiedRecords : false,
11385 lastOptions : null,
11388 * Add Records to the Store and fires the add event.
11389 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11391 add : function(records){
11392 records = [].concat(records);
11393 for(var i = 0, len = records.length; i < len; i++){
11394 records[i].join(this);
11396 var index = this.data.length;
11397 this.data.addAll(records);
11398 this.fireEvent("add", this, records, index);
11402 * Remove a Record from the Store and fires the remove event.
11403 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11405 remove : function(record){
11406 var index = this.data.indexOf(record);
11407 this.data.removeAt(index);
11409 if(this.pruneModifiedRecords){
11410 this.modified.remove(record);
11412 this.fireEvent("remove", this, record, index);
11416 * Remove all Records from the Store and fires the clear event.
11418 removeAll : function(){
11420 if(this.pruneModifiedRecords){
11421 this.modified = [];
11423 this.fireEvent("clear", this);
11427 * Inserts Records to the Store at the given index and fires the add event.
11428 * @param {Number} index The start index at which to insert the passed Records.
11429 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11431 insert : function(index, records){
11432 records = [].concat(records);
11433 for(var i = 0, len = records.length; i < len; i++){
11434 this.data.insert(index, records[i]);
11435 records[i].join(this);
11437 this.fireEvent("add", this, records, index);
11441 * Get the index within the cache of the passed Record.
11442 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11443 * @return {Number} The index of the passed Record. Returns -1 if not found.
11445 indexOf : function(record){
11446 return this.data.indexOf(record);
11450 * Get the index within the cache of the Record with the passed id.
11451 * @param {String} id The id of the Record to find.
11452 * @return {Number} The index of the Record. Returns -1 if not found.
11454 indexOfId : function(id){
11455 return this.data.indexOfKey(id);
11459 * Get the Record with the specified id.
11460 * @param {String} id The id of the Record to find.
11461 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11463 getById : function(id){
11464 return this.data.key(id);
11468 * Get the Record at the specified index.
11469 * @param {Number} index The index of the Record to find.
11470 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11472 getAt : function(index){
11473 return this.data.itemAt(index);
11477 * Returns a range of Records between specified indices.
11478 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11479 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11480 * @return {Roo.data.Record[]} An array of Records
11482 getRange : function(start, end){
11483 return this.data.getRange(start, end);
11487 storeOptions : function(o){
11488 o = Roo.apply({}, o);
11491 this.lastOptions = o;
11495 * Loads the Record cache from the configured Proxy using the configured Reader.
11497 * If using remote paging, then the first load call must specify the <em>start</em>
11498 * and <em>limit</em> properties in the options.params property to establish the initial
11499 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11501 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11502 * and this call will return before the new data has been loaded. Perform any post-processing
11503 * in a callback function, or in a "load" event handler.</strong>
11505 * @param {Object} options An object containing properties which control loading options:<ul>
11506 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11507 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11508 * passed the following arguments:<ul>
11509 * <li>r : Roo.data.Record[]</li>
11510 * <li>options: Options object from the load call</li>
11511 * <li>success: Boolean success indicator</li></ul></li>
11512 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11513 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11516 load : function(options){
11517 options = options || {};
11518 if(this.fireEvent("beforeload", this, options) !== false){
11519 this.storeOptions(options);
11520 var p = Roo.apply(options.params || {}, this.baseParams);
11521 // if meta was not loaded from remote source.. try requesting it.
11522 if (!this.reader.metaFromRemote) {
11523 p._requestMeta = 1;
11525 if(this.sortInfo && this.remoteSort){
11526 var pn = this.paramNames;
11527 p[pn["sort"]] = this.sortInfo.field;
11528 p[pn["dir"]] = this.sortInfo.direction;
11530 if (this.multiSort) {
11531 var pn = this.paramNames;
11532 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11535 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11540 * Reloads the Record cache from the configured Proxy using the configured Reader and
11541 * the options from the last load operation performed.
11542 * @param {Object} options (optional) An object containing properties which may override the options
11543 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11544 * the most recently used options are reused).
11546 reload : function(options){
11547 this.load(Roo.applyIf(options||{}, this.lastOptions));
11551 // Called as a callback by the Reader during a load operation.
11552 loadRecords : function(o, options, success){
11553 if(!o || success === false){
11554 if(success !== false){
11555 this.fireEvent("load", this, [], options, o);
11557 if(options.callback){
11558 options.callback.call(options.scope || this, [], options, false);
11562 // if data returned failure - throw an exception.
11563 if (o.success === false) {
11564 // show a message if no listener is registered.
11565 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11566 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11568 // loadmask wil be hooked into this..
11569 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11572 var r = o.records, t = o.totalRecords || r.length;
11574 this.fireEvent("beforeloadadd", this, r, options, o);
11576 if(!options || options.add !== true){
11577 if(this.pruneModifiedRecords){
11578 this.modified = [];
11580 for(var i = 0, len = r.length; i < len; i++){
11584 this.data = this.snapshot;
11585 delete this.snapshot;
11588 this.data.addAll(r);
11589 this.totalLength = t;
11591 this.fireEvent("datachanged", this);
11593 this.totalLength = Math.max(t, this.data.length+r.length);
11597 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11599 var e = new Roo.data.Record({});
11601 e.set(this.parent.displayField, this.parent.emptyTitle);
11602 e.set(this.parent.valueField, '');
11607 this.fireEvent("load", this, r, options, o);
11608 if(options.callback){
11609 options.callback.call(options.scope || this, r, options, true);
11615 * Loads data from a passed data block. A Reader which understands the format of the data
11616 * must have been configured in the constructor.
11617 * @param {Object} data The data block from which to read the Records. The format of the data expected
11618 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11619 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11621 loadData : function(o, append){
11622 var r = this.reader.readRecords(o);
11623 this.loadRecords(r, {add: append}, true);
11627 * Gets the number of cached records.
11629 * <em>If using paging, this may not be the total size of the dataset. If the data object
11630 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11631 * the data set size</em>
11633 getCount : function(){
11634 return this.data.length || 0;
11638 * Gets the total number of records in the dataset as returned by the server.
11640 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11641 * the dataset size</em>
11643 getTotalCount : function(){
11644 return this.totalLength || 0;
11648 * Returns the sort state of the Store as an object with two properties:
11650 field {String} The name of the field by which the Records are sorted
11651 direction {String} The sort order, "ASC" or "DESC"
11654 getSortState : function(){
11655 return this.sortInfo;
11659 applySort : function(){
11660 if(this.sortInfo && !this.remoteSort){
11661 var s = this.sortInfo, f = s.field;
11662 var st = this.fields.get(f).sortType;
11663 var fn = function(r1, r2){
11664 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11665 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11667 this.data.sort(s.direction, fn);
11668 if(this.snapshot && this.snapshot != this.data){
11669 this.snapshot.sort(s.direction, fn);
11675 * Sets the default sort column and order to be used by the next load operation.
11676 * @param {String} fieldName The name of the field to sort by.
11677 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11679 setDefaultSort : function(field, dir){
11680 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11684 * Sort the Records.
11685 * If remote sorting is used, the sort is performed on the server, and the cache is
11686 * reloaded. If local sorting is used, the cache is sorted internally.
11687 * @param {String} fieldName The name of the field to sort by.
11688 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11690 sort : function(fieldName, dir){
11691 var f = this.fields.get(fieldName);
11693 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11695 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11696 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11701 this.sortToggle[f.name] = dir;
11702 this.sortInfo = {field: f.name, direction: dir};
11703 if(!this.remoteSort){
11705 this.fireEvent("datachanged", this);
11707 this.load(this.lastOptions);
11712 * Calls the specified function for each of the Records in the cache.
11713 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11714 * Returning <em>false</em> aborts and exits the iteration.
11715 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11717 each : function(fn, scope){
11718 this.data.each(fn, scope);
11722 * Gets all records modified since the last commit. Modified records are persisted across load operations
11723 * (e.g., during paging).
11724 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11726 getModifiedRecords : function(){
11727 return this.modified;
11731 createFilterFn : function(property, value, anyMatch){
11732 if(!value.exec){ // not a regex
11733 value = String(value);
11734 if(value.length == 0){
11737 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11739 return function(r){
11740 return value.test(r.data[property]);
11745 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11746 * @param {String} property A field on your records
11747 * @param {Number} start The record index to start at (defaults to 0)
11748 * @param {Number} end The last record index to include (defaults to length - 1)
11749 * @return {Number} The sum
11751 sum : function(property, start, end){
11752 var rs = this.data.items, v = 0;
11753 start = start || 0;
11754 end = (end || end === 0) ? end : rs.length-1;
11756 for(var i = start; i <= end; i++){
11757 v += (rs[i].data[property] || 0);
11763 * Filter the records by a specified property.
11764 * @param {String} field A field on your records
11765 * @param {String/RegExp} value Either a string that the field
11766 * should start with or a RegExp to test against the field
11767 * @param {Boolean} anyMatch True to match any part not just the beginning
11769 filter : function(property, value, anyMatch){
11770 var fn = this.createFilterFn(property, value, anyMatch);
11771 return fn ? this.filterBy(fn) : this.clearFilter();
11775 * Filter by a function. The specified function will be called with each
11776 * record in this data source. If the function returns true the record is included,
11777 * otherwise it is filtered.
11778 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11779 * @param {Object} scope (optional) The scope of the function (defaults to this)
11781 filterBy : function(fn, scope){
11782 this.snapshot = this.snapshot || this.data;
11783 this.data = this.queryBy(fn, scope||this);
11784 this.fireEvent("datachanged", this);
11788 * Query the records by a specified property.
11789 * @param {String} field A field on your records
11790 * @param {String/RegExp} value Either a string that the field
11791 * should start with or a RegExp to test against the field
11792 * @param {Boolean} anyMatch True to match any part not just the beginning
11793 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11795 query : function(property, value, anyMatch){
11796 var fn = this.createFilterFn(property, value, anyMatch);
11797 return fn ? this.queryBy(fn) : this.data.clone();
11801 * Query by a function. The specified function will be called with each
11802 * record in this data source. If the function returns true the record is included
11804 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11805 * @param {Object} scope (optional) The scope of the function (defaults to this)
11806 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11808 queryBy : function(fn, scope){
11809 var data = this.snapshot || this.data;
11810 return data.filterBy(fn, scope||this);
11814 * Collects unique values for a particular dataIndex from this store.
11815 * @param {String} dataIndex The property to collect
11816 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11817 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11818 * @return {Array} An array of the unique values
11820 collect : function(dataIndex, allowNull, bypassFilter){
11821 var d = (bypassFilter === true && this.snapshot) ?
11822 this.snapshot.items : this.data.items;
11823 var v, sv, r = [], l = {};
11824 for(var i = 0, len = d.length; i < len; i++){
11825 v = d[i].data[dataIndex];
11827 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11836 * Revert to a view of the Record cache with no filtering applied.
11837 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11839 clearFilter : function(suppressEvent){
11840 if(this.snapshot && this.snapshot != this.data){
11841 this.data = this.snapshot;
11842 delete this.snapshot;
11843 if(suppressEvent !== true){
11844 this.fireEvent("datachanged", this);
11850 afterEdit : function(record){
11851 if(this.modified.indexOf(record) == -1){
11852 this.modified.push(record);
11854 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11858 afterReject : function(record){
11859 this.modified.remove(record);
11860 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11864 afterCommit : function(record){
11865 this.modified.remove(record);
11866 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11870 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11871 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11873 commitChanges : function(){
11874 var m = this.modified.slice(0);
11875 this.modified = [];
11876 for(var i = 0, len = m.length; i < len; i++){
11882 * Cancel outstanding changes on all changed records.
11884 rejectChanges : function(){
11885 var m = this.modified.slice(0);
11886 this.modified = [];
11887 for(var i = 0, len = m.length; i < len; i++){
11892 onMetaChange : function(meta, rtype, o){
11893 this.recordType = rtype;
11894 this.fields = rtype.prototype.fields;
11895 delete this.snapshot;
11896 this.sortInfo = meta.sortInfo || this.sortInfo;
11897 this.modified = [];
11898 this.fireEvent('metachange', this, this.reader.meta);
11901 moveIndex : function(data, type)
11903 var index = this.indexOf(data);
11905 var newIndex = index + type;
11909 this.insert(newIndex, data);
11914 * Ext JS Library 1.1.1
11915 * Copyright(c) 2006-2007, Ext JS, LLC.
11917 * Originally Released Under LGPL - original licence link has changed is not relivant.
11920 * <script type="text/javascript">
11924 * @class Roo.data.SimpleStore
11925 * @extends Roo.data.Store
11926 * Small helper class to make creating Stores from Array data easier.
11927 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11928 * @cfg {Array} fields An array of field definition objects, or field name strings.
11929 * @cfg {Array} data The multi-dimensional array of data
11931 * @param {Object} config
11933 Roo.data.SimpleStore = function(config){
11934 Roo.data.SimpleStore.superclass.constructor.call(this, {
11936 reader: new Roo.data.ArrayReader({
11939 Roo.data.Record.create(config.fields)
11941 proxy : new Roo.data.MemoryProxy(config.data)
11945 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11947 * Ext JS Library 1.1.1
11948 * Copyright(c) 2006-2007, Ext JS, LLC.
11950 * Originally Released Under LGPL - original licence link has changed is not relivant.
11953 * <script type="text/javascript">
11958 * @extends Roo.data.Store
11959 * @class Roo.data.JsonStore
11960 * Small helper class to make creating Stores for JSON data easier. <br/>
11962 var store = new Roo.data.JsonStore({
11963 url: 'get-images.php',
11965 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11968 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11969 * JsonReader and HttpProxy (unless inline data is provided).</b>
11970 * @cfg {Array} fields An array of field definition objects, or field name strings.
11972 * @param {Object} config
11974 Roo.data.JsonStore = function(c){
11975 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11976 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11977 reader: new Roo.data.JsonReader(c, c.fields)
11980 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11982 * Ext JS Library 1.1.1
11983 * Copyright(c) 2006-2007, Ext JS, LLC.
11985 * Originally Released Under LGPL - original licence link has changed is not relivant.
11988 * <script type="text/javascript">
11992 Roo.data.Field = function(config){
11993 if(typeof config == "string"){
11994 config = {name: config};
11996 Roo.apply(this, config);
11999 this.type = "auto";
12002 var st = Roo.data.SortTypes;
12003 // named sortTypes are supported, here we look them up
12004 if(typeof this.sortType == "string"){
12005 this.sortType = st[this.sortType];
12008 // set default sortType for strings and dates
12009 if(!this.sortType){
12012 this.sortType = st.asUCString;
12015 this.sortType = st.asDate;
12018 this.sortType = st.none;
12023 var stripRe = /[\$,%]/g;
12025 // prebuilt conversion function for this field, instead of
12026 // switching every time we're reading a value
12028 var cv, dateFormat = this.dateFormat;
12033 cv = function(v){ return v; };
12036 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12040 return v !== undefined && v !== null && v !== '' ?
12041 parseInt(String(v).replace(stripRe, ""), 10) : '';
12046 return v !== undefined && v !== null && v !== '' ?
12047 parseFloat(String(v).replace(stripRe, ""), 10) : '';
12052 cv = function(v){ return v === true || v === "true" || v == 1; };
12059 if(v instanceof Date){
12063 if(dateFormat == "timestamp"){
12064 return new Date(v*1000);
12066 return Date.parseDate(v, dateFormat);
12068 var parsed = Date.parse(v);
12069 return parsed ? new Date(parsed) : null;
12078 Roo.data.Field.prototype = {
12086 * Ext JS Library 1.1.1
12087 * Copyright(c) 2006-2007, Ext JS, LLC.
12089 * Originally Released Under LGPL - original licence link has changed is not relivant.
12092 * <script type="text/javascript">
12095 // Base class for reading structured data from a data source. This class is intended to be
12096 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12099 * @class Roo.data.DataReader
12100 * Base class for reading structured data from a data source. This class is intended to be
12101 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12104 Roo.data.DataReader = function(meta, recordType){
12108 this.recordType = recordType instanceof Array ?
12109 Roo.data.Record.create(recordType) : recordType;
12112 Roo.data.DataReader.prototype = {
12114 * Create an empty record
12115 * @param {Object} data (optional) - overlay some values
12116 * @return {Roo.data.Record} record created.
12118 newRow : function(d) {
12120 this.recordType.prototype.fields.each(function(c) {
12122 case 'int' : da[c.name] = 0; break;
12123 case 'date' : da[c.name] = new Date(); break;
12124 case 'float' : da[c.name] = 0.0; break;
12125 case 'boolean' : da[c.name] = false; break;
12126 default : da[c.name] = ""; break;
12130 return new this.recordType(Roo.apply(da, d));
12135 * Ext JS Library 1.1.1
12136 * Copyright(c) 2006-2007, Ext JS, LLC.
12138 * Originally Released Under LGPL - original licence link has changed is not relivant.
12141 * <script type="text/javascript">
12145 * @class Roo.data.DataProxy
12146 * @extends Roo.data.Observable
12147 * This class is an abstract base class for implementations which provide retrieval of
12148 * unformatted data objects.<br>
12150 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12151 * (of the appropriate type which knows how to parse the data object) to provide a block of
12152 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12154 * Custom implementations must implement the load method as described in
12155 * {@link Roo.data.HttpProxy#load}.
12157 Roo.data.DataProxy = function(){
12160 * @event beforeload
12161 * Fires before a network request is made to retrieve a data object.
12162 * @param {Object} This DataProxy object.
12163 * @param {Object} params The params parameter to the load function.
12168 * Fires before the load method's callback is called.
12169 * @param {Object} This DataProxy object.
12170 * @param {Object} o The data object.
12171 * @param {Object} arg The callback argument object passed to the load function.
12175 * @event loadexception
12176 * Fires if an Exception occurs during data retrieval.
12177 * @param {Object} This DataProxy object.
12178 * @param {Object} o The data object.
12179 * @param {Object} arg The callback argument object passed to the load function.
12180 * @param {Object} e The Exception.
12182 loadexception : true
12184 Roo.data.DataProxy.superclass.constructor.call(this);
12187 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12190 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12194 * Ext JS Library 1.1.1
12195 * Copyright(c) 2006-2007, Ext JS, LLC.
12197 * Originally Released Under LGPL - original licence link has changed is not relivant.
12200 * <script type="text/javascript">
12203 * @class Roo.data.MemoryProxy
12204 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12205 * to the Reader when its load method is called.
12207 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12209 Roo.data.MemoryProxy = function(data){
12213 Roo.data.MemoryProxy.superclass.constructor.call(this);
12217 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12220 * Load data from the requested source (in this case an in-memory
12221 * data object passed to the constructor), read the data object into
12222 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12223 * process that block using the passed callback.
12224 * @param {Object} params This parameter is not used by the MemoryProxy class.
12225 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12226 * object into a block of Roo.data.Records.
12227 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12228 * The function must be passed <ul>
12229 * <li>The Record block object</li>
12230 * <li>The "arg" argument from the load function</li>
12231 * <li>A boolean success indicator</li>
12233 * @param {Object} scope The scope in which to call the callback
12234 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12236 load : function(params, reader, callback, scope, arg){
12237 params = params || {};
12240 result = reader.readRecords(this.data);
12242 this.fireEvent("loadexception", this, arg, null, e);
12243 callback.call(scope, null, arg, false);
12246 callback.call(scope, result, arg, true);
12250 update : function(params, records){
12255 * Ext JS Library 1.1.1
12256 * Copyright(c) 2006-2007, Ext JS, LLC.
12258 * Originally Released Under LGPL - original licence link has changed is not relivant.
12261 * <script type="text/javascript">
12264 * @class Roo.data.HttpProxy
12265 * @extends Roo.data.DataProxy
12266 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12267 * configured to reference a certain URL.<br><br>
12269 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12270 * from which the running page was served.<br><br>
12272 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12274 * Be aware that to enable the browser to parse an XML document, the server must set
12275 * the Content-Type header in the HTTP response to "text/xml".
12277 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12278 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12279 * will be used to make the request.
12281 Roo.data.HttpProxy = function(conn){
12282 Roo.data.HttpProxy.superclass.constructor.call(this);
12283 // is conn a conn config or a real conn?
12285 this.useAjax = !conn || !conn.events;
12289 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12290 // thse are take from connection...
12293 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12296 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12297 * extra parameters to each request made by this object. (defaults to undefined)
12300 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12301 * to each request made by this object. (defaults to undefined)
12304 * @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)
12307 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12310 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12316 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12320 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12321 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12322 * a finer-grained basis than the DataProxy events.
12324 getConnection : function(){
12325 return this.useAjax ? Roo.Ajax : this.conn;
12329 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12330 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12331 * process that block using the passed callback.
12332 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12333 * for the request to the remote server.
12334 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12335 * object into a block of Roo.data.Records.
12336 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12337 * The function must be passed <ul>
12338 * <li>The Record block object</li>
12339 * <li>The "arg" argument from the load function</li>
12340 * <li>A boolean success indicator</li>
12342 * @param {Object} scope The scope in which to call the callback
12343 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12345 load : function(params, reader, callback, scope, arg){
12346 if(this.fireEvent("beforeload", this, params) !== false){
12348 params : params || {},
12350 callback : callback,
12355 callback : this.loadResponse,
12359 Roo.applyIf(o, this.conn);
12360 if(this.activeRequest){
12361 Roo.Ajax.abort(this.activeRequest);
12363 this.activeRequest = Roo.Ajax.request(o);
12365 this.conn.request(o);
12368 callback.call(scope||this, null, arg, false);
12373 loadResponse : function(o, success, response){
12374 delete this.activeRequest;
12376 this.fireEvent("loadexception", this, o, response);
12377 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12382 result = o.reader.read(response);
12384 this.fireEvent("loadexception", this, o, response, e);
12385 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12389 this.fireEvent("load", this, o, o.request.arg);
12390 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12394 update : function(dataSet){
12399 updateResponse : function(dataSet){
12404 * Ext JS Library 1.1.1
12405 * Copyright(c) 2006-2007, Ext JS, LLC.
12407 * Originally Released Under LGPL - original licence link has changed is not relivant.
12410 * <script type="text/javascript">
12414 * @class Roo.data.ScriptTagProxy
12415 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12416 * other than the originating domain of the running page.<br><br>
12418 * <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
12419 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12421 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12422 * source code that is used as the source inside a <script> tag.<br><br>
12424 * In order for the browser to process the returned data, the server must wrap the data object
12425 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12426 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12427 * depending on whether the callback name was passed:
12430 boolean scriptTag = false;
12431 String cb = request.getParameter("callback");
12434 response.setContentType("text/javascript");
12436 response.setContentType("application/x-json");
12438 Writer out = response.getWriter();
12440 out.write(cb + "(");
12442 out.print(dataBlock.toJsonString());
12449 * @param {Object} config A configuration object.
12451 Roo.data.ScriptTagProxy = function(config){
12452 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12453 Roo.apply(this, config);
12454 this.head = document.getElementsByTagName("head")[0];
12457 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12459 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12461 * @cfg {String} url The URL from which to request the data object.
12464 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12468 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12469 * the server the name of the callback function set up by the load call to process the returned data object.
12470 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12471 * javascript output which calls this named function passing the data object as its only parameter.
12473 callbackParam : "callback",
12475 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12476 * name to the request.
12481 * Load data from the configured URL, read the data object into
12482 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12483 * process that block using the passed callback.
12484 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12485 * for the request to the remote server.
12486 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12487 * object into a block of Roo.data.Records.
12488 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12489 * The function must be passed <ul>
12490 * <li>The Record block object</li>
12491 * <li>The "arg" argument from the load function</li>
12492 * <li>A boolean success indicator</li>
12494 * @param {Object} scope The scope in which to call the callback
12495 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12497 load : function(params, reader, callback, scope, arg){
12498 if(this.fireEvent("beforeload", this, params) !== false){
12500 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12502 var url = this.url;
12503 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12505 url += "&_dc=" + (new Date().getTime());
12507 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12510 cb : "stcCallback"+transId,
12511 scriptId : "stcScript"+transId,
12515 callback : callback,
12521 window[trans.cb] = function(o){
12522 conn.handleResponse(o, trans);
12525 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12527 if(this.autoAbort !== false){
12531 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12533 var script = document.createElement("script");
12534 script.setAttribute("src", url);
12535 script.setAttribute("type", "text/javascript");
12536 script.setAttribute("id", trans.scriptId);
12537 this.head.appendChild(script);
12539 this.trans = trans;
12541 callback.call(scope||this, null, arg, false);
12546 isLoading : function(){
12547 return this.trans ? true : false;
12551 * Abort the current server request.
12553 abort : function(){
12554 if(this.isLoading()){
12555 this.destroyTrans(this.trans);
12560 destroyTrans : function(trans, isLoaded){
12561 this.head.removeChild(document.getElementById(trans.scriptId));
12562 clearTimeout(trans.timeoutId);
12564 window[trans.cb] = undefined;
12566 delete window[trans.cb];
12569 // if hasn't been loaded, wait for load to remove it to prevent script error
12570 window[trans.cb] = function(){
12571 window[trans.cb] = undefined;
12573 delete window[trans.cb];
12580 handleResponse : function(o, trans){
12581 this.trans = false;
12582 this.destroyTrans(trans, true);
12585 result = trans.reader.readRecords(o);
12587 this.fireEvent("loadexception", this, o, trans.arg, e);
12588 trans.callback.call(trans.scope||window, null, trans.arg, false);
12591 this.fireEvent("load", this, o, trans.arg);
12592 trans.callback.call(trans.scope||window, result, trans.arg, true);
12596 handleFailure : function(trans){
12597 this.trans = false;
12598 this.destroyTrans(trans, false);
12599 this.fireEvent("loadexception", this, null, trans.arg);
12600 trans.callback.call(trans.scope||window, null, trans.arg, false);
12604 * Ext JS Library 1.1.1
12605 * Copyright(c) 2006-2007, Ext JS, LLC.
12607 * Originally Released Under LGPL - original licence link has changed is not relivant.
12610 * <script type="text/javascript">
12614 * @class Roo.data.JsonReader
12615 * @extends Roo.data.DataReader
12616 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12617 * based on mappings in a provided Roo.data.Record constructor.
12619 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12620 * in the reply previously.
12625 var RecordDef = Roo.data.Record.create([
12626 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12627 {name: 'occupation'} // This field will use "occupation" as the mapping.
12629 var myReader = new Roo.data.JsonReader({
12630 totalProperty: "results", // The property which contains the total dataset size (optional)
12631 root: "rows", // The property which contains an Array of row objects
12632 id: "id" // The property within each row object that provides an ID for the record (optional)
12636 * This would consume a JSON file like this:
12638 { 'results': 2, 'rows': [
12639 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12640 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12643 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12644 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12645 * paged from the remote server.
12646 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12647 * @cfg {String} root name of the property which contains the Array of row objects.
12648 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12649 * @cfg {Array} fields Array of field definition objects
12651 * Create a new JsonReader
12652 * @param {Object} meta Metadata configuration options
12653 * @param {Object} recordType Either an Array of field definition objects,
12654 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12656 Roo.data.JsonReader = function(meta, recordType){
12659 // set some defaults:
12660 Roo.applyIf(meta, {
12661 totalProperty: 'total',
12662 successProperty : 'success',
12667 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12669 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12672 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12673 * Used by Store query builder to append _requestMeta to params.
12676 metaFromRemote : false,
12678 * This method is only used by a DataProxy which has retrieved data from a remote server.
12679 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12680 * @return {Object} data A data block which is used by an Roo.data.Store object as
12681 * a cache of Roo.data.Records.
12683 read : function(response){
12684 var json = response.responseText;
12686 var o = /* eval:var:o */ eval("("+json+")");
12688 throw {message: "JsonReader.read: Json object not found"};
12694 this.metaFromRemote = true;
12695 this.meta = o.metaData;
12696 this.recordType = Roo.data.Record.create(o.metaData.fields);
12697 this.onMetaChange(this.meta, this.recordType, o);
12699 return this.readRecords(o);
12702 // private function a store will implement
12703 onMetaChange : function(meta, recordType, o){
12710 simpleAccess: function(obj, subsc) {
12717 getJsonAccessor: function(){
12719 return function(expr) {
12721 return(re.test(expr))
12722 ? new Function("obj", "return obj." + expr)
12727 return Roo.emptyFn;
12732 * Create a data block containing Roo.data.Records from an XML document.
12733 * @param {Object} o An object which contains an Array of row objects in the property specified
12734 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12735 * which contains the total size of the dataset.
12736 * @return {Object} data A data block which is used by an Roo.data.Store object as
12737 * a cache of Roo.data.Records.
12739 readRecords : function(o){
12741 * After any data loads, the raw JSON data is available for further custom processing.
12745 var s = this.meta, Record = this.recordType,
12746 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12748 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12750 if(s.totalProperty) {
12751 this.getTotal = this.getJsonAccessor(s.totalProperty);
12753 if(s.successProperty) {
12754 this.getSuccess = this.getJsonAccessor(s.successProperty);
12756 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12758 var g = this.getJsonAccessor(s.id);
12759 this.getId = function(rec) {
12761 return (r === undefined || r === "") ? null : r;
12764 this.getId = function(){return null;};
12767 for(var jj = 0; jj < fl; jj++){
12769 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12770 this.ef[jj] = this.getJsonAccessor(map);
12774 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12775 if(s.totalProperty){
12776 var vt = parseInt(this.getTotal(o), 10);
12781 if(s.successProperty){
12782 var vs = this.getSuccess(o);
12783 if(vs === false || vs === 'false'){
12788 for(var i = 0; i < c; i++){
12791 var id = this.getId(n);
12792 for(var j = 0; j < fl; j++){
12794 var v = this.ef[j](n);
12796 Roo.log('missing convert for ' + f.name);
12800 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12802 var record = new Record(values, id);
12804 records[i] = record;
12810 totalRecords : totalRecords
12815 * Ext JS Library 1.1.1
12816 * Copyright(c) 2006-2007, Ext JS, LLC.
12818 * Originally Released Under LGPL - original licence link has changed is not relivant.
12821 * <script type="text/javascript">
12825 * @class Roo.data.ArrayReader
12826 * @extends Roo.data.DataReader
12827 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12828 * Each element of that Array represents a row of data fields. The
12829 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12830 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12834 var RecordDef = Roo.data.Record.create([
12835 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12836 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12838 var myReader = new Roo.data.ArrayReader({
12839 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12843 * This would consume an Array like this:
12845 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12847 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12849 * Create a new JsonReader
12850 * @param {Object} meta Metadata configuration options.
12851 * @param {Object} recordType Either an Array of field definition objects
12852 * as specified to {@link Roo.data.Record#create},
12853 * or an {@link Roo.data.Record} object
12854 * created using {@link Roo.data.Record#create}.
12856 Roo.data.ArrayReader = function(meta, recordType){
12857 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12860 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12862 * Create a data block containing Roo.data.Records from an XML document.
12863 * @param {Object} o An Array of row objects which represents the dataset.
12864 * @return {Object} data A data block which is used by an Roo.data.Store object as
12865 * a cache of Roo.data.Records.
12867 readRecords : function(o){
12868 var sid = this.meta ? this.meta.id : null;
12869 var recordType = this.recordType, fields = recordType.prototype.fields;
12872 for(var i = 0; i < root.length; i++){
12875 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12876 for(var j = 0, jlen = fields.length; j < jlen; j++){
12877 var f = fields.items[j];
12878 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12879 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12881 values[f.name] = v;
12883 var record = new recordType(values, id);
12885 records[records.length] = record;
12889 totalRecords : records.length
12898 * @class Roo.bootstrap.ComboBox
12899 * @extends Roo.bootstrap.TriggerField
12900 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12901 * @cfg {Boolean} append (true|false) default false
12902 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12903 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12904 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12905 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12906 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12907 * @cfg {Boolean} animate default true
12908 * @cfg {Boolean} emptyResultText only for touch device
12909 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12910 * @cfg {String} emptyTitle default ''
12912 * Create a new ComboBox.
12913 * @param {Object} config Configuration options
12915 Roo.bootstrap.ComboBox = function(config){
12916 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12920 * Fires when the dropdown list is expanded
12921 * @param {Roo.bootstrap.ComboBox} combo This combo box
12926 * Fires when the dropdown list is collapsed
12927 * @param {Roo.bootstrap.ComboBox} combo This combo box
12931 * @event beforeselect
12932 * Fires before a list item is selected. Return false to cancel the selection.
12933 * @param {Roo.bootstrap.ComboBox} combo This combo box
12934 * @param {Roo.data.Record} record The data record returned from the underlying store
12935 * @param {Number} index The index of the selected item in the dropdown list
12937 'beforeselect' : true,
12940 * Fires when a list item is selected
12941 * @param {Roo.bootstrap.ComboBox} combo This combo box
12942 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12943 * @param {Number} index The index of the selected item in the dropdown list
12947 * @event beforequery
12948 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12949 * The event object passed has these properties:
12950 * @param {Roo.bootstrap.ComboBox} combo This combo box
12951 * @param {String} query The query
12952 * @param {Boolean} forceAll true to force "all" query
12953 * @param {Boolean} cancel true to cancel the query
12954 * @param {Object} e The query event object
12956 'beforequery': true,
12959 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12960 * @param {Roo.bootstrap.ComboBox} combo This combo box
12965 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12966 * @param {Roo.bootstrap.ComboBox} combo This combo box
12967 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12972 * Fires when the remove value from the combobox array
12973 * @param {Roo.bootstrap.ComboBox} combo This combo box
12977 * @event afterremove
12978 * Fires when the remove value from the combobox array
12979 * @param {Roo.bootstrap.ComboBox} combo This combo box
12981 'afterremove' : true,
12983 * @event specialfilter
12984 * Fires when specialfilter
12985 * @param {Roo.bootstrap.ComboBox} combo This combo box
12987 'specialfilter' : true,
12990 * Fires when tick the element
12991 * @param {Roo.bootstrap.ComboBox} combo This combo box
12995 * @event touchviewdisplay
12996 * Fires when touch view require special display (default is using displayField)
12997 * @param {Roo.bootstrap.ComboBox} combo This combo box
12998 * @param {Object} cfg set html .
13000 'touchviewdisplay' : true
13005 this.tickItems = [];
13007 this.selectedIndex = -1;
13008 if(this.mode == 'local'){
13009 if(config.queryDelay === undefined){
13010 this.queryDelay = 10;
13012 if(config.minChars === undefined){
13018 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13021 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13022 * rendering into an Roo.Editor, defaults to false)
13025 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13026 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13029 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13032 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13033 * the dropdown list (defaults to undefined, with no header element)
13037 * @cfg {String/Roo.Template} tpl The template to use to render the output
13041 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13043 listWidth: undefined,
13045 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13046 * mode = 'remote' or 'text' if mode = 'local')
13048 displayField: undefined,
13051 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13052 * mode = 'remote' or 'value' if mode = 'local').
13053 * Note: use of a valueField requires the user make a selection
13054 * in order for a value to be mapped.
13056 valueField: undefined,
13058 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13063 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13064 * field's data value (defaults to the underlying DOM element's name)
13066 hiddenName: undefined,
13068 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13072 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13074 selectedClass: 'active',
13077 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13081 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13082 * anchor positions (defaults to 'tl-bl')
13084 listAlign: 'tl-bl?',
13086 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13090 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13091 * query specified by the allQuery config option (defaults to 'query')
13093 triggerAction: 'query',
13095 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13096 * (defaults to 4, does not apply if editable = false)
13100 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13101 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13105 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13106 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13110 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13111 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13115 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13116 * when editable = true (defaults to false)
13118 selectOnFocus:false,
13120 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13122 queryParam: 'query',
13124 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13125 * when mode = 'remote' (defaults to 'Loading...')
13127 loadingText: 'Loading...',
13129 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13133 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13137 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13138 * traditional select (defaults to true)
13142 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13146 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13150 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13151 * listWidth has a higher value)
13155 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13156 * allow the user to set arbitrary text into the field (defaults to false)
13158 forceSelection:false,
13160 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13161 * if typeAhead = true (defaults to 250)
13163 typeAheadDelay : 250,
13165 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13166 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13168 valueNotFoundText : undefined,
13170 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13172 blockFocus : false,
13175 * @cfg {Boolean} disableClear Disable showing of clear button.
13177 disableClear : false,
13179 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13181 alwaysQuery : false,
13184 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13189 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13191 invalidClass : "has-warning",
13194 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13196 validClass : "has-success",
13199 * @cfg {Boolean} specialFilter (true|false) special filter default false
13201 specialFilter : false,
13204 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13206 mobileTouchView : true,
13209 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13211 useNativeIOS : false,
13214 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13216 mobile_restrict_height : false,
13218 ios_options : false,
13230 btnPosition : 'right',
13231 triggerList : true,
13232 showToggleBtn : true,
13234 emptyResultText: 'Empty',
13235 triggerText : 'Select',
13238 // element that contains real text value.. (when hidden is used..)
13240 getAutoCreate : function()
13245 * Render classic select for iso
13248 if(Roo.isIOS && this.useNativeIOS){
13249 cfg = this.getAutoCreateNativeIOS();
13257 if(Roo.isTouch && this.mobileTouchView){
13258 cfg = this.getAutoCreateTouchView();
13265 if(!this.tickable){
13266 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13271 * ComboBox with tickable selections
13274 var align = this.labelAlign || this.parentLabelAlign();
13277 cls : 'form-group roo-combobox-tickable' //input-group
13280 var btn_text_select = '';
13281 var btn_text_done = '';
13282 var btn_text_cancel = '';
13284 if (this.btn_text_show) {
13285 btn_text_select = 'Select';
13286 btn_text_done = 'Done';
13287 btn_text_cancel = 'Cancel';
13292 cls : 'tickable-buttons',
13297 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13298 //html : this.triggerText
13299 html: btn_text_select
13305 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13307 html: btn_text_done
13313 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13315 html: btn_text_cancel
13321 buttons.cn.unshift({
13323 cls: 'roo-select2-search-field-input'
13329 Roo.each(buttons.cn, function(c){
13331 c.cls += ' btn-' + _this.size;
13334 if (_this.disabled) {
13345 cls: 'form-hidden-field'
13349 cls: 'roo-select2-choices',
13353 cls: 'roo-select2-search-field',
13364 cls: 'roo-select2-container input-group roo-select2-container-multi',
13370 // cls: 'typeahead typeahead-long dropdown-menu',
13371 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13376 if(this.hasFeedback && !this.allowBlank){
13380 cls: 'glyphicon form-control-feedback'
13383 combobox.cn.push(feedback);
13388 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13389 tooltip : 'This field is required'
13391 if (Roo.bootstrap.version == 4) {
13394 style : 'display:none'
13397 if (align ==='left' && this.fieldLabel.length) {
13399 cfg.cls += ' roo-form-group-label-left row';
13406 cls : 'control-label col-form-label',
13407 html : this.fieldLabel
13419 var labelCfg = cfg.cn[1];
13420 var contentCfg = cfg.cn[2];
13423 if(this.indicatorpos == 'right'){
13429 cls : 'control-label col-form-label',
13433 html : this.fieldLabel
13449 labelCfg = cfg.cn[0];
13450 contentCfg = cfg.cn[1];
13454 if(this.labelWidth > 12){
13455 labelCfg.style = "width: " + this.labelWidth + 'px';
13458 if(this.labelWidth < 13 && this.labelmd == 0){
13459 this.labelmd = this.labelWidth;
13462 if(this.labellg > 0){
13463 labelCfg.cls += ' col-lg-' + this.labellg;
13464 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13467 if(this.labelmd > 0){
13468 labelCfg.cls += ' col-md-' + this.labelmd;
13469 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13472 if(this.labelsm > 0){
13473 labelCfg.cls += ' col-sm-' + this.labelsm;
13474 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13477 if(this.labelxs > 0){
13478 labelCfg.cls += ' col-xs-' + this.labelxs;
13479 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13483 } else if ( this.fieldLabel.length) {
13484 // Roo.log(" label");
13489 //cls : 'input-group-addon',
13490 html : this.fieldLabel
13495 if(this.indicatorpos == 'right'){
13499 //cls : 'input-group-addon',
13500 html : this.fieldLabel
13510 // Roo.log(" no label && no align");
13517 ['xs','sm','md','lg'].map(function(size){
13518 if (settings[size]) {
13519 cfg.cls += ' col-' + size + '-' + settings[size];
13527 _initEventsCalled : false,
13530 initEvents: function()
13532 if (this._initEventsCalled) { // as we call render... prevent looping...
13535 this._initEventsCalled = true;
13538 throw "can not find store for combo";
13541 this.indicator = this.indicatorEl();
13543 this.store = Roo.factory(this.store, Roo.data);
13544 this.store.parent = this;
13546 // if we are building from html. then this element is so complex, that we can not really
13547 // use the rendered HTML.
13548 // so we have to trash and replace the previous code.
13549 if (Roo.XComponent.build_from_html) {
13550 // remove this element....
13551 var e = this.el.dom, k=0;
13552 while (e ) { e = e.previousSibling; ++k;}
13557 this.rendered = false;
13559 this.render(this.parent().getChildContainer(true), k);
13562 if(Roo.isIOS && this.useNativeIOS){
13563 this.initIOSView();
13571 if(Roo.isTouch && this.mobileTouchView){
13572 this.initTouchView();
13577 this.initTickableEvents();
13581 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13583 if(this.hiddenName){
13585 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13587 this.hiddenField.dom.value =
13588 this.hiddenValue !== undefined ? this.hiddenValue :
13589 this.value !== undefined ? this.value : '';
13591 // prevent input submission
13592 this.el.dom.removeAttribute('name');
13593 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13598 // this.el.dom.setAttribute('autocomplete', 'off');
13601 var cls = 'x-combo-list';
13603 //this.list = new Roo.Layer({
13604 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13610 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13611 _this.list.setWidth(lw);
13614 this.list.on('mouseover', this.onViewOver, this);
13615 this.list.on('mousemove', this.onViewMove, this);
13616 this.list.on('scroll', this.onViewScroll, this);
13619 this.list.swallowEvent('mousewheel');
13620 this.assetHeight = 0;
13623 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13624 this.assetHeight += this.header.getHeight();
13627 this.innerList = this.list.createChild({cls:cls+'-inner'});
13628 this.innerList.on('mouseover', this.onViewOver, this);
13629 this.innerList.on('mousemove', this.onViewMove, this);
13630 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13632 if(this.allowBlank && !this.pageSize && !this.disableClear){
13633 this.footer = this.list.createChild({cls:cls+'-ft'});
13634 this.pageTb = new Roo.Toolbar(this.footer);
13638 this.footer = this.list.createChild({cls:cls+'-ft'});
13639 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13640 {pageSize: this.pageSize});
13644 if (this.pageTb && this.allowBlank && !this.disableClear) {
13646 this.pageTb.add(new Roo.Toolbar.Fill(), {
13647 cls: 'x-btn-icon x-btn-clear',
13649 handler: function()
13652 _this.clearValue();
13653 _this.onSelect(false, -1);
13658 this.assetHeight += this.footer.getHeight();
13663 this.tpl = Roo.bootstrap.version == 4 ?
13664 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
13665 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13668 this.view = new Roo.View(this.list, this.tpl, {
13669 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13671 //this.view.wrapEl.setDisplayed(false);
13672 this.view.on('click', this.onViewClick, this);
13675 this.store.on('beforeload', this.onBeforeLoad, this);
13676 this.store.on('load', this.onLoad, this);
13677 this.store.on('loadexception', this.onLoadException, this);
13679 if(this.resizable){
13680 this.resizer = new Roo.Resizable(this.list, {
13681 pinned:true, handles:'se'
13683 this.resizer.on('resize', function(r, w, h){
13684 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13685 this.listWidth = w;
13686 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13687 this.restrictHeight();
13689 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13692 if(!this.editable){
13693 this.editable = true;
13694 this.setEditable(false);
13699 if (typeof(this.events.add.listeners) != 'undefined') {
13701 this.addicon = this.wrap.createChild(
13702 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13704 this.addicon.on('click', function(e) {
13705 this.fireEvent('add', this);
13708 if (typeof(this.events.edit.listeners) != 'undefined') {
13710 this.editicon = this.wrap.createChild(
13711 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13712 if (this.addicon) {
13713 this.editicon.setStyle('margin-left', '40px');
13715 this.editicon.on('click', function(e) {
13717 // we fire even if inothing is selected..
13718 this.fireEvent('edit', this, this.lastData );
13724 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13725 "up" : function(e){
13726 this.inKeyMode = true;
13730 "down" : function(e){
13731 if(!this.isExpanded()){
13732 this.onTriggerClick();
13734 this.inKeyMode = true;
13739 "enter" : function(e){
13740 // this.onViewClick();
13744 if(this.fireEvent("specialkey", this, e)){
13745 this.onViewClick(false);
13751 "esc" : function(e){
13755 "tab" : function(e){
13758 if(this.fireEvent("specialkey", this, e)){
13759 this.onViewClick(false);
13767 doRelay : function(foo, bar, hname){
13768 if(hname == 'down' || this.scope.isExpanded()){
13769 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13778 this.queryDelay = Math.max(this.queryDelay || 10,
13779 this.mode == 'local' ? 10 : 250);
13782 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13784 if(this.typeAhead){
13785 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13787 if(this.editable !== false){
13788 this.inputEl().on("keyup", this.onKeyUp, this);
13790 if(this.forceSelection){
13791 this.inputEl().on('blur', this.doForce, this);
13795 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13796 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13800 initTickableEvents: function()
13804 if(this.hiddenName){
13806 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13808 this.hiddenField.dom.value =
13809 this.hiddenValue !== undefined ? this.hiddenValue :
13810 this.value !== undefined ? this.value : '';
13812 // prevent input submission
13813 this.el.dom.removeAttribute('name');
13814 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13819 // this.list = this.el.select('ul.dropdown-menu',true).first();
13821 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13822 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13823 if(this.triggerList){
13824 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13827 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13828 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13830 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13831 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13833 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13834 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13836 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13837 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13838 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13841 this.cancelBtn.hide();
13846 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13847 _this.list.setWidth(lw);
13850 this.list.on('mouseover', this.onViewOver, this);
13851 this.list.on('mousemove', this.onViewMove, this);
13853 this.list.on('scroll', this.onViewScroll, this);
13856 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13857 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13860 this.view = new Roo.View(this.list, this.tpl, {
13865 selectedClass: this.selectedClass
13868 //this.view.wrapEl.setDisplayed(false);
13869 this.view.on('click', this.onViewClick, this);
13873 this.store.on('beforeload', this.onBeforeLoad, this);
13874 this.store.on('load', this.onLoad, this);
13875 this.store.on('loadexception', this.onLoadException, this);
13878 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13879 "up" : function(e){
13880 this.inKeyMode = true;
13884 "down" : function(e){
13885 this.inKeyMode = true;
13889 "enter" : function(e){
13890 if(this.fireEvent("specialkey", this, e)){
13891 this.onViewClick(false);
13897 "esc" : function(e){
13898 this.onTickableFooterButtonClick(e, false, false);
13901 "tab" : function(e){
13902 this.fireEvent("specialkey", this, e);
13904 this.onTickableFooterButtonClick(e, false, false);
13911 doRelay : function(e, fn, key){
13912 if(this.scope.isExpanded()){
13913 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13922 this.queryDelay = Math.max(this.queryDelay || 10,
13923 this.mode == 'local' ? 10 : 250);
13926 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13928 if(this.typeAhead){
13929 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13932 if(this.editable !== false){
13933 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13936 this.indicator = this.indicatorEl();
13938 if(this.indicator){
13939 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13940 this.indicator.hide();
13945 onDestroy : function(){
13947 this.view.setStore(null);
13948 this.view.el.removeAllListeners();
13949 this.view.el.remove();
13950 this.view.purgeListeners();
13953 this.list.dom.innerHTML = '';
13957 this.store.un('beforeload', this.onBeforeLoad, this);
13958 this.store.un('load', this.onLoad, this);
13959 this.store.un('loadexception', this.onLoadException, this);
13961 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13965 fireKey : function(e){
13966 if(e.isNavKeyPress() && !this.list.isVisible()){
13967 this.fireEvent("specialkey", this, e);
13972 onResize: function(w, h){
13973 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13975 // if(typeof w != 'number'){
13976 // // we do not handle it!?!?
13979 // var tw = this.trigger.getWidth();
13980 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13981 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13983 // this.inputEl().setWidth( this.adjustWidth('input', x));
13985 // //this.trigger.setStyle('left', x+'px');
13987 // if(this.list && this.listWidth === undefined){
13988 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13989 // this.list.setWidth(lw);
13990 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13998 * Allow or prevent the user from directly editing the field text. If false is passed,
13999 * the user will only be able to select from the items defined in the dropdown list. This method
14000 * is the runtime equivalent of setting the 'editable' config option at config time.
14001 * @param {Boolean} value True to allow the user to directly edit the field text
14003 setEditable : function(value){
14004 if(value == this.editable){
14007 this.editable = value;
14009 this.inputEl().dom.setAttribute('readOnly', true);
14010 this.inputEl().on('mousedown', this.onTriggerClick, this);
14011 this.inputEl().addClass('x-combo-noedit');
14013 this.inputEl().dom.setAttribute('readOnly', false);
14014 this.inputEl().un('mousedown', this.onTriggerClick, this);
14015 this.inputEl().removeClass('x-combo-noedit');
14021 onBeforeLoad : function(combo,opts){
14022 if(!this.hasFocus){
14026 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14028 this.restrictHeight();
14029 this.selectedIndex = -1;
14033 onLoad : function(){
14035 this.hasQuery = false;
14037 if(!this.hasFocus){
14041 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14042 this.loading.hide();
14045 if(this.store.getCount() > 0){
14048 this.restrictHeight();
14049 if(this.lastQuery == this.allQuery){
14050 if(this.editable && !this.tickable){
14051 this.inputEl().dom.select();
14055 !this.selectByValue(this.value, true) &&
14058 !this.store.lastOptions ||
14059 typeof(this.store.lastOptions.add) == 'undefined' ||
14060 this.store.lastOptions.add != true
14063 this.select(0, true);
14066 if(this.autoFocus){
14069 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14070 this.taTask.delay(this.typeAheadDelay);
14074 this.onEmptyResults();
14080 onLoadException : function()
14082 this.hasQuery = false;
14084 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14085 this.loading.hide();
14088 if(this.tickable && this.editable){
14093 // only causes errors at present
14094 //Roo.log(this.store.reader.jsonData);
14095 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14097 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14103 onTypeAhead : function(){
14104 if(this.store.getCount() > 0){
14105 var r = this.store.getAt(0);
14106 var newValue = r.data[this.displayField];
14107 var len = newValue.length;
14108 var selStart = this.getRawValue().length;
14110 if(selStart != len){
14111 this.setRawValue(newValue);
14112 this.selectText(selStart, newValue.length);
14118 onSelect : function(record, index){
14120 if(this.fireEvent('beforeselect', this, record, index) !== false){
14122 this.setFromData(index > -1 ? record.data : false);
14125 this.fireEvent('select', this, record, index);
14130 * Returns the currently selected field value or empty string if no value is set.
14131 * @return {String} value The selected value
14133 getValue : function()
14135 if(Roo.isIOS && this.useNativeIOS){
14136 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14140 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14143 if(this.valueField){
14144 return typeof this.value != 'undefined' ? this.value : '';
14146 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14150 getRawValue : function()
14152 if(Roo.isIOS && this.useNativeIOS){
14153 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14156 var v = this.inputEl().getValue();
14162 * Clears any text/value currently set in the field
14164 clearValue : function(){
14166 if(this.hiddenField){
14167 this.hiddenField.dom.value = '';
14170 this.setRawValue('');
14171 this.lastSelectionText = '';
14172 this.lastData = false;
14174 var close = this.closeTriggerEl();
14185 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14186 * will be displayed in the field. If the value does not match the data value of an existing item,
14187 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14188 * Otherwise the field will be blank (although the value will still be set).
14189 * @param {String} value The value to match
14191 setValue : function(v)
14193 if(Roo.isIOS && this.useNativeIOS){
14194 this.setIOSValue(v);
14204 if(this.valueField){
14205 var r = this.findRecord(this.valueField, v);
14207 text = r.data[this.displayField];
14208 }else if(this.valueNotFoundText !== undefined){
14209 text = this.valueNotFoundText;
14212 this.lastSelectionText = text;
14213 if(this.hiddenField){
14214 this.hiddenField.dom.value = v;
14216 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14219 var close = this.closeTriggerEl();
14222 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14228 * @property {Object} the last set data for the element
14233 * Sets the value of the field based on a object which is related to the record format for the store.
14234 * @param {Object} value the value to set as. or false on reset?
14236 setFromData : function(o){
14243 var dv = ''; // display value
14244 var vv = ''; // value value..
14246 if (this.displayField) {
14247 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14249 // this is an error condition!!!
14250 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14253 if(this.valueField){
14254 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14257 var close = this.closeTriggerEl();
14260 if(dv.length || vv * 1 > 0){
14262 this.blockFocus=true;
14268 if(this.hiddenField){
14269 this.hiddenField.dom.value = vv;
14271 this.lastSelectionText = dv;
14272 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14276 // no hidden field.. - we store the value in 'value', but still display
14277 // display field!!!!
14278 this.lastSelectionText = dv;
14279 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14286 reset : function(){
14287 // overridden so that last data is reset..
14294 this.setValue(this.originalValue);
14295 //this.clearInvalid();
14296 this.lastData = false;
14298 this.view.clearSelections();
14304 findRecord : function(prop, value){
14306 if(this.store.getCount() > 0){
14307 this.store.each(function(r){
14308 if(r.data[prop] == value){
14318 getName: function()
14320 // returns hidden if it's set..
14321 if (!this.rendered) {return ''};
14322 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14326 onViewMove : function(e, t){
14327 this.inKeyMode = false;
14331 onViewOver : function(e, t){
14332 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14335 var item = this.view.findItemFromChild(t);
14338 var index = this.view.indexOf(item);
14339 this.select(index, false);
14344 onViewClick : function(view, doFocus, el, e)
14346 var index = this.view.getSelectedIndexes()[0];
14348 var r = this.store.getAt(index);
14352 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14359 Roo.each(this.tickItems, function(v,k){
14361 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14363 _this.tickItems.splice(k, 1);
14365 if(typeof(e) == 'undefined' && view == false){
14366 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14378 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14379 this.tickItems.push(r.data);
14382 if(typeof(e) == 'undefined' && view == false){
14383 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14390 this.onSelect(r, index);
14392 if(doFocus !== false && !this.blockFocus){
14393 this.inputEl().focus();
14398 restrictHeight : function(){
14399 //this.innerList.dom.style.height = '';
14400 //var inner = this.innerList.dom;
14401 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14402 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14403 //this.list.beginUpdate();
14404 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14405 this.list.alignTo(this.inputEl(), this.listAlign);
14406 this.list.alignTo(this.inputEl(), this.listAlign);
14407 //this.list.endUpdate();
14411 onEmptyResults : function(){
14413 if(this.tickable && this.editable){
14414 this.hasFocus = false;
14415 this.restrictHeight();
14423 * Returns true if the dropdown list is expanded, else false.
14425 isExpanded : function(){
14426 return this.list.isVisible();
14430 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14431 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14432 * @param {String} value The data value of the item to select
14433 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14434 * selected item if it is not currently in view (defaults to true)
14435 * @return {Boolean} True if the value matched an item in the list, else false
14437 selectByValue : function(v, scrollIntoView){
14438 if(v !== undefined && v !== null){
14439 var r = this.findRecord(this.valueField || this.displayField, v);
14441 this.select(this.store.indexOf(r), scrollIntoView);
14449 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14450 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14451 * @param {Number} index The zero-based index of the list item to select
14452 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14453 * selected item if it is not currently in view (defaults to true)
14455 select : function(index, scrollIntoView){
14456 this.selectedIndex = index;
14457 this.view.select(index);
14458 if(scrollIntoView !== false){
14459 var el = this.view.getNode(index);
14461 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14464 this.list.scrollChildIntoView(el, false);
14470 selectNext : function(){
14471 var ct = this.store.getCount();
14473 if(this.selectedIndex == -1){
14475 }else if(this.selectedIndex < ct-1){
14476 this.select(this.selectedIndex+1);
14482 selectPrev : function(){
14483 var ct = this.store.getCount();
14485 if(this.selectedIndex == -1){
14487 }else if(this.selectedIndex != 0){
14488 this.select(this.selectedIndex-1);
14494 onKeyUp : function(e){
14495 if(this.editable !== false && !e.isSpecialKey()){
14496 this.lastKey = e.getKey();
14497 this.dqTask.delay(this.queryDelay);
14502 validateBlur : function(){
14503 return !this.list || !this.list.isVisible();
14507 initQuery : function(){
14509 var v = this.getRawValue();
14511 if(this.tickable && this.editable){
14512 v = this.tickableInputEl().getValue();
14519 doForce : function(){
14520 if(this.inputEl().dom.value.length > 0){
14521 this.inputEl().dom.value =
14522 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14528 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14529 * query allowing the query action to be canceled if needed.
14530 * @param {String} query The SQL query to execute
14531 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14532 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14533 * saved in the current store (defaults to false)
14535 doQuery : function(q, forceAll){
14537 if(q === undefined || q === null){
14542 forceAll: forceAll,
14546 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14551 forceAll = qe.forceAll;
14552 if(forceAll === true || (q.length >= this.minChars)){
14554 this.hasQuery = true;
14556 if(this.lastQuery != q || this.alwaysQuery){
14557 this.lastQuery = q;
14558 if(this.mode == 'local'){
14559 this.selectedIndex = -1;
14561 this.store.clearFilter();
14564 if(this.specialFilter){
14565 this.fireEvent('specialfilter', this);
14570 this.store.filter(this.displayField, q);
14573 this.store.fireEvent("datachanged", this.store);
14580 this.store.baseParams[this.queryParam] = q;
14582 var options = {params : this.getParams(q)};
14585 options.add = true;
14586 options.params.start = this.page * this.pageSize;
14589 this.store.load(options);
14592 * this code will make the page width larger, at the beginning, the list not align correctly,
14593 * we should expand the list on onLoad
14594 * so command out it
14599 this.selectedIndex = -1;
14604 this.loadNext = false;
14608 getParams : function(q){
14610 //p[this.queryParam] = q;
14614 p.limit = this.pageSize;
14620 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14622 collapse : function(){
14623 if(!this.isExpanded()){
14629 this.hasFocus = false;
14633 this.cancelBtn.hide();
14634 this.trigger.show();
14637 this.tickableInputEl().dom.value = '';
14638 this.tickableInputEl().blur();
14643 Roo.get(document).un('mousedown', this.collapseIf, this);
14644 Roo.get(document).un('mousewheel', this.collapseIf, this);
14645 if (!this.editable) {
14646 Roo.get(document).un('keydown', this.listKeyPress, this);
14648 this.fireEvent('collapse', this);
14654 collapseIf : function(e){
14655 var in_combo = e.within(this.el);
14656 var in_list = e.within(this.list);
14657 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14659 if (in_combo || in_list || is_list) {
14660 //e.stopPropagation();
14665 this.onTickableFooterButtonClick(e, false, false);
14673 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14675 expand : function(){
14677 if(this.isExpanded() || !this.hasFocus){
14681 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14682 this.list.setWidth(lw);
14688 this.restrictHeight();
14692 this.tickItems = Roo.apply([], this.item);
14695 this.cancelBtn.show();
14696 this.trigger.hide();
14699 this.tickableInputEl().focus();
14704 Roo.get(document).on('mousedown', this.collapseIf, this);
14705 Roo.get(document).on('mousewheel', this.collapseIf, this);
14706 if (!this.editable) {
14707 Roo.get(document).on('keydown', this.listKeyPress, this);
14710 this.fireEvent('expand', this);
14714 // Implements the default empty TriggerField.onTriggerClick function
14715 onTriggerClick : function(e)
14717 Roo.log('trigger click');
14719 if(this.disabled || !this.triggerList){
14724 this.loadNext = false;
14726 if(this.isExpanded()){
14728 if (!this.blockFocus) {
14729 this.inputEl().focus();
14733 this.hasFocus = true;
14734 if(this.triggerAction == 'all') {
14735 this.doQuery(this.allQuery, true);
14737 this.doQuery(this.getRawValue());
14739 if (!this.blockFocus) {
14740 this.inputEl().focus();
14745 onTickableTriggerClick : function(e)
14752 this.loadNext = false;
14753 this.hasFocus = true;
14755 if(this.triggerAction == 'all') {
14756 this.doQuery(this.allQuery, true);
14758 this.doQuery(this.getRawValue());
14762 onSearchFieldClick : function(e)
14764 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14765 this.onTickableFooterButtonClick(e, false, false);
14769 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14774 this.loadNext = false;
14775 this.hasFocus = true;
14777 if(this.triggerAction == 'all') {
14778 this.doQuery(this.allQuery, true);
14780 this.doQuery(this.getRawValue());
14784 listKeyPress : function(e)
14786 //Roo.log('listkeypress');
14787 // scroll to first matching element based on key pres..
14788 if (e.isSpecialKey()) {
14791 var k = String.fromCharCode(e.getKey()).toUpperCase();
14794 var csel = this.view.getSelectedNodes();
14795 var cselitem = false;
14797 var ix = this.view.indexOf(csel[0]);
14798 cselitem = this.store.getAt(ix);
14799 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14805 this.store.each(function(v) {
14807 // start at existing selection.
14808 if (cselitem.id == v.id) {
14814 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14815 match = this.store.indexOf(v);
14821 if (match === false) {
14822 return true; // no more action?
14825 this.view.select(match);
14826 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14827 sn.scrollIntoView(sn.dom.parentNode, false);
14830 onViewScroll : function(e, t){
14832 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){
14836 this.hasQuery = true;
14838 this.loading = this.list.select('.loading', true).first();
14840 if(this.loading === null){
14841 this.list.createChild({
14843 cls: 'loading roo-select2-more-results roo-select2-active',
14844 html: 'Loading more results...'
14847 this.loading = this.list.select('.loading', true).first();
14849 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14851 this.loading.hide();
14854 this.loading.show();
14859 this.loadNext = true;
14861 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14866 addItem : function(o)
14868 var dv = ''; // display value
14870 if (this.displayField) {
14871 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14873 // this is an error condition!!!
14874 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14881 var choice = this.choices.createChild({
14883 cls: 'roo-select2-search-choice',
14892 cls: 'roo-select2-search-choice-close fa fa-times',
14897 }, this.searchField);
14899 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14901 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14909 this.inputEl().dom.value = '';
14914 onRemoveItem : function(e, _self, o)
14916 e.preventDefault();
14918 this.lastItem = Roo.apply([], this.item);
14920 var index = this.item.indexOf(o.data) * 1;
14923 Roo.log('not this item?!');
14927 this.item.splice(index, 1);
14932 this.fireEvent('remove', this, e);
14938 syncValue : function()
14940 if(!this.item.length){
14947 Roo.each(this.item, function(i){
14948 if(_this.valueField){
14949 value.push(i[_this.valueField]);
14956 this.value = value.join(',');
14958 if(this.hiddenField){
14959 this.hiddenField.dom.value = this.value;
14962 this.store.fireEvent("datachanged", this.store);
14967 clearItem : function()
14969 if(!this.multiple){
14975 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14983 if(this.tickable && !Roo.isTouch){
14984 this.view.refresh();
14988 inputEl: function ()
14990 if(Roo.isIOS && this.useNativeIOS){
14991 return this.el.select('select.roo-ios-select', true).first();
14994 if(Roo.isTouch && this.mobileTouchView){
14995 return this.el.select('input.form-control',true).first();
14999 return this.searchField;
15002 return this.el.select('input.form-control',true).first();
15005 onTickableFooterButtonClick : function(e, btn, el)
15007 e.preventDefault();
15009 this.lastItem = Roo.apply([], this.item);
15011 if(btn && btn.name == 'cancel'){
15012 this.tickItems = Roo.apply([], this.item);
15021 Roo.each(this.tickItems, function(o){
15029 validate : function()
15031 if(this.getVisibilityEl().hasClass('hidden')){
15035 var v = this.getRawValue();
15038 v = this.getValue();
15041 if(this.disabled || this.allowBlank || v.length){
15046 this.markInvalid();
15050 tickableInputEl : function()
15052 if(!this.tickable || !this.editable){
15053 return this.inputEl();
15056 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15060 getAutoCreateTouchView : function()
15065 cls: 'form-group' //input-group
15071 type : this.inputType,
15072 cls : 'form-control x-combo-noedit',
15073 autocomplete: 'new-password',
15074 placeholder : this.placeholder || '',
15079 input.name = this.name;
15083 input.cls += ' input-' + this.size;
15086 if (this.disabled) {
15087 input.disabled = true;
15098 inputblock.cls += ' input-group';
15100 inputblock.cn.unshift({
15102 cls : 'input-group-addon input-group-prepend input-group-text',
15107 if(this.removable && !this.multiple){
15108 inputblock.cls += ' roo-removable';
15110 inputblock.cn.push({
15113 cls : 'roo-combo-removable-btn close'
15117 if(this.hasFeedback && !this.allowBlank){
15119 inputblock.cls += ' has-feedback';
15121 inputblock.cn.push({
15123 cls: 'glyphicon form-control-feedback'
15130 inputblock.cls += (this.before) ? '' : ' input-group';
15132 inputblock.cn.push({
15134 cls : 'input-group-addon input-group-append input-group-text',
15140 var ibwrap = inputblock;
15145 cls: 'roo-select2-choices',
15149 cls: 'roo-select2-search-field',
15162 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15167 cls: 'form-hidden-field'
15173 if(!this.multiple && this.showToggleBtn){
15180 if (this.caret != false) {
15183 cls: 'fa fa-' + this.caret
15190 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15195 cls: 'combobox-clear',
15209 combobox.cls += ' roo-select2-container-multi';
15212 var align = this.labelAlign || this.parentLabelAlign();
15214 if (align ==='left' && this.fieldLabel.length) {
15219 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15220 tooltip : 'This field is required'
15224 cls : 'control-label col-form-label',
15225 html : this.fieldLabel
15236 var labelCfg = cfg.cn[1];
15237 var contentCfg = cfg.cn[2];
15240 if(this.indicatorpos == 'right'){
15245 cls : 'control-label col-form-label',
15249 html : this.fieldLabel
15253 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15254 tooltip : 'This field is required'
15267 labelCfg = cfg.cn[0];
15268 contentCfg = cfg.cn[1];
15273 if(this.labelWidth > 12){
15274 labelCfg.style = "width: " + this.labelWidth + 'px';
15277 if(this.labelWidth < 13 && this.labelmd == 0){
15278 this.labelmd = this.labelWidth;
15281 if(this.labellg > 0){
15282 labelCfg.cls += ' col-lg-' + this.labellg;
15283 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15286 if(this.labelmd > 0){
15287 labelCfg.cls += ' col-md-' + this.labelmd;
15288 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15291 if(this.labelsm > 0){
15292 labelCfg.cls += ' col-sm-' + this.labelsm;
15293 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15296 if(this.labelxs > 0){
15297 labelCfg.cls += ' col-xs-' + this.labelxs;
15298 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15302 } else if ( this.fieldLabel.length) {
15306 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15307 tooltip : 'This field is required'
15311 cls : 'control-label',
15312 html : this.fieldLabel
15323 if(this.indicatorpos == 'right'){
15327 cls : 'control-label',
15328 html : this.fieldLabel,
15332 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15333 tooltip : 'This field is required'
15350 var settings = this;
15352 ['xs','sm','md','lg'].map(function(size){
15353 if (settings[size]) {
15354 cfg.cls += ' col-' + size + '-' + settings[size];
15361 initTouchView : function()
15363 this.renderTouchView();
15365 this.touchViewEl.on('scroll', function(){
15366 this.el.dom.scrollTop = 0;
15369 this.originalValue = this.getValue();
15371 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15373 this.inputEl().on("click", this.showTouchView, this);
15374 if (this.triggerEl) {
15375 this.triggerEl.on("click", this.showTouchView, this);
15379 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15380 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15382 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15384 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15385 this.store.on('load', this.onTouchViewLoad, this);
15386 this.store.on('loadexception', this.onTouchViewLoadException, this);
15388 if(this.hiddenName){
15390 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15392 this.hiddenField.dom.value =
15393 this.hiddenValue !== undefined ? this.hiddenValue :
15394 this.value !== undefined ? this.value : '';
15396 this.el.dom.removeAttribute('name');
15397 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15401 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15402 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15405 if(this.removable && !this.multiple){
15406 var close = this.closeTriggerEl();
15408 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15409 close.on('click', this.removeBtnClick, this, close);
15413 * fix the bug in Safari iOS8
15415 this.inputEl().on("focus", function(e){
15416 document.activeElement.blur();
15419 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15426 renderTouchView : function()
15428 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15429 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15431 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15432 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15434 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15435 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15436 this.touchViewBodyEl.setStyle('overflow', 'auto');
15438 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15439 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15441 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15442 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15446 showTouchView : function()
15452 this.touchViewHeaderEl.hide();
15454 if(this.modalTitle.length){
15455 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15456 this.touchViewHeaderEl.show();
15459 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15460 this.touchViewEl.show();
15462 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15464 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15465 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15467 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15469 if(this.modalTitle.length){
15470 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15473 this.touchViewBodyEl.setHeight(bodyHeight);
15477 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15479 this.touchViewEl.addClass('in');
15482 if(this._touchViewMask){
15483 Roo.get(document.body).addClass("x-body-masked");
15484 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15485 this._touchViewMask.setStyle('z-index', 10000);
15486 this._touchViewMask.addClass('show');
15489 this.doTouchViewQuery();
15493 hideTouchView : function()
15495 this.touchViewEl.removeClass('in');
15499 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15501 this.touchViewEl.setStyle('display', 'none');
15504 if(this._touchViewMask){
15505 this._touchViewMask.removeClass('show');
15506 Roo.get(document.body).removeClass("x-body-masked");
15510 setTouchViewValue : function()
15517 Roo.each(this.tickItems, function(o){
15522 this.hideTouchView();
15525 doTouchViewQuery : function()
15534 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15538 if(!this.alwaysQuery || this.mode == 'local'){
15539 this.onTouchViewLoad();
15546 onTouchViewBeforeLoad : function(combo,opts)
15552 onTouchViewLoad : function()
15554 if(this.store.getCount() < 1){
15555 this.onTouchViewEmptyResults();
15559 this.clearTouchView();
15561 var rawValue = this.getRawValue();
15563 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15565 this.tickItems = [];
15567 this.store.data.each(function(d, rowIndex){
15568 var row = this.touchViewListGroup.createChild(template);
15570 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15571 row.addClass(d.data.cls);
15574 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15577 html : d.data[this.displayField]
15580 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15581 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15584 row.removeClass('selected');
15585 if(!this.multiple && this.valueField &&
15586 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15589 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15590 row.addClass('selected');
15593 if(this.multiple && this.valueField &&
15594 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15598 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15599 this.tickItems.push(d.data);
15602 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15606 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15608 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15610 if(this.modalTitle.length){
15611 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15614 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15616 if(this.mobile_restrict_height && listHeight < bodyHeight){
15617 this.touchViewBodyEl.setHeight(listHeight);
15622 if(firstChecked && listHeight > bodyHeight){
15623 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15628 onTouchViewLoadException : function()
15630 this.hideTouchView();
15633 onTouchViewEmptyResults : function()
15635 this.clearTouchView();
15637 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15639 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15643 clearTouchView : function()
15645 this.touchViewListGroup.dom.innerHTML = '';
15648 onTouchViewClick : function(e, el, o)
15650 e.preventDefault();
15653 var rowIndex = o.rowIndex;
15655 var r = this.store.getAt(rowIndex);
15657 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15659 if(!this.multiple){
15660 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15661 c.dom.removeAttribute('checked');
15664 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15666 this.setFromData(r.data);
15668 var close = this.closeTriggerEl();
15674 this.hideTouchView();
15676 this.fireEvent('select', this, r, rowIndex);
15681 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15682 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15683 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15687 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15688 this.addItem(r.data);
15689 this.tickItems.push(r.data);
15693 getAutoCreateNativeIOS : function()
15696 cls: 'form-group' //input-group,
15701 cls : 'roo-ios-select'
15705 combobox.name = this.name;
15708 if (this.disabled) {
15709 combobox.disabled = true;
15712 var settings = this;
15714 ['xs','sm','md','lg'].map(function(size){
15715 if (settings[size]) {
15716 cfg.cls += ' col-' + size + '-' + settings[size];
15726 initIOSView : function()
15728 this.store.on('load', this.onIOSViewLoad, this);
15733 onIOSViewLoad : function()
15735 if(this.store.getCount() < 1){
15739 this.clearIOSView();
15741 if(this.allowBlank) {
15743 var default_text = '-- SELECT --';
15745 if(this.placeholder.length){
15746 default_text = this.placeholder;
15749 if(this.emptyTitle.length){
15750 default_text += ' - ' + this.emptyTitle + ' -';
15753 var opt = this.inputEl().createChild({
15756 html : default_text
15760 o[this.valueField] = 0;
15761 o[this.displayField] = default_text;
15763 this.ios_options.push({
15770 this.store.data.each(function(d, rowIndex){
15774 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15775 html = d.data[this.displayField];
15780 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15781 value = d.data[this.valueField];
15790 if(this.value == d.data[this.valueField]){
15791 option['selected'] = true;
15794 var opt = this.inputEl().createChild(option);
15796 this.ios_options.push({
15803 this.inputEl().on('change', function(){
15804 this.fireEvent('select', this);
15809 clearIOSView: function()
15811 this.inputEl().dom.innerHTML = '';
15813 this.ios_options = [];
15816 setIOSValue: function(v)
15820 if(!this.ios_options){
15824 Roo.each(this.ios_options, function(opts){
15826 opts.el.dom.removeAttribute('selected');
15828 if(opts.data[this.valueField] != v){
15832 opts.el.dom.setAttribute('selected', true);
15838 * @cfg {Boolean} grow
15842 * @cfg {Number} growMin
15846 * @cfg {Number} growMax
15855 Roo.apply(Roo.bootstrap.ComboBox, {
15859 cls: 'modal-header',
15881 cls: 'list-group-item',
15885 cls: 'roo-combobox-list-group-item-value'
15889 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15903 listItemCheckbox : {
15905 cls: 'list-group-item',
15909 cls: 'roo-combobox-list-group-item-value'
15913 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15929 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15934 cls: 'modal-footer',
15942 cls: 'col-xs-6 text-left',
15945 cls: 'btn btn-danger roo-touch-view-cancel',
15951 cls: 'col-xs-6 text-right',
15954 cls: 'btn btn-success roo-touch-view-ok',
15965 Roo.apply(Roo.bootstrap.ComboBox, {
15967 touchViewTemplate : {
15969 cls: 'modal fade roo-combobox-touch-view',
15973 cls: 'modal-dialog',
15974 style : 'position:fixed', // we have to fix position....
15978 cls: 'modal-content',
15980 Roo.bootstrap.ComboBox.header,
15981 Roo.bootstrap.ComboBox.body,
15982 Roo.bootstrap.ComboBox.footer
15991 * Ext JS Library 1.1.1
15992 * Copyright(c) 2006-2007, Ext JS, LLC.
15994 * Originally Released Under LGPL - original licence link has changed is not relivant.
15997 * <script type="text/javascript">
16002 * @extends Roo.util.Observable
16003 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
16004 * This class also supports single and multi selection modes. <br>
16005 * Create a data model bound view:
16007 var store = new Roo.data.Store(...);
16009 var view = new Roo.View({
16011 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
16013 singleSelect: true,
16014 selectedClass: "ydataview-selected",
16018 // listen for node click?
16019 view.on("click", function(vw, index, node, e){
16020 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16024 dataModel.load("foobar.xml");
16026 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16028 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16029 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16031 * Note: old style constructor is still suported (container, template, config)
16034 * Create a new View
16035 * @param {Object} config The config object
16038 Roo.View = function(config, depreciated_tpl, depreciated_config){
16040 this.parent = false;
16042 if (typeof(depreciated_tpl) == 'undefined') {
16043 // new way.. - universal constructor.
16044 Roo.apply(this, config);
16045 this.el = Roo.get(this.el);
16048 this.el = Roo.get(config);
16049 this.tpl = depreciated_tpl;
16050 Roo.apply(this, depreciated_config);
16052 this.wrapEl = this.el.wrap().wrap();
16053 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16056 if(typeof(this.tpl) == "string"){
16057 this.tpl = new Roo.Template(this.tpl);
16059 // support xtype ctors..
16060 this.tpl = new Roo.factory(this.tpl, Roo);
16064 this.tpl.compile();
16069 * @event beforeclick
16070 * Fires before a click is processed. Returns false to cancel the default action.
16071 * @param {Roo.View} this
16072 * @param {Number} index The index of the target node
16073 * @param {HTMLElement} node The target node
16074 * @param {Roo.EventObject} e The raw event object
16076 "beforeclick" : true,
16079 * Fires when a template node is clicked.
16080 * @param {Roo.View} this
16081 * @param {Number} index The index of the target node
16082 * @param {HTMLElement} node The target node
16083 * @param {Roo.EventObject} e The raw event object
16088 * Fires when a template node is double clicked.
16089 * @param {Roo.View} this
16090 * @param {Number} index The index of the target node
16091 * @param {HTMLElement} node The target node
16092 * @param {Roo.EventObject} e The raw event object
16096 * @event contextmenu
16097 * Fires when a template node is right clicked.
16098 * @param {Roo.View} this
16099 * @param {Number} index The index of the target node
16100 * @param {HTMLElement} node The target node
16101 * @param {Roo.EventObject} e The raw event object
16103 "contextmenu" : true,
16105 * @event selectionchange
16106 * Fires when the selected nodes change.
16107 * @param {Roo.View} this
16108 * @param {Array} selections Array of the selected nodes
16110 "selectionchange" : true,
16113 * @event beforeselect
16114 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16115 * @param {Roo.View} this
16116 * @param {HTMLElement} node The node to be selected
16117 * @param {Array} selections Array of currently selected nodes
16119 "beforeselect" : true,
16121 * @event preparedata
16122 * Fires on every row to render, to allow you to change the data.
16123 * @param {Roo.View} this
16124 * @param {Object} data to be rendered (change this)
16126 "preparedata" : true
16134 "click": this.onClick,
16135 "dblclick": this.onDblClick,
16136 "contextmenu": this.onContextMenu,
16140 this.selections = [];
16142 this.cmp = new Roo.CompositeElementLite([]);
16144 this.store = Roo.factory(this.store, Roo.data);
16145 this.setStore(this.store, true);
16148 if ( this.footer && this.footer.xtype) {
16150 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16152 this.footer.dataSource = this.store;
16153 this.footer.container = fctr;
16154 this.footer = Roo.factory(this.footer, Roo);
16155 fctr.insertFirst(this.el);
16157 // this is a bit insane - as the paging toolbar seems to detach the el..
16158 // dom.parentNode.parentNode.parentNode
16159 // they get detached?
16163 Roo.View.superclass.constructor.call(this);
16168 Roo.extend(Roo.View, Roo.util.Observable, {
16171 * @cfg {Roo.data.Store} store Data store to load data from.
16176 * @cfg {String|Roo.Element} el The container element.
16181 * @cfg {String|Roo.Template} tpl The template used by this View
16185 * @cfg {String} dataName the named area of the template to use as the data area
16186 * Works with domtemplates roo-name="name"
16190 * @cfg {String} selectedClass The css class to add to selected nodes
16192 selectedClass : "x-view-selected",
16194 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16199 * @cfg {String} text to display on mask (default Loading)
16203 * @cfg {Boolean} multiSelect Allow multiple selection
16205 multiSelect : false,
16207 * @cfg {Boolean} singleSelect Allow single selection
16209 singleSelect: false,
16212 * @cfg {Boolean} toggleSelect - selecting
16214 toggleSelect : false,
16217 * @cfg {Boolean} tickable - selecting
16222 * Returns the element this view is bound to.
16223 * @return {Roo.Element}
16225 getEl : function(){
16226 return this.wrapEl;
16232 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16234 refresh : function(){
16235 //Roo.log('refresh');
16238 // if we are using something like 'domtemplate', then
16239 // the what gets used is:
16240 // t.applySubtemplate(NAME, data, wrapping data..)
16241 // the outer template then get' applied with
16242 // the store 'extra data'
16243 // and the body get's added to the
16244 // roo-name="data" node?
16245 // <span class='roo-tpl-{name}'></span> ?????
16249 this.clearSelections();
16250 this.el.update("");
16252 var records = this.store.getRange();
16253 if(records.length < 1) {
16255 // is this valid?? = should it render a template??
16257 this.el.update(this.emptyText);
16261 if (this.dataName) {
16262 this.el.update(t.apply(this.store.meta)); //????
16263 el = this.el.child('.roo-tpl-' + this.dataName);
16266 for(var i = 0, len = records.length; i < len; i++){
16267 var data = this.prepareData(records[i].data, i, records[i]);
16268 this.fireEvent("preparedata", this, data, i, records[i]);
16270 var d = Roo.apply({}, data);
16273 Roo.apply(d, {'roo-id' : Roo.id()});
16277 Roo.each(this.parent.item, function(item){
16278 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16281 Roo.apply(d, {'roo-data-checked' : 'checked'});
16285 html[html.length] = Roo.util.Format.trim(
16287 t.applySubtemplate(this.dataName, d, this.store.meta) :
16294 el.update(html.join(""));
16295 this.nodes = el.dom.childNodes;
16296 this.updateIndexes(0);
16301 * Function to override to reformat the data that is sent to
16302 * the template for each node.
16303 * DEPRICATED - use the preparedata event handler.
16304 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16305 * a JSON object for an UpdateManager bound view).
16307 prepareData : function(data, index, record)
16309 this.fireEvent("preparedata", this, data, index, record);
16313 onUpdate : function(ds, record){
16314 // Roo.log('on update');
16315 this.clearSelections();
16316 var index = this.store.indexOf(record);
16317 var n = this.nodes[index];
16318 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16319 n.parentNode.removeChild(n);
16320 this.updateIndexes(index, index);
16326 onAdd : function(ds, records, index)
16328 //Roo.log(['on Add', ds, records, index] );
16329 this.clearSelections();
16330 if(this.nodes.length == 0){
16334 var n = this.nodes[index];
16335 for(var i = 0, len = records.length; i < len; i++){
16336 var d = this.prepareData(records[i].data, i, records[i]);
16338 this.tpl.insertBefore(n, d);
16341 this.tpl.append(this.el, d);
16344 this.updateIndexes(index);
16347 onRemove : function(ds, record, index){
16348 // Roo.log('onRemove');
16349 this.clearSelections();
16350 var el = this.dataName ?
16351 this.el.child('.roo-tpl-' + this.dataName) :
16354 el.dom.removeChild(this.nodes[index]);
16355 this.updateIndexes(index);
16359 * Refresh an individual node.
16360 * @param {Number} index
16362 refreshNode : function(index){
16363 this.onUpdate(this.store, this.store.getAt(index));
16366 updateIndexes : function(startIndex, endIndex){
16367 var ns = this.nodes;
16368 startIndex = startIndex || 0;
16369 endIndex = endIndex || ns.length - 1;
16370 for(var i = startIndex; i <= endIndex; i++){
16371 ns[i].nodeIndex = i;
16376 * Changes the data store this view uses and refresh the view.
16377 * @param {Store} store
16379 setStore : function(store, initial){
16380 if(!initial && this.store){
16381 this.store.un("datachanged", this.refresh);
16382 this.store.un("add", this.onAdd);
16383 this.store.un("remove", this.onRemove);
16384 this.store.un("update", this.onUpdate);
16385 this.store.un("clear", this.refresh);
16386 this.store.un("beforeload", this.onBeforeLoad);
16387 this.store.un("load", this.onLoad);
16388 this.store.un("loadexception", this.onLoad);
16392 store.on("datachanged", this.refresh, this);
16393 store.on("add", this.onAdd, this);
16394 store.on("remove", this.onRemove, this);
16395 store.on("update", this.onUpdate, this);
16396 store.on("clear", this.refresh, this);
16397 store.on("beforeload", this.onBeforeLoad, this);
16398 store.on("load", this.onLoad, this);
16399 store.on("loadexception", this.onLoad, this);
16407 * onbeforeLoad - masks the loading area.
16410 onBeforeLoad : function(store,opts)
16412 //Roo.log('onBeforeLoad');
16414 this.el.update("");
16416 this.el.mask(this.mask ? this.mask : "Loading" );
16418 onLoad : function ()
16425 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16426 * @param {HTMLElement} node
16427 * @return {HTMLElement} The template node
16429 findItemFromChild : function(node){
16430 var el = this.dataName ?
16431 this.el.child('.roo-tpl-' + this.dataName,true) :
16434 if(!node || node.parentNode == el){
16437 var p = node.parentNode;
16438 while(p && p != el){
16439 if(p.parentNode == el){
16448 onClick : function(e){
16449 var item = this.findItemFromChild(e.getTarget());
16451 var index = this.indexOf(item);
16452 if(this.onItemClick(item, index, e) !== false){
16453 this.fireEvent("click", this, index, item, e);
16456 this.clearSelections();
16461 onContextMenu : function(e){
16462 var item = this.findItemFromChild(e.getTarget());
16464 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16469 onDblClick : function(e){
16470 var item = this.findItemFromChild(e.getTarget());
16472 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16476 onItemClick : function(item, index, e)
16478 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16481 if (this.toggleSelect) {
16482 var m = this.isSelected(item) ? 'unselect' : 'select';
16485 _t[m](item, true, false);
16488 if(this.multiSelect || this.singleSelect){
16489 if(this.multiSelect && e.shiftKey && this.lastSelection){
16490 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16492 this.select(item, this.multiSelect && e.ctrlKey);
16493 this.lastSelection = item;
16496 if(!this.tickable){
16497 e.preventDefault();
16505 * Get the number of selected nodes.
16508 getSelectionCount : function(){
16509 return this.selections.length;
16513 * Get the currently selected nodes.
16514 * @return {Array} An array of HTMLElements
16516 getSelectedNodes : function(){
16517 return this.selections;
16521 * Get the indexes of the selected nodes.
16524 getSelectedIndexes : function(){
16525 var indexes = [], s = this.selections;
16526 for(var i = 0, len = s.length; i < len; i++){
16527 indexes.push(s[i].nodeIndex);
16533 * Clear all selections
16534 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16536 clearSelections : function(suppressEvent){
16537 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16538 this.cmp.elements = this.selections;
16539 this.cmp.removeClass(this.selectedClass);
16540 this.selections = [];
16541 if(!suppressEvent){
16542 this.fireEvent("selectionchange", this, this.selections);
16548 * Returns true if the passed node is selected
16549 * @param {HTMLElement/Number} node The node or node index
16550 * @return {Boolean}
16552 isSelected : function(node){
16553 var s = this.selections;
16557 node = this.getNode(node);
16558 return s.indexOf(node) !== -1;
16563 * @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
16564 * @param {Boolean} keepExisting (optional) true to keep existing selections
16565 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16567 select : function(nodeInfo, keepExisting, suppressEvent){
16568 if(nodeInfo instanceof Array){
16570 this.clearSelections(true);
16572 for(var i = 0, len = nodeInfo.length; i < len; i++){
16573 this.select(nodeInfo[i], true, true);
16577 var node = this.getNode(nodeInfo);
16578 if(!node || this.isSelected(node)){
16579 return; // already selected.
16582 this.clearSelections(true);
16585 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16586 Roo.fly(node).addClass(this.selectedClass);
16587 this.selections.push(node);
16588 if(!suppressEvent){
16589 this.fireEvent("selectionchange", this, this.selections);
16597 * @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
16598 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16599 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16601 unselect : function(nodeInfo, keepExisting, suppressEvent)
16603 if(nodeInfo instanceof Array){
16604 Roo.each(this.selections, function(s) {
16605 this.unselect(s, nodeInfo);
16609 var node = this.getNode(nodeInfo);
16610 if(!node || !this.isSelected(node)){
16611 //Roo.log("not selected");
16612 return; // not selected.
16616 Roo.each(this.selections, function(s) {
16618 Roo.fly(node).removeClass(this.selectedClass);
16625 this.selections= ns;
16626 this.fireEvent("selectionchange", this, this.selections);
16630 * Gets a template node.
16631 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16632 * @return {HTMLElement} The node or null if it wasn't found
16634 getNode : function(nodeInfo){
16635 if(typeof nodeInfo == "string"){
16636 return document.getElementById(nodeInfo);
16637 }else if(typeof nodeInfo == "number"){
16638 return this.nodes[nodeInfo];
16644 * Gets a range template nodes.
16645 * @param {Number} startIndex
16646 * @param {Number} endIndex
16647 * @return {Array} An array of nodes
16649 getNodes : function(start, end){
16650 var ns = this.nodes;
16651 start = start || 0;
16652 end = typeof end == "undefined" ? ns.length - 1 : end;
16655 for(var i = start; i <= end; i++){
16659 for(var i = start; i >= end; i--){
16667 * Finds the index of the passed node
16668 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16669 * @return {Number} The index of the node or -1
16671 indexOf : function(node){
16672 node = this.getNode(node);
16673 if(typeof node.nodeIndex == "number"){
16674 return node.nodeIndex;
16676 var ns = this.nodes;
16677 for(var i = 0, len = ns.length; i < len; i++){
16688 * based on jquery fullcalendar
16692 Roo.bootstrap = Roo.bootstrap || {};
16694 * @class Roo.bootstrap.Calendar
16695 * @extends Roo.bootstrap.Component
16696 * Bootstrap Calendar class
16697 * @cfg {Boolean} loadMask (true|false) default false
16698 * @cfg {Object} header generate the user specific header of the calendar, default false
16701 * Create a new Container
16702 * @param {Object} config The config object
16707 Roo.bootstrap.Calendar = function(config){
16708 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16712 * Fires when a date is selected
16713 * @param {DatePicker} this
16714 * @param {Date} date The selected date
16718 * @event monthchange
16719 * Fires when the displayed month changes
16720 * @param {DatePicker} this
16721 * @param {Date} date The selected month
16723 'monthchange': true,
16725 * @event evententer
16726 * Fires when mouse over an event
16727 * @param {Calendar} this
16728 * @param {event} Event
16730 'evententer': true,
16732 * @event eventleave
16733 * Fires when the mouse leaves an
16734 * @param {Calendar} this
16737 'eventleave': true,
16739 * @event eventclick
16740 * Fires when the mouse click an
16741 * @param {Calendar} this
16750 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16753 * @cfg {Number} startDay
16754 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16762 getAutoCreate : function(){
16765 var fc_button = function(name, corner, style, content ) {
16766 return Roo.apply({},{
16768 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16770 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16773 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16784 style : 'width:100%',
16791 cls : 'fc-header-left',
16793 fc_button('prev', 'left', 'arrow', '‹' ),
16794 fc_button('next', 'right', 'arrow', '›' ),
16795 { tag: 'span', cls: 'fc-header-space' },
16796 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16804 cls : 'fc-header-center',
16808 cls: 'fc-header-title',
16811 html : 'month / year'
16819 cls : 'fc-header-right',
16821 /* fc_button('month', 'left', '', 'month' ),
16822 fc_button('week', '', '', 'week' ),
16823 fc_button('day', 'right', '', 'day' )
16835 header = this.header;
16838 var cal_heads = function() {
16840 // fixme - handle this.
16842 for (var i =0; i < Date.dayNames.length; i++) {
16843 var d = Date.dayNames[i];
16846 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16847 html : d.substring(0,3)
16851 ret[0].cls += ' fc-first';
16852 ret[6].cls += ' fc-last';
16855 var cal_cell = function(n) {
16858 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16863 cls: 'fc-day-number',
16867 cls: 'fc-day-content',
16871 style: 'position: relative;' // height: 17px;
16883 var cal_rows = function() {
16886 for (var r = 0; r < 6; r++) {
16893 for (var i =0; i < Date.dayNames.length; i++) {
16894 var d = Date.dayNames[i];
16895 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16898 row.cn[0].cls+=' fc-first';
16899 row.cn[0].cn[0].style = 'min-height:90px';
16900 row.cn[6].cls+=' fc-last';
16904 ret[0].cls += ' fc-first';
16905 ret[4].cls += ' fc-prev-last';
16906 ret[5].cls += ' fc-last';
16913 cls: 'fc-border-separate',
16914 style : 'width:100%',
16922 cls : 'fc-first fc-last',
16940 cls : 'fc-content',
16941 style : "position: relative;",
16944 cls : 'fc-view fc-view-month fc-grid',
16945 style : 'position: relative',
16946 unselectable : 'on',
16949 cls : 'fc-event-container',
16950 style : 'position:absolute;z-index:8;top:0;left:0;'
16968 initEvents : function()
16971 throw "can not find store for calendar";
16977 style: "text-align:center",
16981 style: "background-color:white;width:50%;margin:250 auto",
16985 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16996 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16998 var size = this.el.select('.fc-content', true).first().getSize();
16999 this.maskEl.setSize(size.width, size.height);
17000 this.maskEl.enableDisplayMode("block");
17001 if(!this.loadMask){
17002 this.maskEl.hide();
17005 this.store = Roo.factory(this.store, Roo.data);
17006 this.store.on('load', this.onLoad, this);
17007 this.store.on('beforeload', this.onBeforeLoad, this);
17011 this.cells = this.el.select('.fc-day',true);
17012 //Roo.log(this.cells);
17013 this.textNodes = this.el.query('.fc-day-number');
17014 this.cells.addClassOnOver('fc-state-hover');
17016 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17017 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17018 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17019 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17021 this.on('monthchange', this.onMonthChange, this);
17023 this.update(new Date().clearTime());
17026 resize : function() {
17027 var sz = this.el.getSize();
17029 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17030 this.el.select('.fc-day-content div',true).setHeight(34);
17035 showPrevMonth : function(e){
17036 this.update(this.activeDate.add("mo", -1));
17038 showToday : function(e){
17039 this.update(new Date().clearTime());
17042 showNextMonth : function(e){
17043 this.update(this.activeDate.add("mo", 1));
17047 showPrevYear : function(){
17048 this.update(this.activeDate.add("y", -1));
17052 showNextYear : function(){
17053 this.update(this.activeDate.add("y", 1));
17058 update : function(date)
17060 var vd = this.activeDate;
17061 this.activeDate = date;
17062 // if(vd && this.el){
17063 // var t = date.getTime();
17064 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17065 // Roo.log('using add remove');
17067 // this.fireEvent('monthchange', this, date);
17069 // this.cells.removeClass("fc-state-highlight");
17070 // this.cells.each(function(c){
17071 // if(c.dateValue == t){
17072 // c.addClass("fc-state-highlight");
17073 // setTimeout(function(){
17074 // try{c.dom.firstChild.focus();}catch(e){}
17084 var days = date.getDaysInMonth();
17086 var firstOfMonth = date.getFirstDateOfMonth();
17087 var startingPos = firstOfMonth.getDay()-this.startDay;
17089 if(startingPos < this.startDay){
17093 var pm = date.add(Date.MONTH, -1);
17094 var prevStart = pm.getDaysInMonth()-startingPos;
17096 this.cells = this.el.select('.fc-day',true);
17097 this.textNodes = this.el.query('.fc-day-number');
17098 this.cells.addClassOnOver('fc-state-hover');
17100 var cells = this.cells.elements;
17101 var textEls = this.textNodes;
17103 Roo.each(cells, function(cell){
17104 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17107 days += startingPos;
17109 // convert everything to numbers so it's fast
17110 var day = 86400000;
17111 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17114 //Roo.log(prevStart);
17116 var today = new Date().clearTime().getTime();
17117 var sel = date.clearTime().getTime();
17118 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17119 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17120 var ddMatch = this.disabledDatesRE;
17121 var ddText = this.disabledDatesText;
17122 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17123 var ddaysText = this.disabledDaysText;
17124 var format = this.format;
17126 var setCellClass = function(cal, cell){
17130 //Roo.log('set Cell Class');
17132 var t = d.getTime();
17136 cell.dateValue = t;
17138 cell.className += " fc-today";
17139 cell.className += " fc-state-highlight";
17140 cell.title = cal.todayText;
17143 // disable highlight in other month..
17144 //cell.className += " fc-state-highlight";
17149 cell.className = " fc-state-disabled";
17150 cell.title = cal.minText;
17154 cell.className = " fc-state-disabled";
17155 cell.title = cal.maxText;
17159 if(ddays.indexOf(d.getDay()) != -1){
17160 cell.title = ddaysText;
17161 cell.className = " fc-state-disabled";
17164 if(ddMatch && format){
17165 var fvalue = d.dateFormat(format);
17166 if(ddMatch.test(fvalue)){
17167 cell.title = ddText.replace("%0", fvalue);
17168 cell.className = " fc-state-disabled";
17172 if (!cell.initialClassName) {
17173 cell.initialClassName = cell.dom.className;
17176 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17181 for(; i < startingPos; i++) {
17182 textEls[i].innerHTML = (++prevStart);
17183 d.setDate(d.getDate()+1);
17185 cells[i].className = "fc-past fc-other-month";
17186 setCellClass(this, cells[i]);
17191 for(; i < days; i++){
17192 intDay = i - startingPos + 1;
17193 textEls[i].innerHTML = (intDay);
17194 d.setDate(d.getDate()+1);
17196 cells[i].className = ''; // "x-date-active";
17197 setCellClass(this, cells[i]);
17201 for(; i < 42; i++) {
17202 textEls[i].innerHTML = (++extraDays);
17203 d.setDate(d.getDate()+1);
17205 cells[i].className = "fc-future fc-other-month";
17206 setCellClass(this, cells[i]);
17209 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17211 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17213 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17214 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17216 if(totalRows != 6){
17217 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17218 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17221 this.fireEvent('monthchange', this, date);
17225 if(!this.internalRender){
17226 var main = this.el.dom.firstChild;
17227 var w = main.offsetWidth;
17228 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17229 Roo.fly(main).setWidth(w);
17230 this.internalRender = true;
17231 // opera does not respect the auto grow header center column
17232 // then, after it gets a width opera refuses to recalculate
17233 // without a second pass
17234 if(Roo.isOpera && !this.secondPass){
17235 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17236 this.secondPass = true;
17237 this.update.defer(10, this, [date]);
17244 findCell : function(dt) {
17245 dt = dt.clearTime().getTime();
17247 this.cells.each(function(c){
17248 //Roo.log("check " +c.dateValue + '?=' + dt);
17249 if(c.dateValue == dt){
17259 findCells : function(ev) {
17260 var s = ev.start.clone().clearTime().getTime();
17262 var e= ev.end.clone().clearTime().getTime();
17265 this.cells.each(function(c){
17266 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17268 if(c.dateValue > e){
17271 if(c.dateValue < s){
17280 // findBestRow: function(cells)
17284 // for (var i =0 ; i < cells.length;i++) {
17285 // ret = Math.max(cells[i].rows || 0,ret);
17292 addItem : function(ev)
17294 // look for vertical location slot in
17295 var cells = this.findCells(ev);
17297 // ev.row = this.findBestRow(cells);
17299 // work out the location.
17303 for(var i =0; i < cells.length; i++) {
17305 cells[i].row = cells[0].row;
17308 cells[i].row = cells[i].row + 1;
17318 if (crow.start.getY() == cells[i].getY()) {
17320 crow.end = cells[i];
17337 cells[0].events.push(ev);
17339 this.calevents.push(ev);
17342 clearEvents: function() {
17344 if(!this.calevents){
17348 Roo.each(this.cells.elements, function(c){
17354 Roo.each(this.calevents, function(e) {
17355 Roo.each(e.els, function(el) {
17356 el.un('mouseenter' ,this.onEventEnter, this);
17357 el.un('mouseleave' ,this.onEventLeave, this);
17362 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17368 renderEvents: function()
17372 this.cells.each(function(c) {
17381 if(c.row != c.events.length){
17382 r = 4 - (4 - (c.row - c.events.length));
17385 c.events = ev.slice(0, r);
17386 c.more = ev.slice(r);
17388 if(c.more.length && c.more.length == 1){
17389 c.events.push(c.more.pop());
17392 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17396 this.cells.each(function(c) {
17398 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17401 for (var e = 0; e < c.events.length; e++){
17402 var ev = c.events[e];
17403 var rows = ev.rows;
17405 for(var i = 0; i < rows.length; i++) {
17407 // how many rows should it span..
17410 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17411 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17413 unselectable : "on",
17416 cls: 'fc-event-inner',
17420 // cls: 'fc-event-time',
17421 // html : cells.length > 1 ? '' : ev.time
17425 cls: 'fc-event-title',
17426 html : String.format('{0}', ev.title)
17433 cls: 'ui-resizable-handle ui-resizable-e',
17434 html : '  '
17441 cfg.cls += ' fc-event-start';
17443 if ((i+1) == rows.length) {
17444 cfg.cls += ' fc-event-end';
17447 var ctr = _this.el.select('.fc-event-container',true).first();
17448 var cg = ctr.createChild(cfg);
17450 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17451 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17453 var r = (c.more.length) ? 1 : 0;
17454 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17455 cg.setWidth(ebox.right - sbox.x -2);
17457 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17458 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17459 cg.on('click', _this.onEventClick, _this, ev);
17470 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17471 style : 'position: absolute',
17472 unselectable : "on",
17475 cls: 'fc-event-inner',
17479 cls: 'fc-event-title',
17487 cls: 'ui-resizable-handle ui-resizable-e',
17488 html : '  '
17494 var ctr = _this.el.select('.fc-event-container',true).first();
17495 var cg = ctr.createChild(cfg);
17497 var sbox = c.select('.fc-day-content',true).first().getBox();
17498 var ebox = c.select('.fc-day-content',true).first().getBox();
17500 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17501 cg.setWidth(ebox.right - sbox.x -2);
17503 cg.on('click', _this.onMoreEventClick, _this, c.more);
17513 onEventEnter: function (e, el,event,d) {
17514 this.fireEvent('evententer', this, el, event);
17517 onEventLeave: function (e, el,event,d) {
17518 this.fireEvent('eventleave', this, el, event);
17521 onEventClick: function (e, el,event,d) {
17522 this.fireEvent('eventclick', this, el, event);
17525 onMonthChange: function () {
17529 onMoreEventClick: function(e, el, more)
17533 this.calpopover.placement = 'right';
17534 this.calpopover.setTitle('More');
17536 this.calpopover.setContent('');
17538 var ctr = this.calpopover.el.select('.popover-content', true).first();
17540 Roo.each(more, function(m){
17542 cls : 'fc-event-hori fc-event-draggable',
17545 var cg = ctr.createChild(cfg);
17547 cg.on('click', _this.onEventClick, _this, m);
17550 this.calpopover.show(el);
17555 onLoad: function ()
17557 this.calevents = [];
17560 if(this.store.getCount() > 0){
17561 this.store.data.each(function(d){
17564 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17565 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17566 time : d.data.start_time,
17567 title : d.data.title,
17568 description : d.data.description,
17569 venue : d.data.venue
17574 this.renderEvents();
17576 if(this.calevents.length && this.loadMask){
17577 this.maskEl.hide();
17581 onBeforeLoad: function()
17583 this.clearEvents();
17585 this.maskEl.show();
17599 * @class Roo.bootstrap.Popover
17600 * @extends Roo.bootstrap.Component
17601 * Bootstrap Popover class
17602 * @cfg {String} html contents of the popover (or false to use children..)
17603 * @cfg {String} title of popover (or false to hide)
17604 * @cfg {String} placement how it is placed
17605 * @cfg {String} trigger click || hover (or false to trigger manually)
17606 * @cfg {String} over what (parent or false to trigger manually.)
17607 * @cfg {Number} delay - delay before showing
17610 * Create a new Popover
17611 * @param {Object} config The config object
17614 Roo.bootstrap.Popover = function(config){
17615 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17621 * After the popover show
17623 * @param {Roo.bootstrap.Popover} this
17628 * After the popover hide
17630 * @param {Roo.bootstrap.Popover} this
17636 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17638 title: 'Fill in a title',
17641 placement : 'right',
17642 trigger : 'hover', // hover
17648 can_build_overlaid : false,
17650 getChildContainer : function()
17652 return this.el.select('.popover-content',true).first();
17655 getAutoCreate : function(){
17658 cls : 'popover roo-dynamic',
17659 style: 'display:block',
17665 cls : 'popover-inner',
17669 cls: 'popover-title popover-header',
17673 cls : 'popover-content popover-body',
17684 setTitle: function(str)
17687 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17689 setContent: function(str)
17692 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17694 // as it get's added to the bottom of the page.
17695 onRender : function(ct, position)
17697 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17699 var cfg = Roo.apply({}, this.getAutoCreate());
17703 cfg.cls += ' ' + this.cls;
17706 cfg.style = this.style;
17708 //Roo.log("adding to ");
17709 this.el = Roo.get(document.body).createChild(cfg, position);
17710 // Roo.log(this.el);
17715 initEvents : function()
17717 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17718 this.el.enableDisplayMode('block');
17720 if (this.over === false) {
17723 if (this.triggers === false) {
17726 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17727 var triggers = this.trigger ? this.trigger.split(' ') : [];
17728 Roo.each(triggers, function(trigger) {
17730 if (trigger == 'click') {
17731 on_el.on('click', this.toggle, this);
17732 } else if (trigger != 'manual') {
17733 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17734 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17736 on_el.on(eventIn ,this.enter, this);
17737 on_el.on(eventOut, this.leave, this);
17748 toggle : function () {
17749 this.hoverState == 'in' ? this.leave() : this.enter();
17752 enter : function () {
17754 clearTimeout(this.timeout);
17756 this.hoverState = 'in';
17758 if (!this.delay || !this.delay.show) {
17763 this.timeout = setTimeout(function () {
17764 if (_t.hoverState == 'in') {
17767 }, this.delay.show)
17770 leave : function() {
17771 clearTimeout(this.timeout);
17773 this.hoverState = 'out';
17775 if (!this.delay || !this.delay.hide) {
17780 this.timeout = setTimeout(function () {
17781 if (_t.hoverState == 'out') {
17784 }, this.delay.hide)
17787 show : function (on_el)
17790 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17794 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17795 if (this.html !== false) {
17796 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17798 this.el.removeClass([
17799 'fade','top','bottom', 'left', 'right','in',
17800 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17802 if (!this.title.length) {
17803 this.el.select('.popover-title',true).hide();
17806 var placement = typeof this.placement == 'function' ?
17807 this.placement.call(this, this.el, on_el) :
17810 var autoToken = /\s?auto?\s?/i;
17811 var autoPlace = autoToken.test(placement);
17813 placement = placement.replace(autoToken, '') || 'top';
17817 //this.el.setXY([0,0]);
17819 this.el.dom.style.display='block';
17820 this.el.addClass(placement);
17822 //this.el.appendTo(on_el);
17824 var p = this.getPosition();
17825 var box = this.el.getBox();
17830 var align = Roo.bootstrap.Popover.alignment[placement];
17833 this.el.alignTo(on_el, align[0],align[1]);
17834 //var arrow = this.el.select('.arrow',true).first();
17835 //arrow.set(align[2],
17837 this.el.addClass('in');
17840 if (this.el.hasClass('fade')) {
17844 this.hoverState = 'in';
17846 this.fireEvent('show', this);
17851 this.el.setXY([0,0]);
17852 this.el.removeClass('in');
17854 this.hoverState = null;
17856 this.fireEvent('hide', this);
17861 Roo.bootstrap.Popover.alignment = {
17862 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17863 'right' : ['l-r', [10,0], 'left bs-popover-left'],
17864 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17865 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17876 * @class Roo.bootstrap.Progress
17877 * @extends Roo.bootstrap.Component
17878 * Bootstrap Progress class
17879 * @cfg {Boolean} striped striped of the progress bar
17880 * @cfg {Boolean} active animated of the progress bar
17884 * Create a new Progress
17885 * @param {Object} config The config object
17888 Roo.bootstrap.Progress = function(config){
17889 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17892 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17897 getAutoCreate : function(){
17905 cfg.cls += ' progress-striped';
17909 cfg.cls += ' active';
17928 * @class Roo.bootstrap.ProgressBar
17929 * @extends Roo.bootstrap.Component
17930 * Bootstrap ProgressBar class
17931 * @cfg {Number} aria_valuenow aria-value now
17932 * @cfg {Number} aria_valuemin aria-value min
17933 * @cfg {Number} aria_valuemax aria-value max
17934 * @cfg {String} label label for the progress bar
17935 * @cfg {String} panel (success | info | warning | danger )
17936 * @cfg {String} role role of the progress bar
17937 * @cfg {String} sr_only text
17941 * Create a new ProgressBar
17942 * @param {Object} config The config object
17945 Roo.bootstrap.ProgressBar = function(config){
17946 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17949 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17953 aria_valuemax : 100,
17959 getAutoCreate : function()
17964 cls: 'progress-bar',
17965 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17977 cfg.role = this.role;
17980 if(this.aria_valuenow){
17981 cfg['aria-valuenow'] = this.aria_valuenow;
17984 if(this.aria_valuemin){
17985 cfg['aria-valuemin'] = this.aria_valuemin;
17988 if(this.aria_valuemax){
17989 cfg['aria-valuemax'] = this.aria_valuemax;
17992 if(this.label && !this.sr_only){
17993 cfg.html = this.label;
17997 cfg.cls += ' progress-bar-' + this.panel;
18003 update : function(aria_valuenow)
18005 this.aria_valuenow = aria_valuenow;
18007 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18022 * @class Roo.bootstrap.TabGroup
18023 * @extends Roo.bootstrap.Column
18024 * Bootstrap Column class
18025 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18026 * @cfg {Boolean} carousel true to make the group behave like a carousel
18027 * @cfg {Boolean} bullets show bullets for the panels
18028 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18029 * @cfg {Number} timer auto slide timer .. default 0 millisecond
18030 * @cfg {Boolean} showarrow (true|false) show arrow default true
18033 * Create a new TabGroup
18034 * @param {Object} config The config object
18037 Roo.bootstrap.TabGroup = function(config){
18038 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18040 this.navId = Roo.id();
18043 Roo.bootstrap.TabGroup.register(this);
18047 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18050 transition : false,
18055 slideOnTouch : false,
18058 getAutoCreate : function()
18060 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18062 cfg.cls += ' tab-content';
18064 if (this.carousel) {
18065 cfg.cls += ' carousel slide';
18068 cls : 'carousel-inner',
18072 if(this.bullets && !Roo.isTouch){
18075 cls : 'carousel-bullets',
18079 if(this.bullets_cls){
18080 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18087 cfg.cn[0].cn.push(bullets);
18090 if(this.showarrow){
18091 cfg.cn[0].cn.push({
18093 class : 'carousel-arrow',
18097 class : 'carousel-prev',
18101 class : 'fa fa-chevron-left'
18107 class : 'carousel-next',
18111 class : 'fa fa-chevron-right'
18124 initEvents: function()
18126 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18127 // this.el.on("touchstart", this.onTouchStart, this);
18130 if(this.autoslide){
18133 this.slideFn = window.setInterval(function() {
18134 _this.showPanelNext();
18138 if(this.showarrow){
18139 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18140 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18146 // onTouchStart : function(e, el, o)
18148 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18152 // this.showPanelNext();
18156 getChildContainer : function()
18158 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18162 * register a Navigation item
18163 * @param {Roo.bootstrap.NavItem} the navitem to add
18165 register : function(item)
18167 this.tabs.push( item);
18168 item.navId = this.navId; // not really needed..
18173 getActivePanel : function()
18176 Roo.each(this.tabs, function(t) {
18186 getPanelByName : function(n)
18189 Roo.each(this.tabs, function(t) {
18190 if (t.tabId == n) {
18198 indexOfPanel : function(p)
18201 Roo.each(this.tabs, function(t,i) {
18202 if (t.tabId == p.tabId) {
18211 * show a specific panel
18212 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18213 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18215 showPanel : function (pan)
18217 if(this.transition || typeof(pan) == 'undefined'){
18218 Roo.log("waiting for the transitionend");
18222 if (typeof(pan) == 'number') {
18223 pan = this.tabs[pan];
18226 if (typeof(pan) == 'string') {
18227 pan = this.getPanelByName(pan);
18230 var cur = this.getActivePanel();
18233 Roo.log('pan or acitve pan is undefined');
18237 if (pan.tabId == this.getActivePanel().tabId) {
18241 if (false === cur.fireEvent('beforedeactivate')) {
18245 if(this.bullets > 0 && !Roo.isTouch){
18246 this.setActiveBullet(this.indexOfPanel(pan));
18249 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18251 this.transition = true;
18252 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18253 var lr = dir == 'next' ? 'left' : 'right';
18254 pan.el.addClass(dir); // or prev
18255 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18256 cur.el.addClass(lr); // or right
18257 pan.el.addClass(lr);
18260 cur.el.on('transitionend', function() {
18261 Roo.log("trans end?");
18263 pan.el.removeClass([lr,dir]);
18264 pan.setActive(true);
18266 cur.el.removeClass([lr]);
18267 cur.setActive(false);
18269 _this.transition = false;
18271 }, this, { single: true } );
18276 cur.setActive(false);
18277 pan.setActive(true);
18282 showPanelNext : function()
18284 var i = this.indexOfPanel(this.getActivePanel());
18286 if (i >= this.tabs.length - 1 && !this.autoslide) {
18290 if (i >= this.tabs.length - 1 && this.autoslide) {
18294 this.showPanel(this.tabs[i+1]);
18297 showPanelPrev : function()
18299 var i = this.indexOfPanel(this.getActivePanel());
18301 if (i < 1 && !this.autoslide) {
18305 if (i < 1 && this.autoslide) {
18306 i = this.tabs.length;
18309 this.showPanel(this.tabs[i-1]);
18313 addBullet: function()
18315 if(!this.bullets || Roo.isTouch){
18318 var ctr = this.el.select('.carousel-bullets',true).first();
18319 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18320 var bullet = ctr.createChild({
18321 cls : 'bullet bullet-' + i
18322 },ctr.dom.lastChild);
18327 bullet.on('click', (function(e, el, o, ii, t){
18329 e.preventDefault();
18331 this.showPanel(ii);
18333 if(this.autoslide && this.slideFn){
18334 clearInterval(this.slideFn);
18335 this.slideFn = window.setInterval(function() {
18336 _this.showPanelNext();
18340 }).createDelegate(this, [i, bullet], true));
18345 setActiveBullet : function(i)
18351 Roo.each(this.el.select('.bullet', true).elements, function(el){
18352 el.removeClass('selected');
18355 var bullet = this.el.select('.bullet-' + i, true).first();
18361 bullet.addClass('selected');
18372 Roo.apply(Roo.bootstrap.TabGroup, {
18376 * register a Navigation Group
18377 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18379 register : function(navgrp)
18381 this.groups[navgrp.navId] = navgrp;
18385 * fetch a Navigation Group based on the navigation ID
18386 * if one does not exist , it will get created.
18387 * @param {string} the navgroup to add
18388 * @returns {Roo.bootstrap.NavGroup} the navgroup
18390 get: function(navId) {
18391 if (typeof(this.groups[navId]) == 'undefined') {
18392 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18394 return this.groups[navId] ;
18409 * @class Roo.bootstrap.TabPanel
18410 * @extends Roo.bootstrap.Component
18411 * Bootstrap TabPanel class
18412 * @cfg {Boolean} active panel active
18413 * @cfg {String} html panel content
18414 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18415 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18416 * @cfg {String} href click to link..
18420 * Create a new TabPanel
18421 * @param {Object} config The config object
18424 Roo.bootstrap.TabPanel = function(config){
18425 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18429 * Fires when the active status changes
18430 * @param {Roo.bootstrap.TabPanel} this
18431 * @param {Boolean} state the new state
18436 * @event beforedeactivate
18437 * Fires before a tab is de-activated - can be used to do validation on a form.
18438 * @param {Roo.bootstrap.TabPanel} this
18439 * @return {Boolean} false if there is an error
18442 'beforedeactivate': true
18445 this.tabId = this.tabId || Roo.id();
18449 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18457 getAutoCreate : function(){
18460 // item is needed for carousel - not sure if it has any effect otherwise
18461 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18462 html: this.html || ''
18466 cfg.cls += ' active';
18470 cfg.tabId = this.tabId;
18477 initEvents: function()
18479 var p = this.parent();
18481 this.navId = this.navId || p.navId;
18483 if (typeof(this.navId) != 'undefined') {
18484 // not really needed.. but just in case.. parent should be a NavGroup.
18485 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18489 var i = tg.tabs.length - 1;
18491 if(this.active && tg.bullets > 0 && i < tg.bullets){
18492 tg.setActiveBullet(i);
18496 this.el.on('click', this.onClick, this);
18499 this.el.on("touchstart", this.onTouchStart, this);
18500 this.el.on("touchmove", this.onTouchMove, this);
18501 this.el.on("touchend", this.onTouchEnd, this);
18506 onRender : function(ct, position)
18508 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18511 setActive : function(state)
18513 Roo.log("panel - set active " + this.tabId + "=" + state);
18515 this.active = state;
18517 this.el.removeClass('active');
18519 } else if (!this.el.hasClass('active')) {
18520 this.el.addClass('active');
18523 this.fireEvent('changed', this, state);
18526 onClick : function(e)
18528 e.preventDefault();
18530 if(!this.href.length){
18534 window.location.href = this.href;
18543 onTouchStart : function(e)
18545 this.swiping = false;
18547 this.startX = e.browserEvent.touches[0].clientX;
18548 this.startY = e.browserEvent.touches[0].clientY;
18551 onTouchMove : function(e)
18553 this.swiping = true;
18555 this.endX = e.browserEvent.touches[0].clientX;
18556 this.endY = e.browserEvent.touches[0].clientY;
18559 onTouchEnd : function(e)
18566 var tabGroup = this.parent();
18568 if(this.endX > this.startX){ // swiping right
18569 tabGroup.showPanelPrev();
18573 if(this.startX > this.endX){ // swiping left
18574 tabGroup.showPanelNext();
18593 * @class Roo.bootstrap.DateField
18594 * @extends Roo.bootstrap.Input
18595 * Bootstrap DateField class
18596 * @cfg {Number} weekStart default 0
18597 * @cfg {String} viewMode default empty, (months|years)
18598 * @cfg {String} minViewMode default empty, (months|years)
18599 * @cfg {Number} startDate default -Infinity
18600 * @cfg {Number} endDate default Infinity
18601 * @cfg {Boolean} todayHighlight default false
18602 * @cfg {Boolean} todayBtn default false
18603 * @cfg {Boolean} calendarWeeks default false
18604 * @cfg {Object} daysOfWeekDisabled default empty
18605 * @cfg {Boolean} singleMode default false (true | false)
18607 * @cfg {Boolean} keyboardNavigation default true
18608 * @cfg {String} language default en
18611 * Create a new DateField
18612 * @param {Object} config The config object
18615 Roo.bootstrap.DateField = function(config){
18616 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18620 * Fires when this field show.
18621 * @param {Roo.bootstrap.DateField} this
18622 * @param {Mixed} date The date value
18627 * Fires when this field hide.
18628 * @param {Roo.bootstrap.DateField} this
18629 * @param {Mixed} date The date value
18634 * Fires when select a date.
18635 * @param {Roo.bootstrap.DateField} this
18636 * @param {Mixed} date The date value
18640 * @event beforeselect
18641 * Fires when before select a date.
18642 * @param {Roo.bootstrap.DateField} this
18643 * @param {Mixed} date The date value
18645 beforeselect : true
18649 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18652 * @cfg {String} format
18653 * The default date format string which can be overriden for localization support. The format must be
18654 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18658 * @cfg {String} altFormats
18659 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18660 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18662 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18670 todayHighlight : false,
18676 keyboardNavigation: true,
18678 calendarWeeks: false,
18680 startDate: -Infinity,
18684 daysOfWeekDisabled: [],
18688 singleMode : false,
18690 UTCDate: function()
18692 return new Date(Date.UTC.apply(Date, arguments));
18695 UTCToday: function()
18697 var today = new Date();
18698 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18701 getDate: function() {
18702 var d = this.getUTCDate();
18703 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18706 getUTCDate: function() {
18710 setDate: function(d) {
18711 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18714 setUTCDate: function(d) {
18716 this.setValue(this.formatDate(this.date));
18719 onRender: function(ct, position)
18722 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18724 this.language = this.language || 'en';
18725 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18726 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18728 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18729 this.format = this.format || 'm/d/y';
18730 this.isInline = false;
18731 this.isInput = true;
18732 this.component = this.el.select('.add-on', true).first() || false;
18733 this.component = (this.component && this.component.length === 0) ? false : this.component;
18734 this.hasInput = this.component && this.inputEl().length;
18736 if (typeof(this.minViewMode === 'string')) {
18737 switch (this.minViewMode) {
18739 this.minViewMode = 1;
18742 this.minViewMode = 2;
18745 this.minViewMode = 0;
18750 if (typeof(this.viewMode === 'string')) {
18751 switch (this.viewMode) {
18764 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18766 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18768 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18770 this.picker().on('mousedown', this.onMousedown, this);
18771 this.picker().on('click', this.onClick, this);
18773 this.picker().addClass('datepicker-dropdown');
18775 this.startViewMode = this.viewMode;
18777 if(this.singleMode){
18778 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18779 v.setVisibilityMode(Roo.Element.DISPLAY);
18783 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18784 v.setStyle('width', '189px');
18788 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18789 if(!this.calendarWeeks){
18794 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18795 v.attr('colspan', function(i, val){
18796 return parseInt(val) + 1;
18801 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18803 this.setStartDate(this.startDate);
18804 this.setEndDate(this.endDate);
18806 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18813 if(this.isInline) {
18818 picker : function()
18820 return this.pickerEl;
18821 // return this.el.select('.datepicker', true).first();
18824 fillDow: function()
18826 var dowCnt = this.weekStart;
18835 if(this.calendarWeeks){
18843 while (dowCnt < this.weekStart + 7) {
18847 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18851 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18854 fillMonths: function()
18857 var months = this.picker().select('>.datepicker-months td', true).first();
18859 months.dom.innerHTML = '';
18865 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18868 months.createChild(month);
18875 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;
18877 if (this.date < this.startDate) {
18878 this.viewDate = new Date(this.startDate);
18879 } else if (this.date > this.endDate) {
18880 this.viewDate = new Date(this.endDate);
18882 this.viewDate = new Date(this.date);
18890 var d = new Date(this.viewDate),
18891 year = d.getUTCFullYear(),
18892 month = d.getUTCMonth(),
18893 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18894 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18895 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18896 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18897 currentDate = this.date && this.date.valueOf(),
18898 today = this.UTCToday();
18900 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18902 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18904 // this.picker.select('>tfoot th.today').
18905 // .text(dates[this.language].today)
18906 // .toggle(this.todayBtn !== false);
18908 this.updateNavArrows();
18911 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18913 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18915 prevMonth.setUTCDate(day);
18917 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18919 var nextMonth = new Date(prevMonth);
18921 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18923 nextMonth = nextMonth.valueOf();
18925 var fillMonths = false;
18927 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18929 while(prevMonth.valueOf() <= nextMonth) {
18932 if (prevMonth.getUTCDay() === this.weekStart) {
18934 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18942 if(this.calendarWeeks){
18943 // ISO 8601: First week contains first thursday.
18944 // ISO also states week starts on Monday, but we can be more abstract here.
18946 // Start of current week: based on weekstart/current date
18947 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18948 // Thursday of this week
18949 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18950 // First Thursday of year, year from thursday
18951 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18952 // Calendar week: ms between thursdays, div ms per day, div 7 days
18953 calWeek = (th - yth) / 864e5 / 7 + 1;
18955 fillMonths.cn.push({
18963 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18965 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18968 if (this.todayHighlight &&
18969 prevMonth.getUTCFullYear() == today.getFullYear() &&
18970 prevMonth.getUTCMonth() == today.getMonth() &&
18971 prevMonth.getUTCDate() == today.getDate()) {
18972 clsName += ' today';
18975 if (currentDate && prevMonth.valueOf() === currentDate) {
18976 clsName += ' active';
18979 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18980 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18981 clsName += ' disabled';
18984 fillMonths.cn.push({
18986 cls: 'day ' + clsName,
18987 html: prevMonth.getDate()
18990 prevMonth.setDate(prevMonth.getDate()+1);
18993 var currentYear = this.date && this.date.getUTCFullYear();
18994 var currentMonth = this.date && this.date.getUTCMonth();
18996 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18998 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18999 v.removeClass('active');
19001 if(currentYear === year && k === currentMonth){
19002 v.addClass('active');
19005 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19006 v.addClass('disabled');
19012 year = parseInt(year/10, 10) * 10;
19014 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19016 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19019 for (var i = -1; i < 11; i++) {
19020 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19022 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19030 showMode: function(dir)
19033 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19036 Roo.each(this.picker().select('>div',true).elements, function(v){
19037 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19040 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19045 if(this.isInline) {
19049 this.picker().removeClass(['bottom', 'top']);
19051 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19053 * place to the top of element!
19057 this.picker().addClass('top');
19058 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19063 this.picker().addClass('bottom');
19065 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19068 parseDate : function(value)
19070 if(!value || value instanceof Date){
19073 var v = Date.parseDate(value, this.format);
19074 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19075 v = Date.parseDate(value, 'Y-m-d');
19077 if(!v && this.altFormats){
19078 if(!this.altFormatsArray){
19079 this.altFormatsArray = this.altFormats.split("|");
19081 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19082 v = Date.parseDate(value, this.altFormatsArray[i]);
19088 formatDate : function(date, fmt)
19090 return (!date || !(date instanceof Date)) ?
19091 date : date.dateFormat(fmt || this.format);
19094 onFocus : function()
19096 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19100 onBlur : function()
19102 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19104 var d = this.inputEl().getValue();
19111 showPopup : function()
19113 this.picker().show();
19117 this.fireEvent('showpopup', this, this.date);
19120 hidePopup : function()
19122 if(this.isInline) {
19125 this.picker().hide();
19126 this.viewMode = this.startViewMode;
19129 this.fireEvent('hidepopup', this, this.date);
19133 onMousedown: function(e)
19135 e.stopPropagation();
19136 e.preventDefault();
19141 Roo.bootstrap.DateField.superclass.keyup.call(this);
19145 setValue: function(v)
19147 if(this.fireEvent('beforeselect', this, v) !== false){
19148 var d = new Date(this.parseDate(v) ).clearTime();
19150 if(isNaN(d.getTime())){
19151 this.date = this.viewDate = '';
19152 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19156 v = this.formatDate(d);
19158 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19160 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19164 this.fireEvent('select', this, this.date);
19168 getValue: function()
19170 return this.formatDate(this.date);
19173 fireKey: function(e)
19175 if (!this.picker().isVisible()){
19176 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19182 var dateChanged = false,
19184 newDate, newViewDate;
19189 e.preventDefault();
19193 if (!this.keyboardNavigation) {
19196 dir = e.keyCode == 37 ? -1 : 1;
19199 newDate = this.moveYear(this.date, dir);
19200 newViewDate = this.moveYear(this.viewDate, dir);
19201 } else if (e.shiftKey){
19202 newDate = this.moveMonth(this.date, dir);
19203 newViewDate = this.moveMonth(this.viewDate, dir);
19205 newDate = new Date(this.date);
19206 newDate.setUTCDate(this.date.getUTCDate() + dir);
19207 newViewDate = new Date(this.viewDate);
19208 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19210 if (this.dateWithinRange(newDate)){
19211 this.date = newDate;
19212 this.viewDate = newViewDate;
19213 this.setValue(this.formatDate(this.date));
19215 e.preventDefault();
19216 dateChanged = true;
19221 if (!this.keyboardNavigation) {
19224 dir = e.keyCode == 38 ? -1 : 1;
19226 newDate = this.moveYear(this.date, dir);
19227 newViewDate = this.moveYear(this.viewDate, dir);
19228 } else if (e.shiftKey){
19229 newDate = this.moveMonth(this.date, dir);
19230 newViewDate = this.moveMonth(this.viewDate, dir);
19232 newDate = new Date(this.date);
19233 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19234 newViewDate = new Date(this.viewDate);
19235 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19237 if (this.dateWithinRange(newDate)){
19238 this.date = newDate;
19239 this.viewDate = newViewDate;
19240 this.setValue(this.formatDate(this.date));
19242 e.preventDefault();
19243 dateChanged = true;
19247 this.setValue(this.formatDate(this.date));
19249 e.preventDefault();
19252 this.setValue(this.formatDate(this.date));
19266 onClick: function(e)
19268 e.stopPropagation();
19269 e.preventDefault();
19271 var target = e.getTarget();
19273 if(target.nodeName.toLowerCase() === 'i'){
19274 target = Roo.get(target).dom.parentNode;
19277 var nodeName = target.nodeName;
19278 var className = target.className;
19279 var html = target.innerHTML;
19280 //Roo.log(nodeName);
19282 switch(nodeName.toLowerCase()) {
19284 switch(className) {
19290 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19291 switch(this.viewMode){
19293 this.viewDate = this.moveMonth(this.viewDate, dir);
19297 this.viewDate = this.moveYear(this.viewDate, dir);
19303 var date = new Date();
19304 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19306 this.setValue(this.formatDate(this.date));
19313 if (className.indexOf('disabled') < 0) {
19314 this.viewDate.setUTCDate(1);
19315 if (className.indexOf('month') > -1) {
19316 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19318 var year = parseInt(html, 10) || 0;
19319 this.viewDate.setUTCFullYear(year);
19323 if(this.singleMode){
19324 this.setValue(this.formatDate(this.viewDate));
19335 //Roo.log(className);
19336 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19337 var day = parseInt(html, 10) || 1;
19338 var year = this.viewDate.getUTCFullYear(),
19339 month = this.viewDate.getUTCMonth();
19341 if (className.indexOf('old') > -1) {
19348 } else if (className.indexOf('new') > -1) {
19356 //Roo.log([year,month,day]);
19357 this.date = this.UTCDate(year, month, day,0,0,0,0);
19358 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19360 //Roo.log(this.formatDate(this.date));
19361 this.setValue(this.formatDate(this.date));
19368 setStartDate: function(startDate)
19370 this.startDate = startDate || -Infinity;
19371 if (this.startDate !== -Infinity) {
19372 this.startDate = this.parseDate(this.startDate);
19375 this.updateNavArrows();
19378 setEndDate: function(endDate)
19380 this.endDate = endDate || Infinity;
19381 if (this.endDate !== Infinity) {
19382 this.endDate = this.parseDate(this.endDate);
19385 this.updateNavArrows();
19388 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19390 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19391 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19392 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19394 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19395 return parseInt(d, 10);
19398 this.updateNavArrows();
19401 updateNavArrows: function()
19403 if(this.singleMode){
19407 var d = new Date(this.viewDate),
19408 year = d.getUTCFullYear(),
19409 month = d.getUTCMonth();
19411 Roo.each(this.picker().select('.prev', true).elements, function(v){
19413 switch (this.viewMode) {
19416 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19422 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19429 Roo.each(this.picker().select('.next', true).elements, function(v){
19431 switch (this.viewMode) {
19434 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19440 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19448 moveMonth: function(date, dir)
19453 var new_date = new Date(date.valueOf()),
19454 day = new_date.getUTCDate(),
19455 month = new_date.getUTCMonth(),
19456 mag = Math.abs(dir),
19458 dir = dir > 0 ? 1 : -1;
19461 // If going back one month, make sure month is not current month
19462 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19464 return new_date.getUTCMonth() == month;
19466 // If going forward one month, make sure month is as expected
19467 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19469 return new_date.getUTCMonth() != new_month;
19471 new_month = month + dir;
19472 new_date.setUTCMonth(new_month);
19473 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19474 if (new_month < 0 || new_month > 11) {
19475 new_month = (new_month + 12) % 12;
19478 // For magnitudes >1, move one month at a time...
19479 for (var i=0; i<mag; i++) {
19480 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19481 new_date = this.moveMonth(new_date, dir);
19483 // ...then reset the day, keeping it in the new month
19484 new_month = new_date.getUTCMonth();
19485 new_date.setUTCDate(day);
19487 return new_month != new_date.getUTCMonth();
19490 // Common date-resetting loop -- if date is beyond end of month, make it
19493 new_date.setUTCDate(--day);
19494 new_date.setUTCMonth(new_month);
19499 moveYear: function(date, dir)
19501 return this.moveMonth(date, dir*12);
19504 dateWithinRange: function(date)
19506 return date >= this.startDate && date <= this.endDate;
19512 this.picker().remove();
19515 validateValue : function(value)
19517 if(this.getVisibilityEl().hasClass('hidden')){
19521 if(value.length < 1) {
19522 if(this.allowBlank){
19528 if(value.length < this.minLength){
19531 if(value.length > this.maxLength){
19535 var vt = Roo.form.VTypes;
19536 if(!vt[this.vtype](value, this)){
19540 if(typeof this.validator == "function"){
19541 var msg = this.validator(value);
19547 if(this.regex && !this.regex.test(value)){
19551 if(typeof(this.parseDate(value)) == 'undefined'){
19555 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19559 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19569 this.date = this.viewDate = '';
19571 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19576 Roo.apply(Roo.bootstrap.DateField, {
19587 html: '<i class="fa fa-arrow-left"/>'
19597 html: '<i class="fa fa-arrow-right"/>'
19639 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19640 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19641 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19642 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19643 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19656 navFnc: 'FullYear',
19661 navFnc: 'FullYear',
19666 Roo.apply(Roo.bootstrap.DateField, {
19670 cls: 'datepicker dropdown-menu roo-dynamic',
19674 cls: 'datepicker-days',
19678 cls: 'table-condensed',
19680 Roo.bootstrap.DateField.head,
19684 Roo.bootstrap.DateField.footer
19691 cls: 'datepicker-months',
19695 cls: 'table-condensed',
19697 Roo.bootstrap.DateField.head,
19698 Roo.bootstrap.DateField.content,
19699 Roo.bootstrap.DateField.footer
19706 cls: 'datepicker-years',
19710 cls: 'table-condensed',
19712 Roo.bootstrap.DateField.head,
19713 Roo.bootstrap.DateField.content,
19714 Roo.bootstrap.DateField.footer
19733 * @class Roo.bootstrap.TimeField
19734 * @extends Roo.bootstrap.Input
19735 * Bootstrap DateField class
19739 * Create a new TimeField
19740 * @param {Object} config The config object
19743 Roo.bootstrap.TimeField = function(config){
19744 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19748 * Fires when this field show.
19749 * @param {Roo.bootstrap.DateField} thisthis
19750 * @param {Mixed} date The date value
19755 * Fires when this field hide.
19756 * @param {Roo.bootstrap.DateField} this
19757 * @param {Mixed} date The date value
19762 * Fires when select a date.
19763 * @param {Roo.bootstrap.DateField} this
19764 * @param {Mixed} date The date value
19770 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19773 * @cfg {String} format
19774 * The default time format string which can be overriden for localization support. The format must be
19775 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19779 onRender: function(ct, position)
19782 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19784 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19786 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19788 this.pop = this.picker().select('>.datepicker-time',true).first();
19789 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19791 this.picker().on('mousedown', this.onMousedown, this);
19792 this.picker().on('click', this.onClick, this);
19794 this.picker().addClass('datepicker-dropdown');
19799 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19800 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19801 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19802 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19803 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19804 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19808 fireKey: function(e){
19809 if (!this.picker().isVisible()){
19810 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19816 e.preventDefault();
19824 this.onTogglePeriod();
19827 this.onIncrementMinutes();
19830 this.onDecrementMinutes();
19839 onClick: function(e) {
19840 e.stopPropagation();
19841 e.preventDefault();
19844 picker : function()
19846 return this.el.select('.datepicker', true).first();
19849 fillTime: function()
19851 var time = this.pop.select('tbody', true).first();
19853 time.dom.innerHTML = '';
19868 cls: 'hours-up glyphicon glyphicon-chevron-up'
19888 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19909 cls: 'timepicker-hour',
19924 cls: 'timepicker-minute',
19939 cls: 'btn btn-primary period',
19961 cls: 'hours-down glyphicon glyphicon-chevron-down'
19981 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19999 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20006 var hours = this.time.getHours();
20007 var minutes = this.time.getMinutes();
20020 hours = hours - 12;
20024 hours = '0' + hours;
20028 minutes = '0' + minutes;
20031 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20032 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20033 this.pop.select('button', true).first().dom.innerHTML = period;
20039 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20041 var cls = ['bottom'];
20043 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20050 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20055 this.picker().addClass(cls.join('-'));
20059 Roo.each(cls, function(c){
20061 _this.picker().setTop(_this.inputEl().getHeight());
20065 _this.picker().setTop(0 - _this.picker().getHeight());
20070 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20074 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20081 onFocus : function()
20083 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20087 onBlur : function()
20089 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20095 this.picker().show();
20100 this.fireEvent('show', this, this.date);
20105 this.picker().hide();
20108 this.fireEvent('hide', this, this.date);
20111 setTime : function()
20114 this.setValue(this.time.format(this.format));
20116 this.fireEvent('select', this, this.date);
20121 onMousedown: function(e){
20122 e.stopPropagation();
20123 e.preventDefault();
20126 onIncrementHours: function()
20128 Roo.log('onIncrementHours');
20129 this.time = this.time.add(Date.HOUR, 1);
20134 onDecrementHours: function()
20136 Roo.log('onDecrementHours');
20137 this.time = this.time.add(Date.HOUR, -1);
20141 onIncrementMinutes: function()
20143 Roo.log('onIncrementMinutes');
20144 this.time = this.time.add(Date.MINUTE, 1);
20148 onDecrementMinutes: function()
20150 Roo.log('onDecrementMinutes');
20151 this.time = this.time.add(Date.MINUTE, -1);
20155 onTogglePeriod: function()
20157 Roo.log('onTogglePeriod');
20158 this.time = this.time.add(Date.HOUR, 12);
20165 Roo.apply(Roo.bootstrap.TimeField, {
20195 cls: 'btn btn-info ok',
20207 Roo.apply(Roo.bootstrap.TimeField, {
20211 cls: 'datepicker dropdown-menu',
20215 cls: 'datepicker-time',
20219 cls: 'table-condensed',
20221 Roo.bootstrap.TimeField.content,
20222 Roo.bootstrap.TimeField.footer
20241 * @class Roo.bootstrap.MonthField
20242 * @extends Roo.bootstrap.Input
20243 * Bootstrap MonthField class
20245 * @cfg {String} language default en
20248 * Create a new MonthField
20249 * @param {Object} config The config object
20252 Roo.bootstrap.MonthField = function(config){
20253 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20258 * Fires when this field show.
20259 * @param {Roo.bootstrap.MonthField} this
20260 * @param {Mixed} date The date value
20265 * Fires when this field hide.
20266 * @param {Roo.bootstrap.MonthField} this
20267 * @param {Mixed} date The date value
20272 * Fires when select a date.
20273 * @param {Roo.bootstrap.MonthField} this
20274 * @param {String} oldvalue The old value
20275 * @param {String} newvalue The new value
20281 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20283 onRender: function(ct, position)
20286 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20288 this.language = this.language || 'en';
20289 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20290 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20292 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20293 this.isInline = false;
20294 this.isInput = true;
20295 this.component = this.el.select('.add-on', true).first() || false;
20296 this.component = (this.component && this.component.length === 0) ? false : this.component;
20297 this.hasInput = this.component && this.inputEL().length;
20299 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20301 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20303 this.picker().on('mousedown', this.onMousedown, this);
20304 this.picker().on('click', this.onClick, this);
20306 this.picker().addClass('datepicker-dropdown');
20308 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20309 v.setStyle('width', '189px');
20316 if(this.isInline) {
20322 setValue: function(v, suppressEvent)
20324 var o = this.getValue();
20326 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20330 if(suppressEvent !== true){
20331 this.fireEvent('select', this, o, v);
20336 getValue: function()
20341 onClick: function(e)
20343 e.stopPropagation();
20344 e.preventDefault();
20346 var target = e.getTarget();
20348 if(target.nodeName.toLowerCase() === 'i'){
20349 target = Roo.get(target).dom.parentNode;
20352 var nodeName = target.nodeName;
20353 var className = target.className;
20354 var html = target.innerHTML;
20356 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20360 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20362 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20368 picker : function()
20370 return this.pickerEl;
20373 fillMonths: function()
20376 var months = this.picker().select('>.datepicker-months td', true).first();
20378 months.dom.innerHTML = '';
20384 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20387 months.createChild(month);
20396 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20397 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20400 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20401 e.removeClass('active');
20403 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20404 e.addClass('active');
20411 if(this.isInline) {
20415 this.picker().removeClass(['bottom', 'top']);
20417 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20419 * place to the top of element!
20423 this.picker().addClass('top');
20424 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20429 this.picker().addClass('bottom');
20431 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20434 onFocus : function()
20436 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20440 onBlur : function()
20442 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20444 var d = this.inputEl().getValue();
20453 this.picker().show();
20454 this.picker().select('>.datepicker-months', true).first().show();
20458 this.fireEvent('show', this, this.date);
20463 if(this.isInline) {
20466 this.picker().hide();
20467 this.fireEvent('hide', this, this.date);
20471 onMousedown: function(e)
20473 e.stopPropagation();
20474 e.preventDefault();
20479 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20483 fireKey: function(e)
20485 if (!this.picker().isVisible()){
20486 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20497 e.preventDefault();
20501 dir = e.keyCode == 37 ? -1 : 1;
20503 this.vIndex = this.vIndex + dir;
20505 if(this.vIndex < 0){
20509 if(this.vIndex > 11){
20513 if(isNaN(this.vIndex)){
20517 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20523 dir = e.keyCode == 38 ? -1 : 1;
20525 this.vIndex = this.vIndex + dir * 4;
20527 if(this.vIndex < 0){
20531 if(this.vIndex > 11){
20535 if(isNaN(this.vIndex)){
20539 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20544 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20545 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20549 e.preventDefault();
20552 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20553 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20569 this.picker().remove();
20574 Roo.apply(Roo.bootstrap.MonthField, {
20593 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20594 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20599 Roo.apply(Roo.bootstrap.MonthField, {
20603 cls: 'datepicker dropdown-menu roo-dynamic',
20607 cls: 'datepicker-months',
20611 cls: 'table-condensed',
20613 Roo.bootstrap.DateField.content
20633 * @class Roo.bootstrap.CheckBox
20634 * @extends Roo.bootstrap.Input
20635 * Bootstrap CheckBox class
20637 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20638 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20639 * @cfg {String} boxLabel The text that appears beside the checkbox
20640 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20641 * @cfg {Boolean} checked initnal the element
20642 * @cfg {Boolean} inline inline the element (default false)
20643 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20644 * @cfg {String} tooltip label tooltip
20647 * Create a new CheckBox
20648 * @param {Object} config The config object
20651 Roo.bootstrap.CheckBox = function(config){
20652 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20657 * Fires when the element is checked or unchecked.
20658 * @param {Roo.bootstrap.CheckBox} this This input
20659 * @param {Boolean} checked The new checked value
20664 * Fires when the element is click.
20665 * @param {Roo.bootstrap.CheckBox} this This input
20672 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20674 inputType: 'checkbox',
20683 getAutoCreate : function()
20685 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20691 cfg.cls = 'form-group ' + this.inputType; //input-group
20694 cfg.cls += ' ' + this.inputType + '-inline';
20700 type : this.inputType,
20701 value : this.inputValue,
20702 cls : 'roo-' + this.inputType, //'form-box',
20703 placeholder : this.placeholder || ''
20707 if(this.inputType != 'radio'){
20711 cls : 'roo-hidden-value',
20712 value : this.checked ? this.inputValue : this.valueOff
20717 if (this.weight) { // Validity check?
20718 cfg.cls += " " + this.inputType + "-" + this.weight;
20721 if (this.disabled) {
20722 input.disabled=true;
20726 input.checked = this.checked;
20731 input.name = this.name;
20733 if(this.inputType != 'radio'){
20734 hidden.name = this.name;
20735 input.name = '_hidden_' + this.name;
20740 input.cls += ' input-' + this.size;
20745 ['xs','sm','md','lg'].map(function(size){
20746 if (settings[size]) {
20747 cfg.cls += ' col-' + size + '-' + settings[size];
20751 var inputblock = input;
20753 if (this.before || this.after) {
20756 cls : 'input-group',
20761 inputblock.cn.push({
20763 cls : 'input-group-addon',
20768 inputblock.cn.push(input);
20770 if(this.inputType != 'radio'){
20771 inputblock.cn.push(hidden);
20775 inputblock.cn.push({
20777 cls : 'input-group-addon',
20784 if (align ==='left' && this.fieldLabel.length) {
20785 // Roo.log("left and has label");
20790 cls : 'control-label',
20791 html : this.fieldLabel
20801 if(this.labelWidth > 12){
20802 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20805 if(this.labelWidth < 13 && this.labelmd == 0){
20806 this.labelmd = this.labelWidth;
20809 if(this.labellg > 0){
20810 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20811 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20814 if(this.labelmd > 0){
20815 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20816 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20819 if(this.labelsm > 0){
20820 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20821 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20824 if(this.labelxs > 0){
20825 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20826 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20829 } else if ( this.fieldLabel.length) {
20830 // Roo.log(" label");
20834 tag: this.boxLabel ? 'span' : 'label',
20836 cls: 'control-label box-input-label',
20837 //cls : 'input-group-addon',
20838 html : this.fieldLabel
20847 // Roo.log(" no label && no align");
20848 cfg.cn = [ inputblock ] ;
20854 var boxLabelCfg = {
20856 //'for': id, // box label is handled by onclick - so no for...
20858 html: this.boxLabel
20862 boxLabelCfg.tooltip = this.tooltip;
20865 cfg.cn.push(boxLabelCfg);
20868 if(this.inputType != 'radio'){
20869 cfg.cn.push(hidden);
20877 * return the real input element.
20879 inputEl: function ()
20881 return this.el.select('input.roo-' + this.inputType,true).first();
20883 hiddenEl: function ()
20885 return this.el.select('input.roo-hidden-value',true).first();
20888 labelEl: function()
20890 return this.el.select('label.control-label',true).first();
20892 /* depricated... */
20896 return this.labelEl();
20899 boxLabelEl: function()
20901 return this.el.select('label.box-label',true).first();
20904 initEvents : function()
20906 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20908 this.inputEl().on('click', this.onClick, this);
20910 if (this.boxLabel) {
20911 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20914 this.startValue = this.getValue();
20917 Roo.bootstrap.CheckBox.register(this);
20921 onClick : function(e)
20923 if(this.fireEvent('click', this, e) !== false){
20924 this.setChecked(!this.checked);
20929 setChecked : function(state,suppressEvent)
20931 this.startValue = this.getValue();
20933 if(this.inputType == 'radio'){
20935 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20936 e.dom.checked = false;
20939 this.inputEl().dom.checked = true;
20941 this.inputEl().dom.value = this.inputValue;
20943 if(suppressEvent !== true){
20944 this.fireEvent('check', this, true);
20952 this.checked = state;
20954 this.inputEl().dom.checked = state;
20957 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20959 if(suppressEvent !== true){
20960 this.fireEvent('check', this, state);
20966 getValue : function()
20968 if(this.inputType == 'radio'){
20969 return this.getGroupValue();
20972 return this.hiddenEl().dom.value;
20976 getGroupValue : function()
20978 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20982 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20985 setValue : function(v,suppressEvent)
20987 if(this.inputType == 'radio'){
20988 this.setGroupValue(v, suppressEvent);
20992 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20997 setGroupValue : function(v, suppressEvent)
20999 this.startValue = this.getValue();
21001 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21002 e.dom.checked = false;
21004 if(e.dom.value == v){
21005 e.dom.checked = true;
21009 if(suppressEvent !== true){
21010 this.fireEvent('check', this, true);
21018 validate : function()
21020 if(this.getVisibilityEl().hasClass('hidden')){
21026 (this.inputType == 'radio' && this.validateRadio()) ||
21027 (this.inputType == 'checkbox' && this.validateCheckbox())
21033 this.markInvalid();
21037 validateRadio : function()
21039 if(this.getVisibilityEl().hasClass('hidden')){
21043 if(this.allowBlank){
21049 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21050 if(!e.dom.checked){
21062 validateCheckbox : function()
21065 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21066 //return (this.getValue() == this.inputValue) ? true : false;
21069 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21077 for(var i in group){
21078 if(group[i].el.isVisible(true)){
21086 for(var i in group){
21091 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21098 * Mark this field as valid
21100 markValid : function()
21104 this.fireEvent('valid', this);
21106 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21109 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21116 if(this.inputType == 'radio'){
21117 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21118 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21119 e.findParent('.form-group', false, true).addClass(_this.validClass);
21126 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21127 this.el.findParent('.form-group', false, true).addClass(this.validClass);
21131 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21137 for(var i in group){
21138 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21139 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21144 * Mark this field as invalid
21145 * @param {String} msg The validation message
21147 markInvalid : function(msg)
21149 if(this.allowBlank){
21155 this.fireEvent('invalid', this, msg);
21157 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21160 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21164 label.markInvalid();
21167 if(this.inputType == 'radio'){
21168 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21169 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21170 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21177 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21178 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21182 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21188 for(var i in group){
21189 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21190 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21195 clearInvalid : function()
21197 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21199 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21201 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21203 if (label && label.iconEl) {
21204 label.iconEl.removeClass(label.validClass);
21205 label.iconEl.removeClass(label.invalidClass);
21209 disable : function()
21211 if(this.inputType != 'radio'){
21212 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21219 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21220 _this.getActionEl().addClass(this.disabledClass);
21221 e.dom.disabled = true;
21225 this.disabled = true;
21226 this.fireEvent("disable", this);
21230 enable : function()
21232 if(this.inputType != 'radio'){
21233 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21240 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21241 _this.getActionEl().removeClass(this.disabledClass);
21242 e.dom.disabled = false;
21246 this.disabled = false;
21247 this.fireEvent("enable", this);
21251 setBoxLabel : function(v)
21256 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21262 Roo.apply(Roo.bootstrap.CheckBox, {
21267 * register a CheckBox Group
21268 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21270 register : function(checkbox)
21272 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21273 this.groups[checkbox.groupId] = {};
21276 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21280 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21284 * fetch a CheckBox Group based on the group ID
21285 * @param {string} the group ID
21286 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21288 get: function(groupId) {
21289 if (typeof(this.groups[groupId]) == 'undefined') {
21293 return this.groups[groupId] ;
21306 * @class Roo.bootstrap.Radio
21307 * @extends Roo.bootstrap.Component
21308 * Bootstrap Radio class
21309 * @cfg {String} boxLabel - the label associated
21310 * @cfg {String} value - the value of radio
21313 * Create a new Radio
21314 * @param {Object} config The config object
21316 Roo.bootstrap.Radio = function(config){
21317 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21321 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21327 getAutoCreate : function()
21331 cls : 'form-group radio',
21336 html : this.boxLabel
21344 initEvents : function()
21346 this.parent().register(this);
21348 this.el.on('click', this.onClick, this);
21352 onClick : function(e)
21354 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21355 this.setChecked(true);
21359 setChecked : function(state, suppressEvent)
21361 this.parent().setValue(this.value, suppressEvent);
21365 setBoxLabel : function(v)
21370 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21385 * @class Roo.bootstrap.SecurePass
21386 * @extends Roo.bootstrap.Input
21387 * Bootstrap SecurePass class
21391 * Create a new SecurePass
21392 * @param {Object} config The config object
21395 Roo.bootstrap.SecurePass = function (config) {
21396 // these go here, so the translation tool can replace them..
21398 PwdEmpty: "Please type a password, and then retype it to confirm.",
21399 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21400 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21401 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21402 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21403 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21404 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21405 TooWeak: "Your password is Too Weak."
21407 this.meterLabel = "Password strength:";
21408 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21409 this.meterClass = [
21410 "roo-password-meter-tooweak",
21411 "roo-password-meter-weak",
21412 "roo-password-meter-medium",
21413 "roo-password-meter-strong",
21414 "roo-password-meter-grey"
21419 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21422 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21424 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
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."
21442 * @cfg {String/Object} Label for the strength meter (defaults to
21443 * 'Password strength:')
21448 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21449 * ['Weak', 'Medium', 'Strong'])
21452 pwdStrengths: false,
21465 initEvents: function ()
21467 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21469 if (this.el.is('input[type=password]') && Roo.isSafari) {
21470 this.el.on('keydown', this.SafariOnKeyDown, this);
21473 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21476 onRender: function (ct, position)
21478 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21479 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21480 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21482 this.trigger.createChild({
21487 cls: 'roo-password-meter-grey col-xs-12',
21490 //width: this.meterWidth + 'px'
21494 cls: 'roo-password-meter-text'
21500 if (this.hideTrigger) {
21501 this.trigger.setDisplayed(false);
21503 this.setSize(this.width || '', this.height || '');
21506 onDestroy: function ()
21508 if (this.trigger) {
21509 this.trigger.removeAllListeners();
21510 this.trigger.remove();
21513 this.wrap.remove();
21515 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21518 checkStrength: function ()
21520 var pwd = this.inputEl().getValue();
21521 if (pwd == this._lastPwd) {
21526 if (this.ClientSideStrongPassword(pwd)) {
21528 } else if (this.ClientSideMediumPassword(pwd)) {
21530 } else if (this.ClientSideWeakPassword(pwd)) {
21536 Roo.log('strength1: ' + strength);
21538 //var pm = this.trigger.child('div/div/div').dom;
21539 var pm = this.trigger.child('div/div');
21540 pm.removeClass(this.meterClass);
21541 pm.addClass(this.meterClass[strength]);
21544 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21546 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21548 this._lastPwd = pwd;
21552 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21554 this._lastPwd = '';
21556 var pm = this.trigger.child('div/div');
21557 pm.removeClass(this.meterClass);
21558 pm.addClass('roo-password-meter-grey');
21561 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21564 this.inputEl().dom.type='password';
21567 validateValue: function (value)
21570 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21573 if (value.length == 0) {
21574 if (this.allowBlank) {
21575 this.clearInvalid();
21579 this.markInvalid(this.errors.PwdEmpty);
21580 this.errorMsg = this.errors.PwdEmpty;
21588 if ('[\x21-\x7e]*'.match(value)) {
21589 this.markInvalid(this.errors.PwdBadChar);
21590 this.errorMsg = this.errors.PwdBadChar;
21593 if (value.length < 6) {
21594 this.markInvalid(this.errors.PwdShort);
21595 this.errorMsg = this.errors.PwdShort;
21598 if (value.length > 16) {
21599 this.markInvalid(this.errors.PwdLong);
21600 this.errorMsg = this.errors.PwdLong;
21604 if (this.ClientSideStrongPassword(value)) {
21606 } else if (this.ClientSideMediumPassword(value)) {
21608 } else if (this.ClientSideWeakPassword(value)) {
21615 if (strength < 2) {
21616 //this.markInvalid(this.errors.TooWeak);
21617 this.errorMsg = this.errors.TooWeak;
21622 console.log('strength2: ' + strength);
21624 //var pm = this.trigger.child('div/div/div').dom;
21626 var pm = this.trigger.child('div/div');
21627 pm.removeClass(this.meterClass);
21628 pm.addClass(this.meterClass[strength]);
21630 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21632 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21634 this.errorMsg = '';
21638 CharacterSetChecks: function (type)
21641 this.fResult = false;
21644 isctype: function (character, type)
21647 case this.kCapitalLetter:
21648 if (character >= 'A' && character <= 'Z') {
21653 case this.kSmallLetter:
21654 if (character >= 'a' && character <= 'z') {
21660 if (character >= '0' && character <= '9') {
21665 case this.kPunctuation:
21666 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21677 IsLongEnough: function (pwd, size)
21679 return !(pwd == null || isNaN(size) || pwd.length < size);
21682 SpansEnoughCharacterSets: function (word, nb)
21684 if (!this.IsLongEnough(word, nb))
21689 var characterSetChecks = new Array(
21690 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21691 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21694 for (var index = 0; index < word.length; ++index) {
21695 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21696 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21697 characterSetChecks[nCharSet].fResult = true;
21704 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21705 if (characterSetChecks[nCharSet].fResult) {
21710 if (nCharSets < nb) {
21716 ClientSideStrongPassword: function (pwd)
21718 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21721 ClientSideMediumPassword: function (pwd)
21723 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21726 ClientSideWeakPassword: function (pwd)
21728 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21731 })//<script type="text/javascript">
21734 * Based Ext JS Library 1.1.1
21735 * Copyright(c) 2006-2007, Ext JS, LLC.
21741 * @class Roo.HtmlEditorCore
21742 * @extends Roo.Component
21743 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21745 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21748 Roo.HtmlEditorCore = function(config){
21751 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21756 * @event initialize
21757 * Fires when the editor is fully initialized (including the iframe)
21758 * @param {Roo.HtmlEditorCore} this
21763 * Fires when the editor is first receives the focus. Any insertion must wait
21764 * until after this event.
21765 * @param {Roo.HtmlEditorCore} this
21769 * @event beforesync
21770 * Fires before the textarea is updated with content from the editor iframe. Return false
21771 * to cancel the sync.
21772 * @param {Roo.HtmlEditorCore} this
21773 * @param {String} html
21777 * @event beforepush
21778 * Fires before the iframe editor is updated with content from the textarea. Return false
21779 * to cancel the push.
21780 * @param {Roo.HtmlEditorCore} this
21781 * @param {String} html
21786 * Fires when the textarea is updated with content from the editor iframe.
21787 * @param {Roo.HtmlEditorCore} this
21788 * @param {String} html
21793 * Fires when the iframe editor is updated with content from the textarea.
21794 * @param {Roo.HtmlEditorCore} this
21795 * @param {String} html
21800 * @event editorevent
21801 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21802 * @param {Roo.HtmlEditorCore} this
21808 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21810 // defaults : white / black...
21811 this.applyBlacklists();
21818 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21822 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21828 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21833 * @cfg {Number} height (in pixels)
21837 * @cfg {Number} width (in pixels)
21842 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21845 stylesheets: false,
21850 // private properties
21851 validationEvent : false,
21853 initialized : false,
21855 sourceEditMode : false,
21856 onFocus : Roo.emptyFn,
21858 hideMode:'offsets',
21862 // blacklist + whitelisted elements..
21869 * Protected method that will not generally be called directly. It
21870 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21871 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21873 getDocMarkup : function(){
21877 // inherit styels from page...??
21878 if (this.stylesheets === false) {
21880 Roo.get(document.head).select('style').each(function(node) {
21881 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21884 Roo.get(document.head).select('link').each(function(node) {
21885 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21888 } else if (!this.stylesheets.length) {
21890 st = '<style type="text/css">' +
21891 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21894 st = '<style type="text/css">' +
21899 st += '<style type="text/css">' +
21900 'IMG { cursor: pointer } ' +
21903 var cls = 'roo-htmleditor-body';
21905 if(this.bodyCls.length){
21906 cls += ' ' + this.bodyCls;
21909 return '<html><head>' + st +
21910 //<style type="text/css">' +
21911 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21913 ' </head><body class="' + cls + '"></body></html>';
21917 onRender : function(ct, position)
21920 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21921 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21924 this.el.dom.style.border = '0 none';
21925 this.el.dom.setAttribute('tabIndex', -1);
21926 this.el.addClass('x-hidden hide');
21930 if(Roo.isIE){ // fix IE 1px bogus margin
21931 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21935 this.frameId = Roo.id();
21939 var iframe = this.owner.wrap.createChild({
21941 cls: 'form-control', // bootstrap..
21943 name: this.frameId,
21944 frameBorder : 'no',
21945 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21950 this.iframe = iframe.dom;
21952 this.assignDocWin();
21954 this.doc.designMode = 'on';
21957 this.doc.write(this.getDocMarkup());
21961 var task = { // must defer to wait for browser to be ready
21963 //console.log("run task?" + this.doc.readyState);
21964 this.assignDocWin();
21965 if(this.doc.body || this.doc.readyState == 'complete'){
21967 this.doc.designMode="on";
21971 Roo.TaskMgr.stop(task);
21972 this.initEditor.defer(10, this);
21979 Roo.TaskMgr.start(task);
21984 onResize : function(w, h)
21986 Roo.log('resize: ' +w + ',' + h );
21987 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21991 if(typeof w == 'number'){
21993 this.iframe.style.width = w + 'px';
21995 if(typeof h == 'number'){
21997 this.iframe.style.height = h + 'px';
21999 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22006 * Toggles the editor between standard and source edit mode.
22007 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22009 toggleSourceEdit : function(sourceEditMode){
22011 this.sourceEditMode = sourceEditMode === true;
22013 if(this.sourceEditMode){
22015 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
22018 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22019 //this.iframe.className = '';
22022 //this.setSize(this.owner.wrap.getSize());
22023 //this.fireEvent('editmodechange', this, this.sourceEditMode);
22030 * Protected method that will not generally be called directly. If you need/want
22031 * custom HTML cleanup, this is the method you should override.
22032 * @param {String} html The HTML to be cleaned
22033 * return {String} The cleaned HTML
22035 cleanHtml : function(html){
22036 html = String(html);
22037 if(html.length > 5){
22038 if(Roo.isSafari){ // strip safari nonsense
22039 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22042 if(html == ' '){
22049 * HTML Editor -> Textarea
22050 * Protected method that will not generally be called directly. Syncs the contents
22051 * of the editor iframe with the textarea.
22053 syncValue : function(){
22054 if(this.initialized){
22055 var bd = (this.doc.body || this.doc.documentElement);
22056 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22057 var html = bd.innerHTML;
22059 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22060 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22062 html = '<div style="'+m[0]+'">' + html + '</div>';
22065 html = this.cleanHtml(html);
22066 // fix up the special chars.. normaly like back quotes in word...
22067 // however we do not want to do this with chinese..
22068 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22069 var cc = b.charCodeAt();
22071 (cc >= 0x4E00 && cc < 0xA000 ) ||
22072 (cc >= 0x3400 && cc < 0x4E00 ) ||
22073 (cc >= 0xf900 && cc < 0xfb00 )
22079 if(this.owner.fireEvent('beforesync', this, html) !== false){
22080 this.el.dom.value = html;
22081 this.owner.fireEvent('sync', this, html);
22087 * Protected method that will not generally be called directly. Pushes the value of the textarea
22088 * into the iframe editor.
22090 pushValue : function(){
22091 if(this.initialized){
22092 var v = this.el.dom.value.trim();
22094 // if(v.length < 1){
22098 if(this.owner.fireEvent('beforepush', this, v) !== false){
22099 var d = (this.doc.body || this.doc.documentElement);
22101 this.cleanUpPaste();
22102 this.el.dom.value = d.innerHTML;
22103 this.owner.fireEvent('push', this, v);
22109 deferFocus : function(){
22110 this.focus.defer(10, this);
22114 focus : function(){
22115 if(this.win && !this.sourceEditMode){
22122 assignDocWin: function()
22124 var iframe = this.iframe;
22127 this.doc = iframe.contentWindow.document;
22128 this.win = iframe.contentWindow;
22130 // if (!Roo.get(this.frameId)) {
22133 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22134 // this.win = Roo.get(this.frameId).dom.contentWindow;
22136 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22140 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22141 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22146 initEditor : function(){
22147 //console.log("INIT EDITOR");
22148 this.assignDocWin();
22152 this.doc.designMode="on";
22154 this.doc.write(this.getDocMarkup());
22157 var dbody = (this.doc.body || this.doc.documentElement);
22158 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22159 // this copies styles from the containing element into thsi one..
22160 // not sure why we need all of this..
22161 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22163 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22164 //ss['background-attachment'] = 'fixed'; // w3c
22165 dbody.bgProperties = 'fixed'; // ie
22166 //Roo.DomHelper.applyStyles(dbody, ss);
22167 Roo.EventManager.on(this.doc, {
22168 //'mousedown': this.onEditorEvent,
22169 'mouseup': this.onEditorEvent,
22170 'dblclick': this.onEditorEvent,
22171 'click': this.onEditorEvent,
22172 'keyup': this.onEditorEvent,
22177 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22179 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22180 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22182 this.initialized = true;
22184 this.owner.fireEvent('initialize', this);
22189 onDestroy : function(){
22195 //for (var i =0; i < this.toolbars.length;i++) {
22196 // // fixme - ask toolbars for heights?
22197 // this.toolbars[i].onDestroy();
22200 //this.wrap.dom.innerHTML = '';
22201 //this.wrap.remove();
22206 onFirstFocus : function(){
22208 this.assignDocWin();
22211 this.activated = true;
22214 if(Roo.isGecko){ // prevent silly gecko errors
22216 var s = this.win.getSelection();
22217 if(!s.focusNode || s.focusNode.nodeType != 3){
22218 var r = s.getRangeAt(0);
22219 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22224 this.execCmd('useCSS', true);
22225 this.execCmd('styleWithCSS', false);
22228 this.owner.fireEvent('activate', this);
22232 adjustFont: function(btn){
22233 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22234 //if(Roo.isSafari){ // safari
22237 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22238 if(Roo.isSafari){ // safari
22239 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22240 v = (v < 10) ? 10 : v;
22241 v = (v > 48) ? 48 : v;
22242 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22247 v = Math.max(1, v+adjust);
22249 this.execCmd('FontSize', v );
22252 onEditorEvent : function(e)
22254 this.owner.fireEvent('editorevent', this, e);
22255 // this.updateToolbar();
22256 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22259 insertTag : function(tg)
22261 // could be a bit smarter... -> wrap the current selected tRoo..
22262 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22264 range = this.createRange(this.getSelection());
22265 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22266 wrappingNode.appendChild(range.extractContents());
22267 range.insertNode(wrappingNode);
22274 this.execCmd("formatblock", tg);
22278 insertText : function(txt)
22282 var range = this.createRange();
22283 range.deleteContents();
22284 //alert(Sender.getAttribute('label'));
22286 range.insertNode(this.doc.createTextNode(txt));
22292 * Executes a Midas editor command on the editor document and performs necessary focus and
22293 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22294 * @param {String} cmd The Midas command
22295 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22297 relayCmd : function(cmd, value){
22299 this.execCmd(cmd, value);
22300 this.owner.fireEvent('editorevent', this);
22301 //this.updateToolbar();
22302 this.owner.deferFocus();
22306 * Executes a Midas editor command directly on the editor document.
22307 * For visual commands, you should use {@link #relayCmd} instead.
22308 * <b>This should only be called after the editor is initialized.</b>
22309 * @param {String} cmd The Midas command
22310 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22312 execCmd : function(cmd, value){
22313 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22320 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22322 * @param {String} text | dom node..
22324 insertAtCursor : function(text)
22327 if(!this.activated){
22333 var r = this.doc.selection.createRange();
22344 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22348 // from jquery ui (MIT licenced)
22350 var win = this.win;
22352 if (win.getSelection && win.getSelection().getRangeAt) {
22353 range = win.getSelection().getRangeAt(0);
22354 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22355 range.insertNode(node);
22356 } else if (win.document.selection && win.document.selection.createRange) {
22357 // no firefox support
22358 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22359 win.document.selection.createRange().pasteHTML(txt);
22361 // no firefox support
22362 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22363 this.execCmd('InsertHTML', txt);
22372 mozKeyPress : function(e){
22374 var c = e.getCharCode(), cmd;
22377 c = String.fromCharCode(c).toLowerCase();
22391 this.cleanUpPaste.defer(100, this);
22399 e.preventDefault();
22407 fixKeys : function(){ // load time branching for fastest keydown performance
22409 return function(e){
22410 var k = e.getKey(), r;
22413 r = this.doc.selection.createRange();
22416 r.pasteHTML('    ');
22423 r = this.doc.selection.createRange();
22425 var target = r.parentElement();
22426 if(!target || target.tagName.toLowerCase() != 'li'){
22428 r.pasteHTML('<br />');
22434 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22435 this.cleanUpPaste.defer(100, this);
22441 }else if(Roo.isOpera){
22442 return function(e){
22443 var k = e.getKey();
22447 this.execCmd('InsertHTML','    ');
22450 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22451 this.cleanUpPaste.defer(100, this);
22456 }else if(Roo.isSafari){
22457 return function(e){
22458 var k = e.getKey();
22462 this.execCmd('InsertText','\t');
22466 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22467 this.cleanUpPaste.defer(100, this);
22475 getAllAncestors: function()
22477 var p = this.getSelectedNode();
22480 a.push(p); // push blank onto stack..
22481 p = this.getParentElement();
22485 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22489 a.push(this.doc.body);
22493 lastSelNode : false,
22496 getSelection : function()
22498 this.assignDocWin();
22499 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22502 getSelectedNode: function()
22504 // this may only work on Gecko!!!
22506 // should we cache this!!!!
22511 var range = this.createRange(this.getSelection()).cloneRange();
22514 var parent = range.parentElement();
22516 var testRange = range.duplicate();
22517 testRange.moveToElementText(parent);
22518 if (testRange.inRange(range)) {
22521 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22524 parent = parent.parentElement;
22529 // is ancestor a text element.
22530 var ac = range.commonAncestorContainer;
22531 if (ac.nodeType == 3) {
22532 ac = ac.parentNode;
22535 var ar = ac.childNodes;
22538 var other_nodes = [];
22539 var has_other_nodes = false;
22540 for (var i=0;i<ar.length;i++) {
22541 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22544 // fullly contained node.
22546 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22551 // probably selected..
22552 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22553 other_nodes.push(ar[i]);
22557 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22562 has_other_nodes = true;
22564 if (!nodes.length && other_nodes.length) {
22565 nodes= other_nodes;
22567 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22573 createRange: function(sel)
22575 // this has strange effects when using with
22576 // top toolbar - not sure if it's a great idea.
22577 //this.editor.contentWindow.focus();
22578 if (typeof sel != "undefined") {
22580 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22582 return this.doc.createRange();
22585 return this.doc.createRange();
22588 getParentElement: function()
22591 this.assignDocWin();
22592 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22594 var range = this.createRange(sel);
22597 var p = range.commonAncestorContainer;
22598 while (p.nodeType == 3) { // text node
22609 * Range intersection.. the hard stuff...
22613 * [ -- selected range --- ]
22617 * if end is before start or hits it. fail.
22618 * if start is after end or hits it fail.
22620 * if either hits (but other is outside. - then it's not
22626 // @see http://www.thismuchiknow.co.uk/?p=64.
22627 rangeIntersectsNode : function(range, node)
22629 var nodeRange = node.ownerDocument.createRange();
22631 nodeRange.selectNode(node);
22633 nodeRange.selectNodeContents(node);
22636 var rangeStartRange = range.cloneRange();
22637 rangeStartRange.collapse(true);
22639 var rangeEndRange = range.cloneRange();
22640 rangeEndRange.collapse(false);
22642 var nodeStartRange = nodeRange.cloneRange();
22643 nodeStartRange.collapse(true);
22645 var nodeEndRange = nodeRange.cloneRange();
22646 nodeEndRange.collapse(false);
22648 return rangeStartRange.compareBoundaryPoints(
22649 Range.START_TO_START, nodeEndRange) == -1 &&
22650 rangeEndRange.compareBoundaryPoints(
22651 Range.START_TO_START, nodeStartRange) == 1;
22655 rangeCompareNode : function(range, node)
22657 var nodeRange = node.ownerDocument.createRange();
22659 nodeRange.selectNode(node);
22661 nodeRange.selectNodeContents(node);
22665 range.collapse(true);
22667 nodeRange.collapse(true);
22669 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22670 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22672 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22674 var nodeIsBefore = ss == 1;
22675 var nodeIsAfter = ee == -1;
22677 if (nodeIsBefore && nodeIsAfter) {
22680 if (!nodeIsBefore && nodeIsAfter) {
22681 return 1; //right trailed.
22684 if (nodeIsBefore && !nodeIsAfter) {
22685 return 2; // left trailed.
22691 // private? - in a new class?
22692 cleanUpPaste : function()
22694 // cleans up the whole document..
22695 Roo.log('cleanuppaste');
22697 this.cleanUpChildren(this.doc.body);
22698 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22699 if (clean != this.doc.body.innerHTML) {
22700 this.doc.body.innerHTML = clean;
22705 cleanWordChars : function(input) {// change the chars to hex code
22706 var he = Roo.HtmlEditorCore;
22708 var output = input;
22709 Roo.each(he.swapCodes, function(sw) {
22710 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22712 output = output.replace(swapper, sw[1]);
22719 cleanUpChildren : function (n)
22721 if (!n.childNodes.length) {
22724 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22725 this.cleanUpChild(n.childNodes[i]);
22732 cleanUpChild : function (node)
22735 //console.log(node);
22736 if (node.nodeName == "#text") {
22737 // clean up silly Windows -- stuff?
22740 if (node.nodeName == "#comment") {
22741 node.parentNode.removeChild(node);
22742 // clean up silly Windows -- stuff?
22745 var lcname = node.tagName.toLowerCase();
22746 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22747 // whitelist of tags..
22749 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22751 node.parentNode.removeChild(node);
22756 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22758 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22759 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22761 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22762 // remove_keep_children = true;
22765 if (remove_keep_children) {
22766 this.cleanUpChildren(node);
22767 // inserts everything just before this node...
22768 while (node.childNodes.length) {
22769 var cn = node.childNodes[0];
22770 node.removeChild(cn);
22771 node.parentNode.insertBefore(cn, node);
22773 node.parentNode.removeChild(node);
22777 if (!node.attributes || !node.attributes.length) {
22778 this.cleanUpChildren(node);
22782 function cleanAttr(n,v)
22785 if (v.match(/^\./) || v.match(/^\//)) {
22788 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22791 if (v.match(/^#/)) {
22794 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22795 node.removeAttribute(n);
22799 var cwhite = this.cwhite;
22800 var cblack = this.cblack;
22802 function cleanStyle(n,v)
22804 if (v.match(/expression/)) { //XSS?? should we even bother..
22805 node.removeAttribute(n);
22809 var parts = v.split(/;/);
22812 Roo.each(parts, function(p) {
22813 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22817 var l = p.split(':').shift().replace(/\s+/g,'');
22818 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22820 if ( cwhite.length && cblack.indexOf(l) > -1) {
22821 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22822 //node.removeAttribute(n);
22826 // only allow 'c whitelisted system attributes'
22827 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22828 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22829 //node.removeAttribute(n);
22839 if (clean.length) {
22840 node.setAttribute(n, clean.join(';'));
22842 node.removeAttribute(n);
22848 for (var i = node.attributes.length-1; i > -1 ; i--) {
22849 var a = node.attributes[i];
22852 if (a.name.toLowerCase().substr(0,2)=='on') {
22853 node.removeAttribute(a.name);
22856 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22857 node.removeAttribute(a.name);
22860 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22861 cleanAttr(a.name,a.value); // fixme..
22864 if (a.name == 'style') {
22865 cleanStyle(a.name,a.value);
22868 /// clean up MS crap..
22869 // tecnically this should be a list of valid class'es..
22872 if (a.name == 'class') {
22873 if (a.value.match(/^Mso/)) {
22874 node.className = '';
22877 if (a.value.match(/^body$/)) {
22878 node.className = '';
22889 this.cleanUpChildren(node);
22895 * Clean up MS wordisms...
22897 cleanWord : function(node)
22902 this.cleanWord(this.doc.body);
22905 if (node.nodeName == "#text") {
22906 // clean up silly Windows -- stuff?
22909 if (node.nodeName == "#comment") {
22910 node.parentNode.removeChild(node);
22911 // clean up silly Windows -- stuff?
22915 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22916 node.parentNode.removeChild(node);
22920 // remove - but keep children..
22921 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22922 while (node.childNodes.length) {
22923 var cn = node.childNodes[0];
22924 node.removeChild(cn);
22925 node.parentNode.insertBefore(cn, node);
22927 node.parentNode.removeChild(node);
22928 this.iterateChildren(node, this.cleanWord);
22932 if (node.className.length) {
22934 var cn = node.className.split(/\W+/);
22936 Roo.each(cn, function(cls) {
22937 if (cls.match(/Mso[a-zA-Z]+/)) {
22942 node.className = cna.length ? cna.join(' ') : '';
22944 node.removeAttribute("class");
22948 if (node.hasAttribute("lang")) {
22949 node.removeAttribute("lang");
22952 if (node.hasAttribute("style")) {
22954 var styles = node.getAttribute("style").split(";");
22956 Roo.each(styles, function(s) {
22957 if (!s.match(/:/)) {
22960 var kv = s.split(":");
22961 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22964 // what ever is left... we allow.
22967 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22968 if (!nstyle.length) {
22969 node.removeAttribute('style');
22972 this.iterateChildren(node, this.cleanWord);
22978 * iterateChildren of a Node, calling fn each time, using this as the scole..
22979 * @param {DomNode} node node to iterate children of.
22980 * @param {Function} fn method of this class to call on each item.
22982 iterateChildren : function(node, fn)
22984 if (!node.childNodes.length) {
22987 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22988 fn.call(this, node.childNodes[i])
22994 * cleanTableWidths.
22996 * Quite often pasting from word etc.. results in tables with column and widths.
22997 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23000 cleanTableWidths : function(node)
23005 this.cleanTableWidths(this.doc.body);
23010 if (node.nodeName == "#text" || node.nodeName == "#comment") {
23013 Roo.log(node.tagName);
23014 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23015 this.iterateChildren(node, this.cleanTableWidths);
23018 if (node.hasAttribute('width')) {
23019 node.removeAttribute('width');
23023 if (node.hasAttribute("style")) {
23026 var styles = node.getAttribute("style").split(";");
23028 Roo.each(styles, function(s) {
23029 if (!s.match(/:/)) {
23032 var kv = s.split(":");
23033 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23036 // what ever is left... we allow.
23039 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23040 if (!nstyle.length) {
23041 node.removeAttribute('style');
23045 this.iterateChildren(node, this.cleanTableWidths);
23053 domToHTML : function(currentElement, depth, nopadtext) {
23055 depth = depth || 0;
23056 nopadtext = nopadtext || false;
23058 if (!currentElement) {
23059 return this.domToHTML(this.doc.body);
23062 //Roo.log(currentElement);
23064 var allText = false;
23065 var nodeName = currentElement.nodeName;
23066 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23068 if (nodeName == '#text') {
23070 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23075 if (nodeName != 'BODY') {
23078 // Prints the node tagName, such as <A>, <IMG>, etc
23081 for(i = 0; i < currentElement.attributes.length;i++) {
23083 var aname = currentElement.attributes.item(i).name;
23084 if (!currentElement.attributes.item(i).value.length) {
23087 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23090 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23099 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23102 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23107 // Traverse the tree
23109 var currentElementChild = currentElement.childNodes.item(i);
23110 var allText = true;
23111 var innerHTML = '';
23113 while (currentElementChild) {
23114 // Formatting code (indent the tree so it looks nice on the screen)
23115 var nopad = nopadtext;
23116 if (lastnode == 'SPAN') {
23120 if (currentElementChild.nodeName == '#text') {
23121 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23122 toadd = nopadtext ? toadd : toadd.trim();
23123 if (!nopad && toadd.length > 80) {
23124 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23126 innerHTML += toadd;
23129 currentElementChild = currentElement.childNodes.item(i);
23135 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23137 // Recursively traverse the tree structure of the child node
23138 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23139 lastnode = currentElementChild.nodeName;
23141 currentElementChild=currentElement.childNodes.item(i);
23147 // The remaining code is mostly for formatting the tree
23148 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23153 ret+= "</"+tagName+">";
23159 applyBlacklists : function()
23161 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23162 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23166 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23167 if (b.indexOf(tag) > -1) {
23170 this.white.push(tag);
23174 Roo.each(w, function(tag) {
23175 if (b.indexOf(tag) > -1) {
23178 if (this.white.indexOf(tag) > -1) {
23181 this.white.push(tag);
23186 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23187 if (w.indexOf(tag) > -1) {
23190 this.black.push(tag);
23194 Roo.each(b, function(tag) {
23195 if (w.indexOf(tag) > -1) {
23198 if (this.black.indexOf(tag) > -1) {
23201 this.black.push(tag);
23206 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23207 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23211 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23212 if (b.indexOf(tag) > -1) {
23215 this.cwhite.push(tag);
23219 Roo.each(w, function(tag) {
23220 if (b.indexOf(tag) > -1) {
23223 if (this.cwhite.indexOf(tag) > -1) {
23226 this.cwhite.push(tag);
23231 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23232 if (w.indexOf(tag) > -1) {
23235 this.cblack.push(tag);
23239 Roo.each(b, function(tag) {
23240 if (w.indexOf(tag) > -1) {
23243 if (this.cblack.indexOf(tag) > -1) {
23246 this.cblack.push(tag);
23251 setStylesheets : function(stylesheets)
23253 if(typeof(stylesheets) == 'string'){
23254 Roo.get(this.iframe.contentDocument.head).createChild({
23256 rel : 'stylesheet',
23265 Roo.each(stylesheets, function(s) {
23270 Roo.get(_this.iframe.contentDocument.head).createChild({
23272 rel : 'stylesheet',
23281 removeStylesheets : function()
23285 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23290 setStyle : function(style)
23292 Roo.get(this.iframe.contentDocument.head).createChild({
23301 // hide stuff that is not compatible
23315 * @event specialkey
23319 * @cfg {String} fieldClass @hide
23322 * @cfg {String} focusClass @hide
23325 * @cfg {String} autoCreate @hide
23328 * @cfg {String} inputType @hide
23331 * @cfg {String} invalidClass @hide
23334 * @cfg {String} invalidText @hide
23337 * @cfg {String} msgFx @hide
23340 * @cfg {String} validateOnBlur @hide
23344 Roo.HtmlEditorCore.white = [
23345 'area', 'br', 'img', 'input', 'hr', 'wbr',
23347 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23348 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23349 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23350 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23351 'table', 'ul', 'xmp',
23353 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23356 'dir', 'menu', 'ol', 'ul', 'dl',
23362 Roo.HtmlEditorCore.black = [
23363 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23365 'base', 'basefont', 'bgsound', 'blink', 'body',
23366 'frame', 'frameset', 'head', 'html', 'ilayer',
23367 'iframe', 'layer', 'link', 'meta', 'object',
23368 'script', 'style' ,'title', 'xml' // clean later..
23370 Roo.HtmlEditorCore.clean = [
23371 'script', 'style', 'title', 'xml'
23373 Roo.HtmlEditorCore.remove = [
23378 Roo.HtmlEditorCore.ablack = [
23382 Roo.HtmlEditorCore.aclean = [
23383 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23387 Roo.HtmlEditorCore.pwhite= [
23388 'http', 'https', 'mailto'
23391 // white listed style attributes.
23392 Roo.HtmlEditorCore.cwhite= [
23393 // 'text-align', /// default is to allow most things..
23399 // black listed style attributes.
23400 Roo.HtmlEditorCore.cblack= [
23401 // 'font-size' -- this can be set by the project
23405 Roo.HtmlEditorCore.swapCodes =[
23424 * @class Roo.bootstrap.HtmlEditor
23425 * @extends Roo.bootstrap.TextArea
23426 * Bootstrap HtmlEditor class
23429 * Create a new HtmlEditor
23430 * @param {Object} config The config object
23433 Roo.bootstrap.HtmlEditor = function(config){
23434 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23435 if (!this.toolbars) {
23436 this.toolbars = [];
23439 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23442 * @event initialize
23443 * Fires when the editor is fully initialized (including the iframe)
23444 * @param {HtmlEditor} this
23449 * Fires when the editor is first receives the focus. Any insertion must wait
23450 * until after this event.
23451 * @param {HtmlEditor} this
23455 * @event beforesync
23456 * Fires before the textarea is updated with content from the editor iframe. Return false
23457 * to cancel the sync.
23458 * @param {HtmlEditor} this
23459 * @param {String} html
23463 * @event beforepush
23464 * Fires before the iframe editor is updated with content from the textarea. Return false
23465 * to cancel the push.
23466 * @param {HtmlEditor} this
23467 * @param {String} html
23472 * Fires when the textarea is updated with content from the editor iframe.
23473 * @param {HtmlEditor} this
23474 * @param {String} html
23479 * Fires when the iframe editor is updated with content from the textarea.
23480 * @param {HtmlEditor} this
23481 * @param {String} html
23485 * @event editmodechange
23486 * Fires when the editor switches edit modes
23487 * @param {HtmlEditor} this
23488 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23490 editmodechange: true,
23492 * @event editorevent
23493 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23494 * @param {HtmlEditor} this
23498 * @event firstfocus
23499 * Fires when on first focus - needed by toolbars..
23500 * @param {HtmlEditor} this
23505 * Auto save the htmlEditor value as a file into Events
23506 * @param {HtmlEditor} this
23510 * @event savedpreview
23511 * preview the saved version of htmlEditor
23512 * @param {HtmlEditor} this
23519 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23523 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23528 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23533 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23538 * @cfg {Number} height (in pixels)
23542 * @cfg {Number} width (in pixels)
23547 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23550 stylesheets: false,
23555 // private properties
23556 validationEvent : false,
23558 initialized : false,
23561 onFocus : Roo.emptyFn,
23563 hideMode:'offsets',
23565 tbContainer : false,
23569 toolbarContainer :function() {
23570 return this.wrap.select('.x-html-editor-tb',true).first();
23574 * Protected method that will not generally be called directly. It
23575 * is called when the editor creates its toolbar. Override this method if you need to
23576 * add custom toolbar buttons.
23577 * @param {HtmlEditor} editor
23579 createToolbar : function(){
23580 Roo.log('renewing');
23581 Roo.log("create toolbars");
23583 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23584 this.toolbars[0].render(this.toolbarContainer());
23588 // if (!editor.toolbars || !editor.toolbars.length) {
23589 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23592 // for (var i =0 ; i < editor.toolbars.length;i++) {
23593 // editor.toolbars[i] = Roo.factory(
23594 // typeof(editor.toolbars[i]) == 'string' ?
23595 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23596 // Roo.bootstrap.HtmlEditor);
23597 // editor.toolbars[i].init(editor);
23603 onRender : function(ct, position)
23605 // Roo.log("Call onRender: " + this.xtype);
23607 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23609 this.wrap = this.inputEl().wrap({
23610 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23613 this.editorcore.onRender(ct, position);
23615 if (this.resizable) {
23616 this.resizeEl = new Roo.Resizable(this.wrap, {
23620 minHeight : this.height,
23621 height: this.height,
23622 handles : this.resizable,
23625 resize : function(r, w, h) {
23626 _t.onResize(w,h); // -something
23632 this.createToolbar(this);
23635 if(!this.width && this.resizable){
23636 this.setSize(this.wrap.getSize());
23638 if (this.resizeEl) {
23639 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23640 // should trigger onReize..
23646 onResize : function(w, h)
23648 Roo.log('resize: ' +w + ',' + h );
23649 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23653 if(this.inputEl() ){
23654 if(typeof w == 'number'){
23655 var aw = w - this.wrap.getFrameWidth('lr');
23656 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23659 if(typeof h == 'number'){
23660 var tbh = -11; // fixme it needs to tool bar size!
23661 for (var i =0; i < this.toolbars.length;i++) {
23662 // fixme - ask toolbars for heights?
23663 tbh += this.toolbars[i].el.getHeight();
23664 //if (this.toolbars[i].footer) {
23665 // tbh += this.toolbars[i].footer.el.getHeight();
23673 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23674 ah -= 5; // knock a few pixes off for look..
23675 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23679 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23680 this.editorcore.onResize(ew,eh);
23685 * Toggles the editor between standard and source edit mode.
23686 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23688 toggleSourceEdit : function(sourceEditMode)
23690 this.editorcore.toggleSourceEdit(sourceEditMode);
23692 if(this.editorcore.sourceEditMode){
23693 Roo.log('editor - showing textarea');
23696 // Roo.log(this.syncValue());
23698 this.inputEl().removeClass(['hide', 'x-hidden']);
23699 this.inputEl().dom.removeAttribute('tabIndex');
23700 this.inputEl().focus();
23702 Roo.log('editor - hiding textarea');
23704 // Roo.log(this.pushValue());
23707 this.inputEl().addClass(['hide', 'x-hidden']);
23708 this.inputEl().dom.setAttribute('tabIndex', -1);
23709 //this.deferFocus();
23712 if(this.resizable){
23713 this.setSize(this.wrap.getSize());
23716 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23719 // private (for BoxComponent)
23720 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23722 // private (for BoxComponent)
23723 getResizeEl : function(){
23727 // private (for BoxComponent)
23728 getPositionEl : function(){
23733 initEvents : function(){
23734 this.originalValue = this.getValue();
23738 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23741 // markInvalid : Roo.emptyFn,
23743 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23746 // clearInvalid : Roo.emptyFn,
23748 setValue : function(v){
23749 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23750 this.editorcore.pushValue();
23755 deferFocus : function(){
23756 this.focus.defer(10, this);
23760 focus : function(){
23761 this.editorcore.focus();
23767 onDestroy : function(){
23773 for (var i =0; i < this.toolbars.length;i++) {
23774 // fixme - ask toolbars for heights?
23775 this.toolbars[i].onDestroy();
23778 this.wrap.dom.innerHTML = '';
23779 this.wrap.remove();
23784 onFirstFocus : function(){
23785 //Roo.log("onFirstFocus");
23786 this.editorcore.onFirstFocus();
23787 for (var i =0; i < this.toolbars.length;i++) {
23788 this.toolbars[i].onFirstFocus();
23794 syncValue : function()
23796 this.editorcore.syncValue();
23799 pushValue : function()
23801 this.editorcore.pushValue();
23805 // hide stuff that is not compatible
23819 * @event specialkey
23823 * @cfg {String} fieldClass @hide
23826 * @cfg {String} focusClass @hide
23829 * @cfg {String} autoCreate @hide
23832 * @cfg {String} inputType @hide
23835 * @cfg {String} invalidClass @hide
23838 * @cfg {String} invalidText @hide
23841 * @cfg {String} msgFx @hide
23844 * @cfg {String} validateOnBlur @hide
23853 Roo.namespace('Roo.bootstrap.htmleditor');
23855 * @class Roo.bootstrap.HtmlEditorToolbar1
23860 new Roo.bootstrap.HtmlEditor({
23863 new Roo.bootstrap.HtmlEditorToolbar1({
23864 disable : { fonts: 1 , format: 1, ..., ... , ...],
23870 * @cfg {Object} disable List of elements to disable..
23871 * @cfg {Array} btns List of additional buttons.
23875 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23878 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23881 Roo.apply(this, config);
23883 // default disabled, based on 'good practice'..
23884 this.disable = this.disable || {};
23885 Roo.applyIf(this.disable, {
23888 specialElements : true
23890 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23892 this.editor = config.editor;
23893 this.editorcore = config.editor.editorcore;
23895 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23897 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23898 // dont call parent... till later.
23900 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23905 editorcore : false,
23910 "h1","h2","h3","h4","h5","h6",
23912 "abbr", "acronym", "address", "cite", "samp", "var",
23916 onRender : function(ct, position)
23918 // Roo.log("Call onRender: " + this.xtype);
23920 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23922 this.el.dom.style.marginBottom = '0';
23924 var editorcore = this.editorcore;
23925 var editor= this.editor;
23928 var btn = function(id,cmd , toggle, handler, html){
23930 var event = toggle ? 'toggle' : 'click';
23935 xns: Roo.bootstrap,
23938 enableToggle:toggle !== false,
23940 pressed : toggle ? false : null,
23943 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23944 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23950 // var cb_box = function...
23955 xns: Roo.bootstrap,
23956 glyphicon : 'font',
23960 xns: Roo.bootstrap,
23964 Roo.each(this.formats, function(f) {
23965 style.menu.items.push({
23967 xns: Roo.bootstrap,
23968 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23973 editorcore.insertTag(this.tagname);
23980 children.push(style);
23982 btn('bold',false,true);
23983 btn('italic',false,true);
23984 btn('align-left', 'justifyleft',true);
23985 btn('align-center', 'justifycenter',true);
23986 btn('align-right' , 'justifyright',true);
23987 btn('link', false, false, function(btn) {
23988 //Roo.log("create link?");
23989 var url = prompt(this.createLinkText, this.defaultLinkValue);
23990 if(url && url != 'http:/'+'/'){
23991 this.editorcore.relayCmd('createlink', url);
23994 btn('list','insertunorderedlist',true);
23995 btn('pencil', false,true, function(btn){
23997 this.toggleSourceEdit(btn.pressed);
24000 if (this.editor.btns.length > 0) {
24001 for (var i = 0; i<this.editor.btns.length; i++) {
24002 children.push(this.editor.btns[i]);
24010 xns: Roo.bootstrap,
24015 xns: Roo.bootstrap,
24020 cog.menu.items.push({
24022 xns: Roo.bootstrap,
24023 html : Clean styles,
24028 editorcore.insertTag(this.tagname);
24037 this.xtype = 'NavSimplebar';
24039 for(var i=0;i< children.length;i++) {
24041 this.buttons.add(this.addxtypeChild(children[i]));
24045 editor.on('editorevent', this.updateToolbar, this);
24047 onBtnClick : function(id)
24049 this.editorcore.relayCmd(id);
24050 this.editorcore.focus();
24054 * Protected method that will not generally be called directly. It triggers
24055 * a toolbar update by reading the markup state of the current selection in the editor.
24057 updateToolbar: function(){
24059 if(!this.editorcore.activated){
24060 this.editor.onFirstFocus(); // is this neeed?
24064 var btns = this.buttons;
24065 var doc = this.editorcore.doc;
24066 btns.get('bold').setActive(doc.queryCommandState('bold'));
24067 btns.get('italic').setActive(doc.queryCommandState('italic'));
24068 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24070 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24071 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24072 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24074 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24075 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24078 var ans = this.editorcore.getAllAncestors();
24079 if (this.formatCombo) {
24082 var store = this.formatCombo.store;
24083 this.formatCombo.setValue("");
24084 for (var i =0; i < ans.length;i++) {
24085 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24087 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24095 // hides menus... - so this cant be on a menu...
24096 Roo.bootstrap.MenuMgr.hideAll();
24098 Roo.bootstrap.MenuMgr.hideAll();
24099 //this.editorsyncValue();
24101 onFirstFocus: function() {
24102 this.buttons.each(function(item){
24106 toggleSourceEdit : function(sourceEditMode){
24109 if(sourceEditMode){
24110 Roo.log("disabling buttons");
24111 this.buttons.each( function(item){
24112 if(item.cmd != 'pencil'){
24118 Roo.log("enabling buttons");
24119 if(this.editorcore.initialized){
24120 this.buttons.each( function(item){
24126 Roo.log("calling toggole on editor");
24127 // tell the editor that it's been pressed..
24128 this.editor.toggleSourceEdit(sourceEditMode);
24138 * @class Roo.bootstrap.Table.AbstractSelectionModel
24139 * @extends Roo.util.Observable
24140 * Abstract base class for grid SelectionModels. It provides the interface that should be
24141 * implemented by descendant classes. This class should not be directly instantiated.
24144 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24145 this.locked = false;
24146 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24150 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24151 /** @ignore Called by the grid automatically. Do not call directly. */
24152 init : function(grid){
24158 * Locks the selections.
24161 this.locked = true;
24165 * Unlocks the selections.
24167 unlock : function(){
24168 this.locked = false;
24172 * Returns true if the selections are locked.
24173 * @return {Boolean}
24175 isLocked : function(){
24176 return this.locked;
24180 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24181 * @class Roo.bootstrap.Table.RowSelectionModel
24182 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24183 * It supports multiple selections and keyboard selection/navigation.
24185 * @param {Object} config
24188 Roo.bootstrap.Table.RowSelectionModel = function(config){
24189 Roo.apply(this, config);
24190 this.selections = new Roo.util.MixedCollection(false, function(o){
24195 this.lastActive = false;
24199 * @event selectionchange
24200 * Fires when the selection changes
24201 * @param {SelectionModel} this
24203 "selectionchange" : true,
24205 * @event afterselectionchange
24206 * Fires after the selection changes (eg. by key press or clicking)
24207 * @param {SelectionModel} this
24209 "afterselectionchange" : true,
24211 * @event beforerowselect
24212 * Fires when a row is selected being selected, return false to cancel.
24213 * @param {SelectionModel} this
24214 * @param {Number} rowIndex The selected index
24215 * @param {Boolean} keepExisting False if other selections will be cleared
24217 "beforerowselect" : true,
24220 * Fires when a row is selected.
24221 * @param {SelectionModel} this
24222 * @param {Number} rowIndex The selected index
24223 * @param {Roo.data.Record} r The record
24225 "rowselect" : true,
24227 * @event rowdeselect
24228 * Fires when a row is deselected.
24229 * @param {SelectionModel} this
24230 * @param {Number} rowIndex The selected index
24232 "rowdeselect" : true
24234 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24235 this.locked = false;
24238 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24240 * @cfg {Boolean} singleSelect
24241 * True to allow selection of only one row at a time (defaults to false)
24243 singleSelect : false,
24246 initEvents : function()
24249 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24250 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24251 //}else{ // allow click to work like normal
24252 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24254 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24255 this.grid.on("rowclick", this.handleMouseDown, this);
24257 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24258 "up" : function(e){
24260 this.selectPrevious(e.shiftKey);
24261 }else if(this.last !== false && this.lastActive !== false){
24262 var last = this.last;
24263 this.selectRange(this.last, this.lastActive-1);
24264 this.grid.getView().focusRow(this.lastActive);
24265 if(last !== false){
24269 this.selectFirstRow();
24271 this.fireEvent("afterselectionchange", this);
24273 "down" : function(e){
24275 this.selectNext(e.shiftKey);
24276 }else if(this.last !== false && this.lastActive !== false){
24277 var last = this.last;
24278 this.selectRange(this.last, this.lastActive+1);
24279 this.grid.getView().focusRow(this.lastActive);
24280 if(last !== false){
24284 this.selectFirstRow();
24286 this.fireEvent("afterselectionchange", this);
24290 this.grid.store.on('load', function(){
24291 this.selections.clear();
24294 var view = this.grid.view;
24295 view.on("refresh", this.onRefresh, this);
24296 view.on("rowupdated", this.onRowUpdated, this);
24297 view.on("rowremoved", this.onRemove, this);
24302 onRefresh : function()
24304 var ds = this.grid.store, i, v = this.grid.view;
24305 var s = this.selections;
24306 s.each(function(r){
24307 if((i = ds.indexOfId(r.id)) != -1){
24316 onRemove : function(v, index, r){
24317 this.selections.remove(r);
24321 onRowUpdated : function(v, index, r){
24322 if(this.isSelected(r)){
24323 v.onRowSelect(index);
24329 * @param {Array} records The records to select
24330 * @param {Boolean} keepExisting (optional) True to keep existing selections
24332 selectRecords : function(records, keepExisting)
24335 this.clearSelections();
24337 var ds = this.grid.store;
24338 for(var i = 0, len = records.length; i < len; i++){
24339 this.selectRow(ds.indexOf(records[i]), true);
24344 * Gets the number of selected rows.
24347 getCount : function(){
24348 return this.selections.length;
24352 * Selects the first row in the grid.
24354 selectFirstRow : function(){
24359 * Select the last row.
24360 * @param {Boolean} keepExisting (optional) True to keep existing selections
24362 selectLastRow : function(keepExisting){
24363 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24364 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24368 * Selects the row immediately following the last selected row.
24369 * @param {Boolean} keepExisting (optional) True to keep existing selections
24371 selectNext : function(keepExisting)
24373 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24374 this.selectRow(this.last+1, keepExisting);
24375 this.grid.getView().focusRow(this.last);
24380 * Selects the row that precedes the last selected row.
24381 * @param {Boolean} keepExisting (optional) True to keep existing selections
24383 selectPrevious : function(keepExisting){
24385 this.selectRow(this.last-1, keepExisting);
24386 this.grid.getView().focusRow(this.last);
24391 * Returns the selected records
24392 * @return {Array} Array of selected records
24394 getSelections : function(){
24395 return [].concat(this.selections.items);
24399 * Returns the first selected record.
24402 getSelected : function(){
24403 return this.selections.itemAt(0);
24408 * Clears all selections.
24410 clearSelections : function(fast)
24416 var ds = this.grid.store;
24417 var s = this.selections;
24418 s.each(function(r){
24419 this.deselectRow(ds.indexOfId(r.id));
24423 this.selections.clear();
24430 * Selects all rows.
24432 selectAll : function(){
24436 this.selections.clear();
24437 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24438 this.selectRow(i, true);
24443 * Returns True if there is a selection.
24444 * @return {Boolean}
24446 hasSelection : function(){
24447 return this.selections.length > 0;
24451 * Returns True if the specified row is selected.
24452 * @param {Number/Record} record The record or index of the record to check
24453 * @return {Boolean}
24455 isSelected : function(index){
24456 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24457 return (r && this.selections.key(r.id) ? true : false);
24461 * Returns True if the specified record id is selected.
24462 * @param {String} id The id of record to check
24463 * @return {Boolean}
24465 isIdSelected : function(id){
24466 return (this.selections.key(id) ? true : false);
24471 handleMouseDBClick : function(e, t){
24475 handleMouseDown : function(e, t)
24477 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24478 if(this.isLocked() || rowIndex < 0 ){
24481 if(e.shiftKey && this.last !== false){
24482 var last = this.last;
24483 this.selectRange(last, rowIndex, e.ctrlKey);
24484 this.last = last; // reset the last
24488 var isSelected = this.isSelected(rowIndex);
24489 //Roo.log("select row:" + rowIndex);
24491 this.deselectRow(rowIndex);
24493 this.selectRow(rowIndex, true);
24497 if(e.button !== 0 && isSelected){
24498 alert('rowIndex 2: ' + rowIndex);
24499 view.focusRow(rowIndex);
24500 }else if(e.ctrlKey && isSelected){
24501 this.deselectRow(rowIndex);
24502 }else if(!isSelected){
24503 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24504 view.focusRow(rowIndex);
24508 this.fireEvent("afterselectionchange", this);
24511 handleDragableRowClick : function(grid, rowIndex, e)
24513 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24514 this.selectRow(rowIndex, false);
24515 grid.view.focusRow(rowIndex);
24516 this.fireEvent("afterselectionchange", this);
24521 * Selects multiple rows.
24522 * @param {Array} rows Array of the indexes of the row to select
24523 * @param {Boolean} keepExisting (optional) True to keep existing selections
24525 selectRows : function(rows, keepExisting){
24527 this.clearSelections();
24529 for(var i = 0, len = rows.length; i < len; i++){
24530 this.selectRow(rows[i], true);
24535 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24536 * @param {Number} startRow The index of the first row in the range
24537 * @param {Number} endRow The index of the last row in the range
24538 * @param {Boolean} keepExisting (optional) True to retain existing selections
24540 selectRange : function(startRow, endRow, keepExisting){
24545 this.clearSelections();
24547 if(startRow <= endRow){
24548 for(var i = startRow; i <= endRow; i++){
24549 this.selectRow(i, true);
24552 for(var i = startRow; i >= endRow; i--){
24553 this.selectRow(i, true);
24559 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24560 * @param {Number} startRow The index of the first row in the range
24561 * @param {Number} endRow The index of the last row in the range
24563 deselectRange : function(startRow, endRow, preventViewNotify){
24567 for(var i = startRow; i <= endRow; i++){
24568 this.deselectRow(i, preventViewNotify);
24574 * @param {Number} row The index of the row to select
24575 * @param {Boolean} keepExisting (optional) True to keep existing selections
24577 selectRow : function(index, keepExisting, preventViewNotify)
24579 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24582 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24583 if(!keepExisting || this.singleSelect){
24584 this.clearSelections();
24587 var r = this.grid.store.getAt(index);
24588 //console.log('selectRow - record id :' + r.id);
24590 this.selections.add(r);
24591 this.last = this.lastActive = index;
24592 if(!preventViewNotify){
24593 var proxy = new Roo.Element(
24594 this.grid.getRowDom(index)
24596 proxy.addClass('bg-info info');
24598 this.fireEvent("rowselect", this, index, r);
24599 this.fireEvent("selectionchange", this);
24605 * @param {Number} row The index of the row to deselect
24607 deselectRow : function(index, preventViewNotify)
24612 if(this.last == index){
24615 if(this.lastActive == index){
24616 this.lastActive = false;
24619 var r = this.grid.store.getAt(index);
24624 this.selections.remove(r);
24625 //.console.log('deselectRow - record id :' + r.id);
24626 if(!preventViewNotify){
24628 var proxy = new Roo.Element(
24629 this.grid.getRowDom(index)
24631 proxy.removeClass('bg-info info');
24633 this.fireEvent("rowdeselect", this, index);
24634 this.fireEvent("selectionchange", this);
24638 restoreLast : function(){
24640 this.last = this._last;
24645 acceptsNav : function(row, col, cm){
24646 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24650 onEditorKey : function(field, e){
24651 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24656 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24658 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24660 }else if(k == e.ENTER && !e.ctrlKey){
24664 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24666 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24668 }else if(k == e.ESC){
24672 g.startEditing(newCell[0], newCell[1]);
24678 * Ext JS Library 1.1.1
24679 * Copyright(c) 2006-2007, Ext JS, LLC.
24681 * Originally Released Under LGPL - original licence link has changed is not relivant.
24684 * <script type="text/javascript">
24688 * @class Roo.bootstrap.PagingToolbar
24689 * @extends Roo.bootstrap.NavSimplebar
24690 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24692 * Create a new PagingToolbar
24693 * @param {Object} config The config object
24694 * @param {Roo.data.Store} store
24696 Roo.bootstrap.PagingToolbar = function(config)
24698 // old args format still supported... - xtype is prefered..
24699 // created from xtype...
24701 this.ds = config.dataSource;
24703 if (config.store && !this.ds) {
24704 this.store= Roo.factory(config.store, Roo.data);
24705 this.ds = this.store;
24706 this.ds.xmodule = this.xmodule || false;
24709 this.toolbarItems = [];
24710 if (config.items) {
24711 this.toolbarItems = config.items;
24714 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24719 this.bind(this.ds);
24722 if (Roo.bootstrap.version == 4) {
24723 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24725 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24730 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24732 * @cfg {Roo.data.Store} dataSource
24733 * The underlying data store providing the paged data
24736 * @cfg {String/HTMLElement/Element} container
24737 * container The id or element that will contain the toolbar
24740 * @cfg {Boolean} displayInfo
24741 * True to display the displayMsg (defaults to false)
24744 * @cfg {Number} pageSize
24745 * The number of records to display per page (defaults to 20)
24749 * @cfg {String} displayMsg
24750 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24752 displayMsg : 'Displaying {0} - {1} of {2}',
24754 * @cfg {String} emptyMsg
24755 * The message to display when no records are found (defaults to "No data to display")
24757 emptyMsg : 'No data to display',
24759 * Customizable piece of the default paging text (defaults to "Page")
24762 beforePageText : "Page",
24764 * Customizable piece of the default paging text (defaults to "of %0")
24767 afterPageText : "of {0}",
24769 * Customizable piece of the default paging text (defaults to "First Page")
24772 firstText : "First Page",
24774 * Customizable piece of the default paging text (defaults to "Previous Page")
24777 prevText : "Previous Page",
24779 * Customizable piece of the default paging text (defaults to "Next Page")
24782 nextText : "Next Page",
24784 * Customizable piece of the default paging text (defaults to "Last Page")
24787 lastText : "Last Page",
24789 * Customizable piece of the default paging text (defaults to "Refresh")
24792 refreshText : "Refresh",
24796 onRender : function(ct, position)
24798 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24799 this.navgroup.parentId = this.id;
24800 this.navgroup.onRender(this.el, null);
24801 // add the buttons to the navgroup
24803 if(this.displayInfo){
24804 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24805 this.displayEl = this.el.select('.x-paging-info', true).first();
24806 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24807 // this.displayEl = navel.el.select('span',true).first();
24813 Roo.each(_this.buttons, function(e){ // this might need to use render????
24814 Roo.factory(e).render(_this.el);
24818 Roo.each(_this.toolbarItems, function(e) {
24819 _this.navgroup.addItem(e);
24823 this.first = this.navgroup.addItem({
24824 tooltip: this.firstText,
24825 cls: "prev btn-outline-secondary",
24826 html : ' <i class="fa fa-step-backward"></i>',
24828 preventDefault: true,
24829 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24832 this.prev = this.navgroup.addItem({
24833 tooltip: this.prevText,
24834 cls: "prev btn-outline-secondary",
24835 html : ' <i class="fa fa-backward"></i>',
24837 preventDefault: true,
24838 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24840 //this.addSeparator();
24843 var field = this.navgroup.addItem( {
24845 cls : 'x-paging-position btn-outline-secondary',
24847 html : this.beforePageText +
24848 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24849 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24852 this.field = field.el.select('input', true).first();
24853 this.field.on("keydown", this.onPagingKeydown, this);
24854 this.field.on("focus", function(){this.dom.select();});
24857 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24858 //this.field.setHeight(18);
24859 //this.addSeparator();
24860 this.next = this.navgroup.addItem({
24861 tooltip: this.nextText,
24862 cls: "next btn-outline-secondary",
24863 html : ' <i class="fa fa-forward"></i>',
24865 preventDefault: true,
24866 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24868 this.last = this.navgroup.addItem({
24869 tooltip: this.lastText,
24870 html : ' <i class="fa fa-step-forward"></i>',
24871 cls: "next btn-outline-secondary",
24873 preventDefault: true,
24874 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24876 //this.addSeparator();
24877 this.loading = this.navgroup.addItem({
24878 tooltip: this.refreshText,
24879 cls: "btn-outline-secondary",
24880 html : ' <i class="fa fa-refresh"></i>',
24881 preventDefault: true,
24882 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24888 updateInfo : function(){
24889 if(this.displayEl){
24890 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24891 var msg = count == 0 ?
24895 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24897 this.displayEl.update(msg);
24902 onLoad : function(ds, r, o)
24904 this.cursor = o.params.start ? o.params.start : 0;
24906 var d = this.getPageData(),
24911 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24912 this.field.dom.value = ap;
24913 this.first.setDisabled(ap == 1);
24914 this.prev.setDisabled(ap == 1);
24915 this.next.setDisabled(ap == ps);
24916 this.last.setDisabled(ap == ps);
24917 this.loading.enable();
24922 getPageData : function(){
24923 var total = this.ds.getTotalCount();
24926 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24927 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24932 onLoadError : function(){
24933 this.loading.enable();
24937 onPagingKeydown : function(e){
24938 var k = e.getKey();
24939 var d = this.getPageData();
24941 var v = this.field.dom.value, pageNum;
24942 if(!v || isNaN(pageNum = parseInt(v, 10))){
24943 this.field.dom.value = d.activePage;
24946 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24947 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24950 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))
24952 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24953 this.field.dom.value = pageNum;
24954 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24957 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24959 var v = this.field.dom.value, pageNum;
24960 var increment = (e.shiftKey) ? 10 : 1;
24961 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24964 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24965 this.field.dom.value = d.activePage;
24968 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24970 this.field.dom.value = parseInt(v, 10) + increment;
24971 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24972 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24979 beforeLoad : function(){
24981 this.loading.disable();
24986 onClick : function(which){
24995 ds.load({params:{start: 0, limit: this.pageSize}});
24998 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25001 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25004 var total = ds.getTotalCount();
25005 var extra = total % this.pageSize;
25006 var lastStart = extra ? (total - extra) : total-this.pageSize;
25007 ds.load({params:{start: lastStart, limit: this.pageSize}});
25010 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25016 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25017 * @param {Roo.data.Store} store The data store to unbind
25019 unbind : function(ds){
25020 ds.un("beforeload", this.beforeLoad, this);
25021 ds.un("load", this.onLoad, this);
25022 ds.un("loadexception", this.onLoadError, this);
25023 ds.un("remove", this.updateInfo, this);
25024 ds.un("add", this.updateInfo, this);
25025 this.ds = undefined;
25029 * Binds the paging toolbar to the specified {@link Roo.data.Store}
25030 * @param {Roo.data.Store} store The data store to bind
25032 bind : function(ds){
25033 ds.on("beforeload", this.beforeLoad, this);
25034 ds.on("load", this.onLoad, this);
25035 ds.on("loadexception", this.onLoadError, this);
25036 ds.on("remove", this.updateInfo, this);
25037 ds.on("add", this.updateInfo, this);
25048 * @class Roo.bootstrap.MessageBar
25049 * @extends Roo.bootstrap.Component
25050 * Bootstrap MessageBar class
25051 * @cfg {String} html contents of the MessageBar
25052 * @cfg {String} weight (info | success | warning | danger) default info
25053 * @cfg {String} beforeClass insert the bar before the given class
25054 * @cfg {Boolean} closable (true | false) default false
25055 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25058 * Create a new Element
25059 * @param {Object} config The config object
25062 Roo.bootstrap.MessageBar = function(config){
25063 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25066 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25072 beforeClass: 'bootstrap-sticky-wrap',
25074 getAutoCreate : function(){
25078 cls: 'alert alert-dismissable alert-' + this.weight,
25083 html: this.html || ''
25089 cfg.cls += ' alert-messages-fixed';
25103 onRender : function(ct, position)
25105 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25108 var cfg = Roo.apply({}, this.getAutoCreate());
25112 cfg.cls += ' ' + this.cls;
25115 cfg.style = this.style;
25117 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25119 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25122 this.el.select('>button.close').on('click', this.hide, this);
25128 if (!this.rendered) {
25134 this.fireEvent('show', this);
25140 if (!this.rendered) {
25146 this.fireEvent('hide', this);
25149 update : function()
25151 // var e = this.el.dom.firstChild;
25153 // if(this.closable){
25154 // e = e.nextSibling;
25157 // e.data = this.html || '';
25159 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25175 * @class Roo.bootstrap.Graph
25176 * @extends Roo.bootstrap.Component
25177 * Bootstrap Graph class
25181 @cfg {String} graphtype bar | vbar | pie
25182 @cfg {number} g_x coodinator | centre x (pie)
25183 @cfg {number} g_y coodinator | centre y (pie)
25184 @cfg {number} g_r radius (pie)
25185 @cfg {number} g_height height of the chart (respected by all elements in the set)
25186 @cfg {number} g_width width of the chart (respected by all elements in the set)
25187 @cfg {Object} title The title of the chart
25190 -opts (object) options for the chart
25192 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25193 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25195 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.
25196 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25198 o stretch (boolean)
25200 -opts (object) options for the pie
25203 o startAngle (number)
25204 o endAngle (number)
25208 * Create a new Input
25209 * @param {Object} config The config object
25212 Roo.bootstrap.Graph = function(config){
25213 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25219 * The img click event for the img.
25220 * @param {Roo.EventObject} e
25226 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25237 //g_colors: this.colors,
25244 getAutoCreate : function(){
25255 onRender : function(ct,position){
25258 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25260 if (typeof(Raphael) == 'undefined') {
25261 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25265 this.raphael = Raphael(this.el.dom);
25267 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25268 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25269 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25270 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25272 r.text(160, 10, "Single Series Chart").attr(txtattr);
25273 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25274 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25275 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25277 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25278 r.barchart(330, 10, 300, 220, data1);
25279 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25280 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25283 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25284 // r.barchart(30, 30, 560, 250, xdata, {
25285 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25286 // axis : "0 0 1 1",
25287 // axisxlabels : xdata
25288 // //yvalues : cols,
25291 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25293 // this.load(null,xdata,{
25294 // axis : "0 0 1 1",
25295 // axisxlabels : xdata
25300 load : function(graphtype,xdata,opts)
25302 this.raphael.clear();
25304 graphtype = this.graphtype;
25309 var r = this.raphael,
25310 fin = function () {
25311 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25313 fout = function () {
25314 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25316 pfin = function() {
25317 this.sector.stop();
25318 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25321 this.label[0].stop();
25322 this.label[0].attr({ r: 7.5 });
25323 this.label[1].attr({ "font-weight": 800 });
25326 pfout = function() {
25327 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25330 this.label[0].animate({ r: 5 }, 500, "bounce");
25331 this.label[1].attr({ "font-weight": 400 });
25337 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25340 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25343 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25344 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25346 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25353 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25358 setTitle: function(o)
25363 initEvents: function() {
25366 this.el.on('click', this.onClick, this);
25370 onClick : function(e)
25372 Roo.log('img onclick');
25373 this.fireEvent('click', this, e);
25385 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25388 * @class Roo.bootstrap.dash.NumberBox
25389 * @extends Roo.bootstrap.Component
25390 * Bootstrap NumberBox class
25391 * @cfg {String} headline Box headline
25392 * @cfg {String} content Box content
25393 * @cfg {String} icon Box icon
25394 * @cfg {String} footer Footer text
25395 * @cfg {String} fhref Footer href
25398 * Create a new NumberBox
25399 * @param {Object} config The config object
25403 Roo.bootstrap.dash.NumberBox = function(config){
25404 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25408 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25417 getAutoCreate : function(){
25421 cls : 'small-box ',
25429 cls : 'roo-headline',
25430 html : this.headline
25434 cls : 'roo-content',
25435 html : this.content
25449 cls : 'ion ' + this.icon
25458 cls : 'small-box-footer',
25459 href : this.fhref || '#',
25463 cfg.cn.push(footer);
25470 onRender : function(ct,position){
25471 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25478 setHeadline: function (value)
25480 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25483 setFooter: function (value, href)
25485 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25488 this.el.select('a.small-box-footer',true).first().attr('href', href);
25493 setContent: function (value)
25495 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25498 initEvents: function()
25512 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25515 * @class Roo.bootstrap.dash.TabBox
25516 * @extends Roo.bootstrap.Component
25517 * Bootstrap TabBox class
25518 * @cfg {String} title Title of the TabBox
25519 * @cfg {String} icon Icon of the TabBox
25520 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25521 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25524 * Create a new TabBox
25525 * @param {Object} config The config object
25529 Roo.bootstrap.dash.TabBox = function(config){
25530 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25535 * When a pane is added
25536 * @param {Roo.bootstrap.dash.TabPane} pane
25540 * @event activatepane
25541 * When a pane is activated
25542 * @param {Roo.bootstrap.dash.TabPane} pane
25544 "activatepane" : true
25552 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25557 tabScrollable : false,
25559 getChildContainer : function()
25561 return this.el.select('.tab-content', true).first();
25564 getAutoCreate : function(){
25568 cls: 'pull-left header',
25576 cls: 'fa ' + this.icon
25582 cls: 'nav nav-tabs pull-right',
25588 if(this.tabScrollable){
25595 cls: 'nav nav-tabs pull-right',
25606 cls: 'nav-tabs-custom',
25611 cls: 'tab-content no-padding',
25619 initEvents : function()
25621 //Roo.log('add add pane handler');
25622 this.on('addpane', this.onAddPane, this);
25625 * Updates the box title
25626 * @param {String} html to set the title to.
25628 setTitle : function(value)
25630 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25632 onAddPane : function(pane)
25634 this.panes.push(pane);
25635 //Roo.log('addpane');
25637 // tabs are rendere left to right..
25638 if(!this.showtabs){
25642 var ctr = this.el.select('.nav-tabs', true).first();
25645 var existing = ctr.select('.nav-tab',true);
25646 var qty = existing.getCount();;
25649 var tab = ctr.createChild({
25651 cls : 'nav-tab' + (qty ? '' : ' active'),
25659 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25662 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25664 pane.el.addClass('active');
25669 onTabClick : function(ev,un,ob,pane)
25671 //Roo.log('tab - prev default');
25672 ev.preventDefault();
25675 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25676 pane.tab.addClass('active');
25677 //Roo.log(pane.title);
25678 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25679 // technically we should have a deactivate event.. but maybe add later.
25680 // and it should not de-activate the selected tab...
25681 this.fireEvent('activatepane', pane);
25682 pane.el.addClass('active');
25683 pane.fireEvent('activate');
25688 getActivePane : function()
25691 Roo.each(this.panes, function(p) {
25692 if(p.el.hasClass('active')){
25713 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25715 * @class Roo.bootstrap.TabPane
25716 * @extends Roo.bootstrap.Component
25717 * Bootstrap TabPane class
25718 * @cfg {Boolean} active (false | true) Default false
25719 * @cfg {String} title title of panel
25723 * Create a new TabPane
25724 * @param {Object} config The config object
25727 Roo.bootstrap.dash.TabPane = function(config){
25728 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25734 * When a pane is activated
25735 * @param {Roo.bootstrap.dash.TabPane} pane
25742 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25747 // the tabBox that this is attached to.
25750 getAutoCreate : function()
25758 cfg.cls += ' active';
25763 initEvents : function()
25765 //Roo.log('trigger add pane handler');
25766 this.parent().fireEvent('addpane', this)
25770 * Updates the tab title
25771 * @param {String} html to set the title to.
25773 setTitle: function(str)
25779 this.tab.select('a', true).first().dom.innerHTML = str;
25796 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25799 * @class Roo.bootstrap.menu.Menu
25800 * @extends Roo.bootstrap.Component
25801 * Bootstrap Menu class - container for Menu
25802 * @cfg {String} html Text of the menu
25803 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25804 * @cfg {String} icon Font awesome icon
25805 * @cfg {String} pos Menu align to (top | bottom) default bottom
25809 * Create a new Menu
25810 * @param {Object} config The config object
25814 Roo.bootstrap.menu.Menu = function(config){
25815 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25819 * @event beforeshow
25820 * Fires before this menu is displayed
25821 * @param {Roo.bootstrap.menu.Menu} this
25825 * @event beforehide
25826 * Fires before this menu is hidden
25827 * @param {Roo.bootstrap.menu.Menu} this
25832 * Fires after this menu is displayed
25833 * @param {Roo.bootstrap.menu.Menu} this
25838 * Fires after this menu is hidden
25839 * @param {Roo.bootstrap.menu.Menu} this
25844 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25845 * @param {Roo.bootstrap.menu.Menu} this
25846 * @param {Roo.EventObject} e
25853 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25857 weight : 'default',
25862 getChildContainer : function() {
25863 if(this.isSubMenu){
25867 return this.el.select('ul.dropdown-menu', true).first();
25870 getAutoCreate : function()
25875 cls : 'roo-menu-text',
25883 cls : 'fa ' + this.icon
25894 cls : 'dropdown-button btn btn-' + this.weight,
25899 cls : 'dropdown-toggle btn btn-' + this.weight,
25909 cls : 'dropdown-menu'
25915 if(this.pos == 'top'){
25916 cfg.cls += ' dropup';
25919 if(this.isSubMenu){
25922 cls : 'dropdown-menu'
25929 onRender : function(ct, position)
25931 this.isSubMenu = ct.hasClass('dropdown-submenu');
25933 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25936 initEvents : function()
25938 if(this.isSubMenu){
25942 this.hidden = true;
25944 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25945 this.triggerEl.on('click', this.onTriggerPress, this);
25947 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25948 this.buttonEl.on('click', this.onClick, this);
25954 if(this.isSubMenu){
25958 return this.el.select('ul.dropdown-menu', true).first();
25961 onClick : function(e)
25963 this.fireEvent("click", this, e);
25966 onTriggerPress : function(e)
25968 if (this.isVisible()) {
25975 isVisible : function(){
25976 return !this.hidden;
25981 this.fireEvent("beforeshow", this);
25983 this.hidden = false;
25984 this.el.addClass('open');
25986 Roo.get(document).on("mouseup", this.onMouseUp, this);
25988 this.fireEvent("show", this);
25995 this.fireEvent("beforehide", this);
25997 this.hidden = true;
25998 this.el.removeClass('open');
26000 Roo.get(document).un("mouseup", this.onMouseUp);
26002 this.fireEvent("hide", this);
26005 onMouseUp : function()
26019 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26022 * @class Roo.bootstrap.menu.Item
26023 * @extends Roo.bootstrap.Component
26024 * Bootstrap MenuItem class
26025 * @cfg {Boolean} submenu (true | false) default false
26026 * @cfg {String} html text of the item
26027 * @cfg {String} href the link
26028 * @cfg {Boolean} disable (true | false) default false
26029 * @cfg {Boolean} preventDefault (true | false) default true
26030 * @cfg {String} icon Font awesome icon
26031 * @cfg {String} pos Submenu align to (left | right) default right
26035 * Create a new Item
26036 * @param {Object} config The config object
26040 Roo.bootstrap.menu.Item = function(config){
26041 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26045 * Fires when the mouse is hovering over this menu
26046 * @param {Roo.bootstrap.menu.Item} this
26047 * @param {Roo.EventObject} e
26052 * Fires when the mouse exits this menu
26053 * @param {Roo.bootstrap.menu.Item} this
26054 * @param {Roo.EventObject} e
26060 * The raw click event for the entire grid.
26061 * @param {Roo.EventObject} e
26067 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26072 preventDefault: true,
26077 getAutoCreate : function()
26082 cls : 'roo-menu-item-text',
26090 cls : 'fa ' + this.icon
26099 href : this.href || '#',
26106 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26110 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26112 if(this.pos == 'left'){
26113 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26120 initEvents : function()
26122 this.el.on('mouseover', this.onMouseOver, this);
26123 this.el.on('mouseout', this.onMouseOut, this);
26125 this.el.select('a', true).first().on('click', this.onClick, this);
26129 onClick : function(e)
26131 if(this.preventDefault){
26132 e.preventDefault();
26135 this.fireEvent("click", this, e);
26138 onMouseOver : function(e)
26140 if(this.submenu && this.pos == 'left'){
26141 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26144 this.fireEvent("mouseover", this, e);
26147 onMouseOut : function(e)
26149 this.fireEvent("mouseout", this, e);
26161 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26164 * @class Roo.bootstrap.menu.Separator
26165 * @extends Roo.bootstrap.Component
26166 * Bootstrap Separator class
26169 * Create a new Separator
26170 * @param {Object} config The config object
26174 Roo.bootstrap.menu.Separator = function(config){
26175 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26178 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26180 getAutoCreate : function(){
26201 * @class Roo.bootstrap.Tooltip
26202 * Bootstrap Tooltip class
26203 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26204 * to determine which dom element triggers the tooltip.
26206 * It needs to add support for additional attributes like tooltip-position
26209 * Create a new Toolti
26210 * @param {Object} config The config object
26213 Roo.bootstrap.Tooltip = function(config){
26214 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26216 this.alignment = Roo.bootstrap.Tooltip.alignment;
26218 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26219 this.alignment = config.alignment;
26224 Roo.apply(Roo.bootstrap.Tooltip, {
26226 * @function init initialize tooltip monitoring.
26230 currentTip : false,
26231 currentRegion : false,
26237 Roo.get(document).on('mouseover', this.enter ,this);
26238 Roo.get(document).on('mouseout', this.leave, this);
26241 this.currentTip = new Roo.bootstrap.Tooltip();
26244 enter : function(ev)
26246 var dom = ev.getTarget();
26248 //Roo.log(['enter',dom]);
26249 var el = Roo.fly(dom);
26250 if (this.currentEl) {
26252 //Roo.log(this.currentEl);
26253 //Roo.log(this.currentEl.contains(dom));
26254 if (this.currentEl == el) {
26257 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26263 if (this.currentTip.el) {
26264 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26268 if(!el || el.dom == document){
26274 // you can not look for children, as if el is the body.. then everythign is the child..
26275 if (!el.attr('tooltip')) { //
26276 if (!el.select("[tooltip]").elements.length) {
26279 // is the mouse over this child...?
26280 bindEl = el.select("[tooltip]").first();
26281 var xy = ev.getXY();
26282 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26283 //Roo.log("not in region.");
26286 //Roo.log("child element over..");
26289 this.currentEl = bindEl;
26290 this.currentTip.bind(bindEl);
26291 this.currentRegion = Roo.lib.Region.getRegion(dom);
26292 this.currentTip.enter();
26295 leave : function(ev)
26297 var dom = ev.getTarget();
26298 //Roo.log(['leave',dom]);
26299 if (!this.currentEl) {
26304 if (dom != this.currentEl.dom) {
26307 var xy = ev.getXY();
26308 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26311 // only activate leave if mouse cursor is outside... bounding box..
26316 if (this.currentTip) {
26317 this.currentTip.leave();
26319 //Roo.log('clear currentEl');
26320 this.currentEl = false;
26325 'left' : ['r-l', [-2,0], 'right'],
26326 'right' : ['l-r', [2,0], 'left'],
26327 'bottom' : ['t-b', [0,2], 'top'],
26328 'top' : [ 'b-t', [0,-2], 'bottom']
26334 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26339 delay : null, // can be { show : 300 , hide: 500}
26343 hoverState : null, //???
26345 placement : 'bottom',
26349 getAutoCreate : function(){
26356 cls : 'tooltip-arrow'
26359 cls : 'tooltip-inner'
26366 bind : function(el)
26372 enter : function () {
26374 if (this.timeout != null) {
26375 clearTimeout(this.timeout);
26378 this.hoverState = 'in';
26379 //Roo.log("enter - show");
26380 if (!this.delay || !this.delay.show) {
26385 this.timeout = setTimeout(function () {
26386 if (_t.hoverState == 'in') {
26389 }, this.delay.show);
26393 clearTimeout(this.timeout);
26395 this.hoverState = 'out';
26396 if (!this.delay || !this.delay.hide) {
26402 this.timeout = setTimeout(function () {
26403 //Roo.log("leave - timeout");
26405 if (_t.hoverState == 'out') {
26407 Roo.bootstrap.Tooltip.currentEl = false;
26412 show : function (msg)
26415 this.render(document.body);
26418 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26420 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26422 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26424 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26426 var placement = typeof this.placement == 'function' ?
26427 this.placement.call(this, this.el, on_el) :
26430 var autoToken = /\s?auto?\s?/i;
26431 var autoPlace = autoToken.test(placement);
26433 placement = placement.replace(autoToken, '') || 'top';
26437 //this.el.setXY([0,0]);
26439 //this.el.dom.style.display='block';
26441 //this.el.appendTo(on_el);
26443 var p = this.getPosition();
26444 var box = this.el.getBox();
26450 var align = this.alignment[placement];
26452 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26454 if(placement == 'top' || placement == 'bottom'){
26456 placement = 'right';
26459 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26460 placement = 'left';
26463 var scroll = Roo.select('body', true).first().getScroll();
26465 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26469 align = this.alignment[placement];
26472 this.el.alignTo(this.bindEl, align[0],align[1]);
26473 //var arrow = this.el.select('.arrow',true).first();
26474 //arrow.set(align[2],
26476 this.el.addClass(placement);
26478 this.el.addClass('in fade');
26480 this.hoverState = null;
26482 if (this.el.hasClass('fade')) {
26493 //this.el.setXY([0,0]);
26494 this.el.removeClass('in');
26510 * @class Roo.bootstrap.LocationPicker
26511 * @extends Roo.bootstrap.Component
26512 * Bootstrap LocationPicker class
26513 * @cfg {Number} latitude Position when init default 0
26514 * @cfg {Number} longitude Position when init default 0
26515 * @cfg {Number} zoom default 15
26516 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26517 * @cfg {Boolean} mapTypeControl default false
26518 * @cfg {Boolean} disableDoubleClickZoom default false
26519 * @cfg {Boolean} scrollwheel default true
26520 * @cfg {Boolean} streetViewControl default false
26521 * @cfg {Number} radius default 0
26522 * @cfg {String} locationName
26523 * @cfg {Boolean} draggable default true
26524 * @cfg {Boolean} enableAutocomplete default false
26525 * @cfg {Boolean} enableReverseGeocode default true
26526 * @cfg {String} markerTitle
26529 * Create a new LocationPicker
26530 * @param {Object} config The config object
26534 Roo.bootstrap.LocationPicker = function(config){
26536 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26541 * Fires when the picker initialized.
26542 * @param {Roo.bootstrap.LocationPicker} this
26543 * @param {Google Location} location
26547 * @event positionchanged
26548 * Fires when the picker position changed.
26549 * @param {Roo.bootstrap.LocationPicker} this
26550 * @param {Google Location} location
26552 positionchanged : true,
26555 * Fires when the map resize.
26556 * @param {Roo.bootstrap.LocationPicker} this
26561 * Fires when the map show.
26562 * @param {Roo.bootstrap.LocationPicker} this
26567 * Fires when the map hide.
26568 * @param {Roo.bootstrap.LocationPicker} this
26573 * Fires when click the map.
26574 * @param {Roo.bootstrap.LocationPicker} this
26575 * @param {Map event} e
26579 * @event mapRightClick
26580 * Fires when right click the map.
26581 * @param {Roo.bootstrap.LocationPicker} this
26582 * @param {Map event} e
26584 mapRightClick : true,
26586 * @event markerClick
26587 * Fires when click the marker.
26588 * @param {Roo.bootstrap.LocationPicker} this
26589 * @param {Map event} e
26591 markerClick : true,
26593 * @event markerRightClick
26594 * Fires when right click the marker.
26595 * @param {Roo.bootstrap.LocationPicker} this
26596 * @param {Map event} e
26598 markerRightClick : true,
26600 * @event OverlayViewDraw
26601 * Fires when OverlayView Draw
26602 * @param {Roo.bootstrap.LocationPicker} this
26604 OverlayViewDraw : true,
26606 * @event OverlayViewOnAdd
26607 * Fires when OverlayView Draw
26608 * @param {Roo.bootstrap.LocationPicker} this
26610 OverlayViewOnAdd : true,
26612 * @event OverlayViewOnRemove
26613 * Fires when OverlayView Draw
26614 * @param {Roo.bootstrap.LocationPicker} this
26616 OverlayViewOnRemove : true,
26618 * @event OverlayViewShow
26619 * Fires when OverlayView Draw
26620 * @param {Roo.bootstrap.LocationPicker} this
26621 * @param {Pixel} cpx
26623 OverlayViewShow : true,
26625 * @event OverlayViewHide
26626 * Fires when OverlayView Draw
26627 * @param {Roo.bootstrap.LocationPicker} this
26629 OverlayViewHide : true,
26631 * @event loadexception
26632 * Fires when load google lib failed.
26633 * @param {Roo.bootstrap.LocationPicker} this
26635 loadexception : true
26640 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26642 gMapContext: false,
26648 mapTypeControl: false,
26649 disableDoubleClickZoom: false,
26651 streetViewControl: false,
26655 enableAutocomplete: false,
26656 enableReverseGeocode: true,
26659 getAutoCreate: function()
26664 cls: 'roo-location-picker'
26670 initEvents: function(ct, position)
26672 if(!this.el.getWidth() || this.isApplied()){
26676 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26681 initial: function()
26683 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26684 this.fireEvent('loadexception', this);
26688 if(!this.mapTypeId){
26689 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26692 this.gMapContext = this.GMapContext();
26694 this.initOverlayView();
26696 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26700 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26701 _this.setPosition(_this.gMapContext.marker.position);
26704 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26705 _this.fireEvent('mapClick', this, event);
26709 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26710 _this.fireEvent('mapRightClick', this, event);
26714 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26715 _this.fireEvent('markerClick', this, event);
26719 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26720 _this.fireEvent('markerRightClick', this, event);
26724 this.setPosition(this.gMapContext.location);
26726 this.fireEvent('initial', this, this.gMapContext.location);
26729 initOverlayView: function()
26733 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26737 _this.fireEvent('OverlayViewDraw', _this);
26742 _this.fireEvent('OverlayViewOnAdd', _this);
26745 onRemove: function()
26747 _this.fireEvent('OverlayViewOnRemove', _this);
26750 show: function(cpx)
26752 _this.fireEvent('OverlayViewShow', _this, cpx);
26757 _this.fireEvent('OverlayViewHide', _this);
26763 fromLatLngToContainerPixel: function(event)
26765 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26768 isApplied: function()
26770 return this.getGmapContext() == false ? false : true;
26773 getGmapContext: function()
26775 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26778 GMapContext: function()
26780 var position = new google.maps.LatLng(this.latitude, this.longitude);
26782 var _map = new google.maps.Map(this.el.dom, {
26785 mapTypeId: this.mapTypeId,
26786 mapTypeControl: this.mapTypeControl,
26787 disableDoubleClickZoom: this.disableDoubleClickZoom,
26788 scrollwheel: this.scrollwheel,
26789 streetViewControl: this.streetViewControl,
26790 locationName: this.locationName,
26791 draggable: this.draggable,
26792 enableAutocomplete: this.enableAutocomplete,
26793 enableReverseGeocode: this.enableReverseGeocode
26796 var _marker = new google.maps.Marker({
26797 position: position,
26799 title: this.markerTitle,
26800 draggable: this.draggable
26807 location: position,
26808 radius: this.radius,
26809 locationName: this.locationName,
26810 addressComponents: {
26811 formatted_address: null,
26812 addressLine1: null,
26813 addressLine2: null,
26815 streetNumber: null,
26819 stateOrProvince: null
26822 domContainer: this.el.dom,
26823 geodecoder: new google.maps.Geocoder()
26827 drawCircle: function(center, radius, options)
26829 if (this.gMapContext.circle != null) {
26830 this.gMapContext.circle.setMap(null);
26834 options = Roo.apply({}, options, {
26835 strokeColor: "#0000FF",
26836 strokeOpacity: .35,
26838 fillColor: "#0000FF",
26842 options.map = this.gMapContext.map;
26843 options.radius = radius;
26844 options.center = center;
26845 this.gMapContext.circle = new google.maps.Circle(options);
26846 return this.gMapContext.circle;
26852 setPosition: function(location)
26854 this.gMapContext.location = location;
26855 this.gMapContext.marker.setPosition(location);
26856 this.gMapContext.map.panTo(location);
26857 this.drawCircle(location, this.gMapContext.radius, {});
26861 if (this.gMapContext.settings.enableReverseGeocode) {
26862 this.gMapContext.geodecoder.geocode({
26863 latLng: this.gMapContext.location
26864 }, function(results, status) {
26866 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26867 _this.gMapContext.locationName = results[0].formatted_address;
26868 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26870 _this.fireEvent('positionchanged', this, location);
26877 this.fireEvent('positionchanged', this, location);
26882 google.maps.event.trigger(this.gMapContext.map, "resize");
26884 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26886 this.fireEvent('resize', this);
26889 setPositionByLatLng: function(latitude, longitude)
26891 this.setPosition(new google.maps.LatLng(latitude, longitude));
26894 getCurrentPosition: function()
26897 latitude: this.gMapContext.location.lat(),
26898 longitude: this.gMapContext.location.lng()
26902 getAddressName: function()
26904 return this.gMapContext.locationName;
26907 getAddressComponents: function()
26909 return this.gMapContext.addressComponents;
26912 address_component_from_google_geocode: function(address_components)
26916 for (var i = 0; i < address_components.length; i++) {
26917 var component = address_components[i];
26918 if (component.types.indexOf("postal_code") >= 0) {
26919 result.postalCode = component.short_name;
26920 } else if (component.types.indexOf("street_number") >= 0) {
26921 result.streetNumber = component.short_name;
26922 } else if (component.types.indexOf("route") >= 0) {
26923 result.streetName = component.short_name;
26924 } else if (component.types.indexOf("neighborhood") >= 0) {
26925 result.city = component.short_name;
26926 } else if (component.types.indexOf("locality") >= 0) {
26927 result.city = component.short_name;
26928 } else if (component.types.indexOf("sublocality") >= 0) {
26929 result.district = component.short_name;
26930 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26931 result.stateOrProvince = component.short_name;
26932 } else if (component.types.indexOf("country") >= 0) {
26933 result.country = component.short_name;
26937 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26938 result.addressLine2 = "";
26942 setZoomLevel: function(zoom)
26944 this.gMapContext.map.setZoom(zoom);
26957 this.fireEvent('show', this);
26968 this.fireEvent('hide', this);
26973 Roo.apply(Roo.bootstrap.LocationPicker, {
26975 OverlayView : function(map, options)
26977 options = options || {};
26991 * @class Roo.bootstrap.Alert
26992 * @extends Roo.bootstrap.Component
26993 * Bootstrap Alert class
26994 * @cfg {String} title The title of alert
26995 * @cfg {String} html The content of alert
26996 * @cfg {String} weight ( success | info | warning | danger )
26997 * @cfg {String} faicon font-awesomeicon
27000 * Create a new alert
27001 * @param {Object} config The config object
27005 Roo.bootstrap.Alert = function(config){
27006 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27010 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
27017 getAutoCreate : function()
27026 cls : 'roo-alert-icon'
27031 cls : 'roo-alert-title',
27036 cls : 'roo-alert-text',
27043 cfg.cn[0].cls += ' fa ' + this.faicon;
27047 cfg.cls += ' alert-' + this.weight;
27053 initEvents: function()
27055 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27058 setTitle : function(str)
27060 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27063 setText : function(str)
27065 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27068 setWeight : function(weight)
27071 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27074 this.weight = weight;
27076 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27079 setIcon : function(icon)
27082 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27085 this.faicon = icon;
27087 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27108 * @class Roo.bootstrap.UploadCropbox
27109 * @extends Roo.bootstrap.Component
27110 * Bootstrap UploadCropbox class
27111 * @cfg {String} emptyText show when image has been loaded
27112 * @cfg {String} rotateNotify show when image too small to rotate
27113 * @cfg {Number} errorTimeout default 3000
27114 * @cfg {Number} minWidth default 300
27115 * @cfg {Number} minHeight default 300
27116 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27117 * @cfg {Boolean} isDocument (true|false) default false
27118 * @cfg {String} url action url
27119 * @cfg {String} paramName default 'imageUpload'
27120 * @cfg {String} method default POST
27121 * @cfg {Boolean} loadMask (true|false) default true
27122 * @cfg {Boolean} loadingText default 'Loading...'
27125 * Create a new UploadCropbox
27126 * @param {Object} config The config object
27129 Roo.bootstrap.UploadCropbox = function(config){
27130 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27134 * @event beforeselectfile
27135 * Fire before select file
27136 * @param {Roo.bootstrap.UploadCropbox} this
27138 "beforeselectfile" : true,
27141 * Fire after initEvent
27142 * @param {Roo.bootstrap.UploadCropbox} this
27147 * Fire after initEvent
27148 * @param {Roo.bootstrap.UploadCropbox} this
27149 * @param {String} data
27154 * Fire when preparing the file data
27155 * @param {Roo.bootstrap.UploadCropbox} this
27156 * @param {Object} file
27161 * Fire when get exception
27162 * @param {Roo.bootstrap.UploadCropbox} this
27163 * @param {XMLHttpRequest} xhr
27165 "exception" : true,
27167 * @event beforeloadcanvas
27168 * Fire before load the canvas
27169 * @param {Roo.bootstrap.UploadCropbox} this
27170 * @param {String} src
27172 "beforeloadcanvas" : true,
27175 * Fire when trash image
27176 * @param {Roo.bootstrap.UploadCropbox} this
27181 * Fire when download the image
27182 * @param {Roo.bootstrap.UploadCropbox} this
27186 * @event footerbuttonclick
27187 * Fire when footerbuttonclick
27188 * @param {Roo.bootstrap.UploadCropbox} this
27189 * @param {String} type
27191 "footerbuttonclick" : true,
27195 * @param {Roo.bootstrap.UploadCropbox} this
27200 * Fire when rotate the image
27201 * @param {Roo.bootstrap.UploadCropbox} this
27202 * @param {String} pos
27207 * Fire when inspect the file
27208 * @param {Roo.bootstrap.UploadCropbox} this
27209 * @param {Object} file
27214 * Fire when xhr upload the file
27215 * @param {Roo.bootstrap.UploadCropbox} this
27216 * @param {Object} data
27221 * Fire when arrange the file data
27222 * @param {Roo.bootstrap.UploadCropbox} this
27223 * @param {Object} formData
27228 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27231 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27233 emptyText : 'Click to upload image',
27234 rotateNotify : 'Image is too small to rotate',
27235 errorTimeout : 3000,
27249 cropType : 'image/jpeg',
27251 canvasLoaded : false,
27252 isDocument : false,
27254 paramName : 'imageUpload',
27256 loadingText : 'Loading...',
27259 getAutoCreate : function()
27263 cls : 'roo-upload-cropbox',
27267 cls : 'roo-upload-cropbox-selector',
27272 cls : 'roo-upload-cropbox-body',
27273 style : 'cursor:pointer',
27277 cls : 'roo-upload-cropbox-preview'
27281 cls : 'roo-upload-cropbox-thumb'
27285 cls : 'roo-upload-cropbox-empty-notify',
27286 html : this.emptyText
27290 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27291 html : this.rotateNotify
27297 cls : 'roo-upload-cropbox-footer',
27300 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27310 onRender : function(ct, position)
27312 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27314 if (this.buttons.length) {
27316 Roo.each(this.buttons, function(bb) {
27318 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27320 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27326 this.maskEl = this.el;
27330 initEvents : function()
27332 this.urlAPI = (window.createObjectURL && window) ||
27333 (window.URL && URL.revokeObjectURL && URL) ||
27334 (window.webkitURL && webkitURL);
27336 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27337 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27339 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27340 this.selectorEl.hide();
27342 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27343 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27345 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27346 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27347 this.thumbEl.hide();
27349 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27350 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27352 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27353 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27354 this.errorEl.hide();
27356 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27357 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27358 this.footerEl.hide();
27360 this.setThumbBoxSize();
27366 this.fireEvent('initial', this);
27373 window.addEventListener("resize", function() { _this.resize(); } );
27375 this.bodyEl.on('click', this.beforeSelectFile, this);
27378 this.bodyEl.on('touchstart', this.onTouchStart, this);
27379 this.bodyEl.on('touchmove', this.onTouchMove, this);
27380 this.bodyEl.on('touchend', this.onTouchEnd, this);
27384 this.bodyEl.on('mousedown', this.onMouseDown, this);
27385 this.bodyEl.on('mousemove', this.onMouseMove, this);
27386 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27387 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27388 Roo.get(document).on('mouseup', this.onMouseUp, this);
27391 this.selectorEl.on('change', this.onFileSelected, this);
27397 this.baseScale = 1;
27399 this.baseRotate = 1;
27400 this.dragable = false;
27401 this.pinching = false;
27404 this.cropData = false;
27405 this.notifyEl.dom.innerHTML = this.emptyText;
27407 this.selectorEl.dom.value = '';
27411 resize : function()
27413 if(this.fireEvent('resize', this) != false){
27414 this.setThumbBoxPosition();
27415 this.setCanvasPosition();
27419 onFooterButtonClick : function(e, el, o, type)
27422 case 'rotate-left' :
27423 this.onRotateLeft(e);
27425 case 'rotate-right' :
27426 this.onRotateRight(e);
27429 this.beforeSelectFile(e);
27444 this.fireEvent('footerbuttonclick', this, type);
27447 beforeSelectFile : function(e)
27449 e.preventDefault();
27451 if(this.fireEvent('beforeselectfile', this) != false){
27452 this.selectorEl.dom.click();
27456 onFileSelected : function(e)
27458 e.preventDefault();
27460 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27464 var file = this.selectorEl.dom.files[0];
27466 if(this.fireEvent('inspect', this, file) != false){
27467 this.prepare(file);
27472 trash : function(e)
27474 this.fireEvent('trash', this);
27477 download : function(e)
27479 this.fireEvent('download', this);
27482 loadCanvas : function(src)
27484 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27488 this.imageEl = document.createElement('img');
27492 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27494 this.imageEl.src = src;
27498 onLoadCanvas : function()
27500 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27501 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27503 this.bodyEl.un('click', this.beforeSelectFile, this);
27505 this.notifyEl.hide();
27506 this.thumbEl.show();
27507 this.footerEl.show();
27509 this.baseRotateLevel();
27511 if(this.isDocument){
27512 this.setThumbBoxSize();
27515 this.setThumbBoxPosition();
27517 this.baseScaleLevel();
27523 this.canvasLoaded = true;
27526 this.maskEl.unmask();
27531 setCanvasPosition : function()
27533 if(!this.canvasEl){
27537 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27538 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27540 this.previewEl.setLeft(pw);
27541 this.previewEl.setTop(ph);
27545 onMouseDown : function(e)
27549 this.dragable = true;
27550 this.pinching = false;
27552 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27553 this.dragable = false;
27557 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27558 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27562 onMouseMove : function(e)
27566 if(!this.canvasLoaded){
27570 if (!this.dragable){
27574 var minX = Math.ceil(this.thumbEl.getLeft(true));
27575 var minY = Math.ceil(this.thumbEl.getTop(true));
27577 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27578 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27580 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27581 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27583 x = x - this.mouseX;
27584 y = y - this.mouseY;
27586 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27587 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27589 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27590 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27592 this.previewEl.setLeft(bgX);
27593 this.previewEl.setTop(bgY);
27595 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27596 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27599 onMouseUp : function(e)
27603 this.dragable = false;
27606 onMouseWheel : function(e)
27610 this.startScale = this.scale;
27612 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27614 if(!this.zoomable()){
27615 this.scale = this.startScale;
27624 zoomable : function()
27626 var minScale = this.thumbEl.getWidth() / this.minWidth;
27628 if(this.minWidth < this.minHeight){
27629 minScale = this.thumbEl.getHeight() / this.minHeight;
27632 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27633 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27637 (this.rotate == 0 || this.rotate == 180) &&
27639 width > this.imageEl.OriginWidth ||
27640 height > this.imageEl.OriginHeight ||
27641 (width < this.minWidth && height < this.minHeight)
27649 (this.rotate == 90 || this.rotate == 270) &&
27651 width > this.imageEl.OriginWidth ||
27652 height > this.imageEl.OriginHeight ||
27653 (width < this.minHeight && height < this.minWidth)
27660 !this.isDocument &&
27661 (this.rotate == 0 || this.rotate == 180) &&
27663 width < this.minWidth ||
27664 width > this.imageEl.OriginWidth ||
27665 height < this.minHeight ||
27666 height > this.imageEl.OriginHeight
27673 !this.isDocument &&
27674 (this.rotate == 90 || this.rotate == 270) &&
27676 width < this.minHeight ||
27677 width > this.imageEl.OriginWidth ||
27678 height < this.minWidth ||
27679 height > this.imageEl.OriginHeight
27689 onRotateLeft : function(e)
27691 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27693 var minScale = this.thumbEl.getWidth() / this.minWidth;
27695 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27696 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27698 this.startScale = this.scale;
27700 while (this.getScaleLevel() < minScale){
27702 this.scale = this.scale + 1;
27704 if(!this.zoomable()){
27709 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27710 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27715 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27722 this.scale = this.startScale;
27724 this.onRotateFail();
27729 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27731 if(this.isDocument){
27732 this.setThumbBoxSize();
27733 this.setThumbBoxPosition();
27734 this.setCanvasPosition();
27739 this.fireEvent('rotate', this, 'left');
27743 onRotateRight : function(e)
27745 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27747 var minScale = this.thumbEl.getWidth() / this.minWidth;
27749 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27750 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27752 this.startScale = this.scale;
27754 while (this.getScaleLevel() < minScale){
27756 this.scale = this.scale + 1;
27758 if(!this.zoomable()){
27763 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27764 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27769 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27776 this.scale = this.startScale;
27778 this.onRotateFail();
27783 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27785 if(this.isDocument){
27786 this.setThumbBoxSize();
27787 this.setThumbBoxPosition();
27788 this.setCanvasPosition();
27793 this.fireEvent('rotate', this, 'right');
27796 onRotateFail : function()
27798 this.errorEl.show(true);
27802 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27807 this.previewEl.dom.innerHTML = '';
27809 var canvasEl = document.createElement("canvas");
27811 var contextEl = canvasEl.getContext("2d");
27813 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27814 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27815 var center = this.imageEl.OriginWidth / 2;
27817 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27818 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27819 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27820 center = this.imageEl.OriginHeight / 2;
27823 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27825 contextEl.translate(center, center);
27826 contextEl.rotate(this.rotate * Math.PI / 180);
27828 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27830 this.canvasEl = document.createElement("canvas");
27832 this.contextEl = this.canvasEl.getContext("2d");
27834 switch (this.rotate) {
27837 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27838 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27840 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27845 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27846 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27848 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27849 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);
27853 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27858 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27859 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27861 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27862 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);
27866 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);
27871 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27872 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27874 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27875 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27879 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);
27886 this.previewEl.appendChild(this.canvasEl);
27888 this.setCanvasPosition();
27893 if(!this.canvasLoaded){
27897 var imageCanvas = document.createElement("canvas");
27899 var imageContext = imageCanvas.getContext("2d");
27901 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27902 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27904 var center = imageCanvas.width / 2;
27906 imageContext.translate(center, center);
27908 imageContext.rotate(this.rotate * Math.PI / 180);
27910 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27912 var canvas = document.createElement("canvas");
27914 var context = canvas.getContext("2d");
27916 canvas.width = this.minWidth;
27917 canvas.height = this.minHeight;
27919 switch (this.rotate) {
27922 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27923 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27925 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27926 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27928 var targetWidth = this.minWidth - 2 * x;
27929 var targetHeight = this.minHeight - 2 * y;
27933 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27934 scale = targetWidth / width;
27937 if(x > 0 && y == 0){
27938 scale = targetHeight / height;
27941 if(x > 0 && y > 0){
27942 scale = targetWidth / width;
27944 if(width < height){
27945 scale = targetHeight / height;
27949 context.scale(scale, scale);
27951 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27952 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27954 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27955 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27957 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27962 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27963 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27965 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27966 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27968 var targetWidth = this.minWidth - 2 * x;
27969 var targetHeight = this.minHeight - 2 * y;
27973 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27974 scale = targetWidth / width;
27977 if(x > 0 && y == 0){
27978 scale = targetHeight / height;
27981 if(x > 0 && y > 0){
27982 scale = targetWidth / width;
27984 if(width < height){
27985 scale = targetHeight / height;
27989 context.scale(scale, scale);
27991 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27992 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27994 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27995 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27997 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27999 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28004 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28005 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28007 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28008 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28010 var targetWidth = this.minWidth - 2 * x;
28011 var targetHeight = this.minHeight - 2 * y;
28015 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28016 scale = targetWidth / width;
28019 if(x > 0 && y == 0){
28020 scale = targetHeight / height;
28023 if(x > 0 && y > 0){
28024 scale = targetWidth / width;
28026 if(width < height){
28027 scale = targetHeight / height;
28031 context.scale(scale, scale);
28033 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28034 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28036 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28037 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28039 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28040 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28042 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28047 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28048 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28050 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28051 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28053 var targetWidth = this.minWidth - 2 * x;
28054 var targetHeight = this.minHeight - 2 * y;
28058 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28059 scale = targetWidth / width;
28062 if(x > 0 && y == 0){
28063 scale = targetHeight / height;
28066 if(x > 0 && y > 0){
28067 scale = targetWidth / width;
28069 if(width < height){
28070 scale = targetHeight / height;
28074 context.scale(scale, scale);
28076 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28077 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28079 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28080 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28082 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28084 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28091 this.cropData = canvas.toDataURL(this.cropType);
28093 if(this.fireEvent('crop', this, this.cropData) !== false){
28094 this.process(this.file, this.cropData);
28101 setThumbBoxSize : function()
28105 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28106 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28107 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28109 this.minWidth = width;
28110 this.minHeight = height;
28112 if(this.rotate == 90 || this.rotate == 270){
28113 this.minWidth = height;
28114 this.minHeight = width;
28119 width = Math.ceil(this.minWidth * height / this.minHeight);
28121 if(this.minWidth > this.minHeight){
28123 height = Math.ceil(this.minHeight * width / this.minWidth);
28126 this.thumbEl.setStyle({
28127 width : width + 'px',
28128 height : height + 'px'
28135 setThumbBoxPosition : function()
28137 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28138 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28140 this.thumbEl.setLeft(x);
28141 this.thumbEl.setTop(y);
28145 baseRotateLevel : function()
28147 this.baseRotate = 1;
28150 typeof(this.exif) != 'undefined' &&
28151 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28152 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28154 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28157 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28161 baseScaleLevel : function()
28165 if(this.isDocument){
28167 if(this.baseRotate == 6 || this.baseRotate == 8){
28169 height = this.thumbEl.getHeight();
28170 this.baseScale = height / this.imageEl.OriginWidth;
28172 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28173 width = this.thumbEl.getWidth();
28174 this.baseScale = width / this.imageEl.OriginHeight;
28180 height = this.thumbEl.getHeight();
28181 this.baseScale = height / this.imageEl.OriginHeight;
28183 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28184 width = this.thumbEl.getWidth();
28185 this.baseScale = width / this.imageEl.OriginWidth;
28191 if(this.baseRotate == 6 || this.baseRotate == 8){
28193 width = this.thumbEl.getHeight();
28194 this.baseScale = width / this.imageEl.OriginHeight;
28196 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28197 height = this.thumbEl.getWidth();
28198 this.baseScale = height / this.imageEl.OriginHeight;
28201 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28202 height = this.thumbEl.getWidth();
28203 this.baseScale = height / this.imageEl.OriginHeight;
28205 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28206 width = this.thumbEl.getHeight();
28207 this.baseScale = width / this.imageEl.OriginWidth;
28214 width = this.thumbEl.getWidth();
28215 this.baseScale = width / this.imageEl.OriginWidth;
28217 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28218 height = this.thumbEl.getHeight();
28219 this.baseScale = height / this.imageEl.OriginHeight;
28222 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28224 height = this.thumbEl.getHeight();
28225 this.baseScale = height / this.imageEl.OriginHeight;
28227 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28228 width = this.thumbEl.getWidth();
28229 this.baseScale = width / this.imageEl.OriginWidth;
28237 getScaleLevel : function()
28239 return this.baseScale * Math.pow(1.1, this.scale);
28242 onTouchStart : function(e)
28244 if(!this.canvasLoaded){
28245 this.beforeSelectFile(e);
28249 var touches = e.browserEvent.touches;
28255 if(touches.length == 1){
28256 this.onMouseDown(e);
28260 if(touches.length != 2){
28266 for(var i = 0, finger; finger = touches[i]; i++){
28267 coords.push(finger.pageX, finger.pageY);
28270 var x = Math.pow(coords[0] - coords[2], 2);
28271 var y = Math.pow(coords[1] - coords[3], 2);
28273 this.startDistance = Math.sqrt(x + y);
28275 this.startScale = this.scale;
28277 this.pinching = true;
28278 this.dragable = false;
28282 onTouchMove : function(e)
28284 if(!this.pinching && !this.dragable){
28288 var touches = e.browserEvent.touches;
28295 this.onMouseMove(e);
28301 for(var i = 0, finger; finger = touches[i]; i++){
28302 coords.push(finger.pageX, finger.pageY);
28305 var x = Math.pow(coords[0] - coords[2], 2);
28306 var y = Math.pow(coords[1] - coords[3], 2);
28308 this.endDistance = Math.sqrt(x + y);
28310 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28312 if(!this.zoomable()){
28313 this.scale = this.startScale;
28321 onTouchEnd : function(e)
28323 this.pinching = false;
28324 this.dragable = false;
28328 process : function(file, crop)
28331 this.maskEl.mask(this.loadingText);
28334 this.xhr = new XMLHttpRequest();
28336 file.xhr = this.xhr;
28338 this.xhr.open(this.method, this.url, true);
28341 "Accept": "application/json",
28342 "Cache-Control": "no-cache",
28343 "X-Requested-With": "XMLHttpRequest"
28346 for (var headerName in headers) {
28347 var headerValue = headers[headerName];
28349 this.xhr.setRequestHeader(headerName, headerValue);
28355 this.xhr.onload = function()
28357 _this.xhrOnLoad(_this.xhr);
28360 this.xhr.onerror = function()
28362 _this.xhrOnError(_this.xhr);
28365 var formData = new FormData();
28367 formData.append('returnHTML', 'NO');
28370 formData.append('crop', crop);
28373 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28374 formData.append(this.paramName, file, file.name);
28377 if(typeof(file.filename) != 'undefined'){
28378 formData.append('filename', file.filename);
28381 if(typeof(file.mimetype) != 'undefined'){
28382 formData.append('mimetype', file.mimetype);
28385 if(this.fireEvent('arrange', this, formData) != false){
28386 this.xhr.send(formData);
28390 xhrOnLoad : function(xhr)
28393 this.maskEl.unmask();
28396 if (xhr.readyState !== 4) {
28397 this.fireEvent('exception', this, xhr);
28401 var response = Roo.decode(xhr.responseText);
28403 if(!response.success){
28404 this.fireEvent('exception', this, xhr);
28408 var response = Roo.decode(xhr.responseText);
28410 this.fireEvent('upload', this, response);
28414 xhrOnError : function()
28417 this.maskEl.unmask();
28420 Roo.log('xhr on error');
28422 var response = Roo.decode(xhr.responseText);
28428 prepare : function(file)
28431 this.maskEl.mask(this.loadingText);
28437 if(typeof(file) === 'string'){
28438 this.loadCanvas(file);
28442 if(!file || !this.urlAPI){
28447 this.cropType = file.type;
28451 if(this.fireEvent('prepare', this, this.file) != false){
28453 var reader = new FileReader();
28455 reader.onload = function (e) {
28456 if (e.target.error) {
28457 Roo.log(e.target.error);
28461 var buffer = e.target.result,
28462 dataView = new DataView(buffer),
28464 maxOffset = dataView.byteLength - 4,
28468 if (dataView.getUint16(0) === 0xffd8) {
28469 while (offset < maxOffset) {
28470 markerBytes = dataView.getUint16(offset);
28472 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28473 markerLength = dataView.getUint16(offset + 2) + 2;
28474 if (offset + markerLength > dataView.byteLength) {
28475 Roo.log('Invalid meta data: Invalid segment size.');
28479 if(markerBytes == 0xffe1){
28480 _this.parseExifData(
28487 offset += markerLength;
28497 var url = _this.urlAPI.createObjectURL(_this.file);
28499 _this.loadCanvas(url);
28504 reader.readAsArrayBuffer(this.file);
28510 parseExifData : function(dataView, offset, length)
28512 var tiffOffset = offset + 10,
28516 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28517 // No Exif data, might be XMP data instead
28521 // Check for the ASCII code for "Exif" (0x45786966):
28522 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28523 // No Exif data, might be XMP data instead
28526 if (tiffOffset + 8 > dataView.byteLength) {
28527 Roo.log('Invalid Exif data: Invalid segment size.');
28530 // Check for the two null bytes:
28531 if (dataView.getUint16(offset + 8) !== 0x0000) {
28532 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28535 // Check the byte alignment:
28536 switch (dataView.getUint16(tiffOffset)) {
28538 littleEndian = true;
28541 littleEndian = false;
28544 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28547 // Check for the TIFF tag marker (0x002A):
28548 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28549 Roo.log('Invalid Exif data: Missing TIFF marker.');
28552 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28553 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28555 this.parseExifTags(
28558 tiffOffset + dirOffset,
28563 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28568 if (dirOffset + 6 > dataView.byteLength) {
28569 Roo.log('Invalid Exif data: Invalid directory offset.');
28572 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28573 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28574 if (dirEndOffset + 4 > dataView.byteLength) {
28575 Roo.log('Invalid Exif data: Invalid directory size.');
28578 for (i = 0; i < tagsNumber; i += 1) {
28582 dirOffset + 2 + 12 * i, // tag offset
28586 // Return the offset to the next directory:
28587 return dataView.getUint32(dirEndOffset, littleEndian);
28590 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28592 var tag = dataView.getUint16(offset, littleEndian);
28594 this.exif[tag] = this.getExifValue(
28598 dataView.getUint16(offset + 2, littleEndian), // tag type
28599 dataView.getUint32(offset + 4, littleEndian), // tag length
28604 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28606 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28615 Roo.log('Invalid Exif data: Invalid tag type.');
28619 tagSize = tagType.size * length;
28620 // Determine if the value is contained in the dataOffset bytes,
28621 // or if the value at the dataOffset is a pointer to the actual data:
28622 dataOffset = tagSize > 4 ?
28623 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28624 if (dataOffset + tagSize > dataView.byteLength) {
28625 Roo.log('Invalid Exif data: Invalid data offset.');
28628 if (length === 1) {
28629 return tagType.getValue(dataView, dataOffset, littleEndian);
28632 for (i = 0; i < length; i += 1) {
28633 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28636 if (tagType.ascii) {
28638 // Concatenate the chars:
28639 for (i = 0; i < values.length; i += 1) {
28641 // Ignore the terminating NULL byte(s):
28642 if (c === '\u0000') {
28654 Roo.apply(Roo.bootstrap.UploadCropbox, {
28656 'Orientation': 0x0112
28660 1: 0, //'top-left',
28662 3: 180, //'bottom-right',
28663 // 4: 'bottom-left',
28665 6: 90, //'right-top',
28666 // 7: 'right-bottom',
28667 8: 270 //'left-bottom'
28671 // byte, 8-bit unsigned int:
28673 getValue: function (dataView, dataOffset) {
28674 return dataView.getUint8(dataOffset);
28678 // ascii, 8-bit byte:
28680 getValue: function (dataView, dataOffset) {
28681 return String.fromCharCode(dataView.getUint8(dataOffset));
28686 // short, 16 bit int:
28688 getValue: function (dataView, dataOffset, littleEndian) {
28689 return dataView.getUint16(dataOffset, littleEndian);
28693 // long, 32 bit int:
28695 getValue: function (dataView, dataOffset, littleEndian) {
28696 return dataView.getUint32(dataOffset, littleEndian);
28700 // rational = two long values, first is numerator, second is denominator:
28702 getValue: function (dataView, dataOffset, littleEndian) {
28703 return dataView.getUint32(dataOffset, littleEndian) /
28704 dataView.getUint32(dataOffset + 4, littleEndian);
28708 // slong, 32 bit signed int:
28710 getValue: function (dataView, dataOffset, littleEndian) {
28711 return dataView.getInt32(dataOffset, littleEndian);
28715 // srational, two slongs, first is numerator, second is denominator:
28717 getValue: function (dataView, dataOffset, littleEndian) {
28718 return dataView.getInt32(dataOffset, littleEndian) /
28719 dataView.getInt32(dataOffset + 4, littleEndian);
28729 cls : 'btn-group roo-upload-cropbox-rotate-left',
28730 action : 'rotate-left',
28734 cls : 'btn btn-default',
28735 html : '<i class="fa fa-undo"></i>'
28741 cls : 'btn-group roo-upload-cropbox-picture',
28742 action : 'picture',
28746 cls : 'btn btn-default',
28747 html : '<i class="fa fa-picture-o"></i>'
28753 cls : 'btn-group roo-upload-cropbox-rotate-right',
28754 action : 'rotate-right',
28758 cls : 'btn btn-default',
28759 html : '<i class="fa fa-repeat"></i>'
28767 cls : 'btn-group roo-upload-cropbox-rotate-left',
28768 action : 'rotate-left',
28772 cls : 'btn btn-default',
28773 html : '<i class="fa fa-undo"></i>'
28779 cls : 'btn-group roo-upload-cropbox-download',
28780 action : 'download',
28784 cls : 'btn btn-default',
28785 html : '<i class="fa fa-download"></i>'
28791 cls : 'btn-group roo-upload-cropbox-crop',
28796 cls : 'btn btn-default',
28797 html : '<i class="fa fa-crop"></i>'
28803 cls : 'btn-group roo-upload-cropbox-trash',
28808 cls : 'btn btn-default',
28809 html : '<i class="fa fa-trash"></i>'
28815 cls : 'btn-group roo-upload-cropbox-rotate-right',
28816 action : 'rotate-right',
28820 cls : 'btn btn-default',
28821 html : '<i class="fa fa-repeat"></i>'
28829 cls : 'btn-group roo-upload-cropbox-rotate-left',
28830 action : 'rotate-left',
28834 cls : 'btn btn-default',
28835 html : '<i class="fa fa-undo"></i>'
28841 cls : 'btn-group roo-upload-cropbox-rotate-right',
28842 action : 'rotate-right',
28846 cls : 'btn btn-default',
28847 html : '<i class="fa fa-repeat"></i>'
28860 * @class Roo.bootstrap.DocumentManager
28861 * @extends Roo.bootstrap.Component
28862 * Bootstrap DocumentManager class
28863 * @cfg {String} paramName default 'imageUpload'
28864 * @cfg {String} toolTipName default 'filename'
28865 * @cfg {String} method default POST
28866 * @cfg {String} url action url
28867 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28868 * @cfg {Boolean} multiple multiple upload default true
28869 * @cfg {Number} thumbSize default 300
28870 * @cfg {String} fieldLabel
28871 * @cfg {Number} labelWidth default 4
28872 * @cfg {String} labelAlign (left|top) default left
28873 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28874 * @cfg {Number} labellg set the width of label (1-12)
28875 * @cfg {Number} labelmd set the width of label (1-12)
28876 * @cfg {Number} labelsm set the width of label (1-12)
28877 * @cfg {Number} labelxs set the width of label (1-12)
28880 * Create a new DocumentManager
28881 * @param {Object} config The config object
28884 Roo.bootstrap.DocumentManager = function(config){
28885 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28888 this.delegates = [];
28893 * Fire when initial the DocumentManager
28894 * @param {Roo.bootstrap.DocumentManager} this
28899 * inspect selected file
28900 * @param {Roo.bootstrap.DocumentManager} this
28901 * @param {File} file
28906 * Fire when xhr load exception
28907 * @param {Roo.bootstrap.DocumentManager} this
28908 * @param {XMLHttpRequest} xhr
28910 "exception" : true,
28912 * @event afterupload
28913 * Fire when xhr load exception
28914 * @param {Roo.bootstrap.DocumentManager} this
28915 * @param {XMLHttpRequest} xhr
28917 "afterupload" : true,
28920 * prepare the form data
28921 * @param {Roo.bootstrap.DocumentManager} this
28922 * @param {Object} formData
28927 * Fire when remove the file
28928 * @param {Roo.bootstrap.DocumentManager} this
28929 * @param {Object} file
28934 * Fire after refresh the file
28935 * @param {Roo.bootstrap.DocumentManager} this
28940 * Fire after click the image
28941 * @param {Roo.bootstrap.DocumentManager} this
28942 * @param {Object} file
28947 * Fire when upload a image and editable set to true
28948 * @param {Roo.bootstrap.DocumentManager} this
28949 * @param {Object} file
28953 * @event beforeselectfile
28954 * Fire before select file
28955 * @param {Roo.bootstrap.DocumentManager} this
28957 "beforeselectfile" : true,
28960 * Fire before process file
28961 * @param {Roo.bootstrap.DocumentManager} this
28962 * @param {Object} file
28966 * @event previewrendered
28967 * Fire when preview rendered
28968 * @param {Roo.bootstrap.DocumentManager} this
28969 * @param {Object} file
28971 "previewrendered" : true,
28974 "previewResize" : true
28979 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28988 paramName : 'imageUpload',
28989 toolTipName : 'filename',
28992 labelAlign : 'left',
29002 getAutoCreate : function()
29004 var managerWidget = {
29006 cls : 'roo-document-manager',
29010 cls : 'roo-document-manager-selector',
29015 cls : 'roo-document-manager-uploader',
29019 cls : 'roo-document-manager-upload-btn',
29020 html : '<i class="fa fa-plus"></i>'
29031 cls : 'column col-md-12',
29036 if(this.fieldLabel.length){
29041 cls : 'column col-md-12',
29042 html : this.fieldLabel
29046 cls : 'column col-md-12',
29051 if(this.labelAlign == 'left'){
29056 html : this.fieldLabel
29065 if(this.labelWidth > 12){
29066 content[0].style = "width: " + this.labelWidth + 'px';
29069 if(this.labelWidth < 13 && this.labelmd == 0){
29070 this.labelmd = this.labelWidth;
29073 if(this.labellg > 0){
29074 content[0].cls += ' col-lg-' + this.labellg;
29075 content[1].cls += ' col-lg-' + (12 - this.labellg);
29078 if(this.labelmd > 0){
29079 content[0].cls += ' col-md-' + this.labelmd;
29080 content[1].cls += ' col-md-' + (12 - this.labelmd);
29083 if(this.labelsm > 0){
29084 content[0].cls += ' col-sm-' + this.labelsm;
29085 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29088 if(this.labelxs > 0){
29089 content[0].cls += ' col-xs-' + this.labelxs;
29090 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29098 cls : 'row clearfix',
29106 initEvents : function()
29108 this.managerEl = this.el.select('.roo-document-manager', true).first();
29109 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29111 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29112 this.selectorEl.hide();
29115 this.selectorEl.attr('multiple', 'multiple');
29118 this.selectorEl.on('change', this.onFileSelected, this);
29120 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29121 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29123 this.uploader.on('click', this.onUploaderClick, this);
29125 this.renderProgressDialog();
29129 window.addEventListener("resize", function() { _this.refresh(); } );
29131 this.fireEvent('initial', this);
29134 renderProgressDialog : function()
29138 this.progressDialog = new Roo.bootstrap.Modal({
29139 cls : 'roo-document-manager-progress-dialog',
29140 allow_close : false,
29150 btnclick : function() {
29151 _this.uploadCancel();
29157 this.progressDialog.render(Roo.get(document.body));
29159 this.progress = new Roo.bootstrap.Progress({
29160 cls : 'roo-document-manager-progress',
29165 this.progress.render(this.progressDialog.getChildContainer());
29167 this.progressBar = new Roo.bootstrap.ProgressBar({
29168 cls : 'roo-document-manager-progress-bar',
29171 aria_valuemax : 12,
29175 this.progressBar.render(this.progress.getChildContainer());
29178 onUploaderClick : function(e)
29180 e.preventDefault();
29182 if(this.fireEvent('beforeselectfile', this) != false){
29183 this.selectorEl.dom.click();
29188 onFileSelected : function(e)
29190 e.preventDefault();
29192 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29196 Roo.each(this.selectorEl.dom.files, function(file){
29197 if(this.fireEvent('inspect', this, file) != false){
29198 this.files.push(file);
29208 this.selectorEl.dom.value = '';
29210 if(!this.files || !this.files.length){
29214 if(this.boxes > 0 && this.files.length > this.boxes){
29215 this.files = this.files.slice(0, this.boxes);
29218 this.uploader.show();
29220 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29221 this.uploader.hide();
29230 Roo.each(this.files, function(file){
29232 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29233 var f = this.renderPreview(file);
29238 if(file.type.indexOf('image') != -1){
29239 this.delegates.push(
29241 _this.process(file);
29242 }).createDelegate(this)
29250 _this.process(file);
29251 }).createDelegate(this)
29256 this.files = files;
29258 this.delegates = this.delegates.concat(docs);
29260 if(!this.delegates.length){
29265 this.progressBar.aria_valuemax = this.delegates.length;
29272 arrange : function()
29274 if(!this.delegates.length){
29275 this.progressDialog.hide();
29280 var delegate = this.delegates.shift();
29282 this.progressDialog.show();
29284 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29286 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29291 refresh : function()
29293 this.uploader.show();
29295 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29296 this.uploader.hide();
29299 Roo.isTouch ? this.closable(false) : this.closable(true);
29301 this.fireEvent('refresh', this);
29304 onRemove : function(e, el, o)
29306 e.preventDefault();
29308 this.fireEvent('remove', this, o);
29312 remove : function(o)
29316 Roo.each(this.files, function(file){
29317 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29326 this.files = files;
29333 Roo.each(this.files, function(file){
29338 file.target.remove();
29347 onClick : function(e, el, o)
29349 e.preventDefault();
29351 this.fireEvent('click', this, o);
29355 closable : function(closable)
29357 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29359 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29371 xhrOnLoad : function(xhr)
29373 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29377 if (xhr.readyState !== 4) {
29379 this.fireEvent('exception', this, xhr);
29383 var response = Roo.decode(xhr.responseText);
29385 if(!response.success){
29387 this.fireEvent('exception', this, xhr);
29391 var file = this.renderPreview(response.data);
29393 this.files.push(file);
29397 this.fireEvent('afterupload', this, xhr);
29401 xhrOnError : function(xhr)
29403 Roo.log('xhr on error');
29405 var response = Roo.decode(xhr.responseText);
29412 process : function(file)
29414 if(this.fireEvent('process', this, file) !== false){
29415 if(this.editable && file.type.indexOf('image') != -1){
29416 this.fireEvent('edit', this, file);
29420 this.uploadStart(file, false);
29427 uploadStart : function(file, crop)
29429 this.xhr = new XMLHttpRequest();
29431 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29436 file.xhr = this.xhr;
29438 this.managerEl.createChild({
29440 cls : 'roo-document-manager-loading',
29444 tooltip : file.name,
29445 cls : 'roo-document-manager-thumb',
29446 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29452 this.xhr.open(this.method, this.url, true);
29455 "Accept": "application/json",
29456 "Cache-Control": "no-cache",
29457 "X-Requested-With": "XMLHttpRequest"
29460 for (var headerName in headers) {
29461 var headerValue = headers[headerName];
29463 this.xhr.setRequestHeader(headerName, headerValue);
29469 this.xhr.onload = function()
29471 _this.xhrOnLoad(_this.xhr);
29474 this.xhr.onerror = function()
29476 _this.xhrOnError(_this.xhr);
29479 var formData = new FormData();
29481 formData.append('returnHTML', 'NO');
29484 formData.append('crop', crop);
29487 formData.append(this.paramName, file, file.name);
29494 if(this.fireEvent('prepare', this, formData, options) != false){
29496 if(options.manually){
29500 this.xhr.send(formData);
29504 this.uploadCancel();
29507 uploadCancel : function()
29513 this.delegates = [];
29515 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29522 renderPreview : function(file)
29524 if(typeof(file.target) != 'undefined' && file.target){
29528 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29530 var previewEl = this.managerEl.createChild({
29532 cls : 'roo-document-manager-preview',
29536 tooltip : file[this.toolTipName],
29537 cls : 'roo-document-manager-thumb',
29538 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29543 html : '<i class="fa fa-times-circle"></i>'
29548 var close = previewEl.select('button.close', true).first();
29550 close.on('click', this.onRemove, this, file);
29552 file.target = previewEl;
29554 var image = previewEl.select('img', true).first();
29558 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29560 image.on('click', this.onClick, this, file);
29562 this.fireEvent('previewrendered', this, file);
29568 onPreviewLoad : function(file, image)
29570 if(typeof(file.target) == 'undefined' || !file.target){
29574 var width = image.dom.naturalWidth || image.dom.width;
29575 var height = image.dom.naturalHeight || image.dom.height;
29577 if(!this.previewResize) {
29581 if(width > height){
29582 file.target.addClass('wide');
29586 file.target.addClass('tall');
29591 uploadFromSource : function(file, crop)
29593 this.xhr = new XMLHttpRequest();
29595 this.managerEl.createChild({
29597 cls : 'roo-document-manager-loading',
29601 tooltip : file.name,
29602 cls : 'roo-document-manager-thumb',
29603 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29609 this.xhr.open(this.method, this.url, true);
29612 "Accept": "application/json",
29613 "Cache-Control": "no-cache",
29614 "X-Requested-With": "XMLHttpRequest"
29617 for (var headerName in headers) {
29618 var headerValue = headers[headerName];
29620 this.xhr.setRequestHeader(headerName, headerValue);
29626 this.xhr.onload = function()
29628 _this.xhrOnLoad(_this.xhr);
29631 this.xhr.onerror = function()
29633 _this.xhrOnError(_this.xhr);
29636 var formData = new FormData();
29638 formData.append('returnHTML', 'NO');
29640 formData.append('crop', crop);
29642 if(typeof(file.filename) != 'undefined'){
29643 formData.append('filename', file.filename);
29646 if(typeof(file.mimetype) != 'undefined'){
29647 formData.append('mimetype', file.mimetype);
29652 if(this.fireEvent('prepare', this, formData) != false){
29653 this.xhr.send(formData);
29663 * @class Roo.bootstrap.DocumentViewer
29664 * @extends Roo.bootstrap.Component
29665 * Bootstrap DocumentViewer class
29666 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29667 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29670 * Create a new DocumentViewer
29671 * @param {Object} config The config object
29674 Roo.bootstrap.DocumentViewer = function(config){
29675 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29680 * Fire after initEvent
29681 * @param {Roo.bootstrap.DocumentViewer} this
29687 * @param {Roo.bootstrap.DocumentViewer} this
29692 * Fire after download button
29693 * @param {Roo.bootstrap.DocumentViewer} this
29698 * Fire after trash button
29699 * @param {Roo.bootstrap.DocumentViewer} this
29706 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29708 showDownload : true,
29712 getAutoCreate : function()
29716 cls : 'roo-document-viewer',
29720 cls : 'roo-document-viewer-body',
29724 cls : 'roo-document-viewer-thumb',
29728 cls : 'roo-document-viewer-image'
29736 cls : 'roo-document-viewer-footer',
29739 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29743 cls : 'btn-group roo-document-viewer-download',
29747 cls : 'btn btn-default',
29748 html : '<i class="fa fa-download"></i>'
29754 cls : 'btn-group roo-document-viewer-trash',
29758 cls : 'btn btn-default',
29759 html : '<i class="fa fa-trash"></i>'
29772 initEvents : function()
29774 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29775 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29777 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29778 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29780 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29781 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29783 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29784 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29786 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29787 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29789 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29790 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29792 this.bodyEl.on('click', this.onClick, this);
29793 this.downloadBtn.on('click', this.onDownload, this);
29794 this.trashBtn.on('click', this.onTrash, this);
29796 this.downloadBtn.hide();
29797 this.trashBtn.hide();
29799 if(this.showDownload){
29800 this.downloadBtn.show();
29803 if(this.showTrash){
29804 this.trashBtn.show();
29807 if(!this.showDownload && !this.showTrash) {
29808 this.footerEl.hide();
29813 initial : function()
29815 this.fireEvent('initial', this);
29819 onClick : function(e)
29821 e.preventDefault();
29823 this.fireEvent('click', this);
29826 onDownload : function(e)
29828 e.preventDefault();
29830 this.fireEvent('download', this);
29833 onTrash : function(e)
29835 e.preventDefault();
29837 this.fireEvent('trash', this);
29849 * @class Roo.bootstrap.NavProgressBar
29850 * @extends Roo.bootstrap.Component
29851 * Bootstrap NavProgressBar class
29854 * Create a new nav progress bar
29855 * @param {Object} config The config object
29858 Roo.bootstrap.NavProgressBar = function(config){
29859 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29861 this.bullets = this.bullets || [];
29863 // Roo.bootstrap.NavProgressBar.register(this);
29867 * Fires when the active item changes
29868 * @param {Roo.bootstrap.NavProgressBar} this
29869 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29870 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29877 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29882 getAutoCreate : function()
29884 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29888 cls : 'roo-navigation-bar-group',
29892 cls : 'roo-navigation-top-bar'
29896 cls : 'roo-navigation-bullets-bar',
29900 cls : 'roo-navigation-bar'
29907 cls : 'roo-navigation-bottom-bar'
29917 initEvents: function()
29922 onRender : function(ct, position)
29924 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29926 if(this.bullets.length){
29927 Roo.each(this.bullets, function(b){
29936 addItem : function(cfg)
29938 var item = new Roo.bootstrap.NavProgressItem(cfg);
29940 item.parentId = this.id;
29941 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29944 var top = new Roo.bootstrap.Element({
29946 cls : 'roo-navigation-bar-text'
29949 var bottom = new Roo.bootstrap.Element({
29951 cls : 'roo-navigation-bar-text'
29954 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29955 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29957 var topText = new Roo.bootstrap.Element({
29959 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29962 var bottomText = new Roo.bootstrap.Element({
29964 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29967 topText.onRender(top.el, null);
29968 bottomText.onRender(bottom.el, null);
29971 item.bottomEl = bottom;
29974 this.barItems.push(item);
29979 getActive : function()
29981 var active = false;
29983 Roo.each(this.barItems, function(v){
29985 if (!v.isActive()) {
29997 setActiveItem : function(item)
30001 Roo.each(this.barItems, function(v){
30002 if (v.rid == item.rid) {
30006 if (v.isActive()) {
30007 v.setActive(false);
30012 item.setActive(true);
30014 this.fireEvent('changed', this, item, prev);
30017 getBarItem: function(rid)
30021 Roo.each(this.barItems, function(e) {
30022 if (e.rid != rid) {
30033 indexOfItem : function(item)
30037 Roo.each(this.barItems, function(v, i){
30039 if (v.rid != item.rid) {
30050 setActiveNext : function()
30052 var i = this.indexOfItem(this.getActive());
30054 if (i > this.barItems.length) {
30058 this.setActiveItem(this.barItems[i+1]);
30061 setActivePrev : function()
30063 var i = this.indexOfItem(this.getActive());
30069 this.setActiveItem(this.barItems[i-1]);
30072 format : function()
30074 if(!this.barItems.length){
30078 var width = 100 / this.barItems.length;
30080 Roo.each(this.barItems, function(i){
30081 i.el.setStyle('width', width + '%');
30082 i.topEl.el.setStyle('width', width + '%');
30083 i.bottomEl.el.setStyle('width', width + '%');
30092 * Nav Progress Item
30097 * @class Roo.bootstrap.NavProgressItem
30098 * @extends Roo.bootstrap.Component
30099 * Bootstrap NavProgressItem class
30100 * @cfg {String} rid the reference id
30101 * @cfg {Boolean} active (true|false) Is item active default false
30102 * @cfg {Boolean} disabled (true|false) Is item active default false
30103 * @cfg {String} html
30104 * @cfg {String} position (top|bottom) text position default bottom
30105 * @cfg {String} icon show icon instead of number
30108 * Create a new NavProgressItem
30109 * @param {Object} config The config object
30111 Roo.bootstrap.NavProgressItem = function(config){
30112 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30117 * The raw click event for the entire grid.
30118 * @param {Roo.bootstrap.NavProgressItem} this
30119 * @param {Roo.EventObject} e
30126 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30132 position : 'bottom',
30135 getAutoCreate : function()
30137 var iconCls = 'roo-navigation-bar-item-icon';
30139 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30143 cls: 'roo-navigation-bar-item',
30153 cfg.cls += ' active';
30156 cfg.cls += ' disabled';
30162 disable : function()
30164 this.setDisabled(true);
30167 enable : function()
30169 this.setDisabled(false);
30172 initEvents: function()
30174 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30176 this.iconEl.on('click', this.onClick, this);
30179 onClick : function(e)
30181 e.preventDefault();
30187 if(this.fireEvent('click', this, e) === false){
30191 this.parent().setActiveItem(this);
30194 isActive: function ()
30196 return this.active;
30199 setActive : function(state)
30201 if(this.active == state){
30205 this.active = state;
30208 this.el.addClass('active');
30212 this.el.removeClass('active');
30217 setDisabled : function(state)
30219 if(this.disabled == state){
30223 this.disabled = state;
30226 this.el.addClass('disabled');
30230 this.el.removeClass('disabled');
30233 tooltipEl : function()
30235 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30248 * @class Roo.bootstrap.FieldLabel
30249 * @extends Roo.bootstrap.Component
30250 * Bootstrap FieldLabel class
30251 * @cfg {String} html contents of the element
30252 * @cfg {String} tag tag of the element default label
30253 * @cfg {String} cls class of the element
30254 * @cfg {String} target label target
30255 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30256 * @cfg {String} invalidClass default "text-warning"
30257 * @cfg {String} validClass default "text-success"
30258 * @cfg {String} iconTooltip default "This field is required"
30259 * @cfg {String} indicatorpos (left|right) default left
30262 * Create a new FieldLabel
30263 * @param {Object} config The config object
30266 Roo.bootstrap.FieldLabel = function(config){
30267 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30272 * Fires after the field has been marked as invalid.
30273 * @param {Roo.form.FieldLabel} this
30274 * @param {String} msg The validation message
30279 * Fires after the field has been validated with no errors.
30280 * @param {Roo.form.FieldLabel} this
30286 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30293 invalidClass : 'has-warning',
30294 validClass : 'has-success',
30295 iconTooltip : 'This field is required',
30296 indicatorpos : 'left',
30298 getAutoCreate : function(){
30301 if (!this.allowBlank) {
30307 cls : 'roo-bootstrap-field-label ' + this.cls,
30312 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30313 tooltip : this.iconTooltip
30322 if(this.indicatorpos == 'right'){
30325 cls : 'roo-bootstrap-field-label ' + this.cls,
30334 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30335 tooltip : this.iconTooltip
30344 initEvents: function()
30346 Roo.bootstrap.Element.superclass.initEvents.call(this);
30348 this.indicator = this.indicatorEl();
30350 if(this.indicator){
30351 this.indicator.removeClass('visible');
30352 this.indicator.addClass('invisible');
30355 Roo.bootstrap.FieldLabel.register(this);
30358 indicatorEl : function()
30360 var indicator = this.el.select('i.roo-required-indicator',true).first();
30371 * Mark this field as valid
30373 markValid : function()
30375 if(this.indicator){
30376 this.indicator.removeClass('visible');
30377 this.indicator.addClass('invisible');
30380 this.el.removeClass(this.invalidClass);
30382 this.el.addClass(this.validClass);
30384 this.fireEvent('valid', this);
30388 * Mark this field as invalid
30389 * @param {String} msg The validation message
30391 markInvalid : function(msg)
30393 if(this.indicator){
30394 this.indicator.removeClass('invisible');
30395 this.indicator.addClass('visible');
30398 this.el.removeClass(this.validClass);
30400 this.el.addClass(this.invalidClass);
30402 this.fireEvent('invalid', this, msg);
30408 Roo.apply(Roo.bootstrap.FieldLabel, {
30413 * register a FieldLabel Group
30414 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30416 register : function(label)
30418 if(this.groups.hasOwnProperty(label.target)){
30422 this.groups[label.target] = label;
30426 * fetch a FieldLabel Group based on the target
30427 * @param {string} target
30428 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30430 get: function(target) {
30431 if (typeof(this.groups[target]) == 'undefined') {
30435 return this.groups[target] ;
30444 * page DateSplitField.
30450 * @class Roo.bootstrap.DateSplitField
30451 * @extends Roo.bootstrap.Component
30452 * Bootstrap DateSplitField class
30453 * @cfg {string} fieldLabel - the label associated
30454 * @cfg {Number} labelWidth set the width of label (0-12)
30455 * @cfg {String} labelAlign (top|left)
30456 * @cfg {Boolean} dayAllowBlank (true|false) default false
30457 * @cfg {Boolean} monthAllowBlank (true|false) default false
30458 * @cfg {Boolean} yearAllowBlank (true|false) default false
30459 * @cfg {string} dayPlaceholder
30460 * @cfg {string} monthPlaceholder
30461 * @cfg {string} yearPlaceholder
30462 * @cfg {string} dayFormat default 'd'
30463 * @cfg {string} monthFormat default 'm'
30464 * @cfg {string} yearFormat default 'Y'
30465 * @cfg {Number} labellg set the width of label (1-12)
30466 * @cfg {Number} labelmd set the width of label (1-12)
30467 * @cfg {Number} labelsm set the width of label (1-12)
30468 * @cfg {Number} labelxs set the width of label (1-12)
30472 * Create a new DateSplitField
30473 * @param {Object} config The config object
30476 Roo.bootstrap.DateSplitField = function(config){
30477 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30483 * getting the data of years
30484 * @param {Roo.bootstrap.DateSplitField} this
30485 * @param {Object} years
30490 * getting the data of days
30491 * @param {Roo.bootstrap.DateSplitField} this
30492 * @param {Object} days
30497 * Fires after the field has been marked as invalid.
30498 * @param {Roo.form.Field} this
30499 * @param {String} msg The validation message
30504 * Fires after the field has been validated with no errors.
30505 * @param {Roo.form.Field} this
30511 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30514 labelAlign : 'top',
30516 dayAllowBlank : false,
30517 monthAllowBlank : false,
30518 yearAllowBlank : false,
30519 dayPlaceholder : '',
30520 monthPlaceholder : '',
30521 yearPlaceholder : '',
30525 isFormField : true,
30531 getAutoCreate : function()
30535 cls : 'row roo-date-split-field-group',
30540 cls : 'form-hidden-field roo-date-split-field-group-value',
30546 var labelCls = 'col-md-12';
30547 var contentCls = 'col-md-4';
30549 if(this.fieldLabel){
30553 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30557 html : this.fieldLabel
30562 if(this.labelAlign == 'left'){
30564 if(this.labelWidth > 12){
30565 label.style = "width: " + this.labelWidth + 'px';
30568 if(this.labelWidth < 13 && this.labelmd == 0){
30569 this.labelmd = this.labelWidth;
30572 if(this.labellg > 0){
30573 labelCls = ' col-lg-' + this.labellg;
30574 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30577 if(this.labelmd > 0){
30578 labelCls = ' col-md-' + this.labelmd;
30579 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30582 if(this.labelsm > 0){
30583 labelCls = ' col-sm-' + this.labelsm;
30584 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30587 if(this.labelxs > 0){
30588 labelCls = ' col-xs-' + this.labelxs;
30589 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30593 label.cls += ' ' + labelCls;
30595 cfg.cn.push(label);
30598 Roo.each(['day', 'month', 'year'], function(t){
30601 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30608 inputEl: function ()
30610 return this.el.select('.roo-date-split-field-group-value', true).first();
30613 onRender : function(ct, position)
30617 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30619 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30621 this.dayField = new Roo.bootstrap.ComboBox({
30622 allowBlank : this.dayAllowBlank,
30623 alwaysQuery : true,
30624 displayField : 'value',
30627 forceSelection : true,
30629 placeholder : this.dayPlaceholder,
30630 selectOnFocus : true,
30631 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30632 triggerAction : 'all',
30634 valueField : 'value',
30635 store : new Roo.data.SimpleStore({
30636 data : (function() {
30638 _this.fireEvent('days', _this, days);
30641 fields : [ 'value' ]
30644 select : function (_self, record, index)
30646 _this.setValue(_this.getValue());
30651 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30653 this.monthField = new Roo.bootstrap.MonthField({
30654 after : '<i class=\"fa fa-calendar\"></i>',
30655 allowBlank : this.monthAllowBlank,
30656 placeholder : this.monthPlaceholder,
30659 render : function (_self)
30661 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30662 e.preventDefault();
30666 select : function (_self, oldvalue, newvalue)
30668 _this.setValue(_this.getValue());
30673 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30675 this.yearField = new Roo.bootstrap.ComboBox({
30676 allowBlank : this.yearAllowBlank,
30677 alwaysQuery : true,
30678 displayField : 'value',
30681 forceSelection : true,
30683 placeholder : this.yearPlaceholder,
30684 selectOnFocus : true,
30685 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30686 triggerAction : 'all',
30688 valueField : 'value',
30689 store : new Roo.data.SimpleStore({
30690 data : (function() {
30692 _this.fireEvent('years', _this, years);
30695 fields : [ 'value' ]
30698 select : function (_self, record, index)
30700 _this.setValue(_this.getValue());
30705 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30708 setValue : function(v, format)
30710 this.inputEl.dom.value = v;
30712 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30714 var d = Date.parseDate(v, f);
30721 this.setDay(d.format(this.dayFormat));
30722 this.setMonth(d.format(this.monthFormat));
30723 this.setYear(d.format(this.yearFormat));
30730 setDay : function(v)
30732 this.dayField.setValue(v);
30733 this.inputEl.dom.value = this.getValue();
30738 setMonth : function(v)
30740 this.monthField.setValue(v, true);
30741 this.inputEl.dom.value = this.getValue();
30746 setYear : function(v)
30748 this.yearField.setValue(v);
30749 this.inputEl.dom.value = this.getValue();
30754 getDay : function()
30756 return this.dayField.getValue();
30759 getMonth : function()
30761 return this.monthField.getValue();
30764 getYear : function()
30766 return this.yearField.getValue();
30769 getValue : function()
30771 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30773 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30783 this.inputEl.dom.value = '';
30788 validate : function()
30790 var d = this.dayField.validate();
30791 var m = this.monthField.validate();
30792 var y = this.yearField.validate();
30797 (!this.dayAllowBlank && !d) ||
30798 (!this.monthAllowBlank && !m) ||
30799 (!this.yearAllowBlank && !y)
30804 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30813 this.markInvalid();
30818 markValid : function()
30821 var label = this.el.select('label', true).first();
30822 var icon = this.el.select('i.fa-star', true).first();
30828 this.fireEvent('valid', this);
30832 * Mark this field as invalid
30833 * @param {String} msg The validation message
30835 markInvalid : function(msg)
30838 var label = this.el.select('label', true).first();
30839 var icon = this.el.select('i.fa-star', true).first();
30841 if(label && !icon){
30842 this.el.select('.roo-date-split-field-label', true).createChild({
30844 cls : 'text-danger fa fa-lg fa-star',
30845 tooltip : 'This field is required',
30846 style : 'margin-right:5px;'
30850 this.fireEvent('invalid', this, msg);
30853 clearInvalid : function()
30855 var label = this.el.select('label', true).first();
30856 var icon = this.el.select('i.fa-star', true).first();
30862 this.fireEvent('valid', this);
30865 getName: function()
30875 * http://masonry.desandro.com
30877 * The idea is to render all the bricks based on vertical width...
30879 * The original code extends 'outlayer' - we might need to use that....
30885 * @class Roo.bootstrap.LayoutMasonry
30886 * @extends Roo.bootstrap.Component
30887 * Bootstrap Layout Masonry class
30890 * Create a new Element
30891 * @param {Object} config The config object
30894 Roo.bootstrap.LayoutMasonry = function(config){
30896 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30900 Roo.bootstrap.LayoutMasonry.register(this);
30906 * Fire after layout the items
30907 * @param {Roo.bootstrap.LayoutMasonry} this
30908 * @param {Roo.EventObject} e
30915 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30918 * @cfg {Boolean} isLayoutInstant = no animation?
30920 isLayoutInstant : false, // needed?
30923 * @cfg {Number} boxWidth width of the columns
30928 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30933 * @cfg {Number} padWidth padding below box..
30938 * @cfg {Number} gutter gutter width..
30943 * @cfg {Number} maxCols maximum number of columns
30949 * @cfg {Boolean} isAutoInitial defalut true
30951 isAutoInitial : true,
30956 * @cfg {Boolean} isHorizontal defalut false
30958 isHorizontal : false,
30960 currentSize : null,
30966 bricks: null, //CompositeElement
30970 _isLayoutInited : false,
30972 // isAlternative : false, // only use for vertical layout...
30975 * @cfg {Number} alternativePadWidth padding below box..
30977 alternativePadWidth : 50,
30979 selectedBrick : [],
30981 getAutoCreate : function(){
30983 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30987 cls: 'blog-masonary-wrapper ' + this.cls,
30989 cls : 'mas-boxes masonary'
30996 getChildContainer: function( )
30998 if (this.boxesEl) {
30999 return this.boxesEl;
31002 this.boxesEl = this.el.select('.mas-boxes').first();
31004 return this.boxesEl;
31008 initEvents : function()
31012 if(this.isAutoInitial){
31013 Roo.log('hook children rendered');
31014 this.on('childrenrendered', function() {
31015 Roo.log('children rendered');
31021 initial : function()
31023 this.selectedBrick = [];
31025 this.currentSize = this.el.getBox(true);
31027 Roo.EventManager.onWindowResize(this.resize, this);
31029 if(!this.isAutoInitial){
31037 //this.layout.defer(500,this);
31041 resize : function()
31043 var cs = this.el.getBox(true);
31046 this.currentSize.width == cs.width &&
31047 this.currentSize.x == cs.x &&
31048 this.currentSize.height == cs.height &&
31049 this.currentSize.y == cs.y
31051 Roo.log("no change in with or X or Y");
31055 this.currentSize = cs;
31061 layout : function()
31063 this._resetLayout();
31065 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31067 this.layoutItems( isInstant );
31069 this._isLayoutInited = true;
31071 this.fireEvent('layout', this);
31075 _resetLayout : function()
31077 if(this.isHorizontal){
31078 this.horizontalMeasureColumns();
31082 this.verticalMeasureColumns();
31086 verticalMeasureColumns : function()
31088 this.getContainerWidth();
31090 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31091 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31095 var boxWidth = this.boxWidth + this.padWidth;
31097 if(this.containerWidth < this.boxWidth){
31098 boxWidth = this.containerWidth
31101 var containerWidth = this.containerWidth;
31103 var cols = Math.floor(containerWidth / boxWidth);
31105 this.cols = Math.max( cols, 1 );
31107 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31109 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31111 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31113 this.colWidth = boxWidth + avail - this.padWidth;
31115 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31116 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31119 horizontalMeasureColumns : function()
31121 this.getContainerWidth();
31123 var boxWidth = this.boxWidth;
31125 if(this.containerWidth < boxWidth){
31126 boxWidth = this.containerWidth;
31129 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31131 this.el.setHeight(boxWidth);
31135 getContainerWidth : function()
31137 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31140 layoutItems : function( isInstant )
31142 Roo.log(this.bricks);
31144 var items = Roo.apply([], this.bricks);
31146 if(this.isHorizontal){
31147 this._horizontalLayoutItems( items , isInstant );
31151 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31152 // this._verticalAlternativeLayoutItems( items , isInstant );
31156 this._verticalLayoutItems( items , isInstant );
31160 _verticalLayoutItems : function ( items , isInstant)
31162 if ( !items || !items.length ) {
31167 ['xs', 'xs', 'xs', 'tall'],
31168 ['xs', 'xs', 'tall'],
31169 ['xs', 'xs', 'sm'],
31170 ['xs', 'xs', 'xs'],
31176 ['sm', 'xs', 'xs'],
31180 ['tall', 'xs', 'xs', 'xs'],
31181 ['tall', 'xs', 'xs'],
31193 Roo.each(items, function(item, k){
31195 switch (item.size) {
31196 // these layouts take up a full box,
31207 boxes.push([item]);
31230 var filterPattern = function(box, length)
31238 var pattern = box.slice(0, length);
31242 Roo.each(pattern, function(i){
31243 format.push(i.size);
31246 Roo.each(standard, function(s){
31248 if(String(s) != String(format)){
31257 if(!match && length == 1){
31262 filterPattern(box, length - 1);
31266 queue.push(pattern);
31268 box = box.slice(length, box.length);
31270 filterPattern(box, 4);
31276 Roo.each(boxes, function(box, k){
31282 if(box.length == 1){
31287 filterPattern(box, 4);
31291 this._processVerticalLayoutQueue( queue, isInstant );
31295 // _verticalAlternativeLayoutItems : function( items , isInstant )
31297 // if ( !items || !items.length ) {
31301 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31305 _horizontalLayoutItems : function ( items , isInstant)
31307 if ( !items || !items.length || items.length < 3) {
31313 var eItems = items.slice(0, 3);
31315 items = items.slice(3, items.length);
31318 ['xs', 'xs', 'xs', 'wide'],
31319 ['xs', 'xs', 'wide'],
31320 ['xs', 'xs', 'sm'],
31321 ['xs', 'xs', 'xs'],
31327 ['sm', 'xs', 'xs'],
31331 ['wide', 'xs', 'xs', 'xs'],
31332 ['wide', 'xs', 'xs'],
31345 Roo.each(items, function(item, k){
31347 switch (item.size) {
31358 boxes.push([item]);
31382 var filterPattern = function(box, length)
31390 var pattern = box.slice(0, length);
31394 Roo.each(pattern, function(i){
31395 format.push(i.size);
31398 Roo.each(standard, function(s){
31400 if(String(s) != String(format)){
31409 if(!match && length == 1){
31414 filterPattern(box, length - 1);
31418 queue.push(pattern);
31420 box = box.slice(length, box.length);
31422 filterPattern(box, 4);
31428 Roo.each(boxes, function(box, k){
31434 if(box.length == 1){
31439 filterPattern(box, 4);
31446 var pos = this.el.getBox(true);
31450 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31452 var hit_end = false;
31454 Roo.each(queue, function(box){
31458 Roo.each(box, function(b){
31460 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31470 Roo.each(box, function(b){
31472 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31475 mx = Math.max(mx, b.x);
31479 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31483 Roo.each(box, function(b){
31485 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31499 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31502 /** Sets position of item in DOM
31503 * @param {Element} item
31504 * @param {Number} x - horizontal position
31505 * @param {Number} y - vertical position
31506 * @param {Boolean} isInstant - disables transitions
31508 _processVerticalLayoutQueue : function( queue, isInstant )
31510 var pos = this.el.getBox(true);
31515 for (var i = 0; i < this.cols; i++){
31519 Roo.each(queue, function(box, k){
31521 var col = k % this.cols;
31523 Roo.each(box, function(b,kk){
31525 b.el.position('absolute');
31527 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31528 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31530 if(b.size == 'md-left' || b.size == 'md-right'){
31531 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31532 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31535 b.el.setWidth(width);
31536 b.el.setHeight(height);
31538 b.el.select('iframe',true).setSize(width,height);
31542 for (var i = 0; i < this.cols; i++){
31544 if(maxY[i] < maxY[col]){
31549 col = Math.min(col, i);
31553 x = pos.x + col * (this.colWidth + this.padWidth);
31557 var positions = [];
31559 switch (box.length){
31561 positions = this.getVerticalOneBoxColPositions(x, y, box);
31564 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31567 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31570 positions = this.getVerticalFourBoxColPositions(x, y, box);
31576 Roo.each(box, function(b,kk){
31578 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31580 var sz = b.el.getSize();
31582 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31590 for (var i = 0; i < this.cols; i++){
31591 mY = Math.max(mY, maxY[i]);
31594 this.el.setHeight(mY - pos.y);
31598 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31600 // var pos = this.el.getBox(true);
31603 // var maxX = pos.right;
31605 // var maxHeight = 0;
31607 // Roo.each(items, function(item, k){
31611 // item.el.position('absolute');
31613 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31615 // item.el.setWidth(width);
31617 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31619 // item.el.setHeight(height);
31622 // item.el.setXY([x, y], isInstant ? false : true);
31624 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31627 // y = y + height + this.alternativePadWidth;
31629 // maxHeight = maxHeight + height + this.alternativePadWidth;
31633 // this.el.setHeight(maxHeight);
31637 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31639 var pos = this.el.getBox(true);
31644 var maxX = pos.right;
31646 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31648 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31650 Roo.each(queue, function(box, k){
31652 Roo.each(box, function(b, kk){
31654 b.el.position('absolute');
31656 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31657 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31659 if(b.size == 'md-left' || b.size == 'md-right'){
31660 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31661 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31664 b.el.setWidth(width);
31665 b.el.setHeight(height);
31673 var positions = [];
31675 switch (box.length){
31677 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31680 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31683 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31686 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31692 Roo.each(box, function(b,kk){
31694 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31696 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31704 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31706 Roo.each(eItems, function(b,k){
31708 b.size = (k == 0) ? 'sm' : 'xs';
31709 b.x = (k == 0) ? 2 : 1;
31710 b.y = (k == 0) ? 2 : 1;
31712 b.el.position('absolute');
31714 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31716 b.el.setWidth(width);
31718 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31720 b.el.setHeight(height);
31724 var positions = [];
31727 x : maxX - this.unitWidth * 2 - this.gutter,
31732 x : maxX - this.unitWidth,
31733 y : minY + (this.unitWidth + this.gutter) * 2
31737 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31741 Roo.each(eItems, function(b,k){
31743 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31749 getVerticalOneBoxColPositions : function(x, y, box)
31753 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31755 if(box[0].size == 'md-left'){
31759 if(box[0].size == 'md-right'){
31764 x : x + (this.unitWidth + this.gutter) * rand,
31771 getVerticalTwoBoxColPositions : function(x, y, box)
31775 if(box[0].size == 'xs'){
31779 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31783 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31797 x : x + (this.unitWidth + this.gutter) * 2,
31798 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31805 getVerticalThreeBoxColPositions : function(x, y, box)
31809 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31817 x : x + (this.unitWidth + this.gutter) * 1,
31822 x : x + (this.unitWidth + this.gutter) * 2,
31830 if(box[0].size == 'xs' && box[1].size == 'xs'){
31839 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31843 x : x + (this.unitWidth + this.gutter) * 1,
31857 x : x + (this.unitWidth + this.gutter) * 2,
31862 x : x + (this.unitWidth + this.gutter) * 2,
31863 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31870 getVerticalFourBoxColPositions : function(x, y, box)
31874 if(box[0].size == 'xs'){
31883 y : y + (this.unitHeight + this.gutter) * 1
31888 y : y + (this.unitHeight + this.gutter) * 2
31892 x : x + (this.unitWidth + this.gutter) * 1,
31906 x : x + (this.unitWidth + this.gutter) * 2,
31911 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31912 y : y + (this.unitHeight + this.gutter) * 1
31916 x : x + (this.unitWidth + this.gutter) * 2,
31917 y : y + (this.unitWidth + this.gutter) * 2
31924 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31928 if(box[0].size == 'md-left'){
31930 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31937 if(box[0].size == 'md-right'){
31939 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31940 y : minY + (this.unitWidth + this.gutter) * 1
31946 var rand = Math.floor(Math.random() * (4 - box[0].y));
31949 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31950 y : minY + (this.unitWidth + this.gutter) * rand
31957 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31961 if(box[0].size == 'xs'){
31964 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31969 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31970 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31978 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31983 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31984 y : minY + (this.unitWidth + this.gutter) * 2
31991 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31995 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31998 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32003 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32004 y : minY + (this.unitWidth + this.gutter) * 1
32008 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32009 y : minY + (this.unitWidth + this.gutter) * 2
32016 if(box[0].size == 'xs' && box[1].size == 'xs'){
32019 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32024 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32029 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32030 y : minY + (this.unitWidth + this.gutter) * 1
32038 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32043 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32044 y : minY + (this.unitWidth + this.gutter) * 2
32048 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32049 y : minY + (this.unitWidth + this.gutter) * 2
32056 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32060 if(box[0].size == 'xs'){
32063 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32068 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32073 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),
32078 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32079 y : minY + (this.unitWidth + this.gutter) * 1
32087 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32092 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32093 y : minY + (this.unitWidth + this.gutter) * 2
32097 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32098 y : minY + (this.unitWidth + this.gutter) * 2
32102 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),
32103 y : minY + (this.unitWidth + this.gutter) * 2
32111 * remove a Masonry Brick
32112 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32114 removeBrick : function(brick_id)
32120 for (var i = 0; i<this.bricks.length; i++) {
32121 if (this.bricks[i].id == brick_id) {
32122 this.bricks.splice(i,1);
32123 this.el.dom.removeChild(Roo.get(brick_id).dom);
32130 * adds a Masonry Brick
32131 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32133 addBrick : function(cfg)
32135 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32136 //this.register(cn);
32137 cn.parentId = this.id;
32138 cn.render(this.el);
32143 * register a Masonry Brick
32144 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32147 register : function(brick)
32149 this.bricks.push(brick);
32150 brick.masonryId = this.id;
32154 * clear all the Masonry Brick
32156 clearAll : function()
32159 //this.getChildContainer().dom.innerHTML = "";
32160 this.el.dom.innerHTML = '';
32163 getSelected : function()
32165 if (!this.selectedBrick) {
32169 return this.selectedBrick;
32173 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32177 * register a Masonry Layout
32178 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32181 register : function(layout)
32183 this.groups[layout.id] = layout;
32186 * fetch a Masonry Layout based on the masonry layout ID
32187 * @param {string} the masonry layout to add
32188 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32191 get: function(layout_id) {
32192 if (typeof(this.groups[layout_id]) == 'undefined') {
32195 return this.groups[layout_id] ;
32207 * http://masonry.desandro.com
32209 * The idea is to render all the bricks based on vertical width...
32211 * The original code extends 'outlayer' - we might need to use that....
32217 * @class Roo.bootstrap.LayoutMasonryAuto
32218 * @extends Roo.bootstrap.Component
32219 * Bootstrap Layout Masonry class
32222 * Create a new Element
32223 * @param {Object} config The config object
32226 Roo.bootstrap.LayoutMasonryAuto = function(config){
32227 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32230 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32233 * @cfg {Boolean} isFitWidth - resize the width..
32235 isFitWidth : false, // options..
32237 * @cfg {Boolean} isOriginLeft = left align?
32239 isOriginLeft : true,
32241 * @cfg {Boolean} isOriginTop = top align?
32243 isOriginTop : false,
32245 * @cfg {Boolean} isLayoutInstant = no animation?
32247 isLayoutInstant : false, // needed?
32249 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32251 isResizingContainer : true,
32253 * @cfg {Number} columnWidth width of the columns
32259 * @cfg {Number} maxCols maximum number of columns
32264 * @cfg {Number} padHeight padding below box..
32270 * @cfg {Boolean} isAutoInitial defalut true
32273 isAutoInitial : true,
32279 initialColumnWidth : 0,
32280 currentSize : null,
32282 colYs : null, // array.
32289 bricks: null, //CompositeElement
32290 cols : 0, // array?
32291 // element : null, // wrapped now this.el
32292 _isLayoutInited : null,
32295 getAutoCreate : function(){
32299 cls: 'blog-masonary-wrapper ' + this.cls,
32301 cls : 'mas-boxes masonary'
32308 getChildContainer: function( )
32310 if (this.boxesEl) {
32311 return this.boxesEl;
32314 this.boxesEl = this.el.select('.mas-boxes').first();
32316 return this.boxesEl;
32320 initEvents : function()
32324 if(this.isAutoInitial){
32325 Roo.log('hook children rendered');
32326 this.on('childrenrendered', function() {
32327 Roo.log('children rendered');
32334 initial : function()
32336 this.reloadItems();
32338 this.currentSize = this.el.getBox(true);
32340 /// was window resize... - let's see if this works..
32341 Roo.EventManager.onWindowResize(this.resize, this);
32343 if(!this.isAutoInitial){
32348 this.layout.defer(500,this);
32351 reloadItems: function()
32353 this.bricks = this.el.select('.masonry-brick', true);
32355 this.bricks.each(function(b) {
32356 //Roo.log(b.getSize());
32357 if (!b.attr('originalwidth')) {
32358 b.attr('originalwidth', b.getSize().width);
32363 Roo.log(this.bricks.elements.length);
32366 resize : function()
32369 var cs = this.el.getBox(true);
32371 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32372 Roo.log("no change in with or X");
32375 this.currentSize = cs;
32379 layout : function()
32382 this._resetLayout();
32383 //this._manageStamps();
32385 // don't animate first layout
32386 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32387 this.layoutItems( isInstant );
32389 // flag for initalized
32390 this._isLayoutInited = true;
32393 layoutItems : function( isInstant )
32395 //var items = this._getItemsForLayout( this.items );
32396 // original code supports filtering layout items.. we just ignore it..
32398 this._layoutItems( this.bricks , isInstant );
32400 this._postLayout();
32402 _layoutItems : function ( items , isInstant)
32404 //this.fireEvent( 'layout', this, items );
32407 if ( !items || !items.elements.length ) {
32408 // no items, emit event with empty array
32413 items.each(function(item) {
32414 Roo.log("layout item");
32416 // get x/y object from method
32417 var position = this._getItemLayoutPosition( item );
32419 position.item = item;
32420 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32421 queue.push( position );
32424 this._processLayoutQueue( queue );
32426 /** Sets position of item in DOM
32427 * @param {Element} item
32428 * @param {Number} x - horizontal position
32429 * @param {Number} y - vertical position
32430 * @param {Boolean} isInstant - disables transitions
32432 _processLayoutQueue : function( queue )
32434 for ( var i=0, len = queue.length; i < len; i++ ) {
32435 var obj = queue[i];
32436 obj.item.position('absolute');
32437 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32443 * Any logic you want to do after each layout,
32444 * i.e. size the container
32446 _postLayout : function()
32448 this.resizeContainer();
32451 resizeContainer : function()
32453 if ( !this.isResizingContainer ) {
32456 var size = this._getContainerSize();
32458 this.el.setSize(size.width,size.height);
32459 this.boxesEl.setSize(size.width,size.height);
32465 _resetLayout : function()
32467 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32468 this.colWidth = this.el.getWidth();
32469 //this.gutter = this.el.getWidth();
32471 this.measureColumns();
32477 this.colYs.push( 0 );
32483 measureColumns : function()
32485 this.getContainerWidth();
32486 // if columnWidth is 0, default to outerWidth of first item
32487 if ( !this.columnWidth ) {
32488 var firstItem = this.bricks.first();
32489 Roo.log(firstItem);
32490 this.columnWidth = this.containerWidth;
32491 if (firstItem && firstItem.attr('originalwidth') ) {
32492 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32494 // columnWidth fall back to item of first element
32495 Roo.log("set column width?");
32496 this.initialColumnWidth = this.columnWidth ;
32498 // if first elem has no width, default to size of container
32503 if (this.initialColumnWidth) {
32504 this.columnWidth = this.initialColumnWidth;
32509 // column width is fixed at the top - however if container width get's smaller we should
32512 // this bit calcs how man columns..
32514 var columnWidth = this.columnWidth += this.gutter;
32516 // calculate columns
32517 var containerWidth = this.containerWidth + this.gutter;
32519 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32520 // fix rounding errors, typically with gutters
32521 var excess = columnWidth - containerWidth % columnWidth;
32524 // if overshoot is less than a pixel, round up, otherwise floor it
32525 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32526 cols = Math[ mathMethod ]( cols );
32527 this.cols = Math.max( cols, 1 );
32528 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32530 // padding positioning..
32531 var totalColWidth = this.cols * this.columnWidth;
32532 var padavail = this.containerWidth - totalColWidth;
32533 // so for 2 columns - we need 3 'pads'
32535 var padNeeded = (1+this.cols) * this.padWidth;
32537 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32539 this.columnWidth += padExtra
32540 //this.padWidth = Math.floor(padavail / ( this.cols));
32542 // adjust colum width so that padding is fixed??
32544 // we have 3 columns ... total = width * 3
32545 // we have X left over... that should be used by
32547 //if (this.expandC) {
32555 getContainerWidth : function()
32557 /* // container is parent if fit width
32558 var container = this.isFitWidth ? this.element.parentNode : this.element;
32559 // check that this.size and size are there
32560 // IE8 triggers resize on body size change, so they might not be
32562 var size = getSize( container ); //FIXME
32563 this.containerWidth = size && size.innerWidth; //FIXME
32566 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32570 _getItemLayoutPosition : function( item ) // what is item?
32572 // we resize the item to our columnWidth..
32574 item.setWidth(this.columnWidth);
32575 item.autoBoxAdjust = false;
32577 var sz = item.getSize();
32579 // how many columns does this brick span
32580 var remainder = this.containerWidth % this.columnWidth;
32582 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32583 // round if off by 1 pixel, otherwise use ceil
32584 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32585 colSpan = Math.min( colSpan, this.cols );
32587 // normally this should be '1' as we dont' currently allow multi width columns..
32589 var colGroup = this._getColGroup( colSpan );
32590 // get the minimum Y value from the columns
32591 var minimumY = Math.min.apply( Math, colGroup );
32592 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32594 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32596 // position the brick
32598 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32599 y: this.currentSize.y + minimumY + this.padHeight
32603 // apply setHeight to necessary columns
32604 var setHeight = minimumY + sz.height + this.padHeight;
32605 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32607 var setSpan = this.cols + 1 - colGroup.length;
32608 for ( var i = 0; i < setSpan; i++ ) {
32609 this.colYs[ shortColIndex + i ] = setHeight ;
32616 * @param {Number} colSpan - number of columns the element spans
32617 * @returns {Array} colGroup
32619 _getColGroup : function( colSpan )
32621 if ( colSpan < 2 ) {
32622 // if brick spans only one column, use all the column Ys
32627 // how many different places could this brick fit horizontally
32628 var groupCount = this.cols + 1 - colSpan;
32629 // for each group potential horizontal position
32630 for ( var i = 0; i < groupCount; i++ ) {
32631 // make an array of colY values for that one group
32632 var groupColYs = this.colYs.slice( i, i + colSpan );
32633 // and get the max value of the array
32634 colGroup[i] = Math.max.apply( Math, groupColYs );
32639 _manageStamp : function( stamp )
32641 var stampSize = stamp.getSize();
32642 var offset = stamp.getBox();
32643 // get the columns that this stamp affects
32644 var firstX = this.isOriginLeft ? offset.x : offset.right;
32645 var lastX = firstX + stampSize.width;
32646 var firstCol = Math.floor( firstX / this.columnWidth );
32647 firstCol = Math.max( 0, firstCol );
32649 var lastCol = Math.floor( lastX / this.columnWidth );
32650 // lastCol should not go over if multiple of columnWidth #425
32651 lastCol -= lastX % this.columnWidth ? 0 : 1;
32652 lastCol = Math.min( this.cols - 1, lastCol );
32654 // set colYs to bottom of the stamp
32655 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32658 for ( var i = firstCol; i <= lastCol; i++ ) {
32659 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32664 _getContainerSize : function()
32666 this.maxY = Math.max.apply( Math, this.colYs );
32671 if ( this.isFitWidth ) {
32672 size.width = this._getContainerFitWidth();
32678 _getContainerFitWidth : function()
32680 var unusedCols = 0;
32681 // count unused columns
32684 if ( this.colYs[i] !== 0 ) {
32689 // fit container to columns that have been used
32690 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32693 needsResizeLayout : function()
32695 var previousWidth = this.containerWidth;
32696 this.getContainerWidth();
32697 return previousWidth !== this.containerWidth;
32712 * @class Roo.bootstrap.MasonryBrick
32713 * @extends Roo.bootstrap.Component
32714 * Bootstrap MasonryBrick class
32717 * Create a new MasonryBrick
32718 * @param {Object} config The config object
32721 Roo.bootstrap.MasonryBrick = function(config){
32723 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32725 Roo.bootstrap.MasonryBrick.register(this);
32731 * When a MasonryBrick is clcik
32732 * @param {Roo.bootstrap.MasonryBrick} this
32733 * @param {Roo.EventObject} e
32739 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32742 * @cfg {String} title
32746 * @cfg {String} html
32750 * @cfg {String} bgimage
32754 * @cfg {String} videourl
32758 * @cfg {String} cls
32762 * @cfg {String} href
32766 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32771 * @cfg {String} placetitle (center|bottom)
32776 * @cfg {Boolean} isFitContainer defalut true
32778 isFitContainer : true,
32781 * @cfg {Boolean} preventDefault defalut false
32783 preventDefault : false,
32786 * @cfg {Boolean} inverse defalut false
32788 maskInverse : false,
32790 getAutoCreate : function()
32792 if(!this.isFitContainer){
32793 return this.getSplitAutoCreate();
32796 var cls = 'masonry-brick masonry-brick-full';
32798 if(this.href.length){
32799 cls += ' masonry-brick-link';
32802 if(this.bgimage.length){
32803 cls += ' masonry-brick-image';
32806 if(this.maskInverse){
32807 cls += ' mask-inverse';
32810 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32811 cls += ' enable-mask';
32815 cls += ' masonry-' + this.size + '-brick';
32818 if(this.placetitle.length){
32820 switch (this.placetitle) {
32822 cls += ' masonry-center-title';
32825 cls += ' masonry-bottom-title';
32832 if(!this.html.length && !this.bgimage.length){
32833 cls += ' masonry-center-title';
32836 if(!this.html.length && this.bgimage.length){
32837 cls += ' masonry-bottom-title';
32842 cls += ' ' + this.cls;
32846 tag: (this.href.length) ? 'a' : 'div',
32851 cls: 'masonry-brick-mask'
32855 cls: 'masonry-brick-paragraph',
32861 if(this.href.length){
32862 cfg.href = this.href;
32865 var cn = cfg.cn[1].cn;
32867 if(this.title.length){
32870 cls: 'masonry-brick-title',
32875 if(this.html.length){
32878 cls: 'masonry-brick-text',
32883 if (!this.title.length && !this.html.length) {
32884 cfg.cn[1].cls += ' hide';
32887 if(this.bgimage.length){
32890 cls: 'masonry-brick-image-view',
32895 if(this.videourl.length){
32896 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32897 // youtube support only?
32900 cls: 'masonry-brick-image-view',
32903 allowfullscreen : true
32911 getSplitAutoCreate : function()
32913 var cls = 'masonry-brick masonry-brick-split';
32915 if(this.href.length){
32916 cls += ' masonry-brick-link';
32919 if(this.bgimage.length){
32920 cls += ' masonry-brick-image';
32924 cls += ' masonry-' + this.size + '-brick';
32927 switch (this.placetitle) {
32929 cls += ' masonry-center-title';
32932 cls += ' masonry-bottom-title';
32935 if(!this.bgimage.length){
32936 cls += ' masonry-center-title';
32939 if(this.bgimage.length){
32940 cls += ' masonry-bottom-title';
32946 cls += ' ' + this.cls;
32950 tag: (this.href.length) ? 'a' : 'div',
32955 cls: 'masonry-brick-split-head',
32959 cls: 'masonry-brick-paragraph',
32966 cls: 'masonry-brick-split-body',
32972 if(this.href.length){
32973 cfg.href = this.href;
32976 if(this.title.length){
32977 cfg.cn[0].cn[0].cn.push({
32979 cls: 'masonry-brick-title',
32984 if(this.html.length){
32985 cfg.cn[1].cn.push({
32987 cls: 'masonry-brick-text',
32992 if(this.bgimage.length){
32993 cfg.cn[0].cn.push({
32995 cls: 'masonry-brick-image-view',
33000 if(this.videourl.length){
33001 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33002 // youtube support only?
33003 cfg.cn[0].cn.cn.push({
33005 cls: 'masonry-brick-image-view',
33008 allowfullscreen : true
33015 initEvents: function()
33017 switch (this.size) {
33050 this.el.on('touchstart', this.onTouchStart, this);
33051 this.el.on('touchmove', this.onTouchMove, this);
33052 this.el.on('touchend', this.onTouchEnd, this);
33053 this.el.on('contextmenu', this.onContextMenu, this);
33055 this.el.on('mouseenter' ,this.enter, this);
33056 this.el.on('mouseleave', this.leave, this);
33057 this.el.on('click', this.onClick, this);
33060 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33061 this.parent().bricks.push(this);
33066 onClick: function(e, el)
33068 var time = this.endTimer - this.startTimer;
33069 // Roo.log(e.preventDefault());
33072 e.preventDefault();
33077 if(!this.preventDefault){
33081 e.preventDefault();
33083 if (this.activeClass != '') {
33084 this.selectBrick();
33087 this.fireEvent('click', this, e);
33090 enter: function(e, el)
33092 e.preventDefault();
33094 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33098 if(this.bgimage.length && this.html.length){
33099 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33103 leave: function(e, el)
33105 e.preventDefault();
33107 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33111 if(this.bgimage.length && this.html.length){
33112 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33116 onTouchStart: function(e, el)
33118 // e.preventDefault();
33120 this.touchmoved = false;
33122 if(!this.isFitContainer){
33126 if(!this.bgimage.length || !this.html.length){
33130 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33132 this.timer = new Date().getTime();
33136 onTouchMove: function(e, el)
33138 this.touchmoved = true;
33141 onContextMenu : function(e,el)
33143 e.preventDefault();
33144 e.stopPropagation();
33148 onTouchEnd: function(e, el)
33150 // e.preventDefault();
33152 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33159 if(!this.bgimage.length || !this.html.length){
33161 if(this.href.length){
33162 window.location.href = this.href;
33168 if(!this.isFitContainer){
33172 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33174 window.location.href = this.href;
33177 //selection on single brick only
33178 selectBrick : function() {
33180 if (!this.parentId) {
33184 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33185 var index = m.selectedBrick.indexOf(this.id);
33188 m.selectedBrick.splice(index,1);
33189 this.el.removeClass(this.activeClass);
33193 for(var i = 0; i < m.selectedBrick.length; i++) {
33194 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33195 b.el.removeClass(b.activeClass);
33198 m.selectedBrick = [];
33200 m.selectedBrick.push(this.id);
33201 this.el.addClass(this.activeClass);
33205 isSelected : function(){
33206 return this.el.hasClass(this.activeClass);
33211 Roo.apply(Roo.bootstrap.MasonryBrick, {
33214 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33216 * register a Masonry Brick
33217 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33220 register : function(brick)
33222 //this.groups[brick.id] = brick;
33223 this.groups.add(brick.id, brick);
33226 * fetch a masonry brick based on the masonry brick ID
33227 * @param {string} the masonry brick to add
33228 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33231 get: function(brick_id)
33233 // if (typeof(this.groups[brick_id]) == 'undefined') {
33236 // return this.groups[brick_id] ;
33238 if(this.groups.key(brick_id)) {
33239 return this.groups.key(brick_id);
33257 * @class Roo.bootstrap.Brick
33258 * @extends Roo.bootstrap.Component
33259 * Bootstrap Brick class
33262 * Create a new Brick
33263 * @param {Object} config The config object
33266 Roo.bootstrap.Brick = function(config){
33267 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33273 * When a Brick is click
33274 * @param {Roo.bootstrap.Brick} this
33275 * @param {Roo.EventObject} e
33281 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33284 * @cfg {String} title
33288 * @cfg {String} html
33292 * @cfg {String} bgimage
33296 * @cfg {String} cls
33300 * @cfg {String} href
33304 * @cfg {String} video
33308 * @cfg {Boolean} square
33312 getAutoCreate : function()
33314 var cls = 'roo-brick';
33316 if(this.href.length){
33317 cls += ' roo-brick-link';
33320 if(this.bgimage.length){
33321 cls += ' roo-brick-image';
33324 if(!this.html.length && !this.bgimage.length){
33325 cls += ' roo-brick-center-title';
33328 if(!this.html.length && this.bgimage.length){
33329 cls += ' roo-brick-bottom-title';
33333 cls += ' ' + this.cls;
33337 tag: (this.href.length) ? 'a' : 'div',
33342 cls: 'roo-brick-paragraph',
33348 if(this.href.length){
33349 cfg.href = this.href;
33352 var cn = cfg.cn[0].cn;
33354 if(this.title.length){
33357 cls: 'roo-brick-title',
33362 if(this.html.length){
33365 cls: 'roo-brick-text',
33372 if(this.bgimage.length){
33375 cls: 'roo-brick-image-view',
33383 initEvents: function()
33385 if(this.title.length || this.html.length){
33386 this.el.on('mouseenter' ,this.enter, this);
33387 this.el.on('mouseleave', this.leave, this);
33390 Roo.EventManager.onWindowResize(this.resize, this);
33392 if(this.bgimage.length){
33393 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33394 this.imageEl.on('load', this.onImageLoad, this);
33401 onImageLoad : function()
33406 resize : function()
33408 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33410 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33412 if(this.bgimage.length){
33413 var image = this.el.select('.roo-brick-image-view', true).first();
33415 image.setWidth(paragraph.getWidth());
33418 image.setHeight(paragraph.getWidth());
33421 this.el.setHeight(image.getHeight());
33422 paragraph.setHeight(image.getHeight());
33428 enter: function(e, el)
33430 e.preventDefault();
33432 if(this.bgimage.length){
33433 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33434 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33438 leave: function(e, el)
33440 e.preventDefault();
33442 if(this.bgimage.length){
33443 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33444 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33459 * @class Roo.bootstrap.NumberField
33460 * @extends Roo.bootstrap.Input
33461 * Bootstrap NumberField class
33467 * Create a new NumberField
33468 * @param {Object} config The config object
33471 Roo.bootstrap.NumberField = function(config){
33472 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33475 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33478 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33480 allowDecimals : true,
33482 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33484 decimalSeparator : ".",
33486 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33488 decimalPrecision : 2,
33490 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33492 allowNegative : true,
33495 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33499 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33501 minValue : Number.NEGATIVE_INFINITY,
33503 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33505 maxValue : Number.MAX_VALUE,
33507 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33509 minText : "The minimum value for this field is {0}",
33511 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33513 maxText : "The maximum value for this field is {0}",
33515 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33516 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33518 nanText : "{0} is not a valid number",
33520 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33522 thousandsDelimiter : false,
33524 * @cfg {String} valueAlign alignment of value
33526 valueAlign : "left",
33528 getAutoCreate : function()
33530 var hiddenInput = {
33534 cls: 'hidden-number-input'
33538 hiddenInput.name = this.name;
33543 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33545 this.name = hiddenInput.name;
33547 if(cfg.cn.length > 0) {
33548 cfg.cn.push(hiddenInput);
33555 initEvents : function()
33557 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33559 var allowed = "0123456789";
33561 if(this.allowDecimals){
33562 allowed += this.decimalSeparator;
33565 if(this.allowNegative){
33569 if(this.thousandsDelimiter) {
33573 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33575 var keyPress = function(e){
33577 var k = e.getKey();
33579 var c = e.getCharCode();
33582 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33583 allowed.indexOf(String.fromCharCode(c)) === -1
33589 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33593 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33598 this.el.on("keypress", keyPress, this);
33601 validateValue : function(value)
33604 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33608 var num = this.parseValue(value);
33611 this.markInvalid(String.format(this.nanText, value));
33615 if(num < this.minValue){
33616 this.markInvalid(String.format(this.minText, this.minValue));
33620 if(num > this.maxValue){
33621 this.markInvalid(String.format(this.maxText, this.maxValue));
33628 getValue : function()
33630 var v = this.hiddenEl().getValue();
33632 return this.fixPrecision(this.parseValue(v));
33635 parseValue : function(value)
33637 if(this.thousandsDelimiter) {
33639 r = new RegExp(",", "g");
33640 value = value.replace(r, "");
33643 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33644 return isNaN(value) ? '' : value;
33647 fixPrecision : function(value)
33649 if(this.thousandsDelimiter) {
33651 r = new RegExp(",", "g");
33652 value = value.replace(r, "");
33655 var nan = isNaN(value);
33657 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33658 return nan ? '' : value;
33660 return parseFloat(value).toFixed(this.decimalPrecision);
33663 setValue : function(v)
33665 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33671 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33673 this.inputEl().dom.value = (v == '') ? '' :
33674 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33676 if(!this.allowZero && v === '0') {
33677 this.hiddenEl().dom.value = '';
33678 this.inputEl().dom.value = '';
33685 decimalPrecisionFcn : function(v)
33687 return Math.floor(v);
33690 beforeBlur : function()
33692 var v = this.parseValue(this.getRawValue());
33694 if(v || v === 0 || v === ''){
33699 hiddenEl : function()
33701 return this.el.select('input.hidden-number-input',true).first();
33713 * @class Roo.bootstrap.DocumentSlider
33714 * @extends Roo.bootstrap.Component
33715 * Bootstrap DocumentSlider class
33718 * Create a new DocumentViewer
33719 * @param {Object} config The config object
33722 Roo.bootstrap.DocumentSlider = function(config){
33723 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33730 * Fire after initEvent
33731 * @param {Roo.bootstrap.DocumentSlider} this
33736 * Fire after update
33737 * @param {Roo.bootstrap.DocumentSlider} this
33743 * @param {Roo.bootstrap.DocumentSlider} this
33749 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33755 getAutoCreate : function()
33759 cls : 'roo-document-slider',
33763 cls : 'roo-document-slider-header',
33767 cls : 'roo-document-slider-header-title'
33773 cls : 'roo-document-slider-body',
33777 cls : 'roo-document-slider-prev',
33781 cls : 'fa fa-chevron-left'
33787 cls : 'roo-document-slider-thumb',
33791 cls : 'roo-document-slider-image'
33797 cls : 'roo-document-slider-next',
33801 cls : 'fa fa-chevron-right'
33813 initEvents : function()
33815 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33816 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33818 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33819 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33821 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33822 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33824 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33825 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33827 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33828 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33830 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33831 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33833 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33834 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33836 this.thumbEl.on('click', this.onClick, this);
33838 this.prevIndicator.on('click', this.prev, this);
33840 this.nextIndicator.on('click', this.next, this);
33844 initial : function()
33846 if(this.files.length){
33847 this.indicator = 1;
33851 this.fireEvent('initial', this);
33854 update : function()
33856 this.imageEl.attr('src', this.files[this.indicator - 1]);
33858 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33860 this.prevIndicator.show();
33862 if(this.indicator == 1){
33863 this.prevIndicator.hide();
33866 this.nextIndicator.show();
33868 if(this.indicator == this.files.length){
33869 this.nextIndicator.hide();
33872 this.thumbEl.scrollTo('top');
33874 this.fireEvent('update', this);
33877 onClick : function(e)
33879 e.preventDefault();
33881 this.fireEvent('click', this);
33886 e.preventDefault();
33888 this.indicator = Math.max(1, this.indicator - 1);
33895 e.preventDefault();
33897 this.indicator = Math.min(this.files.length, this.indicator + 1);
33911 * @class Roo.bootstrap.RadioSet
33912 * @extends Roo.bootstrap.Input
33913 * Bootstrap RadioSet class
33914 * @cfg {String} indicatorpos (left|right) default left
33915 * @cfg {Boolean} inline (true|false) inline the element (default true)
33916 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33918 * Create a new RadioSet
33919 * @param {Object} config The config object
33922 Roo.bootstrap.RadioSet = function(config){
33924 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33928 Roo.bootstrap.RadioSet.register(this);
33933 * Fires when the element is checked or unchecked.
33934 * @param {Roo.bootstrap.RadioSet} this This radio
33935 * @param {Roo.bootstrap.Radio} item The checked item
33940 * Fires when the element is click.
33941 * @param {Roo.bootstrap.RadioSet} this This radio set
33942 * @param {Roo.bootstrap.Radio} item The checked item
33943 * @param {Roo.EventObject} e The event object
33950 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33958 indicatorpos : 'left',
33960 getAutoCreate : function()
33964 cls : 'roo-radio-set-label',
33968 html : this.fieldLabel
33973 if(this.indicatorpos == 'left'){
33976 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33977 tooltip : 'This field is required'
33982 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33983 tooltip : 'This field is required'
33989 cls : 'roo-radio-set-items'
33992 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33994 if (align === 'left' && this.fieldLabel.length) {
33997 cls : "roo-radio-set-right",
34003 if(this.labelWidth > 12){
34004 label.style = "width: " + this.labelWidth + 'px';
34007 if(this.labelWidth < 13 && this.labelmd == 0){
34008 this.labelmd = this.labelWidth;
34011 if(this.labellg > 0){
34012 label.cls += ' col-lg-' + this.labellg;
34013 items.cls += ' col-lg-' + (12 - this.labellg);
34016 if(this.labelmd > 0){
34017 label.cls += ' col-md-' + this.labelmd;
34018 items.cls += ' col-md-' + (12 - this.labelmd);
34021 if(this.labelsm > 0){
34022 label.cls += ' col-sm-' + this.labelsm;
34023 items.cls += ' col-sm-' + (12 - this.labelsm);
34026 if(this.labelxs > 0){
34027 label.cls += ' col-xs-' + this.labelxs;
34028 items.cls += ' col-xs-' + (12 - this.labelxs);
34034 cls : 'roo-radio-set',
34038 cls : 'roo-radio-set-input',
34041 value : this.value ? this.value : ''
34048 if(this.weight.length){
34049 cfg.cls += ' roo-radio-' + this.weight;
34053 cfg.cls += ' roo-radio-set-inline';
34057 ['xs','sm','md','lg'].map(function(size){
34058 if (settings[size]) {
34059 cfg.cls += ' col-' + size + '-' + settings[size];
34067 initEvents : function()
34069 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34070 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34072 if(!this.fieldLabel.length){
34073 this.labelEl.hide();
34076 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34077 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34079 this.indicator = this.indicatorEl();
34081 if(this.indicator){
34082 this.indicator.addClass('invisible');
34085 this.originalValue = this.getValue();
34089 inputEl: function ()
34091 return this.el.select('.roo-radio-set-input', true).first();
34094 getChildContainer : function()
34096 return this.itemsEl;
34099 register : function(item)
34101 this.radioes.push(item);
34105 validate : function()
34107 if(this.getVisibilityEl().hasClass('hidden')){
34113 Roo.each(this.radioes, function(i){
34122 if(this.allowBlank) {
34126 if(this.disabled || valid){
34131 this.markInvalid();
34136 markValid : function()
34138 if(this.labelEl.isVisible(true)){
34139 this.indicatorEl().removeClass('visible');
34140 this.indicatorEl().addClass('invisible');
34143 this.el.removeClass([this.invalidClass, this.validClass]);
34144 this.el.addClass(this.validClass);
34146 this.fireEvent('valid', this);
34149 markInvalid : function(msg)
34151 if(this.allowBlank || this.disabled){
34155 if(this.labelEl.isVisible(true)){
34156 this.indicatorEl().removeClass('invisible');
34157 this.indicatorEl().addClass('visible');
34160 this.el.removeClass([this.invalidClass, this.validClass]);
34161 this.el.addClass(this.invalidClass);
34163 this.fireEvent('invalid', this, msg);
34167 setValue : function(v, suppressEvent)
34169 if(this.value === v){
34176 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34179 Roo.each(this.radioes, function(i){
34181 i.el.removeClass('checked');
34184 Roo.each(this.radioes, function(i){
34186 if(i.value === v || i.value.toString() === v.toString()){
34188 i.el.addClass('checked');
34190 if(suppressEvent !== true){
34191 this.fireEvent('check', this, i);
34202 clearInvalid : function(){
34204 if(!this.el || this.preventMark){
34208 this.el.removeClass([this.invalidClass]);
34210 this.fireEvent('valid', this);
34215 Roo.apply(Roo.bootstrap.RadioSet, {
34219 register : function(set)
34221 this.groups[set.name] = set;
34224 get: function(name)
34226 if (typeof(this.groups[name]) == 'undefined') {
34230 return this.groups[name] ;
34236 * Ext JS Library 1.1.1
34237 * Copyright(c) 2006-2007, Ext JS, LLC.
34239 * Originally Released Under LGPL - original licence link has changed is not relivant.
34242 * <script type="text/javascript">
34247 * @class Roo.bootstrap.SplitBar
34248 * @extends Roo.util.Observable
34249 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34253 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34254 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34255 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34256 split.minSize = 100;
34257 split.maxSize = 600;
34258 split.animate = true;
34259 split.on('moved', splitterMoved);
34262 * Create a new SplitBar
34263 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34264 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34265 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34266 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34267 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34268 position of the SplitBar).
34270 Roo.bootstrap.SplitBar = function(cfg){
34275 // dragElement : elm
34276 // resizingElement: el,
34278 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34279 // placement : Roo.bootstrap.SplitBar.LEFT ,
34280 // existingProxy ???
34283 this.el = Roo.get(cfg.dragElement, true);
34284 this.el.dom.unselectable = "on";
34286 this.resizingEl = Roo.get(cfg.resizingElement, true);
34290 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34291 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34294 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34297 * The minimum size of the resizing element. (Defaults to 0)
34303 * The maximum size of the resizing element. (Defaults to 2000)
34306 this.maxSize = 2000;
34309 * Whether to animate the transition to the new size
34312 this.animate = false;
34315 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34318 this.useShim = false;
34323 if(!cfg.existingProxy){
34325 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34327 this.proxy = Roo.get(cfg.existingProxy).dom;
34330 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34333 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34336 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34339 this.dragSpecs = {};
34342 * @private The adapter to use to positon and resize elements
34344 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34345 this.adapter.init(this);
34347 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34349 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34350 this.el.addClass("roo-splitbar-h");
34353 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34354 this.el.addClass("roo-splitbar-v");
34360 * Fires when the splitter is moved (alias for {@link #event-moved})
34361 * @param {Roo.bootstrap.SplitBar} this
34362 * @param {Number} newSize the new width or height
34367 * Fires when the splitter is moved
34368 * @param {Roo.bootstrap.SplitBar} this
34369 * @param {Number} newSize the new width or height
34373 * @event beforeresize
34374 * Fires before the splitter is dragged
34375 * @param {Roo.bootstrap.SplitBar} this
34377 "beforeresize" : true,
34379 "beforeapply" : true
34382 Roo.util.Observable.call(this);
34385 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34386 onStartProxyDrag : function(x, y){
34387 this.fireEvent("beforeresize", this);
34389 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34391 o.enableDisplayMode("block");
34392 // all splitbars share the same overlay
34393 Roo.bootstrap.SplitBar.prototype.overlay = o;
34395 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34396 this.overlay.show();
34397 Roo.get(this.proxy).setDisplayed("block");
34398 var size = this.adapter.getElementSize(this);
34399 this.activeMinSize = this.getMinimumSize();;
34400 this.activeMaxSize = this.getMaximumSize();;
34401 var c1 = size - this.activeMinSize;
34402 var c2 = Math.max(this.activeMaxSize - size, 0);
34403 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34404 this.dd.resetConstraints();
34405 this.dd.setXConstraint(
34406 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34407 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34409 this.dd.setYConstraint(0, 0);
34411 this.dd.resetConstraints();
34412 this.dd.setXConstraint(0, 0);
34413 this.dd.setYConstraint(
34414 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34415 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34418 this.dragSpecs.startSize = size;
34419 this.dragSpecs.startPoint = [x, y];
34420 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34424 * @private Called after the drag operation by the DDProxy
34426 onEndProxyDrag : function(e){
34427 Roo.get(this.proxy).setDisplayed(false);
34428 var endPoint = Roo.lib.Event.getXY(e);
34430 this.overlay.hide();
34433 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34434 newSize = this.dragSpecs.startSize +
34435 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34436 endPoint[0] - this.dragSpecs.startPoint[0] :
34437 this.dragSpecs.startPoint[0] - endPoint[0]
34440 newSize = this.dragSpecs.startSize +
34441 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34442 endPoint[1] - this.dragSpecs.startPoint[1] :
34443 this.dragSpecs.startPoint[1] - endPoint[1]
34446 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34447 if(newSize != this.dragSpecs.startSize){
34448 if(this.fireEvent('beforeapply', this, newSize) !== false){
34449 this.adapter.setElementSize(this, newSize);
34450 this.fireEvent("moved", this, newSize);
34451 this.fireEvent("resize", this, newSize);
34457 * Get the adapter this SplitBar uses
34458 * @return The adapter object
34460 getAdapter : function(){
34461 return this.adapter;
34465 * Set the adapter this SplitBar uses
34466 * @param {Object} adapter A SplitBar adapter object
34468 setAdapter : function(adapter){
34469 this.adapter = adapter;
34470 this.adapter.init(this);
34474 * Gets the minimum size for the resizing element
34475 * @return {Number} The minimum size
34477 getMinimumSize : function(){
34478 return this.minSize;
34482 * Sets the minimum size for the resizing element
34483 * @param {Number} minSize The minimum size
34485 setMinimumSize : function(minSize){
34486 this.minSize = minSize;
34490 * Gets the maximum size for the resizing element
34491 * @return {Number} The maximum size
34493 getMaximumSize : function(){
34494 return this.maxSize;
34498 * Sets the maximum size for the resizing element
34499 * @param {Number} maxSize The maximum size
34501 setMaximumSize : function(maxSize){
34502 this.maxSize = maxSize;
34506 * Sets the initialize size for the resizing element
34507 * @param {Number} size The initial size
34509 setCurrentSize : function(size){
34510 var oldAnimate = this.animate;
34511 this.animate = false;
34512 this.adapter.setElementSize(this, size);
34513 this.animate = oldAnimate;
34517 * Destroy this splitbar.
34518 * @param {Boolean} removeEl True to remove the element
34520 destroy : function(removeEl){
34522 this.shim.remove();
34525 this.proxy.parentNode.removeChild(this.proxy);
34533 * @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.
34535 Roo.bootstrap.SplitBar.createProxy = function(dir){
34536 var proxy = new Roo.Element(document.createElement("div"));
34537 proxy.unselectable();
34538 var cls = 'roo-splitbar-proxy';
34539 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34540 document.body.appendChild(proxy.dom);
34545 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34546 * Default Adapter. It assumes the splitter and resizing element are not positioned
34547 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34549 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34552 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34553 // do nothing for now
34554 init : function(s){
34558 * Called before drag operations to get the current size of the resizing element.
34559 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34561 getElementSize : function(s){
34562 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34563 return s.resizingEl.getWidth();
34565 return s.resizingEl.getHeight();
34570 * Called after drag operations to set the size of the resizing element.
34571 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34572 * @param {Number} newSize The new size to set
34573 * @param {Function} onComplete A function to be invoked when resizing is complete
34575 setElementSize : function(s, newSize, onComplete){
34576 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34578 s.resizingEl.setWidth(newSize);
34580 onComplete(s, newSize);
34583 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34588 s.resizingEl.setHeight(newSize);
34590 onComplete(s, newSize);
34593 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34600 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34601 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34602 * Adapter that moves the splitter element to align with the resized sizing element.
34603 * Used with an absolute positioned SplitBar.
34604 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34605 * document.body, make sure you assign an id to the body element.
34607 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34608 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34609 this.container = Roo.get(container);
34612 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34613 init : function(s){
34614 this.basic.init(s);
34617 getElementSize : function(s){
34618 return this.basic.getElementSize(s);
34621 setElementSize : function(s, newSize, onComplete){
34622 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34625 moveSplitter : function(s){
34626 var yes = Roo.bootstrap.SplitBar;
34627 switch(s.placement){
34629 s.el.setX(s.resizingEl.getRight());
34632 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34635 s.el.setY(s.resizingEl.getBottom());
34638 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34645 * Orientation constant - Create a vertical SplitBar
34649 Roo.bootstrap.SplitBar.VERTICAL = 1;
34652 * Orientation constant - Create a horizontal SplitBar
34656 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34659 * Placement constant - The resizing element is to the left of the splitter element
34663 Roo.bootstrap.SplitBar.LEFT = 1;
34666 * Placement constant - The resizing element is to the right of the splitter element
34670 Roo.bootstrap.SplitBar.RIGHT = 2;
34673 * Placement constant - The resizing element is positioned above the splitter element
34677 Roo.bootstrap.SplitBar.TOP = 3;
34680 * Placement constant - The resizing element is positioned under splitter element
34684 Roo.bootstrap.SplitBar.BOTTOM = 4;
34685 Roo.namespace("Roo.bootstrap.layout");/*
34687 * Ext JS Library 1.1.1
34688 * Copyright(c) 2006-2007, Ext JS, LLC.
34690 * Originally Released Under LGPL - original licence link has changed is not relivant.
34693 * <script type="text/javascript">
34697 * @class Roo.bootstrap.layout.Manager
34698 * @extends Roo.bootstrap.Component
34699 * Base class for layout managers.
34701 Roo.bootstrap.layout.Manager = function(config)
34703 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34709 /** false to disable window resize monitoring @type Boolean */
34710 this.monitorWindowResize = true;
34715 * Fires when a layout is performed.
34716 * @param {Roo.LayoutManager} this
34720 * @event regionresized
34721 * Fires when the user resizes a region.
34722 * @param {Roo.LayoutRegion} region The resized region
34723 * @param {Number} newSize The new size (width for east/west, height for north/south)
34725 "regionresized" : true,
34727 * @event regioncollapsed
34728 * Fires when a region is collapsed.
34729 * @param {Roo.LayoutRegion} region The collapsed region
34731 "regioncollapsed" : true,
34733 * @event regionexpanded
34734 * Fires when a region is expanded.
34735 * @param {Roo.LayoutRegion} region The expanded region
34737 "regionexpanded" : true
34739 this.updating = false;
34742 this.el = Roo.get(config.el);
34748 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34753 monitorWindowResize : true,
34759 onRender : function(ct, position)
34762 this.el = Roo.get(ct);
34765 //this.fireEvent('render',this);
34769 initEvents: function()
34773 // ie scrollbar fix
34774 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34775 document.body.scroll = "no";
34776 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34777 this.el.position('relative');
34779 this.id = this.el.id;
34780 this.el.addClass("roo-layout-container");
34781 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34782 if(this.el.dom != document.body ) {
34783 this.el.on('resize', this.layout,this);
34784 this.el.on('show', this.layout,this);
34790 * Returns true if this layout is currently being updated
34791 * @return {Boolean}
34793 isUpdating : function(){
34794 return this.updating;
34798 * Suspend the LayoutManager from doing auto-layouts while
34799 * making multiple add or remove calls
34801 beginUpdate : function(){
34802 this.updating = true;
34806 * Restore auto-layouts and optionally disable the manager from performing a layout
34807 * @param {Boolean} noLayout true to disable a layout update
34809 endUpdate : function(noLayout){
34810 this.updating = false;
34816 layout: function(){
34820 onRegionResized : function(region, newSize){
34821 this.fireEvent("regionresized", region, newSize);
34825 onRegionCollapsed : function(region){
34826 this.fireEvent("regioncollapsed", region);
34829 onRegionExpanded : function(region){
34830 this.fireEvent("regionexpanded", region);
34834 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34835 * performs box-model adjustments.
34836 * @return {Object} The size as an object {width: (the width), height: (the height)}
34838 getViewSize : function()
34841 if(this.el.dom != document.body){
34842 size = this.el.getSize();
34844 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34846 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34847 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34852 * Returns the Element this layout is bound to.
34853 * @return {Roo.Element}
34855 getEl : function(){
34860 * Returns the specified region.
34861 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34862 * @return {Roo.LayoutRegion}
34864 getRegion : function(target){
34865 return this.regions[target.toLowerCase()];
34868 onWindowResize : function(){
34869 if(this.monitorWindowResize){
34876 * Ext JS Library 1.1.1
34877 * Copyright(c) 2006-2007, Ext JS, LLC.
34879 * Originally Released Under LGPL - original licence link has changed is not relivant.
34882 * <script type="text/javascript">
34885 * @class Roo.bootstrap.layout.Border
34886 * @extends Roo.bootstrap.layout.Manager
34887 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34888 * please see: examples/bootstrap/nested.html<br><br>
34890 <b>The container the layout is rendered into can be either the body element or any other element.
34891 If it is not the body element, the container needs to either be an absolute positioned element,
34892 or you will need to add "position:relative" to the css of the container. You will also need to specify
34893 the container size if it is not the body element.</b>
34896 * Create a new Border
34897 * @param {Object} config Configuration options
34899 Roo.bootstrap.layout.Border = function(config){
34900 config = config || {};
34901 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34905 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34906 if(config[region]){
34907 config[region].region = region;
34908 this.addRegion(config[region]);
34914 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34916 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34918 * Creates and adds a new region if it doesn't already exist.
34919 * @param {String} target The target region key (north, south, east, west or center).
34920 * @param {Object} config The regions config object
34921 * @return {BorderLayoutRegion} The new region
34923 addRegion : function(config)
34925 if(!this.regions[config.region]){
34926 var r = this.factory(config);
34927 this.bindRegion(r);
34929 return this.regions[config.region];
34933 bindRegion : function(r){
34934 this.regions[r.config.region] = r;
34936 r.on("visibilitychange", this.layout, this);
34937 r.on("paneladded", this.layout, this);
34938 r.on("panelremoved", this.layout, this);
34939 r.on("invalidated", this.layout, this);
34940 r.on("resized", this.onRegionResized, this);
34941 r.on("collapsed", this.onRegionCollapsed, this);
34942 r.on("expanded", this.onRegionExpanded, this);
34946 * Performs a layout update.
34948 layout : function()
34950 if(this.updating) {
34954 // render all the rebions if they have not been done alreayd?
34955 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34956 if(this.regions[region] && !this.regions[region].bodyEl){
34957 this.regions[region].onRender(this.el)
34961 var size = this.getViewSize();
34962 var w = size.width;
34963 var h = size.height;
34968 //var x = 0, y = 0;
34970 var rs = this.regions;
34971 var north = rs["north"];
34972 var south = rs["south"];
34973 var west = rs["west"];
34974 var east = rs["east"];
34975 var center = rs["center"];
34976 //if(this.hideOnLayout){ // not supported anymore
34977 //c.el.setStyle("display", "none");
34979 if(north && north.isVisible()){
34980 var b = north.getBox();
34981 var m = north.getMargins();
34982 b.width = w - (m.left+m.right);
34985 centerY = b.height + b.y + m.bottom;
34986 centerH -= centerY;
34987 north.updateBox(this.safeBox(b));
34989 if(south && south.isVisible()){
34990 var b = south.getBox();
34991 var m = south.getMargins();
34992 b.width = w - (m.left+m.right);
34994 var totalHeight = (b.height + m.top + m.bottom);
34995 b.y = h - totalHeight + m.top;
34996 centerH -= totalHeight;
34997 south.updateBox(this.safeBox(b));
34999 if(west && west.isVisible()){
35000 var b = west.getBox();
35001 var m = west.getMargins();
35002 b.height = centerH - (m.top+m.bottom);
35004 b.y = centerY + m.top;
35005 var totalWidth = (b.width + m.left + m.right);
35006 centerX += totalWidth;
35007 centerW -= totalWidth;
35008 west.updateBox(this.safeBox(b));
35010 if(east && east.isVisible()){
35011 var b = east.getBox();
35012 var m = east.getMargins();
35013 b.height = centerH - (m.top+m.bottom);
35014 var totalWidth = (b.width + m.left + m.right);
35015 b.x = w - totalWidth + m.left;
35016 b.y = centerY + m.top;
35017 centerW -= totalWidth;
35018 east.updateBox(this.safeBox(b));
35021 var m = center.getMargins();
35023 x: centerX + m.left,
35024 y: centerY + m.top,
35025 width: centerW - (m.left+m.right),
35026 height: centerH - (m.top+m.bottom)
35028 //if(this.hideOnLayout){
35029 //center.el.setStyle("display", "block");
35031 center.updateBox(this.safeBox(centerBox));
35034 this.fireEvent("layout", this);
35038 safeBox : function(box){
35039 box.width = Math.max(0, box.width);
35040 box.height = Math.max(0, box.height);
35045 * Adds a ContentPanel (or subclass) to this layout.
35046 * @param {String} target The target region key (north, south, east, west or center).
35047 * @param {Roo.ContentPanel} panel The panel to add
35048 * @return {Roo.ContentPanel} The added panel
35050 add : function(target, panel){
35052 target = target.toLowerCase();
35053 return this.regions[target].add(panel);
35057 * Remove a ContentPanel (or subclass) to this layout.
35058 * @param {String} target The target region key (north, south, east, west or center).
35059 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35060 * @return {Roo.ContentPanel} The removed panel
35062 remove : function(target, panel){
35063 target = target.toLowerCase();
35064 return this.regions[target].remove(panel);
35068 * Searches all regions for a panel with the specified id
35069 * @param {String} panelId
35070 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35072 findPanel : function(panelId){
35073 var rs = this.regions;
35074 for(var target in rs){
35075 if(typeof rs[target] != "function"){
35076 var p = rs[target].getPanel(panelId);
35086 * Searches all regions for a panel with the specified id and activates (shows) it.
35087 * @param {String/ContentPanel} panelId The panels id or the panel itself
35088 * @return {Roo.ContentPanel} The shown panel or null
35090 showPanel : function(panelId) {
35091 var rs = this.regions;
35092 for(var target in rs){
35093 var r = rs[target];
35094 if(typeof r != "function"){
35095 if(r.hasPanel(panelId)){
35096 return r.showPanel(panelId);
35104 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35105 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35108 restoreState : function(provider){
35110 provider = Roo.state.Manager;
35112 var sm = new Roo.LayoutStateManager();
35113 sm.init(this, provider);
35119 * Adds a xtype elements to the layout.
35123 xtype : 'ContentPanel',
35130 xtype : 'NestedLayoutPanel',
35136 items : [ ... list of content panels or nested layout panels.. ]
35140 * @param {Object} cfg Xtype definition of item to add.
35142 addxtype : function(cfg)
35144 // basically accepts a pannel...
35145 // can accept a layout region..!?!?
35146 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35149 // theory? children can only be panels??
35151 //if (!cfg.xtype.match(/Panel$/)) {
35156 if (typeof(cfg.region) == 'undefined') {
35157 Roo.log("Failed to add Panel, region was not set");
35161 var region = cfg.region;
35167 xitems = cfg.items;
35174 case 'Content': // ContentPanel (el, cfg)
35175 case 'Scroll': // ContentPanel (el, cfg)
35177 cfg.autoCreate = true;
35178 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35180 // var el = this.el.createChild();
35181 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35184 this.add(region, ret);
35188 case 'TreePanel': // our new panel!
35189 cfg.el = this.el.createChild();
35190 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35191 this.add(region, ret);
35196 // create a new Layout (which is a Border Layout...
35198 var clayout = cfg.layout;
35199 clayout.el = this.el.createChild();
35200 clayout.items = clayout.items || [];
35204 // replace this exitems with the clayout ones..
35205 xitems = clayout.items;
35207 // force background off if it's in center...
35208 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35209 cfg.background = false;
35211 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35214 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35215 //console.log('adding nested layout panel ' + cfg.toSource());
35216 this.add(region, ret);
35217 nb = {}; /// find first...
35222 // needs grid and region
35224 //var el = this.getRegion(region).el.createChild();
35226 *var el = this.el.createChild();
35227 // create the grid first...
35228 cfg.grid.container = el;
35229 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35232 if (region == 'center' && this.active ) {
35233 cfg.background = false;
35236 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35238 this.add(region, ret);
35240 if (cfg.background) {
35241 // render grid on panel activation (if panel background)
35242 ret.on('activate', function(gp) {
35243 if (!gp.grid.rendered) {
35244 // gp.grid.render(el);
35248 // cfg.grid.render(el);
35254 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35255 // it was the old xcomponent building that caused this before.
35256 // espeically if border is the top element in the tree.
35266 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35268 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35269 this.add(region, ret);
35273 throw "Can not add '" + cfg.xtype + "' to Border";
35279 this.beginUpdate();
35283 Roo.each(xitems, function(i) {
35284 region = nb && i.region ? i.region : false;
35286 var add = ret.addxtype(i);
35289 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35290 if (!i.background) {
35291 abn[region] = nb[region] ;
35298 // make the last non-background panel active..
35299 //if (nb) { Roo.log(abn); }
35302 for(var r in abn) {
35303 region = this.getRegion(r);
35305 // tried using nb[r], but it does not work..
35307 region.showPanel(abn[r]);
35318 factory : function(cfg)
35321 var validRegions = Roo.bootstrap.layout.Border.regions;
35323 var target = cfg.region;
35326 var r = Roo.bootstrap.layout;
35330 return new r.North(cfg);
35332 return new r.South(cfg);
35334 return new r.East(cfg);
35336 return new r.West(cfg);
35338 return new r.Center(cfg);
35340 throw 'Layout region "'+target+'" not supported.';
35347 * Ext JS Library 1.1.1
35348 * Copyright(c) 2006-2007, Ext JS, LLC.
35350 * Originally Released Under LGPL - original licence link has changed is not relivant.
35353 * <script type="text/javascript">
35357 * @class Roo.bootstrap.layout.Basic
35358 * @extends Roo.util.Observable
35359 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35360 * and does not have a titlebar, tabs or any other features. All it does is size and position
35361 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35362 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35363 * @cfg {string} region the region that it inhabits..
35364 * @cfg {bool} skipConfig skip config?
35368 Roo.bootstrap.layout.Basic = function(config){
35370 this.mgr = config.mgr;
35372 this.position = config.region;
35374 var skipConfig = config.skipConfig;
35378 * @scope Roo.BasicLayoutRegion
35382 * @event beforeremove
35383 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35384 * @param {Roo.LayoutRegion} this
35385 * @param {Roo.ContentPanel} panel The panel
35386 * @param {Object} e The cancel event object
35388 "beforeremove" : true,
35390 * @event invalidated
35391 * Fires when the layout for this region is changed.
35392 * @param {Roo.LayoutRegion} this
35394 "invalidated" : true,
35396 * @event visibilitychange
35397 * Fires when this region is shown or hidden
35398 * @param {Roo.LayoutRegion} this
35399 * @param {Boolean} visibility true or false
35401 "visibilitychange" : true,
35403 * @event paneladded
35404 * Fires when a panel is added.
35405 * @param {Roo.LayoutRegion} this
35406 * @param {Roo.ContentPanel} panel The panel
35408 "paneladded" : true,
35410 * @event panelremoved
35411 * Fires when a panel is removed.
35412 * @param {Roo.LayoutRegion} this
35413 * @param {Roo.ContentPanel} panel The panel
35415 "panelremoved" : true,
35417 * @event beforecollapse
35418 * Fires when this region before collapse.
35419 * @param {Roo.LayoutRegion} this
35421 "beforecollapse" : true,
35424 * Fires when this region is collapsed.
35425 * @param {Roo.LayoutRegion} this
35427 "collapsed" : true,
35430 * Fires when this region is expanded.
35431 * @param {Roo.LayoutRegion} this
35436 * Fires when this region is slid into view.
35437 * @param {Roo.LayoutRegion} this
35439 "slideshow" : true,
35442 * Fires when this region slides out of view.
35443 * @param {Roo.LayoutRegion} this
35445 "slidehide" : true,
35447 * @event panelactivated
35448 * Fires when a panel is activated.
35449 * @param {Roo.LayoutRegion} this
35450 * @param {Roo.ContentPanel} panel The activated panel
35452 "panelactivated" : true,
35455 * Fires when the user resizes this region.
35456 * @param {Roo.LayoutRegion} this
35457 * @param {Number} newSize The new size (width for east/west, height for north/south)
35461 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35462 this.panels = new Roo.util.MixedCollection();
35463 this.panels.getKey = this.getPanelId.createDelegate(this);
35465 this.activePanel = null;
35466 // ensure listeners are added...
35468 if (config.listeners || config.events) {
35469 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35470 listeners : config.listeners || {},
35471 events : config.events || {}
35475 if(skipConfig !== true){
35476 this.applyConfig(config);
35480 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35482 getPanelId : function(p){
35486 applyConfig : function(config){
35487 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35488 this.config = config;
35493 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35494 * the width, for horizontal (north, south) the height.
35495 * @param {Number} newSize The new width or height
35497 resizeTo : function(newSize){
35498 var el = this.el ? this.el :
35499 (this.activePanel ? this.activePanel.getEl() : null);
35501 switch(this.position){
35504 el.setWidth(newSize);
35505 this.fireEvent("resized", this, newSize);
35509 el.setHeight(newSize);
35510 this.fireEvent("resized", this, newSize);
35516 getBox : function(){
35517 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35520 getMargins : function(){
35521 return this.margins;
35524 updateBox : function(box){
35526 var el = this.activePanel.getEl();
35527 el.dom.style.left = box.x + "px";
35528 el.dom.style.top = box.y + "px";
35529 this.activePanel.setSize(box.width, box.height);
35533 * Returns the container element for this region.
35534 * @return {Roo.Element}
35536 getEl : function(){
35537 return this.activePanel;
35541 * Returns true if this region is currently visible.
35542 * @return {Boolean}
35544 isVisible : function(){
35545 return this.activePanel ? true : false;
35548 setActivePanel : function(panel){
35549 panel = this.getPanel(panel);
35550 if(this.activePanel && this.activePanel != panel){
35551 this.activePanel.setActiveState(false);
35552 this.activePanel.getEl().setLeftTop(-10000,-10000);
35554 this.activePanel = panel;
35555 panel.setActiveState(true);
35557 panel.setSize(this.box.width, this.box.height);
35559 this.fireEvent("panelactivated", this, panel);
35560 this.fireEvent("invalidated");
35564 * Show the specified panel.
35565 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35566 * @return {Roo.ContentPanel} The shown panel or null
35568 showPanel : function(panel){
35569 panel = this.getPanel(panel);
35571 this.setActivePanel(panel);
35577 * Get the active panel for this region.
35578 * @return {Roo.ContentPanel} The active panel or null
35580 getActivePanel : function(){
35581 return this.activePanel;
35585 * Add the passed ContentPanel(s)
35586 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35587 * @return {Roo.ContentPanel} The panel added (if only one was added)
35589 add : function(panel){
35590 if(arguments.length > 1){
35591 for(var i = 0, len = arguments.length; i < len; i++) {
35592 this.add(arguments[i]);
35596 if(this.hasPanel(panel)){
35597 this.showPanel(panel);
35600 var el = panel.getEl();
35601 if(el.dom.parentNode != this.mgr.el.dom){
35602 this.mgr.el.dom.appendChild(el.dom);
35604 if(panel.setRegion){
35605 panel.setRegion(this);
35607 this.panels.add(panel);
35608 el.setStyle("position", "absolute");
35609 if(!panel.background){
35610 this.setActivePanel(panel);
35611 if(this.config.initialSize && this.panels.getCount()==1){
35612 this.resizeTo(this.config.initialSize);
35615 this.fireEvent("paneladded", this, panel);
35620 * Returns true if the panel is in this region.
35621 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35622 * @return {Boolean}
35624 hasPanel : function(panel){
35625 if(typeof panel == "object"){ // must be panel obj
35626 panel = panel.getId();
35628 return this.getPanel(panel) ? true : false;
35632 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35633 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35634 * @param {Boolean} preservePanel Overrides the config preservePanel option
35635 * @return {Roo.ContentPanel} The panel that was removed
35637 remove : function(panel, preservePanel){
35638 panel = this.getPanel(panel);
35643 this.fireEvent("beforeremove", this, panel, e);
35644 if(e.cancel === true){
35647 var panelId = panel.getId();
35648 this.panels.removeKey(panelId);
35653 * Returns the panel specified or null if it's not in this region.
35654 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35655 * @return {Roo.ContentPanel}
35657 getPanel : function(id){
35658 if(typeof id == "object"){ // must be panel obj
35661 return this.panels.get(id);
35665 * Returns this regions position (north/south/east/west/center).
35668 getPosition: function(){
35669 return this.position;
35673 * Ext JS Library 1.1.1
35674 * Copyright(c) 2006-2007, Ext JS, LLC.
35676 * Originally Released Under LGPL - original licence link has changed is not relivant.
35679 * <script type="text/javascript">
35683 * @class Roo.bootstrap.layout.Region
35684 * @extends Roo.bootstrap.layout.Basic
35685 * This class represents a region in a layout manager.
35687 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35688 * @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})
35689 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35690 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35691 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35692 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35693 * @cfg {String} title The title for the region (overrides panel titles)
35694 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35695 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35696 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35697 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35698 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35699 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35700 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35701 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35702 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35703 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35705 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35706 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35707 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35708 * @cfg {Number} width For East/West panels
35709 * @cfg {Number} height For North/South panels
35710 * @cfg {Boolean} split To show the splitter
35711 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35713 * @cfg {string} cls Extra CSS classes to add to region
35715 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35716 * @cfg {string} region the region that it inhabits..
35719 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35720 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35722 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35723 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35724 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35726 Roo.bootstrap.layout.Region = function(config)
35728 this.applyConfig(config);
35730 var mgr = config.mgr;
35731 var pos = config.region;
35732 config.skipConfig = true;
35733 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35736 this.onRender(mgr.el);
35739 this.visible = true;
35740 this.collapsed = false;
35741 this.unrendered_panels = [];
35744 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35746 position: '', // set by wrapper (eg. north/south etc..)
35747 unrendered_panels : null, // unrendered panels.
35748 createBody : function(){
35749 /** This region's body element
35750 * @type Roo.Element */
35751 this.bodyEl = this.el.createChild({
35753 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35757 onRender: function(ctr, pos)
35759 var dh = Roo.DomHelper;
35760 /** This region's container element
35761 * @type Roo.Element */
35762 this.el = dh.append(ctr.dom, {
35764 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35766 /** This region's title element
35767 * @type Roo.Element */
35769 this.titleEl = dh.append(this.el.dom,
35772 unselectable: "on",
35773 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35775 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35776 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35779 this.titleEl.enableDisplayMode();
35780 /** This region's title text element
35781 * @type HTMLElement */
35782 this.titleTextEl = this.titleEl.dom.firstChild;
35783 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35785 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35786 this.closeBtn.enableDisplayMode();
35787 this.closeBtn.on("click", this.closeClicked, this);
35788 this.closeBtn.hide();
35790 this.createBody(this.config);
35791 if(this.config.hideWhenEmpty){
35793 this.on("paneladded", this.validateVisibility, this);
35794 this.on("panelremoved", this.validateVisibility, this);
35796 if(this.autoScroll){
35797 this.bodyEl.setStyle("overflow", "auto");
35799 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35801 //if(c.titlebar !== false){
35802 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35803 this.titleEl.hide();
35805 this.titleEl.show();
35806 if(this.config.title){
35807 this.titleTextEl.innerHTML = this.config.title;
35811 if(this.config.collapsed){
35812 this.collapse(true);
35814 if(this.config.hidden){
35818 if (this.unrendered_panels && this.unrendered_panels.length) {
35819 for (var i =0;i< this.unrendered_panels.length; i++) {
35820 this.add(this.unrendered_panels[i]);
35822 this.unrendered_panels = null;
35828 applyConfig : function(c)
35831 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35832 var dh = Roo.DomHelper;
35833 if(c.titlebar !== false){
35834 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35835 this.collapseBtn.on("click", this.collapse, this);
35836 this.collapseBtn.enableDisplayMode();
35838 if(c.showPin === true || this.showPin){
35839 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35840 this.stickBtn.enableDisplayMode();
35841 this.stickBtn.on("click", this.expand, this);
35842 this.stickBtn.hide();
35847 /** This region's collapsed element
35848 * @type Roo.Element */
35851 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35852 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35855 if(c.floatable !== false){
35856 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35857 this.collapsedEl.on("click", this.collapseClick, this);
35860 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35861 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35862 id: "message", unselectable: "on", style:{"float":"left"}});
35863 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35865 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35866 this.expandBtn.on("click", this.expand, this);
35870 if(this.collapseBtn){
35871 this.collapseBtn.setVisible(c.collapsible == true);
35874 this.cmargins = c.cmargins || this.cmargins ||
35875 (this.position == "west" || this.position == "east" ?
35876 {top: 0, left: 2, right:2, bottom: 0} :
35877 {top: 2, left: 0, right:0, bottom: 2});
35879 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35882 this.bottomTabs = c.tabPosition != "top";
35884 this.autoScroll = c.autoScroll || false;
35889 this.duration = c.duration || .30;
35890 this.slideDuration = c.slideDuration || .45;
35895 * Returns true if this region is currently visible.
35896 * @return {Boolean}
35898 isVisible : function(){
35899 return this.visible;
35903 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35904 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35906 //setCollapsedTitle : function(title){
35907 // title = title || " ";
35908 // if(this.collapsedTitleTextEl){
35909 // this.collapsedTitleTextEl.innerHTML = title;
35913 getBox : function(){
35915 // if(!this.collapsed){
35916 b = this.el.getBox(false, true);
35918 // b = this.collapsedEl.getBox(false, true);
35923 getMargins : function(){
35924 return this.margins;
35925 //return this.collapsed ? this.cmargins : this.margins;
35928 highlight : function(){
35929 this.el.addClass("x-layout-panel-dragover");
35932 unhighlight : function(){
35933 this.el.removeClass("x-layout-panel-dragover");
35936 updateBox : function(box)
35938 if (!this.bodyEl) {
35939 return; // not rendered yet..
35943 if(!this.collapsed){
35944 this.el.dom.style.left = box.x + "px";
35945 this.el.dom.style.top = box.y + "px";
35946 this.updateBody(box.width, box.height);
35948 this.collapsedEl.dom.style.left = box.x + "px";
35949 this.collapsedEl.dom.style.top = box.y + "px";
35950 this.collapsedEl.setSize(box.width, box.height);
35953 this.tabs.autoSizeTabs();
35957 updateBody : function(w, h)
35960 this.el.setWidth(w);
35961 w -= this.el.getBorderWidth("rl");
35962 if(this.config.adjustments){
35963 w += this.config.adjustments[0];
35966 if(h !== null && h > 0){
35967 this.el.setHeight(h);
35968 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35969 h -= this.el.getBorderWidth("tb");
35970 if(this.config.adjustments){
35971 h += this.config.adjustments[1];
35973 this.bodyEl.setHeight(h);
35975 h = this.tabs.syncHeight(h);
35978 if(this.panelSize){
35979 w = w !== null ? w : this.panelSize.width;
35980 h = h !== null ? h : this.panelSize.height;
35982 if(this.activePanel){
35983 var el = this.activePanel.getEl();
35984 w = w !== null ? w : el.getWidth();
35985 h = h !== null ? h : el.getHeight();
35986 this.panelSize = {width: w, height: h};
35987 this.activePanel.setSize(w, h);
35989 if(Roo.isIE && this.tabs){
35990 this.tabs.el.repaint();
35995 * Returns the container element for this region.
35996 * @return {Roo.Element}
35998 getEl : function(){
36003 * Hides this region.
36006 //if(!this.collapsed){
36007 this.el.dom.style.left = "-2000px";
36010 // this.collapsedEl.dom.style.left = "-2000px";
36011 // this.collapsedEl.hide();
36013 this.visible = false;
36014 this.fireEvent("visibilitychange", this, false);
36018 * Shows this region if it was previously hidden.
36021 //if(!this.collapsed){
36024 // this.collapsedEl.show();
36026 this.visible = true;
36027 this.fireEvent("visibilitychange", this, true);
36030 closeClicked : function(){
36031 if(this.activePanel){
36032 this.remove(this.activePanel);
36036 collapseClick : function(e){
36038 e.stopPropagation();
36041 e.stopPropagation();
36047 * Collapses this region.
36048 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36051 collapse : function(skipAnim, skipCheck = false){
36052 if(this.collapsed) {
36056 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36058 this.collapsed = true;
36060 this.split.el.hide();
36062 if(this.config.animate && skipAnim !== true){
36063 this.fireEvent("invalidated", this);
36064 this.animateCollapse();
36066 this.el.setLocation(-20000,-20000);
36068 this.collapsedEl.show();
36069 this.fireEvent("collapsed", this);
36070 this.fireEvent("invalidated", this);
36076 animateCollapse : function(){
36081 * Expands this region if it was previously collapsed.
36082 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36083 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36086 expand : function(e, skipAnim){
36088 e.stopPropagation();
36090 if(!this.collapsed || this.el.hasActiveFx()) {
36094 this.afterSlideIn();
36097 this.collapsed = false;
36098 if(this.config.animate && skipAnim !== true){
36099 this.animateExpand();
36103 this.split.el.show();
36105 this.collapsedEl.setLocation(-2000,-2000);
36106 this.collapsedEl.hide();
36107 this.fireEvent("invalidated", this);
36108 this.fireEvent("expanded", this);
36112 animateExpand : function(){
36116 initTabs : function()
36118 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36120 var ts = new Roo.bootstrap.panel.Tabs({
36121 el: this.bodyEl.dom,
36122 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36123 disableTooltips: this.config.disableTabTips,
36124 toolbar : this.config.toolbar
36127 if(this.config.hideTabs){
36128 ts.stripWrap.setDisplayed(false);
36131 ts.resizeTabs = this.config.resizeTabs === true;
36132 ts.minTabWidth = this.config.minTabWidth || 40;
36133 ts.maxTabWidth = this.config.maxTabWidth || 250;
36134 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36135 ts.monitorResize = false;
36136 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36137 ts.bodyEl.addClass('roo-layout-tabs-body');
36138 this.panels.each(this.initPanelAsTab, this);
36141 initPanelAsTab : function(panel){
36142 var ti = this.tabs.addTab(
36146 this.config.closeOnTab && panel.isClosable(),
36149 if(panel.tabTip !== undefined){
36150 ti.setTooltip(panel.tabTip);
36152 ti.on("activate", function(){
36153 this.setActivePanel(panel);
36156 if(this.config.closeOnTab){
36157 ti.on("beforeclose", function(t, e){
36159 this.remove(panel);
36163 panel.tabItem = ti;
36168 updatePanelTitle : function(panel, title)
36170 if(this.activePanel == panel){
36171 this.updateTitle(title);
36174 var ti = this.tabs.getTab(panel.getEl().id);
36176 if(panel.tabTip !== undefined){
36177 ti.setTooltip(panel.tabTip);
36182 updateTitle : function(title){
36183 if(this.titleTextEl && !this.config.title){
36184 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36188 setActivePanel : function(panel)
36190 panel = this.getPanel(panel);
36191 if(this.activePanel && this.activePanel != panel){
36192 if(this.activePanel.setActiveState(false) === false){
36196 this.activePanel = panel;
36197 panel.setActiveState(true);
36198 if(this.panelSize){
36199 panel.setSize(this.panelSize.width, this.panelSize.height);
36202 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36204 this.updateTitle(panel.getTitle());
36206 this.fireEvent("invalidated", this);
36208 this.fireEvent("panelactivated", this, panel);
36212 * Shows the specified panel.
36213 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36214 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36216 showPanel : function(panel)
36218 panel = this.getPanel(panel);
36221 var tab = this.tabs.getTab(panel.getEl().id);
36222 if(tab.isHidden()){
36223 this.tabs.unhideTab(tab.id);
36227 this.setActivePanel(panel);
36234 * Get the active panel for this region.
36235 * @return {Roo.ContentPanel} The active panel or null
36237 getActivePanel : function(){
36238 return this.activePanel;
36241 validateVisibility : function(){
36242 if(this.panels.getCount() < 1){
36243 this.updateTitle(" ");
36244 this.closeBtn.hide();
36247 if(!this.isVisible()){
36254 * Adds the passed ContentPanel(s) to this region.
36255 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36256 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36258 add : function(panel)
36260 if(arguments.length > 1){
36261 for(var i = 0, len = arguments.length; i < len; i++) {
36262 this.add(arguments[i]);
36267 // if we have not been rendered yet, then we can not really do much of this..
36268 if (!this.bodyEl) {
36269 this.unrendered_panels.push(panel);
36276 if(this.hasPanel(panel)){
36277 this.showPanel(panel);
36280 panel.setRegion(this);
36281 this.panels.add(panel);
36282 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36283 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36284 // and hide them... ???
36285 this.bodyEl.dom.appendChild(panel.getEl().dom);
36286 if(panel.background !== true){
36287 this.setActivePanel(panel);
36289 this.fireEvent("paneladded", this, panel);
36296 this.initPanelAsTab(panel);
36300 if(panel.background !== true){
36301 this.tabs.activate(panel.getEl().id);
36303 this.fireEvent("paneladded", this, panel);
36308 * Hides the tab for the specified panel.
36309 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36311 hidePanel : function(panel){
36312 if(this.tabs && (panel = this.getPanel(panel))){
36313 this.tabs.hideTab(panel.getEl().id);
36318 * Unhides the tab for a previously hidden panel.
36319 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36321 unhidePanel : function(panel){
36322 if(this.tabs && (panel = this.getPanel(panel))){
36323 this.tabs.unhideTab(panel.getEl().id);
36327 clearPanels : function(){
36328 while(this.panels.getCount() > 0){
36329 this.remove(this.panels.first());
36334 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36335 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36336 * @param {Boolean} preservePanel Overrides the config preservePanel option
36337 * @return {Roo.ContentPanel} The panel that was removed
36339 remove : function(panel, preservePanel)
36341 panel = this.getPanel(panel);
36346 this.fireEvent("beforeremove", this, panel, e);
36347 if(e.cancel === true){
36350 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36351 var panelId = panel.getId();
36352 this.panels.removeKey(panelId);
36354 document.body.appendChild(panel.getEl().dom);
36357 this.tabs.removeTab(panel.getEl().id);
36358 }else if (!preservePanel){
36359 this.bodyEl.dom.removeChild(panel.getEl().dom);
36361 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36362 var p = this.panels.first();
36363 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36364 tempEl.appendChild(p.getEl().dom);
36365 this.bodyEl.update("");
36366 this.bodyEl.dom.appendChild(p.getEl().dom);
36368 this.updateTitle(p.getTitle());
36370 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36371 this.setActivePanel(p);
36373 panel.setRegion(null);
36374 if(this.activePanel == panel){
36375 this.activePanel = null;
36377 if(this.config.autoDestroy !== false && preservePanel !== true){
36378 try{panel.destroy();}catch(e){}
36380 this.fireEvent("panelremoved", this, panel);
36385 * Returns the TabPanel component used by this region
36386 * @return {Roo.TabPanel}
36388 getTabs : function(){
36392 createTool : function(parentEl, className){
36393 var btn = Roo.DomHelper.append(parentEl, {
36395 cls: "x-layout-tools-button",
36398 cls: "roo-layout-tools-button-inner " + className,
36402 btn.addClassOnOver("roo-layout-tools-button-over");
36407 * Ext JS Library 1.1.1
36408 * Copyright(c) 2006-2007, Ext JS, LLC.
36410 * Originally Released Under LGPL - original licence link has changed is not relivant.
36413 * <script type="text/javascript">
36419 * @class Roo.SplitLayoutRegion
36420 * @extends Roo.LayoutRegion
36421 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36423 Roo.bootstrap.layout.Split = function(config){
36424 this.cursor = config.cursor;
36425 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36428 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36430 splitTip : "Drag to resize.",
36431 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36432 useSplitTips : false,
36434 applyConfig : function(config){
36435 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36438 onRender : function(ctr,pos) {
36440 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36441 if(!this.config.split){
36446 var splitEl = Roo.DomHelper.append(ctr.dom, {
36448 id: this.el.id + "-split",
36449 cls: "roo-layout-split roo-layout-split-"+this.position,
36452 /** The SplitBar for this region
36453 * @type Roo.SplitBar */
36454 // does not exist yet...
36455 Roo.log([this.position, this.orientation]);
36457 this.split = new Roo.bootstrap.SplitBar({
36458 dragElement : splitEl,
36459 resizingElement: this.el,
36460 orientation : this.orientation
36463 this.split.on("moved", this.onSplitMove, this);
36464 this.split.useShim = this.config.useShim === true;
36465 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36466 if(this.useSplitTips){
36467 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36469 //if(config.collapsible){
36470 // this.split.el.on("dblclick", this.collapse, this);
36473 if(typeof this.config.minSize != "undefined"){
36474 this.split.minSize = this.config.minSize;
36476 if(typeof this.config.maxSize != "undefined"){
36477 this.split.maxSize = this.config.maxSize;
36479 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36480 this.hideSplitter();
36485 getHMaxSize : function(){
36486 var cmax = this.config.maxSize || 10000;
36487 var center = this.mgr.getRegion("center");
36488 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36491 getVMaxSize : function(){
36492 var cmax = this.config.maxSize || 10000;
36493 var center = this.mgr.getRegion("center");
36494 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36497 onSplitMove : function(split, newSize){
36498 this.fireEvent("resized", this, newSize);
36502 * Returns the {@link Roo.SplitBar} for this region.
36503 * @return {Roo.SplitBar}
36505 getSplitBar : function(){
36510 this.hideSplitter();
36511 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36514 hideSplitter : function(){
36516 this.split.el.setLocation(-2000,-2000);
36517 this.split.el.hide();
36523 this.split.el.show();
36525 Roo.bootstrap.layout.Split.superclass.show.call(this);
36528 beforeSlide: function(){
36529 if(Roo.isGecko){// firefox overflow auto bug workaround
36530 this.bodyEl.clip();
36532 this.tabs.bodyEl.clip();
36534 if(this.activePanel){
36535 this.activePanel.getEl().clip();
36537 if(this.activePanel.beforeSlide){
36538 this.activePanel.beforeSlide();
36544 afterSlide : function(){
36545 if(Roo.isGecko){// firefox overflow auto bug workaround
36546 this.bodyEl.unclip();
36548 this.tabs.bodyEl.unclip();
36550 if(this.activePanel){
36551 this.activePanel.getEl().unclip();
36552 if(this.activePanel.afterSlide){
36553 this.activePanel.afterSlide();
36559 initAutoHide : function(){
36560 if(this.autoHide !== false){
36561 if(!this.autoHideHd){
36562 var st = new Roo.util.DelayedTask(this.slideIn, this);
36563 this.autoHideHd = {
36564 "mouseout": function(e){
36565 if(!e.within(this.el, true)){
36569 "mouseover" : function(e){
36575 this.el.on(this.autoHideHd);
36579 clearAutoHide : function(){
36580 if(this.autoHide !== false){
36581 this.el.un("mouseout", this.autoHideHd.mouseout);
36582 this.el.un("mouseover", this.autoHideHd.mouseover);
36586 clearMonitor : function(){
36587 Roo.get(document).un("click", this.slideInIf, this);
36590 // these names are backwards but not changed for compat
36591 slideOut : function(){
36592 if(this.isSlid || this.el.hasActiveFx()){
36595 this.isSlid = true;
36596 if(this.collapseBtn){
36597 this.collapseBtn.hide();
36599 this.closeBtnState = this.closeBtn.getStyle('display');
36600 this.closeBtn.hide();
36602 this.stickBtn.show();
36605 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36606 this.beforeSlide();
36607 this.el.setStyle("z-index", 10001);
36608 this.el.slideIn(this.getSlideAnchor(), {
36609 callback: function(){
36611 this.initAutoHide();
36612 Roo.get(document).on("click", this.slideInIf, this);
36613 this.fireEvent("slideshow", this);
36620 afterSlideIn : function(){
36621 this.clearAutoHide();
36622 this.isSlid = false;
36623 this.clearMonitor();
36624 this.el.setStyle("z-index", "");
36625 if(this.collapseBtn){
36626 this.collapseBtn.show();
36628 this.closeBtn.setStyle('display', this.closeBtnState);
36630 this.stickBtn.hide();
36632 this.fireEvent("slidehide", this);
36635 slideIn : function(cb){
36636 if(!this.isSlid || this.el.hasActiveFx()){
36640 this.isSlid = false;
36641 this.beforeSlide();
36642 this.el.slideOut(this.getSlideAnchor(), {
36643 callback: function(){
36644 this.el.setLeftTop(-10000, -10000);
36646 this.afterSlideIn();
36654 slideInIf : function(e){
36655 if(!e.within(this.el)){
36660 animateCollapse : function(){
36661 this.beforeSlide();
36662 this.el.setStyle("z-index", 20000);
36663 var anchor = this.getSlideAnchor();
36664 this.el.slideOut(anchor, {
36665 callback : function(){
36666 this.el.setStyle("z-index", "");
36667 this.collapsedEl.slideIn(anchor, {duration:.3});
36669 this.el.setLocation(-10000,-10000);
36671 this.fireEvent("collapsed", this);
36678 animateExpand : function(){
36679 this.beforeSlide();
36680 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36681 this.el.setStyle("z-index", 20000);
36682 this.collapsedEl.hide({
36685 this.el.slideIn(this.getSlideAnchor(), {
36686 callback : function(){
36687 this.el.setStyle("z-index", "");
36690 this.split.el.show();
36692 this.fireEvent("invalidated", this);
36693 this.fireEvent("expanded", this);
36721 getAnchor : function(){
36722 return this.anchors[this.position];
36725 getCollapseAnchor : function(){
36726 return this.canchors[this.position];
36729 getSlideAnchor : function(){
36730 return this.sanchors[this.position];
36733 getAlignAdj : function(){
36734 var cm = this.cmargins;
36735 switch(this.position){
36751 getExpandAdj : function(){
36752 var c = this.collapsedEl, cm = this.cmargins;
36753 switch(this.position){
36755 return [-(cm.right+c.getWidth()+cm.left), 0];
36758 return [cm.right+c.getWidth()+cm.left, 0];
36761 return [0, -(cm.top+cm.bottom+c.getHeight())];
36764 return [0, cm.top+cm.bottom+c.getHeight()];
36770 * Ext JS Library 1.1.1
36771 * Copyright(c) 2006-2007, Ext JS, LLC.
36773 * Originally Released Under LGPL - original licence link has changed is not relivant.
36776 * <script type="text/javascript">
36779 * These classes are private internal classes
36781 Roo.bootstrap.layout.Center = function(config){
36782 config.region = "center";
36783 Roo.bootstrap.layout.Region.call(this, config);
36784 this.visible = true;
36785 this.minWidth = config.minWidth || 20;
36786 this.minHeight = config.minHeight || 20;
36789 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36791 // center panel can't be hidden
36795 // center panel can't be hidden
36798 getMinWidth: function(){
36799 return this.minWidth;
36802 getMinHeight: function(){
36803 return this.minHeight;
36816 Roo.bootstrap.layout.North = function(config)
36818 config.region = 'north';
36819 config.cursor = 'n-resize';
36821 Roo.bootstrap.layout.Split.call(this, config);
36825 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36826 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36827 this.split.el.addClass("roo-layout-split-v");
36829 var size = config.initialSize || config.height;
36830 if(typeof size != "undefined"){
36831 this.el.setHeight(size);
36834 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36836 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36840 getBox : function(){
36841 if(this.collapsed){
36842 return this.collapsedEl.getBox();
36844 var box = this.el.getBox();
36846 box.height += this.split.el.getHeight();
36851 updateBox : function(box){
36852 if(this.split && !this.collapsed){
36853 box.height -= this.split.el.getHeight();
36854 this.split.el.setLeft(box.x);
36855 this.split.el.setTop(box.y+box.height);
36856 this.split.el.setWidth(box.width);
36858 if(this.collapsed){
36859 this.updateBody(box.width, null);
36861 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36869 Roo.bootstrap.layout.South = function(config){
36870 config.region = 'south';
36871 config.cursor = 's-resize';
36872 Roo.bootstrap.layout.Split.call(this, config);
36874 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36875 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36876 this.split.el.addClass("roo-layout-split-v");
36878 var size = config.initialSize || config.height;
36879 if(typeof size != "undefined"){
36880 this.el.setHeight(size);
36884 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36885 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36886 getBox : function(){
36887 if(this.collapsed){
36888 return this.collapsedEl.getBox();
36890 var box = this.el.getBox();
36892 var sh = this.split.el.getHeight();
36899 updateBox : function(box){
36900 if(this.split && !this.collapsed){
36901 var sh = this.split.el.getHeight();
36904 this.split.el.setLeft(box.x);
36905 this.split.el.setTop(box.y-sh);
36906 this.split.el.setWidth(box.width);
36908 if(this.collapsed){
36909 this.updateBody(box.width, null);
36911 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36915 Roo.bootstrap.layout.East = function(config){
36916 config.region = "east";
36917 config.cursor = "e-resize";
36918 Roo.bootstrap.layout.Split.call(this, config);
36920 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36921 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36922 this.split.el.addClass("roo-layout-split-h");
36924 var size = config.initialSize || config.width;
36925 if(typeof size != "undefined"){
36926 this.el.setWidth(size);
36929 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36930 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36931 getBox : function(){
36932 if(this.collapsed){
36933 return this.collapsedEl.getBox();
36935 var box = this.el.getBox();
36937 var sw = this.split.el.getWidth();
36944 updateBox : function(box){
36945 if(this.split && !this.collapsed){
36946 var sw = this.split.el.getWidth();
36948 this.split.el.setLeft(box.x);
36949 this.split.el.setTop(box.y);
36950 this.split.el.setHeight(box.height);
36953 if(this.collapsed){
36954 this.updateBody(null, box.height);
36956 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36960 Roo.bootstrap.layout.West = function(config){
36961 config.region = "west";
36962 config.cursor = "w-resize";
36964 Roo.bootstrap.layout.Split.call(this, config);
36966 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36967 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36968 this.split.el.addClass("roo-layout-split-h");
36972 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36973 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36975 onRender: function(ctr, pos)
36977 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36978 var size = this.config.initialSize || this.config.width;
36979 if(typeof size != "undefined"){
36980 this.el.setWidth(size);
36984 getBox : function(){
36985 if(this.collapsed){
36986 return this.collapsedEl.getBox();
36988 var box = this.el.getBox();
36990 box.width += this.split.el.getWidth();
36995 updateBox : function(box){
36996 if(this.split && !this.collapsed){
36997 var sw = this.split.el.getWidth();
36999 this.split.el.setLeft(box.x+box.width);
37000 this.split.el.setTop(box.y);
37001 this.split.el.setHeight(box.height);
37003 if(this.collapsed){
37004 this.updateBody(null, box.height);
37006 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37009 Roo.namespace("Roo.bootstrap.panel");/*
37011 * Ext JS Library 1.1.1
37012 * Copyright(c) 2006-2007, Ext JS, LLC.
37014 * Originally Released Under LGPL - original licence link has changed is not relivant.
37017 * <script type="text/javascript">
37020 * @class Roo.ContentPanel
37021 * @extends Roo.util.Observable
37022 * A basic ContentPanel element.
37023 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
37024 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
37025 * @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
37026 * @cfg {Boolean} closable True if the panel can be closed/removed
37027 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
37028 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37029 * @cfg {Toolbar} toolbar A toolbar for this panel
37030 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37031 * @cfg {String} title The title for this panel
37032 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37033 * @cfg {String} url Calls {@link #setUrl} with this value
37034 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37035 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37036 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37037 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37038 * @cfg {Boolean} badges render the badges
37041 * Create a new ContentPanel.
37042 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37043 * @param {String/Object} config A string to set only the title or a config object
37044 * @param {String} content (optional) Set the HTML content for this panel
37045 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37047 Roo.bootstrap.panel.Content = function( config){
37049 this.tpl = config.tpl || false;
37051 var el = config.el;
37052 var content = config.content;
37054 if(config.autoCreate){ // xtype is available if this is called from factory
37057 this.el = Roo.get(el);
37058 if(!this.el && config && config.autoCreate){
37059 if(typeof config.autoCreate == "object"){
37060 if(!config.autoCreate.id){
37061 config.autoCreate.id = config.id||el;
37063 this.el = Roo.DomHelper.append(document.body,
37064 config.autoCreate, true);
37066 var elcfg = { tag: "div",
37067 cls: "roo-layout-inactive-content",
37071 elcfg.html = config.html;
37075 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37078 this.closable = false;
37079 this.loaded = false;
37080 this.active = false;
37083 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37085 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37087 this.wrapEl = this.el; //this.el.wrap();
37089 if (config.toolbar.items) {
37090 ti = config.toolbar.items ;
37091 delete config.toolbar.items ;
37095 this.toolbar.render(this.wrapEl, 'before');
37096 for(var i =0;i < ti.length;i++) {
37097 // Roo.log(['add child', items[i]]);
37098 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37100 this.toolbar.items = nitems;
37101 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37102 delete config.toolbar;
37106 // xtype created footer. - not sure if will work as we normally have to render first..
37107 if (this.footer && !this.footer.el && this.footer.xtype) {
37108 if (!this.wrapEl) {
37109 this.wrapEl = this.el.wrap();
37112 this.footer.container = this.wrapEl.createChild();
37114 this.footer = Roo.factory(this.footer, Roo);
37119 if(typeof config == "string"){
37120 this.title = config;
37122 Roo.apply(this, config);
37126 this.resizeEl = Roo.get(this.resizeEl, true);
37128 this.resizeEl = this.el;
37130 // handle view.xtype
37138 * Fires when this panel is activated.
37139 * @param {Roo.ContentPanel} this
37143 * @event deactivate
37144 * Fires when this panel is activated.
37145 * @param {Roo.ContentPanel} this
37147 "deactivate" : true,
37151 * Fires when this panel is resized if fitToFrame is true.
37152 * @param {Roo.ContentPanel} this
37153 * @param {Number} width The width after any component adjustments
37154 * @param {Number} height The height after any component adjustments
37160 * Fires when this tab is created
37161 * @param {Roo.ContentPanel} this
37172 if(this.autoScroll){
37173 this.resizeEl.setStyle("overflow", "auto");
37175 // fix randome scrolling
37176 //this.el.on('scroll', function() {
37177 // Roo.log('fix random scolling');
37178 // this.scrollTo('top',0);
37181 content = content || this.content;
37183 this.setContent(content);
37185 if(config && config.url){
37186 this.setUrl(this.url, this.params, this.loadOnce);
37191 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37193 if (this.view && typeof(this.view.xtype) != 'undefined') {
37194 this.view.el = this.el.appendChild(document.createElement("div"));
37195 this.view = Roo.factory(this.view);
37196 this.view.render && this.view.render(false, '');
37200 this.fireEvent('render', this);
37203 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37207 setRegion : function(region){
37208 this.region = region;
37209 this.setActiveClass(region && !this.background);
37213 setActiveClass: function(state)
37216 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37217 this.el.setStyle('position','relative');
37219 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37220 this.el.setStyle('position', 'absolute');
37225 * Returns the toolbar for this Panel if one was configured.
37226 * @return {Roo.Toolbar}
37228 getToolbar : function(){
37229 return this.toolbar;
37232 setActiveState : function(active)
37234 this.active = active;
37235 this.setActiveClass(active);
37237 if(this.fireEvent("deactivate", this) === false){
37242 this.fireEvent("activate", this);
37246 * Updates this panel's element
37247 * @param {String} content The new content
37248 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37250 setContent : function(content, loadScripts){
37251 this.el.update(content, loadScripts);
37254 ignoreResize : function(w, h){
37255 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37258 this.lastSize = {width: w, height: h};
37263 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37264 * @return {Roo.UpdateManager} The UpdateManager
37266 getUpdateManager : function(){
37267 return this.el.getUpdateManager();
37270 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37271 * @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:
37274 url: "your-url.php",
37275 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37276 callback: yourFunction,
37277 scope: yourObject, //(optional scope)
37280 text: "Loading...",
37285 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37286 * 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.
37287 * @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}
37288 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37289 * @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.
37290 * @return {Roo.ContentPanel} this
37293 var um = this.el.getUpdateManager();
37294 um.update.apply(um, arguments);
37300 * 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.
37301 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37302 * @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)
37303 * @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)
37304 * @return {Roo.UpdateManager} The UpdateManager
37306 setUrl : function(url, params, loadOnce){
37307 if(this.refreshDelegate){
37308 this.removeListener("activate", this.refreshDelegate);
37310 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37311 this.on("activate", this.refreshDelegate);
37312 return this.el.getUpdateManager();
37315 _handleRefresh : function(url, params, loadOnce){
37316 if(!loadOnce || !this.loaded){
37317 var updater = this.el.getUpdateManager();
37318 updater.update(url, params, this._setLoaded.createDelegate(this));
37322 _setLoaded : function(){
37323 this.loaded = true;
37327 * Returns this panel's id
37330 getId : function(){
37335 * Returns this panel's element - used by regiosn to add.
37336 * @return {Roo.Element}
37338 getEl : function(){
37339 return this.wrapEl || this.el;
37344 adjustForComponents : function(width, height)
37346 //Roo.log('adjustForComponents ');
37347 if(this.resizeEl != this.el){
37348 width -= this.el.getFrameWidth('lr');
37349 height -= this.el.getFrameWidth('tb');
37352 var te = this.toolbar.getEl();
37353 te.setWidth(width);
37354 height -= te.getHeight();
37357 var te = this.footer.getEl();
37358 te.setWidth(width);
37359 height -= te.getHeight();
37363 if(this.adjustments){
37364 width += this.adjustments[0];
37365 height += this.adjustments[1];
37367 return {"width": width, "height": height};
37370 setSize : function(width, height){
37371 if(this.fitToFrame && !this.ignoreResize(width, height)){
37372 if(this.fitContainer && this.resizeEl != this.el){
37373 this.el.setSize(width, height);
37375 var size = this.adjustForComponents(width, height);
37376 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37377 this.fireEvent('resize', this, size.width, size.height);
37382 * Returns this panel's title
37385 getTitle : function(){
37387 if (typeof(this.title) != 'object') {
37392 for (var k in this.title) {
37393 if (!this.title.hasOwnProperty(k)) {
37397 if (k.indexOf('-') >= 0) {
37398 var s = k.split('-');
37399 for (var i = 0; i<s.length; i++) {
37400 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37403 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37410 * Set this panel's title
37411 * @param {String} title
37413 setTitle : function(title){
37414 this.title = title;
37416 this.region.updatePanelTitle(this, title);
37421 * Returns true is this panel was configured to be closable
37422 * @return {Boolean}
37424 isClosable : function(){
37425 return this.closable;
37428 beforeSlide : function(){
37430 this.resizeEl.clip();
37433 afterSlide : function(){
37435 this.resizeEl.unclip();
37439 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37440 * Will fail silently if the {@link #setUrl} method has not been called.
37441 * This does not activate the panel, just updates its content.
37443 refresh : function(){
37444 if(this.refreshDelegate){
37445 this.loaded = false;
37446 this.refreshDelegate();
37451 * Destroys this panel
37453 destroy : function(){
37454 this.el.removeAllListeners();
37455 var tempEl = document.createElement("span");
37456 tempEl.appendChild(this.el.dom);
37457 tempEl.innerHTML = "";
37463 * form - if the content panel contains a form - this is a reference to it.
37464 * @type {Roo.form.Form}
37468 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37469 * This contains a reference to it.
37475 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37485 * @param {Object} cfg Xtype definition of item to add.
37489 getChildContainer: function () {
37490 return this.getEl();
37495 var ret = new Roo.factory(cfg);
37500 if (cfg.xtype.match(/^Form$/)) {
37503 //if (this.footer) {
37504 // el = this.footer.container.insertSibling(false, 'before');
37506 el = this.el.createChild();
37509 this.form = new Roo.form.Form(cfg);
37512 if ( this.form.allItems.length) {
37513 this.form.render(el.dom);
37517 // should only have one of theses..
37518 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37519 // views.. should not be just added - used named prop 'view''
37521 cfg.el = this.el.appendChild(document.createElement("div"));
37524 var ret = new Roo.factory(cfg);
37526 ret.render && ret.render(false, ''); // render blank..
37536 * @class Roo.bootstrap.panel.Grid
37537 * @extends Roo.bootstrap.panel.Content
37539 * Create a new GridPanel.
37540 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37541 * @param {Object} config A the config object
37547 Roo.bootstrap.panel.Grid = function(config)
37551 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37552 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37554 config.el = this.wrapper;
37555 //this.el = this.wrapper;
37557 if (config.container) {
37558 // ctor'ed from a Border/panel.grid
37561 this.wrapper.setStyle("overflow", "hidden");
37562 this.wrapper.addClass('roo-grid-container');
37567 if(config.toolbar){
37568 var tool_el = this.wrapper.createChild();
37569 this.toolbar = Roo.factory(config.toolbar);
37571 if (config.toolbar.items) {
37572 ti = config.toolbar.items ;
37573 delete config.toolbar.items ;
37577 this.toolbar.render(tool_el);
37578 for(var i =0;i < ti.length;i++) {
37579 // Roo.log(['add child', items[i]]);
37580 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37582 this.toolbar.items = nitems;
37584 delete config.toolbar;
37587 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37588 config.grid.scrollBody = true;;
37589 config.grid.monitorWindowResize = false; // turn off autosizing
37590 config.grid.autoHeight = false;
37591 config.grid.autoWidth = false;
37593 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37595 if (config.background) {
37596 // render grid on panel activation (if panel background)
37597 this.on('activate', function(gp) {
37598 if (!gp.grid.rendered) {
37599 gp.grid.render(this.wrapper);
37600 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37605 this.grid.render(this.wrapper);
37606 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37609 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37610 // ??? needed ??? config.el = this.wrapper;
37615 // xtype created footer. - not sure if will work as we normally have to render first..
37616 if (this.footer && !this.footer.el && this.footer.xtype) {
37618 var ctr = this.grid.getView().getFooterPanel(true);
37619 this.footer.dataSource = this.grid.dataSource;
37620 this.footer = Roo.factory(this.footer, Roo);
37621 this.footer.render(ctr);
37631 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37632 getId : function(){
37633 return this.grid.id;
37637 * Returns the grid for this panel
37638 * @return {Roo.bootstrap.Table}
37640 getGrid : function(){
37644 setSize : function(width, height){
37645 if(!this.ignoreResize(width, height)){
37646 var grid = this.grid;
37647 var size = this.adjustForComponents(width, height);
37648 var gridel = grid.getGridEl();
37649 gridel.setSize(size.width, size.height);
37651 var thd = grid.getGridEl().select('thead',true).first();
37652 var tbd = grid.getGridEl().select('tbody', true).first();
37654 tbd.setSize(width, height - thd.getHeight());
37663 beforeSlide : function(){
37664 this.grid.getView().scroller.clip();
37667 afterSlide : function(){
37668 this.grid.getView().scroller.unclip();
37671 destroy : function(){
37672 this.grid.destroy();
37674 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37679 * @class Roo.bootstrap.panel.Nest
37680 * @extends Roo.bootstrap.panel.Content
37682 * Create a new Panel, that can contain a layout.Border.
37685 * @param {Roo.BorderLayout} layout The layout for this panel
37686 * @param {String/Object} config A string to set only the title or a config object
37688 Roo.bootstrap.panel.Nest = function(config)
37690 // construct with only one argument..
37691 /* FIXME - implement nicer consturctors
37692 if (layout.layout) {
37694 layout = config.layout;
37695 delete config.layout;
37697 if (layout.xtype && !layout.getEl) {
37698 // then layout needs constructing..
37699 layout = Roo.factory(layout, Roo);
37703 config.el = config.layout.getEl();
37705 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37707 config.layout.monitorWindowResize = false; // turn off autosizing
37708 this.layout = config.layout;
37709 this.layout.getEl().addClass("roo-layout-nested-layout");
37716 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37718 setSize : function(width, height){
37719 if(!this.ignoreResize(width, height)){
37720 var size = this.adjustForComponents(width, height);
37721 var el = this.layout.getEl();
37722 if (size.height < 1) {
37723 el.setWidth(size.width);
37725 el.setSize(size.width, size.height);
37727 var touch = el.dom.offsetWidth;
37728 this.layout.layout();
37729 // ie requires a double layout on the first pass
37730 if(Roo.isIE && !this.initialized){
37731 this.initialized = true;
37732 this.layout.layout();
37737 // activate all subpanels if not currently active..
37739 setActiveState : function(active){
37740 this.active = active;
37741 this.setActiveClass(active);
37744 this.fireEvent("deactivate", this);
37748 this.fireEvent("activate", this);
37749 // not sure if this should happen before or after..
37750 if (!this.layout) {
37751 return; // should not happen..
37754 for (var r in this.layout.regions) {
37755 reg = this.layout.getRegion(r);
37756 if (reg.getActivePanel()) {
37757 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37758 reg.setActivePanel(reg.getActivePanel());
37761 if (!reg.panels.length) {
37764 reg.showPanel(reg.getPanel(0));
37773 * Returns the nested BorderLayout for this panel
37774 * @return {Roo.BorderLayout}
37776 getLayout : function(){
37777 return this.layout;
37781 * Adds a xtype elements to the layout of the nested panel
37785 xtype : 'ContentPanel',
37792 xtype : 'NestedLayoutPanel',
37798 items : [ ... list of content panels or nested layout panels.. ]
37802 * @param {Object} cfg Xtype definition of item to add.
37804 addxtype : function(cfg) {
37805 return this.layout.addxtype(cfg);
37810 * Ext JS Library 1.1.1
37811 * Copyright(c) 2006-2007, Ext JS, LLC.
37813 * Originally Released Under LGPL - original licence link has changed is not relivant.
37816 * <script type="text/javascript">
37819 * @class Roo.TabPanel
37820 * @extends Roo.util.Observable
37821 * A lightweight tab container.
37825 // basic tabs 1, built from existing content
37826 var tabs = new Roo.TabPanel("tabs1");
37827 tabs.addTab("script", "View Script");
37828 tabs.addTab("markup", "View Markup");
37829 tabs.activate("script");
37831 // more advanced tabs, built from javascript
37832 var jtabs = new Roo.TabPanel("jtabs");
37833 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37835 // set up the UpdateManager
37836 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37837 var updater = tab2.getUpdateManager();
37838 updater.setDefaultUrl("ajax1.htm");
37839 tab2.on('activate', updater.refresh, updater, true);
37841 // Use setUrl for Ajax loading
37842 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37843 tab3.setUrl("ajax2.htm", null, true);
37846 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37849 jtabs.activate("jtabs-1");
37852 * Create a new TabPanel.
37853 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37854 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37856 Roo.bootstrap.panel.Tabs = function(config){
37858 * The container element for this TabPanel.
37859 * @type Roo.Element
37861 this.el = Roo.get(config.el);
37864 if(typeof config == "boolean"){
37865 this.tabPosition = config ? "bottom" : "top";
37867 Roo.apply(this, config);
37871 if(this.tabPosition == "bottom"){
37872 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37873 this.el.addClass("roo-tabs-bottom");
37875 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37876 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37877 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37879 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37881 if(this.tabPosition != "bottom"){
37882 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37883 * @type Roo.Element
37885 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37886 this.el.addClass("roo-tabs-top");
37890 this.bodyEl.setStyle("position", "relative");
37892 this.active = null;
37893 this.activateDelegate = this.activate.createDelegate(this);
37898 * Fires when the active tab changes
37899 * @param {Roo.TabPanel} this
37900 * @param {Roo.TabPanelItem} activePanel The new active tab
37904 * @event beforetabchange
37905 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37906 * @param {Roo.TabPanel} this
37907 * @param {Object} e Set cancel to true on this object to cancel the tab change
37908 * @param {Roo.TabPanelItem} tab The tab being changed to
37910 "beforetabchange" : true
37913 Roo.EventManager.onWindowResize(this.onResize, this);
37914 this.cpad = this.el.getPadding("lr");
37915 this.hiddenCount = 0;
37918 // toolbar on the tabbar support...
37919 if (this.toolbar) {
37920 alert("no toolbar support yet");
37921 this.toolbar = false;
37923 var tcfg = this.toolbar;
37924 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37925 this.toolbar = new Roo.Toolbar(tcfg);
37926 if (Roo.isSafari) {
37927 var tbl = tcfg.container.child('table', true);
37928 tbl.setAttribute('width', '100%');
37936 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37939 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37941 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37943 tabPosition : "top",
37945 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37947 currentTabWidth : 0,
37949 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37953 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37957 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37959 preferredTabWidth : 175,
37961 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37963 resizeTabs : false,
37965 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37967 monitorResize : true,
37969 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37974 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37975 * @param {String} id The id of the div to use <b>or create</b>
37976 * @param {String} text The text for the tab
37977 * @param {String} content (optional) Content to put in the TabPanelItem body
37978 * @param {Boolean} closable (optional) True to create a close icon on the tab
37979 * @return {Roo.TabPanelItem} The created TabPanelItem
37981 addTab : function(id, text, content, closable, tpl)
37983 var item = new Roo.bootstrap.panel.TabItem({
37987 closable : closable,
37990 this.addTabItem(item);
37992 item.setContent(content);
37998 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37999 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38000 * @return {Roo.TabPanelItem}
38002 getTab : function(id){
38003 return this.items[id];
38007 * Hides the {@link Roo.TabPanelItem} with the specified id/index
38008 * @param {String/Number} id The id or index of the TabPanelItem to hide.
38010 hideTab : function(id){
38011 var t = this.items[id];
38014 this.hiddenCount++;
38015 this.autoSizeTabs();
38020 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38021 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38023 unhideTab : function(id){
38024 var t = this.items[id];
38026 t.setHidden(false);
38027 this.hiddenCount--;
38028 this.autoSizeTabs();
38033 * Adds an existing {@link Roo.TabPanelItem}.
38034 * @param {Roo.TabPanelItem} item The TabPanelItem to add
38036 addTabItem : function(item){
38037 this.items[item.id] = item;
38038 this.items.push(item);
38039 // if(this.resizeTabs){
38040 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38041 // this.autoSizeTabs();
38043 // item.autoSize();
38048 * Removes a {@link Roo.TabPanelItem}.
38049 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38051 removeTab : function(id){
38052 var items = this.items;
38053 var tab = items[id];
38054 if(!tab) { return; }
38055 var index = items.indexOf(tab);
38056 if(this.active == tab && items.length > 1){
38057 var newTab = this.getNextAvailable(index);
38062 this.stripEl.dom.removeChild(tab.pnode.dom);
38063 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38064 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38066 items.splice(index, 1);
38067 delete this.items[tab.id];
38068 tab.fireEvent("close", tab);
38069 tab.purgeListeners();
38070 this.autoSizeTabs();
38073 getNextAvailable : function(start){
38074 var items = this.items;
38076 // look for a next tab that will slide over to
38077 // replace the one being removed
38078 while(index < items.length){
38079 var item = items[++index];
38080 if(item && !item.isHidden()){
38084 // if one isn't found select the previous tab (on the left)
38087 var item = items[--index];
38088 if(item && !item.isHidden()){
38096 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38097 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38099 disableTab : function(id){
38100 var tab = this.items[id];
38101 if(tab && this.active != tab){
38107 * Enables a {@link Roo.TabPanelItem} that is disabled.
38108 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38110 enableTab : function(id){
38111 var tab = this.items[id];
38116 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38117 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38118 * @return {Roo.TabPanelItem} The TabPanelItem.
38120 activate : function(id){
38121 var tab = this.items[id];
38125 if(tab == this.active || tab.disabled){
38129 this.fireEvent("beforetabchange", this, e, tab);
38130 if(e.cancel !== true && !tab.disabled){
38132 this.active.hide();
38134 this.active = this.items[id];
38135 this.active.show();
38136 this.fireEvent("tabchange", this, this.active);
38142 * Gets the active {@link Roo.TabPanelItem}.
38143 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38145 getActiveTab : function(){
38146 return this.active;
38150 * Updates the tab body element to fit the height of the container element
38151 * for overflow scrolling
38152 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38154 syncHeight : function(targetHeight){
38155 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38156 var bm = this.bodyEl.getMargins();
38157 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38158 this.bodyEl.setHeight(newHeight);
38162 onResize : function(){
38163 if(this.monitorResize){
38164 this.autoSizeTabs();
38169 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38171 beginUpdate : function(){
38172 this.updating = true;
38176 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38178 endUpdate : function(){
38179 this.updating = false;
38180 this.autoSizeTabs();
38184 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38186 autoSizeTabs : function(){
38187 var count = this.items.length;
38188 var vcount = count - this.hiddenCount;
38189 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38192 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38193 var availWidth = Math.floor(w / vcount);
38194 var b = this.stripBody;
38195 if(b.getWidth() > w){
38196 var tabs = this.items;
38197 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38198 if(availWidth < this.minTabWidth){
38199 /*if(!this.sleft){ // incomplete scrolling code
38200 this.createScrollButtons();
38203 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38206 if(this.currentTabWidth < this.preferredTabWidth){
38207 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38213 * Returns the number of tabs in this TabPanel.
38216 getCount : function(){
38217 return this.items.length;
38221 * Resizes all the tabs to the passed width
38222 * @param {Number} The new width
38224 setTabWidth : function(width){
38225 this.currentTabWidth = width;
38226 for(var i = 0, len = this.items.length; i < len; i++) {
38227 if(!this.items[i].isHidden()) {
38228 this.items[i].setWidth(width);
38234 * Destroys this TabPanel
38235 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38237 destroy : function(removeEl){
38238 Roo.EventManager.removeResizeListener(this.onResize, this);
38239 for(var i = 0, len = this.items.length; i < len; i++){
38240 this.items[i].purgeListeners();
38242 if(removeEl === true){
38243 this.el.update("");
38248 createStrip : function(container)
38250 var strip = document.createElement("nav");
38251 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38252 container.appendChild(strip);
38256 createStripList : function(strip)
38258 // div wrapper for retard IE
38259 // returns the "tr" element.
38260 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38261 //'<div class="x-tabs-strip-wrap">'+
38262 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38263 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38264 return strip.firstChild; //.firstChild.firstChild.firstChild;
38266 createBody : function(container)
38268 var body = document.createElement("div");
38269 Roo.id(body, "tab-body");
38270 //Roo.fly(body).addClass("x-tabs-body");
38271 Roo.fly(body).addClass("tab-content");
38272 container.appendChild(body);
38275 createItemBody :function(bodyEl, id){
38276 var body = Roo.getDom(id);
38278 body = document.createElement("div");
38281 //Roo.fly(body).addClass("x-tabs-item-body");
38282 Roo.fly(body).addClass("tab-pane");
38283 bodyEl.insertBefore(body, bodyEl.firstChild);
38287 createStripElements : function(stripEl, text, closable, tpl)
38289 var td = document.createElement("li"); // was td..
38292 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38295 stripEl.appendChild(td);
38297 td.className = "x-tabs-closable";
38298 if(!this.closeTpl){
38299 this.closeTpl = new Roo.Template(
38300 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38301 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38302 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38305 var el = this.closeTpl.overwrite(td, {"text": text});
38306 var close = el.getElementsByTagName("div")[0];
38307 var inner = el.getElementsByTagName("em")[0];
38308 return {"el": el, "close": close, "inner": inner};
38311 // not sure what this is..
38312 // if(!this.tabTpl){
38313 //this.tabTpl = new Roo.Template(
38314 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38315 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38317 // this.tabTpl = new Roo.Template(
38318 // '<a href="#">' +
38319 // '<span unselectable="on"' +
38320 // (this.disableTooltips ? '' : ' title="{text}"') +
38321 // ' >{text}</span></a>'
38327 var template = tpl || this.tabTpl || false;
38331 template = new Roo.Template(
38333 '<span unselectable="on"' +
38334 (this.disableTooltips ? '' : ' title="{text}"') +
38335 ' >{text}</span></a>'
38339 switch (typeof(template)) {
38343 template = new Roo.Template(template);
38349 var el = template.overwrite(td, {"text": text});
38351 var inner = el.getElementsByTagName("span")[0];
38353 return {"el": el, "inner": inner};
38361 * @class Roo.TabPanelItem
38362 * @extends Roo.util.Observable
38363 * Represents an individual item (tab plus body) in a TabPanel.
38364 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38365 * @param {String} id The id of this TabPanelItem
38366 * @param {String} text The text for the tab of this TabPanelItem
38367 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38369 Roo.bootstrap.panel.TabItem = function(config){
38371 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38372 * @type Roo.TabPanel
38374 this.tabPanel = config.panel;
38376 * The id for this TabPanelItem
38379 this.id = config.id;
38381 this.disabled = false;
38383 this.text = config.text;
38385 this.loaded = false;
38386 this.closable = config.closable;
38389 * The body element for this TabPanelItem.
38390 * @type Roo.Element
38392 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38393 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38394 this.bodyEl.setStyle("display", "block");
38395 this.bodyEl.setStyle("zoom", "1");
38396 //this.hideAction();
38398 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38400 this.el = Roo.get(els.el);
38401 this.inner = Roo.get(els.inner, true);
38402 this.textEl = Roo.get(this.el.dom.firstChild, true);
38403 this.pnode = Roo.get(els.el.parentNode, true);
38404 // this.el.on("mousedown", this.onTabMouseDown, this);
38405 this.el.on("click", this.onTabClick, this);
38407 if(config.closable){
38408 var c = Roo.get(els.close, true);
38409 c.dom.title = this.closeText;
38410 c.addClassOnOver("close-over");
38411 c.on("click", this.closeClick, this);
38417 * Fires when this tab becomes the active tab.
38418 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38419 * @param {Roo.TabPanelItem} this
38423 * @event beforeclose
38424 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38425 * @param {Roo.TabPanelItem} this
38426 * @param {Object} e Set cancel to true on this object to cancel the close.
38428 "beforeclose": true,
38431 * Fires when this tab is closed.
38432 * @param {Roo.TabPanelItem} this
38436 * @event deactivate
38437 * Fires when this tab is no longer the active tab.
38438 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38439 * @param {Roo.TabPanelItem} this
38441 "deactivate" : true
38443 this.hidden = false;
38445 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38448 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38450 purgeListeners : function(){
38451 Roo.util.Observable.prototype.purgeListeners.call(this);
38452 this.el.removeAllListeners();
38455 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38458 this.pnode.addClass("active");
38461 this.tabPanel.stripWrap.repaint();
38463 this.fireEvent("activate", this.tabPanel, this);
38467 * Returns true if this tab is the active tab.
38468 * @return {Boolean}
38470 isActive : function(){
38471 return this.tabPanel.getActiveTab() == this;
38475 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38478 this.pnode.removeClass("active");
38480 this.fireEvent("deactivate", this.tabPanel, this);
38483 hideAction : function(){
38484 this.bodyEl.hide();
38485 this.bodyEl.setStyle("position", "absolute");
38486 this.bodyEl.setLeft("-20000px");
38487 this.bodyEl.setTop("-20000px");
38490 showAction : function(){
38491 this.bodyEl.setStyle("position", "relative");
38492 this.bodyEl.setTop("");
38493 this.bodyEl.setLeft("");
38494 this.bodyEl.show();
38498 * Set the tooltip for the tab.
38499 * @param {String} tooltip The tab's tooltip
38501 setTooltip : function(text){
38502 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38503 this.textEl.dom.qtip = text;
38504 this.textEl.dom.removeAttribute('title');
38506 this.textEl.dom.title = text;
38510 onTabClick : function(e){
38511 e.preventDefault();
38512 this.tabPanel.activate(this.id);
38515 onTabMouseDown : function(e){
38516 e.preventDefault();
38517 this.tabPanel.activate(this.id);
38520 getWidth : function(){
38521 return this.inner.getWidth();
38524 setWidth : function(width){
38525 var iwidth = width - this.pnode.getPadding("lr");
38526 this.inner.setWidth(iwidth);
38527 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38528 this.pnode.setWidth(width);
38532 * Show or hide the tab
38533 * @param {Boolean} hidden True to hide or false to show.
38535 setHidden : function(hidden){
38536 this.hidden = hidden;
38537 this.pnode.setStyle("display", hidden ? "none" : "");
38541 * Returns true if this tab is "hidden"
38542 * @return {Boolean}
38544 isHidden : function(){
38545 return this.hidden;
38549 * Returns the text for this tab
38552 getText : function(){
38556 autoSize : function(){
38557 //this.el.beginMeasure();
38558 this.textEl.setWidth(1);
38560 * #2804 [new] Tabs in Roojs
38561 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38563 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38564 //this.el.endMeasure();
38568 * Sets the text for the tab (Note: this also sets the tooltip text)
38569 * @param {String} text The tab's text and tooltip
38571 setText : function(text){
38573 this.textEl.update(text);
38574 this.setTooltip(text);
38575 //if(!this.tabPanel.resizeTabs){
38576 // this.autoSize();
38580 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38582 activate : function(){
38583 this.tabPanel.activate(this.id);
38587 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38589 disable : function(){
38590 if(this.tabPanel.active != this){
38591 this.disabled = true;
38592 this.pnode.addClass("disabled");
38597 * Enables this TabPanelItem if it was previously disabled.
38599 enable : function(){
38600 this.disabled = false;
38601 this.pnode.removeClass("disabled");
38605 * Sets the content for this TabPanelItem.
38606 * @param {String} content The content
38607 * @param {Boolean} loadScripts true to look for and load scripts
38609 setContent : function(content, loadScripts){
38610 this.bodyEl.update(content, loadScripts);
38614 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38615 * @return {Roo.UpdateManager} The UpdateManager
38617 getUpdateManager : function(){
38618 return this.bodyEl.getUpdateManager();
38622 * Set a URL to be used to load the content for this TabPanelItem.
38623 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38624 * @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)
38625 * @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)
38626 * @return {Roo.UpdateManager} The UpdateManager
38628 setUrl : function(url, params, loadOnce){
38629 if(this.refreshDelegate){
38630 this.un('activate', this.refreshDelegate);
38632 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38633 this.on("activate", this.refreshDelegate);
38634 return this.bodyEl.getUpdateManager();
38638 _handleRefresh : function(url, params, loadOnce){
38639 if(!loadOnce || !this.loaded){
38640 var updater = this.bodyEl.getUpdateManager();
38641 updater.update(url, params, this._setLoaded.createDelegate(this));
38646 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38647 * Will fail silently if the setUrl method has not been called.
38648 * This does not activate the panel, just updates its content.
38650 refresh : function(){
38651 if(this.refreshDelegate){
38652 this.loaded = false;
38653 this.refreshDelegate();
38658 _setLoaded : function(){
38659 this.loaded = true;
38663 closeClick : function(e){
38666 this.fireEvent("beforeclose", this, o);
38667 if(o.cancel !== true){
38668 this.tabPanel.removeTab(this.id);
38672 * The text displayed in the tooltip for the close icon.
38675 closeText : "Close this tab"
38678 * This script refer to:
38679 * Title: International Telephone Input
38680 * Author: Jack O'Connor
38681 * Code version: v12.1.12
38682 * Availability: https://github.com/jackocnr/intl-tel-input.git
38685 Roo.bootstrap.PhoneInputData = function() {
38688 "Afghanistan (افغانستان)",
38693 "Albania (Shqipëri)",
38698 "Algeria (الجزائر)",
38723 "Antigua and Barbuda",
38733 "Armenia (Հայաստան)",
38749 "Austria (Österreich)",
38754 "Azerbaijan (Azərbaycan)",
38764 "Bahrain (البحرين)",
38769 "Bangladesh (বাংলাদেশ)",
38779 "Belarus (Беларусь)",
38784 "Belgium (België)",
38814 "Bosnia and Herzegovina (Босна и Херцеговина)",
38829 "British Indian Ocean Territory",
38834 "British Virgin Islands",
38844 "Bulgaria (България)",
38854 "Burundi (Uburundi)",
38859 "Cambodia (កម្ពុជា)",
38864 "Cameroon (Cameroun)",
38873 ["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"]
38876 "Cape Verde (Kabu Verdi)",
38881 "Caribbean Netherlands",
38892 "Central African Republic (République centrafricaine)",
38912 "Christmas Island",
38918 "Cocos (Keeling) Islands",
38929 "Comoros (جزر القمر)",
38934 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38939 "Congo (Republic) (Congo-Brazzaville)",
38959 "Croatia (Hrvatska)",
38980 "Czech Republic (Česká republika)",
38985 "Denmark (Danmark)",
39000 "Dominican Republic (República Dominicana)",
39004 ["809", "829", "849"]
39022 "Equatorial Guinea (Guinea Ecuatorial)",
39042 "Falkland Islands (Islas Malvinas)",
39047 "Faroe Islands (Føroyar)",
39068 "French Guiana (Guyane française)",
39073 "French Polynesia (Polynésie française)",
39088 "Georgia (საქართველო)",
39093 "Germany (Deutschland)",
39113 "Greenland (Kalaallit Nunaat)",
39150 "Guinea-Bissau (Guiné Bissau)",
39175 "Hungary (Magyarország)",
39180 "Iceland (Ísland)",
39200 "Iraq (العراق)",
39216 "Israel (ישראל)",
39243 "Jordan (الأردن)",
39248 "Kazakhstan (Казахстан)",
39269 "Kuwait (الكويت)",
39274 "Kyrgyzstan (Кыргызстан)",
39284 "Latvia (Latvija)",
39289 "Lebanon (لبنان)",
39304 "Libya (ليبيا)",
39314 "Lithuania (Lietuva)",
39329 "Macedonia (FYROM) (Македонија)",
39334 "Madagascar (Madagasikara)",
39364 "Marshall Islands",
39374 "Mauritania (موريتانيا)",
39379 "Mauritius (Moris)",
39400 "Moldova (Republica Moldova)",
39410 "Mongolia (Монгол)",
39415 "Montenegro (Crna Gora)",
39425 "Morocco (المغرب)",
39431 "Mozambique (Moçambique)",
39436 "Myanmar (Burma) (မြန်မာ)",
39441 "Namibia (Namibië)",
39456 "Netherlands (Nederland)",
39461 "New Caledonia (Nouvelle-Calédonie)",
39496 "North Korea (조선 민주주의 인민 공화국)",
39501 "Northern Mariana Islands",
39517 "Pakistan (پاکستان)",
39527 "Palestine (فلسطين)",
39537 "Papua New Guinea",
39579 "Réunion (La Réunion)",
39585 "Romania (România)",
39601 "Saint Barthélemy",
39612 "Saint Kitts and Nevis",
39622 "Saint Martin (Saint-Martin (partie française))",
39628 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39633 "Saint Vincent and the Grenadines",
39648 "São Tomé and Príncipe (São Tomé e Príncipe)",
39653 "Saudi Arabia (المملكة العربية السعودية)",
39658 "Senegal (Sénégal)",
39688 "Slovakia (Slovensko)",
39693 "Slovenia (Slovenija)",
39703 "Somalia (Soomaaliya)",
39713 "South Korea (대한민국)",
39718 "South Sudan (جنوب السودان)",
39728 "Sri Lanka (ශ්රී ලංකාව)",
39733 "Sudan (السودان)",
39743 "Svalbard and Jan Mayen",
39754 "Sweden (Sverige)",
39759 "Switzerland (Schweiz)",
39764 "Syria (سوريا)",
39809 "Trinidad and Tobago",
39814 "Tunisia (تونس)",
39819 "Turkey (Türkiye)",
39829 "Turks and Caicos Islands",
39839 "U.S. Virgin Islands",
39849 "Ukraine (Україна)",
39854 "United Arab Emirates (الإمارات العربية المتحدة)",
39876 "Uzbekistan (Oʻzbekiston)",
39886 "Vatican City (Città del Vaticano)",
39897 "Vietnam (Việt Nam)",
39902 "Wallis and Futuna (Wallis-et-Futuna)",
39907 "Western Sahara (الصحراء الغربية)",
39913 "Yemen (اليمن)",
39937 * This script refer to:
39938 * Title: International Telephone Input
39939 * Author: Jack O'Connor
39940 * Code version: v12.1.12
39941 * Availability: https://github.com/jackocnr/intl-tel-input.git
39945 * @class Roo.bootstrap.PhoneInput
39946 * @extends Roo.bootstrap.TriggerField
39947 * An input with International dial-code selection
39949 * @cfg {String} defaultDialCode default '+852'
39950 * @cfg {Array} preferedCountries default []
39953 * Create a new PhoneInput.
39954 * @param {Object} config Configuration options
39957 Roo.bootstrap.PhoneInput = function(config) {
39958 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39961 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39963 listWidth: undefined,
39965 selectedClass: 'active',
39967 invalidClass : "has-warning",
39969 validClass: 'has-success',
39971 allowed: '0123456789',
39976 * @cfg {String} defaultDialCode The default dial code when initializing the input
39978 defaultDialCode: '+852',
39981 * @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
39983 preferedCountries: false,
39985 getAutoCreate : function()
39987 var data = Roo.bootstrap.PhoneInputData();
39988 var align = this.labelAlign || this.parentLabelAlign();
39991 this.allCountries = [];
39992 this.dialCodeMapping = [];
39994 for (var i = 0; i < data.length; i++) {
39996 this.allCountries[i] = {
40000 priority: c[3] || 0,
40001 areaCodes: c[4] || null
40003 this.dialCodeMapping[c[2]] = {
40006 priority: c[3] || 0,
40007 areaCodes: c[4] || null
40019 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40020 maxlength: this.max_length,
40021 cls : 'form-control tel-input',
40022 autocomplete: 'new-password'
40025 var hiddenInput = {
40028 cls: 'hidden-tel-input'
40032 hiddenInput.name = this.name;
40035 if (this.disabled) {
40036 input.disabled = true;
40039 var flag_container = {
40056 cls: this.hasFeedback ? 'has-feedback' : '',
40062 cls: 'dial-code-holder',
40069 cls: 'roo-select2-container input-group',
40076 if (this.fieldLabel.length) {
40079 tooltip: 'This field is required'
40085 cls: 'control-label',
40091 html: this.fieldLabel
40094 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40100 if(this.indicatorpos == 'right') {
40101 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40108 if(align == 'left') {
40116 if(this.labelWidth > 12){
40117 label.style = "width: " + this.labelWidth + 'px';
40119 if(this.labelWidth < 13 && this.labelmd == 0){
40120 this.labelmd = this.labelWidth;
40122 if(this.labellg > 0){
40123 label.cls += ' col-lg-' + this.labellg;
40124 input.cls += ' col-lg-' + (12 - this.labellg);
40126 if(this.labelmd > 0){
40127 label.cls += ' col-md-' + this.labelmd;
40128 container.cls += ' col-md-' + (12 - this.labelmd);
40130 if(this.labelsm > 0){
40131 label.cls += ' col-sm-' + this.labelsm;
40132 container.cls += ' col-sm-' + (12 - this.labelsm);
40134 if(this.labelxs > 0){
40135 label.cls += ' col-xs-' + this.labelxs;
40136 container.cls += ' col-xs-' + (12 - this.labelxs);
40146 var settings = this;
40148 ['xs','sm','md','lg'].map(function(size){
40149 if (settings[size]) {
40150 cfg.cls += ' col-' + size + '-' + settings[size];
40154 this.store = new Roo.data.Store({
40155 proxy : new Roo.data.MemoryProxy({}),
40156 reader : new Roo.data.JsonReader({
40167 'name' : 'dialCode',
40171 'name' : 'priority',
40175 'name' : 'areaCodes',
40182 if(!this.preferedCountries) {
40183 this.preferedCountries = [
40190 var p = this.preferedCountries.reverse();
40193 for (var i = 0; i < p.length; i++) {
40194 for (var j = 0; j < this.allCountries.length; j++) {
40195 if(this.allCountries[j].iso2 == p[i]) {
40196 var t = this.allCountries[j];
40197 this.allCountries.splice(j,1);
40198 this.allCountries.unshift(t);
40204 this.store.proxy.data = {
40206 data: this.allCountries
40212 initEvents : function()
40215 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40217 this.indicator = this.indicatorEl();
40218 this.flag = this.flagEl();
40219 this.dialCodeHolder = this.dialCodeHolderEl();
40221 this.trigger = this.el.select('div.flag-box',true).first();
40222 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40227 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40228 _this.list.setWidth(lw);
40231 this.list.on('mouseover', this.onViewOver, this);
40232 this.list.on('mousemove', this.onViewMove, this);
40233 this.inputEl().on("keyup", this.onKeyUp, this);
40234 this.inputEl().on("keypress", this.onKeyPress, this);
40236 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40238 this.view = new Roo.View(this.list, this.tpl, {
40239 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40242 this.view.on('click', this.onViewClick, this);
40243 this.setValue(this.defaultDialCode);
40246 onTriggerClick : function(e)
40248 Roo.log('trigger click');
40253 if(this.isExpanded()){
40255 this.hasFocus = false;
40257 this.store.load({});
40258 this.hasFocus = true;
40263 isExpanded : function()
40265 return this.list.isVisible();
40268 collapse : function()
40270 if(!this.isExpanded()){
40274 Roo.get(document).un('mousedown', this.collapseIf, this);
40275 Roo.get(document).un('mousewheel', this.collapseIf, this);
40276 this.fireEvent('collapse', this);
40280 expand : function()
40284 if(this.isExpanded() || !this.hasFocus){
40288 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40289 this.list.setWidth(lw);
40292 this.restrictHeight();
40294 Roo.get(document).on('mousedown', this.collapseIf, this);
40295 Roo.get(document).on('mousewheel', this.collapseIf, this);
40297 this.fireEvent('expand', this);
40300 restrictHeight : function()
40302 this.list.alignTo(this.inputEl(), this.listAlign);
40303 this.list.alignTo(this.inputEl(), this.listAlign);
40306 onViewOver : function(e, t)
40308 if(this.inKeyMode){
40311 var item = this.view.findItemFromChild(t);
40314 var index = this.view.indexOf(item);
40315 this.select(index, false);
40320 onViewClick : function(view, doFocus, el, e)
40322 var index = this.view.getSelectedIndexes()[0];
40324 var r = this.store.getAt(index);
40327 this.onSelect(r, index);
40329 if(doFocus !== false && !this.blockFocus){
40330 this.inputEl().focus();
40334 onViewMove : function(e, t)
40336 this.inKeyMode = false;
40339 select : function(index, scrollIntoView)
40341 this.selectedIndex = index;
40342 this.view.select(index);
40343 if(scrollIntoView !== false){
40344 var el = this.view.getNode(index);
40346 this.list.scrollChildIntoView(el, false);
40351 createList : function()
40353 this.list = Roo.get(document.body).createChild({
40355 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40356 style: 'display:none'
40359 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40362 collapseIf : function(e)
40364 var in_combo = e.within(this.el);
40365 var in_list = e.within(this.list);
40366 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40368 if (in_combo || in_list || is_list) {
40374 onSelect : function(record, index)
40376 if(this.fireEvent('beforeselect', this, record, index) !== false){
40378 this.setFlagClass(record.data.iso2);
40379 this.setDialCode(record.data.dialCode);
40380 this.hasFocus = false;
40382 this.fireEvent('select', this, record, index);
40386 flagEl : function()
40388 var flag = this.el.select('div.flag',true).first();
40395 dialCodeHolderEl : function()
40397 var d = this.el.select('input.dial-code-holder',true).first();
40404 setDialCode : function(v)
40406 this.dialCodeHolder.dom.value = '+'+v;
40409 setFlagClass : function(n)
40411 this.flag.dom.className = 'flag '+n;
40414 getValue : function()
40416 var v = this.inputEl().getValue();
40417 if(this.dialCodeHolder) {
40418 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40423 setValue : function(v)
40425 var d = this.getDialCode(v);
40427 //invalid dial code
40428 if(v.length == 0 || !d || d.length == 0) {
40430 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40431 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40437 this.setFlagClass(this.dialCodeMapping[d].iso2);
40438 this.setDialCode(d);
40439 this.inputEl().dom.value = v.replace('+'+d,'');
40440 this.hiddenEl().dom.value = this.getValue();
40445 getDialCode : function(v)
40449 if (v.length == 0) {
40450 return this.dialCodeHolder.dom.value;
40454 if (v.charAt(0) != "+") {
40457 var numericChars = "";
40458 for (var i = 1; i < v.length; i++) {
40459 var c = v.charAt(i);
40462 if (this.dialCodeMapping[numericChars]) {
40463 dialCode = v.substr(1, i);
40465 if (numericChars.length == 4) {
40475 this.setValue(this.defaultDialCode);
40479 hiddenEl : function()
40481 return this.el.select('input.hidden-tel-input',true).first();
40484 // after setting val
40485 onKeyUp : function(e){
40486 this.setValue(this.getValue());
40489 onKeyPress : function(e){
40490 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40497 * @class Roo.bootstrap.MoneyField
40498 * @extends Roo.bootstrap.ComboBox
40499 * Bootstrap MoneyField class
40502 * Create a new MoneyField.
40503 * @param {Object} config Configuration options
40506 Roo.bootstrap.MoneyField = function(config) {
40508 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40512 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40515 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40517 allowDecimals : true,
40519 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40521 decimalSeparator : ".",
40523 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40525 decimalPrecision : 0,
40527 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40529 allowNegative : true,
40531 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40535 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40537 minValue : Number.NEGATIVE_INFINITY,
40539 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40541 maxValue : Number.MAX_VALUE,
40543 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40545 minText : "The minimum value for this field is {0}",
40547 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40549 maxText : "The maximum value for this field is {0}",
40551 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40552 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40554 nanText : "{0} is not a valid number",
40556 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40560 * @cfg {String} defaults currency of the MoneyField
40561 * value should be in lkey
40563 defaultCurrency : false,
40565 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40567 thousandsDelimiter : false,
40569 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40580 getAutoCreate : function()
40582 var align = this.labelAlign || this.parentLabelAlign();
40594 cls : 'form-control roo-money-amount-input',
40595 autocomplete: 'new-password'
40598 var hiddenInput = {
40602 cls: 'hidden-number-input'
40605 if(this.max_length) {
40606 input.maxlength = this.max_length;
40610 hiddenInput.name = this.name;
40613 if (this.disabled) {
40614 input.disabled = true;
40617 var clg = 12 - this.inputlg;
40618 var cmd = 12 - this.inputmd;
40619 var csm = 12 - this.inputsm;
40620 var cxs = 12 - this.inputxs;
40624 cls : 'row roo-money-field',
40628 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40632 cls: 'roo-select2-container input-group',
40636 cls : 'form-control roo-money-currency-input',
40637 autocomplete: 'new-password',
40639 name : this.currencyName
40643 cls : 'input-group-addon',
40657 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40661 cls: this.hasFeedback ? 'has-feedback' : '',
40672 if (this.fieldLabel.length) {
40675 tooltip: 'This field is required'
40681 cls: 'control-label',
40687 html: this.fieldLabel
40690 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40696 if(this.indicatorpos == 'right') {
40697 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40704 if(align == 'left') {
40712 if(this.labelWidth > 12){
40713 label.style = "width: " + this.labelWidth + 'px';
40715 if(this.labelWidth < 13 && this.labelmd == 0){
40716 this.labelmd = this.labelWidth;
40718 if(this.labellg > 0){
40719 label.cls += ' col-lg-' + this.labellg;
40720 input.cls += ' col-lg-' + (12 - this.labellg);
40722 if(this.labelmd > 0){
40723 label.cls += ' col-md-' + this.labelmd;
40724 container.cls += ' col-md-' + (12 - this.labelmd);
40726 if(this.labelsm > 0){
40727 label.cls += ' col-sm-' + this.labelsm;
40728 container.cls += ' col-sm-' + (12 - this.labelsm);
40730 if(this.labelxs > 0){
40731 label.cls += ' col-xs-' + this.labelxs;
40732 container.cls += ' col-xs-' + (12 - this.labelxs);
40743 var settings = this;
40745 ['xs','sm','md','lg'].map(function(size){
40746 if (settings[size]) {
40747 cfg.cls += ' col-' + size + '-' + settings[size];
40754 initEvents : function()
40756 this.indicator = this.indicatorEl();
40758 this.initCurrencyEvent();
40760 this.initNumberEvent();
40763 initCurrencyEvent : function()
40766 throw "can not find store for combo";
40769 this.store = Roo.factory(this.store, Roo.data);
40770 this.store.parent = this;
40774 this.triggerEl = this.el.select('.input-group-addon', true).first();
40776 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40781 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40782 _this.list.setWidth(lw);
40785 this.list.on('mouseover', this.onViewOver, this);
40786 this.list.on('mousemove', this.onViewMove, this);
40787 this.list.on('scroll', this.onViewScroll, this);
40790 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40793 this.view = new Roo.View(this.list, this.tpl, {
40794 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40797 this.view.on('click', this.onViewClick, this);
40799 this.store.on('beforeload', this.onBeforeLoad, this);
40800 this.store.on('load', this.onLoad, this);
40801 this.store.on('loadexception', this.onLoadException, this);
40803 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40804 "up" : function(e){
40805 this.inKeyMode = true;
40809 "down" : function(e){
40810 if(!this.isExpanded()){
40811 this.onTriggerClick();
40813 this.inKeyMode = true;
40818 "enter" : function(e){
40821 if(this.fireEvent("specialkey", this, e)){
40822 this.onViewClick(false);
40828 "esc" : function(e){
40832 "tab" : function(e){
40835 if(this.fireEvent("specialkey", this, e)){
40836 this.onViewClick(false);
40844 doRelay : function(foo, bar, hname){
40845 if(hname == 'down' || this.scope.isExpanded()){
40846 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40854 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40858 initNumberEvent : function(e)
40860 this.inputEl().on("keydown" , this.fireKey, this);
40861 this.inputEl().on("focus", this.onFocus, this);
40862 this.inputEl().on("blur", this.onBlur, this);
40864 this.inputEl().relayEvent('keyup', this);
40866 if(this.indicator){
40867 this.indicator.addClass('invisible');
40870 this.originalValue = this.getValue();
40872 if(this.validationEvent == 'keyup'){
40873 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40874 this.inputEl().on('keyup', this.filterValidation, this);
40876 else if(this.validationEvent !== false){
40877 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40880 if(this.selectOnFocus){
40881 this.on("focus", this.preFocus, this);
40884 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40885 this.inputEl().on("keypress", this.filterKeys, this);
40887 this.inputEl().relayEvent('keypress', this);
40890 var allowed = "0123456789";
40892 if(this.allowDecimals){
40893 allowed += this.decimalSeparator;
40896 if(this.allowNegative){
40900 if(this.thousandsDelimiter) {
40904 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40906 var keyPress = function(e){
40908 var k = e.getKey();
40910 var c = e.getCharCode();
40913 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40914 allowed.indexOf(String.fromCharCode(c)) === -1
40920 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40924 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40929 this.inputEl().on("keypress", keyPress, this);
40933 onTriggerClick : function(e)
40940 this.loadNext = false;
40942 if(this.isExpanded()){
40947 this.hasFocus = true;
40949 if(this.triggerAction == 'all') {
40950 this.doQuery(this.allQuery, true);
40954 this.doQuery(this.getRawValue());
40957 getCurrency : function()
40959 var v = this.currencyEl().getValue();
40964 restrictHeight : function()
40966 this.list.alignTo(this.currencyEl(), this.listAlign);
40967 this.list.alignTo(this.currencyEl(), this.listAlign);
40970 onViewClick : function(view, doFocus, el, e)
40972 var index = this.view.getSelectedIndexes()[0];
40974 var r = this.store.getAt(index);
40977 this.onSelect(r, index);
40981 onSelect : function(record, index){
40983 if(this.fireEvent('beforeselect', this, record, index) !== false){
40985 this.setFromCurrencyData(index > -1 ? record.data : false);
40989 this.fireEvent('select', this, record, index);
40993 setFromCurrencyData : function(o)
40997 this.lastCurrency = o;
40999 if (this.currencyField) {
41000 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41002 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
41005 this.lastSelectionText = currency;
41007 //setting default currency
41008 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41009 this.setCurrency(this.defaultCurrency);
41013 this.setCurrency(currency);
41016 setFromData : function(o)
41020 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41022 this.setFromCurrencyData(c);
41027 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41029 Roo.log('no value set for '+ (this.name ? this.name : this.id));
41032 this.setValue(value);
41036 setCurrency : function(v)
41038 this.currencyValue = v;
41041 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41046 setValue : function(v)
41048 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41054 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41056 this.inputEl().dom.value = (v == '') ? '' :
41057 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41059 if(!this.allowZero && v === '0') {
41060 this.hiddenEl().dom.value = '';
41061 this.inputEl().dom.value = '';
41068 getRawValue : function()
41070 var v = this.inputEl().getValue();
41075 getValue : function()
41077 return this.fixPrecision(this.parseValue(this.getRawValue()));
41080 parseValue : function(value)
41082 if(this.thousandsDelimiter) {
41084 r = new RegExp(",", "g");
41085 value = value.replace(r, "");
41088 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41089 return isNaN(value) ? '' : value;
41093 fixPrecision : function(value)
41095 if(this.thousandsDelimiter) {
41097 r = new RegExp(",", "g");
41098 value = value.replace(r, "");
41101 var nan = isNaN(value);
41103 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41104 return nan ? '' : value;
41106 return parseFloat(value).toFixed(this.decimalPrecision);
41109 decimalPrecisionFcn : function(v)
41111 return Math.floor(v);
41114 validateValue : function(value)
41116 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41120 var num = this.parseValue(value);
41123 this.markInvalid(String.format(this.nanText, value));
41127 if(num < this.minValue){
41128 this.markInvalid(String.format(this.minText, this.minValue));
41132 if(num > this.maxValue){
41133 this.markInvalid(String.format(this.maxText, this.maxValue));
41140 validate : function()
41142 if(this.disabled || this.allowBlank){
41147 var currency = this.getCurrency();
41149 if(this.validateValue(this.getRawValue()) && currency.length){
41154 this.markInvalid();
41158 getName: function()
41163 beforeBlur : function()
41169 var v = this.parseValue(this.getRawValue());
41176 onBlur : function()
41180 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41181 //this.el.removeClass(this.focusClass);
41184 this.hasFocus = false;
41186 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41190 var v = this.getValue();
41192 if(String(v) !== String(this.startValue)){
41193 this.fireEvent('change', this, v, this.startValue);
41196 this.fireEvent("blur", this);
41199 inputEl : function()
41201 return this.el.select('.roo-money-amount-input', true).first();
41204 currencyEl : function()
41206 return this.el.select('.roo-money-currency-input', true).first();
41209 hiddenEl : function()
41211 return this.el.select('input.hidden-number-input',true).first();