2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = (
9 Roo.each(document.styleSheets[0], function(s) {
10 if (s.href.match(/css-bootstrap4/)) {
18 * base class for bootstrap elements.
22 Roo.bootstrap = Roo.bootstrap || {};
24 * @class Roo.bootstrap.Component
25 * @extends Roo.Component
26 * Bootstrap Component base class
27 * @cfg {String} cls css class
28 * @cfg {String} style any extra css
29 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
31 * @cfg {string} dataId cutomer id
32 * @cfg {string} name Specifies name attribute
33 * @cfg {string} tooltip Text for the tooltip
34 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
35 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
38 * Do not use directly - it does not do anything..
39 * @param {Object} config The config object
44 Roo.bootstrap.Component = function(config){
45 Roo.bootstrap.Component.superclass.constructor.call(this, config);
49 * @event childrenrendered
50 * Fires when the children have been rendered..
51 * @param {Roo.bootstrap.Component} this
53 "childrenrendered" : true
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
65 allowDomMove : false, // to stop relocations in parent onRender...
75 * Initialize Events for the element
77 initEvents : function() { },
83 can_build_overlaid : true,
85 container_method : false,
92 // returns the parent component..
93 return Roo.ComponentMgr.get(this.parentId)
99 onRender : function(ct, position)
101 // Roo.log("Call onRender: " + this.xtype);
103 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
106 if (this.el.attr('xtype')) {
107 this.el.attr('xtypex', this.el.attr('xtype'));
108 this.el.dom.removeAttribute('xtype');
118 var cfg = Roo.apply({}, this.getAutoCreate());
120 cfg.id = this.id || Roo.id();
122 // fill in the extra attributes
123 if (this.xattr && typeof(this.xattr) =='object') {
124 for (var i in this.xattr) {
125 cfg[i] = this.xattr[i];
130 cfg.dataId = this.dataId;
134 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
137 if (this.style) { // fixme needs to support more complex style data.
138 cfg.style = this.style;
142 cfg.name = this.name;
145 this.el = ct.createChild(cfg, position);
148 this.tooltipEl().attr('tooltip', this.tooltip);
151 if(this.tabIndex !== undefined){
152 this.el.dom.setAttribute('tabIndex', this.tabIndex);
159 * Fetch the element to add children to
160 * @return {Roo.Element} defaults to this.el
162 getChildContainer : function()
167 * Fetch the element to display the tooltip on.
168 * @return {Roo.Element} defaults to this.el
170 tooltipEl : function()
175 addxtype : function(tree,cntr)
179 cn = Roo.factory(tree);
180 //Roo.log(['addxtype', cn]);
182 cn.parentType = this.xtype; //??
183 cn.parentId = this.id;
185 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186 if (typeof(cn.container_method) == 'string') {
187 cntr = cn.container_method;
191 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
193 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
195 var build_from_html = Roo.XComponent.build_from_html;
197 var is_body = (tree.xtype == 'Body') ;
199 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
201 var self_cntr_el = Roo.get(this[cntr](false));
203 // do not try and build conditional elements
204 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
208 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210 return this.addxtypeChild(tree,cntr, is_body);
213 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
216 return this.addxtypeChild(Roo.apply({}, tree),cntr);
219 Roo.log('skipping render');
225 if (!build_from_html) {
229 // this i think handles overlaying multiple children of the same type
230 // with the sam eelement.. - which might be buggy..
232 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
238 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
242 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
249 addxtypeChild : function (tree, cntr, is_body)
251 Roo.debug && Roo.log('addxtypeChild:' + cntr);
253 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
256 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257 (typeof(tree['flexy:foreach']) != 'undefined');
261 skip_children = false;
262 // render the element if it's not BODY.
265 // if parent was disabled, then do not try and create the children..
266 if(!this[cntr](true)){
271 cn = Roo.factory(tree);
273 cn.parentType = this.xtype; //??
274 cn.parentId = this.id;
276 var build_from_html = Roo.XComponent.build_from_html;
279 // does the container contain child eleemnts with 'xtype' attributes.
280 // that match this xtype..
281 // note - when we render we create these as well..
282 // so we should check to see if body has xtype set.
283 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
285 var self_cntr_el = Roo.get(this[cntr](false));
286 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
288 //Roo.log(Roo.XComponent.build_from_html);
289 //Roo.log("got echild:");
292 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293 // and are not displayed -this causes this to use up the wrong element when matching.
294 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
297 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
304 //echild.dom.removeAttribute('xtype');
306 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307 Roo.debug && Roo.log(self_cntr_el);
308 Roo.debug && Roo.log(echild);
309 Roo.debug && Roo.log(cn);
315 // if object has flexy:if - then it may or may not be rendered.
316 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
317 // skip a flexy if element.
318 Roo.debug && Roo.log('skipping render');
319 Roo.debug && Roo.log(tree);
321 Roo.debug && Roo.log('skipping all children');
322 skip_children = true;
327 // actually if flexy:foreach is found, we really want to create
328 // multiple copies here...
330 //Roo.log(this[cntr]());
331 // some elements do not have render methods.. like the layouts...
333 if(this[cntr](true) === false){
338 cn.render && cn.render(this[cntr](true));
341 // then add the element..
348 if (typeof (tree.menu) != 'undefined') {
349 tree.menu.parentType = cn.xtype;
350 tree.menu.triggerEl = cn.el;
351 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
355 if (!tree.items || !tree.items.length) {
357 //Roo.log(["no children", this]);
362 var items = tree.items;
365 //Roo.log(items.length);
367 if (!skip_children) {
368 for(var i =0;i < items.length;i++) {
369 // Roo.log(['add child', items[i]]);
370 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
376 //Roo.log("fire childrenrendered");
378 cn.fireEvent('childrenrendered', this);
384 * Set the element that will be used to show or hide
386 setVisibilityEl : function(el)
388 this.visibilityEl = el;
392 * Get the element that will be used to show or hide
394 getVisibilityEl : function()
396 if (typeof(this.visibilityEl) == 'object') {
397 return this.visibilityEl;
400 if (typeof(this.visibilityEl) == 'string') {
401 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
408 * Show a component - removes 'hidden' class
412 if(!this.getVisibilityEl()){
416 this.getVisibilityEl().removeClass('hidden');
418 this.fireEvent('show', this);
423 * Hide a component - adds 'hidden' class
427 if(!this.getVisibilityEl()){
431 this.getVisibilityEl().addClass('hidden');
433 this.fireEvent('hide', this);
446 * @class Roo.bootstrap.Body
447 * @extends Roo.bootstrap.Component
448 * Bootstrap Body class
452 * @param {Object} config The config object
455 Roo.bootstrap.Body = function(config){
457 config = config || {};
459 Roo.bootstrap.Body.superclass.constructor.call(this, config);
460 this.el = Roo.get(config.el ? config.el : document.body );
461 if (this.cls && this.cls.length) {
462 Roo.get(document.body).addClass(this.cls);
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
468 is_body : true,// just to make sure it's constructed?
473 onRender : function(ct, position)
475 /* Roo.log("Roo.bootstrap.Body - onRender");
476 if (this.cls && this.cls.length) {
477 Roo.get(document.body).addClass(this.cls);
496 * @class Roo.bootstrap.ButtonGroup
497 * @extends Roo.bootstrap.Component
498 * Bootstrap ButtonGroup class
499 * @cfg {String} size lg | sm | xs (default empty normal)
500 * @cfg {String} align vertical | justified (default none)
501 * @cfg {String} direction up | down (default down)
502 * @cfg {Boolean} toolbar false | true
503 * @cfg {Boolean} btn true | false
508 * @param {Object} config The config object
511 Roo.bootstrap.ButtonGroup = function(config){
512 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
523 getAutoCreate : function(){
529 cfg.html = this.html || cfg.html;
540 if (['vertical','justified'].indexOf(this.align)!==-1) {
541 cfg.cls = 'btn-group-' + this.align;
543 if (this.align == 'justified') {
544 console.log(this.items);
548 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549 cfg.cls += ' btn-group-' + this.size;
552 if (this.direction == 'up') {
553 cfg.cls += ' dropup' ;
559 * Add a button to the group (similar to NavItem API.)
561 addItem : function(cfg)
563 var cn = new Roo.bootstrap.Button(cfg);
565 cn.parentId = this.id;
566 cn.onRender(this.el, null);
580 * @class Roo.bootstrap.Button
581 * @extends Roo.bootstrap.Component
582 * Bootstrap Button class
583 * @cfg {String} html The button content
584 * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585 * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587 * @cfg {String} size ( lg | sm | xs)
588 * @cfg {String} tag ( a | input | submit)
589 * @cfg {String} href empty or href
590 * @cfg {Boolean} disabled default false;
591 * @cfg {Boolean} isClose default false;
592 * @cfg {String} glyphicon depricated - use fs
593 * @cfg {String} badge text for badge
594 * @cfg {String} theme (default|glow)
595 * @cfg {Boolean} inverse dark themed version
596 * @cfg {Boolean} toggle is it a slidy toggle button
597 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
598 * @cfg {String} ontext text for on slidy toggle state
599 * @cfg {String} offtext text for off slidy toggle state
600 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
601 * @cfg {Boolean} removeClass remove the standard class..
602 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
605 * Create a new button
606 * @param {Object} config The config object
610 Roo.bootstrap.Button = function(config){
611 Roo.bootstrap.Button.superclass.constructor.call(this, config);
612 this.weightClass = ["btn-default btn-outline-secondary",
624 * When a butotn is pressed
625 * @param {Roo.bootstrap.Button} btn
626 * @param {Roo.EventObject} e
631 * After the button has been toggles
632 * @param {Roo.bootstrap.Button} btn
633 * @param {Roo.EventObject} e
634 * @param {boolean} pressed (also available as button.pressed)
640 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
661 preventDefault: true,
669 getAutoCreate : function(){
677 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
678 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
683 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
685 if (this.toggle == true) {
688 cls: 'slider-frame roo-button',
693 'data-off-text':'OFF',
694 cls: 'slider-button',
700 if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
701 cfg.cls += ' '+this.weight;
710 cfg["aria-hidden"] = true;
712 cfg.html = "×";
718 if (this.theme==='default') {
719 cfg.cls = 'btn roo-button';
721 //if (this.parentType != 'Navbar') {
722 this.weight = this.weight.length ? this.weight : 'default';
724 if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
726 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
727 var weight = this.weight == 'default' ? 'secondary' : this.weight;
728 cfg.cls += ' btn-' + outline + weight;
729 if (this.weight == 'default') {
731 cfg.cls += ' btn-' + this.weight;
734 } else if (this.theme==='glow') {
737 cfg.cls = 'btn-glow roo-button';
739 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
741 cfg.cls += ' ' + this.weight;
747 this.cls += ' inverse';
751 if (this.active || this.pressed === true) {
752 cfg.cls += ' active';
756 cfg.disabled = 'disabled';
760 Roo.log('changing to ul' );
762 this.glyphicon = 'caret';
763 if (Roo.bootstrap.version == 4) {
764 this.fa = 'caret-down';
769 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
771 //gsRoo.log(this.parentType);
772 if (this.parentType === 'Navbar' && !this.parent().bar) {
773 Roo.log('changing to li?');
782 href : this.href || '#'
785 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
786 cfg.cls += ' dropdown';
793 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
795 if (this.glyphicon) {
796 cfg.html = ' ' + cfg.html;
801 cls: 'glyphicon glyphicon-' + this.glyphicon
806 cfg.html = ' ' + cfg.html;
811 cls: 'fa fas fa-' + this.fa
821 // cfg.cls='btn roo-button';
825 var value = cfg.html;
830 cls: 'glyphicon glyphicon-' + this.glyphicon,
837 cls: 'fa fas fa-' + this.fa,
842 var bw = this.badge_weight.length ? this.badge_weight :
843 (this.weight.length ? this.weight : 'secondary');
844 bw = bw == 'default' ? 'secondary' : bw;
850 cls: 'badge badge-' + bw,
859 cfg.cls += ' dropdown';
860 cfg.html = typeof(cfg.html) != 'undefined' ?
861 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
864 if (cfg.tag !== 'a' && this.href !== '') {
865 throw "Tag must be a to set href.";
866 } else if (this.href.length > 0) {
867 cfg.href = this.href;
870 if(this.removeClass){
875 cfg.target = this.target;
880 initEvents: function() {
881 // Roo.log('init events?');
882 // Roo.log(this.el.dom);
885 if (typeof (this.menu) != 'undefined') {
886 this.menu.parentType = this.xtype;
887 this.menu.triggerEl = this.el;
888 this.addxtype(Roo.apply({}, this.menu));
892 if (this.el.hasClass('roo-button')) {
893 this.el.on('click', this.onClick, this);
895 this.el.select('.roo-button').on('click', this.onClick, this);
898 if(this.removeClass){
899 this.el.on('click', this.onClick, this);
902 this.el.enableDisplayMode();
905 onClick : function(e)
911 Roo.log('button on click ');
912 if(this.preventDefault){
916 if (this.pressed === true || this.pressed === false) {
917 this.toggleActive(e);
921 this.fireEvent('click', this, e);
925 * Enables this button
929 this.disabled = false;
930 this.el.removeClass('disabled');
934 * Disable this button
938 this.disabled = true;
939 this.el.addClass('disabled');
942 * sets the active state on/off,
943 * @param {Boolean} state (optional) Force a particular state
945 setActive : function(v) {
947 this.el[v ? 'addClass' : 'removeClass']('active');
951 * toggles the current active state
953 toggleActive : function(e)
955 this.setActive(!this.pressed);
956 this.fireEvent('toggle', this, e, !this.pressed);
959 * get the current active state
960 * @return {boolean} true if it's active
962 isActive : function()
964 return this.el.hasClass('active');
967 * set the text of the first selected button
969 setText : function(str)
971 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
974 * get the text of the first selected button
978 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
981 setWeight : function(str)
983 this.el.removeClass(this.weightClass);
985 var outline = this.outline ? 'outline-' : '';
986 if (str == 'default') {
987 this.el.addClass('btn-default btn-outline-secondary');
990 this.el.addClass('btn-' + outline + str);
1004 * @class Roo.bootstrap.Column
1005 * @extends Roo.bootstrap.Component
1006 * Bootstrap Column class
1007 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1008 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1009 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1010 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1011 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1012 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1013 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1014 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1017 * @cfg {Boolean} hidden (true|false) hide the element
1018 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1019 * @cfg {String} fa (ban|check|...) font awesome icon
1020 * @cfg {Number} fasize (1|2|....) font awsome size
1022 * @cfg {String} icon (info-sign|check|...) glyphicon name
1024 * @cfg {String} html content of column.
1027 * Create a new Column
1028 * @param {Object} config The config object
1031 Roo.bootstrap.Column = function(config){
1032 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1035 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1053 getAutoCreate : function(){
1054 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1062 ['xs','sm','md','lg'].map(function(size){
1063 //Roo.log( size + ':' + settings[size]);
1065 if (settings[size+'off'] !== false) {
1066 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1069 if (settings[size] === false) {
1073 if (!settings[size]) { // 0 = hidden
1074 cfg.cls += ' hidden-' + size;
1077 cfg.cls += ' col-' + size + '-' + settings[size];
1082 cfg.cls += ' hidden';
1085 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1086 cfg.cls +=' alert alert-' + this.alert;
1090 if (this.html.length) {
1091 cfg.html = this.html;
1095 if (this.fasize > 1) {
1096 fasize = ' fa-' + this.fasize + 'x';
1098 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1103 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1122 * @class Roo.bootstrap.Container
1123 * @extends Roo.bootstrap.Component
1124 * Bootstrap Container class
1125 * @cfg {Boolean} jumbotron is it a jumbotron element
1126 * @cfg {String} html content of element
1127 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1128 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1129 * @cfg {String} header content of header (for panel)
1130 * @cfg {String} footer content of footer (for panel)
1131 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1132 * @cfg {String} tag (header|aside|section) type of HTML tag.
1133 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1134 * @cfg {String} fa font awesome icon
1135 * @cfg {String} icon (info-sign|check|...) glyphicon name
1136 * @cfg {Boolean} hidden (true|false) hide the element
1137 * @cfg {Boolean} expandable (true|false) default false
1138 * @cfg {Boolean} expanded (true|false) default true
1139 * @cfg {String} rheader contet on the right of header
1140 * @cfg {Boolean} clickable (true|false) default false
1144 * Create a new Container
1145 * @param {Object} config The config object
1148 Roo.bootstrap.Container = function(config){
1149 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1155 * After the panel has been expand
1157 * @param {Roo.bootstrap.Container} this
1162 * After the panel has been collapsed
1164 * @param {Roo.bootstrap.Container} this
1169 * When a element is chick
1170 * @param {Roo.bootstrap.Container} this
1171 * @param {Roo.EventObject} e
1177 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1195 getChildContainer : function() {
1201 if (this.panel.length) {
1202 return this.el.select('.panel-body',true).first();
1209 getAutoCreate : function(){
1212 tag : this.tag || 'div',
1216 if (this.jumbotron) {
1217 cfg.cls = 'jumbotron';
1222 // - this is applied by the parent..
1224 // cfg.cls = this.cls + '';
1227 if (this.sticky.length) {
1229 var bd = Roo.get(document.body);
1230 if (!bd.hasClass('bootstrap-sticky')) {
1231 bd.addClass('bootstrap-sticky');
1232 Roo.select('html',true).setStyle('height', '100%');
1235 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1239 if (this.well.length) {
1240 switch (this.well) {
1243 cfg.cls +=' well well-' +this.well;
1252 cfg.cls += ' hidden';
1256 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1257 cfg.cls +=' alert alert-' + this.alert;
1262 if (this.panel.length) {
1263 cfg.cls += ' panel panel-' + this.panel;
1265 if (this.header.length) {
1269 if(this.expandable){
1271 cfg.cls = cfg.cls + ' expandable';
1275 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1283 cls : 'panel-title',
1284 html : (this.expandable ? ' ' : '') + this.header
1288 cls: 'panel-header-right',
1294 cls : 'panel-heading',
1295 style : this.expandable ? 'cursor: pointer' : '',
1303 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1308 if (this.footer.length) {
1310 cls : 'panel-footer',
1319 body.html = this.html || cfg.html;
1320 // prefix with the icons..
1322 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1325 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1330 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1331 cfg.cls = 'container';
1337 initEvents: function()
1339 if(this.expandable){
1340 var headerEl = this.headerEl();
1343 headerEl.on('click', this.onToggleClick, this);
1348 this.el.on('click', this.onClick, this);
1353 onToggleClick : function()
1355 var headerEl = this.headerEl();
1371 if(this.fireEvent('expand', this)) {
1373 this.expanded = true;
1375 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1377 this.el.select('.panel-body',true).first().removeClass('hide');
1379 var toggleEl = this.toggleEl();
1385 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1390 collapse : function()
1392 if(this.fireEvent('collapse', this)) {
1394 this.expanded = false;
1396 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1397 this.el.select('.panel-body',true).first().addClass('hide');
1399 var toggleEl = this.toggleEl();
1405 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1409 toggleEl : function()
1411 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1415 return this.el.select('.panel-heading .fa',true).first();
1418 headerEl : function()
1420 if(!this.el || !this.panel.length || !this.header.length){
1424 return this.el.select('.panel-heading',true).first()
1429 if(!this.el || !this.panel.length){
1433 return this.el.select('.panel-body',true).first()
1436 titleEl : function()
1438 if(!this.el || !this.panel.length || !this.header.length){
1442 return this.el.select('.panel-title',true).first();
1445 setTitle : function(v)
1447 var titleEl = this.titleEl();
1453 titleEl.dom.innerHTML = v;
1456 getTitle : function()
1459 var titleEl = this.titleEl();
1465 return titleEl.dom.innerHTML;
1468 setRightTitle : function(v)
1470 var t = this.el.select('.panel-header-right',true).first();
1476 t.dom.innerHTML = v;
1479 onClick : function(e)
1483 this.fireEvent('click', this, e);
1496 * @class Roo.bootstrap.Img
1497 * @extends Roo.bootstrap.Component
1498 * Bootstrap Img class
1499 * @cfg {Boolean} imgResponsive false | true
1500 * @cfg {String} border rounded | circle | thumbnail
1501 * @cfg {String} src image source
1502 * @cfg {String} alt image alternative text
1503 * @cfg {String} href a tag href
1504 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1505 * @cfg {String} xsUrl xs image source
1506 * @cfg {String} smUrl sm image source
1507 * @cfg {String} mdUrl md image source
1508 * @cfg {String} lgUrl lg image source
1511 * Create a new Input
1512 * @param {Object} config The config object
1515 Roo.bootstrap.Img = function(config){
1516 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1522 * The img click event for the img.
1523 * @param {Roo.EventObject} e
1529 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1531 imgResponsive: true,
1541 getAutoCreate : function()
1543 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1544 return this.createSingleImg();
1549 cls: 'roo-image-responsive-group',
1554 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1556 if(!_this[size + 'Url']){
1562 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1563 html: _this.html || cfg.html,
1564 src: _this[size + 'Url']
1567 img.cls += ' roo-image-responsive-' + size;
1569 var s = ['xs', 'sm', 'md', 'lg'];
1571 s.splice(s.indexOf(size), 1);
1573 Roo.each(s, function(ss){
1574 img.cls += ' hidden-' + ss;
1577 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1578 cfg.cls += ' img-' + _this.border;
1582 cfg.alt = _this.alt;
1595 a.target = _this.target;
1599 cfg.cn.push((_this.href) ? a : img);
1606 createSingleImg : function()
1610 cls: (this.imgResponsive) ? 'img-responsive' : '',
1612 src : 'about:blank' // just incase src get's set to undefined?!?
1615 cfg.html = this.html || cfg.html;
1617 cfg.src = this.src || cfg.src;
1619 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1620 cfg.cls += ' img-' + this.border;
1637 a.target = this.target;
1642 return (this.href) ? a : cfg;
1645 initEvents: function()
1648 this.el.on('click', this.onClick, this);
1653 onClick : function(e)
1655 Roo.log('img onclick');
1656 this.fireEvent('click', this, e);
1659 * Sets the url of the image - used to update it
1660 * @param {String} url the url of the image
1663 setSrc : function(url)
1667 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1668 this.el.dom.src = url;
1672 this.el.select('img', true).first().dom.src = url;
1688 * @class Roo.bootstrap.Link
1689 * @extends Roo.bootstrap.Component
1690 * Bootstrap Link Class
1691 * @cfg {String} alt image alternative text
1692 * @cfg {String} href a tag href
1693 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1694 * @cfg {String} html the content of the link.
1695 * @cfg {String} anchor name for the anchor link
1696 * @cfg {String} fa - favicon
1698 * @cfg {Boolean} preventDefault (true | false) default false
1702 * Create a new Input
1703 * @param {Object} config The config object
1706 Roo.bootstrap.Link = function(config){
1707 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1713 * The img click event for the img.
1714 * @param {Roo.EventObject} e
1720 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1724 preventDefault: false,
1730 getAutoCreate : function()
1732 var html = this.html || '';
1734 if (this.fa !== false) {
1735 html = '<i class="fa fa-' + this.fa + '"></i>';
1740 // anchor's do not require html/href...
1741 if (this.anchor === false) {
1743 cfg.href = this.href || '#';
1745 cfg.name = this.anchor;
1746 if (this.html !== false || this.fa !== false) {
1749 if (this.href !== false) {
1750 cfg.href = this.href;
1754 if(this.alt !== false){
1759 if(this.target !== false) {
1760 cfg.target = this.target;
1766 initEvents: function() {
1768 if(!this.href || this.preventDefault){
1769 this.el.on('click', this.onClick, this);
1773 onClick : function(e)
1775 if(this.preventDefault){
1778 //Roo.log('img onclick');
1779 this.fireEvent('click', this, e);
1792 * @class Roo.bootstrap.Header
1793 * @extends Roo.bootstrap.Component
1794 * Bootstrap Header class
1795 * @cfg {String} html content of header
1796 * @cfg {Number} level (1|2|3|4|5|6) default 1
1799 * Create a new Header
1800 * @param {Object} config The config object
1804 Roo.bootstrap.Header = function(config){
1805 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1808 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1816 getAutoCreate : function(){
1821 tag: 'h' + (1 *this.level),
1822 html: this.html || ''
1834 * Ext JS Library 1.1.1
1835 * Copyright(c) 2006-2007, Ext JS, LLC.
1837 * Originally Released Under LGPL - original licence link has changed is not relivant.
1840 * <script type="text/javascript">
1844 * @class Roo.bootstrap.MenuMgr
1845 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1848 Roo.bootstrap.MenuMgr = function(){
1849 var menus, active, groups = {}, attached = false, lastShow = new Date();
1851 // private - called when first menu is created
1854 active = new Roo.util.MixedCollection();
1855 Roo.get(document).addKeyListener(27, function(){
1856 if(active.length > 0){
1864 if(active && active.length > 0){
1865 var c = active.clone();
1875 if(active.length < 1){
1876 Roo.get(document).un("mouseup", onMouseDown);
1884 var last = active.last();
1885 lastShow = new Date();
1888 Roo.get(document).on("mouseup", onMouseDown);
1893 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1894 m.parentMenu.activeChild = m;
1895 }else if(last && last.isVisible()){
1896 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1901 function onBeforeHide(m){
1903 m.activeChild.hide();
1905 if(m.autoHideTimer){
1906 clearTimeout(m.autoHideTimer);
1907 delete m.autoHideTimer;
1912 function onBeforeShow(m){
1913 var pm = m.parentMenu;
1914 if(!pm && !m.allowOtherMenus){
1916 }else if(pm && pm.activeChild && active != m){
1917 pm.activeChild.hide();
1921 // private this should really trigger on mouseup..
1922 function onMouseDown(e){
1923 Roo.log("on Mouse Up");
1925 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1926 Roo.log("MenuManager hideAll");
1935 function onBeforeCheck(mi, state){
1937 var g = groups[mi.group];
1938 for(var i = 0, l = g.length; i < l; i++){
1940 g[i].setChecked(false);
1949 * Hides all menus that are currently visible
1951 hideAll : function(){
1956 register : function(menu){
1960 menus[menu.id] = menu;
1961 menu.on("beforehide", onBeforeHide);
1962 menu.on("hide", onHide);
1963 menu.on("beforeshow", onBeforeShow);
1964 menu.on("show", onShow);
1966 if(g && menu.events["checkchange"]){
1970 groups[g].push(menu);
1971 menu.on("checkchange", onCheck);
1976 * Returns a {@link Roo.menu.Menu} object
1977 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1978 * be used to generate and return a new Menu instance.
1980 get : function(menu){
1981 if(typeof menu == "string"){ // menu id
1983 }else if(menu.events){ // menu instance
1986 /*else if(typeof menu.length == 'number'){ // array of menu items?
1987 return new Roo.bootstrap.Menu({items:menu});
1988 }else{ // otherwise, must be a config
1989 return new Roo.bootstrap.Menu(menu);
1996 unregister : function(menu){
1997 delete menus[menu.id];
1998 menu.un("beforehide", onBeforeHide);
1999 menu.un("hide", onHide);
2000 menu.un("beforeshow", onBeforeShow);
2001 menu.un("show", onShow);
2003 if(g && menu.events["checkchange"]){
2004 groups[g].remove(menu);
2005 menu.un("checkchange", onCheck);
2010 registerCheckable : function(menuItem){
2011 var g = menuItem.group;
2016 groups[g].push(menuItem);
2017 menuItem.on("beforecheckchange", onBeforeCheck);
2022 unregisterCheckable : function(menuItem){
2023 var g = menuItem.group;
2025 groups[g].remove(menuItem);
2026 menuItem.un("beforecheckchange", onBeforeCheck);
2038 * @class Roo.bootstrap.Menu
2039 * @extends Roo.bootstrap.Component
2040 * Bootstrap Menu class - container for MenuItems
2041 * @cfg {String} type (dropdown|treeview|submenu) type of menu
2042 * @cfg {bool} hidden if the menu should be hidden when rendered.
2043 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
2044 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
2048 * @param {Object} config The config object
2052 Roo.bootstrap.Menu = function(config){
2053 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2054 if (this.registerMenu && this.type != 'treeview') {
2055 Roo.bootstrap.MenuMgr.register(this);
2062 * Fires before this menu is displayed
2063 * @param {Roo.menu.Menu} this
2068 * Fires before this menu is hidden
2069 * @param {Roo.menu.Menu} this
2074 * Fires after this menu is displayed
2075 * @param {Roo.menu.Menu} this
2080 * Fires after this menu is hidden
2081 * @param {Roo.menu.Menu} this
2086 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2087 * @param {Roo.menu.Menu} this
2088 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2089 * @param {Roo.EventObject} e
2094 * Fires when the mouse is hovering over this menu
2095 * @param {Roo.menu.Menu} this
2096 * @param {Roo.EventObject} e
2097 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2102 * Fires when the mouse exits this menu
2103 * @param {Roo.menu.Menu} this
2104 * @param {Roo.EventObject} e
2105 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2110 * Fires when a menu item contained in this menu is clicked
2111 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2112 * @param {Roo.EventObject} e
2116 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2119 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2123 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2126 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2128 registerMenu : true,
2130 menuItems :false, // stores the menu items..
2140 getChildContainer : function() {
2144 getAutoCreate : function(){
2146 //if (['right'].indexOf(this.align)!==-1) {
2147 // cfg.cn[1].cls += ' pull-right'
2153 cls : 'dropdown-menu' ,
2154 style : 'z-index:1000'
2158 if (this.type === 'submenu') {
2159 cfg.cls = 'submenu active';
2161 if (this.type === 'treeview') {
2162 cfg.cls = 'treeview-menu';
2167 initEvents : function() {
2169 // Roo.log("ADD event");
2170 // Roo.log(this.triggerEl.dom);
2172 this.triggerEl.on('click', this.onTriggerClick, this);
2174 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2177 if (this.triggerEl.hasClass('nav-item')) {
2178 // dropdown toggle on the 'a' in BS4?
2179 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2181 this.triggerEl.addClass('dropdown-toggle');
2184 this.el.on('touchstart' , this.onTouch, this);
2186 this.el.on('click' , this.onClick, this);
2188 this.el.on("mouseover", this.onMouseOver, this);
2189 this.el.on("mouseout", this.onMouseOut, this);
2193 findTargetItem : function(e)
2195 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2199 //Roo.log(t); Roo.log(t.id);
2201 //Roo.log(this.menuitems);
2202 return this.menuitems.get(t.id);
2204 //return this.items.get(t.menuItemId);
2210 onTouch : function(e)
2212 Roo.log("menu.onTouch");
2213 //e.stopEvent(); this make the user popdown broken
2217 onClick : function(e)
2219 Roo.log("menu.onClick");
2221 var t = this.findTargetItem(e);
2222 if(!t || t.isContainer){
2227 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2228 if(t == this.activeItem && t.shouldDeactivate(e)){
2229 this.activeItem.deactivate();
2230 delete this.activeItem;
2234 this.setActiveItem(t, true);
2242 Roo.log('pass click event');
2246 this.fireEvent("click", this, t, e);
2250 if(!t.href.length || t.href == '#'){
2251 (function() { _this.hide(); }).defer(100);
2256 onMouseOver : function(e){
2257 var t = this.findTargetItem(e);
2260 // if(t.canActivate && !t.disabled){
2261 // this.setActiveItem(t, true);
2265 this.fireEvent("mouseover", this, e, t);
2267 isVisible : function(){
2268 return !this.hidden;
2270 onMouseOut : function(e){
2271 var t = this.findTargetItem(e);
2274 // if(t == this.activeItem && t.shouldDeactivate(e)){
2275 // this.activeItem.deactivate();
2276 // delete this.activeItem;
2279 this.fireEvent("mouseout", this, e, t);
2284 * Displays this menu relative to another element
2285 * @param {String/HTMLElement/Roo.Element} element The element to align to
2286 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2287 * the element (defaults to this.defaultAlign)
2288 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2290 show : function(el, pos, parentMenu){
2291 this.parentMenu = parentMenu;
2295 this.fireEvent("beforeshow", this);
2296 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2299 * Displays this menu at a specific xy position
2300 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2301 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2303 showAt : function(xy, parentMenu, /* private: */_e){
2304 this.parentMenu = parentMenu;
2309 this.fireEvent("beforeshow", this);
2310 //xy = this.el.adjustForConstraints(xy);
2314 this.hideMenuItems();
2315 this.hidden = false;
2316 this.triggerEl.addClass('open');
2317 this.el.addClass('show');
2319 // reassign x when hitting right
2320 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2321 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2324 // reassign y when hitting bottom
2325 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2326 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2329 // but the list may align on trigger left or trigger top... should it be a properity?
2331 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2336 this.fireEvent("show", this);
2342 this.doFocus.defer(50, this);
2346 doFocus : function(){
2348 this.focusEl.focus();
2353 * Hides this menu and optionally all parent menus
2354 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2356 hide : function(deep)
2359 this.hideMenuItems();
2360 if(this.el && this.isVisible()){
2361 this.fireEvent("beforehide", this);
2362 if(this.activeItem){
2363 this.activeItem.deactivate();
2364 this.activeItem = null;
2366 this.triggerEl.removeClass('open');;
2367 this.el.removeClass('show');
2369 this.fireEvent("hide", this);
2371 if(deep === true && this.parentMenu){
2372 this.parentMenu.hide(true);
2376 onTriggerClick : function(e)
2378 Roo.log('trigger click');
2380 var target = e.getTarget();
2382 Roo.log(target.nodeName.toLowerCase());
2384 if(target.nodeName.toLowerCase() === 'i'){
2390 onTriggerPress : function(e)
2392 Roo.log('trigger press');
2393 //Roo.log(e.getTarget());
2394 // Roo.log(this.triggerEl.dom);
2396 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2397 var pel = Roo.get(e.getTarget());
2398 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2399 Roo.log('is treeview or dropdown?');
2403 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2407 if (this.isVisible()) {
2412 this.show(this.triggerEl, false, false);
2415 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2422 hideMenuItems : function()
2424 Roo.log("hide Menu Items");
2428 //$(backdrop).remove()
2429 this.el.select('.open',true).each(function(aa) {
2431 aa.removeClass('open');
2432 //var parent = getParent($(this))
2433 //var relatedTarget = { relatedTarget: this }
2435 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2436 //if (e.isDefaultPrevented()) return
2437 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2440 addxtypeChild : function (tree, cntr) {
2441 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2443 this.menuitems.add(comp);
2455 this.getEl().dom.innerHTML = '';
2456 this.menuitems.clear();
2470 * @class Roo.bootstrap.MenuItem
2471 * @extends Roo.bootstrap.Component
2472 * Bootstrap MenuItem class
2473 * @cfg {String} html the menu label
2474 * @cfg {String} href the link
2475 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2476 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2477 * @cfg {Boolean} active used on sidebars to highlight active itesm
2478 * @cfg {String} fa favicon to show on left of menu item.
2479 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2483 * Create a new MenuItem
2484 * @param {Object} config The config object
2488 Roo.bootstrap.MenuItem = function(config){
2489 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2494 * The raw click event for the entire grid.
2495 * @param {Roo.bootstrap.MenuItem} this
2496 * @param {Roo.EventObject} e
2502 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2506 preventDefault: false,
2507 isContainer : false,
2511 getAutoCreate : function(){
2513 if(this.isContainer){
2516 cls: 'dropdown-menu-item dropdown-item'
2530 if (this.fa !== false) {
2533 cls : 'fa fa-' + this.fa
2542 cls: 'dropdown-menu-item dropdown-item',
2545 if (this.parent().type == 'treeview') {
2546 cfg.cls = 'treeview-menu';
2549 cfg.cls += ' active';
2554 anc.href = this.href || cfg.cn[0].href ;
2555 ctag.html = this.html || cfg.cn[0].html ;
2559 initEvents: function()
2561 if (this.parent().type == 'treeview') {
2562 this.el.select('a').on('click', this.onClick, this);
2566 this.menu.parentType = this.xtype;
2567 this.menu.triggerEl = this.el;
2568 this.menu = this.addxtype(Roo.apply({}, this.menu));
2572 onClick : function(e)
2574 Roo.log('item on click ');
2576 if(this.preventDefault){
2579 //this.parent().hideMenuItems();
2581 this.fireEvent('click', this, e);
2600 * @class Roo.bootstrap.MenuSeparator
2601 * @extends Roo.bootstrap.Component
2602 * Bootstrap MenuSeparator class
2605 * Create a new MenuItem
2606 * @param {Object} config The config object
2610 Roo.bootstrap.MenuSeparator = function(config){
2611 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2614 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2616 getAutoCreate : function(){
2635 * @class Roo.bootstrap.Modal
2636 * @extends Roo.bootstrap.Component
2637 * Bootstrap Modal class
2638 * @cfg {String} title Title of dialog
2639 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2640 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2641 * @cfg {Boolean} specificTitle default false
2642 * @cfg {Array} buttons Array of buttons or standard button set..
2643 * @cfg {String} buttonPosition (left|right|center) default right
2644 * @cfg {Boolean} animate default true
2645 * @cfg {Boolean} allow_close default true
2646 * @cfg {Boolean} fitwindow default false
2647 * @cfg {String} size (sm|lg) default empty
2648 * @cfg {Number} max_width set the max width of modal
2652 * Create a new Modal Dialog
2653 * @param {Object} config The config object
2656 Roo.bootstrap.Modal = function(config){
2657 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2662 * The raw btnclick event for the button
2663 * @param {Roo.EventObject} e
2668 * Fire when dialog resize
2669 * @param {Roo.bootstrap.Modal} this
2670 * @param {Roo.EventObject} e
2674 this.buttons = this.buttons || [];
2677 this.tmpl = Roo.factory(this.tmpl);
2682 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2684 title : 'test dialog',
2694 specificTitle: false,
2696 buttonPosition: 'right',
2719 onRender : function(ct, position)
2721 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2724 var cfg = Roo.apply({}, this.getAutoCreate());
2727 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2729 //if (!cfg.name.length) {
2733 cfg.cls += ' ' + this.cls;
2736 cfg.style = this.style;
2738 this.el = Roo.get(document.body).createChild(cfg, position);
2740 //var type = this.el.dom.type;
2743 if(this.tabIndex !== undefined){
2744 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2747 this.dialogEl = this.el.select('.modal-dialog',true).first();
2748 this.bodyEl = this.el.select('.modal-body',true).first();
2749 this.closeEl = this.el.select('.modal-header .close', true).first();
2750 this.headerEl = this.el.select('.modal-header',true).first();
2751 this.titleEl = this.el.select('.modal-title',true).first();
2752 this.footerEl = this.el.select('.modal-footer',true).first();
2754 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2756 //this.el.addClass("x-dlg-modal");
2758 if (this.buttons.length) {
2759 Roo.each(this.buttons, function(bb) {
2760 var b = Roo.apply({}, bb);
2761 b.xns = b.xns || Roo.bootstrap;
2762 b.xtype = b.xtype || 'Button';
2763 if (typeof(b.listeners) == 'undefined') {
2764 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2767 var btn = Roo.factory(b);
2769 btn.render(this.el.select('.modal-footer div').first());
2773 // render the children.
2776 if(typeof(this.items) != 'undefined'){
2777 var items = this.items;
2780 for(var i =0;i < items.length;i++) {
2781 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2785 this.items = nitems;
2787 // where are these used - they used to be body/close/footer
2791 //this.el.addClass([this.fieldClass, this.cls]);
2795 getAutoCreate : function()
2799 html : this.html || ''
2804 cls : 'modal-title',
2808 if(this.specificTitle){
2814 if (this.allow_close && Roo.bootstrap.version == 3) {
2824 if (this.allow_close && Roo.bootstrap.version == 4) {
2834 if(this.size.length){
2835 size = 'modal-' + this.size;
2842 cls: "modal-dialog " + size,
2845 cls : "modal-content",
2848 cls : 'modal-header',
2853 cls : 'modal-footer',
2857 cls: 'btn-' + this.buttonPosition
2874 modal.cls += ' fade';
2880 getChildContainer : function() {
2885 getButtonContainer : function() {
2886 return this.el.select('.modal-footer div',true).first();
2889 initEvents : function()
2891 if (this.allow_close) {
2892 this.closeEl.on('click', this.hide, this);
2894 Roo.EventManager.onWindowResize(this.resize, this, true);
2901 this.maskEl.setSize(
2902 Roo.lib.Dom.getViewWidth(true),
2903 Roo.lib.Dom.getViewHeight(true)
2906 if (this.fitwindow) {
2908 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2909 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2914 if(this.max_width !== 0) {
2916 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2919 this.setSize(w, this.height);
2923 if(this.max_height) {
2924 this.setSize(w,Math.min(
2926 Roo.lib.Dom.getViewportHeight(true) - 60
2932 if(!this.fit_content) {
2933 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2937 this.setSize(w, Math.min(
2939 this.headerEl.getHeight() +
2940 this.footerEl.getHeight() +
2941 this.getChildHeight(this.bodyEl.dom.childNodes),
2942 Roo.lib.Dom.getViewportHeight(true) - 60)
2948 setSize : function(w,h)
2959 if (!this.rendered) {
2963 //this.el.setStyle('display', 'block');
2964 this.el.removeClass('hideing');
2965 this.el.dom.style.display='block';
2967 Roo.get(document.body).addClass('modal-open');
2969 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2972 this.el.addClass('show');
2973 this.el.addClass('in');
2976 this.el.addClass('show');
2977 this.el.addClass('in');
2980 // not sure how we can show data in here..
2982 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2985 Roo.get(document.body).addClass("x-body-masked");
2987 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2988 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2989 this.maskEl.dom.style.display = 'block';
2990 this.maskEl.addClass('show');
2995 this.fireEvent('show', this);
2997 // set zindex here - otherwise it appears to be ignored...
2998 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3001 this.items.forEach( function(e) {
3002 e.layout ? e.layout() : false;
3010 if(this.fireEvent("beforehide", this) !== false){
3012 this.maskEl.removeClass('show');
3014 this.maskEl.dom.style.display = '';
3015 Roo.get(document.body).removeClass("x-body-masked");
3016 this.el.removeClass('in');
3017 this.el.select('.modal-dialog', true).first().setStyle('transform','');
3019 if(this.animate){ // why
3020 this.el.addClass('hideing');
3021 this.el.removeClass('show');
3023 if (!this.el.hasClass('hideing')) {
3024 return; // it's been shown again...
3027 this.el.dom.style.display='';
3029 Roo.get(document.body).removeClass('modal-open');
3030 this.el.removeClass('hideing');
3034 this.el.removeClass('show');
3035 this.el.dom.style.display='';
3036 Roo.get(document.body).removeClass('modal-open');
3039 this.fireEvent('hide', this);
3042 isVisible : function()
3045 return this.el.hasClass('show') && !this.el.hasClass('hideing');
3049 addButton : function(str, cb)
3053 var b = Roo.apply({}, { html : str } );
3054 b.xns = b.xns || Roo.bootstrap;
3055 b.xtype = b.xtype || 'Button';
3056 if (typeof(b.listeners) == 'undefined') {
3057 b.listeners = { click : cb.createDelegate(this) };
3060 var btn = Roo.factory(b);
3062 btn.render(this.el.select('.modal-footer div').first());
3068 setDefaultButton : function(btn)
3070 //this.el.select('.modal-footer').()
3074 resizeTo: function(w,h)
3078 this.dialogEl.setWidth(w);
3079 if (this.diff === false) {
3080 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3083 this.bodyEl.setHeight(h - this.diff);
3085 this.fireEvent('resize', this);
3088 setContentSize : function(w, h)
3092 onButtonClick: function(btn,e)
3095 this.fireEvent('btnclick', btn.name, e);
3098 * Set the title of the Dialog
3099 * @param {String} str new Title
3101 setTitle: function(str) {
3102 this.titleEl.dom.innerHTML = str;
3105 * Set the body of the Dialog
3106 * @param {String} str new Title
3108 setBody: function(str) {
3109 this.bodyEl.dom.innerHTML = str;
3112 * Set the body of the Dialog using the template
3113 * @param {Obj} data - apply this data to the template and replace the body contents.
3115 applyBody: function(obj)
3118 Roo.log("Error - using apply Body without a template");
3121 this.tmpl.overwrite(this.bodyEl, obj);
3124 getChildHeight : function(child_nodes)
3128 child_nodes.length == 0
3133 var child_height = 0;
3135 for(var i = 0; i < child_nodes.length; i++) {
3138 * for modal with tabs...
3139 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3141 var layout_childs = child_nodes[i].childNodes;
3143 for(var j = 0; j < layout_childs.length; j++) {
3145 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3147 var layout_body_childs = layout_childs[j].childNodes;
3149 for(var k = 0; k < layout_body_childs.length; k++) {
3151 if(layout_body_childs[k].classList.contains('navbar')) {
3152 child_height += layout_body_childs[k].offsetHeight;
3156 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3158 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3160 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3162 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3163 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3178 child_height += child_nodes[i].offsetHeight;
3179 // Roo.log(child_nodes[i].offsetHeight);
3182 return child_height;
3188 Roo.apply(Roo.bootstrap.Modal, {
3190 * Button config that displays a single OK button
3199 * Button config that displays Yes and No buttons
3215 * Button config that displays OK and Cancel buttons
3230 * Button config that displays Yes, No and Cancel buttons
3254 * messagebox - can be used as a replace
3258 * @class Roo.MessageBox
3259 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3263 Roo.Msg.alert('Status', 'Changes saved successfully.');
3265 // Prompt for user data:
3266 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3268 // process text value...
3272 // Show a dialog using config options:
3274 title:'Save Changes?',
3275 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3276 buttons: Roo.Msg.YESNOCANCEL,
3283 Roo.bootstrap.MessageBox = function(){
3284 var dlg, opt, mask, waitTimer;
3285 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3286 var buttons, activeTextEl, bwidth;
3290 var handleButton = function(button){
3292 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3296 var handleHide = function(){
3298 dlg.el.removeClass(opt.cls);
3301 // Roo.TaskMgr.stop(waitTimer);
3302 // waitTimer = null;
3307 var updateButtons = function(b){
3310 buttons["ok"].hide();
3311 buttons["cancel"].hide();
3312 buttons["yes"].hide();
3313 buttons["no"].hide();
3314 //dlg.footer.dom.style.display = 'none';
3317 dlg.footerEl.dom.style.display = '';
3318 for(var k in buttons){
3319 if(typeof buttons[k] != "function"){
3322 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3323 width += buttons[k].el.getWidth()+15;
3333 var handleEsc = function(d, k, e){
3334 if(opt && opt.closable !== false){
3344 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3345 * @return {Roo.BasicDialog} The BasicDialog element
3347 getDialog : function(){
3349 dlg = new Roo.bootstrap.Modal( {
3352 //constraintoviewport:false,
3354 //collapsible : false,
3359 //buttonAlign:"center",
3360 closeClick : function(){
3361 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3364 handleButton("cancel");
3369 dlg.on("hide", handleHide);
3371 //dlg.addKeyListener(27, handleEsc);
3373 this.buttons = buttons;
3374 var bt = this.buttonText;
3375 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3376 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3377 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3378 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3380 bodyEl = dlg.bodyEl.createChild({
3382 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3383 '<textarea class="roo-mb-textarea"></textarea>' +
3384 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3386 msgEl = bodyEl.dom.firstChild;
3387 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3388 textboxEl.enableDisplayMode();
3389 textboxEl.addKeyListener([10,13], function(){
3390 if(dlg.isVisible() && opt && opt.buttons){
3393 }else if(opt.buttons.yes){
3394 handleButton("yes");
3398 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3399 textareaEl.enableDisplayMode();
3400 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3401 progressEl.enableDisplayMode();
3403 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3404 var pf = progressEl.dom.firstChild;
3406 pp = Roo.get(pf.firstChild);
3407 pp.setHeight(pf.offsetHeight);
3415 * Updates the message box body text
3416 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3417 * the XHTML-compliant non-breaking space character '&#160;')
3418 * @return {Roo.MessageBox} This message box
3420 updateText : function(text)
3422 if(!dlg.isVisible() && !opt.width){
3423 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3424 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3426 msgEl.innerHTML = text || ' ';
3428 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3429 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3431 Math.min(opt.width || cw , this.maxWidth),
3432 Math.max(opt.minWidth || this.minWidth, bwidth)
3435 activeTextEl.setWidth(w);
3437 if(dlg.isVisible()){
3438 dlg.fixedcenter = false;
3440 // to big, make it scroll. = But as usual stupid IE does not support
3443 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3444 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3445 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3447 bodyEl.dom.style.height = '';
3448 bodyEl.dom.style.overflowY = '';
3451 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3453 bodyEl.dom.style.overflowX = '';
3456 dlg.setContentSize(w, bodyEl.getHeight());
3457 if(dlg.isVisible()){
3458 dlg.fixedcenter = true;
3464 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3465 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3466 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3467 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3468 * @return {Roo.MessageBox} This message box
3470 updateProgress : function(value, text){
3472 this.updateText(text);
3475 if (pp) { // weird bug on my firefox - for some reason this is not defined
3476 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3477 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3483 * Returns true if the message box is currently displayed
3484 * @return {Boolean} True if the message box is visible, else false
3486 isVisible : function(){
3487 return dlg && dlg.isVisible();
3491 * Hides the message box if it is displayed
3494 if(this.isVisible()){
3500 * Displays a new message box, or reinitializes an existing message box, based on the config options
3501 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3502 * The following config object properties are supported:
3504 Property Type Description
3505 ---------- --------------- ------------------------------------------------------------------------------------
3506 animEl String/Element An id or Element from which the message box should animate as it opens and
3507 closes (defaults to undefined)
3508 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3509 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3510 closable Boolean False to hide the top-right close button (defaults to true). Note that
3511 progress and wait dialogs will ignore this property and always hide the
3512 close button as they can only be closed programmatically.
3513 cls String A custom CSS class to apply to the message box element
3514 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3515 displayed (defaults to 75)
3516 fn Function A callback function to execute after closing the dialog. The arguments to the
3517 function will be btn (the name of the button that was clicked, if applicable,
3518 e.g. "ok"), and text (the value of the active text field, if applicable).
3519 Progress and wait dialogs will ignore this option since they do not respond to
3520 user actions and can only be closed programmatically, so any required function
3521 should be called by the same code after it closes the dialog.
3522 icon String A CSS class that provides a background image to be used as an icon for
3523 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3524 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3525 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3526 modal Boolean False to allow user interaction with the page while the message box is
3527 displayed (defaults to true)
3528 msg String A string that will replace the existing message box body text (defaults
3529 to the XHTML-compliant non-breaking space character ' ')
3530 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3531 progress Boolean True to display a progress bar (defaults to false)
3532 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3533 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3534 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3535 title String The title text
3536 value String The string value to set into the active textbox element if displayed
3537 wait Boolean True to display a progress bar (defaults to false)
3538 width Number The width of the dialog in pixels
3545 msg: 'Please enter your address:',
3547 buttons: Roo.MessageBox.OKCANCEL,
3550 animEl: 'addAddressBtn'
3553 * @param {Object} config Configuration options
3554 * @return {Roo.MessageBox} This message box
3556 show : function(options)
3559 // this causes nightmares if you show one dialog after another
3560 // especially on callbacks..
3562 if(this.isVisible()){
3565 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3566 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3567 Roo.log("New Dialog Message:" + options.msg )
3568 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3569 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3572 var d = this.getDialog();
3574 d.setTitle(opt.title || " ");
3575 d.closeEl.setDisplayed(opt.closable !== false);
3576 activeTextEl = textboxEl;
3577 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3582 textareaEl.setHeight(typeof opt.multiline == "number" ?
3583 opt.multiline : this.defaultTextHeight);
3584 activeTextEl = textareaEl;
3593 progressEl.setDisplayed(opt.progress === true);
3594 this.updateProgress(0);
3595 activeTextEl.dom.value = opt.value || "";
3597 dlg.setDefaultButton(activeTextEl);
3599 var bs = opt.buttons;
3603 }else if(bs && bs.yes){
3604 db = buttons["yes"];
3606 dlg.setDefaultButton(db);
3608 bwidth = updateButtons(opt.buttons);
3609 this.updateText(opt.msg);
3611 d.el.addClass(opt.cls);
3613 d.proxyDrag = opt.proxyDrag === true;
3614 d.modal = opt.modal !== false;
3615 d.mask = opt.modal !== false ? mask : false;
3617 // force it to the end of the z-index stack so it gets a cursor in FF
3618 document.body.appendChild(dlg.el.dom);
3619 d.animateTarget = null;
3620 d.show(options.animEl);
3626 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3627 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3628 * and closing the message box when the process is complete.
3629 * @param {String} title The title bar text
3630 * @param {String} msg The message box body text
3631 * @return {Roo.MessageBox} This message box
3633 progress : function(title, msg){
3640 minWidth: this.minProgressWidth,
3647 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3648 * If a callback function is passed it will be called after the user clicks the button, and the
3649 * id of the button that was clicked will be passed as the only parameter to the callback
3650 * (could also be the top-right close button).
3651 * @param {String} title The title bar text
3652 * @param {String} msg The message box body text
3653 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3654 * @param {Object} scope (optional) The scope of the callback function
3655 * @return {Roo.MessageBox} This message box
3657 alert : function(title, msg, fn, scope)
3672 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3673 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3674 * You are responsible for closing the message box when the process is complete.
3675 * @param {String} msg The message box body text
3676 * @param {String} title (optional) The title bar text
3677 * @return {Roo.MessageBox} This message box
3679 wait : function(msg, title){
3690 waitTimer = Roo.TaskMgr.start({
3692 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3700 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3701 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3702 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3703 * @param {String} title The title bar text
3704 * @param {String} msg The message box body text
3705 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3706 * @param {Object} scope (optional) The scope of the callback function
3707 * @return {Roo.MessageBox} This message box
3709 confirm : function(title, msg, fn, scope){
3713 buttons: this.YESNO,
3722 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3723 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3724 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3725 * (could also be the top-right close button) and the text that was entered will be passed as the two
3726 * parameters to the callback.
3727 * @param {String} title The title bar text
3728 * @param {String} msg The message box body text
3729 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3730 * @param {Object} scope (optional) The scope of the callback function
3731 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3732 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3733 * @return {Roo.MessageBox} This message box
3735 prompt : function(title, msg, fn, scope, multiline){
3739 buttons: this.OKCANCEL,
3744 multiline: multiline,
3751 * Button config that displays a single OK button
3756 * Button config that displays Yes and No buttons
3759 YESNO : {yes:true, no:true},
3761 * Button config that displays OK and Cancel buttons
3764 OKCANCEL : {ok:true, cancel:true},
3766 * Button config that displays Yes, No and Cancel buttons
3769 YESNOCANCEL : {yes:true, no:true, cancel:true},
3772 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3775 defaultTextHeight : 75,
3777 * The maximum width in pixels of the message box (defaults to 600)
3782 * The minimum width in pixels of the message box (defaults to 100)
3787 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3788 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3791 minProgressWidth : 250,
3793 * An object containing the default button text strings that can be overriden for localized language support.
3794 * Supported properties are: ok, cancel, yes and no.
3795 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3808 * Shorthand for {@link Roo.MessageBox}
3810 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3811 Roo.Msg = Roo.Msg || Roo.MessageBox;
3820 * @class Roo.bootstrap.Navbar
3821 * @extends Roo.bootstrap.Component
3822 * Bootstrap Navbar class
3825 * Create a new Navbar
3826 * @param {Object} config The config object
3830 Roo.bootstrap.Navbar = function(config){
3831 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3835 * @event beforetoggle
3836 * Fire before toggle the menu
3837 * @param {Roo.EventObject} e
3839 "beforetoggle" : true
3843 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3852 getAutoCreate : function(){
3855 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3859 initEvents :function ()
3861 //Roo.log(this.el.select('.navbar-toggle',true));
3862 this.el.select('.navbar-toggle',true).on('click', function() {
3863 if(this.fireEvent('beforetoggle', this) !== false){
3864 var ce = this.el.select('.navbar-collapse',true).first();
3865 ce.toggleClass('in'); // old...
3866 if (ce.hasClass('collapse')) {
3868 ce.removeClass('collapse show');
3869 ce.setHeight(ce.getHeight()); // resize it ...
3870 // now flag it as moving..
3871 ce.addClass('collapsing');
3873 (function() { ce.removeClass('collapsing'); }).defer(100);
3875 ce.addClass('collapsing');
3876 ce.removeClass('show');
3878 ce.removeClass('collapsing');
3879 ce.addClass('collapse');
3892 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3894 var size = this.el.getSize();
3895 this.maskEl.setSize(size.width, size.height);
3896 this.maskEl.enableDisplayMode("block");
3905 getChildContainer : function()
3907 if (this.el.select('.collapse').getCount()) {
3908 return this.el.select('.collapse',true).first();
3941 * @class Roo.bootstrap.NavSimplebar
3942 * @extends Roo.bootstrap.Navbar
3943 * Bootstrap Sidebar class
3945 * @cfg {Boolean} inverse is inverted color
3947 * @cfg {String} type (nav | pills | tabs)
3948 * @cfg {Boolean} arrangement stacked | justified
3949 * @cfg {String} align (left | right) alignment
3951 * @cfg {Boolean} main (true|false) main nav bar? default false
3952 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3954 * @cfg {String} tag (header|footer|nav|div) default is nav
3956 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3960 * Create a new Sidebar
3961 * @param {Object} config The config object
3965 Roo.bootstrap.NavSimplebar = function(config){
3966 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3969 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3985 getAutoCreate : function(){
3989 tag : this.tag || 'div',
3990 cls : 'navbar navbar-expand-lg'
3992 if (['light','white'].indexOf(this.weight) > -1) {
3993 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
3995 cfg.cls += ' bg-' + this.weight;
4007 this.type = this.type || 'nav';
4008 if (['tabs','pills'].indexOf(this.type)!==-1) {
4009 cfg.cn[0].cls += ' nav-' + this.type
4013 if (this.type!=='nav') {
4014 Roo.log('nav type must be nav/tabs/pills')
4016 cfg.cn[0].cls += ' navbar-nav'
4022 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4023 cfg.cn[0].cls += ' nav-' + this.arrangement;
4027 if (this.align === 'right') {
4028 cfg.cn[0].cls += ' navbar-right';
4032 cfg.cls += ' navbar-inverse';
4056 * navbar-expand-md fixed-top
4060 * @class Roo.bootstrap.NavHeaderbar
4061 * @extends Roo.bootstrap.NavSimplebar
4062 * Bootstrap Sidebar class
4064 * @cfg {String} brand what is brand
4065 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4066 * @cfg {String} brand_href href of the brand
4067 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
4068 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4069 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4070 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4073 * Create a new Sidebar
4074 * @param {Object} config The config object
4078 Roo.bootstrap.NavHeaderbar = function(config){
4079 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4083 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
4090 desktopCenter : false,
4093 getAutoCreate : function(){
4096 tag: this.nav || 'nav',
4097 cls: 'navbar navbar-expand-md',
4103 if (this.desktopCenter) {
4104 cn.push({cls : 'container', cn : []});
4112 cls: 'navbar-toggle navbar-toggler',
4113 'data-toggle': 'collapse',
4118 html: 'Toggle navigation'
4122 cls: 'icon-bar navbar-toggler-icon'
4135 cn.push( Roo.bootstrap.version == 4 ? btn : {
4137 cls: 'navbar-header',
4146 cls: 'collapse navbar-collapse',
4150 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4152 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4153 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4155 // tag can override this..
4157 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4160 if (this.brand !== '') {
4161 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4162 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4164 href: this.brand_href ? this.brand_href : '#',
4165 cls: 'navbar-brand',
4173 cfg.cls += ' main-nav';
4181 getHeaderChildContainer : function()
4183 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4184 return this.el.select('.navbar-header',true).first();
4187 return this.getChildContainer();
4191 initEvents : function()
4193 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4195 if (this.autohide) {
4200 Roo.get(document).on('scroll',function(e) {
4201 var ns = Roo.get(document).getScroll().top;
4202 var os = prevScroll;
4206 ft.removeClass('slideDown');
4207 ft.addClass('slideUp');
4210 ft.removeClass('slideUp');
4211 ft.addClass('slideDown');
4232 * @class Roo.bootstrap.NavSidebar
4233 * @extends Roo.bootstrap.Navbar
4234 * Bootstrap Sidebar class
4237 * Create a new Sidebar
4238 * @param {Object} config The config object
4242 Roo.bootstrap.NavSidebar = function(config){
4243 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4246 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4248 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4250 getAutoCreate : function(){
4255 cls: 'sidebar sidebar-nav'
4277 * @class Roo.bootstrap.NavGroup
4278 * @extends Roo.bootstrap.Component
4279 * Bootstrap NavGroup class
4280 * @cfg {String} align (left|right)
4281 * @cfg {Boolean} inverse
4282 * @cfg {String} type (nav|pills|tab) default nav
4283 * @cfg {String} navId - reference Id for navbar.
4287 * Create a new nav group
4288 * @param {Object} config The config object
4291 Roo.bootstrap.NavGroup = function(config){
4292 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4295 Roo.bootstrap.NavGroup.register(this);
4299 * Fires when the active item changes
4300 * @param {Roo.bootstrap.NavGroup} this
4301 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4302 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4309 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4320 getAutoCreate : function()
4322 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4329 if (['tabs','pills'].indexOf(this.type)!==-1) {
4330 cfg.cls += ' nav-' + this.type
4332 if (this.type!=='nav') {
4333 Roo.log('nav type must be nav/tabs/pills')
4335 cfg.cls += ' navbar-nav'
4338 if (this.parent() && this.parent().sidebar) {
4341 cls: 'dashboard-menu sidebar-menu'
4347 if (this.form === true) {
4353 if (this.align === 'right') {
4354 cfg.cls += ' navbar-right ml-md-auto';
4356 cfg.cls += ' navbar-left';
4360 if (this.align === 'right') {
4361 cfg.cls += ' navbar-right ml-md-auto';
4363 cfg.cls += ' mr-auto';
4367 cfg.cls += ' navbar-inverse';
4375 * sets the active Navigation item
4376 * @param {Roo.bootstrap.NavItem} the new current navitem
4378 setActiveItem : function(item)
4381 Roo.each(this.navItems, function(v){
4386 v.setActive(false, true);
4393 item.setActive(true, true);
4394 this.fireEvent('changed', this, item, prev);
4399 * gets the active Navigation item
4400 * @return {Roo.bootstrap.NavItem} the current navitem
4402 getActive : function()
4406 Roo.each(this.navItems, function(v){
4417 indexOfNav : function()
4421 Roo.each(this.navItems, function(v,i){
4432 * adds a Navigation item
4433 * @param {Roo.bootstrap.NavItem} the navitem to add
4435 addItem : function(cfg)
4437 var cn = new Roo.bootstrap.NavItem(cfg);
4439 cn.parentId = this.id;
4440 cn.onRender(this.el, null);
4444 * register a Navigation item
4445 * @param {Roo.bootstrap.NavItem} the navitem to add
4447 register : function(item)
4449 this.navItems.push( item);
4450 item.navId = this.navId;
4455 * clear all the Navigation item
4458 clearAll : function()
4461 this.el.dom.innerHTML = '';
4464 getNavItem: function(tabId)
4467 Roo.each(this.navItems, function(e) {
4468 if (e.tabId == tabId) {
4478 setActiveNext : function()
4480 var i = this.indexOfNav(this.getActive());
4481 if (i > this.navItems.length) {
4484 this.setActiveItem(this.navItems[i+1]);
4486 setActivePrev : function()
4488 var i = this.indexOfNav(this.getActive());
4492 this.setActiveItem(this.navItems[i-1]);
4494 clearWasActive : function(except) {
4495 Roo.each(this.navItems, function(e) {
4496 if (e.tabId != except.tabId && e.was_active) {
4497 e.was_active = false;
4504 getWasActive : function ()
4507 Roo.each(this.navItems, function(e) {
4522 Roo.apply(Roo.bootstrap.NavGroup, {
4526 * register a Navigation Group
4527 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4529 register : function(navgrp)
4531 this.groups[navgrp.navId] = navgrp;
4535 * fetch a Navigation Group based on the navigation ID
4536 * @param {string} the navgroup to add
4537 * @returns {Roo.bootstrap.NavGroup} the navgroup
4539 get: function(navId) {
4540 if (typeof(this.groups[navId]) == 'undefined') {
4542 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4544 return this.groups[navId] ;
4559 * @class Roo.bootstrap.NavItem
4560 * @extends Roo.bootstrap.Component
4561 * Bootstrap Navbar.NavItem class
4562 * @cfg {String} href link to
4563 * @cfg {String} html content of button
4564 * @cfg {String} badge text inside badge
4565 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4566 * @cfg {String} glyphicon DEPRICATED - use fa
4567 * @cfg {String} icon DEPRICATED - use fa
4568 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4569 * @cfg {Boolean} active Is item active
4570 * @cfg {Boolean} disabled Is item disabled
4572 * @cfg {Boolean} preventDefault (true | false) default false
4573 * @cfg {String} tabId the tab that this item activates.
4574 * @cfg {String} tagtype (a|span) render as a href or span?
4575 * @cfg {Boolean} animateRef (true|false) link to element default false
4578 * Create a new Navbar Item
4579 * @param {Object} config The config object
4581 Roo.bootstrap.NavItem = function(config){
4582 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4587 * The raw click event for the entire grid.
4588 * @param {Roo.EventObject} e
4593 * Fires when the active item active state changes
4594 * @param {Roo.bootstrap.NavItem} this
4595 * @param {boolean} state the new state
4601 * Fires when scroll to element
4602 * @param {Roo.bootstrap.NavItem} this
4603 * @param {Object} options
4604 * @param {Roo.EventObject} e
4612 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4621 preventDefault : false,
4628 getAutoCreate : function(){
4637 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4639 if (this.disabled) {
4640 cfg.cls += ' disabled';
4643 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4647 href : this.href || "#",
4648 html: this.html || ''
4651 if (this.tagtype == 'a') {
4652 cfg.cn[0].cls = 'nav-link';
4655 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4658 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4660 if(this.glyphicon) {
4661 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4666 cfg.cn[0].html += " <span class='caret'></span>";
4670 if (this.badge !== '') {
4672 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4680 initEvents: function()
4682 if (typeof (this.menu) != 'undefined') {
4683 this.menu.parentType = this.xtype;
4684 this.menu.triggerEl = this.el;
4685 this.menu = this.addxtype(Roo.apply({}, this.menu));
4688 this.el.select('a',true).on('click', this.onClick, this);
4690 if(this.tagtype == 'span'){
4691 this.el.select('span',true).on('click', this.onClick, this);
4694 // at this point parent should be available..
4695 this.parent().register(this);
4698 onClick : function(e)
4700 if (e.getTarget('.dropdown-menu-item')) {
4701 // did you click on a menu itemm.... - then don't trigger onclick..
4706 this.preventDefault ||
4709 Roo.log("NavItem - prevent Default?");
4713 if (this.disabled) {
4717 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4718 if (tg && tg.transition) {
4719 Roo.log("waiting for the transitionend");
4725 //Roo.log("fire event clicked");
4726 if(this.fireEvent('click', this, e) === false){
4730 if(this.tagtype == 'span'){
4734 //Roo.log(this.href);
4735 var ael = this.el.select('a',true).first();
4738 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4739 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4740 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4741 return; // ignore... - it's a 'hash' to another page.
4743 Roo.log("NavItem - prevent Default?");
4745 this.scrollToElement(e);
4749 var p = this.parent();
4751 if (['tabs','pills'].indexOf(p.type)!==-1) {
4752 if (typeof(p.setActiveItem) !== 'undefined') {
4753 p.setActiveItem(this);
4757 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4758 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4759 // remove the collapsed menu expand...
4760 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4764 isActive: function () {
4767 setActive : function(state, fire, is_was_active)
4769 if (this.active && !state && this.navId) {
4770 this.was_active = true;
4771 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4773 nv.clearWasActive(this);
4777 this.active = state;
4780 this.el.removeClass('active');
4781 } else if (!this.el.hasClass('active')) {
4782 this.el.addClass('active');
4785 this.fireEvent('changed', this, state);
4788 // show a panel if it's registered and related..
4790 if (!this.navId || !this.tabId || !state || is_was_active) {
4794 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4798 var pan = tg.getPanelByName(this.tabId);
4802 // if we can not flip to new panel - go back to old nav highlight..
4803 if (false == tg.showPanel(pan)) {
4804 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4806 var onav = nv.getWasActive();
4808 onav.setActive(true, false, true);
4817 // this should not be here...
4818 setDisabled : function(state)
4820 this.disabled = state;
4822 this.el.removeClass('disabled');
4823 } else if (!this.el.hasClass('disabled')) {
4824 this.el.addClass('disabled');
4830 * Fetch the element to display the tooltip on.
4831 * @return {Roo.Element} defaults to this.el
4833 tooltipEl : function()
4835 return this.el.select('' + this.tagtype + '', true).first();
4838 scrollToElement : function(e)
4840 var c = document.body;
4843 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4845 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4846 c = document.documentElement;
4849 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4855 var o = target.calcOffsetsTo(c);
4862 this.fireEvent('scrollto', this, options, e);
4864 Roo.get(c).scrollTo('top', options.value, true);
4877 * <span> icon </span>
4878 * <span> text </span>
4879 * <span>badge </span>
4883 * @class Roo.bootstrap.NavSidebarItem
4884 * @extends Roo.bootstrap.NavItem
4885 * Bootstrap Navbar.NavSidebarItem class
4886 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4887 * {Boolean} open is the menu open
4888 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4889 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4890 * {String} buttonSize (sm|md|lg)the extra classes for the button
4891 * {Boolean} showArrow show arrow next to the text (default true)
4893 * Create a new Navbar Button
4894 * @param {Object} config The config object
4896 Roo.bootstrap.NavSidebarItem = function(config){
4897 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4902 * The raw click event for the entire grid.
4903 * @param {Roo.EventObject} e
4908 * Fires when the active item active state changes
4909 * @param {Roo.bootstrap.NavSidebarItem} this
4910 * @param {boolean} state the new state
4918 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4920 badgeWeight : 'default',
4926 buttonWeight : 'default',
4932 getAutoCreate : function(){
4937 href : this.href || '#',
4943 if(this.buttonView){
4946 href : this.href || '#',
4947 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4960 cfg.cls += ' active';
4963 if (this.disabled) {
4964 cfg.cls += ' disabled';
4967 cfg.cls += ' open x-open';
4970 if (this.glyphicon || this.icon) {
4971 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4972 a.cn.push({ tag : 'i', cls : c }) ;
4975 if(!this.buttonView){
4978 html : this.html || ''
4985 if (this.badge !== '') {
4986 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4992 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4995 a.cls += ' dropdown-toggle treeview' ;
5001 initEvents : function()
5003 if (typeof (this.menu) != 'undefined') {
5004 this.menu.parentType = this.xtype;
5005 this.menu.triggerEl = this.el;
5006 this.menu = this.addxtype(Roo.apply({}, this.menu));
5009 this.el.on('click', this.onClick, this);
5011 if(this.badge !== ''){
5012 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5017 onClick : function(e)
5024 if(this.preventDefault){
5028 this.fireEvent('click', this);
5031 disable : function()
5033 this.setDisabled(true);
5038 this.setDisabled(false);
5041 setDisabled : function(state)
5043 if(this.disabled == state){
5047 this.disabled = state;
5050 this.el.addClass('disabled');
5054 this.el.removeClass('disabled');
5059 setActive : function(state)
5061 if(this.active == state){
5065 this.active = state;
5068 this.el.addClass('active');
5072 this.el.removeClass('active');
5077 isActive: function ()
5082 setBadge : function(str)
5088 this.badgeEl.dom.innerHTML = str;
5105 * @class Roo.bootstrap.Row
5106 * @extends Roo.bootstrap.Component
5107 * Bootstrap Row class (contains columns...)
5111 * @param {Object} config The config object
5114 Roo.bootstrap.Row = function(config){
5115 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5118 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5120 getAutoCreate : function(){
5139 * @class Roo.bootstrap.Element
5140 * @extends Roo.bootstrap.Component
5141 * Bootstrap Element class
5142 * @cfg {String} html contents of the element
5143 * @cfg {String} tag tag of the element
5144 * @cfg {String} cls class of the element
5145 * @cfg {Boolean} preventDefault (true|false) default false
5146 * @cfg {Boolean} clickable (true|false) default false
5149 * Create a new Element
5150 * @param {Object} config The config object
5153 Roo.bootstrap.Element = function(config){
5154 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5160 * When a element is chick
5161 * @param {Roo.bootstrap.Element} this
5162 * @param {Roo.EventObject} e
5168 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5173 preventDefault: false,
5176 getAutoCreate : function(){
5180 // cls: this.cls, double assign in parent class Component.js :: onRender
5187 initEvents: function()
5189 Roo.bootstrap.Element.superclass.initEvents.call(this);
5192 this.el.on('click', this.onClick, this);
5197 onClick : function(e)
5199 if(this.preventDefault){
5203 this.fireEvent('click', this, e);
5206 getValue : function()
5208 return this.el.dom.innerHTML;
5211 setValue : function(value)
5213 this.el.dom.innerHTML = value;
5228 * @class Roo.bootstrap.Pagination
5229 * @extends Roo.bootstrap.Component
5230 * Bootstrap Pagination class
5231 * @cfg {String} size xs | sm | md | lg
5232 * @cfg {Boolean} inverse false | true
5235 * Create a new Pagination
5236 * @param {Object} config The config object
5239 Roo.bootstrap.Pagination = function(config){
5240 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5243 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5249 getAutoCreate : function(){
5255 cfg.cls += ' inverse';
5261 cfg.cls += " " + this.cls;
5279 * @class Roo.bootstrap.PaginationItem
5280 * @extends Roo.bootstrap.Component
5281 * Bootstrap PaginationItem class
5282 * @cfg {String} html text
5283 * @cfg {String} href the link
5284 * @cfg {Boolean} preventDefault (true | false) default true
5285 * @cfg {Boolean} active (true | false) default false
5286 * @cfg {Boolean} disabled default false
5290 * Create a new PaginationItem
5291 * @param {Object} config The config object
5295 Roo.bootstrap.PaginationItem = function(config){
5296 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5301 * The raw click event for the entire grid.
5302 * @param {Roo.EventObject} e
5308 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5312 preventDefault: true,
5317 getAutoCreate : function(){
5323 href : this.href ? this.href : '#',
5324 html : this.html ? this.html : ''
5334 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5338 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5344 initEvents: function() {
5346 this.el.on('click', this.onClick, this);
5349 onClick : function(e)
5351 Roo.log('PaginationItem on click ');
5352 if(this.preventDefault){
5360 this.fireEvent('click', this, e);
5376 * @class Roo.bootstrap.Slider
5377 * @extends Roo.bootstrap.Component
5378 * Bootstrap Slider class
5381 * Create a new Slider
5382 * @param {Object} config The config object
5385 Roo.bootstrap.Slider = function(config){
5386 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5389 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5391 getAutoCreate : function(){
5395 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5399 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5411 * Ext JS Library 1.1.1
5412 * Copyright(c) 2006-2007, Ext JS, LLC.
5414 * Originally Released Under LGPL - original licence link has changed is not relivant.
5417 * <script type="text/javascript">
5422 * @class Roo.grid.ColumnModel
5423 * @extends Roo.util.Observable
5424 * This is the default implementation of a ColumnModel used by the Grid. It defines
5425 * the columns in the grid.
5428 var colModel = new Roo.grid.ColumnModel([
5429 {header: "Ticker", width: 60, sortable: true, locked: true},
5430 {header: "Company Name", width: 150, sortable: true},
5431 {header: "Market Cap.", width: 100, sortable: true},
5432 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5433 {header: "Employees", width: 100, sortable: true, resizable: false}
5438 * The config options listed for this class are options which may appear in each
5439 * individual column definition.
5440 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5442 * @param {Object} config An Array of column config objects. See this class's
5443 * config objects for details.
5445 Roo.grid.ColumnModel = function(config){
5447 * The config passed into the constructor
5449 this.config = config;
5452 // if no id, create one
5453 // if the column does not have a dataIndex mapping,
5454 // map it to the order it is in the config
5455 for(var i = 0, len = config.length; i < len; i++){
5457 if(typeof c.dataIndex == "undefined"){
5460 if(typeof c.renderer == "string"){
5461 c.renderer = Roo.util.Format[c.renderer];
5463 if(typeof c.id == "undefined"){
5466 if(c.editor && c.editor.xtype){
5467 c.editor = Roo.factory(c.editor, Roo.grid);
5469 if(c.editor && c.editor.isFormField){
5470 c.editor = new Roo.grid.GridEditor(c.editor);
5472 this.lookup[c.id] = c;
5476 * The width of columns which have no width specified (defaults to 100)
5479 this.defaultWidth = 100;
5482 * Default sortable of columns which have no sortable specified (defaults to false)
5485 this.defaultSortable = false;
5489 * @event widthchange
5490 * Fires when the width of a column changes.
5491 * @param {ColumnModel} this
5492 * @param {Number} columnIndex The column index
5493 * @param {Number} newWidth The new width
5495 "widthchange": true,
5497 * @event headerchange
5498 * Fires when the text of a header changes.
5499 * @param {ColumnModel} this
5500 * @param {Number} columnIndex The column index
5501 * @param {Number} newText The new header text
5503 "headerchange": true,
5505 * @event hiddenchange
5506 * Fires when a column is hidden or "unhidden".
5507 * @param {ColumnModel} this
5508 * @param {Number} columnIndex The column index
5509 * @param {Boolean} hidden true if hidden, false otherwise
5511 "hiddenchange": true,
5513 * @event columnmoved
5514 * Fires when a column is moved.
5515 * @param {ColumnModel} this
5516 * @param {Number} oldIndex
5517 * @param {Number} newIndex
5519 "columnmoved" : true,
5521 * @event columlockchange
5522 * Fires when a column's locked state is changed
5523 * @param {ColumnModel} this
5524 * @param {Number} colIndex
5525 * @param {Boolean} locked true if locked
5527 "columnlockchange" : true
5529 Roo.grid.ColumnModel.superclass.constructor.call(this);
5531 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5533 * @cfg {String} header The header text to display in the Grid view.
5536 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5537 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5538 * specified, the column's index is used as an index into the Record's data Array.
5541 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5542 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5545 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5546 * Defaults to the value of the {@link #defaultSortable} property.
5547 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5550 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5553 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5556 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5559 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5562 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5563 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5564 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5565 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5568 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5571 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5574 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5577 * @cfg {String} cursor (Optional)
5580 * @cfg {String} tooltip (Optional)
5583 * @cfg {Number} xs (Optional)
5586 * @cfg {Number} sm (Optional)
5589 * @cfg {Number} md (Optional)
5592 * @cfg {Number} lg (Optional)
5595 * Returns the id of the column at the specified index.
5596 * @param {Number} index The column index
5597 * @return {String} the id
5599 getColumnId : function(index){
5600 return this.config[index].id;
5604 * Returns the column for a specified id.
5605 * @param {String} id The column id
5606 * @return {Object} the column
5608 getColumnById : function(id){
5609 return this.lookup[id];
5614 * Returns the column for a specified dataIndex.
5615 * @param {String} dataIndex The column dataIndex
5616 * @return {Object|Boolean} the column or false if not found
5618 getColumnByDataIndex: function(dataIndex){
5619 var index = this.findColumnIndex(dataIndex);
5620 return index > -1 ? this.config[index] : false;
5624 * Returns the index for a specified column id.
5625 * @param {String} id The column id
5626 * @return {Number} the index, or -1 if not found
5628 getIndexById : function(id){
5629 for(var i = 0, len = this.config.length; i < len; i++){
5630 if(this.config[i].id == id){
5638 * Returns the index for a specified column dataIndex.
5639 * @param {String} dataIndex The column dataIndex
5640 * @return {Number} the index, or -1 if not found
5643 findColumnIndex : function(dataIndex){
5644 for(var i = 0, len = this.config.length; i < len; i++){
5645 if(this.config[i].dataIndex == dataIndex){
5653 moveColumn : function(oldIndex, newIndex){
5654 var c = this.config[oldIndex];
5655 this.config.splice(oldIndex, 1);
5656 this.config.splice(newIndex, 0, c);
5657 this.dataMap = null;
5658 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5661 isLocked : function(colIndex){
5662 return this.config[colIndex].locked === true;
5665 setLocked : function(colIndex, value, suppressEvent){
5666 if(this.isLocked(colIndex) == value){
5669 this.config[colIndex].locked = value;
5671 this.fireEvent("columnlockchange", this, colIndex, value);
5675 getTotalLockedWidth : function(){
5677 for(var i = 0; i < this.config.length; i++){
5678 if(this.isLocked(i) && !this.isHidden(i)){
5679 this.totalWidth += this.getColumnWidth(i);
5685 getLockedCount : function(){
5686 for(var i = 0, len = this.config.length; i < len; i++){
5687 if(!this.isLocked(i)){
5692 return this.config.length;
5696 * Returns the number of columns.
5699 getColumnCount : function(visibleOnly){
5700 if(visibleOnly === true){
5702 for(var i = 0, len = this.config.length; i < len; i++){
5703 if(!this.isHidden(i)){
5709 return this.config.length;
5713 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5714 * @param {Function} fn
5715 * @param {Object} scope (optional)
5716 * @return {Array} result
5718 getColumnsBy : function(fn, scope){
5720 for(var i = 0, len = this.config.length; i < len; i++){
5721 var c = this.config[i];
5722 if(fn.call(scope||this, c, i) === true){
5730 * Returns true if the specified column is sortable.
5731 * @param {Number} col The column index
5734 isSortable : function(col){
5735 if(typeof this.config[col].sortable == "undefined"){
5736 return this.defaultSortable;
5738 return this.config[col].sortable;
5742 * Returns the rendering (formatting) function defined for the column.
5743 * @param {Number} col The column index.
5744 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5746 getRenderer : function(col){
5747 if(!this.config[col].renderer){
5748 return Roo.grid.ColumnModel.defaultRenderer;
5750 return this.config[col].renderer;
5754 * Sets the rendering (formatting) function for a column.
5755 * @param {Number} col The column index
5756 * @param {Function} fn The function to use to process the cell's raw data
5757 * to return HTML markup for the grid view. The render function is called with
5758 * the following parameters:<ul>
5759 * <li>Data value.</li>
5760 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5761 * <li>css A CSS style string to apply to the table cell.</li>
5762 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5763 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5764 * <li>Row index</li>
5765 * <li>Column index</li>
5766 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5768 setRenderer : function(col, fn){
5769 this.config[col].renderer = fn;
5773 * Returns the width for the specified column.
5774 * @param {Number} col The column index
5777 getColumnWidth : function(col){
5778 return this.config[col].width * 1 || this.defaultWidth;
5782 * Sets the width for a column.
5783 * @param {Number} col The column index
5784 * @param {Number} width The new width
5786 setColumnWidth : function(col, width, suppressEvent){
5787 this.config[col].width = width;
5788 this.totalWidth = null;
5790 this.fireEvent("widthchange", this, col, width);
5795 * Returns the total width of all columns.
5796 * @param {Boolean} includeHidden True to include hidden column widths
5799 getTotalWidth : function(includeHidden){
5800 if(!this.totalWidth){
5801 this.totalWidth = 0;
5802 for(var i = 0, len = this.config.length; i < len; i++){
5803 if(includeHidden || !this.isHidden(i)){
5804 this.totalWidth += this.getColumnWidth(i);
5808 return this.totalWidth;
5812 * Returns the header for the specified column.
5813 * @param {Number} col The column index
5816 getColumnHeader : function(col){
5817 return this.config[col].header;
5821 * Sets the header for a column.
5822 * @param {Number} col The column index
5823 * @param {String} header The new header
5825 setColumnHeader : function(col, header){
5826 this.config[col].header = header;
5827 this.fireEvent("headerchange", this, col, header);
5831 * Returns the tooltip for the specified column.
5832 * @param {Number} col The column index
5835 getColumnTooltip : function(col){
5836 return this.config[col].tooltip;
5839 * Sets the tooltip for a column.
5840 * @param {Number} col The column index
5841 * @param {String} tooltip The new tooltip
5843 setColumnTooltip : function(col, tooltip){
5844 this.config[col].tooltip = tooltip;
5848 * Returns the dataIndex for the specified column.
5849 * @param {Number} col The column index
5852 getDataIndex : function(col){
5853 return this.config[col].dataIndex;
5857 * Sets the dataIndex for a column.
5858 * @param {Number} col The column index
5859 * @param {Number} dataIndex The new dataIndex
5861 setDataIndex : function(col, dataIndex){
5862 this.config[col].dataIndex = dataIndex;
5868 * Returns true if the cell is editable.
5869 * @param {Number} colIndex The column index
5870 * @param {Number} rowIndex The row index - this is nto actually used..?
5873 isCellEditable : function(colIndex, rowIndex){
5874 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5878 * Returns the editor defined for the cell/column.
5879 * return false or null to disable editing.
5880 * @param {Number} colIndex The column index
5881 * @param {Number} rowIndex The row index
5884 getCellEditor : function(colIndex, rowIndex){
5885 return this.config[colIndex].editor;
5889 * Sets if a column is editable.
5890 * @param {Number} col The column index
5891 * @param {Boolean} editable True if the column is editable
5893 setEditable : function(col, editable){
5894 this.config[col].editable = editable;
5899 * Returns true if the column is hidden.
5900 * @param {Number} colIndex The column index
5903 isHidden : function(colIndex){
5904 return this.config[colIndex].hidden;
5909 * Returns true if the column width cannot be changed
5911 isFixed : function(colIndex){
5912 return this.config[colIndex].fixed;
5916 * Returns true if the column can be resized
5919 isResizable : function(colIndex){
5920 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5923 * Sets if a column is hidden.
5924 * @param {Number} colIndex The column index
5925 * @param {Boolean} hidden True if the column is hidden
5927 setHidden : function(colIndex, hidden){
5928 this.config[colIndex].hidden = hidden;
5929 this.totalWidth = null;
5930 this.fireEvent("hiddenchange", this, colIndex, hidden);
5934 * Sets the editor for a column.
5935 * @param {Number} col The column index
5936 * @param {Object} editor The editor object
5938 setEditor : function(col, editor){
5939 this.config[col].editor = editor;
5943 Roo.grid.ColumnModel.defaultRenderer = function(value)
5945 if(typeof value == "object") {
5948 if(typeof value == "string" && value.length < 1){
5952 return String.format("{0}", value);
5955 // Alias for backwards compatibility
5956 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5959 * Ext JS Library 1.1.1
5960 * Copyright(c) 2006-2007, Ext JS, LLC.
5962 * Originally Released Under LGPL - original licence link has changed is not relivant.
5965 * <script type="text/javascript">
5969 * @class Roo.LoadMask
5970 * A simple utility class for generically masking elements while loading data. If the element being masked has
5971 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5972 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5973 * element's UpdateManager load indicator and will be destroyed after the initial load.
5975 * Create a new LoadMask
5976 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5977 * @param {Object} config The config object
5979 Roo.LoadMask = function(el, config){
5980 this.el = Roo.get(el);
5981 Roo.apply(this, config);
5983 this.store.on('beforeload', this.onBeforeLoad, this);
5984 this.store.on('load', this.onLoad, this);
5985 this.store.on('loadexception', this.onLoadException, this);
5986 this.removeMask = false;
5988 var um = this.el.getUpdateManager();
5989 um.showLoadIndicator = false; // disable the default indicator
5990 um.on('beforeupdate', this.onBeforeLoad, this);
5991 um.on('update', this.onLoad, this);
5992 um.on('failure', this.onLoad, this);
5993 this.removeMask = true;
5997 Roo.LoadMask.prototype = {
5999 * @cfg {Boolean} removeMask
6000 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6001 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
6005 * The text to display in a centered loading message box (defaults to 'Loading...')
6009 * @cfg {String} msgCls
6010 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6012 msgCls : 'x-mask-loading',
6015 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6021 * Disables the mask to prevent it from being displayed
6023 disable : function(){
6024 this.disabled = true;
6028 * Enables the mask so that it can be displayed
6030 enable : function(){
6031 this.disabled = false;
6034 onLoadException : function()
6038 if (typeof(arguments[3]) != 'undefined') {
6039 Roo.MessageBox.alert("Error loading",arguments[3]);
6043 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6044 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6051 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6056 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6060 onBeforeLoad : function(){
6062 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6067 destroy : function(){
6069 this.store.un('beforeload', this.onBeforeLoad, this);
6070 this.store.un('load', this.onLoad, this);
6071 this.store.un('loadexception', this.onLoadException, this);
6073 var um = this.el.getUpdateManager();
6074 um.un('beforeupdate', this.onBeforeLoad, this);
6075 um.un('update', this.onLoad, this);
6076 um.un('failure', this.onLoad, this);
6087 * @class Roo.bootstrap.Table
6088 * @extends Roo.bootstrap.Component
6089 * Bootstrap Table class
6090 * @cfg {String} cls table class
6091 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6092 * @cfg {String} bgcolor Specifies the background color for a table
6093 * @cfg {Number} border Specifies whether the table cells should have borders or not
6094 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6095 * @cfg {Number} cellspacing Specifies the space between cells
6096 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6097 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6098 * @cfg {String} sortable Specifies that the table should be sortable
6099 * @cfg {String} summary Specifies a summary of the content of a table
6100 * @cfg {Number} width Specifies the width of a table
6101 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6103 * @cfg {boolean} striped Should the rows be alternative striped
6104 * @cfg {boolean} bordered Add borders to the table
6105 * @cfg {boolean} hover Add hover highlighting
6106 * @cfg {boolean} condensed Format condensed
6107 * @cfg {boolean} responsive Format condensed
6108 * @cfg {Boolean} loadMask (true|false) default false
6109 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6110 * @cfg {Boolean} headerShow (true|false) generate thead, default true
6111 * @cfg {Boolean} rowSelection (true|false) default false
6112 * @cfg {Boolean} cellSelection (true|false) default false
6113 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6114 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
6115 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
6116 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6120 * Create a new Table
6121 * @param {Object} config The config object
6124 Roo.bootstrap.Table = function(config){
6125 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6130 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6131 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6132 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6133 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6135 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6137 this.sm.grid = this;
6138 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6139 this.sm = this.selModel;
6140 this.sm.xmodule = this.xmodule || false;
6143 if (this.cm && typeof(this.cm.config) == 'undefined') {
6144 this.colModel = new Roo.grid.ColumnModel(this.cm);
6145 this.cm = this.colModel;
6146 this.cm.xmodule = this.xmodule || false;
6149 this.store= Roo.factory(this.store, Roo.data);
6150 this.ds = this.store;
6151 this.ds.xmodule = this.xmodule || false;
6154 if (this.footer && this.store) {
6155 this.footer.dataSource = this.ds;
6156 this.footer = Roo.factory(this.footer);
6163 * Fires when a cell is clicked
6164 * @param {Roo.bootstrap.Table} this
6165 * @param {Roo.Element} el
6166 * @param {Number} rowIndex
6167 * @param {Number} columnIndex
6168 * @param {Roo.EventObject} e
6172 * @event celldblclick
6173 * Fires when a cell is double clicked
6174 * @param {Roo.bootstrap.Table} this
6175 * @param {Roo.Element} el
6176 * @param {Number} rowIndex
6177 * @param {Number} columnIndex
6178 * @param {Roo.EventObject} e
6180 "celldblclick" : true,
6183 * Fires when a row is clicked
6184 * @param {Roo.bootstrap.Table} this
6185 * @param {Roo.Element} el
6186 * @param {Number} rowIndex
6187 * @param {Roo.EventObject} e
6191 * @event rowdblclick
6192 * Fires when a row is double clicked
6193 * @param {Roo.bootstrap.Table} this
6194 * @param {Roo.Element} el
6195 * @param {Number} rowIndex
6196 * @param {Roo.EventObject} e
6198 "rowdblclick" : true,
6201 * Fires when a mouseover occur
6202 * @param {Roo.bootstrap.Table} this
6203 * @param {Roo.Element} el
6204 * @param {Number} rowIndex
6205 * @param {Number} columnIndex
6206 * @param {Roo.EventObject} e
6211 * Fires when a mouseout occur
6212 * @param {Roo.bootstrap.Table} this
6213 * @param {Roo.Element} el
6214 * @param {Number} rowIndex
6215 * @param {Number} columnIndex
6216 * @param {Roo.EventObject} e
6221 * Fires when a row is rendered, so you can change add a style to it.
6222 * @param {Roo.bootstrap.Table} this
6223 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6227 * @event rowsrendered
6228 * Fires when all the rows have been rendered
6229 * @param {Roo.bootstrap.Table} this
6231 'rowsrendered' : true,
6233 * @event contextmenu
6234 * The raw contextmenu event for the entire grid.
6235 * @param {Roo.EventObject} e
6237 "contextmenu" : true,
6239 * @event rowcontextmenu
6240 * Fires when a row is right clicked
6241 * @param {Roo.bootstrap.Table} this
6242 * @param {Number} rowIndex
6243 * @param {Roo.EventObject} e
6245 "rowcontextmenu" : true,
6247 * @event cellcontextmenu
6248 * Fires when a cell is right clicked
6249 * @param {Roo.bootstrap.Table} this
6250 * @param {Number} rowIndex
6251 * @param {Number} cellIndex
6252 * @param {Roo.EventObject} e
6254 "cellcontextmenu" : true,
6256 * @event headercontextmenu
6257 * Fires when a header is right clicked
6258 * @param {Roo.bootstrap.Table} this
6259 * @param {Number} columnIndex
6260 * @param {Roo.EventObject} e
6262 "headercontextmenu" : true
6266 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6292 rowSelection : false,
6293 cellSelection : false,
6296 // Roo.Element - the tbody
6298 // Roo.Element - thead element
6301 container: false, // used by gridpanel...
6307 auto_hide_footer : false,
6309 getAutoCreate : function()
6311 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6318 if (this.scrollBody) {
6319 cfg.cls += ' table-body-fixed';
6322 cfg.cls += ' table-striped';
6326 cfg.cls += ' table-hover';
6328 if (this.bordered) {
6329 cfg.cls += ' table-bordered';
6331 if (this.condensed) {
6332 cfg.cls += ' table-condensed';
6334 if (this.responsive) {
6335 cfg.cls += ' table-responsive';
6339 cfg.cls+= ' ' +this.cls;
6342 // this lot should be simplifed...
6355 ].forEach(function(k) {
6363 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6366 if(this.store || this.cm){
6367 if(this.headerShow){
6368 cfg.cn.push(this.renderHeader());
6371 cfg.cn.push(this.renderBody());
6373 if(this.footerShow){
6374 cfg.cn.push(this.renderFooter());
6376 // where does this come from?
6377 //cfg.cls+= ' TableGrid';
6380 return { cn : [ cfg ] };
6383 initEvents : function()
6385 if(!this.store || !this.cm){
6388 if (this.selModel) {
6389 this.selModel.initEvents();
6393 //Roo.log('initEvents with ds!!!!');
6395 this.mainBody = this.el.select('tbody', true).first();
6396 this.mainHead = this.el.select('thead', true).first();
6397 this.mainFoot = this.el.select('tfoot', true).first();
6403 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6404 e.on('click', _this.sort, _this);
6407 this.mainBody.on("click", this.onClick, this);
6408 this.mainBody.on("dblclick", this.onDblClick, this);
6410 // why is this done????? = it breaks dialogs??
6411 //this.parent().el.setStyle('position', 'relative');
6415 this.footer.parentId = this.id;
6416 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6419 this.el.select('tfoot tr td').first().addClass('hide');
6424 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6427 this.store.on('load', this.onLoad, this);
6428 this.store.on('beforeload', this.onBeforeLoad, this);
6429 this.store.on('update', this.onUpdate, this);
6430 this.store.on('add', this.onAdd, this);
6431 this.store.on("clear", this.clear, this);
6433 this.el.on("contextmenu", this.onContextMenu, this);
6435 this.mainBody.on('scroll', this.onBodyScroll, this);
6437 this.cm.on("headerchange", this.onHeaderChange, this);
6439 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6443 onContextMenu : function(e, t)
6445 this.processEvent("contextmenu", e);
6448 processEvent : function(name, e)
6450 if (name != 'touchstart' ) {
6451 this.fireEvent(name, e);
6454 var t = e.getTarget();
6456 var cell = Roo.get(t);
6462 if(cell.findParent('tfoot', false, true)){
6466 if(cell.findParent('thead', false, true)){
6468 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6469 cell = Roo.get(t).findParent('th', false, true);
6471 Roo.log("failed to find th in thead?");
6472 Roo.log(e.getTarget());
6477 var cellIndex = cell.dom.cellIndex;
6479 var ename = name == 'touchstart' ? 'click' : name;
6480 this.fireEvent("header" + ename, this, cellIndex, e);
6485 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6486 cell = Roo.get(t).findParent('td', false, true);
6488 Roo.log("failed to find th in tbody?");
6489 Roo.log(e.getTarget());
6494 var row = cell.findParent('tr', false, true);
6495 var cellIndex = cell.dom.cellIndex;
6496 var rowIndex = row.dom.rowIndex - 1;
6500 this.fireEvent("row" + name, this, rowIndex, e);
6504 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6510 onMouseover : function(e, el)
6512 var cell = Roo.get(el);
6518 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6519 cell = cell.findParent('td', false, true);
6522 var row = cell.findParent('tr', false, true);
6523 var cellIndex = cell.dom.cellIndex;
6524 var rowIndex = row.dom.rowIndex - 1; // start from 0
6526 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6530 onMouseout : function(e, el)
6532 var cell = Roo.get(el);
6538 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6539 cell = cell.findParent('td', false, true);
6542 var row = cell.findParent('tr', false, true);
6543 var cellIndex = cell.dom.cellIndex;
6544 var rowIndex = row.dom.rowIndex - 1; // start from 0
6546 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6550 onClick : function(e, el)
6552 var cell = Roo.get(el);
6554 if(!cell || (!this.cellSelection && !this.rowSelection)){
6558 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6559 cell = cell.findParent('td', false, true);
6562 if(!cell || typeof(cell) == 'undefined'){
6566 var row = cell.findParent('tr', false, true);
6568 if(!row || typeof(row) == 'undefined'){
6572 var cellIndex = cell.dom.cellIndex;
6573 var rowIndex = this.getRowIndex(row);
6575 // why??? - should these not be based on SelectionModel?
6576 if(this.cellSelection){
6577 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6580 if(this.rowSelection){
6581 this.fireEvent('rowclick', this, row, rowIndex, e);
6587 onDblClick : function(e,el)
6589 var cell = Roo.get(el);
6591 if(!cell || (!this.cellSelection && !this.rowSelection)){
6595 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6596 cell = cell.findParent('td', false, true);
6599 if(!cell || typeof(cell) == 'undefined'){
6603 var row = cell.findParent('tr', false, true);
6605 if(!row || typeof(row) == 'undefined'){
6609 var cellIndex = cell.dom.cellIndex;
6610 var rowIndex = this.getRowIndex(row);
6612 if(this.cellSelection){
6613 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6616 if(this.rowSelection){
6617 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6621 sort : function(e,el)
6623 var col = Roo.get(el);
6625 if(!col.hasClass('sortable')){
6629 var sort = col.attr('sort');
6632 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6636 this.store.sortInfo = {field : sort, direction : dir};
6639 Roo.log("calling footer first");
6640 this.footer.onClick('first');
6643 this.store.load({ params : { start : 0 } });
6647 renderHeader : function()
6655 this.totalWidth = 0;
6657 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6659 var config = cm.config[i];
6663 cls : 'x-hcol-' + i,
6665 html: cm.getColumnHeader(i)
6670 if(typeof(config.sortable) != 'undefined' && config.sortable){
6672 c.html = '<i class="glyphicon"></i>' + c.html;
6675 if(typeof(config.lgHeader) != 'undefined'){
6676 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6679 if(typeof(config.mdHeader) != 'undefined'){
6680 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6683 if(typeof(config.smHeader) != 'undefined'){
6684 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6687 if(typeof(config.xsHeader) != 'undefined'){
6688 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6695 if(typeof(config.tooltip) != 'undefined'){
6696 c.tooltip = config.tooltip;
6699 if(typeof(config.colspan) != 'undefined'){
6700 c.colspan = config.colspan;
6703 if(typeof(config.hidden) != 'undefined' && config.hidden){
6704 c.style += ' display:none;';
6707 if(typeof(config.dataIndex) != 'undefined'){
6708 c.sort = config.dataIndex;
6713 if(typeof(config.align) != 'undefined' && config.align.length){
6714 c.style += ' text-align:' + config.align + ';';
6717 if(typeof(config.width) != 'undefined'){
6718 c.style += ' width:' + config.width + 'px;';
6719 this.totalWidth += config.width;
6721 this.totalWidth += 100; // assume minimum of 100 per column?
6724 if(typeof(config.cls) != 'undefined'){
6725 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6728 ['xs','sm','md','lg'].map(function(size){
6730 if(typeof(config[size]) == 'undefined'){
6734 if (!config[size]) { // 0 = hidden
6735 c.cls += ' hidden-' + size;
6739 c.cls += ' col-' + size + '-' + config[size];
6749 renderBody : function()
6759 colspan : this.cm.getColumnCount()
6769 renderFooter : function()
6779 colspan : this.cm.getColumnCount()
6793 // Roo.log('ds onload');
6798 var ds = this.store;
6800 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6801 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6802 if (_this.store.sortInfo) {
6804 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6805 e.select('i', true).addClass(['glyphicon-arrow-up']);
6808 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6809 e.select('i', true).addClass(['glyphicon-arrow-down']);
6814 var tbody = this.mainBody;
6816 if(ds.getCount() > 0){
6817 ds.data.each(function(d,rowIndex){
6818 var row = this.renderRow(cm, ds, rowIndex);
6820 tbody.createChild(row);
6824 if(row.cellObjects.length){
6825 Roo.each(row.cellObjects, function(r){
6826 _this.renderCellObject(r);
6833 var tfoot = this.el.select('tfoot', true).first();
6835 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6837 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6839 var total = this.ds.getTotalCount();
6841 if(this.footer.pageSize < total){
6842 this.mainFoot.show();
6846 Roo.each(this.el.select('tbody td', true).elements, function(e){
6847 e.on('mouseover', _this.onMouseover, _this);
6850 Roo.each(this.el.select('tbody td', true).elements, function(e){
6851 e.on('mouseout', _this.onMouseout, _this);
6853 this.fireEvent('rowsrendered', this);
6859 onUpdate : function(ds,record)
6861 this.refreshRow(record);
6865 onRemove : function(ds, record, index, isUpdate){
6866 if(isUpdate !== true){
6867 this.fireEvent("beforerowremoved", this, index, record);
6869 var bt = this.mainBody.dom;
6871 var rows = this.el.select('tbody > tr', true).elements;
6873 if(typeof(rows[index]) != 'undefined'){
6874 bt.removeChild(rows[index].dom);
6877 // if(bt.rows[index]){
6878 // bt.removeChild(bt.rows[index]);
6881 if(isUpdate !== true){
6882 //this.stripeRows(index);
6883 //this.syncRowHeights(index, index);
6885 this.fireEvent("rowremoved", this, index, record);
6889 onAdd : function(ds, records, rowIndex)
6891 //Roo.log('on Add called');
6892 // - note this does not handle multiple adding very well..
6893 var bt = this.mainBody.dom;
6894 for (var i =0 ; i < records.length;i++) {
6895 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6896 //Roo.log(records[i]);
6897 //Roo.log(this.store.getAt(rowIndex+i));
6898 this.insertRow(this.store, rowIndex + i, false);
6905 refreshRow : function(record){
6906 var ds = this.store, index;
6907 if(typeof record == 'number'){
6909 record = ds.getAt(index);
6911 index = ds.indexOf(record);
6913 this.insertRow(ds, index, true);
6915 this.onRemove(ds, record, index+1, true);
6917 //this.syncRowHeights(index, index);
6919 this.fireEvent("rowupdated", this, index, record);
6922 insertRow : function(dm, rowIndex, isUpdate){
6925 this.fireEvent("beforerowsinserted", this, rowIndex);
6927 //var s = this.getScrollState();
6928 var row = this.renderRow(this.cm, this.store, rowIndex);
6929 // insert before rowIndex..
6930 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6934 if(row.cellObjects.length){
6935 Roo.each(row.cellObjects, function(r){
6936 _this.renderCellObject(r);
6941 this.fireEvent("rowsinserted", this, rowIndex);
6942 //this.syncRowHeights(firstRow, lastRow);
6943 //this.stripeRows(firstRow);
6950 getRowDom : function(rowIndex)
6952 var rows = this.el.select('tbody > tr', true).elements;
6954 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6957 // returns the object tree for a tr..
6960 renderRow : function(cm, ds, rowIndex)
6962 var d = ds.getAt(rowIndex);
6966 cls : 'x-row-' + rowIndex,
6970 var cellObjects = [];
6972 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6973 var config = cm.config[i];
6975 var renderer = cm.getRenderer(i);
6979 if(typeof(renderer) !== 'undefined'){
6980 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6982 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6983 // and are rendered into the cells after the row is rendered - using the id for the element.
6985 if(typeof(value) === 'object'){
6995 rowIndex : rowIndex,
7000 this.fireEvent('rowclass', this, rowcfg);
7004 cls : rowcfg.rowClass + ' x-col-' + i,
7006 html: (typeof(value) === 'object') ? '' : value
7013 if(typeof(config.colspan) != 'undefined'){
7014 td.colspan = config.colspan;
7017 if(typeof(config.hidden) != 'undefined' && config.hidden){
7018 td.style += ' display:none;';
7021 if(typeof(config.align) != 'undefined' && config.align.length){
7022 td.style += ' text-align:' + config.align + ';';
7024 if(typeof(config.valign) != 'undefined' && config.valign.length){
7025 td.style += ' vertical-align:' + config.valign + ';';
7028 if(typeof(config.width) != 'undefined'){
7029 td.style += ' width:' + config.width + 'px;';
7032 if(typeof(config.cursor) != 'undefined'){
7033 td.style += ' cursor:' + config.cursor + ';';
7036 if(typeof(config.cls) != 'undefined'){
7037 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7040 ['xs','sm','md','lg'].map(function(size){
7042 if(typeof(config[size]) == 'undefined'){
7046 if (!config[size]) { // 0 = hidden
7047 td.cls += ' hidden-' + size;
7051 td.cls += ' col-' + size + '-' + config[size];
7059 row.cellObjects = cellObjects;
7067 onBeforeLoad : function()
7076 this.el.select('tbody', true).first().dom.innerHTML = '';
7079 * Show or hide a row.
7080 * @param {Number} rowIndex to show or hide
7081 * @param {Boolean} state hide
7083 setRowVisibility : function(rowIndex, state)
7085 var bt = this.mainBody.dom;
7087 var rows = this.el.select('tbody > tr', true).elements;
7089 if(typeof(rows[rowIndex]) == 'undefined'){
7092 rows[rowIndex].dom.style.display = state ? '' : 'none';
7096 getSelectionModel : function(){
7098 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7100 return this.selModel;
7103 * Render the Roo.bootstrap object from renderder
7105 renderCellObject : function(r)
7109 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7111 var t = r.cfg.render(r.container);
7114 Roo.each(r.cfg.cn, function(c){
7116 container: t.getChildContainer(),
7119 _this.renderCellObject(child);
7124 getRowIndex : function(row)
7128 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7139 * Returns the grid's underlying element = used by panel.Grid
7140 * @return {Element} The element
7142 getGridEl : function(){
7146 * Forces a resize - used by panel.Grid
7147 * @return {Element} The element
7149 autoSize : function()
7151 //var ctr = Roo.get(this.container.dom.parentElement);
7152 var ctr = Roo.get(this.el.dom);
7154 var thd = this.getGridEl().select('thead',true).first();
7155 var tbd = this.getGridEl().select('tbody', true).first();
7156 var tfd = this.getGridEl().select('tfoot', true).first();
7158 var cw = ctr.getWidth();
7162 tbd.setSize(ctr.getWidth(),
7163 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7165 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7168 cw = Math.max(cw, this.totalWidth);
7169 this.getGridEl().select('tr',true).setWidth(cw);
7170 // resize 'expandable coloumn?
7172 return; // we doe not have a view in this design..
7175 onBodyScroll: function()
7177 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7179 this.mainHead.setStyle({
7180 'position' : 'relative',
7181 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7187 var scrollHeight = this.mainBody.dom.scrollHeight;
7189 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7191 var height = this.mainBody.getHeight();
7193 if(scrollHeight - height == scrollTop) {
7195 var total = this.ds.getTotalCount();
7197 if(this.footer.cursor + this.footer.pageSize < total){
7199 this.footer.ds.load({
7201 start : this.footer.cursor + this.footer.pageSize,
7202 limit : this.footer.pageSize
7212 onHeaderChange : function()
7214 var header = this.renderHeader();
7215 var table = this.el.select('table', true).first();
7217 this.mainHead.remove();
7218 this.mainHead = table.createChild(header, this.mainBody, false);
7221 onHiddenChange : function(colModel, colIndex, hidden)
7223 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7224 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7226 this.CSS.updateRule(thSelector, "display", "");
7227 this.CSS.updateRule(tdSelector, "display", "");
7230 this.CSS.updateRule(thSelector, "display", "none");
7231 this.CSS.updateRule(tdSelector, "display", "none");
7234 this.onHeaderChange();
7238 setColumnWidth: function(col_index, width)
7240 // width = "md-2 xs-2..."
7241 if(!this.colModel.config[col_index]) {
7245 var w = width.split(" ");
7247 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7249 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7252 for(var j = 0; j < w.length; j++) {
7258 var size_cls = w[j].split("-");
7260 if(!Number.isInteger(size_cls[1] * 1)) {
7264 if(!this.colModel.config[col_index][size_cls[0]]) {
7268 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7272 h_row[0].classList.replace(
7273 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7274 "col-"+size_cls[0]+"-"+size_cls[1]
7277 for(var i = 0; i < rows.length; i++) {
7279 var size_cls = w[j].split("-");
7281 if(!Number.isInteger(size_cls[1] * 1)) {
7285 if(!this.colModel.config[col_index][size_cls[0]]) {
7289 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7293 rows[i].classList.replace(
7294 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7295 "col-"+size_cls[0]+"-"+size_cls[1]
7299 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7314 * @class Roo.bootstrap.TableCell
7315 * @extends Roo.bootstrap.Component
7316 * Bootstrap TableCell class
7317 * @cfg {String} html cell contain text
7318 * @cfg {String} cls cell class
7319 * @cfg {String} tag cell tag (td|th) default td
7320 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7321 * @cfg {String} align Aligns the content in a cell
7322 * @cfg {String} axis Categorizes cells
7323 * @cfg {String} bgcolor Specifies the background color of a cell
7324 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7325 * @cfg {Number} colspan Specifies the number of columns a cell should span
7326 * @cfg {String} headers Specifies one or more header cells a cell is related to
7327 * @cfg {Number} height Sets the height of a cell
7328 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7329 * @cfg {Number} rowspan Sets the number of rows a cell should span
7330 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7331 * @cfg {String} valign Vertical aligns the content in a cell
7332 * @cfg {Number} width Specifies the width of a cell
7335 * Create a new TableCell
7336 * @param {Object} config The config object
7339 Roo.bootstrap.TableCell = function(config){
7340 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7343 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7363 getAutoCreate : function(){
7364 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7384 cfg.align=this.align
7390 cfg.bgcolor=this.bgcolor
7393 cfg.charoff=this.charoff
7396 cfg.colspan=this.colspan
7399 cfg.headers=this.headers
7402 cfg.height=this.height
7405 cfg.nowrap=this.nowrap
7408 cfg.rowspan=this.rowspan
7411 cfg.scope=this.scope
7414 cfg.valign=this.valign
7417 cfg.width=this.width
7436 * @class Roo.bootstrap.TableRow
7437 * @extends Roo.bootstrap.Component
7438 * Bootstrap TableRow class
7439 * @cfg {String} cls row class
7440 * @cfg {String} align Aligns the content in a table row
7441 * @cfg {String} bgcolor Specifies a background color for a table row
7442 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7443 * @cfg {String} valign Vertical aligns the content in a table row
7446 * Create a new TableRow
7447 * @param {Object} config The config object
7450 Roo.bootstrap.TableRow = function(config){
7451 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7454 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7462 getAutoCreate : function(){
7463 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7473 cfg.align = this.align;
7476 cfg.bgcolor = this.bgcolor;
7479 cfg.charoff = this.charoff;
7482 cfg.valign = this.valign;
7500 * @class Roo.bootstrap.TableBody
7501 * @extends Roo.bootstrap.Component
7502 * Bootstrap TableBody class
7503 * @cfg {String} cls element class
7504 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7505 * @cfg {String} align Aligns the content inside the element
7506 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7507 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7510 * Create a new TableBody
7511 * @param {Object} config The config object
7514 Roo.bootstrap.TableBody = function(config){
7515 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7518 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7526 getAutoCreate : function(){
7527 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7541 cfg.align = this.align;
7544 cfg.charoff = this.charoff;
7547 cfg.valign = this.valign;
7554 // initEvents : function()
7561 // this.store = Roo.factory(this.store, Roo.data);
7562 // this.store.on('load', this.onLoad, this);
7564 // this.store.load();
7568 // onLoad: function ()
7570 // this.fireEvent('load', this);
7580 * Ext JS Library 1.1.1
7581 * Copyright(c) 2006-2007, Ext JS, LLC.
7583 * Originally Released Under LGPL - original licence link has changed is not relivant.
7586 * <script type="text/javascript">
7589 // as we use this in bootstrap.
7590 Roo.namespace('Roo.form');
7592 * @class Roo.form.Action
7593 * Internal Class used to handle form actions
7595 * @param {Roo.form.BasicForm} el The form element or its id
7596 * @param {Object} config Configuration options
7601 // define the action interface
7602 Roo.form.Action = function(form, options){
7604 this.options = options || {};
7607 * Client Validation Failed
7610 Roo.form.Action.CLIENT_INVALID = 'client';
7612 * Server Validation Failed
7615 Roo.form.Action.SERVER_INVALID = 'server';
7617 * Connect to Server Failed
7620 Roo.form.Action.CONNECT_FAILURE = 'connect';
7622 * Reading Data from Server Failed
7625 Roo.form.Action.LOAD_FAILURE = 'load';
7627 Roo.form.Action.prototype = {
7629 failureType : undefined,
7630 response : undefined,
7634 run : function(options){
7639 success : function(response){
7644 handleResponse : function(response){
7648 // default connection failure
7649 failure : function(response){
7651 this.response = response;
7652 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7653 this.form.afterAction(this, false);
7656 processResponse : function(response){
7657 this.response = response;
7658 if(!response.responseText){
7661 this.result = this.handleResponse(response);
7665 // utility functions used internally
7666 getUrl : function(appendParams){
7667 var url = this.options.url || this.form.url || this.form.el.dom.action;
7669 var p = this.getParams();
7671 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7677 getMethod : function(){
7678 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7681 getParams : function(){
7682 var bp = this.form.baseParams;
7683 var p = this.options.params;
7685 if(typeof p == "object"){
7686 p = Roo.urlEncode(Roo.applyIf(p, bp));
7687 }else if(typeof p == 'string' && bp){
7688 p += '&' + Roo.urlEncode(bp);
7691 p = Roo.urlEncode(bp);
7696 createCallback : function(){
7698 success: this.success,
7699 failure: this.failure,
7701 timeout: (this.form.timeout*1000),
7702 upload: this.form.fileUpload ? this.success : undefined
7707 Roo.form.Action.Submit = function(form, options){
7708 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7711 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7714 haveProgress : false,
7715 uploadComplete : false,
7717 // uploadProgress indicator.
7718 uploadProgress : function()
7720 if (!this.form.progressUrl) {
7724 if (!this.haveProgress) {
7725 Roo.MessageBox.progress("Uploading", "Uploading");
7727 if (this.uploadComplete) {
7728 Roo.MessageBox.hide();
7732 this.haveProgress = true;
7734 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7736 var c = new Roo.data.Connection();
7738 url : this.form.progressUrl,
7743 success : function(req){
7744 //console.log(data);
7748 rdata = Roo.decode(req.responseText)
7750 Roo.log("Invalid data from server..");
7754 if (!rdata || !rdata.success) {
7756 Roo.MessageBox.alert(Roo.encode(rdata));
7759 var data = rdata.data;
7761 if (this.uploadComplete) {
7762 Roo.MessageBox.hide();
7767 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7768 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7771 this.uploadProgress.defer(2000,this);
7774 failure: function(data) {
7775 Roo.log('progress url failed ');
7786 // run get Values on the form, so it syncs any secondary forms.
7787 this.form.getValues();
7789 var o = this.options;
7790 var method = this.getMethod();
7791 var isPost = method == 'POST';
7792 if(o.clientValidation === false || this.form.isValid()){
7794 if (this.form.progressUrl) {
7795 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7796 (new Date() * 1) + '' + Math.random());
7801 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7802 form:this.form.el.dom,
7803 url:this.getUrl(!isPost),
7805 params:isPost ? this.getParams() : null,
7806 isUpload: this.form.fileUpload
7809 this.uploadProgress();
7811 }else if (o.clientValidation !== false){ // client validation failed
7812 this.failureType = Roo.form.Action.CLIENT_INVALID;
7813 this.form.afterAction(this, false);
7817 success : function(response)
7819 this.uploadComplete= true;
7820 if (this.haveProgress) {
7821 Roo.MessageBox.hide();
7825 var result = this.processResponse(response);
7826 if(result === true || result.success){
7827 this.form.afterAction(this, true);
7831 this.form.markInvalid(result.errors);
7832 this.failureType = Roo.form.Action.SERVER_INVALID;
7834 this.form.afterAction(this, false);
7836 failure : function(response)
7838 this.uploadComplete= true;
7839 if (this.haveProgress) {
7840 Roo.MessageBox.hide();
7843 this.response = response;
7844 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7845 this.form.afterAction(this, false);
7848 handleResponse : function(response){
7849 if(this.form.errorReader){
7850 var rs = this.form.errorReader.read(response);
7853 for(var i = 0, len = rs.records.length; i < len; i++) {
7854 var r = rs.records[i];
7858 if(errors.length < 1){
7862 success : rs.success,
7868 ret = Roo.decode(response.responseText);
7872 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7882 Roo.form.Action.Load = function(form, options){
7883 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7884 this.reader = this.form.reader;
7887 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7892 Roo.Ajax.request(Roo.apply(
7893 this.createCallback(), {
7894 method:this.getMethod(),
7895 url:this.getUrl(false),
7896 params:this.getParams()
7900 success : function(response){
7902 var result = this.processResponse(response);
7903 if(result === true || !result.success || !result.data){
7904 this.failureType = Roo.form.Action.LOAD_FAILURE;
7905 this.form.afterAction(this, false);
7908 this.form.clearInvalid();
7909 this.form.setValues(result.data);
7910 this.form.afterAction(this, true);
7913 handleResponse : function(response){
7914 if(this.form.reader){
7915 var rs = this.form.reader.read(response);
7916 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7918 success : rs.success,
7922 return Roo.decode(response.responseText);
7926 Roo.form.Action.ACTION_TYPES = {
7927 'load' : Roo.form.Action.Load,
7928 'submit' : Roo.form.Action.Submit
7937 * @class Roo.bootstrap.Form
7938 * @extends Roo.bootstrap.Component
7939 * Bootstrap Form class
7940 * @cfg {String} method GET | POST (default POST)
7941 * @cfg {String} labelAlign top | left (default top)
7942 * @cfg {String} align left | right - for navbars
7943 * @cfg {Boolean} loadMask load mask when submit (default true)
7948 * @param {Object} config The config object
7952 Roo.bootstrap.Form = function(config){
7954 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7956 Roo.bootstrap.Form.popover.apply();
7960 * @event clientvalidation
7961 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7962 * @param {Form} this
7963 * @param {Boolean} valid true if the form has passed client-side validation
7965 clientvalidation: true,
7967 * @event beforeaction
7968 * Fires before any action is performed. Return false to cancel the action.
7969 * @param {Form} this
7970 * @param {Action} action The action to be performed
7974 * @event actionfailed
7975 * Fires when an action fails.
7976 * @param {Form} this
7977 * @param {Action} action The action that failed
7979 actionfailed : true,
7981 * @event actioncomplete
7982 * Fires when an action is completed.
7983 * @param {Form} this
7984 * @param {Action} action The action that completed
7986 actioncomplete : true
7990 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7993 * @cfg {String} method
7994 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7999 * The URL to use for form actions if one isn't supplied in the action options.
8002 * @cfg {Boolean} fileUpload
8003 * Set to true if this form is a file upload.
8007 * @cfg {Object} baseParams
8008 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8012 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8016 * @cfg {Sting} align (left|right) for navbar forms
8021 activeAction : null,
8024 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8025 * element by passing it or its id or mask the form itself by passing in true.
8028 waitMsgTarget : false,
8033 * @cfg {Boolean} errorMask (true|false) default false
8038 * @cfg {Number} maskOffset Default 100
8043 * @cfg {Boolean} maskBody
8047 getAutoCreate : function(){
8051 method : this.method || 'POST',
8052 id : this.id || Roo.id(),
8055 if (this.parent().xtype.match(/^Nav/)) {
8056 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8060 if (this.labelAlign == 'left' ) {
8061 cfg.cls += ' form-horizontal';
8067 initEvents : function()
8069 this.el.on('submit', this.onSubmit, this);
8070 // this was added as random key presses on the form where triggering form submit.
8071 this.el.on('keypress', function(e) {
8072 if (e.getCharCode() != 13) {
8075 // we might need to allow it for textareas.. and some other items.
8076 // check e.getTarget().
8078 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8082 Roo.log("keypress blocked");
8090 onSubmit : function(e){
8095 * Returns true if client-side validation on the form is successful.
8098 isValid : function(){
8099 var items = this.getItems();
8103 items.each(function(f){
8109 Roo.log('invalid field: ' + f.name);
8113 if(!target && f.el.isVisible(true)){
8119 if(this.errorMask && !valid){
8120 Roo.bootstrap.Form.popover.mask(this, target);
8127 * Returns true if any fields in this form have changed since their original load.
8130 isDirty : function(){
8132 var items = this.getItems();
8133 items.each(function(f){
8143 * Performs a predefined action (submit or load) or custom actions you define on this form.
8144 * @param {String} actionName The name of the action type
8145 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8146 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8147 * accept other config options):
8149 Property Type Description
8150 ---------------- --------------- ----------------------------------------------------------------------------------
8151 url String The url for the action (defaults to the form's url)
8152 method String The form method to use (defaults to the form's method, or POST if not defined)
8153 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8154 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8155 validate the form on the client (defaults to false)
8157 * @return {BasicForm} this
8159 doAction : function(action, options){
8160 if(typeof action == 'string'){
8161 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8163 if(this.fireEvent('beforeaction', this, action) !== false){
8164 this.beforeAction(action);
8165 action.run.defer(100, action);
8171 beforeAction : function(action){
8172 var o = action.options;
8177 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8179 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8182 // not really supported yet.. ??
8184 //if(this.waitMsgTarget === true){
8185 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8186 //}else if(this.waitMsgTarget){
8187 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8188 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8190 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8196 afterAction : function(action, success){
8197 this.activeAction = null;
8198 var o = action.options;
8203 Roo.get(document.body).unmask();
8209 //if(this.waitMsgTarget === true){
8210 // this.el.unmask();
8211 //}else if(this.waitMsgTarget){
8212 // this.waitMsgTarget.unmask();
8214 // Roo.MessageBox.updateProgress(1);
8215 // Roo.MessageBox.hide();
8222 Roo.callback(o.success, o.scope, [this, action]);
8223 this.fireEvent('actioncomplete', this, action);
8227 // failure condition..
8228 // we have a scenario where updates need confirming.
8229 // eg. if a locking scenario exists..
8230 // we look for { errors : { needs_confirm : true }} in the response.
8232 (typeof(action.result) != 'undefined') &&
8233 (typeof(action.result.errors) != 'undefined') &&
8234 (typeof(action.result.errors.needs_confirm) != 'undefined')
8237 Roo.log("not supported yet");
8240 Roo.MessageBox.confirm(
8241 "Change requires confirmation",
8242 action.result.errorMsg,
8247 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8257 Roo.callback(o.failure, o.scope, [this, action]);
8258 // show an error message if no failed handler is set..
8259 if (!this.hasListener('actionfailed')) {
8260 Roo.log("need to add dialog support");
8262 Roo.MessageBox.alert("Error",
8263 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8264 action.result.errorMsg :
8265 "Saving Failed, please check your entries or try again"
8270 this.fireEvent('actionfailed', this, action);
8275 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8276 * @param {String} id The value to search for
8279 findField : function(id){
8280 var items = this.getItems();
8281 var field = items.get(id);
8283 items.each(function(f){
8284 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8291 return field || null;
8294 * Mark fields in this form invalid in bulk.
8295 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8296 * @return {BasicForm} this
8298 markInvalid : function(errors){
8299 if(errors instanceof Array){
8300 for(var i = 0, len = errors.length; i < len; i++){
8301 var fieldError = errors[i];
8302 var f = this.findField(fieldError.id);
8304 f.markInvalid(fieldError.msg);
8310 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8311 field.markInvalid(errors[id]);
8315 //Roo.each(this.childForms || [], function (f) {
8316 // f.markInvalid(errors);
8323 * Set values for fields in this form in bulk.
8324 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8325 * @return {BasicForm} this
8327 setValues : function(values){
8328 if(values instanceof Array){ // array of objects
8329 for(var i = 0, len = values.length; i < len; i++){
8331 var f = this.findField(v.id);
8333 f.setValue(v.value);
8334 if(this.trackResetOnLoad){
8335 f.originalValue = f.getValue();
8339 }else{ // object hash
8342 if(typeof values[id] != 'function' && (field = this.findField(id))){
8344 if (field.setFromData &&
8346 field.displayField &&
8347 // combos' with local stores can
8348 // be queried via setValue()
8349 // to set their value..
8350 (field.store && !field.store.isLocal)
8354 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8355 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8356 field.setFromData(sd);
8358 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8360 field.setFromData(values);
8363 field.setValue(values[id]);
8367 if(this.trackResetOnLoad){
8368 field.originalValue = field.getValue();
8374 //Roo.each(this.childForms || [], function (f) {
8375 // f.setValues(values);
8382 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8383 * they are returned as an array.
8384 * @param {Boolean} asString
8387 getValues : function(asString){
8388 //if (this.childForms) {
8389 // copy values from the child forms
8390 // Roo.each(this.childForms, function (f) {
8391 // this.setValues(f.getValues());
8397 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8398 if(asString === true){
8401 return Roo.urlDecode(fs);
8405 * Returns the fields in this form as an object with key/value pairs.
8406 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8409 getFieldValues : function(with_hidden)
8411 var items = this.getItems();
8413 items.each(function(f){
8419 var v = f.getValue();
8421 if (f.inputType =='radio') {
8422 if (typeof(ret[f.getName()]) == 'undefined') {
8423 ret[f.getName()] = ''; // empty..
8426 if (!f.el.dom.checked) {
8434 if(f.xtype == 'MoneyField'){
8435 ret[f.currencyName] = f.getCurrency();
8438 // not sure if this supported any more..
8439 if ((typeof(v) == 'object') && f.getRawValue) {
8440 v = f.getRawValue() ; // dates..
8442 // combo boxes where name != hiddenName...
8443 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8444 ret[f.name] = f.getRawValue();
8446 ret[f.getName()] = v;
8453 * Clears all invalid messages in this form.
8454 * @return {BasicForm} this
8456 clearInvalid : function(){
8457 var items = this.getItems();
8459 items.each(function(f){
8468 * @return {BasicForm} this
8471 var items = this.getItems();
8472 items.each(function(f){
8476 Roo.each(this.childForms || [], function (f) {
8484 getItems : function()
8486 var r=new Roo.util.MixedCollection(false, function(o){
8487 return o.id || (o.id = Roo.id());
8489 var iter = function(el) {
8496 Roo.each(el.items,function(e) {
8505 hideFields : function(items)
8507 Roo.each(items, function(i){
8509 var f = this.findField(i);
8520 showFields : function(items)
8522 Roo.each(items, function(i){
8524 var f = this.findField(i);
8537 Roo.apply(Roo.bootstrap.Form, {
8564 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8565 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8566 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8567 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8570 this.maskEl.top.enableDisplayMode("block");
8571 this.maskEl.left.enableDisplayMode("block");
8572 this.maskEl.bottom.enableDisplayMode("block");
8573 this.maskEl.right.enableDisplayMode("block");
8575 this.toolTip = new Roo.bootstrap.Tooltip({
8576 cls : 'roo-form-error-popover',
8578 'left' : ['r-l', [-2,0], 'right'],
8579 'right' : ['l-r', [2,0], 'left'],
8580 'bottom' : ['tl-bl', [0,2], 'top'],
8581 'top' : [ 'bl-tl', [0,-2], 'bottom']
8585 this.toolTip.render(Roo.get(document.body));
8587 this.toolTip.el.enableDisplayMode("block");
8589 Roo.get(document.body).on('click', function(){
8593 Roo.get(document.body).on('touchstart', function(){
8597 this.isApplied = true
8600 mask : function(form, target)
8604 this.target = target;
8606 if(!this.form.errorMask || !target.el){
8610 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8612 Roo.log(scrollable);
8614 var ot = this.target.el.calcOffsetsTo(scrollable);
8616 var scrollTo = ot[1] - this.form.maskOffset;
8618 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8620 scrollable.scrollTo('top', scrollTo);
8622 var box = this.target.el.getBox();
8624 var zIndex = Roo.bootstrap.Modal.zIndex++;
8627 this.maskEl.top.setStyle('position', 'absolute');
8628 this.maskEl.top.setStyle('z-index', zIndex);
8629 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8630 this.maskEl.top.setLeft(0);
8631 this.maskEl.top.setTop(0);
8632 this.maskEl.top.show();
8634 this.maskEl.left.setStyle('position', 'absolute');
8635 this.maskEl.left.setStyle('z-index', zIndex);
8636 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8637 this.maskEl.left.setLeft(0);
8638 this.maskEl.left.setTop(box.y - this.padding);
8639 this.maskEl.left.show();
8641 this.maskEl.bottom.setStyle('position', 'absolute');
8642 this.maskEl.bottom.setStyle('z-index', zIndex);
8643 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8644 this.maskEl.bottom.setLeft(0);
8645 this.maskEl.bottom.setTop(box.bottom + this.padding);
8646 this.maskEl.bottom.show();
8648 this.maskEl.right.setStyle('position', 'absolute');
8649 this.maskEl.right.setStyle('z-index', zIndex);
8650 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8651 this.maskEl.right.setLeft(box.right + this.padding);
8652 this.maskEl.right.setTop(box.y - this.padding);
8653 this.maskEl.right.show();
8655 this.toolTip.bindEl = this.target.el;
8657 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8659 var tip = this.target.blankText;
8661 if(this.target.getValue() !== '' ) {
8663 if (this.target.invalidText.length) {
8664 tip = this.target.invalidText;
8665 } else if (this.target.regexText.length){
8666 tip = this.target.regexText;
8670 this.toolTip.show(tip);
8672 this.intervalID = window.setInterval(function() {
8673 Roo.bootstrap.Form.popover.unmask();
8676 window.onwheel = function(){ return false;};
8678 (function(){ this.isMasked = true; }).defer(500, this);
8684 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8688 this.maskEl.top.setStyle('position', 'absolute');
8689 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8690 this.maskEl.top.hide();
8692 this.maskEl.left.setStyle('position', 'absolute');
8693 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8694 this.maskEl.left.hide();
8696 this.maskEl.bottom.setStyle('position', 'absolute');
8697 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8698 this.maskEl.bottom.hide();
8700 this.maskEl.right.setStyle('position', 'absolute');
8701 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8702 this.maskEl.right.hide();
8704 this.toolTip.hide();
8706 this.toolTip.el.hide();
8708 window.onwheel = function(){ return true;};
8710 if(this.intervalID){
8711 window.clearInterval(this.intervalID);
8712 this.intervalID = false;
8715 this.isMasked = false;
8725 * Ext JS Library 1.1.1
8726 * Copyright(c) 2006-2007, Ext JS, LLC.
8728 * Originally Released Under LGPL - original licence link has changed is not relivant.
8731 * <script type="text/javascript">
8734 * @class Roo.form.VTypes
8735 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8738 Roo.form.VTypes = function(){
8739 // closure these in so they are only created once.
8740 var alpha = /^[a-zA-Z_]+$/;
8741 var alphanum = /^[a-zA-Z0-9_]+$/;
8742 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8743 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8745 // All these messages and functions are configurable
8748 * The function used to validate email addresses
8749 * @param {String} value The email address
8751 'email' : function(v){
8752 return email.test(v);
8755 * The error text to display when the email validation function returns false
8758 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8760 * The keystroke filter mask to be applied on email input
8763 'emailMask' : /[a-z0-9_\.\-@]/i,
8766 * The function used to validate URLs
8767 * @param {String} value The URL
8769 'url' : function(v){
8773 * The error text to display when the url validation function returns false
8776 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8779 * The function used to validate alpha values
8780 * @param {String} value The value
8782 'alpha' : function(v){
8783 return alpha.test(v);
8786 * The error text to display when the alpha validation function returns false
8789 'alphaText' : 'This field should only contain letters and _',
8791 * The keystroke filter mask to be applied on alpha input
8794 'alphaMask' : /[a-z_]/i,
8797 * The function used to validate alphanumeric values
8798 * @param {String} value The value
8800 'alphanum' : function(v){
8801 return alphanum.test(v);
8804 * The error text to display when the alphanumeric validation function returns false
8807 'alphanumText' : 'This field should only contain letters, numbers and _',
8809 * The keystroke filter mask to be applied on alphanumeric input
8812 'alphanumMask' : /[a-z0-9_]/i
8822 * @class Roo.bootstrap.Input
8823 * @extends Roo.bootstrap.Component
8824 * Bootstrap Input class
8825 * @cfg {Boolean} disabled is it disabled
8826 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8827 * @cfg {String} name name of the input
8828 * @cfg {string} fieldLabel - the label associated
8829 * @cfg {string} placeholder - placeholder to put in text.
8830 * @cfg {string} before - input group add on before
8831 * @cfg {string} after - input group add on after
8832 * @cfg {string} size - (lg|sm) or leave empty..
8833 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8834 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8835 * @cfg {Number} md colspan out of 12 for computer-sized screens
8836 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8837 * @cfg {string} value default value of the input
8838 * @cfg {Number} labelWidth set the width of label
8839 * @cfg {Number} labellg set the width of label (1-12)
8840 * @cfg {Number} labelmd set the width of label (1-12)
8841 * @cfg {Number} labelsm set the width of label (1-12)
8842 * @cfg {Number} labelxs set the width of label (1-12)
8843 * @cfg {String} labelAlign (top|left)
8844 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8845 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8846 * @cfg {String} indicatorpos (left|right) default left
8847 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8848 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8850 * @cfg {String} align (left|center|right) Default left
8851 * @cfg {Boolean} forceFeedback (true|false) Default false
8854 * Create a new Input
8855 * @param {Object} config The config object
8858 Roo.bootstrap.Input = function(config){
8860 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8865 * Fires when this field receives input focus.
8866 * @param {Roo.form.Field} this
8871 * Fires when this field loses input focus.
8872 * @param {Roo.form.Field} this
8877 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8878 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8879 * @param {Roo.form.Field} this
8880 * @param {Roo.EventObject} e The event object
8885 * Fires just before the field blurs if the field value has changed.
8886 * @param {Roo.form.Field} this
8887 * @param {Mixed} newValue The new value
8888 * @param {Mixed} oldValue The original value
8893 * Fires after the field has been marked as invalid.
8894 * @param {Roo.form.Field} this
8895 * @param {String} msg The validation message
8900 * Fires after the field has been validated with no errors.
8901 * @param {Roo.form.Field} this
8906 * Fires after the key up
8907 * @param {Roo.form.Field} this
8908 * @param {Roo.EventObject} e The event Object
8914 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8916 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8917 automatic validation (defaults to "keyup").
8919 validationEvent : "keyup",
8921 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8923 validateOnBlur : true,
8925 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8927 validationDelay : 250,
8929 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8931 focusClass : "x-form-focus", // not needed???
8935 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8937 invalidClass : "has-warning",
8940 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8942 validClass : "has-success",
8945 * @cfg {Boolean} hasFeedback (true|false) default true
8950 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8952 invalidFeedbackClass : "glyphicon-warning-sign",
8955 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8957 validFeedbackClass : "glyphicon-ok",
8960 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8962 selectOnFocus : false,
8965 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8969 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8974 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8976 disableKeyFilter : false,
8979 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8983 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8987 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8989 blankText : "Please complete this mandatory field",
8992 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8996 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8998 maxLength : Number.MAX_VALUE,
9000 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9002 minLengthText : "The minimum length for this field is {0}",
9004 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9006 maxLengthText : "The maximum length for this field is {0}",
9010 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9011 * If available, this function will be called only after the basic validators all return true, and will be passed the
9012 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9016 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9017 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9018 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
9022 * @cfg {String} regexText -- Depricated - use Invalid Text
9027 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9033 autocomplete: false,
9052 formatedValue : false,
9053 forceFeedback : false,
9055 indicatorpos : 'left',
9065 parentLabelAlign : function()
9068 while (parent.parent()) {
9069 parent = parent.parent();
9070 if (typeof(parent.labelAlign) !='undefined') {
9071 return parent.labelAlign;
9078 getAutoCreate : function()
9080 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9086 if(this.inputType != 'hidden'){
9087 cfg.cls = 'form-group' //input-group
9093 type : this.inputType,
9095 cls : 'form-control',
9096 placeholder : this.placeholder || '',
9097 autocomplete : this.autocomplete || 'new-password'
9100 if(this.capture.length){
9101 input.capture = this.capture;
9104 if(this.accept.length){
9105 input.accept = this.accept + "/*";
9109 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9112 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9113 input.maxLength = this.maxLength;
9116 if (this.disabled) {
9117 input.disabled=true;
9120 if (this.readOnly) {
9121 input.readonly=true;
9125 input.name = this.name;
9129 input.cls += ' input-' + this.size;
9133 ['xs','sm','md','lg'].map(function(size){
9134 if (settings[size]) {
9135 cfg.cls += ' col-' + size + '-' + settings[size];
9139 var inputblock = input;
9143 cls: 'glyphicon form-control-feedback'
9146 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9149 cls : 'has-feedback',
9157 if (this.before || this.after) {
9160 cls : 'input-group',
9164 if (this.before && typeof(this.before) == 'string') {
9166 inputblock.cn.push({
9168 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9172 if (this.before && typeof(this.before) == 'object') {
9173 this.before = Roo.factory(this.before);
9175 inputblock.cn.push({
9177 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9178 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9182 inputblock.cn.push(input);
9184 if (this.after && typeof(this.after) == 'string') {
9185 inputblock.cn.push({
9187 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9191 if (this.after && typeof(this.after) == 'object') {
9192 this.after = Roo.factory(this.after);
9194 inputblock.cn.push({
9196 cls : 'roo-input-after input-group-append input-group-text input-group-' +
9197 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9201 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9202 inputblock.cls += ' has-feedback';
9203 inputblock.cn.push(feedback);
9208 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9209 tooltip : 'This field is required'
9211 if (Roo.bootstrap.version == 4) {
9214 style : 'display-none'
9217 if (align ==='left' && this.fieldLabel.length) {
9219 cfg.cls += ' roo-form-group-label-left row';
9226 cls : 'control-label col-form-label',
9227 html : this.fieldLabel
9238 var labelCfg = cfg.cn[1];
9239 var contentCfg = cfg.cn[2];
9241 if(this.indicatorpos == 'right'){
9246 cls : 'control-label col-form-label',
9250 html : this.fieldLabel
9264 labelCfg = cfg.cn[0];
9265 contentCfg = cfg.cn[1];
9269 if(this.labelWidth > 12){
9270 labelCfg.style = "width: " + this.labelWidth + 'px';
9273 if(this.labelWidth < 13 && this.labelmd == 0){
9274 this.labelmd = this.labelWidth;
9277 if(this.labellg > 0){
9278 labelCfg.cls += ' col-lg-' + this.labellg;
9279 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9282 if(this.labelmd > 0){
9283 labelCfg.cls += ' col-md-' + this.labelmd;
9284 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9287 if(this.labelsm > 0){
9288 labelCfg.cls += ' col-sm-' + this.labelsm;
9289 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9292 if(this.labelxs > 0){
9293 labelCfg.cls += ' col-xs-' + this.labelxs;
9294 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9298 } else if ( this.fieldLabel.length) {
9303 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9304 tooltip : 'This field is required'
9308 //cls : 'input-group-addon',
9309 html : this.fieldLabel
9317 if(this.indicatorpos == 'right'){
9322 //cls : 'input-group-addon',
9323 html : this.fieldLabel
9328 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9329 tooltip : 'This field is required'
9349 if (this.parentType === 'Navbar' && this.parent().bar) {
9350 cfg.cls += ' navbar-form';
9353 if (this.parentType === 'NavGroup') {
9354 cfg.cls += ' navbar-form';
9362 * return the real input element.
9364 inputEl: function ()
9366 return this.el.select('input.form-control',true).first();
9369 tooltipEl : function()
9371 return this.inputEl();
9374 indicatorEl : function()
9376 if (Roo.bootstrap.version == 4) {
9377 return false; // not enabled in v4 yet.
9380 var indicator = this.el.select('i.roo-required-indicator',true).first();
9390 setDisabled : function(v)
9392 var i = this.inputEl().dom;
9394 i.removeAttribute('disabled');
9398 i.setAttribute('disabled','true');
9400 initEvents : function()
9403 this.inputEl().on("keydown" , this.fireKey, this);
9404 this.inputEl().on("focus", this.onFocus, this);
9405 this.inputEl().on("blur", this.onBlur, this);
9407 this.inputEl().relayEvent('keyup', this);
9409 this.indicator = this.indicatorEl();
9412 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9415 // reference to original value for reset
9416 this.originalValue = this.getValue();
9417 //Roo.form.TextField.superclass.initEvents.call(this);
9418 if(this.validationEvent == 'keyup'){
9419 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9420 this.inputEl().on('keyup', this.filterValidation, this);
9422 else if(this.validationEvent !== false){
9423 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9426 if(this.selectOnFocus){
9427 this.on("focus", this.preFocus, this);
9430 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9431 this.inputEl().on("keypress", this.filterKeys, this);
9433 this.inputEl().relayEvent('keypress', this);
9436 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9437 this.el.on("click", this.autoSize, this);
9440 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9441 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9444 if (typeof(this.before) == 'object') {
9445 this.before.render(this.el.select('.roo-input-before',true).first());
9447 if (typeof(this.after) == 'object') {
9448 this.after.render(this.el.select('.roo-input-after',true).first());
9451 this.inputEl().on('change', this.onChange, this);
9454 filterValidation : function(e){
9455 if(!e.isNavKeyPress()){
9456 this.validationTask.delay(this.validationDelay);
9460 * Validates the field value
9461 * @return {Boolean} True if the value is valid, else false
9463 validate : function(){
9464 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9465 if(this.disabled || this.validateValue(this.getRawValue())){
9476 * Validates a value according to the field's validation rules and marks the field as invalid
9477 * if the validation fails
9478 * @param {Mixed} value The value to validate
9479 * @return {Boolean} True if the value is valid, else false
9481 validateValue : function(value)
9483 if(this.getVisibilityEl().hasClass('hidden')){
9487 if(value.length < 1) { // if it's blank
9488 if(this.allowBlank){
9494 if(value.length < this.minLength){
9497 if(value.length > this.maxLength){
9501 var vt = Roo.form.VTypes;
9502 if(!vt[this.vtype](value, this)){
9506 if(typeof this.validator == "function"){
9507 var msg = this.validator(value);
9511 if (typeof(msg) == 'string') {
9512 this.invalidText = msg;
9516 if(this.regex && !this.regex.test(value)){
9524 fireKey : function(e){
9525 //Roo.log('field ' + e.getKey());
9526 if(e.isNavKeyPress()){
9527 this.fireEvent("specialkey", this, e);
9530 focus : function (selectText){
9532 this.inputEl().focus();
9533 if(selectText === true){
9534 this.inputEl().dom.select();
9540 onFocus : function(){
9541 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9542 // this.el.addClass(this.focusClass);
9545 this.hasFocus = true;
9546 this.startValue = this.getValue();
9547 this.fireEvent("focus", this);
9551 beforeBlur : Roo.emptyFn,
9555 onBlur : function(){
9557 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9558 //this.el.removeClass(this.focusClass);
9560 this.hasFocus = false;
9561 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9564 var v = this.getValue();
9565 if(String(v) !== String(this.startValue)){
9566 this.fireEvent('change', this, v, this.startValue);
9568 this.fireEvent("blur", this);
9571 onChange : function(e)
9573 var v = this.getValue();
9574 if(String(v) !== String(this.startValue)){
9575 this.fireEvent('change', this, v, this.startValue);
9581 * Resets the current field value to the originally loaded value and clears any validation messages
9584 this.setValue(this.originalValue);
9588 * Returns the name of the field
9589 * @return {Mixed} name The name field
9591 getName: function(){
9595 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9596 * @return {Mixed} value The field value
9598 getValue : function(){
9600 var v = this.inputEl().getValue();
9605 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9606 * @return {Mixed} value The field value
9608 getRawValue : function(){
9609 var v = this.inputEl().getValue();
9615 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9616 * @param {Mixed} value The value to set
9618 setRawValue : function(v){
9619 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9622 selectText : function(start, end){
9623 var v = this.getRawValue();
9625 start = start === undefined ? 0 : start;
9626 end = end === undefined ? v.length : end;
9627 var d = this.inputEl().dom;
9628 if(d.setSelectionRange){
9629 d.setSelectionRange(start, end);
9630 }else if(d.createTextRange){
9631 var range = d.createTextRange();
9632 range.moveStart("character", start);
9633 range.moveEnd("character", v.length-end);
9640 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9641 * @param {Mixed} value The value to set
9643 setValue : function(v){
9646 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9652 processValue : function(value){
9653 if(this.stripCharsRe){
9654 var newValue = value.replace(this.stripCharsRe, '');
9655 if(newValue !== value){
9656 this.setRawValue(newValue);
9663 preFocus : function(){
9665 if(this.selectOnFocus){
9666 this.inputEl().dom.select();
9669 filterKeys : function(e){
9671 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9674 var c = e.getCharCode(), cc = String.fromCharCode(c);
9675 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9678 if(!this.maskRe.test(cc)){
9683 * Clear any invalid styles/messages for this field
9685 clearInvalid : function(){
9687 if(!this.el || this.preventMark){ // not rendered
9692 this.el.removeClass(this.invalidClass);
9694 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9696 var feedback = this.el.select('.form-control-feedback', true).first();
9699 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9705 this.indicator.removeClass('visible');
9706 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9709 this.fireEvent('valid', this);
9713 * Mark this field as valid
9715 markValid : function()
9717 if(!this.el || this.preventMark){ // not rendered...
9721 this.el.removeClass([this.invalidClass, this.validClass]);
9723 var feedback = this.el.select('.form-control-feedback', true).first();
9726 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9730 this.indicator.removeClass('visible');
9731 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9738 if(this.allowBlank && !this.getRawValue().length){
9742 this.el.addClass(this.validClass);
9744 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9746 var feedback = this.el.select('.form-control-feedback', true).first();
9749 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9750 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9755 this.fireEvent('valid', this);
9759 * Mark this field as invalid
9760 * @param {String} msg The validation message
9762 markInvalid : function(msg)
9764 if(!this.el || this.preventMark){ // not rendered
9768 this.el.removeClass([this.invalidClass, this.validClass]);
9770 var feedback = this.el.select('.form-control-feedback', true).first();
9773 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9780 if(this.allowBlank && !this.getRawValue().length){
9785 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9786 this.indicator.addClass('visible');
9789 this.el.addClass(this.invalidClass);
9791 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9793 var feedback = this.el.select('.form-control-feedback', true).first();
9796 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9798 if(this.getValue().length || this.forceFeedback){
9799 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9806 this.fireEvent('invalid', this, msg);
9809 SafariOnKeyDown : function(event)
9811 // this is a workaround for a password hang bug on chrome/ webkit.
9812 if (this.inputEl().dom.type != 'password') {
9816 var isSelectAll = false;
9818 if(this.inputEl().dom.selectionEnd > 0){
9819 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9821 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9822 event.preventDefault();
9827 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9829 event.preventDefault();
9830 // this is very hacky as keydown always get's upper case.
9832 var cc = String.fromCharCode(event.getCharCode());
9833 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9837 adjustWidth : function(tag, w){
9838 tag = tag.toLowerCase();
9839 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9840 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9844 if(tag == 'textarea'){
9847 }else if(Roo.isOpera){
9851 if(tag == 'textarea'){
9859 setFieldLabel : function(v)
9865 if(this.indicatorEl()){
9866 var ar = this.el.select('label > span',true);
9868 if (ar.elements.length) {
9869 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9870 this.fieldLabel = v;
9874 var br = this.el.select('label',true);
9876 if(br.elements.length) {
9877 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9878 this.fieldLabel = v;
9882 Roo.log('Cannot Found any of label > span || label in input');
9886 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9887 this.fieldLabel = v;
9902 * @class Roo.bootstrap.TextArea
9903 * @extends Roo.bootstrap.Input
9904 * Bootstrap TextArea class
9905 * @cfg {Number} cols Specifies the visible width of a text area
9906 * @cfg {Number} rows Specifies the visible number of lines in a text area
9907 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9908 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9909 * @cfg {string} html text
9912 * Create a new TextArea
9913 * @param {Object} config The config object
9916 Roo.bootstrap.TextArea = function(config){
9917 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9921 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9931 getAutoCreate : function(){
9933 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9939 if(this.inputType != 'hidden'){
9940 cfg.cls = 'form-group' //input-group
9948 value : this.value || '',
9949 html: this.html || '',
9950 cls : 'form-control',
9951 placeholder : this.placeholder || ''
9955 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9956 input.maxLength = this.maxLength;
9960 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9964 input.cols = this.cols;
9967 if (this.readOnly) {
9968 input.readonly = true;
9972 input.name = this.name;
9976 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9980 ['xs','sm','md','lg'].map(function(size){
9981 if (settings[size]) {
9982 cfg.cls += ' col-' + size + '-' + settings[size];
9986 var inputblock = input;
9988 if(this.hasFeedback && !this.allowBlank){
9992 cls: 'glyphicon form-control-feedback'
9996 cls : 'has-feedback',
10005 if (this.before || this.after) {
10008 cls : 'input-group',
10012 inputblock.cn.push({
10014 cls : 'input-group-addon',
10019 inputblock.cn.push(input);
10021 if(this.hasFeedback && !this.allowBlank){
10022 inputblock.cls += ' has-feedback';
10023 inputblock.cn.push(feedback);
10027 inputblock.cn.push({
10029 cls : 'input-group-addon',
10036 if (align ==='left' && this.fieldLabel.length) {
10041 cls : 'control-label',
10042 html : this.fieldLabel
10053 if(this.labelWidth > 12){
10054 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10057 if(this.labelWidth < 13 && this.labelmd == 0){
10058 this.labelmd = this.labelWidth;
10061 if(this.labellg > 0){
10062 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10063 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10066 if(this.labelmd > 0){
10067 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10068 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10071 if(this.labelsm > 0){
10072 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10073 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10076 if(this.labelxs > 0){
10077 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10078 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10081 } else if ( this.fieldLabel.length) {
10086 //cls : 'input-group-addon',
10087 html : this.fieldLabel
10105 if (this.disabled) {
10106 input.disabled=true;
10113 * return the real textarea element.
10115 inputEl: function ()
10117 return this.el.select('textarea.form-control',true).first();
10121 * Clear any invalid styles/messages for this field
10123 clearInvalid : function()
10126 if(!this.el || this.preventMark){ // not rendered
10130 var label = this.el.select('label', true).first();
10131 var icon = this.el.select('i.fa-star', true).first();
10137 this.el.removeClass(this.invalidClass);
10139 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10141 var feedback = this.el.select('.form-control-feedback', true).first();
10144 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10149 this.fireEvent('valid', this);
10153 * Mark this field as valid
10155 markValid : function()
10157 if(!this.el || this.preventMark){ // not rendered
10161 this.el.removeClass([this.invalidClass, this.validClass]);
10163 var feedback = this.el.select('.form-control-feedback', true).first();
10166 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10169 if(this.disabled || this.allowBlank){
10173 var label = this.el.select('label', true).first();
10174 var icon = this.el.select('i.fa-star', true).first();
10180 this.el.addClass(this.validClass);
10182 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10184 var feedback = this.el.select('.form-control-feedback', true).first();
10187 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10188 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10193 this.fireEvent('valid', this);
10197 * Mark this field as invalid
10198 * @param {String} msg The validation message
10200 markInvalid : function(msg)
10202 if(!this.el || this.preventMark){ // not rendered
10206 this.el.removeClass([this.invalidClass, this.validClass]);
10208 var feedback = this.el.select('.form-control-feedback', true).first();
10211 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10214 if(this.disabled || this.allowBlank){
10218 var label = this.el.select('label', true).first();
10219 var icon = this.el.select('i.fa-star', true).first();
10221 if(!this.getValue().length && label && !icon){
10222 this.el.createChild({
10224 cls : 'text-danger fa fa-lg fa-star',
10225 tooltip : 'This field is required',
10226 style : 'margin-right:5px;'
10230 this.el.addClass(this.invalidClass);
10232 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10234 var feedback = this.el.select('.form-control-feedback', true).first();
10237 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10239 if(this.getValue().length || this.forceFeedback){
10240 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10247 this.fireEvent('invalid', this, msg);
10255 * trigger field - base class for combo..
10260 * @class Roo.bootstrap.TriggerField
10261 * @extends Roo.bootstrap.Input
10262 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10263 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10264 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10265 * for which you can provide a custom implementation. For example:
10267 var trigger = new Roo.bootstrap.TriggerField();
10268 trigger.onTriggerClick = myTriggerFn;
10269 trigger.applyTo('my-field');
10272 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10273 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10274 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10275 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10276 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10279 * Create a new TriggerField.
10280 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10281 * to the base TextField)
10283 Roo.bootstrap.TriggerField = function(config){
10284 this.mimicing = false;
10285 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10288 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10290 * @cfg {String} triggerClass A CSS class to apply to the trigger
10293 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10298 * @cfg {Boolean} removable (true|false) special filter default false
10302 /** @cfg {Boolean} grow @hide */
10303 /** @cfg {Number} growMin @hide */
10304 /** @cfg {Number} growMax @hide */
10310 autoSize: Roo.emptyFn,
10314 deferHeight : true,
10317 actionMode : 'wrap',
10322 getAutoCreate : function(){
10324 var align = this.labelAlign || this.parentLabelAlign();
10329 cls: 'form-group' //input-group
10336 type : this.inputType,
10337 cls : 'form-control',
10338 autocomplete: 'new-password',
10339 placeholder : this.placeholder || ''
10343 input.name = this.name;
10346 input.cls += ' input-' + this.size;
10349 if (this.disabled) {
10350 input.disabled=true;
10353 var inputblock = input;
10355 if(this.hasFeedback && !this.allowBlank){
10359 cls: 'glyphicon form-control-feedback'
10362 if(this.removable && !this.editable && !this.tickable){
10364 cls : 'has-feedback',
10370 cls : 'roo-combo-removable-btn close'
10377 cls : 'has-feedback',
10386 if(this.removable && !this.editable && !this.tickable){
10388 cls : 'roo-removable',
10394 cls : 'roo-combo-removable-btn close'
10401 if (this.before || this.after) {
10404 cls : 'input-group',
10408 inputblock.cn.push({
10410 cls : 'input-group-addon input-group-prepend input-group-text',
10415 inputblock.cn.push(input);
10417 if(this.hasFeedback && !this.allowBlank){
10418 inputblock.cls += ' has-feedback';
10419 inputblock.cn.push(feedback);
10423 inputblock.cn.push({
10425 cls : 'input-group-addon input-group-append input-group-text',
10434 var ibwrap = inputblock;
10439 cls: 'roo-select2-choices',
10443 cls: 'roo-select2-search-field',
10455 cls: 'roo-select2-container input-group',
10460 cls: 'form-hidden-field'
10466 if(!this.multiple && this.showToggleBtn){
10472 if (this.caret != false) {
10475 cls: 'fa fa-' + this.caret
10482 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10487 cls: 'combobox-clear',
10501 combobox.cls += ' roo-select2-container-multi';
10505 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10506 tooltip : 'This field is required'
10508 if (Roo.bootstrap.version == 4) {
10511 style : 'display:none'
10516 if (align ==='left' && this.fieldLabel.length) {
10518 cfg.cls += ' roo-form-group-label-left row';
10525 cls : 'control-label',
10526 html : this.fieldLabel
10538 var labelCfg = cfg.cn[1];
10539 var contentCfg = cfg.cn[2];
10541 if(this.indicatorpos == 'right'){
10546 cls : 'control-label',
10550 html : this.fieldLabel
10564 labelCfg = cfg.cn[0];
10565 contentCfg = cfg.cn[1];
10568 if(this.labelWidth > 12){
10569 labelCfg.style = "width: " + this.labelWidth + 'px';
10572 if(this.labelWidth < 13 && this.labelmd == 0){
10573 this.labelmd = this.labelWidth;
10576 if(this.labellg > 0){
10577 labelCfg.cls += ' col-lg-' + this.labellg;
10578 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10581 if(this.labelmd > 0){
10582 labelCfg.cls += ' col-md-' + this.labelmd;
10583 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10586 if(this.labelsm > 0){
10587 labelCfg.cls += ' col-sm-' + this.labelsm;
10588 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10591 if(this.labelxs > 0){
10592 labelCfg.cls += ' col-xs-' + this.labelxs;
10593 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10596 } else if ( this.fieldLabel.length) {
10597 // Roo.log(" label");
10602 //cls : 'input-group-addon',
10603 html : this.fieldLabel
10611 if(this.indicatorpos == 'right'){
10619 html : this.fieldLabel
10633 // Roo.log(" no label && no align");
10640 ['xs','sm','md','lg'].map(function(size){
10641 if (settings[size]) {
10642 cfg.cls += ' col-' + size + '-' + settings[size];
10653 onResize : function(w, h){
10654 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10655 // if(typeof w == 'number'){
10656 // var x = w - this.trigger.getWidth();
10657 // this.inputEl().setWidth(this.adjustWidth('input', x));
10658 // this.trigger.setStyle('left', x+'px');
10663 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10666 getResizeEl : function(){
10667 return this.inputEl();
10671 getPositionEl : function(){
10672 return this.inputEl();
10676 alignErrorIcon : function(){
10677 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10681 initEvents : function(){
10685 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10686 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10687 if(!this.multiple && this.showToggleBtn){
10688 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10689 if(this.hideTrigger){
10690 this.trigger.setDisplayed(false);
10692 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10696 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10699 if(this.removable && !this.editable && !this.tickable){
10700 var close = this.closeTriggerEl();
10703 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10704 close.on('click', this.removeBtnClick, this, close);
10708 //this.trigger.addClassOnOver('x-form-trigger-over');
10709 //this.trigger.addClassOnClick('x-form-trigger-click');
10712 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10716 closeTriggerEl : function()
10718 var close = this.el.select('.roo-combo-removable-btn', true).first();
10719 return close ? close : false;
10722 removeBtnClick : function(e, h, el)
10724 e.preventDefault();
10726 if(this.fireEvent("remove", this) !== false){
10728 this.fireEvent("afterremove", this)
10732 createList : function()
10734 this.list = Roo.get(document.body).createChild({
10735 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10736 cls: 'typeahead typeahead-long dropdown-menu',
10737 style: 'display:none'
10740 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10745 initTrigger : function(){
10750 onDestroy : function(){
10752 this.trigger.removeAllListeners();
10753 // this.trigger.remove();
10756 // this.wrap.remove();
10758 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10762 onFocus : function(){
10763 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10765 if(!this.mimicing){
10766 this.wrap.addClass('x-trigger-wrap-focus');
10767 this.mimicing = true;
10768 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10769 if(this.monitorTab){
10770 this.el.on("keydown", this.checkTab, this);
10777 checkTab : function(e){
10778 if(e.getKey() == e.TAB){
10779 this.triggerBlur();
10784 onBlur : function(){
10789 mimicBlur : function(e, t){
10791 if(!this.wrap.contains(t) && this.validateBlur()){
10792 this.triggerBlur();
10798 triggerBlur : function(){
10799 this.mimicing = false;
10800 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10801 if(this.monitorTab){
10802 this.el.un("keydown", this.checkTab, this);
10804 //this.wrap.removeClass('x-trigger-wrap-focus');
10805 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10809 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10810 validateBlur : function(e, t){
10815 onDisable : function(){
10816 this.inputEl().dom.disabled = true;
10817 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10819 // this.wrap.addClass('x-item-disabled');
10824 onEnable : function(){
10825 this.inputEl().dom.disabled = false;
10826 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10828 // this.el.removeClass('x-item-disabled');
10833 onShow : function(){
10834 var ae = this.getActionEl();
10837 ae.dom.style.display = '';
10838 ae.dom.style.visibility = 'visible';
10844 onHide : function(){
10845 var ae = this.getActionEl();
10846 ae.dom.style.display = 'none';
10850 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10851 * by an implementing function.
10853 * @param {EventObject} e
10855 onTriggerClick : Roo.emptyFn
10859 * Ext JS Library 1.1.1
10860 * Copyright(c) 2006-2007, Ext JS, LLC.
10862 * Originally Released Under LGPL - original licence link has changed is not relivant.
10865 * <script type="text/javascript">
10870 * @class Roo.data.SortTypes
10872 * Defines the default sorting (casting?) comparison functions used when sorting data.
10874 Roo.data.SortTypes = {
10876 * Default sort that does nothing
10877 * @param {Mixed} s The value being converted
10878 * @return {Mixed} The comparison value
10880 none : function(s){
10885 * The regular expression used to strip tags
10889 stripTagsRE : /<\/?[^>]+>/gi,
10892 * Strips all HTML tags to sort on text only
10893 * @param {Mixed} s The value being converted
10894 * @return {String} The comparison value
10896 asText : function(s){
10897 return String(s).replace(this.stripTagsRE, "");
10901 * Strips all HTML tags to sort on text only - Case insensitive
10902 * @param {Mixed} s The value being converted
10903 * @return {String} The comparison value
10905 asUCText : function(s){
10906 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10910 * Case insensitive string
10911 * @param {Mixed} s The value being converted
10912 * @return {String} The comparison value
10914 asUCString : function(s) {
10915 return String(s).toUpperCase();
10920 * @param {Mixed} s The value being converted
10921 * @return {Number} The comparison value
10923 asDate : function(s) {
10927 if(s instanceof Date){
10928 return s.getTime();
10930 return Date.parse(String(s));
10935 * @param {Mixed} s The value being converted
10936 * @return {Float} The comparison value
10938 asFloat : function(s) {
10939 var val = parseFloat(String(s).replace(/,/g, ""));
10948 * @param {Mixed} s The value being converted
10949 * @return {Number} The comparison value
10951 asInt : function(s) {
10952 var val = parseInt(String(s).replace(/,/g, ""));
10960 * Ext JS Library 1.1.1
10961 * Copyright(c) 2006-2007, Ext JS, LLC.
10963 * Originally Released Under LGPL - original licence link has changed is not relivant.
10966 * <script type="text/javascript">
10970 * @class Roo.data.Record
10971 * Instances of this class encapsulate both record <em>definition</em> information, and record
10972 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10973 * to access Records cached in an {@link Roo.data.Store} object.<br>
10975 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10976 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10979 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10981 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10982 * {@link #create}. The parameters are the same.
10983 * @param {Array} data An associative Array of data values keyed by the field name.
10984 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10985 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10986 * not specified an integer id is generated.
10988 Roo.data.Record = function(data, id){
10989 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10994 * Generate a constructor for a specific record layout.
10995 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10996 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10997 * Each field definition object may contain the following properties: <ul>
10998 * <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,
10999 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11000 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11001 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11002 * is being used, then this is a string containing the javascript expression to reference the data relative to
11003 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11004 * to the data item relative to the record element. If the mapping expression is the same as the field name,
11005 * this may be omitted.</p></li>
11006 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11007 * <ul><li>auto (Default, implies no conversion)</li>
11012 * <li>date</li></ul></p></li>
11013 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11014 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11015 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11016 * by the Reader into an object that will be stored in the Record. It is passed the
11017 * following parameters:<ul>
11018 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11020 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11022 * <br>usage:<br><pre><code>
11023 var TopicRecord = Roo.data.Record.create(
11024 {name: 'title', mapping: 'topic_title'},
11025 {name: 'author', mapping: 'username'},
11026 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11027 {name: 'lastPost', mapping: 'post_time', type: 'date'},
11028 {name: 'lastPoster', mapping: 'user2'},
11029 {name: 'excerpt', mapping: 'post_text'}
11032 var myNewRecord = new TopicRecord({
11033 title: 'Do my job please',
11036 lastPost: new Date(),
11037 lastPoster: 'Animal',
11038 excerpt: 'No way dude!'
11040 myStore.add(myNewRecord);
11045 Roo.data.Record.create = function(o){
11046 var f = function(){
11047 f.superclass.constructor.apply(this, arguments);
11049 Roo.extend(f, Roo.data.Record);
11050 var p = f.prototype;
11051 p.fields = new Roo.util.MixedCollection(false, function(field){
11054 for(var i = 0, len = o.length; i < len; i++){
11055 p.fields.add(new Roo.data.Field(o[i]));
11057 f.getField = function(name){
11058 return p.fields.get(name);
11063 Roo.data.Record.AUTO_ID = 1000;
11064 Roo.data.Record.EDIT = 'edit';
11065 Roo.data.Record.REJECT = 'reject';
11066 Roo.data.Record.COMMIT = 'commit';
11068 Roo.data.Record.prototype = {
11070 * Readonly flag - true if this record has been modified.
11079 join : function(store){
11080 this.store = store;
11084 * Set the named field to the specified value.
11085 * @param {String} name The name of the field to set.
11086 * @param {Object} value The value to set the field to.
11088 set : function(name, value){
11089 if(this.data[name] == value){
11093 if(!this.modified){
11094 this.modified = {};
11096 if(typeof this.modified[name] == 'undefined'){
11097 this.modified[name] = this.data[name];
11099 this.data[name] = value;
11100 if(!this.editing && this.store){
11101 this.store.afterEdit(this);
11106 * Get the value of the named field.
11107 * @param {String} name The name of the field to get the value of.
11108 * @return {Object} The value of the field.
11110 get : function(name){
11111 return this.data[name];
11115 beginEdit : function(){
11116 this.editing = true;
11117 this.modified = {};
11121 cancelEdit : function(){
11122 this.editing = false;
11123 delete this.modified;
11127 endEdit : function(){
11128 this.editing = false;
11129 if(this.dirty && this.store){
11130 this.store.afterEdit(this);
11135 * Usually called by the {@link Roo.data.Store} which owns the Record.
11136 * Rejects all changes made to the Record since either creation, or the last commit operation.
11137 * Modified fields are reverted to their original values.
11139 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11140 * of reject operations.
11142 reject : function(){
11143 var m = this.modified;
11145 if(typeof m[n] != "function"){
11146 this.data[n] = m[n];
11149 this.dirty = false;
11150 delete this.modified;
11151 this.editing = false;
11153 this.store.afterReject(this);
11158 * Usually called by the {@link Roo.data.Store} which owns the Record.
11159 * Commits all changes made to the Record since either creation, or the last commit operation.
11161 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11162 * of commit operations.
11164 commit : function(){
11165 this.dirty = false;
11166 delete this.modified;
11167 this.editing = false;
11169 this.store.afterCommit(this);
11174 hasError : function(){
11175 return this.error != null;
11179 clearError : function(){
11184 * Creates a copy of this record.
11185 * @param {String} id (optional) A new record id if you don't want to use this record's id
11188 copy : function(newId) {
11189 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11193 * Ext JS Library 1.1.1
11194 * Copyright(c) 2006-2007, Ext JS, LLC.
11196 * Originally Released Under LGPL - original licence link has changed is not relivant.
11199 * <script type="text/javascript">
11205 * @class Roo.data.Store
11206 * @extends Roo.util.Observable
11207 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11208 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11210 * 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
11211 * has no knowledge of the format of the data returned by the Proxy.<br>
11213 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11214 * instances from the data object. These records are cached and made available through accessor functions.
11216 * Creates a new Store.
11217 * @param {Object} config A config object containing the objects needed for the Store to access data,
11218 * and read the data into Records.
11220 Roo.data.Store = function(config){
11221 this.data = new Roo.util.MixedCollection(false);
11222 this.data.getKey = function(o){
11225 this.baseParams = {};
11227 this.paramNames = {
11232 "multisort" : "_multisort"
11235 if(config && config.data){
11236 this.inlineData = config.data;
11237 delete config.data;
11240 Roo.apply(this, config);
11242 if(this.reader){ // reader passed
11243 this.reader = Roo.factory(this.reader, Roo.data);
11244 this.reader.xmodule = this.xmodule || false;
11245 if(!this.recordType){
11246 this.recordType = this.reader.recordType;
11248 if(this.reader.onMetaChange){
11249 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11253 if(this.recordType){
11254 this.fields = this.recordType.prototype.fields;
11256 this.modified = [];
11260 * @event datachanged
11261 * Fires when the data cache has changed, and a widget which is using this Store
11262 * as a Record cache should refresh its view.
11263 * @param {Store} this
11265 datachanged : true,
11267 * @event metachange
11268 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11269 * @param {Store} this
11270 * @param {Object} meta The JSON metadata
11275 * Fires when Records have been added to the Store
11276 * @param {Store} this
11277 * @param {Roo.data.Record[]} records The array of Records added
11278 * @param {Number} index The index at which the record(s) were added
11283 * Fires when a Record has been removed from the Store
11284 * @param {Store} this
11285 * @param {Roo.data.Record} record The Record that was removed
11286 * @param {Number} index The index at which the record was removed
11291 * Fires when a Record has been updated
11292 * @param {Store} this
11293 * @param {Roo.data.Record} record The Record that was updated
11294 * @param {String} operation The update operation being performed. Value may be one of:
11296 Roo.data.Record.EDIT
11297 Roo.data.Record.REJECT
11298 Roo.data.Record.COMMIT
11304 * Fires when the data cache has been cleared.
11305 * @param {Store} this
11309 * @event beforeload
11310 * Fires before a request is made for a new data object. If the beforeload handler returns false
11311 * the load action will be canceled.
11312 * @param {Store} this
11313 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11317 * @event beforeloadadd
11318 * Fires after a new set of Records has been loaded.
11319 * @param {Store} this
11320 * @param {Roo.data.Record[]} records The Records that were loaded
11321 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11323 beforeloadadd : true,
11326 * Fires after a new set of Records has been loaded, before they are added to the store.
11327 * @param {Store} this
11328 * @param {Roo.data.Record[]} records The Records that were loaded
11329 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11330 * @params {Object} return from reader
11334 * @event loadexception
11335 * Fires if an exception occurs in the Proxy during loading.
11336 * Called with the signature of the Proxy's "loadexception" event.
11337 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11340 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11341 * @param {Object} load options
11342 * @param {Object} jsonData from your request (normally this contains the Exception)
11344 loadexception : true
11348 this.proxy = Roo.factory(this.proxy, Roo.data);
11349 this.proxy.xmodule = this.xmodule || false;
11350 this.relayEvents(this.proxy, ["loadexception"]);
11352 this.sortToggle = {};
11353 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11355 Roo.data.Store.superclass.constructor.call(this);
11357 if(this.inlineData){
11358 this.loadData(this.inlineData);
11359 delete this.inlineData;
11363 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11365 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11366 * without a remote query - used by combo/forms at present.
11370 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11373 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11376 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11377 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11380 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11381 * on any HTTP request
11384 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11387 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11391 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11392 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11394 remoteSort : false,
11397 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11398 * loaded or when a record is removed. (defaults to false).
11400 pruneModifiedRecords : false,
11403 lastOptions : null,
11406 * Add Records to the Store and fires the add event.
11407 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11409 add : function(records){
11410 records = [].concat(records);
11411 for(var i = 0, len = records.length; i < len; i++){
11412 records[i].join(this);
11414 var index = this.data.length;
11415 this.data.addAll(records);
11416 this.fireEvent("add", this, records, index);
11420 * Remove a Record from the Store and fires the remove event.
11421 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11423 remove : function(record){
11424 var index = this.data.indexOf(record);
11425 this.data.removeAt(index);
11427 if(this.pruneModifiedRecords){
11428 this.modified.remove(record);
11430 this.fireEvent("remove", this, record, index);
11434 * Remove all Records from the Store and fires the clear event.
11436 removeAll : function(){
11438 if(this.pruneModifiedRecords){
11439 this.modified = [];
11441 this.fireEvent("clear", this);
11445 * Inserts Records to the Store at the given index and fires the add event.
11446 * @param {Number} index The start index at which to insert the passed Records.
11447 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11449 insert : function(index, records){
11450 records = [].concat(records);
11451 for(var i = 0, len = records.length; i < len; i++){
11452 this.data.insert(index, records[i]);
11453 records[i].join(this);
11455 this.fireEvent("add", this, records, index);
11459 * Get the index within the cache of the passed Record.
11460 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11461 * @return {Number} The index of the passed Record. Returns -1 if not found.
11463 indexOf : function(record){
11464 return this.data.indexOf(record);
11468 * Get the index within the cache of the Record with the passed id.
11469 * @param {String} id The id of the Record to find.
11470 * @return {Number} The index of the Record. Returns -1 if not found.
11472 indexOfId : function(id){
11473 return this.data.indexOfKey(id);
11477 * Get the Record with the specified id.
11478 * @param {String} id The id of the Record to find.
11479 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11481 getById : function(id){
11482 return this.data.key(id);
11486 * Get the Record at the specified index.
11487 * @param {Number} index The index of the Record to find.
11488 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11490 getAt : function(index){
11491 return this.data.itemAt(index);
11495 * Returns a range of Records between specified indices.
11496 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11497 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11498 * @return {Roo.data.Record[]} An array of Records
11500 getRange : function(start, end){
11501 return this.data.getRange(start, end);
11505 storeOptions : function(o){
11506 o = Roo.apply({}, o);
11509 this.lastOptions = o;
11513 * Loads the Record cache from the configured Proxy using the configured Reader.
11515 * If using remote paging, then the first load call must specify the <em>start</em>
11516 * and <em>limit</em> properties in the options.params property to establish the initial
11517 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11519 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11520 * and this call will return before the new data has been loaded. Perform any post-processing
11521 * in a callback function, or in a "load" event handler.</strong>
11523 * @param {Object} options An object containing properties which control loading options:<ul>
11524 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11525 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11526 * passed the following arguments:<ul>
11527 * <li>r : Roo.data.Record[]</li>
11528 * <li>options: Options object from the load call</li>
11529 * <li>success: Boolean success indicator</li></ul></li>
11530 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11531 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11534 load : function(options){
11535 options = options || {};
11536 if(this.fireEvent("beforeload", this, options) !== false){
11537 this.storeOptions(options);
11538 var p = Roo.apply(options.params || {}, this.baseParams);
11539 // if meta was not loaded from remote source.. try requesting it.
11540 if (!this.reader.metaFromRemote) {
11541 p._requestMeta = 1;
11543 if(this.sortInfo && this.remoteSort){
11544 var pn = this.paramNames;
11545 p[pn["sort"]] = this.sortInfo.field;
11546 p[pn["dir"]] = this.sortInfo.direction;
11548 if (this.multiSort) {
11549 var pn = this.paramNames;
11550 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11553 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11558 * Reloads the Record cache from the configured Proxy using the configured Reader and
11559 * the options from the last load operation performed.
11560 * @param {Object} options (optional) An object containing properties which may override the options
11561 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11562 * the most recently used options are reused).
11564 reload : function(options){
11565 this.load(Roo.applyIf(options||{}, this.lastOptions));
11569 // Called as a callback by the Reader during a load operation.
11570 loadRecords : function(o, options, success){
11571 if(!o || success === false){
11572 if(success !== false){
11573 this.fireEvent("load", this, [], options, o);
11575 if(options.callback){
11576 options.callback.call(options.scope || this, [], options, false);
11580 // if data returned failure - throw an exception.
11581 if (o.success === false) {
11582 // show a message if no listener is registered.
11583 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11584 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11586 // loadmask wil be hooked into this..
11587 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11590 var r = o.records, t = o.totalRecords || r.length;
11592 this.fireEvent("beforeloadadd", this, r, options, o);
11594 if(!options || options.add !== true){
11595 if(this.pruneModifiedRecords){
11596 this.modified = [];
11598 for(var i = 0, len = r.length; i < len; i++){
11602 this.data = this.snapshot;
11603 delete this.snapshot;
11606 this.data.addAll(r);
11607 this.totalLength = t;
11609 this.fireEvent("datachanged", this);
11611 this.totalLength = Math.max(t, this.data.length+r.length);
11615 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11617 var e = new Roo.data.Record({});
11619 e.set(this.parent.displayField, this.parent.emptyTitle);
11620 e.set(this.parent.valueField, '');
11625 this.fireEvent("load", this, r, options, o);
11626 if(options.callback){
11627 options.callback.call(options.scope || this, r, options, true);
11633 * Loads data from a passed data block. A Reader which understands the format of the data
11634 * must have been configured in the constructor.
11635 * @param {Object} data The data block from which to read the Records. The format of the data expected
11636 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11637 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11639 loadData : function(o, append){
11640 var r = this.reader.readRecords(o);
11641 this.loadRecords(r, {add: append}, true);
11645 * Gets the number of cached records.
11647 * <em>If using paging, this may not be the total size of the dataset. If the data object
11648 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11649 * the data set size</em>
11651 getCount : function(){
11652 return this.data.length || 0;
11656 * Gets the total number of records in the dataset as returned by the server.
11658 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11659 * the dataset size</em>
11661 getTotalCount : function(){
11662 return this.totalLength || 0;
11666 * Returns the sort state of the Store as an object with two properties:
11668 field {String} The name of the field by which the Records are sorted
11669 direction {String} The sort order, "ASC" or "DESC"
11672 getSortState : function(){
11673 return this.sortInfo;
11677 applySort : function(){
11678 if(this.sortInfo && !this.remoteSort){
11679 var s = this.sortInfo, f = s.field;
11680 var st = this.fields.get(f).sortType;
11681 var fn = function(r1, r2){
11682 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11683 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11685 this.data.sort(s.direction, fn);
11686 if(this.snapshot && this.snapshot != this.data){
11687 this.snapshot.sort(s.direction, fn);
11693 * Sets the default sort column and order to be used by the next load operation.
11694 * @param {String} fieldName The name of the field to sort by.
11695 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11697 setDefaultSort : function(field, dir){
11698 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11702 * Sort the Records.
11703 * If remote sorting is used, the sort is performed on the server, and the cache is
11704 * reloaded. If local sorting is used, the cache is sorted internally.
11705 * @param {String} fieldName The name of the field to sort by.
11706 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11708 sort : function(fieldName, dir){
11709 var f = this.fields.get(fieldName);
11711 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11713 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11714 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11719 this.sortToggle[f.name] = dir;
11720 this.sortInfo = {field: f.name, direction: dir};
11721 if(!this.remoteSort){
11723 this.fireEvent("datachanged", this);
11725 this.load(this.lastOptions);
11730 * Calls the specified function for each of the Records in the cache.
11731 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11732 * Returning <em>false</em> aborts and exits the iteration.
11733 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11735 each : function(fn, scope){
11736 this.data.each(fn, scope);
11740 * Gets all records modified since the last commit. Modified records are persisted across load operations
11741 * (e.g., during paging).
11742 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11744 getModifiedRecords : function(){
11745 return this.modified;
11749 createFilterFn : function(property, value, anyMatch){
11750 if(!value.exec){ // not a regex
11751 value = String(value);
11752 if(value.length == 0){
11755 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11757 return function(r){
11758 return value.test(r.data[property]);
11763 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11764 * @param {String} property A field on your records
11765 * @param {Number} start The record index to start at (defaults to 0)
11766 * @param {Number} end The last record index to include (defaults to length - 1)
11767 * @return {Number} The sum
11769 sum : function(property, start, end){
11770 var rs = this.data.items, v = 0;
11771 start = start || 0;
11772 end = (end || end === 0) ? end : rs.length-1;
11774 for(var i = start; i <= end; i++){
11775 v += (rs[i].data[property] || 0);
11781 * Filter the records by a specified property.
11782 * @param {String} field A field on your records
11783 * @param {String/RegExp} value Either a string that the field
11784 * should start with or a RegExp to test against the field
11785 * @param {Boolean} anyMatch True to match any part not just the beginning
11787 filter : function(property, value, anyMatch){
11788 var fn = this.createFilterFn(property, value, anyMatch);
11789 return fn ? this.filterBy(fn) : this.clearFilter();
11793 * Filter by a function. The specified function will be called with each
11794 * record in this data source. If the function returns true the record is included,
11795 * otherwise it is filtered.
11796 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11797 * @param {Object} scope (optional) The scope of the function (defaults to this)
11799 filterBy : function(fn, scope){
11800 this.snapshot = this.snapshot || this.data;
11801 this.data = this.queryBy(fn, scope||this);
11802 this.fireEvent("datachanged", this);
11806 * Query the records by a specified property.
11807 * @param {String} field A field on your records
11808 * @param {String/RegExp} value Either a string that the field
11809 * should start with or a RegExp to test against the field
11810 * @param {Boolean} anyMatch True to match any part not just the beginning
11811 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11813 query : function(property, value, anyMatch){
11814 var fn = this.createFilterFn(property, value, anyMatch);
11815 return fn ? this.queryBy(fn) : this.data.clone();
11819 * Query by a function. The specified function will be called with each
11820 * record in this data source. If the function returns true the record is included
11822 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11823 * @param {Object} scope (optional) The scope of the function (defaults to this)
11824 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11826 queryBy : function(fn, scope){
11827 var data = this.snapshot || this.data;
11828 return data.filterBy(fn, scope||this);
11832 * Collects unique values for a particular dataIndex from this store.
11833 * @param {String} dataIndex The property to collect
11834 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11835 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11836 * @return {Array} An array of the unique values
11838 collect : function(dataIndex, allowNull, bypassFilter){
11839 var d = (bypassFilter === true && this.snapshot) ?
11840 this.snapshot.items : this.data.items;
11841 var v, sv, r = [], l = {};
11842 for(var i = 0, len = d.length; i < len; i++){
11843 v = d[i].data[dataIndex];
11845 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11854 * Revert to a view of the Record cache with no filtering applied.
11855 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11857 clearFilter : function(suppressEvent){
11858 if(this.snapshot && this.snapshot != this.data){
11859 this.data = this.snapshot;
11860 delete this.snapshot;
11861 if(suppressEvent !== true){
11862 this.fireEvent("datachanged", this);
11868 afterEdit : function(record){
11869 if(this.modified.indexOf(record) == -1){
11870 this.modified.push(record);
11872 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11876 afterReject : function(record){
11877 this.modified.remove(record);
11878 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11882 afterCommit : function(record){
11883 this.modified.remove(record);
11884 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11888 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11889 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11891 commitChanges : function(){
11892 var m = this.modified.slice(0);
11893 this.modified = [];
11894 for(var i = 0, len = m.length; i < len; i++){
11900 * Cancel outstanding changes on all changed records.
11902 rejectChanges : function(){
11903 var m = this.modified.slice(0);
11904 this.modified = [];
11905 for(var i = 0, len = m.length; i < len; i++){
11910 onMetaChange : function(meta, rtype, o){
11911 this.recordType = rtype;
11912 this.fields = rtype.prototype.fields;
11913 delete this.snapshot;
11914 this.sortInfo = meta.sortInfo || this.sortInfo;
11915 this.modified = [];
11916 this.fireEvent('metachange', this, this.reader.meta);
11919 moveIndex : function(data, type)
11921 var index = this.indexOf(data);
11923 var newIndex = index + type;
11927 this.insert(newIndex, data);
11932 * Ext JS Library 1.1.1
11933 * Copyright(c) 2006-2007, Ext JS, LLC.
11935 * Originally Released Under LGPL - original licence link has changed is not relivant.
11938 * <script type="text/javascript">
11942 * @class Roo.data.SimpleStore
11943 * @extends Roo.data.Store
11944 * Small helper class to make creating Stores from Array data easier.
11945 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11946 * @cfg {Array} fields An array of field definition objects, or field name strings.
11947 * @cfg {Array} data The multi-dimensional array of data
11949 * @param {Object} config
11951 Roo.data.SimpleStore = function(config){
11952 Roo.data.SimpleStore.superclass.constructor.call(this, {
11954 reader: new Roo.data.ArrayReader({
11957 Roo.data.Record.create(config.fields)
11959 proxy : new Roo.data.MemoryProxy(config.data)
11963 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11965 * Ext JS Library 1.1.1
11966 * Copyright(c) 2006-2007, Ext JS, LLC.
11968 * Originally Released Under LGPL - original licence link has changed is not relivant.
11971 * <script type="text/javascript">
11976 * @extends Roo.data.Store
11977 * @class Roo.data.JsonStore
11978 * Small helper class to make creating Stores for JSON data easier. <br/>
11980 var store = new Roo.data.JsonStore({
11981 url: 'get-images.php',
11983 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11986 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11987 * JsonReader and HttpProxy (unless inline data is provided).</b>
11988 * @cfg {Array} fields An array of field definition objects, or field name strings.
11990 * @param {Object} config
11992 Roo.data.JsonStore = function(c){
11993 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11994 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11995 reader: new Roo.data.JsonReader(c, c.fields)
11998 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12000 * Ext JS Library 1.1.1
12001 * Copyright(c) 2006-2007, Ext JS, LLC.
12003 * Originally Released Under LGPL - original licence link has changed is not relivant.
12006 * <script type="text/javascript">
12010 Roo.data.Field = function(config){
12011 if(typeof config == "string"){
12012 config = {name: config};
12014 Roo.apply(this, config);
12017 this.type = "auto";
12020 var st = Roo.data.SortTypes;
12021 // named sortTypes are supported, here we look them up
12022 if(typeof this.sortType == "string"){
12023 this.sortType = st[this.sortType];
12026 // set default sortType for strings and dates
12027 if(!this.sortType){
12030 this.sortType = st.asUCString;
12033 this.sortType = st.asDate;
12036 this.sortType = st.none;
12041 var stripRe = /[\$,%]/g;
12043 // prebuilt conversion function for this field, instead of
12044 // switching every time we're reading a value
12046 var cv, dateFormat = this.dateFormat;
12051 cv = function(v){ return v; };
12054 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12058 return v !== undefined && v !== null && v !== '' ?
12059 parseInt(String(v).replace(stripRe, ""), 10) : '';
12064 return v !== undefined && v !== null && v !== '' ?
12065 parseFloat(String(v).replace(stripRe, ""), 10) : '';
12070 cv = function(v){ return v === true || v === "true" || v == 1; };
12077 if(v instanceof Date){
12081 if(dateFormat == "timestamp"){
12082 return new Date(v*1000);
12084 return Date.parseDate(v, dateFormat);
12086 var parsed = Date.parse(v);
12087 return parsed ? new Date(parsed) : null;
12096 Roo.data.Field.prototype = {
12104 * Ext JS Library 1.1.1
12105 * Copyright(c) 2006-2007, Ext JS, LLC.
12107 * Originally Released Under LGPL - original licence link has changed is not relivant.
12110 * <script type="text/javascript">
12113 // Base class for reading structured data from a data source. This class is intended to be
12114 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12117 * @class Roo.data.DataReader
12118 * Base class for reading structured data from a data source. This class is intended to be
12119 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12122 Roo.data.DataReader = function(meta, recordType){
12126 this.recordType = recordType instanceof Array ?
12127 Roo.data.Record.create(recordType) : recordType;
12130 Roo.data.DataReader.prototype = {
12132 * Create an empty record
12133 * @param {Object} data (optional) - overlay some values
12134 * @return {Roo.data.Record} record created.
12136 newRow : function(d) {
12138 this.recordType.prototype.fields.each(function(c) {
12140 case 'int' : da[c.name] = 0; break;
12141 case 'date' : da[c.name] = new Date(); break;
12142 case 'float' : da[c.name] = 0.0; break;
12143 case 'boolean' : da[c.name] = false; break;
12144 default : da[c.name] = ""; break;
12148 return new this.recordType(Roo.apply(da, d));
12153 * Ext JS Library 1.1.1
12154 * Copyright(c) 2006-2007, Ext JS, LLC.
12156 * Originally Released Under LGPL - original licence link has changed is not relivant.
12159 * <script type="text/javascript">
12163 * @class Roo.data.DataProxy
12164 * @extends Roo.data.Observable
12165 * This class is an abstract base class for implementations which provide retrieval of
12166 * unformatted data objects.<br>
12168 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12169 * (of the appropriate type which knows how to parse the data object) to provide a block of
12170 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12172 * Custom implementations must implement the load method as described in
12173 * {@link Roo.data.HttpProxy#load}.
12175 Roo.data.DataProxy = function(){
12178 * @event beforeload
12179 * Fires before a network request is made to retrieve a data object.
12180 * @param {Object} This DataProxy object.
12181 * @param {Object} params The params parameter to the load function.
12186 * Fires before the load method's callback is called.
12187 * @param {Object} This DataProxy object.
12188 * @param {Object} o The data object.
12189 * @param {Object} arg The callback argument object passed to the load function.
12193 * @event loadexception
12194 * Fires if an Exception occurs during data retrieval.
12195 * @param {Object} This DataProxy object.
12196 * @param {Object} o The data object.
12197 * @param {Object} arg The callback argument object passed to the load function.
12198 * @param {Object} e The Exception.
12200 loadexception : true
12202 Roo.data.DataProxy.superclass.constructor.call(this);
12205 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12208 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12212 * Ext JS Library 1.1.1
12213 * Copyright(c) 2006-2007, Ext JS, LLC.
12215 * Originally Released Under LGPL - original licence link has changed is not relivant.
12218 * <script type="text/javascript">
12221 * @class Roo.data.MemoryProxy
12222 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12223 * to the Reader when its load method is called.
12225 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12227 Roo.data.MemoryProxy = function(data){
12231 Roo.data.MemoryProxy.superclass.constructor.call(this);
12235 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12238 * Load data from the requested source (in this case an in-memory
12239 * data object passed to the constructor), read the data object into
12240 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12241 * process that block using the passed callback.
12242 * @param {Object} params This parameter is not used by the MemoryProxy class.
12243 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12244 * object into a block of Roo.data.Records.
12245 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12246 * The function must be passed <ul>
12247 * <li>The Record block object</li>
12248 * <li>The "arg" argument from the load function</li>
12249 * <li>A boolean success indicator</li>
12251 * @param {Object} scope The scope in which to call the callback
12252 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12254 load : function(params, reader, callback, scope, arg){
12255 params = params || {};
12258 result = reader.readRecords(this.data);
12260 this.fireEvent("loadexception", this, arg, null, e);
12261 callback.call(scope, null, arg, false);
12264 callback.call(scope, result, arg, true);
12268 update : function(params, records){
12273 * Ext JS Library 1.1.1
12274 * Copyright(c) 2006-2007, Ext JS, LLC.
12276 * Originally Released Under LGPL - original licence link has changed is not relivant.
12279 * <script type="text/javascript">
12282 * @class Roo.data.HttpProxy
12283 * @extends Roo.data.DataProxy
12284 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12285 * configured to reference a certain URL.<br><br>
12287 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12288 * from which the running page was served.<br><br>
12290 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12292 * Be aware that to enable the browser to parse an XML document, the server must set
12293 * the Content-Type header in the HTTP response to "text/xml".
12295 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12296 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12297 * will be used to make the request.
12299 Roo.data.HttpProxy = function(conn){
12300 Roo.data.HttpProxy.superclass.constructor.call(this);
12301 // is conn a conn config or a real conn?
12303 this.useAjax = !conn || !conn.events;
12307 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12308 // thse are take from connection...
12311 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12314 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12315 * extra parameters to each request made by this object. (defaults to undefined)
12318 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12319 * to each request made by this object. (defaults to undefined)
12322 * @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)
12325 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12328 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12334 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12338 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12339 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12340 * a finer-grained basis than the DataProxy events.
12342 getConnection : function(){
12343 return this.useAjax ? Roo.Ajax : this.conn;
12347 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12348 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12349 * process that block using the passed callback.
12350 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12351 * for the request to the remote server.
12352 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12353 * object into a block of Roo.data.Records.
12354 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12355 * The function must be passed <ul>
12356 * <li>The Record block object</li>
12357 * <li>The "arg" argument from the load function</li>
12358 * <li>A boolean success indicator</li>
12360 * @param {Object} scope The scope in which to call the callback
12361 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12363 load : function(params, reader, callback, scope, arg){
12364 if(this.fireEvent("beforeload", this, params) !== false){
12366 params : params || {},
12368 callback : callback,
12373 callback : this.loadResponse,
12377 Roo.applyIf(o, this.conn);
12378 if(this.activeRequest){
12379 Roo.Ajax.abort(this.activeRequest);
12381 this.activeRequest = Roo.Ajax.request(o);
12383 this.conn.request(o);
12386 callback.call(scope||this, null, arg, false);
12391 loadResponse : function(o, success, response){
12392 delete this.activeRequest;
12394 this.fireEvent("loadexception", this, o, response);
12395 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12400 result = o.reader.read(response);
12402 this.fireEvent("loadexception", this, o, response, e);
12403 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12407 this.fireEvent("load", this, o, o.request.arg);
12408 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12412 update : function(dataSet){
12417 updateResponse : function(dataSet){
12422 * Ext JS Library 1.1.1
12423 * Copyright(c) 2006-2007, Ext JS, LLC.
12425 * Originally Released Under LGPL - original licence link has changed is not relivant.
12428 * <script type="text/javascript">
12432 * @class Roo.data.ScriptTagProxy
12433 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12434 * other than the originating domain of the running page.<br><br>
12436 * <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
12437 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12439 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12440 * source code that is used as the source inside a <script> tag.<br><br>
12442 * In order for the browser to process the returned data, the server must wrap the data object
12443 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12444 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12445 * depending on whether the callback name was passed:
12448 boolean scriptTag = false;
12449 String cb = request.getParameter("callback");
12452 response.setContentType("text/javascript");
12454 response.setContentType("application/x-json");
12456 Writer out = response.getWriter();
12458 out.write(cb + "(");
12460 out.print(dataBlock.toJsonString());
12467 * @param {Object} config A configuration object.
12469 Roo.data.ScriptTagProxy = function(config){
12470 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12471 Roo.apply(this, config);
12472 this.head = document.getElementsByTagName("head")[0];
12475 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12477 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12479 * @cfg {String} url The URL from which to request the data object.
12482 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12486 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12487 * the server the name of the callback function set up by the load call to process the returned data object.
12488 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12489 * javascript output which calls this named function passing the data object as its only parameter.
12491 callbackParam : "callback",
12493 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12494 * name to the request.
12499 * Load data from the configured URL, read the data object into
12500 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12501 * process that block using the passed callback.
12502 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12503 * for the request to the remote server.
12504 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12505 * object into a block of Roo.data.Records.
12506 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12507 * The function must be passed <ul>
12508 * <li>The Record block object</li>
12509 * <li>The "arg" argument from the load function</li>
12510 * <li>A boolean success indicator</li>
12512 * @param {Object} scope The scope in which to call the callback
12513 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12515 load : function(params, reader, callback, scope, arg){
12516 if(this.fireEvent("beforeload", this, params) !== false){
12518 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12520 var url = this.url;
12521 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12523 url += "&_dc=" + (new Date().getTime());
12525 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12528 cb : "stcCallback"+transId,
12529 scriptId : "stcScript"+transId,
12533 callback : callback,
12539 window[trans.cb] = function(o){
12540 conn.handleResponse(o, trans);
12543 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12545 if(this.autoAbort !== false){
12549 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12551 var script = document.createElement("script");
12552 script.setAttribute("src", url);
12553 script.setAttribute("type", "text/javascript");
12554 script.setAttribute("id", trans.scriptId);
12555 this.head.appendChild(script);
12557 this.trans = trans;
12559 callback.call(scope||this, null, arg, false);
12564 isLoading : function(){
12565 return this.trans ? true : false;
12569 * Abort the current server request.
12571 abort : function(){
12572 if(this.isLoading()){
12573 this.destroyTrans(this.trans);
12578 destroyTrans : function(trans, isLoaded){
12579 this.head.removeChild(document.getElementById(trans.scriptId));
12580 clearTimeout(trans.timeoutId);
12582 window[trans.cb] = undefined;
12584 delete window[trans.cb];
12587 // if hasn't been loaded, wait for load to remove it to prevent script error
12588 window[trans.cb] = function(){
12589 window[trans.cb] = undefined;
12591 delete window[trans.cb];
12598 handleResponse : function(o, trans){
12599 this.trans = false;
12600 this.destroyTrans(trans, true);
12603 result = trans.reader.readRecords(o);
12605 this.fireEvent("loadexception", this, o, trans.arg, e);
12606 trans.callback.call(trans.scope||window, null, trans.arg, false);
12609 this.fireEvent("load", this, o, trans.arg);
12610 trans.callback.call(trans.scope||window, result, trans.arg, true);
12614 handleFailure : function(trans){
12615 this.trans = false;
12616 this.destroyTrans(trans, false);
12617 this.fireEvent("loadexception", this, null, trans.arg);
12618 trans.callback.call(trans.scope||window, null, trans.arg, false);
12622 * Ext JS Library 1.1.1
12623 * Copyright(c) 2006-2007, Ext JS, LLC.
12625 * Originally Released Under LGPL - original licence link has changed is not relivant.
12628 * <script type="text/javascript">
12632 * @class Roo.data.JsonReader
12633 * @extends Roo.data.DataReader
12634 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12635 * based on mappings in a provided Roo.data.Record constructor.
12637 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12638 * in the reply previously.
12643 var RecordDef = Roo.data.Record.create([
12644 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12645 {name: 'occupation'} // This field will use "occupation" as the mapping.
12647 var myReader = new Roo.data.JsonReader({
12648 totalProperty: "results", // The property which contains the total dataset size (optional)
12649 root: "rows", // The property which contains an Array of row objects
12650 id: "id" // The property within each row object that provides an ID for the record (optional)
12654 * This would consume a JSON file like this:
12656 { 'results': 2, 'rows': [
12657 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12658 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12661 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12662 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12663 * paged from the remote server.
12664 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12665 * @cfg {String} root name of the property which contains the Array of row objects.
12666 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12667 * @cfg {Array} fields Array of field definition objects
12669 * Create a new JsonReader
12670 * @param {Object} meta Metadata configuration options
12671 * @param {Object} recordType Either an Array of field definition objects,
12672 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12674 Roo.data.JsonReader = function(meta, recordType){
12677 // set some defaults:
12678 Roo.applyIf(meta, {
12679 totalProperty: 'total',
12680 successProperty : 'success',
12685 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12687 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12690 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12691 * Used by Store query builder to append _requestMeta to params.
12694 metaFromRemote : false,
12696 * This method is only used by a DataProxy which has retrieved data from a remote server.
12697 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12698 * @return {Object} data A data block which is used by an Roo.data.Store object as
12699 * a cache of Roo.data.Records.
12701 read : function(response){
12702 var json = response.responseText;
12704 var o = /* eval:var:o */ eval("("+json+")");
12706 throw {message: "JsonReader.read: Json object not found"};
12712 this.metaFromRemote = true;
12713 this.meta = o.metaData;
12714 this.recordType = Roo.data.Record.create(o.metaData.fields);
12715 this.onMetaChange(this.meta, this.recordType, o);
12717 return this.readRecords(o);
12720 // private function a store will implement
12721 onMetaChange : function(meta, recordType, o){
12728 simpleAccess: function(obj, subsc) {
12735 getJsonAccessor: function(){
12737 return function(expr) {
12739 return(re.test(expr))
12740 ? new Function("obj", "return obj." + expr)
12745 return Roo.emptyFn;
12750 * Create a data block containing Roo.data.Records from an XML document.
12751 * @param {Object} o An object which contains an Array of row objects in the property specified
12752 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12753 * which contains the total size of the dataset.
12754 * @return {Object} data A data block which is used by an Roo.data.Store object as
12755 * a cache of Roo.data.Records.
12757 readRecords : function(o){
12759 * After any data loads, the raw JSON data is available for further custom processing.
12763 var s = this.meta, Record = this.recordType,
12764 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12766 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12768 if(s.totalProperty) {
12769 this.getTotal = this.getJsonAccessor(s.totalProperty);
12771 if(s.successProperty) {
12772 this.getSuccess = this.getJsonAccessor(s.successProperty);
12774 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12776 var g = this.getJsonAccessor(s.id);
12777 this.getId = function(rec) {
12779 return (r === undefined || r === "") ? null : r;
12782 this.getId = function(){return null;};
12785 for(var jj = 0; jj < fl; jj++){
12787 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12788 this.ef[jj] = this.getJsonAccessor(map);
12792 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12793 if(s.totalProperty){
12794 var vt = parseInt(this.getTotal(o), 10);
12799 if(s.successProperty){
12800 var vs = this.getSuccess(o);
12801 if(vs === false || vs === 'false'){
12806 for(var i = 0; i < c; i++){
12809 var id = this.getId(n);
12810 for(var j = 0; j < fl; j++){
12812 var v = this.ef[j](n);
12814 Roo.log('missing convert for ' + f.name);
12818 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12820 var record = new Record(values, id);
12822 records[i] = record;
12828 totalRecords : totalRecords
12833 * Ext JS Library 1.1.1
12834 * Copyright(c) 2006-2007, Ext JS, LLC.
12836 * Originally Released Under LGPL - original licence link has changed is not relivant.
12839 * <script type="text/javascript">
12843 * @class Roo.data.ArrayReader
12844 * @extends Roo.data.DataReader
12845 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12846 * Each element of that Array represents a row of data fields. The
12847 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12848 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12852 var RecordDef = Roo.data.Record.create([
12853 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12854 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12856 var myReader = new Roo.data.ArrayReader({
12857 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12861 * This would consume an Array like this:
12863 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12865 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12867 * Create a new JsonReader
12868 * @param {Object} meta Metadata configuration options.
12869 * @param {Object} recordType Either an Array of field definition objects
12870 * as specified to {@link Roo.data.Record#create},
12871 * or an {@link Roo.data.Record} object
12872 * created using {@link Roo.data.Record#create}.
12874 Roo.data.ArrayReader = function(meta, recordType){
12875 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12878 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12880 * Create a data block containing Roo.data.Records from an XML document.
12881 * @param {Object} o An Array of row objects which represents the dataset.
12882 * @return {Object} data A data block which is used by an Roo.data.Store object as
12883 * a cache of Roo.data.Records.
12885 readRecords : function(o){
12886 var sid = this.meta ? this.meta.id : null;
12887 var recordType = this.recordType, fields = recordType.prototype.fields;
12890 for(var i = 0; i < root.length; i++){
12893 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12894 for(var j = 0, jlen = fields.length; j < jlen; j++){
12895 var f = fields.items[j];
12896 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12897 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12899 values[f.name] = v;
12901 var record = new recordType(values, id);
12903 records[records.length] = record;
12907 totalRecords : records.length
12916 * @class Roo.bootstrap.ComboBox
12917 * @extends Roo.bootstrap.TriggerField
12918 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12919 * @cfg {Boolean} append (true|false) default false
12920 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12921 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12922 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12923 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12924 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12925 * @cfg {Boolean} animate default true
12926 * @cfg {Boolean} emptyResultText only for touch device
12927 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12928 * @cfg {String} emptyTitle default ''
12930 * Create a new ComboBox.
12931 * @param {Object} config Configuration options
12933 Roo.bootstrap.ComboBox = function(config){
12934 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12938 * Fires when the dropdown list is expanded
12939 * @param {Roo.bootstrap.ComboBox} combo This combo box
12944 * Fires when the dropdown list is collapsed
12945 * @param {Roo.bootstrap.ComboBox} combo This combo box
12949 * @event beforeselect
12950 * Fires before a list item is selected. Return false to cancel the selection.
12951 * @param {Roo.bootstrap.ComboBox} combo This combo box
12952 * @param {Roo.data.Record} record The data record returned from the underlying store
12953 * @param {Number} index The index of the selected item in the dropdown list
12955 'beforeselect' : true,
12958 * Fires when a list item is selected
12959 * @param {Roo.bootstrap.ComboBox} combo This combo box
12960 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12961 * @param {Number} index The index of the selected item in the dropdown list
12965 * @event beforequery
12966 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12967 * The event object passed has these properties:
12968 * @param {Roo.bootstrap.ComboBox} combo This combo box
12969 * @param {String} query The query
12970 * @param {Boolean} forceAll true to force "all" query
12971 * @param {Boolean} cancel true to cancel the query
12972 * @param {Object} e The query event object
12974 'beforequery': true,
12977 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12978 * @param {Roo.bootstrap.ComboBox} combo This combo box
12983 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12984 * @param {Roo.bootstrap.ComboBox} combo This combo box
12985 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12990 * Fires when the remove value from the combobox array
12991 * @param {Roo.bootstrap.ComboBox} combo This combo box
12995 * @event afterremove
12996 * Fires when the remove value from the combobox array
12997 * @param {Roo.bootstrap.ComboBox} combo This combo box
12999 'afterremove' : true,
13001 * @event specialfilter
13002 * Fires when specialfilter
13003 * @param {Roo.bootstrap.ComboBox} combo This combo box
13005 'specialfilter' : true,
13008 * Fires when tick the element
13009 * @param {Roo.bootstrap.ComboBox} combo This combo box
13013 * @event touchviewdisplay
13014 * Fires when touch view require special display (default is using displayField)
13015 * @param {Roo.bootstrap.ComboBox} combo This combo box
13016 * @param {Object} cfg set html .
13018 'touchviewdisplay' : true
13023 this.tickItems = [];
13025 this.selectedIndex = -1;
13026 if(this.mode == 'local'){
13027 if(config.queryDelay === undefined){
13028 this.queryDelay = 10;
13030 if(config.minChars === undefined){
13036 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13039 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13040 * rendering into an Roo.Editor, defaults to false)
13043 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13044 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13047 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13050 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13051 * the dropdown list (defaults to undefined, with no header element)
13055 * @cfg {String/Roo.Template} tpl The template to use to render the output
13059 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13061 listWidth: undefined,
13063 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13064 * mode = 'remote' or 'text' if mode = 'local')
13066 displayField: undefined,
13069 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13070 * mode = 'remote' or 'value' if mode = 'local').
13071 * Note: use of a valueField requires the user make a selection
13072 * in order for a value to be mapped.
13074 valueField: undefined,
13076 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13081 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13082 * field's data value (defaults to the underlying DOM element's name)
13084 hiddenName: undefined,
13086 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13090 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13092 selectedClass: 'active',
13095 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13099 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13100 * anchor positions (defaults to 'tl-bl')
13102 listAlign: 'tl-bl?',
13104 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13108 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13109 * query specified by the allQuery config option (defaults to 'query')
13111 triggerAction: 'query',
13113 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13114 * (defaults to 4, does not apply if editable = false)
13118 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13119 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13123 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13124 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13128 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13129 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13133 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13134 * when editable = true (defaults to false)
13136 selectOnFocus:false,
13138 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13140 queryParam: 'query',
13142 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13143 * when mode = 'remote' (defaults to 'Loading...')
13145 loadingText: 'Loading...',
13147 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13151 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13155 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13156 * traditional select (defaults to true)
13160 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13164 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13168 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13169 * listWidth has a higher value)
13173 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13174 * allow the user to set arbitrary text into the field (defaults to false)
13176 forceSelection:false,
13178 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13179 * if typeAhead = true (defaults to 250)
13181 typeAheadDelay : 250,
13183 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13184 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13186 valueNotFoundText : undefined,
13188 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13190 blockFocus : false,
13193 * @cfg {Boolean} disableClear Disable showing of clear button.
13195 disableClear : false,
13197 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13199 alwaysQuery : false,
13202 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13207 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13209 invalidClass : "has-warning",
13212 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13214 validClass : "has-success",
13217 * @cfg {Boolean} specialFilter (true|false) special filter default false
13219 specialFilter : false,
13222 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13224 mobileTouchView : true,
13227 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13229 useNativeIOS : false,
13232 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13234 mobile_restrict_height : false,
13236 ios_options : false,
13248 btnPosition : 'right',
13249 triggerList : true,
13250 showToggleBtn : true,
13252 emptyResultText: 'Empty',
13253 triggerText : 'Select',
13256 // element that contains real text value.. (when hidden is used..)
13258 getAutoCreate : function()
13263 * Render classic select for iso
13266 if(Roo.isIOS && this.useNativeIOS){
13267 cfg = this.getAutoCreateNativeIOS();
13275 if(Roo.isTouch && this.mobileTouchView){
13276 cfg = this.getAutoCreateTouchView();
13283 if(!this.tickable){
13284 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13289 * ComboBox with tickable selections
13292 var align = this.labelAlign || this.parentLabelAlign();
13295 cls : 'form-group roo-combobox-tickable' //input-group
13298 var btn_text_select = '';
13299 var btn_text_done = '';
13300 var btn_text_cancel = '';
13302 if (this.btn_text_show) {
13303 btn_text_select = 'Select';
13304 btn_text_done = 'Done';
13305 btn_text_cancel = 'Cancel';
13310 cls : 'tickable-buttons',
13315 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13316 //html : this.triggerText
13317 html: btn_text_select
13323 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13325 html: btn_text_done
13331 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13333 html: btn_text_cancel
13339 buttons.cn.unshift({
13341 cls: 'roo-select2-search-field-input'
13347 Roo.each(buttons.cn, function(c){
13349 c.cls += ' btn-' + _this.size;
13352 if (_this.disabled) {
13363 cls: 'form-hidden-field'
13367 cls: 'roo-select2-choices',
13371 cls: 'roo-select2-search-field',
13382 cls: 'roo-select2-container input-group roo-select2-container-multi',
13388 // cls: 'typeahead typeahead-long dropdown-menu',
13389 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13394 if(this.hasFeedback && !this.allowBlank){
13398 cls: 'glyphicon form-control-feedback'
13401 combobox.cn.push(feedback);
13406 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13407 tooltip : 'This field is required'
13409 if (Roo.bootstrap.version == 4) {
13412 style : 'display:none'
13415 if (align ==='left' && this.fieldLabel.length) {
13417 cfg.cls += ' roo-form-group-label-left row';
13424 cls : 'control-label col-form-label',
13425 html : this.fieldLabel
13437 var labelCfg = cfg.cn[1];
13438 var contentCfg = cfg.cn[2];
13441 if(this.indicatorpos == 'right'){
13447 cls : 'control-label col-form-label',
13451 html : this.fieldLabel
13467 labelCfg = cfg.cn[0];
13468 contentCfg = cfg.cn[1];
13472 if(this.labelWidth > 12){
13473 labelCfg.style = "width: " + this.labelWidth + 'px';
13476 if(this.labelWidth < 13 && this.labelmd == 0){
13477 this.labelmd = this.labelWidth;
13480 if(this.labellg > 0){
13481 labelCfg.cls += ' col-lg-' + this.labellg;
13482 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13485 if(this.labelmd > 0){
13486 labelCfg.cls += ' col-md-' + this.labelmd;
13487 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13490 if(this.labelsm > 0){
13491 labelCfg.cls += ' col-sm-' + this.labelsm;
13492 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13495 if(this.labelxs > 0){
13496 labelCfg.cls += ' col-xs-' + this.labelxs;
13497 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13501 } else if ( this.fieldLabel.length) {
13502 // Roo.log(" label");
13507 //cls : 'input-group-addon',
13508 html : this.fieldLabel
13513 if(this.indicatorpos == 'right'){
13517 //cls : 'input-group-addon',
13518 html : this.fieldLabel
13528 // Roo.log(" no label && no align");
13535 ['xs','sm','md','lg'].map(function(size){
13536 if (settings[size]) {
13537 cfg.cls += ' col-' + size + '-' + settings[size];
13545 _initEventsCalled : false,
13548 initEvents: function()
13550 if (this._initEventsCalled) { // as we call render... prevent looping...
13553 this._initEventsCalled = true;
13556 throw "can not find store for combo";
13559 this.indicator = this.indicatorEl();
13561 this.store = Roo.factory(this.store, Roo.data);
13562 this.store.parent = this;
13564 // if we are building from html. then this element is so complex, that we can not really
13565 // use the rendered HTML.
13566 // so we have to trash and replace the previous code.
13567 if (Roo.XComponent.build_from_html) {
13568 // remove this element....
13569 var e = this.el.dom, k=0;
13570 while (e ) { e = e.previousSibling; ++k;}
13575 this.rendered = false;
13577 this.render(this.parent().getChildContainer(true), k);
13580 if(Roo.isIOS && this.useNativeIOS){
13581 this.initIOSView();
13589 if(Roo.isTouch && this.mobileTouchView){
13590 this.initTouchView();
13595 this.initTickableEvents();
13599 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13601 if(this.hiddenName){
13603 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13605 this.hiddenField.dom.value =
13606 this.hiddenValue !== undefined ? this.hiddenValue :
13607 this.value !== undefined ? this.value : '';
13609 // prevent input submission
13610 this.el.dom.removeAttribute('name');
13611 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13616 // this.el.dom.setAttribute('autocomplete', 'off');
13619 var cls = 'x-combo-list';
13621 //this.list = new Roo.Layer({
13622 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13628 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13629 _this.list.setWidth(lw);
13632 this.list.on('mouseover', this.onViewOver, this);
13633 this.list.on('mousemove', this.onViewMove, this);
13634 this.list.on('scroll', this.onViewScroll, this);
13637 this.list.swallowEvent('mousewheel');
13638 this.assetHeight = 0;
13641 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13642 this.assetHeight += this.header.getHeight();
13645 this.innerList = this.list.createChild({cls:cls+'-inner'});
13646 this.innerList.on('mouseover', this.onViewOver, this);
13647 this.innerList.on('mousemove', this.onViewMove, this);
13648 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13650 if(this.allowBlank && !this.pageSize && !this.disableClear){
13651 this.footer = this.list.createChild({cls:cls+'-ft'});
13652 this.pageTb = new Roo.Toolbar(this.footer);
13656 this.footer = this.list.createChild({cls:cls+'-ft'});
13657 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13658 {pageSize: this.pageSize});
13662 if (this.pageTb && this.allowBlank && !this.disableClear) {
13664 this.pageTb.add(new Roo.Toolbar.Fill(), {
13665 cls: 'x-btn-icon x-btn-clear',
13667 handler: function()
13670 _this.clearValue();
13671 _this.onSelect(false, -1);
13676 this.assetHeight += this.footer.getHeight();
13681 this.tpl = Roo.bootstrap.version == 4 ?
13682 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
13683 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13686 this.view = new Roo.View(this.list, this.tpl, {
13687 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13689 //this.view.wrapEl.setDisplayed(false);
13690 this.view.on('click', this.onViewClick, this);
13693 this.store.on('beforeload', this.onBeforeLoad, this);
13694 this.store.on('load', this.onLoad, this);
13695 this.store.on('loadexception', this.onLoadException, this);
13697 if(this.resizable){
13698 this.resizer = new Roo.Resizable(this.list, {
13699 pinned:true, handles:'se'
13701 this.resizer.on('resize', function(r, w, h){
13702 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13703 this.listWidth = w;
13704 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13705 this.restrictHeight();
13707 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13710 if(!this.editable){
13711 this.editable = true;
13712 this.setEditable(false);
13717 if (typeof(this.events.add.listeners) != 'undefined') {
13719 this.addicon = this.wrap.createChild(
13720 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13722 this.addicon.on('click', function(e) {
13723 this.fireEvent('add', this);
13726 if (typeof(this.events.edit.listeners) != 'undefined') {
13728 this.editicon = this.wrap.createChild(
13729 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13730 if (this.addicon) {
13731 this.editicon.setStyle('margin-left', '40px');
13733 this.editicon.on('click', function(e) {
13735 // we fire even if inothing is selected..
13736 this.fireEvent('edit', this, this.lastData );
13742 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13743 "up" : function(e){
13744 this.inKeyMode = true;
13748 "down" : function(e){
13749 if(!this.isExpanded()){
13750 this.onTriggerClick();
13752 this.inKeyMode = true;
13757 "enter" : function(e){
13758 // this.onViewClick();
13762 if(this.fireEvent("specialkey", this, e)){
13763 this.onViewClick(false);
13769 "esc" : function(e){
13773 "tab" : function(e){
13776 if(this.fireEvent("specialkey", this, e)){
13777 this.onViewClick(false);
13785 doRelay : function(foo, bar, hname){
13786 if(hname == 'down' || this.scope.isExpanded()){
13787 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13796 this.queryDelay = Math.max(this.queryDelay || 10,
13797 this.mode == 'local' ? 10 : 250);
13800 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13802 if(this.typeAhead){
13803 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13805 if(this.editable !== false){
13806 this.inputEl().on("keyup", this.onKeyUp, this);
13808 if(this.forceSelection){
13809 this.inputEl().on('blur', this.doForce, this);
13813 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13814 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13818 initTickableEvents: function()
13822 if(this.hiddenName){
13824 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13826 this.hiddenField.dom.value =
13827 this.hiddenValue !== undefined ? this.hiddenValue :
13828 this.value !== undefined ? this.value : '';
13830 // prevent input submission
13831 this.el.dom.removeAttribute('name');
13832 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13837 // this.list = this.el.select('ul.dropdown-menu',true).first();
13839 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13840 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13841 if(this.triggerList){
13842 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13845 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13846 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13848 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13849 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13851 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13852 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13854 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13855 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13856 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13859 this.cancelBtn.hide();
13864 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13865 _this.list.setWidth(lw);
13868 this.list.on('mouseover', this.onViewOver, this);
13869 this.list.on('mousemove', this.onViewMove, this);
13871 this.list.on('scroll', this.onViewScroll, this);
13874 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13875 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13878 this.view = new Roo.View(this.list, this.tpl, {
13883 selectedClass: this.selectedClass
13886 //this.view.wrapEl.setDisplayed(false);
13887 this.view.on('click', this.onViewClick, this);
13891 this.store.on('beforeload', this.onBeforeLoad, this);
13892 this.store.on('load', this.onLoad, this);
13893 this.store.on('loadexception', this.onLoadException, this);
13896 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13897 "up" : function(e){
13898 this.inKeyMode = true;
13902 "down" : function(e){
13903 this.inKeyMode = true;
13907 "enter" : function(e){
13908 if(this.fireEvent("specialkey", this, e)){
13909 this.onViewClick(false);
13915 "esc" : function(e){
13916 this.onTickableFooterButtonClick(e, false, false);
13919 "tab" : function(e){
13920 this.fireEvent("specialkey", this, e);
13922 this.onTickableFooterButtonClick(e, false, false);
13929 doRelay : function(e, fn, key){
13930 if(this.scope.isExpanded()){
13931 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13940 this.queryDelay = Math.max(this.queryDelay || 10,
13941 this.mode == 'local' ? 10 : 250);
13944 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13946 if(this.typeAhead){
13947 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13950 if(this.editable !== false){
13951 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13954 this.indicator = this.indicatorEl();
13956 if(this.indicator){
13957 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13958 this.indicator.hide();
13963 onDestroy : function(){
13965 this.view.setStore(null);
13966 this.view.el.removeAllListeners();
13967 this.view.el.remove();
13968 this.view.purgeListeners();
13971 this.list.dom.innerHTML = '';
13975 this.store.un('beforeload', this.onBeforeLoad, this);
13976 this.store.un('load', this.onLoad, this);
13977 this.store.un('loadexception', this.onLoadException, this);
13979 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13983 fireKey : function(e){
13984 if(e.isNavKeyPress() && !this.list.isVisible()){
13985 this.fireEvent("specialkey", this, e);
13990 onResize: function(w, h){
13991 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13993 // if(typeof w != 'number'){
13994 // // we do not handle it!?!?
13997 // var tw = this.trigger.getWidth();
13998 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13999 // // tw += this.editicon ? this.editicon.getWidth() : 0;
14001 // this.inputEl().setWidth( this.adjustWidth('input', x));
14003 // //this.trigger.setStyle('left', x+'px');
14005 // if(this.list && this.listWidth === undefined){
14006 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14007 // this.list.setWidth(lw);
14008 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14016 * Allow or prevent the user from directly editing the field text. If false is passed,
14017 * the user will only be able to select from the items defined in the dropdown list. This method
14018 * is the runtime equivalent of setting the 'editable' config option at config time.
14019 * @param {Boolean} value True to allow the user to directly edit the field text
14021 setEditable : function(value){
14022 if(value == this.editable){
14025 this.editable = value;
14027 this.inputEl().dom.setAttribute('readOnly', true);
14028 this.inputEl().on('mousedown', this.onTriggerClick, this);
14029 this.inputEl().addClass('x-combo-noedit');
14031 this.inputEl().dom.setAttribute('readOnly', false);
14032 this.inputEl().un('mousedown', this.onTriggerClick, this);
14033 this.inputEl().removeClass('x-combo-noedit');
14039 onBeforeLoad : function(combo,opts){
14040 if(!this.hasFocus){
14044 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14046 this.restrictHeight();
14047 this.selectedIndex = -1;
14051 onLoad : function(){
14053 this.hasQuery = false;
14055 if(!this.hasFocus){
14059 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14060 this.loading.hide();
14063 if(this.store.getCount() > 0){
14066 this.restrictHeight();
14067 if(this.lastQuery == this.allQuery){
14068 if(this.editable && !this.tickable){
14069 this.inputEl().dom.select();
14073 !this.selectByValue(this.value, true) &&
14076 !this.store.lastOptions ||
14077 typeof(this.store.lastOptions.add) == 'undefined' ||
14078 this.store.lastOptions.add != true
14081 this.select(0, true);
14084 if(this.autoFocus){
14087 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14088 this.taTask.delay(this.typeAheadDelay);
14092 this.onEmptyResults();
14098 onLoadException : function()
14100 this.hasQuery = false;
14102 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14103 this.loading.hide();
14106 if(this.tickable && this.editable){
14111 // only causes errors at present
14112 //Roo.log(this.store.reader.jsonData);
14113 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14115 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14121 onTypeAhead : function(){
14122 if(this.store.getCount() > 0){
14123 var r = this.store.getAt(0);
14124 var newValue = r.data[this.displayField];
14125 var len = newValue.length;
14126 var selStart = this.getRawValue().length;
14128 if(selStart != len){
14129 this.setRawValue(newValue);
14130 this.selectText(selStart, newValue.length);
14136 onSelect : function(record, index){
14138 if(this.fireEvent('beforeselect', this, record, index) !== false){
14140 this.setFromData(index > -1 ? record.data : false);
14143 this.fireEvent('select', this, record, index);
14148 * Returns the currently selected field value or empty string if no value is set.
14149 * @return {String} value The selected value
14151 getValue : function()
14153 if(Roo.isIOS && this.useNativeIOS){
14154 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14158 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14161 if(this.valueField){
14162 return typeof this.value != 'undefined' ? this.value : '';
14164 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14168 getRawValue : function()
14170 if(Roo.isIOS && this.useNativeIOS){
14171 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14174 var v = this.inputEl().getValue();
14180 * Clears any text/value currently set in the field
14182 clearValue : function(){
14184 if(this.hiddenField){
14185 this.hiddenField.dom.value = '';
14188 this.setRawValue('');
14189 this.lastSelectionText = '';
14190 this.lastData = false;
14192 var close = this.closeTriggerEl();
14203 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14204 * will be displayed in the field. If the value does not match the data value of an existing item,
14205 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14206 * Otherwise the field will be blank (although the value will still be set).
14207 * @param {String} value The value to match
14209 setValue : function(v)
14211 if(Roo.isIOS && this.useNativeIOS){
14212 this.setIOSValue(v);
14222 if(this.valueField){
14223 var r = this.findRecord(this.valueField, v);
14225 text = r.data[this.displayField];
14226 }else if(this.valueNotFoundText !== undefined){
14227 text = this.valueNotFoundText;
14230 this.lastSelectionText = text;
14231 if(this.hiddenField){
14232 this.hiddenField.dom.value = v;
14234 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14237 var close = this.closeTriggerEl();
14240 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14246 * @property {Object} the last set data for the element
14251 * Sets the value of the field based on a object which is related to the record format for the store.
14252 * @param {Object} value the value to set as. or false on reset?
14254 setFromData : function(o){
14261 var dv = ''; // display value
14262 var vv = ''; // value value..
14264 if (this.displayField) {
14265 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14267 // this is an error condition!!!
14268 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14271 if(this.valueField){
14272 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14275 var close = this.closeTriggerEl();
14278 if(dv.length || vv * 1 > 0){
14280 this.blockFocus=true;
14286 if(this.hiddenField){
14287 this.hiddenField.dom.value = vv;
14289 this.lastSelectionText = dv;
14290 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14294 // no hidden field.. - we store the value in 'value', but still display
14295 // display field!!!!
14296 this.lastSelectionText = dv;
14297 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14304 reset : function(){
14305 // overridden so that last data is reset..
14312 this.setValue(this.originalValue);
14313 //this.clearInvalid();
14314 this.lastData = false;
14316 this.view.clearSelections();
14322 findRecord : function(prop, value){
14324 if(this.store.getCount() > 0){
14325 this.store.each(function(r){
14326 if(r.data[prop] == value){
14336 getName: function()
14338 // returns hidden if it's set..
14339 if (!this.rendered) {return ''};
14340 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14344 onViewMove : function(e, t){
14345 this.inKeyMode = false;
14349 onViewOver : function(e, t){
14350 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14353 var item = this.view.findItemFromChild(t);
14356 var index = this.view.indexOf(item);
14357 this.select(index, false);
14362 onViewClick : function(view, doFocus, el, e)
14364 var index = this.view.getSelectedIndexes()[0];
14366 var r = this.store.getAt(index);
14370 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14377 Roo.each(this.tickItems, function(v,k){
14379 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14381 _this.tickItems.splice(k, 1);
14383 if(typeof(e) == 'undefined' && view == false){
14384 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14396 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14397 this.tickItems.push(r.data);
14400 if(typeof(e) == 'undefined' && view == false){
14401 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14408 this.onSelect(r, index);
14410 if(doFocus !== false && !this.blockFocus){
14411 this.inputEl().focus();
14416 restrictHeight : function(){
14417 //this.innerList.dom.style.height = '';
14418 //var inner = this.innerList.dom;
14419 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14420 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14421 //this.list.beginUpdate();
14422 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14423 this.list.alignTo(this.inputEl(), this.listAlign);
14424 this.list.alignTo(this.inputEl(), this.listAlign);
14425 //this.list.endUpdate();
14429 onEmptyResults : function(){
14431 if(this.tickable && this.editable){
14432 this.hasFocus = false;
14433 this.restrictHeight();
14441 * Returns true if the dropdown list is expanded, else false.
14443 isExpanded : function(){
14444 return this.list.isVisible();
14448 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14449 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14450 * @param {String} value The data value of the item to select
14451 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14452 * selected item if it is not currently in view (defaults to true)
14453 * @return {Boolean} True if the value matched an item in the list, else false
14455 selectByValue : function(v, scrollIntoView){
14456 if(v !== undefined && v !== null){
14457 var r = this.findRecord(this.valueField || this.displayField, v);
14459 this.select(this.store.indexOf(r), scrollIntoView);
14467 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14468 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14469 * @param {Number} index The zero-based index of the list item to select
14470 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14471 * selected item if it is not currently in view (defaults to true)
14473 select : function(index, scrollIntoView){
14474 this.selectedIndex = index;
14475 this.view.select(index);
14476 if(scrollIntoView !== false){
14477 var el = this.view.getNode(index);
14479 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14482 this.list.scrollChildIntoView(el, false);
14488 selectNext : function(){
14489 var ct = this.store.getCount();
14491 if(this.selectedIndex == -1){
14493 }else if(this.selectedIndex < ct-1){
14494 this.select(this.selectedIndex+1);
14500 selectPrev : function(){
14501 var ct = this.store.getCount();
14503 if(this.selectedIndex == -1){
14505 }else if(this.selectedIndex != 0){
14506 this.select(this.selectedIndex-1);
14512 onKeyUp : function(e){
14513 if(this.editable !== false && !e.isSpecialKey()){
14514 this.lastKey = e.getKey();
14515 this.dqTask.delay(this.queryDelay);
14520 validateBlur : function(){
14521 return !this.list || !this.list.isVisible();
14525 initQuery : function(){
14527 var v = this.getRawValue();
14529 if(this.tickable && this.editable){
14530 v = this.tickableInputEl().getValue();
14537 doForce : function(){
14538 if(this.inputEl().dom.value.length > 0){
14539 this.inputEl().dom.value =
14540 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14546 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14547 * query allowing the query action to be canceled if needed.
14548 * @param {String} query The SQL query to execute
14549 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14550 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14551 * saved in the current store (defaults to false)
14553 doQuery : function(q, forceAll){
14555 if(q === undefined || q === null){
14560 forceAll: forceAll,
14564 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14569 forceAll = qe.forceAll;
14570 if(forceAll === true || (q.length >= this.minChars)){
14572 this.hasQuery = true;
14574 if(this.lastQuery != q || this.alwaysQuery){
14575 this.lastQuery = q;
14576 if(this.mode == 'local'){
14577 this.selectedIndex = -1;
14579 this.store.clearFilter();
14582 if(this.specialFilter){
14583 this.fireEvent('specialfilter', this);
14588 this.store.filter(this.displayField, q);
14591 this.store.fireEvent("datachanged", this.store);
14598 this.store.baseParams[this.queryParam] = q;
14600 var options = {params : this.getParams(q)};
14603 options.add = true;
14604 options.params.start = this.page * this.pageSize;
14607 this.store.load(options);
14610 * this code will make the page width larger, at the beginning, the list not align correctly,
14611 * we should expand the list on onLoad
14612 * so command out it
14617 this.selectedIndex = -1;
14622 this.loadNext = false;
14626 getParams : function(q){
14628 //p[this.queryParam] = q;
14632 p.limit = this.pageSize;
14638 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14640 collapse : function(){
14641 if(!this.isExpanded()){
14647 this.hasFocus = false;
14651 this.cancelBtn.hide();
14652 this.trigger.show();
14655 this.tickableInputEl().dom.value = '';
14656 this.tickableInputEl().blur();
14661 Roo.get(document).un('mousedown', this.collapseIf, this);
14662 Roo.get(document).un('mousewheel', this.collapseIf, this);
14663 if (!this.editable) {
14664 Roo.get(document).un('keydown', this.listKeyPress, this);
14666 this.fireEvent('collapse', this);
14672 collapseIf : function(e){
14673 var in_combo = e.within(this.el);
14674 var in_list = e.within(this.list);
14675 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14677 if (in_combo || in_list || is_list) {
14678 //e.stopPropagation();
14683 this.onTickableFooterButtonClick(e, false, false);
14691 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14693 expand : function(){
14695 if(this.isExpanded() || !this.hasFocus){
14699 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14700 this.list.setWidth(lw);
14706 this.restrictHeight();
14710 this.tickItems = Roo.apply([], this.item);
14713 this.cancelBtn.show();
14714 this.trigger.hide();
14717 this.tickableInputEl().focus();
14722 Roo.get(document).on('mousedown', this.collapseIf, this);
14723 Roo.get(document).on('mousewheel', this.collapseIf, this);
14724 if (!this.editable) {
14725 Roo.get(document).on('keydown', this.listKeyPress, this);
14728 this.fireEvent('expand', this);
14732 // Implements the default empty TriggerField.onTriggerClick function
14733 onTriggerClick : function(e)
14735 Roo.log('trigger click');
14737 if(this.disabled || !this.triggerList){
14742 this.loadNext = false;
14744 if(this.isExpanded()){
14746 if (!this.blockFocus) {
14747 this.inputEl().focus();
14751 this.hasFocus = true;
14752 if(this.triggerAction == 'all') {
14753 this.doQuery(this.allQuery, true);
14755 this.doQuery(this.getRawValue());
14757 if (!this.blockFocus) {
14758 this.inputEl().focus();
14763 onTickableTriggerClick : function(e)
14770 this.loadNext = false;
14771 this.hasFocus = true;
14773 if(this.triggerAction == 'all') {
14774 this.doQuery(this.allQuery, true);
14776 this.doQuery(this.getRawValue());
14780 onSearchFieldClick : function(e)
14782 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14783 this.onTickableFooterButtonClick(e, false, false);
14787 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14792 this.loadNext = false;
14793 this.hasFocus = true;
14795 if(this.triggerAction == 'all') {
14796 this.doQuery(this.allQuery, true);
14798 this.doQuery(this.getRawValue());
14802 listKeyPress : function(e)
14804 //Roo.log('listkeypress');
14805 // scroll to first matching element based on key pres..
14806 if (e.isSpecialKey()) {
14809 var k = String.fromCharCode(e.getKey()).toUpperCase();
14812 var csel = this.view.getSelectedNodes();
14813 var cselitem = false;
14815 var ix = this.view.indexOf(csel[0]);
14816 cselitem = this.store.getAt(ix);
14817 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14823 this.store.each(function(v) {
14825 // start at existing selection.
14826 if (cselitem.id == v.id) {
14832 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14833 match = this.store.indexOf(v);
14839 if (match === false) {
14840 return true; // no more action?
14843 this.view.select(match);
14844 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14845 sn.scrollIntoView(sn.dom.parentNode, false);
14848 onViewScroll : function(e, t){
14850 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){
14854 this.hasQuery = true;
14856 this.loading = this.list.select('.loading', true).first();
14858 if(this.loading === null){
14859 this.list.createChild({
14861 cls: 'loading roo-select2-more-results roo-select2-active',
14862 html: 'Loading more results...'
14865 this.loading = this.list.select('.loading', true).first();
14867 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14869 this.loading.hide();
14872 this.loading.show();
14877 this.loadNext = true;
14879 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14884 addItem : function(o)
14886 var dv = ''; // display value
14888 if (this.displayField) {
14889 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14891 // this is an error condition!!!
14892 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14899 var choice = this.choices.createChild({
14901 cls: 'roo-select2-search-choice',
14910 cls: 'roo-select2-search-choice-close fa fa-times',
14915 }, this.searchField);
14917 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14919 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14927 this.inputEl().dom.value = '';
14932 onRemoveItem : function(e, _self, o)
14934 e.preventDefault();
14936 this.lastItem = Roo.apply([], this.item);
14938 var index = this.item.indexOf(o.data) * 1;
14941 Roo.log('not this item?!');
14945 this.item.splice(index, 1);
14950 this.fireEvent('remove', this, e);
14956 syncValue : function()
14958 if(!this.item.length){
14965 Roo.each(this.item, function(i){
14966 if(_this.valueField){
14967 value.push(i[_this.valueField]);
14974 this.value = value.join(',');
14976 if(this.hiddenField){
14977 this.hiddenField.dom.value = this.value;
14980 this.store.fireEvent("datachanged", this.store);
14985 clearItem : function()
14987 if(!this.multiple){
14993 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15001 if(this.tickable && !Roo.isTouch){
15002 this.view.refresh();
15006 inputEl: function ()
15008 if(Roo.isIOS && this.useNativeIOS){
15009 return this.el.select('select.roo-ios-select', true).first();
15012 if(Roo.isTouch && this.mobileTouchView){
15013 return this.el.select('input.form-control',true).first();
15017 return this.searchField;
15020 return this.el.select('input.form-control',true).first();
15023 onTickableFooterButtonClick : function(e, btn, el)
15025 e.preventDefault();
15027 this.lastItem = Roo.apply([], this.item);
15029 if(btn && btn.name == 'cancel'){
15030 this.tickItems = Roo.apply([], this.item);
15039 Roo.each(this.tickItems, function(o){
15047 validate : function()
15049 if(this.getVisibilityEl().hasClass('hidden')){
15053 var v = this.getRawValue();
15056 v = this.getValue();
15059 if(this.disabled || this.allowBlank || v.length){
15064 this.markInvalid();
15068 tickableInputEl : function()
15070 if(!this.tickable || !this.editable){
15071 return this.inputEl();
15074 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15078 getAutoCreateTouchView : function()
15083 cls: 'form-group' //input-group
15089 type : this.inputType,
15090 cls : 'form-control x-combo-noedit',
15091 autocomplete: 'new-password',
15092 placeholder : this.placeholder || '',
15097 input.name = this.name;
15101 input.cls += ' input-' + this.size;
15104 if (this.disabled) {
15105 input.disabled = true;
15116 inputblock.cls += ' input-group';
15118 inputblock.cn.unshift({
15120 cls : 'input-group-addon input-group-prepend input-group-text',
15125 if(this.removable && !this.multiple){
15126 inputblock.cls += ' roo-removable';
15128 inputblock.cn.push({
15131 cls : 'roo-combo-removable-btn close'
15135 if(this.hasFeedback && !this.allowBlank){
15137 inputblock.cls += ' has-feedback';
15139 inputblock.cn.push({
15141 cls: 'glyphicon form-control-feedback'
15148 inputblock.cls += (this.before) ? '' : ' input-group';
15150 inputblock.cn.push({
15152 cls : 'input-group-addon input-group-append input-group-text',
15158 var ibwrap = inputblock;
15163 cls: 'roo-select2-choices',
15167 cls: 'roo-select2-search-field',
15180 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15185 cls: 'form-hidden-field'
15191 if(!this.multiple && this.showToggleBtn){
15198 if (this.caret != false) {
15201 cls: 'fa fa-' + this.caret
15208 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15213 cls: 'combobox-clear',
15227 combobox.cls += ' roo-select2-container-multi';
15230 var align = this.labelAlign || this.parentLabelAlign();
15232 if (align ==='left' && this.fieldLabel.length) {
15237 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15238 tooltip : 'This field is required'
15242 cls : 'control-label col-form-label',
15243 html : this.fieldLabel
15254 var labelCfg = cfg.cn[1];
15255 var contentCfg = cfg.cn[2];
15258 if(this.indicatorpos == 'right'){
15263 cls : 'control-label col-form-label',
15267 html : this.fieldLabel
15271 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15272 tooltip : 'This field is required'
15285 labelCfg = cfg.cn[0];
15286 contentCfg = cfg.cn[1];
15291 if(this.labelWidth > 12){
15292 labelCfg.style = "width: " + this.labelWidth + 'px';
15295 if(this.labelWidth < 13 && this.labelmd == 0){
15296 this.labelmd = this.labelWidth;
15299 if(this.labellg > 0){
15300 labelCfg.cls += ' col-lg-' + this.labellg;
15301 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15304 if(this.labelmd > 0){
15305 labelCfg.cls += ' col-md-' + this.labelmd;
15306 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15309 if(this.labelsm > 0){
15310 labelCfg.cls += ' col-sm-' + this.labelsm;
15311 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15314 if(this.labelxs > 0){
15315 labelCfg.cls += ' col-xs-' + this.labelxs;
15316 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15320 } else if ( this.fieldLabel.length) {
15324 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15325 tooltip : 'This field is required'
15329 cls : 'control-label',
15330 html : this.fieldLabel
15341 if(this.indicatorpos == 'right'){
15345 cls : 'control-label',
15346 html : this.fieldLabel,
15350 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15351 tooltip : 'This field is required'
15368 var settings = this;
15370 ['xs','sm','md','lg'].map(function(size){
15371 if (settings[size]) {
15372 cfg.cls += ' col-' + size + '-' + settings[size];
15379 initTouchView : function()
15381 this.renderTouchView();
15383 this.touchViewEl.on('scroll', function(){
15384 this.el.dom.scrollTop = 0;
15387 this.originalValue = this.getValue();
15389 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15391 this.inputEl().on("click", this.showTouchView, this);
15392 if (this.triggerEl) {
15393 this.triggerEl.on("click", this.showTouchView, this);
15397 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15398 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15400 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15402 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15403 this.store.on('load', this.onTouchViewLoad, this);
15404 this.store.on('loadexception', this.onTouchViewLoadException, this);
15406 if(this.hiddenName){
15408 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15410 this.hiddenField.dom.value =
15411 this.hiddenValue !== undefined ? this.hiddenValue :
15412 this.value !== undefined ? this.value : '';
15414 this.el.dom.removeAttribute('name');
15415 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15419 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15420 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15423 if(this.removable && !this.multiple){
15424 var close = this.closeTriggerEl();
15426 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15427 close.on('click', this.removeBtnClick, this, close);
15431 * fix the bug in Safari iOS8
15433 this.inputEl().on("focus", function(e){
15434 document.activeElement.blur();
15437 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15444 renderTouchView : function()
15446 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15447 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15449 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15450 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15452 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15453 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15454 this.touchViewBodyEl.setStyle('overflow', 'auto');
15456 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15457 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15459 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15460 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15464 showTouchView : function()
15470 this.touchViewHeaderEl.hide();
15472 if(this.modalTitle.length){
15473 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15474 this.touchViewHeaderEl.show();
15477 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15478 this.touchViewEl.show();
15480 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15482 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15483 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15485 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15487 if(this.modalTitle.length){
15488 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15491 this.touchViewBodyEl.setHeight(bodyHeight);
15495 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15497 this.touchViewEl.addClass('in');
15500 if(this._touchViewMask){
15501 Roo.get(document.body).addClass("x-body-masked");
15502 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15503 this._touchViewMask.setStyle('z-index', 10000);
15504 this._touchViewMask.addClass('show');
15507 this.doTouchViewQuery();
15511 hideTouchView : function()
15513 this.touchViewEl.removeClass('in');
15517 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15519 this.touchViewEl.setStyle('display', 'none');
15522 if(this._touchViewMask){
15523 this._touchViewMask.removeClass('show');
15524 Roo.get(document.body).removeClass("x-body-masked");
15528 setTouchViewValue : function()
15535 Roo.each(this.tickItems, function(o){
15540 this.hideTouchView();
15543 doTouchViewQuery : function()
15552 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15556 if(!this.alwaysQuery || this.mode == 'local'){
15557 this.onTouchViewLoad();
15564 onTouchViewBeforeLoad : function(combo,opts)
15570 onTouchViewLoad : function()
15572 if(this.store.getCount() < 1){
15573 this.onTouchViewEmptyResults();
15577 this.clearTouchView();
15579 var rawValue = this.getRawValue();
15581 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15583 this.tickItems = [];
15585 this.store.data.each(function(d, rowIndex){
15586 var row = this.touchViewListGroup.createChild(template);
15588 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15589 row.addClass(d.data.cls);
15592 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15595 html : d.data[this.displayField]
15598 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15599 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15602 row.removeClass('selected');
15603 if(!this.multiple && this.valueField &&
15604 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15607 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15608 row.addClass('selected');
15611 if(this.multiple && this.valueField &&
15612 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15616 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15617 this.tickItems.push(d.data);
15620 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15624 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15626 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15628 if(this.modalTitle.length){
15629 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15632 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15634 if(this.mobile_restrict_height && listHeight < bodyHeight){
15635 this.touchViewBodyEl.setHeight(listHeight);
15640 if(firstChecked && listHeight > bodyHeight){
15641 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15646 onTouchViewLoadException : function()
15648 this.hideTouchView();
15651 onTouchViewEmptyResults : function()
15653 this.clearTouchView();
15655 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15657 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15661 clearTouchView : function()
15663 this.touchViewListGroup.dom.innerHTML = '';
15666 onTouchViewClick : function(e, el, o)
15668 e.preventDefault();
15671 var rowIndex = o.rowIndex;
15673 var r = this.store.getAt(rowIndex);
15675 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15677 if(!this.multiple){
15678 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15679 c.dom.removeAttribute('checked');
15682 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15684 this.setFromData(r.data);
15686 var close = this.closeTriggerEl();
15692 this.hideTouchView();
15694 this.fireEvent('select', this, r, rowIndex);
15699 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15700 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15701 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15705 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15706 this.addItem(r.data);
15707 this.tickItems.push(r.data);
15711 getAutoCreateNativeIOS : function()
15714 cls: 'form-group' //input-group,
15719 cls : 'roo-ios-select'
15723 combobox.name = this.name;
15726 if (this.disabled) {
15727 combobox.disabled = true;
15730 var settings = this;
15732 ['xs','sm','md','lg'].map(function(size){
15733 if (settings[size]) {
15734 cfg.cls += ' col-' + size + '-' + settings[size];
15744 initIOSView : function()
15746 this.store.on('load', this.onIOSViewLoad, this);
15751 onIOSViewLoad : function()
15753 if(this.store.getCount() < 1){
15757 this.clearIOSView();
15759 if(this.allowBlank) {
15761 var default_text = '-- SELECT --';
15763 if(this.placeholder.length){
15764 default_text = this.placeholder;
15767 if(this.emptyTitle.length){
15768 default_text += ' - ' + this.emptyTitle + ' -';
15771 var opt = this.inputEl().createChild({
15774 html : default_text
15778 o[this.valueField] = 0;
15779 o[this.displayField] = default_text;
15781 this.ios_options.push({
15788 this.store.data.each(function(d, rowIndex){
15792 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15793 html = d.data[this.displayField];
15798 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15799 value = d.data[this.valueField];
15808 if(this.value == d.data[this.valueField]){
15809 option['selected'] = true;
15812 var opt = this.inputEl().createChild(option);
15814 this.ios_options.push({
15821 this.inputEl().on('change', function(){
15822 this.fireEvent('select', this);
15827 clearIOSView: function()
15829 this.inputEl().dom.innerHTML = '';
15831 this.ios_options = [];
15834 setIOSValue: function(v)
15838 if(!this.ios_options){
15842 Roo.each(this.ios_options, function(opts){
15844 opts.el.dom.removeAttribute('selected');
15846 if(opts.data[this.valueField] != v){
15850 opts.el.dom.setAttribute('selected', true);
15856 * @cfg {Boolean} grow
15860 * @cfg {Number} growMin
15864 * @cfg {Number} growMax
15873 Roo.apply(Roo.bootstrap.ComboBox, {
15877 cls: 'modal-header',
15899 cls: 'list-group-item',
15903 cls: 'roo-combobox-list-group-item-value'
15907 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15921 listItemCheckbox : {
15923 cls: 'list-group-item',
15927 cls: 'roo-combobox-list-group-item-value'
15931 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15947 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15952 cls: 'modal-footer',
15960 cls: 'col-xs-6 text-left',
15963 cls: 'btn btn-danger roo-touch-view-cancel',
15969 cls: 'col-xs-6 text-right',
15972 cls: 'btn btn-success roo-touch-view-ok',
15983 Roo.apply(Roo.bootstrap.ComboBox, {
15985 touchViewTemplate : {
15987 cls: 'modal fade roo-combobox-touch-view',
15991 cls: 'modal-dialog',
15992 style : 'position:fixed', // we have to fix position....
15996 cls: 'modal-content',
15998 Roo.bootstrap.ComboBox.header,
15999 Roo.bootstrap.ComboBox.body,
16000 Roo.bootstrap.ComboBox.footer
16009 * Ext JS Library 1.1.1
16010 * Copyright(c) 2006-2007, Ext JS, LLC.
16012 * Originally Released Under LGPL - original licence link has changed is not relivant.
16015 * <script type="text/javascript">
16020 * @extends Roo.util.Observable
16021 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
16022 * This class also supports single and multi selection modes. <br>
16023 * Create a data model bound view:
16025 var store = new Roo.data.Store(...);
16027 var view = new Roo.View({
16029 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
16031 singleSelect: true,
16032 selectedClass: "ydataview-selected",
16036 // listen for node click?
16037 view.on("click", function(vw, index, node, e){
16038 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16042 dataModel.load("foobar.xml");
16044 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16046 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16047 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16049 * Note: old style constructor is still suported (container, template, config)
16052 * Create a new View
16053 * @param {Object} config The config object
16056 Roo.View = function(config, depreciated_tpl, depreciated_config){
16058 this.parent = false;
16060 if (typeof(depreciated_tpl) == 'undefined') {
16061 // new way.. - universal constructor.
16062 Roo.apply(this, config);
16063 this.el = Roo.get(this.el);
16066 this.el = Roo.get(config);
16067 this.tpl = depreciated_tpl;
16068 Roo.apply(this, depreciated_config);
16070 this.wrapEl = this.el.wrap().wrap();
16071 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16074 if(typeof(this.tpl) == "string"){
16075 this.tpl = new Roo.Template(this.tpl);
16077 // support xtype ctors..
16078 this.tpl = new Roo.factory(this.tpl, Roo);
16082 this.tpl.compile();
16087 * @event beforeclick
16088 * Fires before a click is processed. Returns false to cancel the default action.
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
16094 "beforeclick" : true,
16097 * Fires when a template node is 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
16106 * Fires when a template node is double clicked.
16107 * @param {Roo.View} this
16108 * @param {Number} index The index of the target node
16109 * @param {HTMLElement} node The target node
16110 * @param {Roo.EventObject} e The raw event object
16114 * @event contextmenu
16115 * Fires when a template node is right clicked.
16116 * @param {Roo.View} this
16117 * @param {Number} index The index of the target node
16118 * @param {HTMLElement} node The target node
16119 * @param {Roo.EventObject} e The raw event object
16121 "contextmenu" : true,
16123 * @event selectionchange
16124 * Fires when the selected nodes change.
16125 * @param {Roo.View} this
16126 * @param {Array} selections Array of the selected nodes
16128 "selectionchange" : true,
16131 * @event beforeselect
16132 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16133 * @param {Roo.View} this
16134 * @param {HTMLElement} node The node to be selected
16135 * @param {Array} selections Array of currently selected nodes
16137 "beforeselect" : true,
16139 * @event preparedata
16140 * Fires on every row to render, to allow you to change the data.
16141 * @param {Roo.View} this
16142 * @param {Object} data to be rendered (change this)
16144 "preparedata" : true
16152 "click": this.onClick,
16153 "dblclick": this.onDblClick,
16154 "contextmenu": this.onContextMenu,
16158 this.selections = [];
16160 this.cmp = new Roo.CompositeElementLite([]);
16162 this.store = Roo.factory(this.store, Roo.data);
16163 this.setStore(this.store, true);
16166 if ( this.footer && this.footer.xtype) {
16168 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16170 this.footer.dataSource = this.store;
16171 this.footer.container = fctr;
16172 this.footer = Roo.factory(this.footer, Roo);
16173 fctr.insertFirst(this.el);
16175 // this is a bit insane - as the paging toolbar seems to detach the el..
16176 // dom.parentNode.parentNode.parentNode
16177 // they get detached?
16181 Roo.View.superclass.constructor.call(this);
16186 Roo.extend(Roo.View, Roo.util.Observable, {
16189 * @cfg {Roo.data.Store} store Data store to load data from.
16194 * @cfg {String|Roo.Element} el The container element.
16199 * @cfg {String|Roo.Template} tpl The template used by this View
16203 * @cfg {String} dataName the named area of the template to use as the data area
16204 * Works with domtemplates roo-name="name"
16208 * @cfg {String} selectedClass The css class to add to selected nodes
16210 selectedClass : "x-view-selected",
16212 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16217 * @cfg {String} text to display on mask (default Loading)
16221 * @cfg {Boolean} multiSelect Allow multiple selection
16223 multiSelect : false,
16225 * @cfg {Boolean} singleSelect Allow single selection
16227 singleSelect: false,
16230 * @cfg {Boolean} toggleSelect - selecting
16232 toggleSelect : false,
16235 * @cfg {Boolean} tickable - selecting
16240 * Returns the element this view is bound to.
16241 * @return {Roo.Element}
16243 getEl : function(){
16244 return this.wrapEl;
16250 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16252 refresh : function(){
16253 //Roo.log('refresh');
16256 // if we are using something like 'domtemplate', then
16257 // the what gets used is:
16258 // t.applySubtemplate(NAME, data, wrapping data..)
16259 // the outer template then get' applied with
16260 // the store 'extra data'
16261 // and the body get's added to the
16262 // roo-name="data" node?
16263 // <span class='roo-tpl-{name}'></span> ?????
16267 this.clearSelections();
16268 this.el.update("");
16270 var records = this.store.getRange();
16271 if(records.length < 1) {
16273 // is this valid?? = should it render a template??
16275 this.el.update(this.emptyText);
16279 if (this.dataName) {
16280 this.el.update(t.apply(this.store.meta)); //????
16281 el = this.el.child('.roo-tpl-' + this.dataName);
16284 for(var i = 0, len = records.length; i < len; i++){
16285 var data = this.prepareData(records[i].data, i, records[i]);
16286 this.fireEvent("preparedata", this, data, i, records[i]);
16288 var d = Roo.apply({}, data);
16291 Roo.apply(d, {'roo-id' : Roo.id()});
16295 Roo.each(this.parent.item, function(item){
16296 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16299 Roo.apply(d, {'roo-data-checked' : 'checked'});
16303 html[html.length] = Roo.util.Format.trim(
16305 t.applySubtemplate(this.dataName, d, this.store.meta) :
16312 el.update(html.join(""));
16313 this.nodes = el.dom.childNodes;
16314 this.updateIndexes(0);
16319 * Function to override to reformat the data that is sent to
16320 * the template for each node.
16321 * DEPRICATED - use the preparedata event handler.
16322 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16323 * a JSON object for an UpdateManager bound view).
16325 prepareData : function(data, index, record)
16327 this.fireEvent("preparedata", this, data, index, record);
16331 onUpdate : function(ds, record){
16332 // Roo.log('on update');
16333 this.clearSelections();
16334 var index = this.store.indexOf(record);
16335 var n = this.nodes[index];
16336 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16337 n.parentNode.removeChild(n);
16338 this.updateIndexes(index, index);
16344 onAdd : function(ds, records, index)
16346 //Roo.log(['on Add', ds, records, index] );
16347 this.clearSelections();
16348 if(this.nodes.length == 0){
16352 var n = this.nodes[index];
16353 for(var i = 0, len = records.length; i < len; i++){
16354 var d = this.prepareData(records[i].data, i, records[i]);
16356 this.tpl.insertBefore(n, d);
16359 this.tpl.append(this.el, d);
16362 this.updateIndexes(index);
16365 onRemove : function(ds, record, index){
16366 // Roo.log('onRemove');
16367 this.clearSelections();
16368 var el = this.dataName ?
16369 this.el.child('.roo-tpl-' + this.dataName) :
16372 el.dom.removeChild(this.nodes[index]);
16373 this.updateIndexes(index);
16377 * Refresh an individual node.
16378 * @param {Number} index
16380 refreshNode : function(index){
16381 this.onUpdate(this.store, this.store.getAt(index));
16384 updateIndexes : function(startIndex, endIndex){
16385 var ns = this.nodes;
16386 startIndex = startIndex || 0;
16387 endIndex = endIndex || ns.length - 1;
16388 for(var i = startIndex; i <= endIndex; i++){
16389 ns[i].nodeIndex = i;
16394 * Changes the data store this view uses and refresh the view.
16395 * @param {Store} store
16397 setStore : function(store, initial){
16398 if(!initial && this.store){
16399 this.store.un("datachanged", this.refresh);
16400 this.store.un("add", this.onAdd);
16401 this.store.un("remove", this.onRemove);
16402 this.store.un("update", this.onUpdate);
16403 this.store.un("clear", this.refresh);
16404 this.store.un("beforeload", this.onBeforeLoad);
16405 this.store.un("load", this.onLoad);
16406 this.store.un("loadexception", this.onLoad);
16410 store.on("datachanged", this.refresh, this);
16411 store.on("add", this.onAdd, this);
16412 store.on("remove", this.onRemove, this);
16413 store.on("update", this.onUpdate, this);
16414 store.on("clear", this.refresh, this);
16415 store.on("beforeload", this.onBeforeLoad, this);
16416 store.on("load", this.onLoad, this);
16417 store.on("loadexception", this.onLoad, this);
16425 * onbeforeLoad - masks the loading area.
16428 onBeforeLoad : function(store,opts)
16430 //Roo.log('onBeforeLoad');
16432 this.el.update("");
16434 this.el.mask(this.mask ? this.mask : "Loading" );
16436 onLoad : function ()
16443 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16444 * @param {HTMLElement} node
16445 * @return {HTMLElement} The template node
16447 findItemFromChild : function(node){
16448 var el = this.dataName ?
16449 this.el.child('.roo-tpl-' + this.dataName,true) :
16452 if(!node || node.parentNode == el){
16455 var p = node.parentNode;
16456 while(p && p != el){
16457 if(p.parentNode == el){
16466 onClick : function(e){
16467 var item = this.findItemFromChild(e.getTarget());
16469 var index = this.indexOf(item);
16470 if(this.onItemClick(item, index, e) !== false){
16471 this.fireEvent("click", this, index, item, e);
16474 this.clearSelections();
16479 onContextMenu : function(e){
16480 var item = this.findItemFromChild(e.getTarget());
16482 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16487 onDblClick : function(e){
16488 var item = this.findItemFromChild(e.getTarget());
16490 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16494 onItemClick : function(item, index, e)
16496 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16499 if (this.toggleSelect) {
16500 var m = this.isSelected(item) ? 'unselect' : 'select';
16503 _t[m](item, true, false);
16506 if(this.multiSelect || this.singleSelect){
16507 if(this.multiSelect && e.shiftKey && this.lastSelection){
16508 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16510 this.select(item, this.multiSelect && e.ctrlKey);
16511 this.lastSelection = item;
16514 if(!this.tickable){
16515 e.preventDefault();
16523 * Get the number of selected nodes.
16526 getSelectionCount : function(){
16527 return this.selections.length;
16531 * Get the currently selected nodes.
16532 * @return {Array} An array of HTMLElements
16534 getSelectedNodes : function(){
16535 return this.selections;
16539 * Get the indexes of the selected nodes.
16542 getSelectedIndexes : function(){
16543 var indexes = [], s = this.selections;
16544 for(var i = 0, len = s.length; i < len; i++){
16545 indexes.push(s[i].nodeIndex);
16551 * Clear all selections
16552 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16554 clearSelections : function(suppressEvent){
16555 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16556 this.cmp.elements = this.selections;
16557 this.cmp.removeClass(this.selectedClass);
16558 this.selections = [];
16559 if(!suppressEvent){
16560 this.fireEvent("selectionchange", this, this.selections);
16566 * Returns true if the passed node is selected
16567 * @param {HTMLElement/Number} node The node or node index
16568 * @return {Boolean}
16570 isSelected : function(node){
16571 var s = this.selections;
16575 node = this.getNode(node);
16576 return s.indexOf(node) !== -1;
16581 * @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
16582 * @param {Boolean} keepExisting (optional) true to keep existing selections
16583 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16585 select : function(nodeInfo, keepExisting, suppressEvent){
16586 if(nodeInfo instanceof Array){
16588 this.clearSelections(true);
16590 for(var i = 0, len = nodeInfo.length; i < len; i++){
16591 this.select(nodeInfo[i], true, true);
16595 var node = this.getNode(nodeInfo);
16596 if(!node || this.isSelected(node)){
16597 return; // already selected.
16600 this.clearSelections(true);
16603 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16604 Roo.fly(node).addClass(this.selectedClass);
16605 this.selections.push(node);
16606 if(!suppressEvent){
16607 this.fireEvent("selectionchange", this, this.selections);
16615 * @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
16616 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16617 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16619 unselect : function(nodeInfo, keepExisting, suppressEvent)
16621 if(nodeInfo instanceof Array){
16622 Roo.each(this.selections, function(s) {
16623 this.unselect(s, nodeInfo);
16627 var node = this.getNode(nodeInfo);
16628 if(!node || !this.isSelected(node)){
16629 //Roo.log("not selected");
16630 return; // not selected.
16634 Roo.each(this.selections, function(s) {
16636 Roo.fly(node).removeClass(this.selectedClass);
16643 this.selections= ns;
16644 this.fireEvent("selectionchange", this, this.selections);
16648 * Gets a template node.
16649 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16650 * @return {HTMLElement} The node or null if it wasn't found
16652 getNode : function(nodeInfo){
16653 if(typeof nodeInfo == "string"){
16654 return document.getElementById(nodeInfo);
16655 }else if(typeof nodeInfo == "number"){
16656 return this.nodes[nodeInfo];
16662 * Gets a range template nodes.
16663 * @param {Number} startIndex
16664 * @param {Number} endIndex
16665 * @return {Array} An array of nodes
16667 getNodes : function(start, end){
16668 var ns = this.nodes;
16669 start = start || 0;
16670 end = typeof end == "undefined" ? ns.length - 1 : end;
16673 for(var i = start; i <= end; i++){
16677 for(var i = start; i >= end; i--){
16685 * Finds the index of the passed node
16686 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16687 * @return {Number} The index of the node or -1
16689 indexOf : function(node){
16690 node = this.getNode(node);
16691 if(typeof node.nodeIndex == "number"){
16692 return node.nodeIndex;
16694 var ns = this.nodes;
16695 for(var i = 0, len = ns.length; i < len; i++){
16706 * based on jquery fullcalendar
16710 Roo.bootstrap = Roo.bootstrap || {};
16712 * @class Roo.bootstrap.Calendar
16713 * @extends Roo.bootstrap.Component
16714 * Bootstrap Calendar class
16715 * @cfg {Boolean} loadMask (true|false) default false
16716 * @cfg {Object} header generate the user specific header of the calendar, default false
16719 * Create a new Container
16720 * @param {Object} config The config object
16725 Roo.bootstrap.Calendar = function(config){
16726 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16730 * Fires when a date is selected
16731 * @param {DatePicker} this
16732 * @param {Date} date The selected date
16736 * @event monthchange
16737 * Fires when the displayed month changes
16738 * @param {DatePicker} this
16739 * @param {Date} date The selected month
16741 'monthchange': true,
16743 * @event evententer
16744 * Fires when mouse over an event
16745 * @param {Calendar} this
16746 * @param {event} Event
16748 'evententer': true,
16750 * @event eventleave
16751 * Fires when the mouse leaves an
16752 * @param {Calendar} this
16755 'eventleave': true,
16757 * @event eventclick
16758 * Fires when the mouse click an
16759 * @param {Calendar} this
16768 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16771 * @cfg {Number} startDay
16772 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16780 getAutoCreate : function(){
16783 var fc_button = function(name, corner, style, content ) {
16784 return Roo.apply({},{
16786 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16788 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16791 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16802 style : 'width:100%',
16809 cls : 'fc-header-left',
16811 fc_button('prev', 'left', 'arrow', '‹' ),
16812 fc_button('next', 'right', 'arrow', '›' ),
16813 { tag: 'span', cls: 'fc-header-space' },
16814 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16822 cls : 'fc-header-center',
16826 cls: 'fc-header-title',
16829 html : 'month / year'
16837 cls : 'fc-header-right',
16839 /* fc_button('month', 'left', '', 'month' ),
16840 fc_button('week', '', '', 'week' ),
16841 fc_button('day', 'right', '', 'day' )
16853 header = this.header;
16856 var cal_heads = function() {
16858 // fixme - handle this.
16860 for (var i =0; i < Date.dayNames.length; i++) {
16861 var d = Date.dayNames[i];
16864 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16865 html : d.substring(0,3)
16869 ret[0].cls += ' fc-first';
16870 ret[6].cls += ' fc-last';
16873 var cal_cell = function(n) {
16876 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16881 cls: 'fc-day-number',
16885 cls: 'fc-day-content',
16889 style: 'position: relative;' // height: 17px;
16901 var cal_rows = function() {
16904 for (var r = 0; r < 6; r++) {
16911 for (var i =0; i < Date.dayNames.length; i++) {
16912 var d = Date.dayNames[i];
16913 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16916 row.cn[0].cls+=' fc-first';
16917 row.cn[0].cn[0].style = 'min-height:90px';
16918 row.cn[6].cls+=' fc-last';
16922 ret[0].cls += ' fc-first';
16923 ret[4].cls += ' fc-prev-last';
16924 ret[5].cls += ' fc-last';
16931 cls: 'fc-border-separate',
16932 style : 'width:100%',
16940 cls : 'fc-first fc-last',
16958 cls : 'fc-content',
16959 style : "position: relative;",
16962 cls : 'fc-view fc-view-month fc-grid',
16963 style : 'position: relative',
16964 unselectable : 'on',
16967 cls : 'fc-event-container',
16968 style : 'position:absolute;z-index:8;top:0;left:0;'
16986 initEvents : function()
16989 throw "can not find store for calendar";
16995 style: "text-align:center",
16999 style: "background-color:white;width:50%;margin:250 auto",
17003 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
17014 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17016 var size = this.el.select('.fc-content', true).first().getSize();
17017 this.maskEl.setSize(size.width, size.height);
17018 this.maskEl.enableDisplayMode("block");
17019 if(!this.loadMask){
17020 this.maskEl.hide();
17023 this.store = Roo.factory(this.store, Roo.data);
17024 this.store.on('load', this.onLoad, this);
17025 this.store.on('beforeload', this.onBeforeLoad, this);
17029 this.cells = this.el.select('.fc-day',true);
17030 //Roo.log(this.cells);
17031 this.textNodes = this.el.query('.fc-day-number');
17032 this.cells.addClassOnOver('fc-state-hover');
17034 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17035 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17036 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17037 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17039 this.on('monthchange', this.onMonthChange, this);
17041 this.update(new Date().clearTime());
17044 resize : function() {
17045 var sz = this.el.getSize();
17047 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17048 this.el.select('.fc-day-content div',true).setHeight(34);
17053 showPrevMonth : function(e){
17054 this.update(this.activeDate.add("mo", -1));
17056 showToday : function(e){
17057 this.update(new Date().clearTime());
17060 showNextMonth : function(e){
17061 this.update(this.activeDate.add("mo", 1));
17065 showPrevYear : function(){
17066 this.update(this.activeDate.add("y", -1));
17070 showNextYear : function(){
17071 this.update(this.activeDate.add("y", 1));
17076 update : function(date)
17078 var vd = this.activeDate;
17079 this.activeDate = date;
17080 // if(vd && this.el){
17081 // var t = date.getTime();
17082 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17083 // Roo.log('using add remove');
17085 // this.fireEvent('monthchange', this, date);
17087 // this.cells.removeClass("fc-state-highlight");
17088 // this.cells.each(function(c){
17089 // if(c.dateValue == t){
17090 // c.addClass("fc-state-highlight");
17091 // setTimeout(function(){
17092 // try{c.dom.firstChild.focus();}catch(e){}
17102 var days = date.getDaysInMonth();
17104 var firstOfMonth = date.getFirstDateOfMonth();
17105 var startingPos = firstOfMonth.getDay()-this.startDay;
17107 if(startingPos < this.startDay){
17111 var pm = date.add(Date.MONTH, -1);
17112 var prevStart = pm.getDaysInMonth()-startingPos;
17114 this.cells = this.el.select('.fc-day',true);
17115 this.textNodes = this.el.query('.fc-day-number');
17116 this.cells.addClassOnOver('fc-state-hover');
17118 var cells = this.cells.elements;
17119 var textEls = this.textNodes;
17121 Roo.each(cells, function(cell){
17122 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17125 days += startingPos;
17127 // convert everything to numbers so it's fast
17128 var day = 86400000;
17129 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17132 //Roo.log(prevStart);
17134 var today = new Date().clearTime().getTime();
17135 var sel = date.clearTime().getTime();
17136 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17137 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17138 var ddMatch = this.disabledDatesRE;
17139 var ddText = this.disabledDatesText;
17140 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17141 var ddaysText = this.disabledDaysText;
17142 var format = this.format;
17144 var setCellClass = function(cal, cell){
17148 //Roo.log('set Cell Class');
17150 var t = d.getTime();
17154 cell.dateValue = t;
17156 cell.className += " fc-today";
17157 cell.className += " fc-state-highlight";
17158 cell.title = cal.todayText;
17161 // disable highlight in other month..
17162 //cell.className += " fc-state-highlight";
17167 cell.className = " fc-state-disabled";
17168 cell.title = cal.minText;
17172 cell.className = " fc-state-disabled";
17173 cell.title = cal.maxText;
17177 if(ddays.indexOf(d.getDay()) != -1){
17178 cell.title = ddaysText;
17179 cell.className = " fc-state-disabled";
17182 if(ddMatch && format){
17183 var fvalue = d.dateFormat(format);
17184 if(ddMatch.test(fvalue)){
17185 cell.title = ddText.replace("%0", fvalue);
17186 cell.className = " fc-state-disabled";
17190 if (!cell.initialClassName) {
17191 cell.initialClassName = cell.dom.className;
17194 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17199 for(; i < startingPos; i++) {
17200 textEls[i].innerHTML = (++prevStart);
17201 d.setDate(d.getDate()+1);
17203 cells[i].className = "fc-past fc-other-month";
17204 setCellClass(this, cells[i]);
17209 for(; i < days; i++){
17210 intDay = i - startingPos + 1;
17211 textEls[i].innerHTML = (intDay);
17212 d.setDate(d.getDate()+1);
17214 cells[i].className = ''; // "x-date-active";
17215 setCellClass(this, cells[i]);
17219 for(; i < 42; i++) {
17220 textEls[i].innerHTML = (++extraDays);
17221 d.setDate(d.getDate()+1);
17223 cells[i].className = "fc-future fc-other-month";
17224 setCellClass(this, cells[i]);
17227 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17229 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17231 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17232 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17234 if(totalRows != 6){
17235 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17236 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17239 this.fireEvent('monthchange', this, date);
17243 if(!this.internalRender){
17244 var main = this.el.dom.firstChild;
17245 var w = main.offsetWidth;
17246 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17247 Roo.fly(main).setWidth(w);
17248 this.internalRender = true;
17249 // opera does not respect the auto grow header center column
17250 // then, after it gets a width opera refuses to recalculate
17251 // without a second pass
17252 if(Roo.isOpera && !this.secondPass){
17253 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17254 this.secondPass = true;
17255 this.update.defer(10, this, [date]);
17262 findCell : function(dt) {
17263 dt = dt.clearTime().getTime();
17265 this.cells.each(function(c){
17266 //Roo.log("check " +c.dateValue + '?=' + dt);
17267 if(c.dateValue == dt){
17277 findCells : function(ev) {
17278 var s = ev.start.clone().clearTime().getTime();
17280 var e= ev.end.clone().clearTime().getTime();
17283 this.cells.each(function(c){
17284 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17286 if(c.dateValue > e){
17289 if(c.dateValue < s){
17298 // findBestRow: function(cells)
17302 // for (var i =0 ; i < cells.length;i++) {
17303 // ret = Math.max(cells[i].rows || 0,ret);
17310 addItem : function(ev)
17312 // look for vertical location slot in
17313 var cells = this.findCells(ev);
17315 // ev.row = this.findBestRow(cells);
17317 // work out the location.
17321 for(var i =0; i < cells.length; i++) {
17323 cells[i].row = cells[0].row;
17326 cells[i].row = cells[i].row + 1;
17336 if (crow.start.getY() == cells[i].getY()) {
17338 crow.end = cells[i];
17355 cells[0].events.push(ev);
17357 this.calevents.push(ev);
17360 clearEvents: function() {
17362 if(!this.calevents){
17366 Roo.each(this.cells.elements, function(c){
17372 Roo.each(this.calevents, function(e) {
17373 Roo.each(e.els, function(el) {
17374 el.un('mouseenter' ,this.onEventEnter, this);
17375 el.un('mouseleave' ,this.onEventLeave, this);
17380 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17386 renderEvents: function()
17390 this.cells.each(function(c) {
17399 if(c.row != c.events.length){
17400 r = 4 - (4 - (c.row - c.events.length));
17403 c.events = ev.slice(0, r);
17404 c.more = ev.slice(r);
17406 if(c.more.length && c.more.length == 1){
17407 c.events.push(c.more.pop());
17410 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17414 this.cells.each(function(c) {
17416 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17419 for (var e = 0; e < c.events.length; e++){
17420 var ev = c.events[e];
17421 var rows = ev.rows;
17423 for(var i = 0; i < rows.length; i++) {
17425 // how many rows should it span..
17428 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17429 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17431 unselectable : "on",
17434 cls: 'fc-event-inner',
17438 // cls: 'fc-event-time',
17439 // html : cells.length > 1 ? '' : ev.time
17443 cls: 'fc-event-title',
17444 html : String.format('{0}', ev.title)
17451 cls: 'ui-resizable-handle ui-resizable-e',
17452 html : '  '
17459 cfg.cls += ' fc-event-start';
17461 if ((i+1) == rows.length) {
17462 cfg.cls += ' fc-event-end';
17465 var ctr = _this.el.select('.fc-event-container',true).first();
17466 var cg = ctr.createChild(cfg);
17468 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17469 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17471 var r = (c.more.length) ? 1 : 0;
17472 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17473 cg.setWidth(ebox.right - sbox.x -2);
17475 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17476 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17477 cg.on('click', _this.onEventClick, _this, ev);
17488 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17489 style : 'position: absolute',
17490 unselectable : "on",
17493 cls: 'fc-event-inner',
17497 cls: 'fc-event-title',
17505 cls: 'ui-resizable-handle ui-resizable-e',
17506 html : '  '
17512 var ctr = _this.el.select('.fc-event-container',true).first();
17513 var cg = ctr.createChild(cfg);
17515 var sbox = c.select('.fc-day-content',true).first().getBox();
17516 var ebox = c.select('.fc-day-content',true).first().getBox();
17518 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17519 cg.setWidth(ebox.right - sbox.x -2);
17521 cg.on('click', _this.onMoreEventClick, _this, c.more);
17531 onEventEnter: function (e, el,event,d) {
17532 this.fireEvent('evententer', this, el, event);
17535 onEventLeave: function (e, el,event,d) {
17536 this.fireEvent('eventleave', this, el, event);
17539 onEventClick: function (e, el,event,d) {
17540 this.fireEvent('eventclick', this, el, event);
17543 onMonthChange: function () {
17547 onMoreEventClick: function(e, el, more)
17551 this.calpopover.placement = 'right';
17552 this.calpopover.setTitle('More');
17554 this.calpopover.setContent('');
17556 var ctr = this.calpopover.el.select('.popover-content', true).first();
17558 Roo.each(more, function(m){
17560 cls : 'fc-event-hori fc-event-draggable',
17563 var cg = ctr.createChild(cfg);
17565 cg.on('click', _this.onEventClick, _this, m);
17568 this.calpopover.show(el);
17573 onLoad: function ()
17575 this.calevents = [];
17578 if(this.store.getCount() > 0){
17579 this.store.data.each(function(d){
17582 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17583 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17584 time : d.data.start_time,
17585 title : d.data.title,
17586 description : d.data.description,
17587 venue : d.data.venue
17592 this.renderEvents();
17594 if(this.calevents.length && this.loadMask){
17595 this.maskEl.hide();
17599 onBeforeLoad: function()
17601 this.clearEvents();
17603 this.maskEl.show();
17617 * @class Roo.bootstrap.Popover
17618 * @extends Roo.bootstrap.Component
17619 * Bootstrap Popover class
17620 * @cfg {String} html contents of the popover (or false to use children..)
17621 * @cfg {String} title of popover (or false to hide)
17622 * @cfg {String} placement how it is placed
17623 * @cfg {String} trigger click || hover (or false to trigger manually)
17624 * @cfg {String} over what (parent or false to trigger manually.)
17625 * @cfg {Number} delay - delay before showing
17628 * Create a new Popover
17629 * @param {Object} config The config object
17632 Roo.bootstrap.Popover = function(config){
17633 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17639 * After the popover show
17641 * @param {Roo.bootstrap.Popover} this
17646 * After the popover hide
17648 * @param {Roo.bootstrap.Popover} this
17654 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17656 title: 'Fill in a title',
17659 placement : 'right',
17660 trigger : 'hover', // hover
17666 can_build_overlaid : false,
17668 getChildContainer : function()
17670 return this.el.select('.popover-content',true).first();
17673 getAutoCreate : function(){
17676 cls : 'popover roo-dynamic',
17677 style: 'display:block',
17683 cls : 'popover-inner',
17687 cls: 'popover-title popover-header',
17691 cls : 'popover-content popover-body',
17702 setTitle: function(str)
17705 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17707 setContent: function(str)
17710 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17712 // as it get's added to the bottom of the page.
17713 onRender : function(ct, position)
17715 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17717 var cfg = Roo.apply({}, this.getAutoCreate());
17721 cfg.cls += ' ' + this.cls;
17724 cfg.style = this.style;
17726 //Roo.log("adding to ");
17727 this.el = Roo.get(document.body).createChild(cfg, position);
17728 // Roo.log(this.el);
17733 initEvents : function()
17735 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17736 this.el.enableDisplayMode('block');
17738 if (this.over === false) {
17741 if (this.triggers === false) {
17744 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17745 var triggers = this.trigger ? this.trigger.split(' ') : [];
17746 Roo.each(triggers, function(trigger) {
17748 if (trigger == 'click') {
17749 on_el.on('click', this.toggle, this);
17750 } else if (trigger != 'manual') {
17751 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17752 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17754 on_el.on(eventIn ,this.enter, this);
17755 on_el.on(eventOut, this.leave, this);
17766 toggle : function () {
17767 this.hoverState == 'in' ? this.leave() : this.enter();
17770 enter : function () {
17772 clearTimeout(this.timeout);
17774 this.hoverState = 'in';
17776 if (!this.delay || !this.delay.show) {
17781 this.timeout = setTimeout(function () {
17782 if (_t.hoverState == 'in') {
17785 }, this.delay.show)
17788 leave : function() {
17789 clearTimeout(this.timeout);
17791 this.hoverState = 'out';
17793 if (!this.delay || !this.delay.hide) {
17798 this.timeout = setTimeout(function () {
17799 if (_t.hoverState == 'out') {
17802 }, this.delay.hide)
17805 show : function (on_el)
17808 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17812 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17813 if (this.html !== false) {
17814 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17816 this.el.removeClass([
17817 'fade','top','bottom', 'left', 'right','in',
17818 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17820 if (!this.title.length) {
17821 this.el.select('.popover-title',true).hide();
17824 var placement = typeof this.placement == 'function' ?
17825 this.placement.call(this, this.el, on_el) :
17828 var autoToken = /\s?auto?\s?/i;
17829 var autoPlace = autoToken.test(placement);
17831 placement = placement.replace(autoToken, '') || 'top';
17835 //this.el.setXY([0,0]);
17837 this.el.dom.style.display='block';
17838 this.el.addClass(placement);
17840 //this.el.appendTo(on_el);
17842 var p = this.getPosition();
17843 var box = this.el.getBox();
17848 var align = Roo.bootstrap.Popover.alignment[placement];
17851 this.el.alignTo(on_el, align[0],align[1]);
17852 //var arrow = this.el.select('.arrow',true).first();
17853 //arrow.set(align[2],
17855 this.el.addClass('in');
17858 if (this.el.hasClass('fade')) {
17862 this.hoverState = 'in';
17864 this.fireEvent('show', this);
17869 this.el.setXY([0,0]);
17870 this.el.removeClass('in');
17872 this.hoverState = null;
17874 this.fireEvent('hide', this);
17879 Roo.bootstrap.Popover.alignment = {
17880 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17881 'right' : ['l-r', [10,0], 'left bs-popover-left'],
17882 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17883 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17894 * @class Roo.bootstrap.Progress
17895 * @extends Roo.bootstrap.Component
17896 * Bootstrap Progress class
17897 * @cfg {Boolean} striped striped of the progress bar
17898 * @cfg {Boolean} active animated of the progress bar
17902 * Create a new Progress
17903 * @param {Object} config The config object
17906 Roo.bootstrap.Progress = function(config){
17907 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17910 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17915 getAutoCreate : function(){
17923 cfg.cls += ' progress-striped';
17927 cfg.cls += ' active';
17946 * @class Roo.bootstrap.ProgressBar
17947 * @extends Roo.bootstrap.Component
17948 * Bootstrap ProgressBar class
17949 * @cfg {Number} aria_valuenow aria-value now
17950 * @cfg {Number} aria_valuemin aria-value min
17951 * @cfg {Number} aria_valuemax aria-value max
17952 * @cfg {String} label label for the progress bar
17953 * @cfg {String} panel (success | info | warning | danger )
17954 * @cfg {String} role role of the progress bar
17955 * @cfg {String} sr_only text
17959 * Create a new ProgressBar
17960 * @param {Object} config The config object
17963 Roo.bootstrap.ProgressBar = function(config){
17964 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17967 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17971 aria_valuemax : 100,
17977 getAutoCreate : function()
17982 cls: 'progress-bar',
17983 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17995 cfg.role = this.role;
17998 if(this.aria_valuenow){
17999 cfg['aria-valuenow'] = this.aria_valuenow;
18002 if(this.aria_valuemin){
18003 cfg['aria-valuemin'] = this.aria_valuemin;
18006 if(this.aria_valuemax){
18007 cfg['aria-valuemax'] = this.aria_valuemax;
18010 if(this.label && !this.sr_only){
18011 cfg.html = this.label;
18015 cfg.cls += ' progress-bar-' + this.panel;
18021 update : function(aria_valuenow)
18023 this.aria_valuenow = aria_valuenow;
18025 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18040 * @class Roo.bootstrap.TabGroup
18041 * @extends Roo.bootstrap.Column
18042 * Bootstrap Column class
18043 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18044 * @cfg {Boolean} carousel true to make the group behave like a carousel
18045 * @cfg {Boolean} bullets show bullets for the panels
18046 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18047 * @cfg {Number} timer auto slide timer .. default 0 millisecond
18048 * @cfg {Boolean} showarrow (true|false) show arrow default true
18051 * Create a new TabGroup
18052 * @param {Object} config The config object
18055 Roo.bootstrap.TabGroup = function(config){
18056 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18058 this.navId = Roo.id();
18061 Roo.bootstrap.TabGroup.register(this);
18065 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18068 transition : false,
18073 slideOnTouch : false,
18076 getAutoCreate : function()
18078 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18080 cfg.cls += ' tab-content';
18082 if (this.carousel) {
18083 cfg.cls += ' carousel slide';
18086 cls : 'carousel-inner',
18090 if(this.bullets && !Roo.isTouch){
18093 cls : 'carousel-bullets',
18097 if(this.bullets_cls){
18098 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18105 cfg.cn[0].cn.push(bullets);
18108 if(this.showarrow){
18109 cfg.cn[0].cn.push({
18111 class : 'carousel-arrow',
18115 class : 'carousel-prev',
18119 class : 'fa fa-chevron-left'
18125 class : 'carousel-next',
18129 class : 'fa fa-chevron-right'
18142 initEvents: function()
18144 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18145 // this.el.on("touchstart", this.onTouchStart, this);
18148 if(this.autoslide){
18151 this.slideFn = window.setInterval(function() {
18152 _this.showPanelNext();
18156 if(this.showarrow){
18157 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18158 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18164 // onTouchStart : function(e, el, o)
18166 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18170 // this.showPanelNext();
18174 getChildContainer : function()
18176 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18180 * register a Navigation item
18181 * @param {Roo.bootstrap.NavItem} the navitem to add
18183 register : function(item)
18185 this.tabs.push( item);
18186 item.navId = this.navId; // not really needed..
18191 getActivePanel : function()
18194 Roo.each(this.tabs, function(t) {
18204 getPanelByName : function(n)
18207 Roo.each(this.tabs, function(t) {
18208 if (t.tabId == n) {
18216 indexOfPanel : function(p)
18219 Roo.each(this.tabs, function(t,i) {
18220 if (t.tabId == p.tabId) {
18229 * show a specific panel
18230 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18231 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18233 showPanel : function (pan)
18235 if(this.transition || typeof(pan) == 'undefined'){
18236 Roo.log("waiting for the transitionend");
18240 if (typeof(pan) == 'number') {
18241 pan = this.tabs[pan];
18244 if (typeof(pan) == 'string') {
18245 pan = this.getPanelByName(pan);
18248 var cur = this.getActivePanel();
18251 Roo.log('pan or acitve pan is undefined');
18255 if (pan.tabId == this.getActivePanel().tabId) {
18259 if (false === cur.fireEvent('beforedeactivate')) {
18263 if(this.bullets > 0 && !Roo.isTouch){
18264 this.setActiveBullet(this.indexOfPanel(pan));
18267 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18269 this.transition = true;
18270 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18271 var lr = dir == 'next' ? 'left' : 'right';
18272 pan.el.addClass(dir); // or prev
18273 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18274 cur.el.addClass(lr); // or right
18275 pan.el.addClass(lr);
18278 cur.el.on('transitionend', function() {
18279 Roo.log("trans end?");
18281 pan.el.removeClass([lr,dir]);
18282 pan.setActive(true);
18284 cur.el.removeClass([lr]);
18285 cur.setActive(false);
18287 _this.transition = false;
18289 }, this, { single: true } );
18294 cur.setActive(false);
18295 pan.setActive(true);
18300 showPanelNext : function()
18302 var i = this.indexOfPanel(this.getActivePanel());
18304 if (i >= this.tabs.length - 1 && !this.autoslide) {
18308 if (i >= this.tabs.length - 1 && this.autoslide) {
18312 this.showPanel(this.tabs[i+1]);
18315 showPanelPrev : function()
18317 var i = this.indexOfPanel(this.getActivePanel());
18319 if (i < 1 && !this.autoslide) {
18323 if (i < 1 && this.autoslide) {
18324 i = this.tabs.length;
18327 this.showPanel(this.tabs[i-1]);
18331 addBullet: function()
18333 if(!this.bullets || Roo.isTouch){
18336 var ctr = this.el.select('.carousel-bullets',true).first();
18337 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18338 var bullet = ctr.createChild({
18339 cls : 'bullet bullet-' + i
18340 },ctr.dom.lastChild);
18345 bullet.on('click', (function(e, el, o, ii, t){
18347 e.preventDefault();
18349 this.showPanel(ii);
18351 if(this.autoslide && this.slideFn){
18352 clearInterval(this.slideFn);
18353 this.slideFn = window.setInterval(function() {
18354 _this.showPanelNext();
18358 }).createDelegate(this, [i, bullet], true));
18363 setActiveBullet : function(i)
18369 Roo.each(this.el.select('.bullet', true).elements, function(el){
18370 el.removeClass('selected');
18373 var bullet = this.el.select('.bullet-' + i, true).first();
18379 bullet.addClass('selected');
18390 Roo.apply(Roo.bootstrap.TabGroup, {
18394 * register a Navigation Group
18395 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18397 register : function(navgrp)
18399 this.groups[navgrp.navId] = navgrp;
18403 * fetch a Navigation Group based on the navigation ID
18404 * if one does not exist , it will get created.
18405 * @param {string} the navgroup to add
18406 * @returns {Roo.bootstrap.NavGroup} the navgroup
18408 get: function(navId) {
18409 if (typeof(this.groups[navId]) == 'undefined') {
18410 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18412 return this.groups[navId] ;
18427 * @class Roo.bootstrap.TabPanel
18428 * @extends Roo.bootstrap.Component
18429 * Bootstrap TabPanel class
18430 * @cfg {Boolean} active panel active
18431 * @cfg {String} html panel content
18432 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18433 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18434 * @cfg {String} href click to link..
18438 * Create a new TabPanel
18439 * @param {Object} config The config object
18442 Roo.bootstrap.TabPanel = function(config){
18443 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18447 * Fires when the active status changes
18448 * @param {Roo.bootstrap.TabPanel} this
18449 * @param {Boolean} state the new state
18454 * @event beforedeactivate
18455 * Fires before a tab is de-activated - can be used to do validation on a form.
18456 * @param {Roo.bootstrap.TabPanel} this
18457 * @return {Boolean} false if there is an error
18460 'beforedeactivate': true
18463 this.tabId = this.tabId || Roo.id();
18467 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18475 getAutoCreate : function(){
18478 // item is needed for carousel - not sure if it has any effect otherwise
18479 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18480 html: this.html || ''
18484 cfg.cls += ' active';
18488 cfg.tabId = this.tabId;
18495 initEvents: function()
18497 var p = this.parent();
18499 this.navId = this.navId || p.navId;
18501 if (typeof(this.navId) != 'undefined') {
18502 // not really needed.. but just in case.. parent should be a NavGroup.
18503 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18507 var i = tg.tabs.length - 1;
18509 if(this.active && tg.bullets > 0 && i < tg.bullets){
18510 tg.setActiveBullet(i);
18514 this.el.on('click', this.onClick, this);
18517 this.el.on("touchstart", this.onTouchStart, this);
18518 this.el.on("touchmove", this.onTouchMove, this);
18519 this.el.on("touchend", this.onTouchEnd, this);
18524 onRender : function(ct, position)
18526 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18529 setActive : function(state)
18531 Roo.log("panel - set active " + this.tabId + "=" + state);
18533 this.active = state;
18535 this.el.removeClass('active');
18537 } else if (!this.el.hasClass('active')) {
18538 this.el.addClass('active');
18541 this.fireEvent('changed', this, state);
18544 onClick : function(e)
18546 e.preventDefault();
18548 if(!this.href.length){
18552 window.location.href = this.href;
18561 onTouchStart : function(e)
18563 this.swiping = false;
18565 this.startX = e.browserEvent.touches[0].clientX;
18566 this.startY = e.browserEvent.touches[0].clientY;
18569 onTouchMove : function(e)
18571 this.swiping = true;
18573 this.endX = e.browserEvent.touches[0].clientX;
18574 this.endY = e.browserEvent.touches[0].clientY;
18577 onTouchEnd : function(e)
18584 var tabGroup = this.parent();
18586 if(this.endX > this.startX){ // swiping right
18587 tabGroup.showPanelPrev();
18591 if(this.startX > this.endX){ // swiping left
18592 tabGroup.showPanelNext();
18611 * @class Roo.bootstrap.DateField
18612 * @extends Roo.bootstrap.Input
18613 * Bootstrap DateField class
18614 * @cfg {Number} weekStart default 0
18615 * @cfg {String} viewMode default empty, (months|years)
18616 * @cfg {String} minViewMode default empty, (months|years)
18617 * @cfg {Number} startDate default -Infinity
18618 * @cfg {Number} endDate default Infinity
18619 * @cfg {Boolean} todayHighlight default false
18620 * @cfg {Boolean} todayBtn default false
18621 * @cfg {Boolean} calendarWeeks default false
18622 * @cfg {Object} daysOfWeekDisabled default empty
18623 * @cfg {Boolean} singleMode default false (true | false)
18625 * @cfg {Boolean} keyboardNavigation default true
18626 * @cfg {String} language default en
18629 * Create a new DateField
18630 * @param {Object} config The config object
18633 Roo.bootstrap.DateField = function(config){
18634 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18638 * Fires when this field show.
18639 * @param {Roo.bootstrap.DateField} this
18640 * @param {Mixed} date The date value
18645 * Fires when this field hide.
18646 * @param {Roo.bootstrap.DateField} this
18647 * @param {Mixed} date The date value
18652 * Fires when select a date.
18653 * @param {Roo.bootstrap.DateField} this
18654 * @param {Mixed} date The date value
18658 * @event beforeselect
18659 * Fires when before select a date.
18660 * @param {Roo.bootstrap.DateField} this
18661 * @param {Mixed} date The date value
18663 beforeselect : true
18667 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18670 * @cfg {String} format
18671 * The default date format string which can be overriden for localization support. The format must be
18672 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18676 * @cfg {String} altFormats
18677 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18678 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18680 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18688 todayHighlight : false,
18694 keyboardNavigation: true,
18696 calendarWeeks: false,
18698 startDate: -Infinity,
18702 daysOfWeekDisabled: [],
18706 singleMode : false,
18708 UTCDate: function()
18710 return new Date(Date.UTC.apply(Date, arguments));
18713 UTCToday: function()
18715 var today = new Date();
18716 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18719 getDate: function() {
18720 var d = this.getUTCDate();
18721 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18724 getUTCDate: function() {
18728 setDate: function(d) {
18729 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18732 setUTCDate: function(d) {
18734 this.setValue(this.formatDate(this.date));
18737 onRender: function(ct, position)
18740 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18742 this.language = this.language || 'en';
18743 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18744 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18746 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18747 this.format = this.format || 'm/d/y';
18748 this.isInline = false;
18749 this.isInput = true;
18750 this.component = this.el.select('.add-on', true).first() || false;
18751 this.component = (this.component && this.component.length === 0) ? false : this.component;
18752 this.hasInput = this.component && this.inputEl().length;
18754 if (typeof(this.minViewMode === 'string')) {
18755 switch (this.minViewMode) {
18757 this.minViewMode = 1;
18760 this.minViewMode = 2;
18763 this.minViewMode = 0;
18768 if (typeof(this.viewMode === 'string')) {
18769 switch (this.viewMode) {
18782 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18784 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18786 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18788 this.picker().on('mousedown', this.onMousedown, this);
18789 this.picker().on('click', this.onClick, this);
18791 this.picker().addClass('datepicker-dropdown');
18793 this.startViewMode = this.viewMode;
18795 if(this.singleMode){
18796 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18797 v.setVisibilityMode(Roo.Element.DISPLAY);
18801 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18802 v.setStyle('width', '189px');
18806 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18807 if(!this.calendarWeeks){
18812 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18813 v.attr('colspan', function(i, val){
18814 return parseInt(val) + 1;
18819 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18821 this.setStartDate(this.startDate);
18822 this.setEndDate(this.endDate);
18824 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18831 if(this.isInline) {
18836 picker : function()
18838 return this.pickerEl;
18839 // return this.el.select('.datepicker', true).first();
18842 fillDow: function()
18844 var dowCnt = this.weekStart;
18853 if(this.calendarWeeks){
18861 while (dowCnt < this.weekStart + 7) {
18865 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18869 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18872 fillMonths: function()
18875 var months = this.picker().select('>.datepicker-months td', true).first();
18877 months.dom.innerHTML = '';
18883 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18886 months.createChild(month);
18893 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;
18895 if (this.date < this.startDate) {
18896 this.viewDate = new Date(this.startDate);
18897 } else if (this.date > this.endDate) {
18898 this.viewDate = new Date(this.endDate);
18900 this.viewDate = new Date(this.date);
18908 var d = new Date(this.viewDate),
18909 year = d.getUTCFullYear(),
18910 month = d.getUTCMonth(),
18911 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18912 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18913 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18914 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18915 currentDate = this.date && this.date.valueOf(),
18916 today = this.UTCToday();
18918 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18920 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18922 // this.picker.select('>tfoot th.today').
18923 // .text(dates[this.language].today)
18924 // .toggle(this.todayBtn !== false);
18926 this.updateNavArrows();
18929 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18931 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18933 prevMonth.setUTCDate(day);
18935 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18937 var nextMonth = new Date(prevMonth);
18939 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18941 nextMonth = nextMonth.valueOf();
18943 var fillMonths = false;
18945 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18947 while(prevMonth.valueOf() <= nextMonth) {
18950 if (prevMonth.getUTCDay() === this.weekStart) {
18952 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18960 if(this.calendarWeeks){
18961 // ISO 8601: First week contains first thursday.
18962 // ISO also states week starts on Monday, but we can be more abstract here.
18964 // Start of current week: based on weekstart/current date
18965 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18966 // Thursday of this week
18967 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18968 // First Thursday of year, year from thursday
18969 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18970 // Calendar week: ms between thursdays, div ms per day, div 7 days
18971 calWeek = (th - yth) / 864e5 / 7 + 1;
18973 fillMonths.cn.push({
18981 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18983 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18986 if (this.todayHighlight &&
18987 prevMonth.getUTCFullYear() == today.getFullYear() &&
18988 prevMonth.getUTCMonth() == today.getMonth() &&
18989 prevMonth.getUTCDate() == today.getDate()) {
18990 clsName += ' today';
18993 if (currentDate && prevMonth.valueOf() === currentDate) {
18994 clsName += ' active';
18997 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18998 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18999 clsName += ' disabled';
19002 fillMonths.cn.push({
19004 cls: 'day ' + clsName,
19005 html: prevMonth.getDate()
19008 prevMonth.setDate(prevMonth.getDate()+1);
19011 var currentYear = this.date && this.date.getUTCFullYear();
19012 var currentMonth = this.date && this.date.getUTCMonth();
19014 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19016 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19017 v.removeClass('active');
19019 if(currentYear === year && k === currentMonth){
19020 v.addClass('active');
19023 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19024 v.addClass('disabled');
19030 year = parseInt(year/10, 10) * 10;
19032 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19034 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19037 for (var i = -1; i < 11; i++) {
19038 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19040 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19048 showMode: function(dir)
19051 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19054 Roo.each(this.picker().select('>div',true).elements, function(v){
19055 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19058 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19063 if(this.isInline) {
19067 this.picker().removeClass(['bottom', 'top']);
19069 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19071 * place to the top of element!
19075 this.picker().addClass('top');
19076 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19081 this.picker().addClass('bottom');
19083 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19086 parseDate : function(value)
19088 if(!value || value instanceof Date){
19091 var v = Date.parseDate(value, this.format);
19092 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19093 v = Date.parseDate(value, 'Y-m-d');
19095 if(!v && this.altFormats){
19096 if(!this.altFormatsArray){
19097 this.altFormatsArray = this.altFormats.split("|");
19099 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19100 v = Date.parseDate(value, this.altFormatsArray[i]);
19106 formatDate : function(date, fmt)
19108 return (!date || !(date instanceof Date)) ?
19109 date : date.dateFormat(fmt || this.format);
19112 onFocus : function()
19114 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19118 onBlur : function()
19120 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19122 var d = this.inputEl().getValue();
19129 showPopup : function()
19131 this.picker().show();
19135 this.fireEvent('showpopup', this, this.date);
19138 hidePopup : function()
19140 if(this.isInline) {
19143 this.picker().hide();
19144 this.viewMode = this.startViewMode;
19147 this.fireEvent('hidepopup', this, this.date);
19151 onMousedown: function(e)
19153 e.stopPropagation();
19154 e.preventDefault();
19159 Roo.bootstrap.DateField.superclass.keyup.call(this);
19163 setValue: function(v)
19165 if(this.fireEvent('beforeselect', this, v) !== false){
19166 var d = new Date(this.parseDate(v) ).clearTime();
19168 if(isNaN(d.getTime())){
19169 this.date = this.viewDate = '';
19170 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19174 v = this.formatDate(d);
19176 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19178 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19182 this.fireEvent('select', this, this.date);
19186 getValue: function()
19188 return this.formatDate(this.date);
19191 fireKey: function(e)
19193 if (!this.picker().isVisible()){
19194 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19200 var dateChanged = false,
19202 newDate, newViewDate;
19207 e.preventDefault();
19211 if (!this.keyboardNavigation) {
19214 dir = e.keyCode == 37 ? -1 : 1;
19217 newDate = this.moveYear(this.date, dir);
19218 newViewDate = this.moveYear(this.viewDate, dir);
19219 } else if (e.shiftKey){
19220 newDate = this.moveMonth(this.date, dir);
19221 newViewDate = this.moveMonth(this.viewDate, dir);
19223 newDate = new Date(this.date);
19224 newDate.setUTCDate(this.date.getUTCDate() + dir);
19225 newViewDate = new Date(this.viewDate);
19226 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19228 if (this.dateWithinRange(newDate)){
19229 this.date = newDate;
19230 this.viewDate = newViewDate;
19231 this.setValue(this.formatDate(this.date));
19233 e.preventDefault();
19234 dateChanged = true;
19239 if (!this.keyboardNavigation) {
19242 dir = e.keyCode == 38 ? -1 : 1;
19244 newDate = this.moveYear(this.date, dir);
19245 newViewDate = this.moveYear(this.viewDate, dir);
19246 } else if (e.shiftKey){
19247 newDate = this.moveMonth(this.date, dir);
19248 newViewDate = this.moveMonth(this.viewDate, dir);
19250 newDate = new Date(this.date);
19251 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19252 newViewDate = new Date(this.viewDate);
19253 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19255 if (this.dateWithinRange(newDate)){
19256 this.date = newDate;
19257 this.viewDate = newViewDate;
19258 this.setValue(this.formatDate(this.date));
19260 e.preventDefault();
19261 dateChanged = true;
19265 this.setValue(this.formatDate(this.date));
19267 e.preventDefault();
19270 this.setValue(this.formatDate(this.date));
19284 onClick: function(e)
19286 e.stopPropagation();
19287 e.preventDefault();
19289 var target = e.getTarget();
19291 if(target.nodeName.toLowerCase() === 'i'){
19292 target = Roo.get(target).dom.parentNode;
19295 var nodeName = target.nodeName;
19296 var className = target.className;
19297 var html = target.innerHTML;
19298 //Roo.log(nodeName);
19300 switch(nodeName.toLowerCase()) {
19302 switch(className) {
19308 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19309 switch(this.viewMode){
19311 this.viewDate = this.moveMonth(this.viewDate, dir);
19315 this.viewDate = this.moveYear(this.viewDate, dir);
19321 var date = new Date();
19322 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19324 this.setValue(this.formatDate(this.date));
19331 if (className.indexOf('disabled') < 0) {
19332 this.viewDate.setUTCDate(1);
19333 if (className.indexOf('month') > -1) {
19334 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19336 var year = parseInt(html, 10) || 0;
19337 this.viewDate.setUTCFullYear(year);
19341 if(this.singleMode){
19342 this.setValue(this.formatDate(this.viewDate));
19353 //Roo.log(className);
19354 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19355 var day = parseInt(html, 10) || 1;
19356 var year = this.viewDate.getUTCFullYear(),
19357 month = this.viewDate.getUTCMonth();
19359 if (className.indexOf('old') > -1) {
19366 } else if (className.indexOf('new') > -1) {
19374 //Roo.log([year,month,day]);
19375 this.date = this.UTCDate(year, month, day,0,0,0,0);
19376 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19378 //Roo.log(this.formatDate(this.date));
19379 this.setValue(this.formatDate(this.date));
19386 setStartDate: function(startDate)
19388 this.startDate = startDate || -Infinity;
19389 if (this.startDate !== -Infinity) {
19390 this.startDate = this.parseDate(this.startDate);
19393 this.updateNavArrows();
19396 setEndDate: function(endDate)
19398 this.endDate = endDate || Infinity;
19399 if (this.endDate !== Infinity) {
19400 this.endDate = this.parseDate(this.endDate);
19403 this.updateNavArrows();
19406 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19408 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19409 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19410 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19412 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19413 return parseInt(d, 10);
19416 this.updateNavArrows();
19419 updateNavArrows: function()
19421 if(this.singleMode){
19425 var d = new Date(this.viewDate),
19426 year = d.getUTCFullYear(),
19427 month = d.getUTCMonth();
19429 Roo.each(this.picker().select('.prev', true).elements, function(v){
19431 switch (this.viewMode) {
19434 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19440 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19447 Roo.each(this.picker().select('.next', true).elements, function(v){
19449 switch (this.viewMode) {
19452 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19458 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19466 moveMonth: function(date, dir)
19471 var new_date = new Date(date.valueOf()),
19472 day = new_date.getUTCDate(),
19473 month = new_date.getUTCMonth(),
19474 mag = Math.abs(dir),
19476 dir = dir > 0 ? 1 : -1;
19479 // If going back one month, make sure month is not current month
19480 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19482 return new_date.getUTCMonth() == month;
19484 // If going forward one month, make sure month is as expected
19485 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19487 return new_date.getUTCMonth() != new_month;
19489 new_month = month + dir;
19490 new_date.setUTCMonth(new_month);
19491 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19492 if (new_month < 0 || new_month > 11) {
19493 new_month = (new_month + 12) % 12;
19496 // For magnitudes >1, move one month at a time...
19497 for (var i=0; i<mag; i++) {
19498 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19499 new_date = this.moveMonth(new_date, dir);
19501 // ...then reset the day, keeping it in the new month
19502 new_month = new_date.getUTCMonth();
19503 new_date.setUTCDate(day);
19505 return new_month != new_date.getUTCMonth();
19508 // Common date-resetting loop -- if date is beyond end of month, make it
19511 new_date.setUTCDate(--day);
19512 new_date.setUTCMonth(new_month);
19517 moveYear: function(date, dir)
19519 return this.moveMonth(date, dir*12);
19522 dateWithinRange: function(date)
19524 return date >= this.startDate && date <= this.endDate;
19530 this.picker().remove();
19533 validateValue : function(value)
19535 if(this.getVisibilityEl().hasClass('hidden')){
19539 if(value.length < 1) {
19540 if(this.allowBlank){
19546 if(value.length < this.minLength){
19549 if(value.length > this.maxLength){
19553 var vt = Roo.form.VTypes;
19554 if(!vt[this.vtype](value, this)){
19558 if(typeof this.validator == "function"){
19559 var msg = this.validator(value);
19565 if(this.regex && !this.regex.test(value)){
19569 if(typeof(this.parseDate(value)) == 'undefined'){
19573 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19577 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19587 this.date = this.viewDate = '';
19589 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19594 Roo.apply(Roo.bootstrap.DateField, {
19605 html: '<i class="fa fa-arrow-left"/>'
19615 html: '<i class="fa fa-arrow-right"/>'
19657 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19658 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19659 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19660 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19661 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19674 navFnc: 'FullYear',
19679 navFnc: 'FullYear',
19684 Roo.apply(Roo.bootstrap.DateField, {
19688 cls: 'datepicker dropdown-menu roo-dynamic',
19692 cls: 'datepicker-days',
19696 cls: 'table-condensed',
19698 Roo.bootstrap.DateField.head,
19702 Roo.bootstrap.DateField.footer
19709 cls: 'datepicker-months',
19713 cls: 'table-condensed',
19715 Roo.bootstrap.DateField.head,
19716 Roo.bootstrap.DateField.content,
19717 Roo.bootstrap.DateField.footer
19724 cls: 'datepicker-years',
19728 cls: 'table-condensed',
19730 Roo.bootstrap.DateField.head,
19731 Roo.bootstrap.DateField.content,
19732 Roo.bootstrap.DateField.footer
19751 * @class Roo.bootstrap.TimeField
19752 * @extends Roo.bootstrap.Input
19753 * Bootstrap DateField class
19757 * Create a new TimeField
19758 * @param {Object} config The config object
19761 Roo.bootstrap.TimeField = function(config){
19762 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19766 * Fires when this field show.
19767 * @param {Roo.bootstrap.DateField} thisthis
19768 * @param {Mixed} date The date value
19773 * Fires when this field hide.
19774 * @param {Roo.bootstrap.DateField} this
19775 * @param {Mixed} date The date value
19780 * Fires when select a date.
19781 * @param {Roo.bootstrap.DateField} this
19782 * @param {Mixed} date The date value
19788 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19791 * @cfg {String} format
19792 * The default time format string which can be overriden for localization support. The format must be
19793 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19797 onRender: function(ct, position)
19800 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19802 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19804 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19806 this.pop = this.picker().select('>.datepicker-time',true).first();
19807 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19809 this.picker().on('mousedown', this.onMousedown, this);
19810 this.picker().on('click', this.onClick, this);
19812 this.picker().addClass('datepicker-dropdown');
19817 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19818 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19819 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19820 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19821 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19822 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19826 fireKey: function(e){
19827 if (!this.picker().isVisible()){
19828 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19834 e.preventDefault();
19842 this.onTogglePeriod();
19845 this.onIncrementMinutes();
19848 this.onDecrementMinutes();
19857 onClick: function(e) {
19858 e.stopPropagation();
19859 e.preventDefault();
19862 picker : function()
19864 return this.el.select('.datepicker', true).first();
19867 fillTime: function()
19869 var time = this.pop.select('tbody', true).first();
19871 time.dom.innerHTML = '';
19886 cls: 'hours-up glyphicon glyphicon-chevron-up'
19906 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19927 cls: 'timepicker-hour',
19942 cls: 'timepicker-minute',
19957 cls: 'btn btn-primary period',
19979 cls: 'hours-down glyphicon glyphicon-chevron-down'
19999 cls: 'minutes-down glyphicon glyphicon-chevron-down'
20017 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20024 var hours = this.time.getHours();
20025 var minutes = this.time.getMinutes();
20038 hours = hours - 12;
20042 hours = '0' + hours;
20046 minutes = '0' + minutes;
20049 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20050 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20051 this.pop.select('button', true).first().dom.innerHTML = period;
20057 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20059 var cls = ['bottom'];
20061 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20068 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20073 this.picker().addClass(cls.join('-'));
20077 Roo.each(cls, function(c){
20079 _this.picker().setTop(_this.inputEl().getHeight());
20083 _this.picker().setTop(0 - _this.picker().getHeight());
20088 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20092 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20099 onFocus : function()
20101 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20105 onBlur : function()
20107 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20113 this.picker().show();
20118 this.fireEvent('show', this, this.date);
20123 this.picker().hide();
20126 this.fireEvent('hide', this, this.date);
20129 setTime : function()
20132 this.setValue(this.time.format(this.format));
20134 this.fireEvent('select', this, this.date);
20139 onMousedown: function(e){
20140 e.stopPropagation();
20141 e.preventDefault();
20144 onIncrementHours: function()
20146 Roo.log('onIncrementHours');
20147 this.time = this.time.add(Date.HOUR, 1);
20152 onDecrementHours: function()
20154 Roo.log('onDecrementHours');
20155 this.time = this.time.add(Date.HOUR, -1);
20159 onIncrementMinutes: function()
20161 Roo.log('onIncrementMinutes');
20162 this.time = this.time.add(Date.MINUTE, 1);
20166 onDecrementMinutes: function()
20168 Roo.log('onDecrementMinutes');
20169 this.time = this.time.add(Date.MINUTE, -1);
20173 onTogglePeriod: function()
20175 Roo.log('onTogglePeriod');
20176 this.time = this.time.add(Date.HOUR, 12);
20183 Roo.apply(Roo.bootstrap.TimeField, {
20213 cls: 'btn btn-info ok',
20225 Roo.apply(Roo.bootstrap.TimeField, {
20229 cls: 'datepicker dropdown-menu',
20233 cls: 'datepicker-time',
20237 cls: 'table-condensed',
20239 Roo.bootstrap.TimeField.content,
20240 Roo.bootstrap.TimeField.footer
20259 * @class Roo.bootstrap.MonthField
20260 * @extends Roo.bootstrap.Input
20261 * Bootstrap MonthField class
20263 * @cfg {String} language default en
20266 * Create a new MonthField
20267 * @param {Object} config The config object
20270 Roo.bootstrap.MonthField = function(config){
20271 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20276 * Fires when this field show.
20277 * @param {Roo.bootstrap.MonthField} this
20278 * @param {Mixed} date The date value
20283 * Fires when this field hide.
20284 * @param {Roo.bootstrap.MonthField} this
20285 * @param {Mixed} date The date value
20290 * Fires when select a date.
20291 * @param {Roo.bootstrap.MonthField} this
20292 * @param {String} oldvalue The old value
20293 * @param {String} newvalue The new value
20299 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20301 onRender: function(ct, position)
20304 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20306 this.language = this.language || 'en';
20307 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20308 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20310 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20311 this.isInline = false;
20312 this.isInput = true;
20313 this.component = this.el.select('.add-on', true).first() || false;
20314 this.component = (this.component && this.component.length === 0) ? false : this.component;
20315 this.hasInput = this.component && this.inputEL().length;
20317 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20319 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20321 this.picker().on('mousedown', this.onMousedown, this);
20322 this.picker().on('click', this.onClick, this);
20324 this.picker().addClass('datepicker-dropdown');
20326 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20327 v.setStyle('width', '189px');
20334 if(this.isInline) {
20340 setValue: function(v, suppressEvent)
20342 var o = this.getValue();
20344 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20348 if(suppressEvent !== true){
20349 this.fireEvent('select', this, o, v);
20354 getValue: function()
20359 onClick: function(e)
20361 e.stopPropagation();
20362 e.preventDefault();
20364 var target = e.getTarget();
20366 if(target.nodeName.toLowerCase() === 'i'){
20367 target = Roo.get(target).dom.parentNode;
20370 var nodeName = target.nodeName;
20371 var className = target.className;
20372 var html = target.innerHTML;
20374 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20378 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20380 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20386 picker : function()
20388 return this.pickerEl;
20391 fillMonths: function()
20394 var months = this.picker().select('>.datepicker-months td', true).first();
20396 months.dom.innerHTML = '';
20402 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20405 months.createChild(month);
20414 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20415 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20418 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20419 e.removeClass('active');
20421 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20422 e.addClass('active');
20429 if(this.isInline) {
20433 this.picker().removeClass(['bottom', 'top']);
20435 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20437 * place to the top of element!
20441 this.picker().addClass('top');
20442 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20447 this.picker().addClass('bottom');
20449 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20452 onFocus : function()
20454 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20458 onBlur : function()
20460 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20462 var d = this.inputEl().getValue();
20471 this.picker().show();
20472 this.picker().select('>.datepicker-months', true).first().show();
20476 this.fireEvent('show', this, this.date);
20481 if(this.isInline) {
20484 this.picker().hide();
20485 this.fireEvent('hide', this, this.date);
20489 onMousedown: function(e)
20491 e.stopPropagation();
20492 e.preventDefault();
20497 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20501 fireKey: function(e)
20503 if (!this.picker().isVisible()){
20504 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20515 e.preventDefault();
20519 dir = e.keyCode == 37 ? -1 : 1;
20521 this.vIndex = this.vIndex + dir;
20523 if(this.vIndex < 0){
20527 if(this.vIndex > 11){
20531 if(isNaN(this.vIndex)){
20535 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20541 dir = e.keyCode == 38 ? -1 : 1;
20543 this.vIndex = this.vIndex + dir * 4;
20545 if(this.vIndex < 0){
20549 if(this.vIndex > 11){
20553 if(isNaN(this.vIndex)){
20557 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20562 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20563 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20567 e.preventDefault();
20570 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20571 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20587 this.picker().remove();
20592 Roo.apply(Roo.bootstrap.MonthField, {
20611 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20612 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20617 Roo.apply(Roo.bootstrap.MonthField, {
20621 cls: 'datepicker dropdown-menu roo-dynamic',
20625 cls: 'datepicker-months',
20629 cls: 'table-condensed',
20631 Roo.bootstrap.DateField.content
20651 * @class Roo.bootstrap.CheckBox
20652 * @extends Roo.bootstrap.Input
20653 * Bootstrap CheckBox class
20655 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20656 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20657 * @cfg {String} boxLabel The text that appears beside the checkbox
20658 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20659 * @cfg {Boolean} checked initnal the element
20660 * @cfg {Boolean} inline inline the element (default false)
20661 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20662 * @cfg {String} tooltip label tooltip
20665 * Create a new CheckBox
20666 * @param {Object} config The config object
20669 Roo.bootstrap.CheckBox = function(config){
20670 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20675 * Fires when the element is checked or unchecked.
20676 * @param {Roo.bootstrap.CheckBox} this This input
20677 * @param {Boolean} checked The new checked value
20682 * Fires when the element is click.
20683 * @param {Roo.bootstrap.CheckBox} this This input
20690 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20692 inputType: 'checkbox',
20701 getAutoCreate : function()
20703 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20709 cfg.cls = 'form-group ' + this.inputType; //input-group
20712 cfg.cls += ' ' + this.inputType + '-inline';
20718 type : this.inputType,
20719 value : this.inputValue,
20720 cls : 'roo-' + this.inputType, //'form-box',
20721 placeholder : this.placeholder || ''
20725 if(this.inputType != 'radio'){
20729 cls : 'roo-hidden-value',
20730 value : this.checked ? this.inputValue : this.valueOff
20735 if (this.weight) { // Validity check?
20736 cfg.cls += " " + this.inputType + "-" + this.weight;
20739 if (this.disabled) {
20740 input.disabled=true;
20744 input.checked = this.checked;
20749 input.name = this.name;
20751 if(this.inputType != 'radio'){
20752 hidden.name = this.name;
20753 input.name = '_hidden_' + this.name;
20758 input.cls += ' input-' + this.size;
20763 ['xs','sm','md','lg'].map(function(size){
20764 if (settings[size]) {
20765 cfg.cls += ' col-' + size + '-' + settings[size];
20769 var inputblock = input;
20771 if (this.before || this.after) {
20774 cls : 'input-group',
20779 inputblock.cn.push({
20781 cls : 'input-group-addon',
20786 inputblock.cn.push(input);
20788 if(this.inputType != 'radio'){
20789 inputblock.cn.push(hidden);
20793 inputblock.cn.push({
20795 cls : 'input-group-addon',
20802 if (align ==='left' && this.fieldLabel.length) {
20803 // Roo.log("left and has label");
20808 cls : 'control-label',
20809 html : this.fieldLabel
20819 if(this.labelWidth > 12){
20820 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20823 if(this.labelWidth < 13 && this.labelmd == 0){
20824 this.labelmd = this.labelWidth;
20827 if(this.labellg > 0){
20828 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20829 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20832 if(this.labelmd > 0){
20833 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20834 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20837 if(this.labelsm > 0){
20838 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20839 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20842 if(this.labelxs > 0){
20843 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20844 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20847 } else if ( this.fieldLabel.length) {
20848 // Roo.log(" label");
20852 tag: this.boxLabel ? 'span' : 'label',
20854 cls: 'control-label box-input-label',
20855 //cls : 'input-group-addon',
20856 html : this.fieldLabel
20865 // Roo.log(" no label && no align");
20866 cfg.cn = [ inputblock ] ;
20872 var boxLabelCfg = {
20874 //'for': id, // box label is handled by onclick - so no for...
20876 html: this.boxLabel
20880 boxLabelCfg.tooltip = this.tooltip;
20883 cfg.cn.push(boxLabelCfg);
20886 if(this.inputType != 'radio'){
20887 cfg.cn.push(hidden);
20895 * return the real input element.
20897 inputEl: function ()
20899 return this.el.select('input.roo-' + this.inputType,true).first();
20901 hiddenEl: function ()
20903 return this.el.select('input.roo-hidden-value',true).first();
20906 labelEl: function()
20908 return this.el.select('label.control-label',true).first();
20910 /* depricated... */
20914 return this.labelEl();
20917 boxLabelEl: function()
20919 return this.el.select('label.box-label',true).first();
20922 initEvents : function()
20924 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20926 this.inputEl().on('click', this.onClick, this);
20928 if (this.boxLabel) {
20929 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20932 this.startValue = this.getValue();
20935 Roo.bootstrap.CheckBox.register(this);
20939 onClick : function(e)
20941 if(this.fireEvent('click', this, e) !== false){
20942 this.setChecked(!this.checked);
20947 setChecked : function(state,suppressEvent)
20949 this.startValue = this.getValue();
20951 if(this.inputType == 'radio'){
20953 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20954 e.dom.checked = false;
20957 this.inputEl().dom.checked = true;
20959 this.inputEl().dom.value = this.inputValue;
20961 if(suppressEvent !== true){
20962 this.fireEvent('check', this, true);
20970 this.checked = state;
20972 this.inputEl().dom.checked = state;
20975 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20977 if(suppressEvent !== true){
20978 this.fireEvent('check', this, state);
20984 getValue : function()
20986 if(this.inputType == 'radio'){
20987 return this.getGroupValue();
20990 return this.hiddenEl().dom.value;
20994 getGroupValue : function()
20996 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21000 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21003 setValue : function(v,suppressEvent)
21005 if(this.inputType == 'radio'){
21006 this.setGroupValue(v, suppressEvent);
21010 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21015 setGroupValue : function(v, suppressEvent)
21017 this.startValue = this.getValue();
21019 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21020 e.dom.checked = false;
21022 if(e.dom.value == v){
21023 e.dom.checked = true;
21027 if(suppressEvent !== true){
21028 this.fireEvent('check', this, true);
21036 validate : function()
21038 if(this.getVisibilityEl().hasClass('hidden')){
21044 (this.inputType == 'radio' && this.validateRadio()) ||
21045 (this.inputType == 'checkbox' && this.validateCheckbox())
21051 this.markInvalid();
21055 validateRadio : function()
21057 if(this.getVisibilityEl().hasClass('hidden')){
21061 if(this.allowBlank){
21067 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21068 if(!e.dom.checked){
21080 validateCheckbox : function()
21083 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21084 //return (this.getValue() == this.inputValue) ? true : false;
21087 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21095 for(var i in group){
21096 if(group[i].el.isVisible(true)){
21104 for(var i in group){
21109 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21116 * Mark this field as valid
21118 markValid : function()
21122 this.fireEvent('valid', this);
21124 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21127 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21134 if(this.inputType == 'radio'){
21135 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21136 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21137 e.findParent('.form-group', false, true).addClass(_this.validClass);
21144 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21145 this.el.findParent('.form-group', false, true).addClass(this.validClass);
21149 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21155 for(var i in group){
21156 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21157 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21162 * Mark this field as invalid
21163 * @param {String} msg The validation message
21165 markInvalid : function(msg)
21167 if(this.allowBlank){
21173 this.fireEvent('invalid', this, msg);
21175 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21178 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21182 label.markInvalid();
21185 if(this.inputType == 'radio'){
21186 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21187 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21188 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21195 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21196 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21200 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21206 for(var i in group){
21207 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21208 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21213 clearInvalid : function()
21215 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21217 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21219 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21221 if (label && label.iconEl) {
21222 label.iconEl.removeClass(label.validClass);
21223 label.iconEl.removeClass(label.invalidClass);
21227 disable : function()
21229 if(this.inputType != 'radio'){
21230 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21237 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21238 _this.getActionEl().addClass(this.disabledClass);
21239 e.dom.disabled = true;
21243 this.disabled = true;
21244 this.fireEvent("disable", this);
21248 enable : function()
21250 if(this.inputType != 'radio'){
21251 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21258 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21259 _this.getActionEl().removeClass(this.disabledClass);
21260 e.dom.disabled = false;
21264 this.disabled = false;
21265 this.fireEvent("enable", this);
21269 setBoxLabel : function(v)
21274 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21280 Roo.apply(Roo.bootstrap.CheckBox, {
21285 * register a CheckBox Group
21286 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21288 register : function(checkbox)
21290 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21291 this.groups[checkbox.groupId] = {};
21294 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21298 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21302 * fetch a CheckBox Group based on the group ID
21303 * @param {string} the group ID
21304 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21306 get: function(groupId) {
21307 if (typeof(this.groups[groupId]) == 'undefined') {
21311 return this.groups[groupId] ;
21324 * @class Roo.bootstrap.Radio
21325 * @extends Roo.bootstrap.Component
21326 * Bootstrap Radio class
21327 * @cfg {String} boxLabel - the label associated
21328 * @cfg {String} value - the value of radio
21331 * Create a new Radio
21332 * @param {Object} config The config object
21334 Roo.bootstrap.Radio = function(config){
21335 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21339 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21345 getAutoCreate : function()
21349 cls : 'form-group radio',
21354 html : this.boxLabel
21362 initEvents : function()
21364 this.parent().register(this);
21366 this.el.on('click', this.onClick, this);
21370 onClick : function(e)
21372 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21373 this.setChecked(true);
21377 setChecked : function(state, suppressEvent)
21379 this.parent().setValue(this.value, suppressEvent);
21383 setBoxLabel : function(v)
21388 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21403 * @class Roo.bootstrap.SecurePass
21404 * @extends Roo.bootstrap.Input
21405 * Bootstrap SecurePass class
21409 * Create a new SecurePass
21410 * @param {Object} config The config object
21413 Roo.bootstrap.SecurePass = function (config) {
21414 // these go here, so the translation tool can replace them..
21416 PwdEmpty: "Please type a password, and then retype it to confirm.",
21417 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21418 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21419 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21420 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21421 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21422 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21423 TooWeak: "Your password is Too Weak."
21425 this.meterLabel = "Password strength:";
21426 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21427 this.meterClass = [
21428 "roo-password-meter-tooweak",
21429 "roo-password-meter-weak",
21430 "roo-password-meter-medium",
21431 "roo-password-meter-strong",
21432 "roo-password-meter-grey"
21437 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21440 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21442 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21444 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21445 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21446 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21447 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21448 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21449 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21450 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21460 * @cfg {String/Object} Label for the strength meter (defaults to
21461 * 'Password strength:')
21466 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21467 * ['Weak', 'Medium', 'Strong'])
21470 pwdStrengths: false,
21483 initEvents: function ()
21485 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21487 if (this.el.is('input[type=password]') && Roo.isSafari) {
21488 this.el.on('keydown', this.SafariOnKeyDown, this);
21491 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21494 onRender: function (ct, position)
21496 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21497 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21498 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21500 this.trigger.createChild({
21505 cls: 'roo-password-meter-grey col-xs-12',
21508 //width: this.meterWidth + 'px'
21512 cls: 'roo-password-meter-text'
21518 if (this.hideTrigger) {
21519 this.trigger.setDisplayed(false);
21521 this.setSize(this.width || '', this.height || '');
21524 onDestroy: function ()
21526 if (this.trigger) {
21527 this.trigger.removeAllListeners();
21528 this.trigger.remove();
21531 this.wrap.remove();
21533 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21536 checkStrength: function ()
21538 var pwd = this.inputEl().getValue();
21539 if (pwd == this._lastPwd) {
21544 if (this.ClientSideStrongPassword(pwd)) {
21546 } else if (this.ClientSideMediumPassword(pwd)) {
21548 } else if (this.ClientSideWeakPassword(pwd)) {
21554 Roo.log('strength1: ' + strength);
21556 //var pm = this.trigger.child('div/div/div').dom;
21557 var pm = this.trigger.child('div/div');
21558 pm.removeClass(this.meterClass);
21559 pm.addClass(this.meterClass[strength]);
21562 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21564 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21566 this._lastPwd = pwd;
21570 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21572 this._lastPwd = '';
21574 var pm = this.trigger.child('div/div');
21575 pm.removeClass(this.meterClass);
21576 pm.addClass('roo-password-meter-grey');
21579 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21582 this.inputEl().dom.type='password';
21585 validateValue: function (value)
21588 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21591 if (value.length == 0) {
21592 if (this.allowBlank) {
21593 this.clearInvalid();
21597 this.markInvalid(this.errors.PwdEmpty);
21598 this.errorMsg = this.errors.PwdEmpty;
21606 if ('[\x21-\x7e]*'.match(value)) {
21607 this.markInvalid(this.errors.PwdBadChar);
21608 this.errorMsg = this.errors.PwdBadChar;
21611 if (value.length < 6) {
21612 this.markInvalid(this.errors.PwdShort);
21613 this.errorMsg = this.errors.PwdShort;
21616 if (value.length > 16) {
21617 this.markInvalid(this.errors.PwdLong);
21618 this.errorMsg = this.errors.PwdLong;
21622 if (this.ClientSideStrongPassword(value)) {
21624 } else if (this.ClientSideMediumPassword(value)) {
21626 } else if (this.ClientSideWeakPassword(value)) {
21633 if (strength < 2) {
21634 //this.markInvalid(this.errors.TooWeak);
21635 this.errorMsg = this.errors.TooWeak;
21640 console.log('strength2: ' + strength);
21642 //var pm = this.trigger.child('div/div/div').dom;
21644 var pm = this.trigger.child('div/div');
21645 pm.removeClass(this.meterClass);
21646 pm.addClass(this.meterClass[strength]);
21648 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21650 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21652 this.errorMsg = '';
21656 CharacterSetChecks: function (type)
21659 this.fResult = false;
21662 isctype: function (character, type)
21665 case this.kCapitalLetter:
21666 if (character >= 'A' && character <= 'Z') {
21671 case this.kSmallLetter:
21672 if (character >= 'a' && character <= 'z') {
21678 if (character >= '0' && character <= '9') {
21683 case this.kPunctuation:
21684 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21695 IsLongEnough: function (pwd, size)
21697 return !(pwd == null || isNaN(size) || pwd.length < size);
21700 SpansEnoughCharacterSets: function (word, nb)
21702 if (!this.IsLongEnough(word, nb))
21707 var characterSetChecks = new Array(
21708 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21709 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21712 for (var index = 0; index < word.length; ++index) {
21713 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21714 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21715 characterSetChecks[nCharSet].fResult = true;
21722 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21723 if (characterSetChecks[nCharSet].fResult) {
21728 if (nCharSets < nb) {
21734 ClientSideStrongPassword: function (pwd)
21736 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21739 ClientSideMediumPassword: function (pwd)
21741 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21744 ClientSideWeakPassword: function (pwd)
21746 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21749 })//<script type="text/javascript">
21752 * Based Ext JS Library 1.1.1
21753 * Copyright(c) 2006-2007, Ext JS, LLC.
21759 * @class Roo.HtmlEditorCore
21760 * @extends Roo.Component
21761 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21763 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21766 Roo.HtmlEditorCore = function(config){
21769 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21774 * @event initialize
21775 * Fires when the editor is fully initialized (including the iframe)
21776 * @param {Roo.HtmlEditorCore} this
21781 * Fires when the editor is first receives the focus. Any insertion must wait
21782 * until after this event.
21783 * @param {Roo.HtmlEditorCore} this
21787 * @event beforesync
21788 * Fires before the textarea is updated with content from the editor iframe. Return false
21789 * to cancel the sync.
21790 * @param {Roo.HtmlEditorCore} this
21791 * @param {String} html
21795 * @event beforepush
21796 * Fires before the iframe editor is updated with content from the textarea. Return false
21797 * to cancel the push.
21798 * @param {Roo.HtmlEditorCore} this
21799 * @param {String} html
21804 * Fires when the textarea is updated with content from the editor iframe.
21805 * @param {Roo.HtmlEditorCore} this
21806 * @param {String} html
21811 * Fires when the iframe editor is updated with content from the textarea.
21812 * @param {Roo.HtmlEditorCore} this
21813 * @param {String} html
21818 * @event editorevent
21819 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21820 * @param {Roo.HtmlEditorCore} this
21826 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21828 // defaults : white / black...
21829 this.applyBlacklists();
21836 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21840 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21846 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21851 * @cfg {Number} height (in pixels)
21855 * @cfg {Number} width (in pixels)
21860 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21863 stylesheets: false,
21868 // private properties
21869 validationEvent : false,
21871 initialized : false,
21873 sourceEditMode : false,
21874 onFocus : Roo.emptyFn,
21876 hideMode:'offsets',
21880 // blacklist + whitelisted elements..
21887 * Protected method that will not generally be called directly. It
21888 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21889 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21891 getDocMarkup : function(){
21895 // inherit styels from page...??
21896 if (this.stylesheets === false) {
21898 Roo.get(document.head).select('style').each(function(node) {
21899 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21902 Roo.get(document.head).select('link').each(function(node) {
21903 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21906 } else if (!this.stylesheets.length) {
21908 st = '<style type="text/css">' +
21909 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21912 st = '<style type="text/css">' +
21917 st += '<style type="text/css">' +
21918 'IMG { cursor: pointer } ' +
21921 var cls = 'roo-htmleditor-body';
21923 if(this.bodyCls.length){
21924 cls += ' ' + this.bodyCls;
21927 return '<html><head>' + st +
21928 //<style type="text/css">' +
21929 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21931 ' </head><body class="' + cls + '"></body></html>';
21935 onRender : function(ct, position)
21938 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21939 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21942 this.el.dom.style.border = '0 none';
21943 this.el.dom.setAttribute('tabIndex', -1);
21944 this.el.addClass('x-hidden hide');
21948 if(Roo.isIE){ // fix IE 1px bogus margin
21949 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21953 this.frameId = Roo.id();
21957 var iframe = this.owner.wrap.createChild({
21959 cls: 'form-control', // bootstrap..
21961 name: this.frameId,
21962 frameBorder : 'no',
21963 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21968 this.iframe = iframe.dom;
21970 this.assignDocWin();
21972 this.doc.designMode = 'on';
21975 this.doc.write(this.getDocMarkup());
21979 var task = { // must defer to wait for browser to be ready
21981 //console.log("run task?" + this.doc.readyState);
21982 this.assignDocWin();
21983 if(this.doc.body || this.doc.readyState == 'complete'){
21985 this.doc.designMode="on";
21989 Roo.TaskMgr.stop(task);
21990 this.initEditor.defer(10, this);
21997 Roo.TaskMgr.start(task);
22002 onResize : function(w, h)
22004 Roo.log('resize: ' +w + ',' + h );
22005 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22009 if(typeof w == 'number'){
22011 this.iframe.style.width = w + 'px';
22013 if(typeof h == 'number'){
22015 this.iframe.style.height = h + 'px';
22017 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22024 * Toggles the editor between standard and source edit mode.
22025 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22027 toggleSourceEdit : function(sourceEditMode){
22029 this.sourceEditMode = sourceEditMode === true;
22031 if(this.sourceEditMode){
22033 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
22036 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22037 //this.iframe.className = '';
22040 //this.setSize(this.owner.wrap.getSize());
22041 //this.fireEvent('editmodechange', this, this.sourceEditMode);
22048 * Protected method that will not generally be called directly. If you need/want
22049 * custom HTML cleanup, this is the method you should override.
22050 * @param {String} html The HTML to be cleaned
22051 * return {String} The cleaned HTML
22053 cleanHtml : function(html){
22054 html = String(html);
22055 if(html.length > 5){
22056 if(Roo.isSafari){ // strip safari nonsense
22057 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22060 if(html == ' '){
22067 * HTML Editor -> Textarea
22068 * Protected method that will not generally be called directly. Syncs the contents
22069 * of the editor iframe with the textarea.
22071 syncValue : function(){
22072 if(this.initialized){
22073 var bd = (this.doc.body || this.doc.documentElement);
22074 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22075 var html = bd.innerHTML;
22077 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22078 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22080 html = '<div style="'+m[0]+'">' + html + '</div>';
22083 html = this.cleanHtml(html);
22084 // fix up the special chars.. normaly like back quotes in word...
22085 // however we do not want to do this with chinese..
22086 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22087 var cc = b.charCodeAt();
22089 (cc >= 0x4E00 && cc < 0xA000 ) ||
22090 (cc >= 0x3400 && cc < 0x4E00 ) ||
22091 (cc >= 0xf900 && cc < 0xfb00 )
22097 if(this.owner.fireEvent('beforesync', this, html) !== false){
22098 this.el.dom.value = html;
22099 this.owner.fireEvent('sync', this, html);
22105 * Protected method that will not generally be called directly. Pushes the value of the textarea
22106 * into the iframe editor.
22108 pushValue : function(){
22109 if(this.initialized){
22110 var v = this.el.dom.value.trim();
22112 // if(v.length < 1){
22116 if(this.owner.fireEvent('beforepush', this, v) !== false){
22117 var d = (this.doc.body || this.doc.documentElement);
22119 this.cleanUpPaste();
22120 this.el.dom.value = d.innerHTML;
22121 this.owner.fireEvent('push', this, v);
22127 deferFocus : function(){
22128 this.focus.defer(10, this);
22132 focus : function(){
22133 if(this.win && !this.sourceEditMode){
22140 assignDocWin: function()
22142 var iframe = this.iframe;
22145 this.doc = iframe.contentWindow.document;
22146 this.win = iframe.contentWindow;
22148 // if (!Roo.get(this.frameId)) {
22151 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22152 // this.win = Roo.get(this.frameId).dom.contentWindow;
22154 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22158 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22159 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22164 initEditor : function(){
22165 //console.log("INIT EDITOR");
22166 this.assignDocWin();
22170 this.doc.designMode="on";
22172 this.doc.write(this.getDocMarkup());
22175 var dbody = (this.doc.body || this.doc.documentElement);
22176 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22177 // this copies styles from the containing element into thsi one..
22178 // not sure why we need all of this..
22179 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22181 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22182 //ss['background-attachment'] = 'fixed'; // w3c
22183 dbody.bgProperties = 'fixed'; // ie
22184 //Roo.DomHelper.applyStyles(dbody, ss);
22185 Roo.EventManager.on(this.doc, {
22186 //'mousedown': this.onEditorEvent,
22187 'mouseup': this.onEditorEvent,
22188 'dblclick': this.onEditorEvent,
22189 'click': this.onEditorEvent,
22190 'keyup': this.onEditorEvent,
22195 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22197 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22198 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22200 this.initialized = true;
22202 this.owner.fireEvent('initialize', this);
22207 onDestroy : function(){
22213 //for (var i =0; i < this.toolbars.length;i++) {
22214 // // fixme - ask toolbars for heights?
22215 // this.toolbars[i].onDestroy();
22218 //this.wrap.dom.innerHTML = '';
22219 //this.wrap.remove();
22224 onFirstFocus : function(){
22226 this.assignDocWin();
22229 this.activated = true;
22232 if(Roo.isGecko){ // prevent silly gecko errors
22234 var s = this.win.getSelection();
22235 if(!s.focusNode || s.focusNode.nodeType != 3){
22236 var r = s.getRangeAt(0);
22237 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22242 this.execCmd('useCSS', true);
22243 this.execCmd('styleWithCSS', false);
22246 this.owner.fireEvent('activate', this);
22250 adjustFont: function(btn){
22251 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22252 //if(Roo.isSafari){ // safari
22255 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22256 if(Roo.isSafari){ // safari
22257 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22258 v = (v < 10) ? 10 : v;
22259 v = (v > 48) ? 48 : v;
22260 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22265 v = Math.max(1, v+adjust);
22267 this.execCmd('FontSize', v );
22270 onEditorEvent : function(e)
22272 this.owner.fireEvent('editorevent', this, e);
22273 // this.updateToolbar();
22274 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22277 insertTag : function(tg)
22279 // could be a bit smarter... -> wrap the current selected tRoo..
22280 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22282 range = this.createRange(this.getSelection());
22283 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22284 wrappingNode.appendChild(range.extractContents());
22285 range.insertNode(wrappingNode);
22292 this.execCmd("formatblock", tg);
22296 insertText : function(txt)
22300 var range = this.createRange();
22301 range.deleteContents();
22302 //alert(Sender.getAttribute('label'));
22304 range.insertNode(this.doc.createTextNode(txt));
22310 * Executes a Midas editor command on the editor document and performs necessary focus and
22311 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22312 * @param {String} cmd The Midas command
22313 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22315 relayCmd : function(cmd, value){
22317 this.execCmd(cmd, value);
22318 this.owner.fireEvent('editorevent', this);
22319 //this.updateToolbar();
22320 this.owner.deferFocus();
22324 * Executes a Midas editor command directly on the editor document.
22325 * For visual commands, you should use {@link #relayCmd} instead.
22326 * <b>This should only be called after the editor is initialized.</b>
22327 * @param {String} cmd The Midas command
22328 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22330 execCmd : function(cmd, value){
22331 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22338 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22340 * @param {String} text | dom node..
22342 insertAtCursor : function(text)
22345 if(!this.activated){
22351 var r = this.doc.selection.createRange();
22362 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22366 // from jquery ui (MIT licenced)
22368 var win = this.win;
22370 if (win.getSelection && win.getSelection().getRangeAt) {
22371 range = win.getSelection().getRangeAt(0);
22372 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22373 range.insertNode(node);
22374 } else if (win.document.selection && win.document.selection.createRange) {
22375 // no firefox support
22376 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22377 win.document.selection.createRange().pasteHTML(txt);
22379 // no firefox support
22380 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22381 this.execCmd('InsertHTML', txt);
22390 mozKeyPress : function(e){
22392 var c = e.getCharCode(), cmd;
22395 c = String.fromCharCode(c).toLowerCase();
22409 this.cleanUpPaste.defer(100, this);
22417 e.preventDefault();
22425 fixKeys : function(){ // load time branching for fastest keydown performance
22427 return function(e){
22428 var k = e.getKey(), r;
22431 r = this.doc.selection.createRange();
22434 r.pasteHTML('    ');
22441 r = this.doc.selection.createRange();
22443 var target = r.parentElement();
22444 if(!target || target.tagName.toLowerCase() != 'li'){
22446 r.pasteHTML('<br />');
22452 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22453 this.cleanUpPaste.defer(100, this);
22459 }else if(Roo.isOpera){
22460 return function(e){
22461 var k = e.getKey();
22465 this.execCmd('InsertHTML','    ');
22468 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22469 this.cleanUpPaste.defer(100, this);
22474 }else if(Roo.isSafari){
22475 return function(e){
22476 var k = e.getKey();
22480 this.execCmd('InsertText','\t');
22484 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22485 this.cleanUpPaste.defer(100, this);
22493 getAllAncestors: function()
22495 var p = this.getSelectedNode();
22498 a.push(p); // push blank onto stack..
22499 p = this.getParentElement();
22503 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22507 a.push(this.doc.body);
22511 lastSelNode : false,
22514 getSelection : function()
22516 this.assignDocWin();
22517 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22520 getSelectedNode: function()
22522 // this may only work on Gecko!!!
22524 // should we cache this!!!!
22529 var range = this.createRange(this.getSelection()).cloneRange();
22532 var parent = range.parentElement();
22534 var testRange = range.duplicate();
22535 testRange.moveToElementText(parent);
22536 if (testRange.inRange(range)) {
22539 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22542 parent = parent.parentElement;
22547 // is ancestor a text element.
22548 var ac = range.commonAncestorContainer;
22549 if (ac.nodeType == 3) {
22550 ac = ac.parentNode;
22553 var ar = ac.childNodes;
22556 var other_nodes = [];
22557 var has_other_nodes = false;
22558 for (var i=0;i<ar.length;i++) {
22559 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22562 // fullly contained node.
22564 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22569 // probably selected..
22570 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22571 other_nodes.push(ar[i]);
22575 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22580 has_other_nodes = true;
22582 if (!nodes.length && other_nodes.length) {
22583 nodes= other_nodes;
22585 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22591 createRange: function(sel)
22593 // this has strange effects when using with
22594 // top toolbar - not sure if it's a great idea.
22595 //this.editor.contentWindow.focus();
22596 if (typeof sel != "undefined") {
22598 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22600 return this.doc.createRange();
22603 return this.doc.createRange();
22606 getParentElement: function()
22609 this.assignDocWin();
22610 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22612 var range = this.createRange(sel);
22615 var p = range.commonAncestorContainer;
22616 while (p.nodeType == 3) { // text node
22627 * Range intersection.. the hard stuff...
22631 * [ -- selected range --- ]
22635 * if end is before start or hits it. fail.
22636 * if start is after end or hits it fail.
22638 * if either hits (but other is outside. - then it's not
22644 // @see http://www.thismuchiknow.co.uk/?p=64.
22645 rangeIntersectsNode : function(range, node)
22647 var nodeRange = node.ownerDocument.createRange();
22649 nodeRange.selectNode(node);
22651 nodeRange.selectNodeContents(node);
22654 var rangeStartRange = range.cloneRange();
22655 rangeStartRange.collapse(true);
22657 var rangeEndRange = range.cloneRange();
22658 rangeEndRange.collapse(false);
22660 var nodeStartRange = nodeRange.cloneRange();
22661 nodeStartRange.collapse(true);
22663 var nodeEndRange = nodeRange.cloneRange();
22664 nodeEndRange.collapse(false);
22666 return rangeStartRange.compareBoundaryPoints(
22667 Range.START_TO_START, nodeEndRange) == -1 &&
22668 rangeEndRange.compareBoundaryPoints(
22669 Range.START_TO_START, nodeStartRange) == 1;
22673 rangeCompareNode : function(range, node)
22675 var nodeRange = node.ownerDocument.createRange();
22677 nodeRange.selectNode(node);
22679 nodeRange.selectNodeContents(node);
22683 range.collapse(true);
22685 nodeRange.collapse(true);
22687 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22688 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22690 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22692 var nodeIsBefore = ss == 1;
22693 var nodeIsAfter = ee == -1;
22695 if (nodeIsBefore && nodeIsAfter) {
22698 if (!nodeIsBefore && nodeIsAfter) {
22699 return 1; //right trailed.
22702 if (nodeIsBefore && !nodeIsAfter) {
22703 return 2; // left trailed.
22709 // private? - in a new class?
22710 cleanUpPaste : function()
22712 // cleans up the whole document..
22713 Roo.log('cleanuppaste');
22715 this.cleanUpChildren(this.doc.body);
22716 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22717 if (clean != this.doc.body.innerHTML) {
22718 this.doc.body.innerHTML = clean;
22723 cleanWordChars : function(input) {// change the chars to hex code
22724 var he = Roo.HtmlEditorCore;
22726 var output = input;
22727 Roo.each(he.swapCodes, function(sw) {
22728 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22730 output = output.replace(swapper, sw[1]);
22737 cleanUpChildren : function (n)
22739 if (!n.childNodes.length) {
22742 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22743 this.cleanUpChild(n.childNodes[i]);
22750 cleanUpChild : function (node)
22753 //console.log(node);
22754 if (node.nodeName == "#text") {
22755 // clean up silly Windows -- stuff?
22758 if (node.nodeName == "#comment") {
22759 node.parentNode.removeChild(node);
22760 // clean up silly Windows -- stuff?
22763 var lcname = node.tagName.toLowerCase();
22764 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22765 // whitelist of tags..
22767 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22769 node.parentNode.removeChild(node);
22774 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22776 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22777 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22779 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22780 // remove_keep_children = true;
22783 if (remove_keep_children) {
22784 this.cleanUpChildren(node);
22785 // inserts everything just before this node...
22786 while (node.childNodes.length) {
22787 var cn = node.childNodes[0];
22788 node.removeChild(cn);
22789 node.parentNode.insertBefore(cn, node);
22791 node.parentNode.removeChild(node);
22795 if (!node.attributes || !node.attributes.length) {
22796 this.cleanUpChildren(node);
22800 function cleanAttr(n,v)
22803 if (v.match(/^\./) || v.match(/^\//)) {
22806 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22809 if (v.match(/^#/)) {
22812 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22813 node.removeAttribute(n);
22817 var cwhite = this.cwhite;
22818 var cblack = this.cblack;
22820 function cleanStyle(n,v)
22822 if (v.match(/expression/)) { //XSS?? should we even bother..
22823 node.removeAttribute(n);
22827 var parts = v.split(/;/);
22830 Roo.each(parts, function(p) {
22831 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22835 var l = p.split(':').shift().replace(/\s+/g,'');
22836 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22838 if ( cwhite.length && cblack.indexOf(l) > -1) {
22839 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22840 //node.removeAttribute(n);
22844 // only allow 'c whitelisted system attributes'
22845 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22846 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22847 //node.removeAttribute(n);
22857 if (clean.length) {
22858 node.setAttribute(n, clean.join(';'));
22860 node.removeAttribute(n);
22866 for (var i = node.attributes.length-1; i > -1 ; i--) {
22867 var a = node.attributes[i];
22870 if (a.name.toLowerCase().substr(0,2)=='on') {
22871 node.removeAttribute(a.name);
22874 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22875 node.removeAttribute(a.name);
22878 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22879 cleanAttr(a.name,a.value); // fixme..
22882 if (a.name == 'style') {
22883 cleanStyle(a.name,a.value);
22886 /// clean up MS crap..
22887 // tecnically this should be a list of valid class'es..
22890 if (a.name == 'class') {
22891 if (a.value.match(/^Mso/)) {
22892 node.className = '';
22895 if (a.value.match(/^body$/)) {
22896 node.className = '';
22907 this.cleanUpChildren(node);
22913 * Clean up MS wordisms...
22915 cleanWord : function(node)
22920 this.cleanWord(this.doc.body);
22923 if (node.nodeName == "#text") {
22924 // clean up silly Windows -- stuff?
22927 if (node.nodeName == "#comment") {
22928 node.parentNode.removeChild(node);
22929 // clean up silly Windows -- stuff?
22933 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22934 node.parentNode.removeChild(node);
22938 // remove - but keep children..
22939 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22940 while (node.childNodes.length) {
22941 var cn = node.childNodes[0];
22942 node.removeChild(cn);
22943 node.parentNode.insertBefore(cn, node);
22945 node.parentNode.removeChild(node);
22946 this.iterateChildren(node, this.cleanWord);
22950 if (node.className.length) {
22952 var cn = node.className.split(/\W+/);
22954 Roo.each(cn, function(cls) {
22955 if (cls.match(/Mso[a-zA-Z]+/)) {
22960 node.className = cna.length ? cna.join(' ') : '';
22962 node.removeAttribute("class");
22966 if (node.hasAttribute("lang")) {
22967 node.removeAttribute("lang");
22970 if (node.hasAttribute("style")) {
22972 var styles = node.getAttribute("style").split(";");
22974 Roo.each(styles, function(s) {
22975 if (!s.match(/:/)) {
22978 var kv = s.split(":");
22979 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22982 // what ever is left... we allow.
22985 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22986 if (!nstyle.length) {
22987 node.removeAttribute('style');
22990 this.iterateChildren(node, this.cleanWord);
22996 * iterateChildren of a Node, calling fn each time, using this as the scole..
22997 * @param {DomNode} node node to iterate children of.
22998 * @param {Function} fn method of this class to call on each item.
23000 iterateChildren : function(node, fn)
23002 if (!node.childNodes.length) {
23005 for (var i = node.childNodes.length-1; i > -1 ; i--) {
23006 fn.call(this, node.childNodes[i])
23012 * cleanTableWidths.
23014 * Quite often pasting from word etc.. results in tables with column and widths.
23015 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23018 cleanTableWidths : function(node)
23023 this.cleanTableWidths(this.doc.body);
23028 if (node.nodeName == "#text" || node.nodeName == "#comment") {
23031 Roo.log(node.tagName);
23032 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23033 this.iterateChildren(node, this.cleanTableWidths);
23036 if (node.hasAttribute('width')) {
23037 node.removeAttribute('width');
23041 if (node.hasAttribute("style")) {
23044 var styles = node.getAttribute("style").split(";");
23046 Roo.each(styles, function(s) {
23047 if (!s.match(/:/)) {
23050 var kv = s.split(":");
23051 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23054 // what ever is left... we allow.
23057 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23058 if (!nstyle.length) {
23059 node.removeAttribute('style');
23063 this.iterateChildren(node, this.cleanTableWidths);
23071 domToHTML : function(currentElement, depth, nopadtext) {
23073 depth = depth || 0;
23074 nopadtext = nopadtext || false;
23076 if (!currentElement) {
23077 return this.domToHTML(this.doc.body);
23080 //Roo.log(currentElement);
23082 var allText = false;
23083 var nodeName = currentElement.nodeName;
23084 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23086 if (nodeName == '#text') {
23088 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23093 if (nodeName != 'BODY') {
23096 // Prints the node tagName, such as <A>, <IMG>, etc
23099 for(i = 0; i < currentElement.attributes.length;i++) {
23101 var aname = currentElement.attributes.item(i).name;
23102 if (!currentElement.attributes.item(i).value.length) {
23105 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23108 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23117 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23120 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23125 // Traverse the tree
23127 var currentElementChild = currentElement.childNodes.item(i);
23128 var allText = true;
23129 var innerHTML = '';
23131 while (currentElementChild) {
23132 // Formatting code (indent the tree so it looks nice on the screen)
23133 var nopad = nopadtext;
23134 if (lastnode == 'SPAN') {
23138 if (currentElementChild.nodeName == '#text') {
23139 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23140 toadd = nopadtext ? toadd : toadd.trim();
23141 if (!nopad && toadd.length > 80) {
23142 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23144 innerHTML += toadd;
23147 currentElementChild = currentElement.childNodes.item(i);
23153 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23155 // Recursively traverse the tree structure of the child node
23156 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23157 lastnode = currentElementChild.nodeName;
23159 currentElementChild=currentElement.childNodes.item(i);
23165 // The remaining code is mostly for formatting the tree
23166 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23171 ret+= "</"+tagName+">";
23177 applyBlacklists : function()
23179 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23180 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23184 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23185 if (b.indexOf(tag) > -1) {
23188 this.white.push(tag);
23192 Roo.each(w, function(tag) {
23193 if (b.indexOf(tag) > -1) {
23196 if (this.white.indexOf(tag) > -1) {
23199 this.white.push(tag);
23204 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23205 if (w.indexOf(tag) > -1) {
23208 this.black.push(tag);
23212 Roo.each(b, function(tag) {
23213 if (w.indexOf(tag) > -1) {
23216 if (this.black.indexOf(tag) > -1) {
23219 this.black.push(tag);
23224 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23225 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23229 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23230 if (b.indexOf(tag) > -1) {
23233 this.cwhite.push(tag);
23237 Roo.each(w, function(tag) {
23238 if (b.indexOf(tag) > -1) {
23241 if (this.cwhite.indexOf(tag) > -1) {
23244 this.cwhite.push(tag);
23249 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23250 if (w.indexOf(tag) > -1) {
23253 this.cblack.push(tag);
23257 Roo.each(b, function(tag) {
23258 if (w.indexOf(tag) > -1) {
23261 if (this.cblack.indexOf(tag) > -1) {
23264 this.cblack.push(tag);
23269 setStylesheets : function(stylesheets)
23271 if(typeof(stylesheets) == 'string'){
23272 Roo.get(this.iframe.contentDocument.head).createChild({
23274 rel : 'stylesheet',
23283 Roo.each(stylesheets, function(s) {
23288 Roo.get(_this.iframe.contentDocument.head).createChild({
23290 rel : 'stylesheet',
23299 removeStylesheets : function()
23303 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23308 setStyle : function(style)
23310 Roo.get(this.iframe.contentDocument.head).createChild({
23319 // hide stuff that is not compatible
23333 * @event specialkey
23337 * @cfg {String} fieldClass @hide
23340 * @cfg {String} focusClass @hide
23343 * @cfg {String} autoCreate @hide
23346 * @cfg {String} inputType @hide
23349 * @cfg {String} invalidClass @hide
23352 * @cfg {String} invalidText @hide
23355 * @cfg {String} msgFx @hide
23358 * @cfg {String} validateOnBlur @hide
23362 Roo.HtmlEditorCore.white = [
23363 'area', 'br', 'img', 'input', 'hr', 'wbr',
23365 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23366 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23367 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23368 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23369 'table', 'ul', 'xmp',
23371 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23374 'dir', 'menu', 'ol', 'ul', 'dl',
23380 Roo.HtmlEditorCore.black = [
23381 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23383 'base', 'basefont', 'bgsound', 'blink', 'body',
23384 'frame', 'frameset', 'head', 'html', 'ilayer',
23385 'iframe', 'layer', 'link', 'meta', 'object',
23386 'script', 'style' ,'title', 'xml' // clean later..
23388 Roo.HtmlEditorCore.clean = [
23389 'script', 'style', 'title', 'xml'
23391 Roo.HtmlEditorCore.remove = [
23396 Roo.HtmlEditorCore.ablack = [
23400 Roo.HtmlEditorCore.aclean = [
23401 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23405 Roo.HtmlEditorCore.pwhite= [
23406 'http', 'https', 'mailto'
23409 // white listed style attributes.
23410 Roo.HtmlEditorCore.cwhite= [
23411 // 'text-align', /// default is to allow most things..
23417 // black listed style attributes.
23418 Roo.HtmlEditorCore.cblack= [
23419 // 'font-size' -- this can be set by the project
23423 Roo.HtmlEditorCore.swapCodes =[
23442 * @class Roo.bootstrap.HtmlEditor
23443 * @extends Roo.bootstrap.TextArea
23444 * Bootstrap HtmlEditor class
23447 * Create a new HtmlEditor
23448 * @param {Object} config The config object
23451 Roo.bootstrap.HtmlEditor = function(config){
23452 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23453 if (!this.toolbars) {
23454 this.toolbars = [];
23457 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23460 * @event initialize
23461 * Fires when the editor is fully initialized (including the iframe)
23462 * @param {HtmlEditor} this
23467 * Fires when the editor is first receives the focus. Any insertion must wait
23468 * until after this event.
23469 * @param {HtmlEditor} this
23473 * @event beforesync
23474 * Fires before the textarea is updated with content from the editor iframe. Return false
23475 * to cancel the sync.
23476 * @param {HtmlEditor} this
23477 * @param {String} html
23481 * @event beforepush
23482 * Fires before the iframe editor is updated with content from the textarea. Return false
23483 * to cancel the push.
23484 * @param {HtmlEditor} this
23485 * @param {String} html
23490 * Fires when the textarea is updated with content from the editor iframe.
23491 * @param {HtmlEditor} this
23492 * @param {String} html
23497 * Fires when the iframe editor is updated with content from the textarea.
23498 * @param {HtmlEditor} this
23499 * @param {String} html
23503 * @event editmodechange
23504 * Fires when the editor switches edit modes
23505 * @param {HtmlEditor} this
23506 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23508 editmodechange: true,
23510 * @event editorevent
23511 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23512 * @param {HtmlEditor} this
23516 * @event firstfocus
23517 * Fires when on first focus - needed by toolbars..
23518 * @param {HtmlEditor} this
23523 * Auto save the htmlEditor value as a file into Events
23524 * @param {HtmlEditor} this
23528 * @event savedpreview
23529 * preview the saved version of htmlEditor
23530 * @param {HtmlEditor} this
23537 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23541 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23546 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23551 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23556 * @cfg {Number} height (in pixels)
23560 * @cfg {Number} width (in pixels)
23565 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23568 stylesheets: false,
23573 // private properties
23574 validationEvent : false,
23576 initialized : false,
23579 onFocus : Roo.emptyFn,
23581 hideMode:'offsets',
23583 tbContainer : false,
23587 toolbarContainer :function() {
23588 return this.wrap.select('.x-html-editor-tb',true).first();
23592 * Protected method that will not generally be called directly. It
23593 * is called when the editor creates its toolbar. Override this method if you need to
23594 * add custom toolbar buttons.
23595 * @param {HtmlEditor} editor
23597 createToolbar : function(){
23598 Roo.log('renewing');
23599 Roo.log("create toolbars");
23601 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23602 this.toolbars[0].render(this.toolbarContainer());
23606 // if (!editor.toolbars || !editor.toolbars.length) {
23607 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23610 // for (var i =0 ; i < editor.toolbars.length;i++) {
23611 // editor.toolbars[i] = Roo.factory(
23612 // typeof(editor.toolbars[i]) == 'string' ?
23613 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23614 // Roo.bootstrap.HtmlEditor);
23615 // editor.toolbars[i].init(editor);
23621 onRender : function(ct, position)
23623 // Roo.log("Call onRender: " + this.xtype);
23625 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23627 this.wrap = this.inputEl().wrap({
23628 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23631 this.editorcore.onRender(ct, position);
23633 if (this.resizable) {
23634 this.resizeEl = new Roo.Resizable(this.wrap, {
23638 minHeight : this.height,
23639 height: this.height,
23640 handles : this.resizable,
23643 resize : function(r, w, h) {
23644 _t.onResize(w,h); // -something
23650 this.createToolbar(this);
23653 if(!this.width && this.resizable){
23654 this.setSize(this.wrap.getSize());
23656 if (this.resizeEl) {
23657 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23658 // should trigger onReize..
23664 onResize : function(w, h)
23666 Roo.log('resize: ' +w + ',' + h );
23667 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23671 if(this.inputEl() ){
23672 if(typeof w == 'number'){
23673 var aw = w - this.wrap.getFrameWidth('lr');
23674 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23677 if(typeof h == 'number'){
23678 var tbh = -11; // fixme it needs to tool bar size!
23679 for (var i =0; i < this.toolbars.length;i++) {
23680 // fixme - ask toolbars for heights?
23681 tbh += this.toolbars[i].el.getHeight();
23682 //if (this.toolbars[i].footer) {
23683 // tbh += this.toolbars[i].footer.el.getHeight();
23691 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23692 ah -= 5; // knock a few pixes off for look..
23693 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23697 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23698 this.editorcore.onResize(ew,eh);
23703 * Toggles the editor between standard and source edit mode.
23704 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23706 toggleSourceEdit : function(sourceEditMode)
23708 this.editorcore.toggleSourceEdit(sourceEditMode);
23710 if(this.editorcore.sourceEditMode){
23711 Roo.log('editor - showing textarea');
23714 // Roo.log(this.syncValue());
23716 this.inputEl().removeClass(['hide', 'x-hidden']);
23717 this.inputEl().dom.removeAttribute('tabIndex');
23718 this.inputEl().focus();
23720 Roo.log('editor - hiding textarea');
23722 // Roo.log(this.pushValue());
23725 this.inputEl().addClass(['hide', 'x-hidden']);
23726 this.inputEl().dom.setAttribute('tabIndex', -1);
23727 //this.deferFocus();
23730 if(this.resizable){
23731 this.setSize(this.wrap.getSize());
23734 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23737 // private (for BoxComponent)
23738 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23740 // private (for BoxComponent)
23741 getResizeEl : function(){
23745 // private (for BoxComponent)
23746 getPositionEl : function(){
23751 initEvents : function(){
23752 this.originalValue = this.getValue();
23756 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23759 // markInvalid : Roo.emptyFn,
23761 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23764 // clearInvalid : Roo.emptyFn,
23766 setValue : function(v){
23767 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23768 this.editorcore.pushValue();
23773 deferFocus : function(){
23774 this.focus.defer(10, this);
23778 focus : function(){
23779 this.editorcore.focus();
23785 onDestroy : function(){
23791 for (var i =0; i < this.toolbars.length;i++) {
23792 // fixme - ask toolbars for heights?
23793 this.toolbars[i].onDestroy();
23796 this.wrap.dom.innerHTML = '';
23797 this.wrap.remove();
23802 onFirstFocus : function(){
23803 //Roo.log("onFirstFocus");
23804 this.editorcore.onFirstFocus();
23805 for (var i =0; i < this.toolbars.length;i++) {
23806 this.toolbars[i].onFirstFocus();
23812 syncValue : function()
23814 this.editorcore.syncValue();
23817 pushValue : function()
23819 this.editorcore.pushValue();
23823 // hide stuff that is not compatible
23837 * @event specialkey
23841 * @cfg {String} fieldClass @hide
23844 * @cfg {String} focusClass @hide
23847 * @cfg {String} autoCreate @hide
23850 * @cfg {String} inputType @hide
23853 * @cfg {String} invalidClass @hide
23856 * @cfg {String} invalidText @hide
23859 * @cfg {String} msgFx @hide
23862 * @cfg {String} validateOnBlur @hide
23871 Roo.namespace('Roo.bootstrap.htmleditor');
23873 * @class Roo.bootstrap.HtmlEditorToolbar1
23878 new Roo.bootstrap.HtmlEditor({
23881 new Roo.bootstrap.HtmlEditorToolbar1({
23882 disable : { fonts: 1 , format: 1, ..., ... , ...],
23888 * @cfg {Object} disable List of elements to disable..
23889 * @cfg {Array} btns List of additional buttons.
23893 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23896 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23899 Roo.apply(this, config);
23901 // default disabled, based on 'good practice'..
23902 this.disable = this.disable || {};
23903 Roo.applyIf(this.disable, {
23906 specialElements : true
23908 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23910 this.editor = config.editor;
23911 this.editorcore = config.editor.editorcore;
23913 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23915 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23916 // dont call parent... till later.
23918 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23923 editorcore : false,
23928 "h1","h2","h3","h4","h5","h6",
23930 "abbr", "acronym", "address", "cite", "samp", "var",
23934 onRender : function(ct, position)
23936 // Roo.log("Call onRender: " + this.xtype);
23938 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23940 this.el.dom.style.marginBottom = '0';
23942 var editorcore = this.editorcore;
23943 var editor= this.editor;
23946 var btn = function(id,cmd , toggle, handler, html){
23948 var event = toggle ? 'toggle' : 'click';
23953 xns: Roo.bootstrap,
23956 enableToggle:toggle !== false,
23958 pressed : toggle ? false : null,
23961 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23962 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23968 // var cb_box = function...
23973 xns: Roo.bootstrap,
23974 glyphicon : 'font',
23978 xns: Roo.bootstrap,
23982 Roo.each(this.formats, function(f) {
23983 style.menu.items.push({
23985 xns: Roo.bootstrap,
23986 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23991 editorcore.insertTag(this.tagname);
23998 children.push(style);
24000 btn('bold',false,true);
24001 btn('italic',false,true);
24002 btn('align-left', 'justifyleft',true);
24003 btn('align-center', 'justifycenter',true);
24004 btn('align-right' , 'justifyright',true);
24005 btn('link', false, false, function(btn) {
24006 //Roo.log("create link?");
24007 var url = prompt(this.createLinkText, this.defaultLinkValue);
24008 if(url && url != 'http:/'+'/'){
24009 this.editorcore.relayCmd('createlink', url);
24012 btn('list','insertunorderedlist',true);
24013 btn('pencil', false,true, function(btn){
24015 this.toggleSourceEdit(btn.pressed);
24018 if (this.editor.btns.length > 0) {
24019 for (var i = 0; i<this.editor.btns.length; i++) {
24020 children.push(this.editor.btns[i]);
24028 xns: Roo.bootstrap,
24033 xns: Roo.bootstrap,
24038 cog.menu.items.push({
24040 xns: Roo.bootstrap,
24041 html : Clean styles,
24046 editorcore.insertTag(this.tagname);
24055 this.xtype = 'NavSimplebar';
24057 for(var i=0;i< children.length;i++) {
24059 this.buttons.add(this.addxtypeChild(children[i]));
24063 editor.on('editorevent', this.updateToolbar, this);
24065 onBtnClick : function(id)
24067 this.editorcore.relayCmd(id);
24068 this.editorcore.focus();
24072 * Protected method that will not generally be called directly. It triggers
24073 * a toolbar update by reading the markup state of the current selection in the editor.
24075 updateToolbar: function(){
24077 if(!this.editorcore.activated){
24078 this.editor.onFirstFocus(); // is this neeed?
24082 var btns = this.buttons;
24083 var doc = this.editorcore.doc;
24084 btns.get('bold').setActive(doc.queryCommandState('bold'));
24085 btns.get('italic').setActive(doc.queryCommandState('italic'));
24086 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24088 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24089 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24090 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24092 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24093 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24096 var ans = this.editorcore.getAllAncestors();
24097 if (this.formatCombo) {
24100 var store = this.formatCombo.store;
24101 this.formatCombo.setValue("");
24102 for (var i =0; i < ans.length;i++) {
24103 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24105 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24113 // hides menus... - so this cant be on a menu...
24114 Roo.bootstrap.MenuMgr.hideAll();
24116 Roo.bootstrap.MenuMgr.hideAll();
24117 //this.editorsyncValue();
24119 onFirstFocus: function() {
24120 this.buttons.each(function(item){
24124 toggleSourceEdit : function(sourceEditMode){
24127 if(sourceEditMode){
24128 Roo.log("disabling buttons");
24129 this.buttons.each( function(item){
24130 if(item.cmd != 'pencil'){
24136 Roo.log("enabling buttons");
24137 if(this.editorcore.initialized){
24138 this.buttons.each( function(item){
24144 Roo.log("calling toggole on editor");
24145 // tell the editor that it's been pressed..
24146 this.editor.toggleSourceEdit(sourceEditMode);
24156 * @class Roo.bootstrap.Table.AbstractSelectionModel
24157 * @extends Roo.util.Observable
24158 * Abstract base class for grid SelectionModels. It provides the interface that should be
24159 * implemented by descendant classes. This class should not be directly instantiated.
24162 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24163 this.locked = false;
24164 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24168 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24169 /** @ignore Called by the grid automatically. Do not call directly. */
24170 init : function(grid){
24176 * Locks the selections.
24179 this.locked = true;
24183 * Unlocks the selections.
24185 unlock : function(){
24186 this.locked = false;
24190 * Returns true if the selections are locked.
24191 * @return {Boolean}
24193 isLocked : function(){
24194 return this.locked;
24198 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24199 * @class Roo.bootstrap.Table.RowSelectionModel
24200 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24201 * It supports multiple selections and keyboard selection/navigation.
24203 * @param {Object} config
24206 Roo.bootstrap.Table.RowSelectionModel = function(config){
24207 Roo.apply(this, config);
24208 this.selections = new Roo.util.MixedCollection(false, function(o){
24213 this.lastActive = false;
24217 * @event selectionchange
24218 * Fires when the selection changes
24219 * @param {SelectionModel} this
24221 "selectionchange" : true,
24223 * @event afterselectionchange
24224 * Fires after the selection changes (eg. by key press or clicking)
24225 * @param {SelectionModel} this
24227 "afterselectionchange" : true,
24229 * @event beforerowselect
24230 * Fires when a row is selected being selected, return false to cancel.
24231 * @param {SelectionModel} this
24232 * @param {Number} rowIndex The selected index
24233 * @param {Boolean} keepExisting False if other selections will be cleared
24235 "beforerowselect" : true,
24238 * Fires when a row is selected.
24239 * @param {SelectionModel} this
24240 * @param {Number} rowIndex The selected index
24241 * @param {Roo.data.Record} r The record
24243 "rowselect" : true,
24245 * @event rowdeselect
24246 * Fires when a row is deselected.
24247 * @param {SelectionModel} this
24248 * @param {Number} rowIndex The selected index
24250 "rowdeselect" : true
24252 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24253 this.locked = false;
24256 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24258 * @cfg {Boolean} singleSelect
24259 * True to allow selection of only one row at a time (defaults to false)
24261 singleSelect : false,
24264 initEvents : function()
24267 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24268 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24269 //}else{ // allow click to work like normal
24270 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24272 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24273 this.grid.on("rowclick", this.handleMouseDown, this);
24275 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24276 "up" : function(e){
24278 this.selectPrevious(e.shiftKey);
24279 }else if(this.last !== false && this.lastActive !== false){
24280 var last = this.last;
24281 this.selectRange(this.last, this.lastActive-1);
24282 this.grid.getView().focusRow(this.lastActive);
24283 if(last !== false){
24287 this.selectFirstRow();
24289 this.fireEvent("afterselectionchange", this);
24291 "down" : function(e){
24293 this.selectNext(e.shiftKey);
24294 }else if(this.last !== false && this.lastActive !== false){
24295 var last = this.last;
24296 this.selectRange(this.last, this.lastActive+1);
24297 this.grid.getView().focusRow(this.lastActive);
24298 if(last !== false){
24302 this.selectFirstRow();
24304 this.fireEvent("afterselectionchange", this);
24308 this.grid.store.on('load', function(){
24309 this.selections.clear();
24312 var view = this.grid.view;
24313 view.on("refresh", this.onRefresh, this);
24314 view.on("rowupdated", this.onRowUpdated, this);
24315 view.on("rowremoved", this.onRemove, this);
24320 onRefresh : function()
24322 var ds = this.grid.store, i, v = this.grid.view;
24323 var s = this.selections;
24324 s.each(function(r){
24325 if((i = ds.indexOfId(r.id)) != -1){
24334 onRemove : function(v, index, r){
24335 this.selections.remove(r);
24339 onRowUpdated : function(v, index, r){
24340 if(this.isSelected(r)){
24341 v.onRowSelect(index);
24347 * @param {Array} records The records to select
24348 * @param {Boolean} keepExisting (optional) True to keep existing selections
24350 selectRecords : function(records, keepExisting)
24353 this.clearSelections();
24355 var ds = this.grid.store;
24356 for(var i = 0, len = records.length; i < len; i++){
24357 this.selectRow(ds.indexOf(records[i]), true);
24362 * Gets the number of selected rows.
24365 getCount : function(){
24366 return this.selections.length;
24370 * Selects the first row in the grid.
24372 selectFirstRow : function(){
24377 * Select the last row.
24378 * @param {Boolean} keepExisting (optional) True to keep existing selections
24380 selectLastRow : function(keepExisting){
24381 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24382 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24386 * Selects the row immediately following the last selected row.
24387 * @param {Boolean} keepExisting (optional) True to keep existing selections
24389 selectNext : function(keepExisting)
24391 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24392 this.selectRow(this.last+1, keepExisting);
24393 this.grid.getView().focusRow(this.last);
24398 * Selects the row that precedes the last selected row.
24399 * @param {Boolean} keepExisting (optional) True to keep existing selections
24401 selectPrevious : function(keepExisting){
24403 this.selectRow(this.last-1, keepExisting);
24404 this.grid.getView().focusRow(this.last);
24409 * Returns the selected records
24410 * @return {Array} Array of selected records
24412 getSelections : function(){
24413 return [].concat(this.selections.items);
24417 * Returns the first selected record.
24420 getSelected : function(){
24421 return this.selections.itemAt(0);
24426 * Clears all selections.
24428 clearSelections : function(fast)
24434 var ds = this.grid.store;
24435 var s = this.selections;
24436 s.each(function(r){
24437 this.deselectRow(ds.indexOfId(r.id));
24441 this.selections.clear();
24448 * Selects all rows.
24450 selectAll : function(){
24454 this.selections.clear();
24455 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24456 this.selectRow(i, true);
24461 * Returns True if there is a selection.
24462 * @return {Boolean}
24464 hasSelection : function(){
24465 return this.selections.length > 0;
24469 * Returns True if the specified row is selected.
24470 * @param {Number/Record} record The record or index of the record to check
24471 * @return {Boolean}
24473 isSelected : function(index){
24474 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24475 return (r && this.selections.key(r.id) ? true : false);
24479 * Returns True if the specified record id is selected.
24480 * @param {String} id The id of record to check
24481 * @return {Boolean}
24483 isIdSelected : function(id){
24484 return (this.selections.key(id) ? true : false);
24489 handleMouseDBClick : function(e, t){
24493 handleMouseDown : function(e, t)
24495 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24496 if(this.isLocked() || rowIndex < 0 ){
24499 if(e.shiftKey && this.last !== false){
24500 var last = this.last;
24501 this.selectRange(last, rowIndex, e.ctrlKey);
24502 this.last = last; // reset the last
24506 var isSelected = this.isSelected(rowIndex);
24507 //Roo.log("select row:" + rowIndex);
24509 this.deselectRow(rowIndex);
24511 this.selectRow(rowIndex, true);
24515 if(e.button !== 0 && isSelected){
24516 alert('rowIndex 2: ' + rowIndex);
24517 view.focusRow(rowIndex);
24518 }else if(e.ctrlKey && isSelected){
24519 this.deselectRow(rowIndex);
24520 }else if(!isSelected){
24521 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24522 view.focusRow(rowIndex);
24526 this.fireEvent("afterselectionchange", this);
24529 handleDragableRowClick : function(grid, rowIndex, e)
24531 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24532 this.selectRow(rowIndex, false);
24533 grid.view.focusRow(rowIndex);
24534 this.fireEvent("afterselectionchange", this);
24539 * Selects multiple rows.
24540 * @param {Array} rows Array of the indexes of the row to select
24541 * @param {Boolean} keepExisting (optional) True to keep existing selections
24543 selectRows : function(rows, keepExisting){
24545 this.clearSelections();
24547 for(var i = 0, len = rows.length; i < len; i++){
24548 this.selectRow(rows[i], true);
24553 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24554 * @param {Number} startRow The index of the first row in the range
24555 * @param {Number} endRow The index of the last row in the range
24556 * @param {Boolean} keepExisting (optional) True to retain existing selections
24558 selectRange : function(startRow, endRow, keepExisting){
24563 this.clearSelections();
24565 if(startRow <= endRow){
24566 for(var i = startRow; i <= endRow; i++){
24567 this.selectRow(i, true);
24570 for(var i = startRow; i >= endRow; i--){
24571 this.selectRow(i, true);
24577 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24578 * @param {Number} startRow The index of the first row in the range
24579 * @param {Number} endRow The index of the last row in the range
24581 deselectRange : function(startRow, endRow, preventViewNotify){
24585 for(var i = startRow; i <= endRow; i++){
24586 this.deselectRow(i, preventViewNotify);
24592 * @param {Number} row The index of the row to select
24593 * @param {Boolean} keepExisting (optional) True to keep existing selections
24595 selectRow : function(index, keepExisting, preventViewNotify)
24597 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24600 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24601 if(!keepExisting || this.singleSelect){
24602 this.clearSelections();
24605 var r = this.grid.store.getAt(index);
24606 //console.log('selectRow - record id :' + r.id);
24608 this.selections.add(r);
24609 this.last = this.lastActive = index;
24610 if(!preventViewNotify){
24611 var proxy = new Roo.Element(
24612 this.grid.getRowDom(index)
24614 proxy.addClass('bg-info info');
24616 this.fireEvent("rowselect", this, index, r);
24617 this.fireEvent("selectionchange", this);
24623 * @param {Number} row The index of the row to deselect
24625 deselectRow : function(index, preventViewNotify)
24630 if(this.last == index){
24633 if(this.lastActive == index){
24634 this.lastActive = false;
24637 var r = this.grid.store.getAt(index);
24642 this.selections.remove(r);
24643 //.console.log('deselectRow - record id :' + r.id);
24644 if(!preventViewNotify){
24646 var proxy = new Roo.Element(
24647 this.grid.getRowDom(index)
24649 proxy.removeClass('bg-info info');
24651 this.fireEvent("rowdeselect", this, index);
24652 this.fireEvent("selectionchange", this);
24656 restoreLast : function(){
24658 this.last = this._last;
24663 acceptsNav : function(row, col, cm){
24664 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24668 onEditorKey : function(field, e){
24669 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24674 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24676 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24678 }else if(k == e.ENTER && !e.ctrlKey){
24682 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24684 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24686 }else if(k == e.ESC){
24690 g.startEditing(newCell[0], newCell[1]);
24696 * Ext JS Library 1.1.1
24697 * Copyright(c) 2006-2007, Ext JS, LLC.
24699 * Originally Released Under LGPL - original licence link has changed is not relivant.
24702 * <script type="text/javascript">
24706 * @class Roo.bootstrap.PagingToolbar
24707 * @extends Roo.bootstrap.NavSimplebar
24708 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24710 * Create a new PagingToolbar
24711 * @param {Object} config The config object
24712 * @param {Roo.data.Store} store
24714 Roo.bootstrap.PagingToolbar = function(config)
24716 // old args format still supported... - xtype is prefered..
24717 // created from xtype...
24719 this.ds = config.dataSource;
24721 if (config.store && !this.ds) {
24722 this.store= Roo.factory(config.store, Roo.data);
24723 this.ds = this.store;
24724 this.ds.xmodule = this.xmodule || false;
24727 this.toolbarItems = [];
24728 if (config.items) {
24729 this.toolbarItems = config.items;
24732 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24737 this.bind(this.ds);
24740 if (Roo.bootstrap.version == 4) {
24741 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24743 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24748 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24750 * @cfg {Roo.data.Store} dataSource
24751 * The underlying data store providing the paged data
24754 * @cfg {String/HTMLElement/Element} container
24755 * container The id or element that will contain the toolbar
24758 * @cfg {Boolean} displayInfo
24759 * True to display the displayMsg (defaults to false)
24762 * @cfg {Number} pageSize
24763 * The number of records to display per page (defaults to 20)
24767 * @cfg {String} displayMsg
24768 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24770 displayMsg : 'Displaying {0} - {1} of {2}',
24772 * @cfg {String} emptyMsg
24773 * The message to display when no records are found (defaults to "No data to display")
24775 emptyMsg : 'No data to display',
24777 * Customizable piece of the default paging text (defaults to "Page")
24780 beforePageText : "Page",
24782 * Customizable piece of the default paging text (defaults to "of %0")
24785 afterPageText : "of {0}",
24787 * Customizable piece of the default paging text (defaults to "First Page")
24790 firstText : "First Page",
24792 * Customizable piece of the default paging text (defaults to "Previous Page")
24795 prevText : "Previous Page",
24797 * Customizable piece of the default paging text (defaults to "Next Page")
24800 nextText : "Next Page",
24802 * Customizable piece of the default paging text (defaults to "Last Page")
24805 lastText : "Last Page",
24807 * Customizable piece of the default paging text (defaults to "Refresh")
24810 refreshText : "Refresh",
24814 onRender : function(ct, position)
24816 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24817 this.navgroup.parentId = this.id;
24818 this.navgroup.onRender(this.el, null);
24819 // add the buttons to the navgroup
24821 if(this.displayInfo){
24822 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24823 this.displayEl = this.el.select('.x-paging-info', true).first();
24824 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24825 // this.displayEl = navel.el.select('span',true).first();
24831 Roo.each(_this.buttons, function(e){ // this might need to use render????
24832 Roo.factory(e).render(_this.el);
24836 Roo.each(_this.toolbarItems, function(e) {
24837 _this.navgroup.addItem(e);
24841 this.first = this.navgroup.addItem({
24842 tooltip: this.firstText,
24843 cls: "prev btn-outline-secondary",
24844 html : ' <i class="fa fa-step-backward"></i>',
24846 preventDefault: true,
24847 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24850 this.prev = this.navgroup.addItem({
24851 tooltip: this.prevText,
24852 cls: "prev btn-outline-secondary",
24853 html : ' <i class="fa fa-backward"></i>',
24855 preventDefault: true,
24856 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24858 //this.addSeparator();
24861 var field = this.navgroup.addItem( {
24863 cls : 'x-paging-position btn-outline-secondary',
24865 html : this.beforePageText +
24866 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24867 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24870 this.field = field.el.select('input', true).first();
24871 this.field.on("keydown", this.onPagingKeydown, this);
24872 this.field.on("focus", function(){this.dom.select();});
24875 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24876 //this.field.setHeight(18);
24877 //this.addSeparator();
24878 this.next = this.navgroup.addItem({
24879 tooltip: this.nextText,
24880 cls: "next btn-outline-secondary",
24881 html : ' <i class="fa fa-forward"></i>',
24883 preventDefault: true,
24884 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24886 this.last = this.navgroup.addItem({
24887 tooltip: this.lastText,
24888 html : ' <i class="fa fa-step-forward"></i>',
24889 cls: "next btn-outline-secondary",
24891 preventDefault: true,
24892 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24894 //this.addSeparator();
24895 this.loading = this.navgroup.addItem({
24896 tooltip: this.refreshText,
24897 cls: "btn-outline-secondary",
24898 html : ' <i class="fa fa-refresh"></i>',
24899 preventDefault: true,
24900 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24906 updateInfo : function(){
24907 if(this.displayEl){
24908 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24909 var msg = count == 0 ?
24913 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24915 this.displayEl.update(msg);
24920 onLoad : function(ds, r, o)
24922 this.cursor = o.params.start ? o.params.start : 0;
24924 var d = this.getPageData(),
24929 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24930 this.field.dom.value = ap;
24931 this.first.setDisabled(ap == 1);
24932 this.prev.setDisabled(ap == 1);
24933 this.next.setDisabled(ap == ps);
24934 this.last.setDisabled(ap == ps);
24935 this.loading.enable();
24940 getPageData : function(){
24941 var total = this.ds.getTotalCount();
24944 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24945 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24950 onLoadError : function(){
24951 this.loading.enable();
24955 onPagingKeydown : function(e){
24956 var k = e.getKey();
24957 var d = this.getPageData();
24959 var v = this.field.dom.value, pageNum;
24960 if(!v || isNaN(pageNum = parseInt(v, 10))){
24961 this.field.dom.value = d.activePage;
24964 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24965 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24968 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))
24970 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24971 this.field.dom.value = pageNum;
24972 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24975 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24977 var v = this.field.dom.value, pageNum;
24978 var increment = (e.shiftKey) ? 10 : 1;
24979 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24982 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24983 this.field.dom.value = d.activePage;
24986 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24988 this.field.dom.value = parseInt(v, 10) + increment;
24989 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24990 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24997 beforeLoad : function(){
24999 this.loading.disable();
25004 onClick : function(which){
25013 ds.load({params:{start: 0, limit: this.pageSize}});
25016 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25019 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25022 var total = ds.getTotalCount();
25023 var extra = total % this.pageSize;
25024 var lastStart = extra ? (total - extra) : total-this.pageSize;
25025 ds.load({params:{start: lastStart, limit: this.pageSize}});
25028 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25034 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25035 * @param {Roo.data.Store} store The data store to unbind
25037 unbind : function(ds){
25038 ds.un("beforeload", this.beforeLoad, this);
25039 ds.un("load", this.onLoad, this);
25040 ds.un("loadexception", this.onLoadError, this);
25041 ds.un("remove", this.updateInfo, this);
25042 ds.un("add", this.updateInfo, this);
25043 this.ds = undefined;
25047 * Binds the paging toolbar to the specified {@link Roo.data.Store}
25048 * @param {Roo.data.Store} store The data store to bind
25050 bind : function(ds){
25051 ds.on("beforeload", this.beforeLoad, this);
25052 ds.on("load", this.onLoad, this);
25053 ds.on("loadexception", this.onLoadError, this);
25054 ds.on("remove", this.updateInfo, this);
25055 ds.on("add", this.updateInfo, this);
25066 * @class Roo.bootstrap.MessageBar
25067 * @extends Roo.bootstrap.Component
25068 * Bootstrap MessageBar class
25069 * @cfg {String} html contents of the MessageBar
25070 * @cfg {String} weight (info | success | warning | danger) default info
25071 * @cfg {String} beforeClass insert the bar before the given class
25072 * @cfg {Boolean} closable (true | false) default false
25073 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25076 * Create a new Element
25077 * @param {Object} config The config object
25080 Roo.bootstrap.MessageBar = function(config){
25081 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25084 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25090 beforeClass: 'bootstrap-sticky-wrap',
25092 getAutoCreate : function(){
25096 cls: 'alert alert-dismissable alert-' + this.weight,
25101 html: this.html || ''
25107 cfg.cls += ' alert-messages-fixed';
25121 onRender : function(ct, position)
25123 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25126 var cfg = Roo.apply({}, this.getAutoCreate());
25130 cfg.cls += ' ' + this.cls;
25133 cfg.style = this.style;
25135 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25137 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25140 this.el.select('>button.close').on('click', this.hide, this);
25146 if (!this.rendered) {
25152 this.fireEvent('show', this);
25158 if (!this.rendered) {
25164 this.fireEvent('hide', this);
25167 update : function()
25169 // var e = this.el.dom.firstChild;
25171 // if(this.closable){
25172 // e = e.nextSibling;
25175 // e.data = this.html || '';
25177 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25193 * @class Roo.bootstrap.Graph
25194 * @extends Roo.bootstrap.Component
25195 * Bootstrap Graph class
25199 @cfg {String} graphtype bar | vbar | pie
25200 @cfg {number} g_x coodinator | centre x (pie)
25201 @cfg {number} g_y coodinator | centre y (pie)
25202 @cfg {number} g_r radius (pie)
25203 @cfg {number} g_height height of the chart (respected by all elements in the set)
25204 @cfg {number} g_width width of the chart (respected by all elements in the set)
25205 @cfg {Object} title The title of the chart
25208 -opts (object) options for the chart
25210 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25211 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25213 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.
25214 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25216 o stretch (boolean)
25218 -opts (object) options for the pie
25221 o startAngle (number)
25222 o endAngle (number)
25226 * Create a new Input
25227 * @param {Object} config The config object
25230 Roo.bootstrap.Graph = function(config){
25231 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25237 * The img click event for the img.
25238 * @param {Roo.EventObject} e
25244 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25255 //g_colors: this.colors,
25262 getAutoCreate : function(){
25273 onRender : function(ct,position){
25276 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25278 if (typeof(Raphael) == 'undefined') {
25279 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25283 this.raphael = Raphael(this.el.dom);
25285 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25286 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25287 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25288 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25290 r.text(160, 10, "Single Series Chart").attr(txtattr);
25291 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25292 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25293 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25295 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25296 r.barchart(330, 10, 300, 220, data1);
25297 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25298 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25301 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25302 // r.barchart(30, 30, 560, 250, xdata, {
25303 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25304 // axis : "0 0 1 1",
25305 // axisxlabels : xdata
25306 // //yvalues : cols,
25309 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25311 // this.load(null,xdata,{
25312 // axis : "0 0 1 1",
25313 // axisxlabels : xdata
25318 load : function(graphtype,xdata,opts)
25320 this.raphael.clear();
25322 graphtype = this.graphtype;
25327 var r = this.raphael,
25328 fin = function () {
25329 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25331 fout = function () {
25332 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25334 pfin = function() {
25335 this.sector.stop();
25336 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25339 this.label[0].stop();
25340 this.label[0].attr({ r: 7.5 });
25341 this.label[1].attr({ "font-weight": 800 });
25344 pfout = function() {
25345 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25348 this.label[0].animate({ r: 5 }, 500, "bounce");
25349 this.label[1].attr({ "font-weight": 400 });
25355 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25358 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25361 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25362 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25364 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25371 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25376 setTitle: function(o)
25381 initEvents: function() {
25384 this.el.on('click', this.onClick, this);
25388 onClick : function(e)
25390 Roo.log('img onclick');
25391 this.fireEvent('click', this, e);
25403 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25406 * @class Roo.bootstrap.dash.NumberBox
25407 * @extends Roo.bootstrap.Component
25408 * Bootstrap NumberBox class
25409 * @cfg {String} headline Box headline
25410 * @cfg {String} content Box content
25411 * @cfg {String} icon Box icon
25412 * @cfg {String} footer Footer text
25413 * @cfg {String} fhref Footer href
25416 * Create a new NumberBox
25417 * @param {Object} config The config object
25421 Roo.bootstrap.dash.NumberBox = function(config){
25422 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25426 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25435 getAutoCreate : function(){
25439 cls : 'small-box ',
25447 cls : 'roo-headline',
25448 html : this.headline
25452 cls : 'roo-content',
25453 html : this.content
25467 cls : 'ion ' + this.icon
25476 cls : 'small-box-footer',
25477 href : this.fhref || '#',
25481 cfg.cn.push(footer);
25488 onRender : function(ct,position){
25489 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25496 setHeadline: function (value)
25498 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25501 setFooter: function (value, href)
25503 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25506 this.el.select('a.small-box-footer',true).first().attr('href', href);
25511 setContent: function (value)
25513 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25516 initEvents: function()
25530 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25533 * @class Roo.bootstrap.dash.TabBox
25534 * @extends Roo.bootstrap.Component
25535 * Bootstrap TabBox class
25536 * @cfg {String} title Title of the TabBox
25537 * @cfg {String} icon Icon of the TabBox
25538 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25539 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25542 * Create a new TabBox
25543 * @param {Object} config The config object
25547 Roo.bootstrap.dash.TabBox = function(config){
25548 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25553 * When a pane is added
25554 * @param {Roo.bootstrap.dash.TabPane} pane
25558 * @event activatepane
25559 * When a pane is activated
25560 * @param {Roo.bootstrap.dash.TabPane} pane
25562 "activatepane" : true
25570 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25575 tabScrollable : false,
25577 getChildContainer : function()
25579 return this.el.select('.tab-content', true).first();
25582 getAutoCreate : function(){
25586 cls: 'pull-left header',
25594 cls: 'fa ' + this.icon
25600 cls: 'nav nav-tabs pull-right',
25606 if(this.tabScrollable){
25613 cls: 'nav nav-tabs pull-right',
25624 cls: 'nav-tabs-custom',
25629 cls: 'tab-content no-padding',
25637 initEvents : function()
25639 //Roo.log('add add pane handler');
25640 this.on('addpane', this.onAddPane, this);
25643 * Updates the box title
25644 * @param {String} html to set the title to.
25646 setTitle : function(value)
25648 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25650 onAddPane : function(pane)
25652 this.panes.push(pane);
25653 //Roo.log('addpane');
25655 // tabs are rendere left to right..
25656 if(!this.showtabs){
25660 var ctr = this.el.select('.nav-tabs', true).first();
25663 var existing = ctr.select('.nav-tab',true);
25664 var qty = existing.getCount();;
25667 var tab = ctr.createChild({
25669 cls : 'nav-tab' + (qty ? '' : ' active'),
25677 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25680 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25682 pane.el.addClass('active');
25687 onTabClick : function(ev,un,ob,pane)
25689 //Roo.log('tab - prev default');
25690 ev.preventDefault();
25693 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25694 pane.tab.addClass('active');
25695 //Roo.log(pane.title);
25696 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25697 // technically we should have a deactivate event.. but maybe add later.
25698 // and it should not de-activate the selected tab...
25699 this.fireEvent('activatepane', pane);
25700 pane.el.addClass('active');
25701 pane.fireEvent('activate');
25706 getActivePane : function()
25709 Roo.each(this.panes, function(p) {
25710 if(p.el.hasClass('active')){
25731 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25733 * @class Roo.bootstrap.TabPane
25734 * @extends Roo.bootstrap.Component
25735 * Bootstrap TabPane class
25736 * @cfg {Boolean} active (false | true) Default false
25737 * @cfg {String} title title of panel
25741 * Create a new TabPane
25742 * @param {Object} config The config object
25745 Roo.bootstrap.dash.TabPane = function(config){
25746 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25752 * When a pane is activated
25753 * @param {Roo.bootstrap.dash.TabPane} pane
25760 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25765 // the tabBox that this is attached to.
25768 getAutoCreate : function()
25776 cfg.cls += ' active';
25781 initEvents : function()
25783 //Roo.log('trigger add pane handler');
25784 this.parent().fireEvent('addpane', this)
25788 * Updates the tab title
25789 * @param {String} html to set the title to.
25791 setTitle: function(str)
25797 this.tab.select('a', true).first().dom.innerHTML = str;
25814 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25817 * @class Roo.bootstrap.menu.Menu
25818 * @extends Roo.bootstrap.Component
25819 * Bootstrap Menu class - container for Menu
25820 * @cfg {String} html Text of the menu
25821 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25822 * @cfg {String} icon Font awesome icon
25823 * @cfg {String} pos Menu align to (top | bottom) default bottom
25827 * Create a new Menu
25828 * @param {Object} config The config object
25832 Roo.bootstrap.menu.Menu = function(config){
25833 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25837 * @event beforeshow
25838 * Fires before this menu is displayed
25839 * @param {Roo.bootstrap.menu.Menu} this
25843 * @event beforehide
25844 * Fires before this menu is hidden
25845 * @param {Roo.bootstrap.menu.Menu} this
25850 * Fires after this menu is displayed
25851 * @param {Roo.bootstrap.menu.Menu} this
25856 * Fires after this menu is hidden
25857 * @param {Roo.bootstrap.menu.Menu} this
25862 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25863 * @param {Roo.bootstrap.menu.Menu} this
25864 * @param {Roo.EventObject} e
25871 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25875 weight : 'default',
25880 getChildContainer : function() {
25881 if(this.isSubMenu){
25885 return this.el.select('ul.dropdown-menu', true).first();
25888 getAutoCreate : function()
25893 cls : 'roo-menu-text',
25901 cls : 'fa ' + this.icon
25912 cls : 'dropdown-button btn btn-' + this.weight,
25917 cls : 'dropdown-toggle btn btn-' + this.weight,
25927 cls : 'dropdown-menu'
25933 if(this.pos == 'top'){
25934 cfg.cls += ' dropup';
25937 if(this.isSubMenu){
25940 cls : 'dropdown-menu'
25947 onRender : function(ct, position)
25949 this.isSubMenu = ct.hasClass('dropdown-submenu');
25951 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25954 initEvents : function()
25956 if(this.isSubMenu){
25960 this.hidden = true;
25962 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25963 this.triggerEl.on('click', this.onTriggerPress, this);
25965 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25966 this.buttonEl.on('click', this.onClick, this);
25972 if(this.isSubMenu){
25976 return this.el.select('ul.dropdown-menu', true).first();
25979 onClick : function(e)
25981 this.fireEvent("click", this, e);
25984 onTriggerPress : function(e)
25986 if (this.isVisible()) {
25993 isVisible : function(){
25994 return !this.hidden;
25999 this.fireEvent("beforeshow", this);
26001 this.hidden = false;
26002 this.el.addClass('open');
26004 Roo.get(document).on("mouseup", this.onMouseUp, this);
26006 this.fireEvent("show", this);
26013 this.fireEvent("beforehide", this);
26015 this.hidden = true;
26016 this.el.removeClass('open');
26018 Roo.get(document).un("mouseup", this.onMouseUp);
26020 this.fireEvent("hide", this);
26023 onMouseUp : function()
26037 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26040 * @class Roo.bootstrap.menu.Item
26041 * @extends Roo.bootstrap.Component
26042 * Bootstrap MenuItem class
26043 * @cfg {Boolean} submenu (true | false) default false
26044 * @cfg {String} html text of the item
26045 * @cfg {String} href the link
26046 * @cfg {Boolean} disable (true | false) default false
26047 * @cfg {Boolean} preventDefault (true | false) default true
26048 * @cfg {String} icon Font awesome icon
26049 * @cfg {String} pos Submenu align to (left | right) default right
26053 * Create a new Item
26054 * @param {Object} config The config object
26058 Roo.bootstrap.menu.Item = function(config){
26059 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26063 * Fires when the mouse is hovering over this menu
26064 * @param {Roo.bootstrap.menu.Item} this
26065 * @param {Roo.EventObject} e
26070 * Fires when the mouse exits this menu
26071 * @param {Roo.bootstrap.menu.Item} this
26072 * @param {Roo.EventObject} e
26078 * The raw click event for the entire grid.
26079 * @param {Roo.EventObject} e
26085 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26090 preventDefault: true,
26095 getAutoCreate : function()
26100 cls : 'roo-menu-item-text',
26108 cls : 'fa ' + this.icon
26117 href : this.href || '#',
26124 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26128 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26130 if(this.pos == 'left'){
26131 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26138 initEvents : function()
26140 this.el.on('mouseover', this.onMouseOver, this);
26141 this.el.on('mouseout', this.onMouseOut, this);
26143 this.el.select('a', true).first().on('click', this.onClick, this);
26147 onClick : function(e)
26149 if(this.preventDefault){
26150 e.preventDefault();
26153 this.fireEvent("click", this, e);
26156 onMouseOver : function(e)
26158 if(this.submenu && this.pos == 'left'){
26159 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26162 this.fireEvent("mouseover", this, e);
26165 onMouseOut : function(e)
26167 this.fireEvent("mouseout", this, e);
26179 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26182 * @class Roo.bootstrap.menu.Separator
26183 * @extends Roo.bootstrap.Component
26184 * Bootstrap Separator class
26187 * Create a new Separator
26188 * @param {Object} config The config object
26192 Roo.bootstrap.menu.Separator = function(config){
26193 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26196 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26198 getAutoCreate : function(){
26219 * @class Roo.bootstrap.Tooltip
26220 * Bootstrap Tooltip class
26221 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26222 * to determine which dom element triggers the tooltip.
26224 * It needs to add support for additional attributes like tooltip-position
26227 * Create a new Toolti
26228 * @param {Object} config The config object
26231 Roo.bootstrap.Tooltip = function(config){
26232 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26234 this.alignment = Roo.bootstrap.Tooltip.alignment;
26236 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26237 this.alignment = config.alignment;
26242 Roo.apply(Roo.bootstrap.Tooltip, {
26244 * @function init initialize tooltip monitoring.
26248 currentTip : false,
26249 currentRegion : false,
26255 Roo.get(document).on('mouseover', this.enter ,this);
26256 Roo.get(document).on('mouseout', this.leave, this);
26259 this.currentTip = new Roo.bootstrap.Tooltip();
26262 enter : function(ev)
26264 var dom = ev.getTarget();
26266 //Roo.log(['enter',dom]);
26267 var el = Roo.fly(dom);
26268 if (this.currentEl) {
26270 //Roo.log(this.currentEl);
26271 //Roo.log(this.currentEl.contains(dom));
26272 if (this.currentEl == el) {
26275 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26281 if (this.currentTip.el) {
26282 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26286 if(!el || el.dom == document){
26292 // you can not look for children, as if el is the body.. then everythign is the child..
26293 if (!el.attr('tooltip')) { //
26294 if (!el.select("[tooltip]").elements.length) {
26297 // is the mouse over this child...?
26298 bindEl = el.select("[tooltip]").first();
26299 var xy = ev.getXY();
26300 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26301 //Roo.log("not in region.");
26304 //Roo.log("child element over..");
26307 this.currentEl = bindEl;
26308 this.currentTip.bind(bindEl);
26309 this.currentRegion = Roo.lib.Region.getRegion(dom);
26310 this.currentTip.enter();
26313 leave : function(ev)
26315 var dom = ev.getTarget();
26316 //Roo.log(['leave',dom]);
26317 if (!this.currentEl) {
26322 if (dom != this.currentEl.dom) {
26325 var xy = ev.getXY();
26326 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26329 // only activate leave if mouse cursor is outside... bounding box..
26334 if (this.currentTip) {
26335 this.currentTip.leave();
26337 //Roo.log('clear currentEl');
26338 this.currentEl = false;
26343 'left' : ['r-l', [-2,0], 'right'],
26344 'right' : ['l-r', [2,0], 'left'],
26345 'bottom' : ['t-b', [0,2], 'top'],
26346 'top' : [ 'b-t', [0,-2], 'bottom']
26352 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26357 delay : null, // can be { show : 300 , hide: 500}
26361 hoverState : null, //???
26363 placement : 'bottom',
26367 getAutoCreate : function(){
26374 cls : 'tooltip-arrow'
26377 cls : 'tooltip-inner'
26384 bind : function(el)
26390 enter : function () {
26392 if (this.timeout != null) {
26393 clearTimeout(this.timeout);
26396 this.hoverState = 'in';
26397 //Roo.log("enter - show");
26398 if (!this.delay || !this.delay.show) {
26403 this.timeout = setTimeout(function () {
26404 if (_t.hoverState == 'in') {
26407 }, this.delay.show);
26411 clearTimeout(this.timeout);
26413 this.hoverState = 'out';
26414 if (!this.delay || !this.delay.hide) {
26420 this.timeout = setTimeout(function () {
26421 //Roo.log("leave - timeout");
26423 if (_t.hoverState == 'out') {
26425 Roo.bootstrap.Tooltip.currentEl = false;
26430 show : function (msg)
26433 this.render(document.body);
26436 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26438 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26440 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26442 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26444 var placement = typeof this.placement == 'function' ?
26445 this.placement.call(this, this.el, on_el) :
26448 var autoToken = /\s?auto?\s?/i;
26449 var autoPlace = autoToken.test(placement);
26451 placement = placement.replace(autoToken, '') || 'top';
26455 //this.el.setXY([0,0]);
26457 //this.el.dom.style.display='block';
26459 //this.el.appendTo(on_el);
26461 var p = this.getPosition();
26462 var box = this.el.getBox();
26468 var align = this.alignment[placement];
26470 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26472 if(placement == 'top' || placement == 'bottom'){
26474 placement = 'right';
26477 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26478 placement = 'left';
26481 var scroll = Roo.select('body', true).first().getScroll();
26483 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26487 align = this.alignment[placement];
26490 this.el.alignTo(this.bindEl, align[0],align[1]);
26491 //var arrow = this.el.select('.arrow',true).first();
26492 //arrow.set(align[2],
26494 this.el.addClass(placement);
26496 this.el.addClass('in fade');
26498 this.hoverState = null;
26500 if (this.el.hasClass('fade')) {
26511 //this.el.setXY([0,0]);
26512 this.el.removeClass('in');
26528 * @class Roo.bootstrap.LocationPicker
26529 * @extends Roo.bootstrap.Component
26530 * Bootstrap LocationPicker class
26531 * @cfg {Number} latitude Position when init default 0
26532 * @cfg {Number} longitude Position when init default 0
26533 * @cfg {Number} zoom default 15
26534 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26535 * @cfg {Boolean} mapTypeControl default false
26536 * @cfg {Boolean} disableDoubleClickZoom default false
26537 * @cfg {Boolean} scrollwheel default true
26538 * @cfg {Boolean} streetViewControl default false
26539 * @cfg {Number} radius default 0
26540 * @cfg {String} locationName
26541 * @cfg {Boolean} draggable default true
26542 * @cfg {Boolean} enableAutocomplete default false
26543 * @cfg {Boolean} enableReverseGeocode default true
26544 * @cfg {String} markerTitle
26547 * Create a new LocationPicker
26548 * @param {Object} config The config object
26552 Roo.bootstrap.LocationPicker = function(config){
26554 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26559 * Fires when the picker initialized.
26560 * @param {Roo.bootstrap.LocationPicker} this
26561 * @param {Google Location} location
26565 * @event positionchanged
26566 * Fires when the picker position changed.
26567 * @param {Roo.bootstrap.LocationPicker} this
26568 * @param {Google Location} location
26570 positionchanged : true,
26573 * Fires when the map resize.
26574 * @param {Roo.bootstrap.LocationPicker} this
26579 * Fires when the map show.
26580 * @param {Roo.bootstrap.LocationPicker} this
26585 * Fires when the map hide.
26586 * @param {Roo.bootstrap.LocationPicker} this
26591 * Fires when click the map.
26592 * @param {Roo.bootstrap.LocationPicker} this
26593 * @param {Map event} e
26597 * @event mapRightClick
26598 * Fires when right click the map.
26599 * @param {Roo.bootstrap.LocationPicker} this
26600 * @param {Map event} e
26602 mapRightClick : true,
26604 * @event markerClick
26605 * Fires when click the marker.
26606 * @param {Roo.bootstrap.LocationPicker} this
26607 * @param {Map event} e
26609 markerClick : true,
26611 * @event markerRightClick
26612 * Fires when right click the marker.
26613 * @param {Roo.bootstrap.LocationPicker} this
26614 * @param {Map event} e
26616 markerRightClick : true,
26618 * @event OverlayViewDraw
26619 * Fires when OverlayView Draw
26620 * @param {Roo.bootstrap.LocationPicker} this
26622 OverlayViewDraw : true,
26624 * @event OverlayViewOnAdd
26625 * Fires when OverlayView Draw
26626 * @param {Roo.bootstrap.LocationPicker} this
26628 OverlayViewOnAdd : true,
26630 * @event OverlayViewOnRemove
26631 * Fires when OverlayView Draw
26632 * @param {Roo.bootstrap.LocationPicker} this
26634 OverlayViewOnRemove : true,
26636 * @event OverlayViewShow
26637 * Fires when OverlayView Draw
26638 * @param {Roo.bootstrap.LocationPicker} this
26639 * @param {Pixel} cpx
26641 OverlayViewShow : true,
26643 * @event OverlayViewHide
26644 * Fires when OverlayView Draw
26645 * @param {Roo.bootstrap.LocationPicker} this
26647 OverlayViewHide : true,
26649 * @event loadexception
26650 * Fires when load google lib failed.
26651 * @param {Roo.bootstrap.LocationPicker} this
26653 loadexception : true
26658 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26660 gMapContext: false,
26666 mapTypeControl: false,
26667 disableDoubleClickZoom: false,
26669 streetViewControl: false,
26673 enableAutocomplete: false,
26674 enableReverseGeocode: true,
26677 getAutoCreate: function()
26682 cls: 'roo-location-picker'
26688 initEvents: function(ct, position)
26690 if(!this.el.getWidth() || this.isApplied()){
26694 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26699 initial: function()
26701 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26702 this.fireEvent('loadexception', this);
26706 if(!this.mapTypeId){
26707 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26710 this.gMapContext = this.GMapContext();
26712 this.initOverlayView();
26714 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26718 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26719 _this.setPosition(_this.gMapContext.marker.position);
26722 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26723 _this.fireEvent('mapClick', this, event);
26727 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26728 _this.fireEvent('mapRightClick', this, event);
26732 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26733 _this.fireEvent('markerClick', this, event);
26737 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26738 _this.fireEvent('markerRightClick', this, event);
26742 this.setPosition(this.gMapContext.location);
26744 this.fireEvent('initial', this, this.gMapContext.location);
26747 initOverlayView: function()
26751 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26755 _this.fireEvent('OverlayViewDraw', _this);
26760 _this.fireEvent('OverlayViewOnAdd', _this);
26763 onRemove: function()
26765 _this.fireEvent('OverlayViewOnRemove', _this);
26768 show: function(cpx)
26770 _this.fireEvent('OverlayViewShow', _this, cpx);
26775 _this.fireEvent('OverlayViewHide', _this);
26781 fromLatLngToContainerPixel: function(event)
26783 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26786 isApplied: function()
26788 return this.getGmapContext() == false ? false : true;
26791 getGmapContext: function()
26793 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26796 GMapContext: function()
26798 var position = new google.maps.LatLng(this.latitude, this.longitude);
26800 var _map = new google.maps.Map(this.el.dom, {
26803 mapTypeId: this.mapTypeId,
26804 mapTypeControl: this.mapTypeControl,
26805 disableDoubleClickZoom: this.disableDoubleClickZoom,
26806 scrollwheel: this.scrollwheel,
26807 streetViewControl: this.streetViewControl,
26808 locationName: this.locationName,
26809 draggable: this.draggable,
26810 enableAutocomplete: this.enableAutocomplete,
26811 enableReverseGeocode: this.enableReverseGeocode
26814 var _marker = new google.maps.Marker({
26815 position: position,
26817 title: this.markerTitle,
26818 draggable: this.draggable
26825 location: position,
26826 radius: this.radius,
26827 locationName: this.locationName,
26828 addressComponents: {
26829 formatted_address: null,
26830 addressLine1: null,
26831 addressLine2: null,
26833 streetNumber: null,
26837 stateOrProvince: null
26840 domContainer: this.el.dom,
26841 geodecoder: new google.maps.Geocoder()
26845 drawCircle: function(center, radius, options)
26847 if (this.gMapContext.circle != null) {
26848 this.gMapContext.circle.setMap(null);
26852 options = Roo.apply({}, options, {
26853 strokeColor: "#0000FF",
26854 strokeOpacity: .35,
26856 fillColor: "#0000FF",
26860 options.map = this.gMapContext.map;
26861 options.radius = radius;
26862 options.center = center;
26863 this.gMapContext.circle = new google.maps.Circle(options);
26864 return this.gMapContext.circle;
26870 setPosition: function(location)
26872 this.gMapContext.location = location;
26873 this.gMapContext.marker.setPosition(location);
26874 this.gMapContext.map.panTo(location);
26875 this.drawCircle(location, this.gMapContext.radius, {});
26879 if (this.gMapContext.settings.enableReverseGeocode) {
26880 this.gMapContext.geodecoder.geocode({
26881 latLng: this.gMapContext.location
26882 }, function(results, status) {
26884 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26885 _this.gMapContext.locationName = results[0].formatted_address;
26886 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26888 _this.fireEvent('positionchanged', this, location);
26895 this.fireEvent('positionchanged', this, location);
26900 google.maps.event.trigger(this.gMapContext.map, "resize");
26902 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26904 this.fireEvent('resize', this);
26907 setPositionByLatLng: function(latitude, longitude)
26909 this.setPosition(new google.maps.LatLng(latitude, longitude));
26912 getCurrentPosition: function()
26915 latitude: this.gMapContext.location.lat(),
26916 longitude: this.gMapContext.location.lng()
26920 getAddressName: function()
26922 return this.gMapContext.locationName;
26925 getAddressComponents: function()
26927 return this.gMapContext.addressComponents;
26930 address_component_from_google_geocode: function(address_components)
26934 for (var i = 0; i < address_components.length; i++) {
26935 var component = address_components[i];
26936 if (component.types.indexOf("postal_code") >= 0) {
26937 result.postalCode = component.short_name;
26938 } else if (component.types.indexOf("street_number") >= 0) {
26939 result.streetNumber = component.short_name;
26940 } else if (component.types.indexOf("route") >= 0) {
26941 result.streetName = component.short_name;
26942 } else if (component.types.indexOf("neighborhood") >= 0) {
26943 result.city = component.short_name;
26944 } else if (component.types.indexOf("locality") >= 0) {
26945 result.city = component.short_name;
26946 } else if (component.types.indexOf("sublocality") >= 0) {
26947 result.district = component.short_name;
26948 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26949 result.stateOrProvince = component.short_name;
26950 } else if (component.types.indexOf("country") >= 0) {
26951 result.country = component.short_name;
26955 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26956 result.addressLine2 = "";
26960 setZoomLevel: function(zoom)
26962 this.gMapContext.map.setZoom(zoom);
26975 this.fireEvent('show', this);
26986 this.fireEvent('hide', this);
26991 Roo.apply(Roo.bootstrap.LocationPicker, {
26993 OverlayView : function(map, options)
26995 options = options || {};
27009 * @class Roo.bootstrap.Alert
27010 * @extends Roo.bootstrap.Component
27011 * Bootstrap Alert class
27012 * @cfg {String} title The title of alert
27013 * @cfg {String} html The content of alert
27014 * @cfg {String} weight ( success | info | warning | danger )
27015 * @cfg {String} faicon font-awesomeicon
27018 * Create a new alert
27019 * @param {Object} config The config object
27023 Roo.bootstrap.Alert = function(config){
27024 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27028 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
27035 getAutoCreate : function()
27044 cls : 'roo-alert-icon'
27049 cls : 'roo-alert-title',
27054 cls : 'roo-alert-text',
27061 cfg.cn[0].cls += ' fa ' + this.faicon;
27065 cfg.cls += ' alert-' + this.weight;
27071 initEvents: function()
27073 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27076 setTitle : function(str)
27078 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27081 setText : function(str)
27083 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27086 setWeight : function(weight)
27089 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27092 this.weight = weight;
27094 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27097 setIcon : function(icon)
27100 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27103 this.faicon = icon;
27105 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27126 * @class Roo.bootstrap.UploadCropbox
27127 * @extends Roo.bootstrap.Component
27128 * Bootstrap UploadCropbox class
27129 * @cfg {String} emptyText show when image has been loaded
27130 * @cfg {String} rotateNotify show when image too small to rotate
27131 * @cfg {Number} errorTimeout default 3000
27132 * @cfg {Number} minWidth default 300
27133 * @cfg {Number} minHeight default 300
27134 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27135 * @cfg {Boolean} isDocument (true|false) default false
27136 * @cfg {String} url action url
27137 * @cfg {String} paramName default 'imageUpload'
27138 * @cfg {String} method default POST
27139 * @cfg {Boolean} loadMask (true|false) default true
27140 * @cfg {Boolean} loadingText default 'Loading...'
27143 * Create a new UploadCropbox
27144 * @param {Object} config The config object
27147 Roo.bootstrap.UploadCropbox = function(config){
27148 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27152 * @event beforeselectfile
27153 * Fire before select file
27154 * @param {Roo.bootstrap.UploadCropbox} this
27156 "beforeselectfile" : true,
27159 * Fire after initEvent
27160 * @param {Roo.bootstrap.UploadCropbox} this
27165 * Fire after initEvent
27166 * @param {Roo.bootstrap.UploadCropbox} this
27167 * @param {String} data
27172 * Fire when preparing the file data
27173 * @param {Roo.bootstrap.UploadCropbox} this
27174 * @param {Object} file
27179 * Fire when get exception
27180 * @param {Roo.bootstrap.UploadCropbox} this
27181 * @param {XMLHttpRequest} xhr
27183 "exception" : true,
27185 * @event beforeloadcanvas
27186 * Fire before load the canvas
27187 * @param {Roo.bootstrap.UploadCropbox} this
27188 * @param {String} src
27190 "beforeloadcanvas" : true,
27193 * Fire when trash image
27194 * @param {Roo.bootstrap.UploadCropbox} this
27199 * Fire when download the image
27200 * @param {Roo.bootstrap.UploadCropbox} this
27204 * @event footerbuttonclick
27205 * Fire when footerbuttonclick
27206 * @param {Roo.bootstrap.UploadCropbox} this
27207 * @param {String} type
27209 "footerbuttonclick" : true,
27213 * @param {Roo.bootstrap.UploadCropbox} this
27218 * Fire when rotate the image
27219 * @param {Roo.bootstrap.UploadCropbox} this
27220 * @param {String} pos
27225 * Fire when inspect the file
27226 * @param {Roo.bootstrap.UploadCropbox} this
27227 * @param {Object} file
27232 * Fire when xhr upload the file
27233 * @param {Roo.bootstrap.UploadCropbox} this
27234 * @param {Object} data
27239 * Fire when arrange the file data
27240 * @param {Roo.bootstrap.UploadCropbox} this
27241 * @param {Object} formData
27246 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27249 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27251 emptyText : 'Click to upload image',
27252 rotateNotify : 'Image is too small to rotate',
27253 errorTimeout : 3000,
27267 cropType : 'image/jpeg',
27269 canvasLoaded : false,
27270 isDocument : false,
27272 paramName : 'imageUpload',
27274 loadingText : 'Loading...',
27277 getAutoCreate : function()
27281 cls : 'roo-upload-cropbox',
27285 cls : 'roo-upload-cropbox-selector',
27290 cls : 'roo-upload-cropbox-body',
27291 style : 'cursor:pointer',
27295 cls : 'roo-upload-cropbox-preview'
27299 cls : 'roo-upload-cropbox-thumb'
27303 cls : 'roo-upload-cropbox-empty-notify',
27304 html : this.emptyText
27308 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27309 html : this.rotateNotify
27315 cls : 'roo-upload-cropbox-footer',
27318 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27328 onRender : function(ct, position)
27330 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27332 if (this.buttons.length) {
27334 Roo.each(this.buttons, function(bb) {
27336 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27338 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27344 this.maskEl = this.el;
27348 initEvents : function()
27350 this.urlAPI = (window.createObjectURL && window) ||
27351 (window.URL && URL.revokeObjectURL && URL) ||
27352 (window.webkitURL && webkitURL);
27354 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27355 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27357 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27358 this.selectorEl.hide();
27360 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27361 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27363 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27364 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27365 this.thumbEl.hide();
27367 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27368 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27370 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27371 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27372 this.errorEl.hide();
27374 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27375 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27376 this.footerEl.hide();
27378 this.setThumbBoxSize();
27384 this.fireEvent('initial', this);
27391 window.addEventListener("resize", function() { _this.resize(); } );
27393 this.bodyEl.on('click', this.beforeSelectFile, this);
27396 this.bodyEl.on('touchstart', this.onTouchStart, this);
27397 this.bodyEl.on('touchmove', this.onTouchMove, this);
27398 this.bodyEl.on('touchend', this.onTouchEnd, this);
27402 this.bodyEl.on('mousedown', this.onMouseDown, this);
27403 this.bodyEl.on('mousemove', this.onMouseMove, this);
27404 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27405 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27406 Roo.get(document).on('mouseup', this.onMouseUp, this);
27409 this.selectorEl.on('change', this.onFileSelected, this);
27415 this.baseScale = 1;
27417 this.baseRotate = 1;
27418 this.dragable = false;
27419 this.pinching = false;
27422 this.cropData = false;
27423 this.notifyEl.dom.innerHTML = this.emptyText;
27425 this.selectorEl.dom.value = '';
27429 resize : function()
27431 if(this.fireEvent('resize', this) != false){
27432 this.setThumbBoxPosition();
27433 this.setCanvasPosition();
27437 onFooterButtonClick : function(e, el, o, type)
27440 case 'rotate-left' :
27441 this.onRotateLeft(e);
27443 case 'rotate-right' :
27444 this.onRotateRight(e);
27447 this.beforeSelectFile(e);
27462 this.fireEvent('footerbuttonclick', this, type);
27465 beforeSelectFile : function(e)
27467 e.preventDefault();
27469 if(this.fireEvent('beforeselectfile', this) != false){
27470 this.selectorEl.dom.click();
27474 onFileSelected : function(e)
27476 e.preventDefault();
27478 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27482 var file = this.selectorEl.dom.files[0];
27484 if(this.fireEvent('inspect', this, file) != false){
27485 this.prepare(file);
27490 trash : function(e)
27492 this.fireEvent('trash', this);
27495 download : function(e)
27497 this.fireEvent('download', this);
27500 loadCanvas : function(src)
27502 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27506 this.imageEl = document.createElement('img');
27510 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27512 this.imageEl.src = src;
27516 onLoadCanvas : function()
27518 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27519 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27521 this.bodyEl.un('click', this.beforeSelectFile, this);
27523 this.notifyEl.hide();
27524 this.thumbEl.show();
27525 this.footerEl.show();
27527 this.baseRotateLevel();
27529 if(this.isDocument){
27530 this.setThumbBoxSize();
27533 this.setThumbBoxPosition();
27535 this.baseScaleLevel();
27541 this.canvasLoaded = true;
27544 this.maskEl.unmask();
27549 setCanvasPosition : function()
27551 if(!this.canvasEl){
27555 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27556 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27558 this.previewEl.setLeft(pw);
27559 this.previewEl.setTop(ph);
27563 onMouseDown : function(e)
27567 this.dragable = true;
27568 this.pinching = false;
27570 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27571 this.dragable = false;
27575 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27576 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27580 onMouseMove : function(e)
27584 if(!this.canvasLoaded){
27588 if (!this.dragable){
27592 var minX = Math.ceil(this.thumbEl.getLeft(true));
27593 var minY = Math.ceil(this.thumbEl.getTop(true));
27595 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27596 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27598 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27599 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27601 x = x - this.mouseX;
27602 y = y - this.mouseY;
27604 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27605 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27607 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27608 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27610 this.previewEl.setLeft(bgX);
27611 this.previewEl.setTop(bgY);
27613 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27614 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27617 onMouseUp : function(e)
27621 this.dragable = false;
27624 onMouseWheel : function(e)
27628 this.startScale = this.scale;
27630 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27632 if(!this.zoomable()){
27633 this.scale = this.startScale;
27642 zoomable : function()
27644 var minScale = this.thumbEl.getWidth() / this.minWidth;
27646 if(this.minWidth < this.minHeight){
27647 minScale = this.thumbEl.getHeight() / this.minHeight;
27650 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27651 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27655 (this.rotate == 0 || this.rotate == 180) &&
27657 width > this.imageEl.OriginWidth ||
27658 height > this.imageEl.OriginHeight ||
27659 (width < this.minWidth && height < this.minHeight)
27667 (this.rotate == 90 || this.rotate == 270) &&
27669 width > this.imageEl.OriginWidth ||
27670 height > this.imageEl.OriginHeight ||
27671 (width < this.minHeight && height < this.minWidth)
27678 !this.isDocument &&
27679 (this.rotate == 0 || this.rotate == 180) &&
27681 width < this.minWidth ||
27682 width > this.imageEl.OriginWidth ||
27683 height < this.minHeight ||
27684 height > this.imageEl.OriginHeight
27691 !this.isDocument &&
27692 (this.rotate == 90 || this.rotate == 270) &&
27694 width < this.minHeight ||
27695 width > this.imageEl.OriginWidth ||
27696 height < this.minWidth ||
27697 height > this.imageEl.OriginHeight
27707 onRotateLeft : function(e)
27709 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27711 var minScale = this.thumbEl.getWidth() / this.minWidth;
27713 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27714 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27716 this.startScale = this.scale;
27718 while (this.getScaleLevel() < minScale){
27720 this.scale = this.scale + 1;
27722 if(!this.zoomable()){
27727 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27728 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27733 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27740 this.scale = this.startScale;
27742 this.onRotateFail();
27747 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27749 if(this.isDocument){
27750 this.setThumbBoxSize();
27751 this.setThumbBoxPosition();
27752 this.setCanvasPosition();
27757 this.fireEvent('rotate', this, 'left');
27761 onRotateRight : function(e)
27763 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27765 var minScale = this.thumbEl.getWidth() / this.minWidth;
27767 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27768 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27770 this.startScale = this.scale;
27772 while (this.getScaleLevel() < minScale){
27774 this.scale = this.scale + 1;
27776 if(!this.zoomable()){
27781 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27782 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27787 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27794 this.scale = this.startScale;
27796 this.onRotateFail();
27801 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27803 if(this.isDocument){
27804 this.setThumbBoxSize();
27805 this.setThumbBoxPosition();
27806 this.setCanvasPosition();
27811 this.fireEvent('rotate', this, 'right');
27814 onRotateFail : function()
27816 this.errorEl.show(true);
27820 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27825 this.previewEl.dom.innerHTML = '';
27827 var canvasEl = document.createElement("canvas");
27829 var contextEl = canvasEl.getContext("2d");
27831 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27832 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27833 var center = this.imageEl.OriginWidth / 2;
27835 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27836 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27837 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27838 center = this.imageEl.OriginHeight / 2;
27841 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27843 contextEl.translate(center, center);
27844 contextEl.rotate(this.rotate * Math.PI / 180);
27846 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27848 this.canvasEl = document.createElement("canvas");
27850 this.contextEl = this.canvasEl.getContext("2d");
27852 switch (this.rotate) {
27855 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27856 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27858 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27863 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27864 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27866 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27867 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27871 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27876 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27877 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27879 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27880 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27884 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);
27889 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27890 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27892 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27893 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27897 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);
27904 this.previewEl.appendChild(this.canvasEl);
27906 this.setCanvasPosition();
27911 if(!this.canvasLoaded){
27915 var imageCanvas = document.createElement("canvas");
27917 var imageContext = imageCanvas.getContext("2d");
27919 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27920 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27922 var center = imageCanvas.width / 2;
27924 imageContext.translate(center, center);
27926 imageContext.rotate(this.rotate * Math.PI / 180);
27928 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27930 var canvas = document.createElement("canvas");
27932 var context = canvas.getContext("2d");
27934 canvas.width = this.minWidth;
27935 canvas.height = this.minHeight;
27937 switch (this.rotate) {
27940 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27941 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27943 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27944 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27946 var targetWidth = this.minWidth - 2 * x;
27947 var targetHeight = this.minHeight - 2 * y;
27951 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27952 scale = targetWidth / width;
27955 if(x > 0 && y == 0){
27956 scale = targetHeight / height;
27959 if(x > 0 && y > 0){
27960 scale = targetWidth / width;
27962 if(width < height){
27963 scale = targetHeight / height;
27967 context.scale(scale, scale);
27969 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27970 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27972 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27973 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27975 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27980 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27981 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27983 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27984 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27986 var targetWidth = this.minWidth - 2 * x;
27987 var targetHeight = this.minHeight - 2 * y;
27991 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27992 scale = targetWidth / width;
27995 if(x > 0 && y == 0){
27996 scale = targetHeight / height;
27999 if(x > 0 && y > 0){
28000 scale = targetWidth / width;
28002 if(width < height){
28003 scale = targetHeight / height;
28007 context.scale(scale, scale);
28009 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28010 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28012 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28013 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28015 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28017 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28022 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28023 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28025 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28026 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28028 var targetWidth = this.minWidth - 2 * x;
28029 var targetHeight = this.minHeight - 2 * y;
28033 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28034 scale = targetWidth / width;
28037 if(x > 0 && y == 0){
28038 scale = targetHeight / height;
28041 if(x > 0 && y > 0){
28042 scale = targetWidth / width;
28044 if(width < height){
28045 scale = targetHeight / height;
28049 context.scale(scale, scale);
28051 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28052 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28054 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28055 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28057 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28058 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28060 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28065 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28066 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28068 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28069 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28071 var targetWidth = this.minWidth - 2 * x;
28072 var targetHeight = this.minHeight - 2 * y;
28076 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28077 scale = targetWidth / width;
28080 if(x > 0 && y == 0){
28081 scale = targetHeight / height;
28084 if(x > 0 && y > 0){
28085 scale = targetWidth / width;
28087 if(width < height){
28088 scale = targetHeight / height;
28092 context.scale(scale, scale);
28094 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28095 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28097 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28098 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28100 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28102 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28109 this.cropData = canvas.toDataURL(this.cropType);
28111 if(this.fireEvent('crop', this, this.cropData) !== false){
28112 this.process(this.file, this.cropData);
28119 setThumbBoxSize : function()
28123 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28124 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28125 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28127 this.minWidth = width;
28128 this.minHeight = height;
28130 if(this.rotate == 90 || this.rotate == 270){
28131 this.minWidth = height;
28132 this.minHeight = width;
28137 width = Math.ceil(this.minWidth * height / this.minHeight);
28139 if(this.minWidth > this.minHeight){
28141 height = Math.ceil(this.minHeight * width / this.minWidth);
28144 this.thumbEl.setStyle({
28145 width : width + 'px',
28146 height : height + 'px'
28153 setThumbBoxPosition : function()
28155 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28156 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28158 this.thumbEl.setLeft(x);
28159 this.thumbEl.setTop(y);
28163 baseRotateLevel : function()
28165 this.baseRotate = 1;
28168 typeof(this.exif) != 'undefined' &&
28169 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28170 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28172 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28175 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28179 baseScaleLevel : function()
28183 if(this.isDocument){
28185 if(this.baseRotate == 6 || this.baseRotate == 8){
28187 height = this.thumbEl.getHeight();
28188 this.baseScale = height / this.imageEl.OriginWidth;
28190 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28191 width = this.thumbEl.getWidth();
28192 this.baseScale = width / this.imageEl.OriginHeight;
28198 height = this.thumbEl.getHeight();
28199 this.baseScale = height / this.imageEl.OriginHeight;
28201 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28202 width = this.thumbEl.getWidth();
28203 this.baseScale = width / this.imageEl.OriginWidth;
28209 if(this.baseRotate == 6 || this.baseRotate == 8){
28211 width = this.thumbEl.getHeight();
28212 this.baseScale = width / this.imageEl.OriginHeight;
28214 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28215 height = this.thumbEl.getWidth();
28216 this.baseScale = height / this.imageEl.OriginHeight;
28219 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28220 height = this.thumbEl.getWidth();
28221 this.baseScale = height / this.imageEl.OriginHeight;
28223 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28224 width = this.thumbEl.getHeight();
28225 this.baseScale = width / this.imageEl.OriginWidth;
28232 width = this.thumbEl.getWidth();
28233 this.baseScale = width / this.imageEl.OriginWidth;
28235 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28236 height = this.thumbEl.getHeight();
28237 this.baseScale = height / this.imageEl.OriginHeight;
28240 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28242 height = this.thumbEl.getHeight();
28243 this.baseScale = height / this.imageEl.OriginHeight;
28245 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28246 width = this.thumbEl.getWidth();
28247 this.baseScale = width / this.imageEl.OriginWidth;
28255 getScaleLevel : function()
28257 return this.baseScale * Math.pow(1.1, this.scale);
28260 onTouchStart : function(e)
28262 if(!this.canvasLoaded){
28263 this.beforeSelectFile(e);
28267 var touches = e.browserEvent.touches;
28273 if(touches.length == 1){
28274 this.onMouseDown(e);
28278 if(touches.length != 2){
28284 for(var i = 0, finger; finger = touches[i]; i++){
28285 coords.push(finger.pageX, finger.pageY);
28288 var x = Math.pow(coords[0] - coords[2], 2);
28289 var y = Math.pow(coords[1] - coords[3], 2);
28291 this.startDistance = Math.sqrt(x + y);
28293 this.startScale = this.scale;
28295 this.pinching = true;
28296 this.dragable = false;
28300 onTouchMove : function(e)
28302 if(!this.pinching && !this.dragable){
28306 var touches = e.browserEvent.touches;
28313 this.onMouseMove(e);
28319 for(var i = 0, finger; finger = touches[i]; i++){
28320 coords.push(finger.pageX, finger.pageY);
28323 var x = Math.pow(coords[0] - coords[2], 2);
28324 var y = Math.pow(coords[1] - coords[3], 2);
28326 this.endDistance = Math.sqrt(x + y);
28328 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28330 if(!this.zoomable()){
28331 this.scale = this.startScale;
28339 onTouchEnd : function(e)
28341 this.pinching = false;
28342 this.dragable = false;
28346 process : function(file, crop)
28349 this.maskEl.mask(this.loadingText);
28352 this.xhr = new XMLHttpRequest();
28354 file.xhr = this.xhr;
28356 this.xhr.open(this.method, this.url, true);
28359 "Accept": "application/json",
28360 "Cache-Control": "no-cache",
28361 "X-Requested-With": "XMLHttpRequest"
28364 for (var headerName in headers) {
28365 var headerValue = headers[headerName];
28367 this.xhr.setRequestHeader(headerName, headerValue);
28373 this.xhr.onload = function()
28375 _this.xhrOnLoad(_this.xhr);
28378 this.xhr.onerror = function()
28380 _this.xhrOnError(_this.xhr);
28383 var formData = new FormData();
28385 formData.append('returnHTML', 'NO');
28388 formData.append('crop', crop);
28391 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28392 formData.append(this.paramName, file, file.name);
28395 if(typeof(file.filename) != 'undefined'){
28396 formData.append('filename', file.filename);
28399 if(typeof(file.mimetype) != 'undefined'){
28400 formData.append('mimetype', file.mimetype);
28403 if(this.fireEvent('arrange', this, formData) != false){
28404 this.xhr.send(formData);
28408 xhrOnLoad : function(xhr)
28411 this.maskEl.unmask();
28414 if (xhr.readyState !== 4) {
28415 this.fireEvent('exception', this, xhr);
28419 var response = Roo.decode(xhr.responseText);
28421 if(!response.success){
28422 this.fireEvent('exception', this, xhr);
28426 var response = Roo.decode(xhr.responseText);
28428 this.fireEvent('upload', this, response);
28432 xhrOnError : function()
28435 this.maskEl.unmask();
28438 Roo.log('xhr on error');
28440 var response = Roo.decode(xhr.responseText);
28446 prepare : function(file)
28449 this.maskEl.mask(this.loadingText);
28455 if(typeof(file) === 'string'){
28456 this.loadCanvas(file);
28460 if(!file || !this.urlAPI){
28465 this.cropType = file.type;
28469 if(this.fireEvent('prepare', this, this.file) != false){
28471 var reader = new FileReader();
28473 reader.onload = function (e) {
28474 if (e.target.error) {
28475 Roo.log(e.target.error);
28479 var buffer = e.target.result,
28480 dataView = new DataView(buffer),
28482 maxOffset = dataView.byteLength - 4,
28486 if (dataView.getUint16(0) === 0xffd8) {
28487 while (offset < maxOffset) {
28488 markerBytes = dataView.getUint16(offset);
28490 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28491 markerLength = dataView.getUint16(offset + 2) + 2;
28492 if (offset + markerLength > dataView.byteLength) {
28493 Roo.log('Invalid meta data: Invalid segment size.');
28497 if(markerBytes == 0xffe1){
28498 _this.parseExifData(
28505 offset += markerLength;
28515 var url = _this.urlAPI.createObjectURL(_this.file);
28517 _this.loadCanvas(url);
28522 reader.readAsArrayBuffer(this.file);
28528 parseExifData : function(dataView, offset, length)
28530 var tiffOffset = offset + 10,
28534 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28535 // No Exif data, might be XMP data instead
28539 // Check for the ASCII code for "Exif" (0x45786966):
28540 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28541 // No Exif data, might be XMP data instead
28544 if (tiffOffset + 8 > dataView.byteLength) {
28545 Roo.log('Invalid Exif data: Invalid segment size.');
28548 // Check for the two null bytes:
28549 if (dataView.getUint16(offset + 8) !== 0x0000) {
28550 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28553 // Check the byte alignment:
28554 switch (dataView.getUint16(tiffOffset)) {
28556 littleEndian = true;
28559 littleEndian = false;
28562 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28565 // Check for the TIFF tag marker (0x002A):
28566 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28567 Roo.log('Invalid Exif data: Missing TIFF marker.');
28570 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28571 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28573 this.parseExifTags(
28576 tiffOffset + dirOffset,
28581 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28586 if (dirOffset + 6 > dataView.byteLength) {
28587 Roo.log('Invalid Exif data: Invalid directory offset.');
28590 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28591 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28592 if (dirEndOffset + 4 > dataView.byteLength) {
28593 Roo.log('Invalid Exif data: Invalid directory size.');
28596 for (i = 0; i < tagsNumber; i += 1) {
28600 dirOffset + 2 + 12 * i, // tag offset
28604 // Return the offset to the next directory:
28605 return dataView.getUint32(dirEndOffset, littleEndian);
28608 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28610 var tag = dataView.getUint16(offset, littleEndian);
28612 this.exif[tag] = this.getExifValue(
28616 dataView.getUint16(offset + 2, littleEndian), // tag type
28617 dataView.getUint32(offset + 4, littleEndian), // tag length
28622 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28624 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28633 Roo.log('Invalid Exif data: Invalid tag type.');
28637 tagSize = tagType.size * length;
28638 // Determine if the value is contained in the dataOffset bytes,
28639 // or if the value at the dataOffset is a pointer to the actual data:
28640 dataOffset = tagSize > 4 ?
28641 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28642 if (dataOffset + tagSize > dataView.byteLength) {
28643 Roo.log('Invalid Exif data: Invalid data offset.');
28646 if (length === 1) {
28647 return tagType.getValue(dataView, dataOffset, littleEndian);
28650 for (i = 0; i < length; i += 1) {
28651 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28654 if (tagType.ascii) {
28656 // Concatenate the chars:
28657 for (i = 0; i < values.length; i += 1) {
28659 // Ignore the terminating NULL byte(s):
28660 if (c === '\u0000') {
28672 Roo.apply(Roo.bootstrap.UploadCropbox, {
28674 'Orientation': 0x0112
28678 1: 0, //'top-left',
28680 3: 180, //'bottom-right',
28681 // 4: 'bottom-left',
28683 6: 90, //'right-top',
28684 // 7: 'right-bottom',
28685 8: 270 //'left-bottom'
28689 // byte, 8-bit unsigned int:
28691 getValue: function (dataView, dataOffset) {
28692 return dataView.getUint8(dataOffset);
28696 // ascii, 8-bit byte:
28698 getValue: function (dataView, dataOffset) {
28699 return String.fromCharCode(dataView.getUint8(dataOffset));
28704 // short, 16 bit int:
28706 getValue: function (dataView, dataOffset, littleEndian) {
28707 return dataView.getUint16(dataOffset, littleEndian);
28711 // long, 32 bit int:
28713 getValue: function (dataView, dataOffset, littleEndian) {
28714 return dataView.getUint32(dataOffset, littleEndian);
28718 // rational = two long values, first is numerator, second is denominator:
28720 getValue: function (dataView, dataOffset, littleEndian) {
28721 return dataView.getUint32(dataOffset, littleEndian) /
28722 dataView.getUint32(dataOffset + 4, littleEndian);
28726 // slong, 32 bit signed int:
28728 getValue: function (dataView, dataOffset, littleEndian) {
28729 return dataView.getInt32(dataOffset, littleEndian);
28733 // srational, two slongs, first is numerator, second is denominator:
28735 getValue: function (dataView, dataOffset, littleEndian) {
28736 return dataView.getInt32(dataOffset, littleEndian) /
28737 dataView.getInt32(dataOffset + 4, littleEndian);
28747 cls : 'btn-group roo-upload-cropbox-rotate-left',
28748 action : 'rotate-left',
28752 cls : 'btn btn-default',
28753 html : '<i class="fa fa-undo"></i>'
28759 cls : 'btn-group roo-upload-cropbox-picture',
28760 action : 'picture',
28764 cls : 'btn btn-default',
28765 html : '<i class="fa fa-picture-o"></i>'
28771 cls : 'btn-group roo-upload-cropbox-rotate-right',
28772 action : 'rotate-right',
28776 cls : 'btn btn-default',
28777 html : '<i class="fa fa-repeat"></i>'
28785 cls : 'btn-group roo-upload-cropbox-rotate-left',
28786 action : 'rotate-left',
28790 cls : 'btn btn-default',
28791 html : '<i class="fa fa-undo"></i>'
28797 cls : 'btn-group roo-upload-cropbox-download',
28798 action : 'download',
28802 cls : 'btn btn-default',
28803 html : '<i class="fa fa-download"></i>'
28809 cls : 'btn-group roo-upload-cropbox-crop',
28814 cls : 'btn btn-default',
28815 html : '<i class="fa fa-crop"></i>'
28821 cls : 'btn-group roo-upload-cropbox-trash',
28826 cls : 'btn btn-default',
28827 html : '<i class="fa fa-trash"></i>'
28833 cls : 'btn-group roo-upload-cropbox-rotate-right',
28834 action : 'rotate-right',
28838 cls : 'btn btn-default',
28839 html : '<i class="fa fa-repeat"></i>'
28847 cls : 'btn-group roo-upload-cropbox-rotate-left',
28848 action : 'rotate-left',
28852 cls : 'btn btn-default',
28853 html : '<i class="fa fa-undo"></i>'
28859 cls : 'btn-group roo-upload-cropbox-rotate-right',
28860 action : 'rotate-right',
28864 cls : 'btn btn-default',
28865 html : '<i class="fa fa-repeat"></i>'
28878 * @class Roo.bootstrap.DocumentManager
28879 * @extends Roo.bootstrap.Component
28880 * Bootstrap DocumentManager class
28881 * @cfg {String} paramName default 'imageUpload'
28882 * @cfg {String} toolTipName default 'filename'
28883 * @cfg {String} method default POST
28884 * @cfg {String} url action url
28885 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28886 * @cfg {Boolean} multiple multiple upload default true
28887 * @cfg {Number} thumbSize default 300
28888 * @cfg {String} fieldLabel
28889 * @cfg {Number} labelWidth default 4
28890 * @cfg {String} labelAlign (left|top) default left
28891 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28892 * @cfg {Number} labellg set the width of label (1-12)
28893 * @cfg {Number} labelmd set the width of label (1-12)
28894 * @cfg {Number} labelsm set the width of label (1-12)
28895 * @cfg {Number} labelxs set the width of label (1-12)
28898 * Create a new DocumentManager
28899 * @param {Object} config The config object
28902 Roo.bootstrap.DocumentManager = function(config){
28903 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28906 this.delegates = [];
28911 * Fire when initial the DocumentManager
28912 * @param {Roo.bootstrap.DocumentManager} this
28917 * inspect selected file
28918 * @param {Roo.bootstrap.DocumentManager} this
28919 * @param {File} file
28924 * Fire when xhr load exception
28925 * @param {Roo.bootstrap.DocumentManager} this
28926 * @param {XMLHttpRequest} xhr
28928 "exception" : true,
28930 * @event afterupload
28931 * Fire when xhr load exception
28932 * @param {Roo.bootstrap.DocumentManager} this
28933 * @param {XMLHttpRequest} xhr
28935 "afterupload" : true,
28938 * prepare the form data
28939 * @param {Roo.bootstrap.DocumentManager} this
28940 * @param {Object} formData
28945 * Fire when remove the file
28946 * @param {Roo.bootstrap.DocumentManager} this
28947 * @param {Object} file
28952 * Fire after refresh the file
28953 * @param {Roo.bootstrap.DocumentManager} this
28958 * Fire after click the image
28959 * @param {Roo.bootstrap.DocumentManager} this
28960 * @param {Object} file
28965 * Fire when upload a image and editable set to true
28966 * @param {Roo.bootstrap.DocumentManager} this
28967 * @param {Object} file
28971 * @event beforeselectfile
28972 * Fire before select file
28973 * @param {Roo.bootstrap.DocumentManager} this
28975 "beforeselectfile" : true,
28978 * Fire before process file
28979 * @param {Roo.bootstrap.DocumentManager} this
28980 * @param {Object} file
28984 * @event previewrendered
28985 * Fire when preview rendered
28986 * @param {Roo.bootstrap.DocumentManager} this
28987 * @param {Object} file
28989 "previewrendered" : true,
28992 "previewResize" : true
28997 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
29006 paramName : 'imageUpload',
29007 toolTipName : 'filename',
29010 labelAlign : 'left',
29020 getAutoCreate : function()
29022 var managerWidget = {
29024 cls : 'roo-document-manager',
29028 cls : 'roo-document-manager-selector',
29033 cls : 'roo-document-manager-uploader',
29037 cls : 'roo-document-manager-upload-btn',
29038 html : '<i class="fa fa-plus"></i>'
29049 cls : 'column col-md-12',
29054 if(this.fieldLabel.length){
29059 cls : 'column col-md-12',
29060 html : this.fieldLabel
29064 cls : 'column col-md-12',
29069 if(this.labelAlign == 'left'){
29074 html : this.fieldLabel
29083 if(this.labelWidth > 12){
29084 content[0].style = "width: " + this.labelWidth + 'px';
29087 if(this.labelWidth < 13 && this.labelmd == 0){
29088 this.labelmd = this.labelWidth;
29091 if(this.labellg > 0){
29092 content[0].cls += ' col-lg-' + this.labellg;
29093 content[1].cls += ' col-lg-' + (12 - this.labellg);
29096 if(this.labelmd > 0){
29097 content[0].cls += ' col-md-' + this.labelmd;
29098 content[1].cls += ' col-md-' + (12 - this.labelmd);
29101 if(this.labelsm > 0){
29102 content[0].cls += ' col-sm-' + this.labelsm;
29103 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29106 if(this.labelxs > 0){
29107 content[0].cls += ' col-xs-' + this.labelxs;
29108 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29116 cls : 'row clearfix',
29124 initEvents : function()
29126 this.managerEl = this.el.select('.roo-document-manager', true).first();
29127 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29129 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29130 this.selectorEl.hide();
29133 this.selectorEl.attr('multiple', 'multiple');
29136 this.selectorEl.on('change', this.onFileSelected, this);
29138 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29139 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29141 this.uploader.on('click', this.onUploaderClick, this);
29143 this.renderProgressDialog();
29147 window.addEventListener("resize", function() { _this.refresh(); } );
29149 this.fireEvent('initial', this);
29152 renderProgressDialog : function()
29156 this.progressDialog = new Roo.bootstrap.Modal({
29157 cls : 'roo-document-manager-progress-dialog',
29158 allow_close : false,
29168 btnclick : function() {
29169 _this.uploadCancel();
29175 this.progressDialog.render(Roo.get(document.body));
29177 this.progress = new Roo.bootstrap.Progress({
29178 cls : 'roo-document-manager-progress',
29183 this.progress.render(this.progressDialog.getChildContainer());
29185 this.progressBar = new Roo.bootstrap.ProgressBar({
29186 cls : 'roo-document-manager-progress-bar',
29189 aria_valuemax : 12,
29193 this.progressBar.render(this.progress.getChildContainer());
29196 onUploaderClick : function(e)
29198 e.preventDefault();
29200 if(this.fireEvent('beforeselectfile', this) != false){
29201 this.selectorEl.dom.click();
29206 onFileSelected : function(e)
29208 e.preventDefault();
29210 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29214 Roo.each(this.selectorEl.dom.files, function(file){
29215 if(this.fireEvent('inspect', this, file) != false){
29216 this.files.push(file);
29226 this.selectorEl.dom.value = '';
29228 if(!this.files || !this.files.length){
29232 if(this.boxes > 0 && this.files.length > this.boxes){
29233 this.files = this.files.slice(0, this.boxes);
29236 this.uploader.show();
29238 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29239 this.uploader.hide();
29248 Roo.each(this.files, function(file){
29250 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29251 var f = this.renderPreview(file);
29256 if(file.type.indexOf('image') != -1){
29257 this.delegates.push(
29259 _this.process(file);
29260 }).createDelegate(this)
29268 _this.process(file);
29269 }).createDelegate(this)
29274 this.files = files;
29276 this.delegates = this.delegates.concat(docs);
29278 if(!this.delegates.length){
29283 this.progressBar.aria_valuemax = this.delegates.length;
29290 arrange : function()
29292 if(!this.delegates.length){
29293 this.progressDialog.hide();
29298 var delegate = this.delegates.shift();
29300 this.progressDialog.show();
29302 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29304 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29309 refresh : function()
29311 this.uploader.show();
29313 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29314 this.uploader.hide();
29317 Roo.isTouch ? this.closable(false) : this.closable(true);
29319 this.fireEvent('refresh', this);
29322 onRemove : function(e, el, o)
29324 e.preventDefault();
29326 this.fireEvent('remove', this, o);
29330 remove : function(o)
29334 Roo.each(this.files, function(file){
29335 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29344 this.files = files;
29351 Roo.each(this.files, function(file){
29356 file.target.remove();
29365 onClick : function(e, el, o)
29367 e.preventDefault();
29369 this.fireEvent('click', this, o);
29373 closable : function(closable)
29375 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29377 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29389 xhrOnLoad : function(xhr)
29391 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29395 if (xhr.readyState !== 4) {
29397 this.fireEvent('exception', this, xhr);
29401 var response = Roo.decode(xhr.responseText);
29403 if(!response.success){
29405 this.fireEvent('exception', this, xhr);
29409 var file = this.renderPreview(response.data);
29411 this.files.push(file);
29415 this.fireEvent('afterupload', this, xhr);
29419 xhrOnError : function(xhr)
29421 Roo.log('xhr on error');
29423 var response = Roo.decode(xhr.responseText);
29430 process : function(file)
29432 if(this.fireEvent('process', this, file) !== false){
29433 if(this.editable && file.type.indexOf('image') != -1){
29434 this.fireEvent('edit', this, file);
29438 this.uploadStart(file, false);
29445 uploadStart : function(file, crop)
29447 this.xhr = new XMLHttpRequest();
29449 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29454 file.xhr = this.xhr;
29456 this.managerEl.createChild({
29458 cls : 'roo-document-manager-loading',
29462 tooltip : file.name,
29463 cls : 'roo-document-manager-thumb',
29464 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29470 this.xhr.open(this.method, this.url, true);
29473 "Accept": "application/json",
29474 "Cache-Control": "no-cache",
29475 "X-Requested-With": "XMLHttpRequest"
29478 for (var headerName in headers) {
29479 var headerValue = headers[headerName];
29481 this.xhr.setRequestHeader(headerName, headerValue);
29487 this.xhr.onload = function()
29489 _this.xhrOnLoad(_this.xhr);
29492 this.xhr.onerror = function()
29494 _this.xhrOnError(_this.xhr);
29497 var formData = new FormData();
29499 formData.append('returnHTML', 'NO');
29502 formData.append('crop', crop);
29505 formData.append(this.paramName, file, file.name);
29512 if(this.fireEvent('prepare', this, formData, options) != false){
29514 if(options.manually){
29518 this.xhr.send(formData);
29522 this.uploadCancel();
29525 uploadCancel : function()
29531 this.delegates = [];
29533 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29540 renderPreview : function(file)
29542 if(typeof(file.target) != 'undefined' && file.target){
29546 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29548 var previewEl = this.managerEl.createChild({
29550 cls : 'roo-document-manager-preview',
29554 tooltip : file[this.toolTipName],
29555 cls : 'roo-document-manager-thumb',
29556 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29561 html : '<i class="fa fa-times-circle"></i>'
29566 var close = previewEl.select('button.close', true).first();
29568 close.on('click', this.onRemove, this, file);
29570 file.target = previewEl;
29572 var image = previewEl.select('img', true).first();
29576 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29578 image.on('click', this.onClick, this, file);
29580 this.fireEvent('previewrendered', this, file);
29586 onPreviewLoad : function(file, image)
29588 if(typeof(file.target) == 'undefined' || !file.target){
29592 var width = image.dom.naturalWidth || image.dom.width;
29593 var height = image.dom.naturalHeight || image.dom.height;
29595 if(!this.previewResize) {
29599 if(width > height){
29600 file.target.addClass('wide');
29604 file.target.addClass('tall');
29609 uploadFromSource : function(file, crop)
29611 this.xhr = new XMLHttpRequest();
29613 this.managerEl.createChild({
29615 cls : 'roo-document-manager-loading',
29619 tooltip : file.name,
29620 cls : 'roo-document-manager-thumb',
29621 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29627 this.xhr.open(this.method, this.url, true);
29630 "Accept": "application/json",
29631 "Cache-Control": "no-cache",
29632 "X-Requested-With": "XMLHttpRequest"
29635 for (var headerName in headers) {
29636 var headerValue = headers[headerName];
29638 this.xhr.setRequestHeader(headerName, headerValue);
29644 this.xhr.onload = function()
29646 _this.xhrOnLoad(_this.xhr);
29649 this.xhr.onerror = function()
29651 _this.xhrOnError(_this.xhr);
29654 var formData = new FormData();
29656 formData.append('returnHTML', 'NO');
29658 formData.append('crop', crop);
29660 if(typeof(file.filename) != 'undefined'){
29661 formData.append('filename', file.filename);
29664 if(typeof(file.mimetype) != 'undefined'){
29665 formData.append('mimetype', file.mimetype);
29670 if(this.fireEvent('prepare', this, formData) != false){
29671 this.xhr.send(formData);
29681 * @class Roo.bootstrap.DocumentViewer
29682 * @extends Roo.bootstrap.Component
29683 * Bootstrap DocumentViewer class
29684 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29685 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29688 * Create a new DocumentViewer
29689 * @param {Object} config The config object
29692 Roo.bootstrap.DocumentViewer = function(config){
29693 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29698 * Fire after initEvent
29699 * @param {Roo.bootstrap.DocumentViewer} this
29705 * @param {Roo.bootstrap.DocumentViewer} this
29710 * Fire after download button
29711 * @param {Roo.bootstrap.DocumentViewer} this
29716 * Fire after trash button
29717 * @param {Roo.bootstrap.DocumentViewer} this
29724 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29726 showDownload : true,
29730 getAutoCreate : function()
29734 cls : 'roo-document-viewer',
29738 cls : 'roo-document-viewer-body',
29742 cls : 'roo-document-viewer-thumb',
29746 cls : 'roo-document-viewer-image'
29754 cls : 'roo-document-viewer-footer',
29757 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29761 cls : 'btn-group roo-document-viewer-download',
29765 cls : 'btn btn-default',
29766 html : '<i class="fa fa-download"></i>'
29772 cls : 'btn-group roo-document-viewer-trash',
29776 cls : 'btn btn-default',
29777 html : '<i class="fa fa-trash"></i>'
29790 initEvents : function()
29792 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29793 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29795 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29796 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29798 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29799 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29801 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29802 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29804 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29805 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29807 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29808 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29810 this.bodyEl.on('click', this.onClick, this);
29811 this.downloadBtn.on('click', this.onDownload, this);
29812 this.trashBtn.on('click', this.onTrash, this);
29814 this.downloadBtn.hide();
29815 this.trashBtn.hide();
29817 if(this.showDownload){
29818 this.downloadBtn.show();
29821 if(this.showTrash){
29822 this.trashBtn.show();
29825 if(!this.showDownload && !this.showTrash) {
29826 this.footerEl.hide();
29831 initial : function()
29833 this.fireEvent('initial', this);
29837 onClick : function(e)
29839 e.preventDefault();
29841 this.fireEvent('click', this);
29844 onDownload : function(e)
29846 e.preventDefault();
29848 this.fireEvent('download', this);
29851 onTrash : function(e)
29853 e.preventDefault();
29855 this.fireEvent('trash', this);
29867 * @class Roo.bootstrap.NavProgressBar
29868 * @extends Roo.bootstrap.Component
29869 * Bootstrap NavProgressBar class
29872 * Create a new nav progress bar
29873 * @param {Object} config The config object
29876 Roo.bootstrap.NavProgressBar = function(config){
29877 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29879 this.bullets = this.bullets || [];
29881 // Roo.bootstrap.NavProgressBar.register(this);
29885 * Fires when the active item changes
29886 * @param {Roo.bootstrap.NavProgressBar} this
29887 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29888 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29895 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29900 getAutoCreate : function()
29902 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29906 cls : 'roo-navigation-bar-group',
29910 cls : 'roo-navigation-top-bar'
29914 cls : 'roo-navigation-bullets-bar',
29918 cls : 'roo-navigation-bar'
29925 cls : 'roo-navigation-bottom-bar'
29935 initEvents: function()
29940 onRender : function(ct, position)
29942 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29944 if(this.bullets.length){
29945 Roo.each(this.bullets, function(b){
29954 addItem : function(cfg)
29956 var item = new Roo.bootstrap.NavProgressItem(cfg);
29958 item.parentId = this.id;
29959 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29962 var top = new Roo.bootstrap.Element({
29964 cls : 'roo-navigation-bar-text'
29967 var bottom = new Roo.bootstrap.Element({
29969 cls : 'roo-navigation-bar-text'
29972 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29973 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29975 var topText = new Roo.bootstrap.Element({
29977 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29980 var bottomText = new Roo.bootstrap.Element({
29982 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29985 topText.onRender(top.el, null);
29986 bottomText.onRender(bottom.el, null);
29989 item.bottomEl = bottom;
29992 this.barItems.push(item);
29997 getActive : function()
29999 var active = false;
30001 Roo.each(this.barItems, function(v){
30003 if (!v.isActive()) {
30015 setActiveItem : function(item)
30019 Roo.each(this.barItems, function(v){
30020 if (v.rid == item.rid) {
30024 if (v.isActive()) {
30025 v.setActive(false);
30030 item.setActive(true);
30032 this.fireEvent('changed', this, item, prev);
30035 getBarItem: function(rid)
30039 Roo.each(this.barItems, function(e) {
30040 if (e.rid != rid) {
30051 indexOfItem : function(item)
30055 Roo.each(this.barItems, function(v, i){
30057 if (v.rid != item.rid) {
30068 setActiveNext : function()
30070 var i = this.indexOfItem(this.getActive());
30072 if (i > this.barItems.length) {
30076 this.setActiveItem(this.barItems[i+1]);
30079 setActivePrev : function()
30081 var i = this.indexOfItem(this.getActive());
30087 this.setActiveItem(this.barItems[i-1]);
30090 format : function()
30092 if(!this.barItems.length){
30096 var width = 100 / this.barItems.length;
30098 Roo.each(this.barItems, function(i){
30099 i.el.setStyle('width', width + '%');
30100 i.topEl.el.setStyle('width', width + '%');
30101 i.bottomEl.el.setStyle('width', width + '%');
30110 * Nav Progress Item
30115 * @class Roo.bootstrap.NavProgressItem
30116 * @extends Roo.bootstrap.Component
30117 * Bootstrap NavProgressItem class
30118 * @cfg {String} rid the reference id
30119 * @cfg {Boolean} active (true|false) Is item active default false
30120 * @cfg {Boolean} disabled (true|false) Is item active default false
30121 * @cfg {String} html
30122 * @cfg {String} position (top|bottom) text position default bottom
30123 * @cfg {String} icon show icon instead of number
30126 * Create a new NavProgressItem
30127 * @param {Object} config The config object
30129 Roo.bootstrap.NavProgressItem = function(config){
30130 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30135 * The raw click event for the entire grid.
30136 * @param {Roo.bootstrap.NavProgressItem} this
30137 * @param {Roo.EventObject} e
30144 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30150 position : 'bottom',
30153 getAutoCreate : function()
30155 var iconCls = 'roo-navigation-bar-item-icon';
30157 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30161 cls: 'roo-navigation-bar-item',
30171 cfg.cls += ' active';
30174 cfg.cls += ' disabled';
30180 disable : function()
30182 this.setDisabled(true);
30185 enable : function()
30187 this.setDisabled(false);
30190 initEvents: function()
30192 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30194 this.iconEl.on('click', this.onClick, this);
30197 onClick : function(e)
30199 e.preventDefault();
30205 if(this.fireEvent('click', this, e) === false){
30209 this.parent().setActiveItem(this);
30212 isActive: function ()
30214 return this.active;
30217 setActive : function(state)
30219 if(this.active == state){
30223 this.active = state;
30226 this.el.addClass('active');
30230 this.el.removeClass('active');
30235 setDisabled : function(state)
30237 if(this.disabled == state){
30241 this.disabled = state;
30244 this.el.addClass('disabled');
30248 this.el.removeClass('disabled');
30251 tooltipEl : function()
30253 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30266 * @class Roo.bootstrap.FieldLabel
30267 * @extends Roo.bootstrap.Component
30268 * Bootstrap FieldLabel class
30269 * @cfg {String} html contents of the element
30270 * @cfg {String} tag tag of the element default label
30271 * @cfg {String} cls class of the element
30272 * @cfg {String} target label target
30273 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30274 * @cfg {String} invalidClass default "text-warning"
30275 * @cfg {String} validClass default "text-success"
30276 * @cfg {String} iconTooltip default "This field is required"
30277 * @cfg {String} indicatorpos (left|right) default left
30280 * Create a new FieldLabel
30281 * @param {Object} config The config object
30284 Roo.bootstrap.FieldLabel = function(config){
30285 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30290 * Fires after the field has been marked as invalid.
30291 * @param {Roo.form.FieldLabel} this
30292 * @param {String} msg The validation message
30297 * Fires after the field has been validated with no errors.
30298 * @param {Roo.form.FieldLabel} this
30304 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30311 invalidClass : 'has-warning',
30312 validClass : 'has-success',
30313 iconTooltip : 'This field is required',
30314 indicatorpos : 'left',
30316 getAutoCreate : function(){
30319 if (!this.allowBlank) {
30325 cls : 'roo-bootstrap-field-label ' + this.cls,
30330 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30331 tooltip : this.iconTooltip
30340 if(this.indicatorpos == 'right'){
30343 cls : 'roo-bootstrap-field-label ' + this.cls,
30352 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30353 tooltip : this.iconTooltip
30362 initEvents: function()
30364 Roo.bootstrap.Element.superclass.initEvents.call(this);
30366 this.indicator = this.indicatorEl();
30368 if(this.indicator){
30369 this.indicator.removeClass('visible');
30370 this.indicator.addClass('invisible');
30373 Roo.bootstrap.FieldLabel.register(this);
30376 indicatorEl : function()
30378 var indicator = this.el.select('i.roo-required-indicator',true).first();
30389 * Mark this field as valid
30391 markValid : function()
30393 if(this.indicator){
30394 this.indicator.removeClass('visible');
30395 this.indicator.addClass('invisible');
30398 this.el.removeClass(this.invalidClass);
30400 this.el.addClass(this.validClass);
30402 this.fireEvent('valid', this);
30406 * Mark this field as invalid
30407 * @param {String} msg The validation message
30409 markInvalid : function(msg)
30411 if(this.indicator){
30412 this.indicator.removeClass('invisible');
30413 this.indicator.addClass('visible');
30416 this.el.removeClass(this.validClass);
30418 this.el.addClass(this.invalidClass);
30420 this.fireEvent('invalid', this, msg);
30426 Roo.apply(Roo.bootstrap.FieldLabel, {
30431 * register a FieldLabel Group
30432 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30434 register : function(label)
30436 if(this.groups.hasOwnProperty(label.target)){
30440 this.groups[label.target] = label;
30444 * fetch a FieldLabel Group based on the target
30445 * @param {string} target
30446 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30448 get: function(target) {
30449 if (typeof(this.groups[target]) == 'undefined') {
30453 return this.groups[target] ;
30462 * page DateSplitField.
30468 * @class Roo.bootstrap.DateSplitField
30469 * @extends Roo.bootstrap.Component
30470 * Bootstrap DateSplitField class
30471 * @cfg {string} fieldLabel - the label associated
30472 * @cfg {Number} labelWidth set the width of label (0-12)
30473 * @cfg {String} labelAlign (top|left)
30474 * @cfg {Boolean} dayAllowBlank (true|false) default false
30475 * @cfg {Boolean} monthAllowBlank (true|false) default false
30476 * @cfg {Boolean} yearAllowBlank (true|false) default false
30477 * @cfg {string} dayPlaceholder
30478 * @cfg {string} monthPlaceholder
30479 * @cfg {string} yearPlaceholder
30480 * @cfg {string} dayFormat default 'd'
30481 * @cfg {string} monthFormat default 'm'
30482 * @cfg {string} yearFormat default 'Y'
30483 * @cfg {Number} labellg set the width of label (1-12)
30484 * @cfg {Number} labelmd set the width of label (1-12)
30485 * @cfg {Number} labelsm set the width of label (1-12)
30486 * @cfg {Number} labelxs set the width of label (1-12)
30490 * Create a new DateSplitField
30491 * @param {Object} config The config object
30494 Roo.bootstrap.DateSplitField = function(config){
30495 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30501 * getting the data of years
30502 * @param {Roo.bootstrap.DateSplitField} this
30503 * @param {Object} years
30508 * getting the data of days
30509 * @param {Roo.bootstrap.DateSplitField} this
30510 * @param {Object} days
30515 * Fires after the field has been marked as invalid.
30516 * @param {Roo.form.Field} this
30517 * @param {String} msg The validation message
30522 * Fires after the field has been validated with no errors.
30523 * @param {Roo.form.Field} this
30529 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30532 labelAlign : 'top',
30534 dayAllowBlank : false,
30535 monthAllowBlank : false,
30536 yearAllowBlank : false,
30537 dayPlaceholder : '',
30538 monthPlaceholder : '',
30539 yearPlaceholder : '',
30543 isFormField : true,
30549 getAutoCreate : function()
30553 cls : 'row roo-date-split-field-group',
30558 cls : 'form-hidden-field roo-date-split-field-group-value',
30564 var labelCls = 'col-md-12';
30565 var contentCls = 'col-md-4';
30567 if(this.fieldLabel){
30571 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30575 html : this.fieldLabel
30580 if(this.labelAlign == 'left'){
30582 if(this.labelWidth > 12){
30583 label.style = "width: " + this.labelWidth + 'px';
30586 if(this.labelWidth < 13 && this.labelmd == 0){
30587 this.labelmd = this.labelWidth;
30590 if(this.labellg > 0){
30591 labelCls = ' col-lg-' + this.labellg;
30592 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30595 if(this.labelmd > 0){
30596 labelCls = ' col-md-' + this.labelmd;
30597 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30600 if(this.labelsm > 0){
30601 labelCls = ' col-sm-' + this.labelsm;
30602 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30605 if(this.labelxs > 0){
30606 labelCls = ' col-xs-' + this.labelxs;
30607 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30611 label.cls += ' ' + labelCls;
30613 cfg.cn.push(label);
30616 Roo.each(['day', 'month', 'year'], function(t){
30619 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30626 inputEl: function ()
30628 return this.el.select('.roo-date-split-field-group-value', true).first();
30631 onRender : function(ct, position)
30635 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30637 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30639 this.dayField = new Roo.bootstrap.ComboBox({
30640 allowBlank : this.dayAllowBlank,
30641 alwaysQuery : true,
30642 displayField : 'value',
30645 forceSelection : true,
30647 placeholder : this.dayPlaceholder,
30648 selectOnFocus : true,
30649 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30650 triggerAction : 'all',
30652 valueField : 'value',
30653 store : new Roo.data.SimpleStore({
30654 data : (function() {
30656 _this.fireEvent('days', _this, days);
30659 fields : [ 'value' ]
30662 select : function (_self, record, index)
30664 _this.setValue(_this.getValue());
30669 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30671 this.monthField = new Roo.bootstrap.MonthField({
30672 after : '<i class=\"fa fa-calendar\"></i>',
30673 allowBlank : this.monthAllowBlank,
30674 placeholder : this.monthPlaceholder,
30677 render : function (_self)
30679 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30680 e.preventDefault();
30684 select : function (_self, oldvalue, newvalue)
30686 _this.setValue(_this.getValue());
30691 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30693 this.yearField = new Roo.bootstrap.ComboBox({
30694 allowBlank : this.yearAllowBlank,
30695 alwaysQuery : true,
30696 displayField : 'value',
30699 forceSelection : true,
30701 placeholder : this.yearPlaceholder,
30702 selectOnFocus : true,
30703 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30704 triggerAction : 'all',
30706 valueField : 'value',
30707 store : new Roo.data.SimpleStore({
30708 data : (function() {
30710 _this.fireEvent('years', _this, years);
30713 fields : [ 'value' ]
30716 select : function (_self, record, index)
30718 _this.setValue(_this.getValue());
30723 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30726 setValue : function(v, format)
30728 this.inputEl.dom.value = v;
30730 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30732 var d = Date.parseDate(v, f);
30739 this.setDay(d.format(this.dayFormat));
30740 this.setMonth(d.format(this.monthFormat));
30741 this.setYear(d.format(this.yearFormat));
30748 setDay : function(v)
30750 this.dayField.setValue(v);
30751 this.inputEl.dom.value = this.getValue();
30756 setMonth : function(v)
30758 this.monthField.setValue(v, true);
30759 this.inputEl.dom.value = this.getValue();
30764 setYear : function(v)
30766 this.yearField.setValue(v);
30767 this.inputEl.dom.value = this.getValue();
30772 getDay : function()
30774 return this.dayField.getValue();
30777 getMonth : function()
30779 return this.monthField.getValue();
30782 getYear : function()
30784 return this.yearField.getValue();
30787 getValue : function()
30789 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30791 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30801 this.inputEl.dom.value = '';
30806 validate : function()
30808 var d = this.dayField.validate();
30809 var m = this.monthField.validate();
30810 var y = this.yearField.validate();
30815 (!this.dayAllowBlank && !d) ||
30816 (!this.monthAllowBlank && !m) ||
30817 (!this.yearAllowBlank && !y)
30822 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30831 this.markInvalid();
30836 markValid : function()
30839 var label = this.el.select('label', true).first();
30840 var icon = this.el.select('i.fa-star', true).first();
30846 this.fireEvent('valid', this);
30850 * Mark this field as invalid
30851 * @param {String} msg The validation message
30853 markInvalid : function(msg)
30856 var label = this.el.select('label', true).first();
30857 var icon = this.el.select('i.fa-star', true).first();
30859 if(label && !icon){
30860 this.el.select('.roo-date-split-field-label', true).createChild({
30862 cls : 'text-danger fa fa-lg fa-star',
30863 tooltip : 'This field is required',
30864 style : 'margin-right:5px;'
30868 this.fireEvent('invalid', this, msg);
30871 clearInvalid : function()
30873 var label = this.el.select('label', true).first();
30874 var icon = this.el.select('i.fa-star', true).first();
30880 this.fireEvent('valid', this);
30883 getName: function()
30893 * http://masonry.desandro.com
30895 * The idea is to render all the bricks based on vertical width...
30897 * The original code extends 'outlayer' - we might need to use that....
30903 * @class Roo.bootstrap.LayoutMasonry
30904 * @extends Roo.bootstrap.Component
30905 * Bootstrap Layout Masonry class
30908 * Create a new Element
30909 * @param {Object} config The config object
30912 Roo.bootstrap.LayoutMasonry = function(config){
30914 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30918 Roo.bootstrap.LayoutMasonry.register(this);
30924 * Fire after layout the items
30925 * @param {Roo.bootstrap.LayoutMasonry} this
30926 * @param {Roo.EventObject} e
30933 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30936 * @cfg {Boolean} isLayoutInstant = no animation?
30938 isLayoutInstant : false, // needed?
30941 * @cfg {Number} boxWidth width of the columns
30946 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30951 * @cfg {Number} padWidth padding below box..
30956 * @cfg {Number} gutter gutter width..
30961 * @cfg {Number} maxCols maximum number of columns
30967 * @cfg {Boolean} isAutoInitial defalut true
30969 isAutoInitial : true,
30974 * @cfg {Boolean} isHorizontal defalut false
30976 isHorizontal : false,
30978 currentSize : null,
30984 bricks: null, //CompositeElement
30988 _isLayoutInited : false,
30990 // isAlternative : false, // only use for vertical layout...
30993 * @cfg {Number} alternativePadWidth padding below box..
30995 alternativePadWidth : 50,
30997 selectedBrick : [],
30999 getAutoCreate : function(){
31001 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31005 cls: 'blog-masonary-wrapper ' + this.cls,
31007 cls : 'mas-boxes masonary'
31014 getChildContainer: function( )
31016 if (this.boxesEl) {
31017 return this.boxesEl;
31020 this.boxesEl = this.el.select('.mas-boxes').first();
31022 return this.boxesEl;
31026 initEvents : function()
31030 if(this.isAutoInitial){
31031 Roo.log('hook children rendered');
31032 this.on('childrenrendered', function() {
31033 Roo.log('children rendered');
31039 initial : function()
31041 this.selectedBrick = [];
31043 this.currentSize = this.el.getBox(true);
31045 Roo.EventManager.onWindowResize(this.resize, this);
31047 if(!this.isAutoInitial){
31055 //this.layout.defer(500,this);
31059 resize : function()
31061 var cs = this.el.getBox(true);
31064 this.currentSize.width == cs.width &&
31065 this.currentSize.x == cs.x &&
31066 this.currentSize.height == cs.height &&
31067 this.currentSize.y == cs.y
31069 Roo.log("no change in with or X or Y");
31073 this.currentSize = cs;
31079 layout : function()
31081 this._resetLayout();
31083 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31085 this.layoutItems( isInstant );
31087 this._isLayoutInited = true;
31089 this.fireEvent('layout', this);
31093 _resetLayout : function()
31095 if(this.isHorizontal){
31096 this.horizontalMeasureColumns();
31100 this.verticalMeasureColumns();
31104 verticalMeasureColumns : function()
31106 this.getContainerWidth();
31108 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31109 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31113 var boxWidth = this.boxWidth + this.padWidth;
31115 if(this.containerWidth < this.boxWidth){
31116 boxWidth = this.containerWidth
31119 var containerWidth = this.containerWidth;
31121 var cols = Math.floor(containerWidth / boxWidth);
31123 this.cols = Math.max( cols, 1 );
31125 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31127 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31129 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31131 this.colWidth = boxWidth + avail - this.padWidth;
31133 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31134 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31137 horizontalMeasureColumns : function()
31139 this.getContainerWidth();
31141 var boxWidth = this.boxWidth;
31143 if(this.containerWidth < boxWidth){
31144 boxWidth = this.containerWidth;
31147 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31149 this.el.setHeight(boxWidth);
31153 getContainerWidth : function()
31155 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31158 layoutItems : function( isInstant )
31160 Roo.log(this.bricks);
31162 var items = Roo.apply([], this.bricks);
31164 if(this.isHorizontal){
31165 this._horizontalLayoutItems( items , isInstant );
31169 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31170 // this._verticalAlternativeLayoutItems( items , isInstant );
31174 this._verticalLayoutItems( items , isInstant );
31178 _verticalLayoutItems : function ( items , isInstant)
31180 if ( !items || !items.length ) {
31185 ['xs', 'xs', 'xs', 'tall'],
31186 ['xs', 'xs', 'tall'],
31187 ['xs', 'xs', 'sm'],
31188 ['xs', 'xs', 'xs'],
31194 ['sm', 'xs', 'xs'],
31198 ['tall', 'xs', 'xs', 'xs'],
31199 ['tall', 'xs', 'xs'],
31211 Roo.each(items, function(item, k){
31213 switch (item.size) {
31214 // these layouts take up a full box,
31225 boxes.push([item]);
31248 var filterPattern = function(box, length)
31256 var pattern = box.slice(0, length);
31260 Roo.each(pattern, function(i){
31261 format.push(i.size);
31264 Roo.each(standard, function(s){
31266 if(String(s) != String(format)){
31275 if(!match && length == 1){
31280 filterPattern(box, length - 1);
31284 queue.push(pattern);
31286 box = box.slice(length, box.length);
31288 filterPattern(box, 4);
31294 Roo.each(boxes, function(box, k){
31300 if(box.length == 1){
31305 filterPattern(box, 4);
31309 this._processVerticalLayoutQueue( queue, isInstant );
31313 // _verticalAlternativeLayoutItems : function( items , isInstant )
31315 // if ( !items || !items.length ) {
31319 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31323 _horizontalLayoutItems : function ( items , isInstant)
31325 if ( !items || !items.length || items.length < 3) {
31331 var eItems = items.slice(0, 3);
31333 items = items.slice(3, items.length);
31336 ['xs', 'xs', 'xs', 'wide'],
31337 ['xs', 'xs', 'wide'],
31338 ['xs', 'xs', 'sm'],
31339 ['xs', 'xs', 'xs'],
31345 ['sm', 'xs', 'xs'],
31349 ['wide', 'xs', 'xs', 'xs'],
31350 ['wide', 'xs', 'xs'],
31363 Roo.each(items, function(item, k){
31365 switch (item.size) {
31376 boxes.push([item]);
31400 var filterPattern = function(box, length)
31408 var pattern = box.slice(0, length);
31412 Roo.each(pattern, function(i){
31413 format.push(i.size);
31416 Roo.each(standard, function(s){
31418 if(String(s) != String(format)){
31427 if(!match && length == 1){
31432 filterPattern(box, length - 1);
31436 queue.push(pattern);
31438 box = box.slice(length, box.length);
31440 filterPattern(box, 4);
31446 Roo.each(boxes, function(box, k){
31452 if(box.length == 1){
31457 filterPattern(box, 4);
31464 var pos = this.el.getBox(true);
31468 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31470 var hit_end = false;
31472 Roo.each(queue, function(box){
31476 Roo.each(box, function(b){
31478 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31488 Roo.each(box, function(b){
31490 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31493 mx = Math.max(mx, b.x);
31497 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31501 Roo.each(box, function(b){
31503 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31517 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31520 /** Sets position of item in DOM
31521 * @param {Element} item
31522 * @param {Number} x - horizontal position
31523 * @param {Number} y - vertical position
31524 * @param {Boolean} isInstant - disables transitions
31526 _processVerticalLayoutQueue : function( queue, isInstant )
31528 var pos = this.el.getBox(true);
31533 for (var i = 0; i < this.cols; i++){
31537 Roo.each(queue, function(box, k){
31539 var col = k % this.cols;
31541 Roo.each(box, function(b,kk){
31543 b.el.position('absolute');
31545 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31546 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31548 if(b.size == 'md-left' || b.size == 'md-right'){
31549 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31550 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31553 b.el.setWidth(width);
31554 b.el.setHeight(height);
31556 b.el.select('iframe',true).setSize(width,height);
31560 for (var i = 0; i < this.cols; i++){
31562 if(maxY[i] < maxY[col]){
31567 col = Math.min(col, i);
31571 x = pos.x + col * (this.colWidth + this.padWidth);
31575 var positions = [];
31577 switch (box.length){
31579 positions = this.getVerticalOneBoxColPositions(x, y, box);
31582 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31585 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31588 positions = this.getVerticalFourBoxColPositions(x, y, box);
31594 Roo.each(box, function(b,kk){
31596 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31598 var sz = b.el.getSize();
31600 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31608 for (var i = 0; i < this.cols; i++){
31609 mY = Math.max(mY, maxY[i]);
31612 this.el.setHeight(mY - pos.y);
31616 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31618 // var pos = this.el.getBox(true);
31621 // var maxX = pos.right;
31623 // var maxHeight = 0;
31625 // Roo.each(items, function(item, k){
31629 // item.el.position('absolute');
31631 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31633 // item.el.setWidth(width);
31635 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31637 // item.el.setHeight(height);
31640 // item.el.setXY([x, y], isInstant ? false : true);
31642 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31645 // y = y + height + this.alternativePadWidth;
31647 // maxHeight = maxHeight + height + this.alternativePadWidth;
31651 // this.el.setHeight(maxHeight);
31655 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31657 var pos = this.el.getBox(true);
31662 var maxX = pos.right;
31664 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31666 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31668 Roo.each(queue, function(box, k){
31670 Roo.each(box, function(b, kk){
31672 b.el.position('absolute');
31674 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31675 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31677 if(b.size == 'md-left' || b.size == 'md-right'){
31678 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31679 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31682 b.el.setWidth(width);
31683 b.el.setHeight(height);
31691 var positions = [];
31693 switch (box.length){
31695 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31698 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31701 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31704 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31710 Roo.each(box, function(b,kk){
31712 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31714 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31722 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31724 Roo.each(eItems, function(b,k){
31726 b.size = (k == 0) ? 'sm' : 'xs';
31727 b.x = (k == 0) ? 2 : 1;
31728 b.y = (k == 0) ? 2 : 1;
31730 b.el.position('absolute');
31732 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31734 b.el.setWidth(width);
31736 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31738 b.el.setHeight(height);
31742 var positions = [];
31745 x : maxX - this.unitWidth * 2 - this.gutter,
31750 x : maxX - this.unitWidth,
31751 y : minY + (this.unitWidth + this.gutter) * 2
31755 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31759 Roo.each(eItems, function(b,k){
31761 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31767 getVerticalOneBoxColPositions : function(x, y, box)
31771 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31773 if(box[0].size == 'md-left'){
31777 if(box[0].size == 'md-right'){
31782 x : x + (this.unitWidth + this.gutter) * rand,
31789 getVerticalTwoBoxColPositions : function(x, y, box)
31793 if(box[0].size == 'xs'){
31797 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31801 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31815 x : x + (this.unitWidth + this.gutter) * 2,
31816 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31823 getVerticalThreeBoxColPositions : function(x, y, box)
31827 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31835 x : x + (this.unitWidth + this.gutter) * 1,
31840 x : x + (this.unitWidth + this.gutter) * 2,
31848 if(box[0].size == 'xs' && box[1].size == 'xs'){
31857 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31861 x : x + (this.unitWidth + this.gutter) * 1,
31875 x : x + (this.unitWidth + this.gutter) * 2,
31880 x : x + (this.unitWidth + this.gutter) * 2,
31881 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31888 getVerticalFourBoxColPositions : function(x, y, box)
31892 if(box[0].size == 'xs'){
31901 y : y + (this.unitHeight + this.gutter) * 1
31906 y : y + (this.unitHeight + this.gutter) * 2
31910 x : x + (this.unitWidth + this.gutter) * 1,
31924 x : x + (this.unitWidth + this.gutter) * 2,
31929 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31930 y : y + (this.unitHeight + this.gutter) * 1
31934 x : x + (this.unitWidth + this.gutter) * 2,
31935 y : y + (this.unitWidth + this.gutter) * 2
31942 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31946 if(box[0].size == 'md-left'){
31948 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31955 if(box[0].size == 'md-right'){
31957 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31958 y : minY + (this.unitWidth + this.gutter) * 1
31964 var rand = Math.floor(Math.random() * (4 - box[0].y));
31967 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31968 y : minY + (this.unitWidth + this.gutter) * rand
31975 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31979 if(box[0].size == 'xs'){
31982 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31987 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31988 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31996 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32001 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32002 y : minY + (this.unitWidth + this.gutter) * 2
32009 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32013 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32016 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32021 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32022 y : minY + (this.unitWidth + this.gutter) * 1
32026 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32027 y : minY + (this.unitWidth + this.gutter) * 2
32034 if(box[0].size == 'xs' && box[1].size == 'xs'){
32037 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32042 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32047 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32048 y : minY + (this.unitWidth + this.gutter) * 1
32056 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32061 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32062 y : minY + (this.unitWidth + this.gutter) * 2
32066 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32067 y : minY + (this.unitWidth + this.gutter) * 2
32074 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32078 if(box[0].size == 'xs'){
32081 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32086 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32091 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),
32096 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32097 y : minY + (this.unitWidth + this.gutter) * 1
32105 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32110 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32111 y : minY + (this.unitWidth + this.gutter) * 2
32115 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32116 y : minY + (this.unitWidth + this.gutter) * 2
32120 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),
32121 y : minY + (this.unitWidth + this.gutter) * 2
32129 * remove a Masonry Brick
32130 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32132 removeBrick : function(brick_id)
32138 for (var i = 0; i<this.bricks.length; i++) {
32139 if (this.bricks[i].id == brick_id) {
32140 this.bricks.splice(i,1);
32141 this.el.dom.removeChild(Roo.get(brick_id).dom);
32148 * adds a Masonry Brick
32149 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32151 addBrick : function(cfg)
32153 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32154 //this.register(cn);
32155 cn.parentId = this.id;
32156 cn.render(this.el);
32161 * register a Masonry Brick
32162 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32165 register : function(brick)
32167 this.bricks.push(brick);
32168 brick.masonryId = this.id;
32172 * clear all the Masonry Brick
32174 clearAll : function()
32177 //this.getChildContainer().dom.innerHTML = "";
32178 this.el.dom.innerHTML = '';
32181 getSelected : function()
32183 if (!this.selectedBrick) {
32187 return this.selectedBrick;
32191 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32195 * register a Masonry Layout
32196 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32199 register : function(layout)
32201 this.groups[layout.id] = layout;
32204 * fetch a Masonry Layout based on the masonry layout ID
32205 * @param {string} the masonry layout to add
32206 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32209 get: function(layout_id) {
32210 if (typeof(this.groups[layout_id]) == 'undefined') {
32213 return this.groups[layout_id] ;
32225 * http://masonry.desandro.com
32227 * The idea is to render all the bricks based on vertical width...
32229 * The original code extends 'outlayer' - we might need to use that....
32235 * @class Roo.bootstrap.LayoutMasonryAuto
32236 * @extends Roo.bootstrap.Component
32237 * Bootstrap Layout Masonry class
32240 * Create a new Element
32241 * @param {Object} config The config object
32244 Roo.bootstrap.LayoutMasonryAuto = function(config){
32245 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32248 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32251 * @cfg {Boolean} isFitWidth - resize the width..
32253 isFitWidth : false, // options..
32255 * @cfg {Boolean} isOriginLeft = left align?
32257 isOriginLeft : true,
32259 * @cfg {Boolean} isOriginTop = top align?
32261 isOriginTop : false,
32263 * @cfg {Boolean} isLayoutInstant = no animation?
32265 isLayoutInstant : false, // needed?
32267 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32269 isResizingContainer : true,
32271 * @cfg {Number} columnWidth width of the columns
32277 * @cfg {Number} maxCols maximum number of columns
32282 * @cfg {Number} padHeight padding below box..
32288 * @cfg {Boolean} isAutoInitial defalut true
32291 isAutoInitial : true,
32297 initialColumnWidth : 0,
32298 currentSize : null,
32300 colYs : null, // array.
32307 bricks: null, //CompositeElement
32308 cols : 0, // array?
32309 // element : null, // wrapped now this.el
32310 _isLayoutInited : null,
32313 getAutoCreate : function(){
32317 cls: 'blog-masonary-wrapper ' + this.cls,
32319 cls : 'mas-boxes masonary'
32326 getChildContainer: function( )
32328 if (this.boxesEl) {
32329 return this.boxesEl;
32332 this.boxesEl = this.el.select('.mas-boxes').first();
32334 return this.boxesEl;
32338 initEvents : function()
32342 if(this.isAutoInitial){
32343 Roo.log('hook children rendered');
32344 this.on('childrenrendered', function() {
32345 Roo.log('children rendered');
32352 initial : function()
32354 this.reloadItems();
32356 this.currentSize = this.el.getBox(true);
32358 /// was window resize... - let's see if this works..
32359 Roo.EventManager.onWindowResize(this.resize, this);
32361 if(!this.isAutoInitial){
32366 this.layout.defer(500,this);
32369 reloadItems: function()
32371 this.bricks = this.el.select('.masonry-brick', true);
32373 this.bricks.each(function(b) {
32374 //Roo.log(b.getSize());
32375 if (!b.attr('originalwidth')) {
32376 b.attr('originalwidth', b.getSize().width);
32381 Roo.log(this.bricks.elements.length);
32384 resize : function()
32387 var cs = this.el.getBox(true);
32389 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32390 Roo.log("no change in with or X");
32393 this.currentSize = cs;
32397 layout : function()
32400 this._resetLayout();
32401 //this._manageStamps();
32403 // don't animate first layout
32404 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32405 this.layoutItems( isInstant );
32407 // flag for initalized
32408 this._isLayoutInited = true;
32411 layoutItems : function( isInstant )
32413 //var items = this._getItemsForLayout( this.items );
32414 // original code supports filtering layout items.. we just ignore it..
32416 this._layoutItems( this.bricks , isInstant );
32418 this._postLayout();
32420 _layoutItems : function ( items , isInstant)
32422 //this.fireEvent( 'layout', this, items );
32425 if ( !items || !items.elements.length ) {
32426 // no items, emit event with empty array
32431 items.each(function(item) {
32432 Roo.log("layout item");
32434 // get x/y object from method
32435 var position = this._getItemLayoutPosition( item );
32437 position.item = item;
32438 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32439 queue.push( position );
32442 this._processLayoutQueue( queue );
32444 /** Sets position of item in DOM
32445 * @param {Element} item
32446 * @param {Number} x - horizontal position
32447 * @param {Number} y - vertical position
32448 * @param {Boolean} isInstant - disables transitions
32450 _processLayoutQueue : function( queue )
32452 for ( var i=0, len = queue.length; i < len; i++ ) {
32453 var obj = queue[i];
32454 obj.item.position('absolute');
32455 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32461 * Any logic you want to do after each layout,
32462 * i.e. size the container
32464 _postLayout : function()
32466 this.resizeContainer();
32469 resizeContainer : function()
32471 if ( !this.isResizingContainer ) {
32474 var size = this._getContainerSize();
32476 this.el.setSize(size.width,size.height);
32477 this.boxesEl.setSize(size.width,size.height);
32483 _resetLayout : function()
32485 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32486 this.colWidth = this.el.getWidth();
32487 //this.gutter = this.el.getWidth();
32489 this.measureColumns();
32495 this.colYs.push( 0 );
32501 measureColumns : function()
32503 this.getContainerWidth();
32504 // if columnWidth is 0, default to outerWidth of first item
32505 if ( !this.columnWidth ) {
32506 var firstItem = this.bricks.first();
32507 Roo.log(firstItem);
32508 this.columnWidth = this.containerWidth;
32509 if (firstItem && firstItem.attr('originalwidth') ) {
32510 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32512 // columnWidth fall back to item of first element
32513 Roo.log("set column width?");
32514 this.initialColumnWidth = this.columnWidth ;
32516 // if first elem has no width, default to size of container
32521 if (this.initialColumnWidth) {
32522 this.columnWidth = this.initialColumnWidth;
32527 // column width is fixed at the top - however if container width get's smaller we should
32530 // this bit calcs how man columns..
32532 var columnWidth = this.columnWidth += this.gutter;
32534 // calculate columns
32535 var containerWidth = this.containerWidth + this.gutter;
32537 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32538 // fix rounding errors, typically with gutters
32539 var excess = columnWidth - containerWidth % columnWidth;
32542 // if overshoot is less than a pixel, round up, otherwise floor it
32543 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32544 cols = Math[ mathMethod ]( cols );
32545 this.cols = Math.max( cols, 1 );
32546 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32548 // padding positioning..
32549 var totalColWidth = this.cols * this.columnWidth;
32550 var padavail = this.containerWidth - totalColWidth;
32551 // so for 2 columns - we need 3 'pads'
32553 var padNeeded = (1+this.cols) * this.padWidth;
32555 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32557 this.columnWidth += padExtra
32558 //this.padWidth = Math.floor(padavail / ( this.cols));
32560 // adjust colum width so that padding is fixed??
32562 // we have 3 columns ... total = width * 3
32563 // we have X left over... that should be used by
32565 //if (this.expandC) {
32573 getContainerWidth : function()
32575 /* // container is parent if fit width
32576 var container = this.isFitWidth ? this.element.parentNode : this.element;
32577 // check that this.size and size are there
32578 // IE8 triggers resize on body size change, so they might not be
32580 var size = getSize( container ); //FIXME
32581 this.containerWidth = size && size.innerWidth; //FIXME
32584 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32588 _getItemLayoutPosition : function( item ) // what is item?
32590 // we resize the item to our columnWidth..
32592 item.setWidth(this.columnWidth);
32593 item.autoBoxAdjust = false;
32595 var sz = item.getSize();
32597 // how many columns does this brick span
32598 var remainder = this.containerWidth % this.columnWidth;
32600 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32601 // round if off by 1 pixel, otherwise use ceil
32602 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32603 colSpan = Math.min( colSpan, this.cols );
32605 // normally this should be '1' as we dont' currently allow multi width columns..
32607 var colGroup = this._getColGroup( colSpan );
32608 // get the minimum Y value from the columns
32609 var minimumY = Math.min.apply( Math, colGroup );
32610 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32612 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32614 // position the brick
32616 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32617 y: this.currentSize.y + minimumY + this.padHeight
32621 // apply setHeight to necessary columns
32622 var setHeight = minimumY + sz.height + this.padHeight;
32623 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32625 var setSpan = this.cols + 1 - colGroup.length;
32626 for ( var i = 0; i < setSpan; i++ ) {
32627 this.colYs[ shortColIndex + i ] = setHeight ;
32634 * @param {Number} colSpan - number of columns the element spans
32635 * @returns {Array} colGroup
32637 _getColGroup : function( colSpan )
32639 if ( colSpan < 2 ) {
32640 // if brick spans only one column, use all the column Ys
32645 // how many different places could this brick fit horizontally
32646 var groupCount = this.cols + 1 - colSpan;
32647 // for each group potential horizontal position
32648 for ( var i = 0; i < groupCount; i++ ) {
32649 // make an array of colY values for that one group
32650 var groupColYs = this.colYs.slice( i, i + colSpan );
32651 // and get the max value of the array
32652 colGroup[i] = Math.max.apply( Math, groupColYs );
32657 _manageStamp : function( stamp )
32659 var stampSize = stamp.getSize();
32660 var offset = stamp.getBox();
32661 // get the columns that this stamp affects
32662 var firstX = this.isOriginLeft ? offset.x : offset.right;
32663 var lastX = firstX + stampSize.width;
32664 var firstCol = Math.floor( firstX / this.columnWidth );
32665 firstCol = Math.max( 0, firstCol );
32667 var lastCol = Math.floor( lastX / this.columnWidth );
32668 // lastCol should not go over if multiple of columnWidth #425
32669 lastCol -= lastX % this.columnWidth ? 0 : 1;
32670 lastCol = Math.min( this.cols - 1, lastCol );
32672 // set colYs to bottom of the stamp
32673 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32676 for ( var i = firstCol; i <= lastCol; i++ ) {
32677 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32682 _getContainerSize : function()
32684 this.maxY = Math.max.apply( Math, this.colYs );
32689 if ( this.isFitWidth ) {
32690 size.width = this._getContainerFitWidth();
32696 _getContainerFitWidth : function()
32698 var unusedCols = 0;
32699 // count unused columns
32702 if ( this.colYs[i] !== 0 ) {
32707 // fit container to columns that have been used
32708 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32711 needsResizeLayout : function()
32713 var previousWidth = this.containerWidth;
32714 this.getContainerWidth();
32715 return previousWidth !== this.containerWidth;
32730 * @class Roo.bootstrap.MasonryBrick
32731 * @extends Roo.bootstrap.Component
32732 * Bootstrap MasonryBrick class
32735 * Create a new MasonryBrick
32736 * @param {Object} config The config object
32739 Roo.bootstrap.MasonryBrick = function(config){
32741 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32743 Roo.bootstrap.MasonryBrick.register(this);
32749 * When a MasonryBrick is clcik
32750 * @param {Roo.bootstrap.MasonryBrick} this
32751 * @param {Roo.EventObject} e
32757 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32760 * @cfg {String} title
32764 * @cfg {String} html
32768 * @cfg {String} bgimage
32772 * @cfg {String} videourl
32776 * @cfg {String} cls
32780 * @cfg {String} href
32784 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32789 * @cfg {String} placetitle (center|bottom)
32794 * @cfg {Boolean} isFitContainer defalut true
32796 isFitContainer : true,
32799 * @cfg {Boolean} preventDefault defalut false
32801 preventDefault : false,
32804 * @cfg {Boolean} inverse defalut false
32806 maskInverse : false,
32808 getAutoCreate : function()
32810 if(!this.isFitContainer){
32811 return this.getSplitAutoCreate();
32814 var cls = 'masonry-brick masonry-brick-full';
32816 if(this.href.length){
32817 cls += ' masonry-brick-link';
32820 if(this.bgimage.length){
32821 cls += ' masonry-brick-image';
32824 if(this.maskInverse){
32825 cls += ' mask-inverse';
32828 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32829 cls += ' enable-mask';
32833 cls += ' masonry-' + this.size + '-brick';
32836 if(this.placetitle.length){
32838 switch (this.placetitle) {
32840 cls += ' masonry-center-title';
32843 cls += ' masonry-bottom-title';
32850 if(!this.html.length && !this.bgimage.length){
32851 cls += ' masonry-center-title';
32854 if(!this.html.length && this.bgimage.length){
32855 cls += ' masonry-bottom-title';
32860 cls += ' ' + this.cls;
32864 tag: (this.href.length) ? 'a' : 'div',
32869 cls: 'masonry-brick-mask'
32873 cls: 'masonry-brick-paragraph',
32879 if(this.href.length){
32880 cfg.href = this.href;
32883 var cn = cfg.cn[1].cn;
32885 if(this.title.length){
32888 cls: 'masonry-brick-title',
32893 if(this.html.length){
32896 cls: 'masonry-brick-text',
32901 if (!this.title.length && !this.html.length) {
32902 cfg.cn[1].cls += ' hide';
32905 if(this.bgimage.length){
32908 cls: 'masonry-brick-image-view',
32913 if(this.videourl.length){
32914 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32915 // youtube support only?
32918 cls: 'masonry-brick-image-view',
32921 allowfullscreen : true
32929 getSplitAutoCreate : function()
32931 var cls = 'masonry-brick masonry-brick-split';
32933 if(this.href.length){
32934 cls += ' masonry-brick-link';
32937 if(this.bgimage.length){
32938 cls += ' masonry-brick-image';
32942 cls += ' masonry-' + this.size + '-brick';
32945 switch (this.placetitle) {
32947 cls += ' masonry-center-title';
32950 cls += ' masonry-bottom-title';
32953 if(!this.bgimage.length){
32954 cls += ' masonry-center-title';
32957 if(this.bgimage.length){
32958 cls += ' masonry-bottom-title';
32964 cls += ' ' + this.cls;
32968 tag: (this.href.length) ? 'a' : 'div',
32973 cls: 'masonry-brick-split-head',
32977 cls: 'masonry-brick-paragraph',
32984 cls: 'masonry-brick-split-body',
32990 if(this.href.length){
32991 cfg.href = this.href;
32994 if(this.title.length){
32995 cfg.cn[0].cn[0].cn.push({
32997 cls: 'masonry-brick-title',
33002 if(this.html.length){
33003 cfg.cn[1].cn.push({
33005 cls: 'masonry-brick-text',
33010 if(this.bgimage.length){
33011 cfg.cn[0].cn.push({
33013 cls: 'masonry-brick-image-view',
33018 if(this.videourl.length){
33019 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33020 // youtube support only?
33021 cfg.cn[0].cn.cn.push({
33023 cls: 'masonry-brick-image-view',
33026 allowfullscreen : true
33033 initEvents: function()
33035 switch (this.size) {
33068 this.el.on('touchstart', this.onTouchStart, this);
33069 this.el.on('touchmove', this.onTouchMove, this);
33070 this.el.on('touchend', this.onTouchEnd, this);
33071 this.el.on('contextmenu', this.onContextMenu, this);
33073 this.el.on('mouseenter' ,this.enter, this);
33074 this.el.on('mouseleave', this.leave, this);
33075 this.el.on('click', this.onClick, this);
33078 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33079 this.parent().bricks.push(this);
33084 onClick: function(e, el)
33086 var time = this.endTimer - this.startTimer;
33087 // Roo.log(e.preventDefault());
33090 e.preventDefault();
33095 if(!this.preventDefault){
33099 e.preventDefault();
33101 if (this.activeClass != '') {
33102 this.selectBrick();
33105 this.fireEvent('click', this, e);
33108 enter: function(e, el)
33110 e.preventDefault();
33112 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33116 if(this.bgimage.length && this.html.length){
33117 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33121 leave: function(e, el)
33123 e.preventDefault();
33125 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33129 if(this.bgimage.length && this.html.length){
33130 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33134 onTouchStart: function(e, el)
33136 // e.preventDefault();
33138 this.touchmoved = false;
33140 if(!this.isFitContainer){
33144 if(!this.bgimage.length || !this.html.length){
33148 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33150 this.timer = new Date().getTime();
33154 onTouchMove: function(e, el)
33156 this.touchmoved = true;
33159 onContextMenu : function(e,el)
33161 e.preventDefault();
33162 e.stopPropagation();
33166 onTouchEnd: function(e, el)
33168 // e.preventDefault();
33170 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33177 if(!this.bgimage.length || !this.html.length){
33179 if(this.href.length){
33180 window.location.href = this.href;
33186 if(!this.isFitContainer){
33190 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33192 window.location.href = this.href;
33195 //selection on single brick only
33196 selectBrick : function() {
33198 if (!this.parentId) {
33202 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33203 var index = m.selectedBrick.indexOf(this.id);
33206 m.selectedBrick.splice(index,1);
33207 this.el.removeClass(this.activeClass);
33211 for(var i = 0; i < m.selectedBrick.length; i++) {
33212 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33213 b.el.removeClass(b.activeClass);
33216 m.selectedBrick = [];
33218 m.selectedBrick.push(this.id);
33219 this.el.addClass(this.activeClass);
33223 isSelected : function(){
33224 return this.el.hasClass(this.activeClass);
33229 Roo.apply(Roo.bootstrap.MasonryBrick, {
33232 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33234 * register a Masonry Brick
33235 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33238 register : function(brick)
33240 //this.groups[brick.id] = brick;
33241 this.groups.add(brick.id, brick);
33244 * fetch a masonry brick based on the masonry brick ID
33245 * @param {string} the masonry brick to add
33246 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33249 get: function(brick_id)
33251 // if (typeof(this.groups[brick_id]) == 'undefined') {
33254 // return this.groups[brick_id] ;
33256 if(this.groups.key(brick_id)) {
33257 return this.groups.key(brick_id);
33275 * @class Roo.bootstrap.Brick
33276 * @extends Roo.bootstrap.Component
33277 * Bootstrap Brick class
33280 * Create a new Brick
33281 * @param {Object} config The config object
33284 Roo.bootstrap.Brick = function(config){
33285 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33291 * When a Brick is click
33292 * @param {Roo.bootstrap.Brick} this
33293 * @param {Roo.EventObject} e
33299 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33302 * @cfg {String} title
33306 * @cfg {String} html
33310 * @cfg {String} bgimage
33314 * @cfg {String} cls
33318 * @cfg {String} href
33322 * @cfg {String} video
33326 * @cfg {Boolean} square
33330 getAutoCreate : function()
33332 var cls = 'roo-brick';
33334 if(this.href.length){
33335 cls += ' roo-brick-link';
33338 if(this.bgimage.length){
33339 cls += ' roo-brick-image';
33342 if(!this.html.length && !this.bgimage.length){
33343 cls += ' roo-brick-center-title';
33346 if(!this.html.length && this.bgimage.length){
33347 cls += ' roo-brick-bottom-title';
33351 cls += ' ' + this.cls;
33355 tag: (this.href.length) ? 'a' : 'div',
33360 cls: 'roo-brick-paragraph',
33366 if(this.href.length){
33367 cfg.href = this.href;
33370 var cn = cfg.cn[0].cn;
33372 if(this.title.length){
33375 cls: 'roo-brick-title',
33380 if(this.html.length){
33383 cls: 'roo-brick-text',
33390 if(this.bgimage.length){
33393 cls: 'roo-brick-image-view',
33401 initEvents: function()
33403 if(this.title.length || this.html.length){
33404 this.el.on('mouseenter' ,this.enter, this);
33405 this.el.on('mouseleave', this.leave, this);
33408 Roo.EventManager.onWindowResize(this.resize, this);
33410 if(this.bgimage.length){
33411 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33412 this.imageEl.on('load', this.onImageLoad, this);
33419 onImageLoad : function()
33424 resize : function()
33426 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33428 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33430 if(this.bgimage.length){
33431 var image = this.el.select('.roo-brick-image-view', true).first();
33433 image.setWidth(paragraph.getWidth());
33436 image.setHeight(paragraph.getWidth());
33439 this.el.setHeight(image.getHeight());
33440 paragraph.setHeight(image.getHeight());
33446 enter: function(e, el)
33448 e.preventDefault();
33450 if(this.bgimage.length){
33451 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33452 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33456 leave: function(e, el)
33458 e.preventDefault();
33460 if(this.bgimage.length){
33461 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33462 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33477 * @class Roo.bootstrap.NumberField
33478 * @extends Roo.bootstrap.Input
33479 * Bootstrap NumberField class
33485 * Create a new NumberField
33486 * @param {Object} config The config object
33489 Roo.bootstrap.NumberField = function(config){
33490 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33493 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33496 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33498 allowDecimals : true,
33500 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33502 decimalSeparator : ".",
33504 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33506 decimalPrecision : 2,
33508 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33510 allowNegative : true,
33513 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33517 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33519 minValue : Number.NEGATIVE_INFINITY,
33521 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33523 maxValue : Number.MAX_VALUE,
33525 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33527 minText : "The minimum value for this field is {0}",
33529 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33531 maxText : "The maximum value for this field is {0}",
33533 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33534 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33536 nanText : "{0} is not a valid number",
33538 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33540 thousandsDelimiter : false,
33542 * @cfg {String} valueAlign alignment of value
33544 valueAlign : "left",
33546 getAutoCreate : function()
33548 var hiddenInput = {
33552 cls: 'hidden-number-input'
33556 hiddenInput.name = this.name;
33561 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33563 this.name = hiddenInput.name;
33565 if(cfg.cn.length > 0) {
33566 cfg.cn.push(hiddenInput);
33573 initEvents : function()
33575 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33577 var allowed = "0123456789";
33579 if(this.allowDecimals){
33580 allowed += this.decimalSeparator;
33583 if(this.allowNegative){
33587 if(this.thousandsDelimiter) {
33591 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33593 var keyPress = function(e){
33595 var k = e.getKey();
33597 var c = e.getCharCode();
33600 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33601 allowed.indexOf(String.fromCharCode(c)) === -1
33607 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33611 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33616 this.el.on("keypress", keyPress, this);
33619 validateValue : function(value)
33622 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33626 var num = this.parseValue(value);
33629 this.markInvalid(String.format(this.nanText, value));
33633 if(num < this.minValue){
33634 this.markInvalid(String.format(this.minText, this.minValue));
33638 if(num > this.maxValue){
33639 this.markInvalid(String.format(this.maxText, this.maxValue));
33646 getValue : function()
33648 var v = this.hiddenEl().getValue();
33650 return this.fixPrecision(this.parseValue(v));
33653 parseValue : function(value)
33655 if(this.thousandsDelimiter) {
33657 r = new RegExp(",", "g");
33658 value = value.replace(r, "");
33661 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33662 return isNaN(value) ? '' : value;
33665 fixPrecision : function(value)
33667 if(this.thousandsDelimiter) {
33669 r = new RegExp(",", "g");
33670 value = value.replace(r, "");
33673 var nan = isNaN(value);
33675 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33676 return nan ? '' : value;
33678 return parseFloat(value).toFixed(this.decimalPrecision);
33681 setValue : function(v)
33683 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33689 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33691 this.inputEl().dom.value = (v == '') ? '' :
33692 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33694 if(!this.allowZero && v === '0') {
33695 this.hiddenEl().dom.value = '';
33696 this.inputEl().dom.value = '';
33703 decimalPrecisionFcn : function(v)
33705 return Math.floor(v);
33708 beforeBlur : function()
33710 var v = this.parseValue(this.getRawValue());
33712 if(v || v === 0 || v === ''){
33717 hiddenEl : function()
33719 return this.el.select('input.hidden-number-input',true).first();
33731 * @class Roo.bootstrap.DocumentSlider
33732 * @extends Roo.bootstrap.Component
33733 * Bootstrap DocumentSlider class
33736 * Create a new DocumentViewer
33737 * @param {Object} config The config object
33740 Roo.bootstrap.DocumentSlider = function(config){
33741 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33748 * Fire after initEvent
33749 * @param {Roo.bootstrap.DocumentSlider} this
33754 * Fire after update
33755 * @param {Roo.bootstrap.DocumentSlider} this
33761 * @param {Roo.bootstrap.DocumentSlider} this
33767 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33773 getAutoCreate : function()
33777 cls : 'roo-document-slider',
33781 cls : 'roo-document-slider-header',
33785 cls : 'roo-document-slider-header-title'
33791 cls : 'roo-document-slider-body',
33795 cls : 'roo-document-slider-prev',
33799 cls : 'fa fa-chevron-left'
33805 cls : 'roo-document-slider-thumb',
33809 cls : 'roo-document-slider-image'
33815 cls : 'roo-document-slider-next',
33819 cls : 'fa fa-chevron-right'
33831 initEvents : function()
33833 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33834 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33836 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33837 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33839 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33840 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33842 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33843 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33845 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33846 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33848 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33849 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33851 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33852 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33854 this.thumbEl.on('click', this.onClick, this);
33856 this.prevIndicator.on('click', this.prev, this);
33858 this.nextIndicator.on('click', this.next, this);
33862 initial : function()
33864 if(this.files.length){
33865 this.indicator = 1;
33869 this.fireEvent('initial', this);
33872 update : function()
33874 this.imageEl.attr('src', this.files[this.indicator - 1]);
33876 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33878 this.prevIndicator.show();
33880 if(this.indicator == 1){
33881 this.prevIndicator.hide();
33884 this.nextIndicator.show();
33886 if(this.indicator == this.files.length){
33887 this.nextIndicator.hide();
33890 this.thumbEl.scrollTo('top');
33892 this.fireEvent('update', this);
33895 onClick : function(e)
33897 e.preventDefault();
33899 this.fireEvent('click', this);
33904 e.preventDefault();
33906 this.indicator = Math.max(1, this.indicator - 1);
33913 e.preventDefault();
33915 this.indicator = Math.min(this.files.length, this.indicator + 1);
33929 * @class Roo.bootstrap.RadioSet
33930 * @extends Roo.bootstrap.Input
33931 * Bootstrap RadioSet class
33932 * @cfg {String} indicatorpos (left|right) default left
33933 * @cfg {Boolean} inline (true|false) inline the element (default true)
33934 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33936 * Create a new RadioSet
33937 * @param {Object} config The config object
33940 Roo.bootstrap.RadioSet = function(config){
33942 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33946 Roo.bootstrap.RadioSet.register(this);
33951 * Fires when the element is checked or unchecked.
33952 * @param {Roo.bootstrap.RadioSet} this This radio
33953 * @param {Roo.bootstrap.Radio} item The checked item
33958 * Fires when the element is click.
33959 * @param {Roo.bootstrap.RadioSet} this This radio set
33960 * @param {Roo.bootstrap.Radio} item The checked item
33961 * @param {Roo.EventObject} e The event object
33968 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33976 indicatorpos : 'left',
33978 getAutoCreate : function()
33982 cls : 'roo-radio-set-label',
33986 html : this.fieldLabel
33991 if(this.indicatorpos == 'left'){
33994 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33995 tooltip : 'This field is required'
34000 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34001 tooltip : 'This field is required'
34007 cls : 'roo-radio-set-items'
34010 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34012 if (align === 'left' && this.fieldLabel.length) {
34015 cls : "roo-radio-set-right",
34021 if(this.labelWidth > 12){
34022 label.style = "width: " + this.labelWidth + 'px';
34025 if(this.labelWidth < 13 && this.labelmd == 0){
34026 this.labelmd = this.labelWidth;
34029 if(this.labellg > 0){
34030 label.cls += ' col-lg-' + this.labellg;
34031 items.cls += ' col-lg-' + (12 - this.labellg);
34034 if(this.labelmd > 0){
34035 label.cls += ' col-md-' + this.labelmd;
34036 items.cls += ' col-md-' + (12 - this.labelmd);
34039 if(this.labelsm > 0){
34040 label.cls += ' col-sm-' + this.labelsm;
34041 items.cls += ' col-sm-' + (12 - this.labelsm);
34044 if(this.labelxs > 0){
34045 label.cls += ' col-xs-' + this.labelxs;
34046 items.cls += ' col-xs-' + (12 - this.labelxs);
34052 cls : 'roo-radio-set',
34056 cls : 'roo-radio-set-input',
34059 value : this.value ? this.value : ''
34066 if(this.weight.length){
34067 cfg.cls += ' roo-radio-' + this.weight;
34071 cfg.cls += ' roo-radio-set-inline';
34075 ['xs','sm','md','lg'].map(function(size){
34076 if (settings[size]) {
34077 cfg.cls += ' col-' + size + '-' + settings[size];
34085 initEvents : function()
34087 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34088 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34090 if(!this.fieldLabel.length){
34091 this.labelEl.hide();
34094 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34095 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34097 this.indicator = this.indicatorEl();
34099 if(this.indicator){
34100 this.indicator.addClass('invisible');
34103 this.originalValue = this.getValue();
34107 inputEl: function ()
34109 return this.el.select('.roo-radio-set-input', true).first();
34112 getChildContainer : function()
34114 return this.itemsEl;
34117 register : function(item)
34119 this.radioes.push(item);
34123 validate : function()
34125 if(this.getVisibilityEl().hasClass('hidden')){
34131 Roo.each(this.radioes, function(i){
34140 if(this.allowBlank) {
34144 if(this.disabled || valid){
34149 this.markInvalid();
34154 markValid : function()
34156 if(this.labelEl.isVisible(true)){
34157 this.indicatorEl().removeClass('visible');
34158 this.indicatorEl().addClass('invisible');
34161 this.el.removeClass([this.invalidClass, this.validClass]);
34162 this.el.addClass(this.validClass);
34164 this.fireEvent('valid', this);
34167 markInvalid : function(msg)
34169 if(this.allowBlank || this.disabled){
34173 if(this.labelEl.isVisible(true)){
34174 this.indicatorEl().removeClass('invisible');
34175 this.indicatorEl().addClass('visible');
34178 this.el.removeClass([this.invalidClass, this.validClass]);
34179 this.el.addClass(this.invalidClass);
34181 this.fireEvent('invalid', this, msg);
34185 setValue : function(v, suppressEvent)
34187 if(this.value === v){
34194 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34197 Roo.each(this.radioes, function(i){
34199 i.el.removeClass('checked');
34202 Roo.each(this.radioes, function(i){
34204 if(i.value === v || i.value.toString() === v.toString()){
34206 i.el.addClass('checked');
34208 if(suppressEvent !== true){
34209 this.fireEvent('check', this, i);
34220 clearInvalid : function(){
34222 if(!this.el || this.preventMark){
34226 this.el.removeClass([this.invalidClass]);
34228 this.fireEvent('valid', this);
34233 Roo.apply(Roo.bootstrap.RadioSet, {
34237 register : function(set)
34239 this.groups[set.name] = set;
34242 get: function(name)
34244 if (typeof(this.groups[name]) == 'undefined') {
34248 return this.groups[name] ;
34254 * Ext JS Library 1.1.1
34255 * Copyright(c) 2006-2007, Ext JS, LLC.
34257 * Originally Released Under LGPL - original licence link has changed is not relivant.
34260 * <script type="text/javascript">
34265 * @class Roo.bootstrap.SplitBar
34266 * @extends Roo.util.Observable
34267 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34271 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34272 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34273 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34274 split.minSize = 100;
34275 split.maxSize = 600;
34276 split.animate = true;
34277 split.on('moved', splitterMoved);
34280 * Create a new SplitBar
34281 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34282 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34283 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34284 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34285 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34286 position of the SplitBar).
34288 Roo.bootstrap.SplitBar = function(cfg){
34293 // dragElement : elm
34294 // resizingElement: el,
34296 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34297 // placement : Roo.bootstrap.SplitBar.LEFT ,
34298 // existingProxy ???
34301 this.el = Roo.get(cfg.dragElement, true);
34302 this.el.dom.unselectable = "on";
34304 this.resizingEl = Roo.get(cfg.resizingElement, true);
34308 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34309 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34312 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34315 * The minimum size of the resizing element. (Defaults to 0)
34321 * The maximum size of the resizing element. (Defaults to 2000)
34324 this.maxSize = 2000;
34327 * Whether to animate the transition to the new size
34330 this.animate = false;
34333 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34336 this.useShim = false;
34341 if(!cfg.existingProxy){
34343 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34345 this.proxy = Roo.get(cfg.existingProxy).dom;
34348 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34351 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34354 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34357 this.dragSpecs = {};
34360 * @private The adapter to use to positon and resize elements
34362 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34363 this.adapter.init(this);
34365 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34367 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34368 this.el.addClass("roo-splitbar-h");
34371 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34372 this.el.addClass("roo-splitbar-v");
34378 * Fires when the splitter is moved (alias for {@link #event-moved})
34379 * @param {Roo.bootstrap.SplitBar} this
34380 * @param {Number} newSize the new width or height
34385 * Fires when the splitter is moved
34386 * @param {Roo.bootstrap.SplitBar} this
34387 * @param {Number} newSize the new width or height
34391 * @event beforeresize
34392 * Fires before the splitter is dragged
34393 * @param {Roo.bootstrap.SplitBar} this
34395 "beforeresize" : true,
34397 "beforeapply" : true
34400 Roo.util.Observable.call(this);
34403 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34404 onStartProxyDrag : function(x, y){
34405 this.fireEvent("beforeresize", this);
34407 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34409 o.enableDisplayMode("block");
34410 // all splitbars share the same overlay
34411 Roo.bootstrap.SplitBar.prototype.overlay = o;
34413 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34414 this.overlay.show();
34415 Roo.get(this.proxy).setDisplayed("block");
34416 var size = this.adapter.getElementSize(this);
34417 this.activeMinSize = this.getMinimumSize();;
34418 this.activeMaxSize = this.getMaximumSize();;
34419 var c1 = size - this.activeMinSize;
34420 var c2 = Math.max(this.activeMaxSize - size, 0);
34421 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34422 this.dd.resetConstraints();
34423 this.dd.setXConstraint(
34424 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34425 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34427 this.dd.setYConstraint(0, 0);
34429 this.dd.resetConstraints();
34430 this.dd.setXConstraint(0, 0);
34431 this.dd.setYConstraint(
34432 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34433 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34436 this.dragSpecs.startSize = size;
34437 this.dragSpecs.startPoint = [x, y];
34438 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34442 * @private Called after the drag operation by the DDProxy
34444 onEndProxyDrag : function(e){
34445 Roo.get(this.proxy).setDisplayed(false);
34446 var endPoint = Roo.lib.Event.getXY(e);
34448 this.overlay.hide();
34451 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34452 newSize = this.dragSpecs.startSize +
34453 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34454 endPoint[0] - this.dragSpecs.startPoint[0] :
34455 this.dragSpecs.startPoint[0] - endPoint[0]
34458 newSize = this.dragSpecs.startSize +
34459 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34460 endPoint[1] - this.dragSpecs.startPoint[1] :
34461 this.dragSpecs.startPoint[1] - endPoint[1]
34464 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34465 if(newSize != this.dragSpecs.startSize){
34466 if(this.fireEvent('beforeapply', this, newSize) !== false){
34467 this.adapter.setElementSize(this, newSize);
34468 this.fireEvent("moved", this, newSize);
34469 this.fireEvent("resize", this, newSize);
34475 * Get the adapter this SplitBar uses
34476 * @return The adapter object
34478 getAdapter : function(){
34479 return this.adapter;
34483 * Set the adapter this SplitBar uses
34484 * @param {Object} adapter A SplitBar adapter object
34486 setAdapter : function(adapter){
34487 this.adapter = adapter;
34488 this.adapter.init(this);
34492 * Gets the minimum size for the resizing element
34493 * @return {Number} The minimum size
34495 getMinimumSize : function(){
34496 return this.minSize;
34500 * Sets the minimum size for the resizing element
34501 * @param {Number} minSize The minimum size
34503 setMinimumSize : function(minSize){
34504 this.minSize = minSize;
34508 * Gets the maximum size for the resizing element
34509 * @return {Number} The maximum size
34511 getMaximumSize : function(){
34512 return this.maxSize;
34516 * Sets the maximum size for the resizing element
34517 * @param {Number} maxSize The maximum size
34519 setMaximumSize : function(maxSize){
34520 this.maxSize = maxSize;
34524 * Sets the initialize size for the resizing element
34525 * @param {Number} size The initial size
34527 setCurrentSize : function(size){
34528 var oldAnimate = this.animate;
34529 this.animate = false;
34530 this.adapter.setElementSize(this, size);
34531 this.animate = oldAnimate;
34535 * Destroy this splitbar.
34536 * @param {Boolean} removeEl True to remove the element
34538 destroy : function(removeEl){
34540 this.shim.remove();
34543 this.proxy.parentNode.removeChild(this.proxy);
34551 * @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.
34553 Roo.bootstrap.SplitBar.createProxy = function(dir){
34554 var proxy = new Roo.Element(document.createElement("div"));
34555 proxy.unselectable();
34556 var cls = 'roo-splitbar-proxy';
34557 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34558 document.body.appendChild(proxy.dom);
34563 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34564 * Default Adapter. It assumes the splitter and resizing element are not positioned
34565 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34567 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34570 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34571 // do nothing for now
34572 init : function(s){
34576 * Called before drag operations to get the current size of the resizing element.
34577 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34579 getElementSize : function(s){
34580 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34581 return s.resizingEl.getWidth();
34583 return s.resizingEl.getHeight();
34588 * Called after drag operations to set the size of the resizing element.
34589 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34590 * @param {Number} newSize The new size to set
34591 * @param {Function} onComplete A function to be invoked when resizing is complete
34593 setElementSize : function(s, newSize, onComplete){
34594 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34596 s.resizingEl.setWidth(newSize);
34598 onComplete(s, newSize);
34601 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34606 s.resizingEl.setHeight(newSize);
34608 onComplete(s, newSize);
34611 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34618 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34619 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34620 * Adapter that moves the splitter element to align with the resized sizing element.
34621 * Used with an absolute positioned SplitBar.
34622 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34623 * document.body, make sure you assign an id to the body element.
34625 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34626 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34627 this.container = Roo.get(container);
34630 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34631 init : function(s){
34632 this.basic.init(s);
34635 getElementSize : function(s){
34636 return this.basic.getElementSize(s);
34639 setElementSize : function(s, newSize, onComplete){
34640 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34643 moveSplitter : function(s){
34644 var yes = Roo.bootstrap.SplitBar;
34645 switch(s.placement){
34647 s.el.setX(s.resizingEl.getRight());
34650 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34653 s.el.setY(s.resizingEl.getBottom());
34656 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34663 * Orientation constant - Create a vertical SplitBar
34667 Roo.bootstrap.SplitBar.VERTICAL = 1;
34670 * Orientation constant - Create a horizontal SplitBar
34674 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34677 * Placement constant - The resizing element is to the left of the splitter element
34681 Roo.bootstrap.SplitBar.LEFT = 1;
34684 * Placement constant - The resizing element is to the right of the splitter element
34688 Roo.bootstrap.SplitBar.RIGHT = 2;
34691 * Placement constant - The resizing element is positioned above the splitter element
34695 Roo.bootstrap.SplitBar.TOP = 3;
34698 * Placement constant - The resizing element is positioned under splitter element
34702 Roo.bootstrap.SplitBar.BOTTOM = 4;
34703 Roo.namespace("Roo.bootstrap.layout");/*
34705 * Ext JS Library 1.1.1
34706 * Copyright(c) 2006-2007, Ext JS, LLC.
34708 * Originally Released Under LGPL - original licence link has changed is not relivant.
34711 * <script type="text/javascript">
34715 * @class Roo.bootstrap.layout.Manager
34716 * @extends Roo.bootstrap.Component
34717 * Base class for layout managers.
34719 Roo.bootstrap.layout.Manager = function(config)
34721 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34727 /** false to disable window resize monitoring @type Boolean */
34728 this.monitorWindowResize = true;
34733 * Fires when a layout is performed.
34734 * @param {Roo.LayoutManager} this
34738 * @event regionresized
34739 * Fires when the user resizes a region.
34740 * @param {Roo.LayoutRegion} region The resized region
34741 * @param {Number} newSize The new size (width for east/west, height for north/south)
34743 "regionresized" : true,
34745 * @event regioncollapsed
34746 * Fires when a region is collapsed.
34747 * @param {Roo.LayoutRegion} region The collapsed region
34749 "regioncollapsed" : true,
34751 * @event regionexpanded
34752 * Fires when a region is expanded.
34753 * @param {Roo.LayoutRegion} region The expanded region
34755 "regionexpanded" : true
34757 this.updating = false;
34760 this.el = Roo.get(config.el);
34766 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34771 monitorWindowResize : true,
34777 onRender : function(ct, position)
34780 this.el = Roo.get(ct);
34783 //this.fireEvent('render',this);
34787 initEvents: function()
34791 // ie scrollbar fix
34792 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34793 document.body.scroll = "no";
34794 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34795 this.el.position('relative');
34797 this.id = this.el.id;
34798 this.el.addClass("roo-layout-container");
34799 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34800 if(this.el.dom != document.body ) {
34801 this.el.on('resize', this.layout,this);
34802 this.el.on('show', this.layout,this);
34808 * Returns true if this layout is currently being updated
34809 * @return {Boolean}
34811 isUpdating : function(){
34812 return this.updating;
34816 * Suspend the LayoutManager from doing auto-layouts while
34817 * making multiple add or remove calls
34819 beginUpdate : function(){
34820 this.updating = true;
34824 * Restore auto-layouts and optionally disable the manager from performing a layout
34825 * @param {Boolean} noLayout true to disable a layout update
34827 endUpdate : function(noLayout){
34828 this.updating = false;
34834 layout: function(){
34838 onRegionResized : function(region, newSize){
34839 this.fireEvent("regionresized", region, newSize);
34843 onRegionCollapsed : function(region){
34844 this.fireEvent("regioncollapsed", region);
34847 onRegionExpanded : function(region){
34848 this.fireEvent("regionexpanded", region);
34852 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34853 * performs box-model adjustments.
34854 * @return {Object} The size as an object {width: (the width), height: (the height)}
34856 getViewSize : function()
34859 if(this.el.dom != document.body){
34860 size = this.el.getSize();
34862 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34864 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34865 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34870 * Returns the Element this layout is bound to.
34871 * @return {Roo.Element}
34873 getEl : function(){
34878 * Returns the specified region.
34879 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34880 * @return {Roo.LayoutRegion}
34882 getRegion : function(target){
34883 return this.regions[target.toLowerCase()];
34886 onWindowResize : function(){
34887 if(this.monitorWindowResize){
34894 * Ext JS Library 1.1.1
34895 * Copyright(c) 2006-2007, Ext JS, LLC.
34897 * Originally Released Under LGPL - original licence link has changed is not relivant.
34900 * <script type="text/javascript">
34903 * @class Roo.bootstrap.layout.Border
34904 * @extends Roo.bootstrap.layout.Manager
34905 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34906 * please see: examples/bootstrap/nested.html<br><br>
34908 <b>The container the layout is rendered into can be either the body element or any other element.
34909 If it is not the body element, the container needs to either be an absolute positioned element,
34910 or you will need to add "position:relative" to the css of the container. You will also need to specify
34911 the container size if it is not the body element.</b>
34914 * Create a new Border
34915 * @param {Object} config Configuration options
34917 Roo.bootstrap.layout.Border = function(config){
34918 config = config || {};
34919 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34923 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34924 if(config[region]){
34925 config[region].region = region;
34926 this.addRegion(config[region]);
34932 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34934 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34936 * Creates and adds a new region if it doesn't already exist.
34937 * @param {String} target The target region key (north, south, east, west or center).
34938 * @param {Object} config The regions config object
34939 * @return {BorderLayoutRegion} The new region
34941 addRegion : function(config)
34943 if(!this.regions[config.region]){
34944 var r = this.factory(config);
34945 this.bindRegion(r);
34947 return this.regions[config.region];
34951 bindRegion : function(r){
34952 this.regions[r.config.region] = r;
34954 r.on("visibilitychange", this.layout, this);
34955 r.on("paneladded", this.layout, this);
34956 r.on("panelremoved", this.layout, this);
34957 r.on("invalidated", this.layout, this);
34958 r.on("resized", this.onRegionResized, this);
34959 r.on("collapsed", this.onRegionCollapsed, this);
34960 r.on("expanded", this.onRegionExpanded, this);
34964 * Performs a layout update.
34966 layout : function()
34968 if(this.updating) {
34972 // render all the rebions if they have not been done alreayd?
34973 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34974 if(this.regions[region] && !this.regions[region].bodyEl){
34975 this.regions[region].onRender(this.el)
34979 var size = this.getViewSize();
34980 var w = size.width;
34981 var h = size.height;
34986 //var x = 0, y = 0;
34988 var rs = this.regions;
34989 var north = rs["north"];
34990 var south = rs["south"];
34991 var west = rs["west"];
34992 var east = rs["east"];
34993 var center = rs["center"];
34994 //if(this.hideOnLayout){ // not supported anymore
34995 //c.el.setStyle("display", "none");
34997 if(north && north.isVisible()){
34998 var b = north.getBox();
34999 var m = north.getMargins();
35000 b.width = w - (m.left+m.right);
35003 centerY = b.height + b.y + m.bottom;
35004 centerH -= centerY;
35005 north.updateBox(this.safeBox(b));
35007 if(south && south.isVisible()){
35008 var b = south.getBox();
35009 var m = south.getMargins();
35010 b.width = w - (m.left+m.right);
35012 var totalHeight = (b.height + m.top + m.bottom);
35013 b.y = h - totalHeight + m.top;
35014 centerH -= totalHeight;
35015 south.updateBox(this.safeBox(b));
35017 if(west && west.isVisible()){
35018 var b = west.getBox();
35019 var m = west.getMargins();
35020 b.height = centerH - (m.top+m.bottom);
35022 b.y = centerY + m.top;
35023 var totalWidth = (b.width + m.left + m.right);
35024 centerX += totalWidth;
35025 centerW -= totalWidth;
35026 west.updateBox(this.safeBox(b));
35028 if(east && east.isVisible()){
35029 var b = east.getBox();
35030 var m = east.getMargins();
35031 b.height = centerH - (m.top+m.bottom);
35032 var totalWidth = (b.width + m.left + m.right);
35033 b.x = w - totalWidth + m.left;
35034 b.y = centerY + m.top;
35035 centerW -= totalWidth;
35036 east.updateBox(this.safeBox(b));
35039 var m = center.getMargins();
35041 x: centerX + m.left,
35042 y: centerY + m.top,
35043 width: centerW - (m.left+m.right),
35044 height: centerH - (m.top+m.bottom)
35046 //if(this.hideOnLayout){
35047 //center.el.setStyle("display", "block");
35049 center.updateBox(this.safeBox(centerBox));
35052 this.fireEvent("layout", this);
35056 safeBox : function(box){
35057 box.width = Math.max(0, box.width);
35058 box.height = Math.max(0, box.height);
35063 * Adds a ContentPanel (or subclass) to this layout.
35064 * @param {String} target The target region key (north, south, east, west or center).
35065 * @param {Roo.ContentPanel} panel The panel to add
35066 * @return {Roo.ContentPanel} The added panel
35068 add : function(target, panel){
35070 target = target.toLowerCase();
35071 return this.regions[target].add(panel);
35075 * Remove a ContentPanel (or subclass) to this layout.
35076 * @param {String} target The target region key (north, south, east, west or center).
35077 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35078 * @return {Roo.ContentPanel} The removed panel
35080 remove : function(target, panel){
35081 target = target.toLowerCase();
35082 return this.regions[target].remove(panel);
35086 * Searches all regions for a panel with the specified id
35087 * @param {String} panelId
35088 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35090 findPanel : function(panelId){
35091 var rs = this.regions;
35092 for(var target in rs){
35093 if(typeof rs[target] != "function"){
35094 var p = rs[target].getPanel(panelId);
35104 * Searches all regions for a panel with the specified id and activates (shows) it.
35105 * @param {String/ContentPanel} panelId The panels id or the panel itself
35106 * @return {Roo.ContentPanel} The shown panel or null
35108 showPanel : function(panelId) {
35109 var rs = this.regions;
35110 for(var target in rs){
35111 var r = rs[target];
35112 if(typeof r != "function"){
35113 if(r.hasPanel(panelId)){
35114 return r.showPanel(panelId);
35122 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35123 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35126 restoreState : function(provider){
35128 provider = Roo.state.Manager;
35130 var sm = new Roo.LayoutStateManager();
35131 sm.init(this, provider);
35137 * Adds a xtype elements to the layout.
35141 xtype : 'ContentPanel',
35148 xtype : 'NestedLayoutPanel',
35154 items : [ ... list of content panels or nested layout panels.. ]
35158 * @param {Object} cfg Xtype definition of item to add.
35160 addxtype : function(cfg)
35162 // basically accepts a pannel...
35163 // can accept a layout region..!?!?
35164 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35167 // theory? children can only be panels??
35169 //if (!cfg.xtype.match(/Panel$/)) {
35174 if (typeof(cfg.region) == 'undefined') {
35175 Roo.log("Failed to add Panel, region was not set");
35179 var region = cfg.region;
35185 xitems = cfg.items;
35192 case 'Content': // ContentPanel (el, cfg)
35193 case 'Scroll': // ContentPanel (el, cfg)
35195 cfg.autoCreate = true;
35196 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35198 // var el = this.el.createChild();
35199 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35202 this.add(region, ret);
35206 case 'TreePanel': // our new panel!
35207 cfg.el = this.el.createChild();
35208 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35209 this.add(region, ret);
35214 // create a new Layout (which is a Border Layout...
35216 var clayout = cfg.layout;
35217 clayout.el = this.el.createChild();
35218 clayout.items = clayout.items || [];
35222 // replace this exitems with the clayout ones..
35223 xitems = clayout.items;
35225 // force background off if it's in center...
35226 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35227 cfg.background = false;
35229 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35232 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35233 //console.log('adding nested layout panel ' + cfg.toSource());
35234 this.add(region, ret);
35235 nb = {}; /// find first...
35240 // needs grid and region
35242 //var el = this.getRegion(region).el.createChild();
35244 *var el = this.el.createChild();
35245 // create the grid first...
35246 cfg.grid.container = el;
35247 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35250 if (region == 'center' && this.active ) {
35251 cfg.background = false;
35254 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35256 this.add(region, ret);
35258 if (cfg.background) {
35259 // render grid on panel activation (if panel background)
35260 ret.on('activate', function(gp) {
35261 if (!gp.grid.rendered) {
35262 // gp.grid.render(el);
35266 // cfg.grid.render(el);
35272 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35273 // it was the old xcomponent building that caused this before.
35274 // espeically if border is the top element in the tree.
35284 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35286 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35287 this.add(region, ret);
35291 throw "Can not add '" + cfg.xtype + "' to Border";
35297 this.beginUpdate();
35301 Roo.each(xitems, function(i) {
35302 region = nb && i.region ? i.region : false;
35304 var add = ret.addxtype(i);
35307 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35308 if (!i.background) {
35309 abn[region] = nb[region] ;
35316 // make the last non-background panel active..
35317 //if (nb) { Roo.log(abn); }
35320 for(var r in abn) {
35321 region = this.getRegion(r);
35323 // tried using nb[r], but it does not work..
35325 region.showPanel(abn[r]);
35336 factory : function(cfg)
35339 var validRegions = Roo.bootstrap.layout.Border.regions;
35341 var target = cfg.region;
35344 var r = Roo.bootstrap.layout;
35348 return new r.North(cfg);
35350 return new r.South(cfg);
35352 return new r.East(cfg);
35354 return new r.West(cfg);
35356 return new r.Center(cfg);
35358 throw 'Layout region "'+target+'" not supported.';
35365 * Ext JS Library 1.1.1
35366 * Copyright(c) 2006-2007, Ext JS, LLC.
35368 * Originally Released Under LGPL - original licence link has changed is not relivant.
35371 * <script type="text/javascript">
35375 * @class Roo.bootstrap.layout.Basic
35376 * @extends Roo.util.Observable
35377 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35378 * and does not have a titlebar, tabs or any other features. All it does is size and position
35379 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35380 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35381 * @cfg {string} region the region that it inhabits..
35382 * @cfg {bool} skipConfig skip config?
35386 Roo.bootstrap.layout.Basic = function(config){
35388 this.mgr = config.mgr;
35390 this.position = config.region;
35392 var skipConfig = config.skipConfig;
35396 * @scope Roo.BasicLayoutRegion
35400 * @event beforeremove
35401 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35402 * @param {Roo.LayoutRegion} this
35403 * @param {Roo.ContentPanel} panel The panel
35404 * @param {Object} e The cancel event object
35406 "beforeremove" : true,
35408 * @event invalidated
35409 * Fires when the layout for this region is changed.
35410 * @param {Roo.LayoutRegion} this
35412 "invalidated" : true,
35414 * @event visibilitychange
35415 * Fires when this region is shown or hidden
35416 * @param {Roo.LayoutRegion} this
35417 * @param {Boolean} visibility true or false
35419 "visibilitychange" : true,
35421 * @event paneladded
35422 * Fires when a panel is added.
35423 * @param {Roo.LayoutRegion} this
35424 * @param {Roo.ContentPanel} panel The panel
35426 "paneladded" : true,
35428 * @event panelremoved
35429 * Fires when a panel is removed.
35430 * @param {Roo.LayoutRegion} this
35431 * @param {Roo.ContentPanel} panel The panel
35433 "panelremoved" : true,
35435 * @event beforecollapse
35436 * Fires when this region before collapse.
35437 * @param {Roo.LayoutRegion} this
35439 "beforecollapse" : true,
35442 * Fires when this region is collapsed.
35443 * @param {Roo.LayoutRegion} this
35445 "collapsed" : true,
35448 * Fires when this region is expanded.
35449 * @param {Roo.LayoutRegion} this
35454 * Fires when this region is slid into view.
35455 * @param {Roo.LayoutRegion} this
35457 "slideshow" : true,
35460 * Fires when this region slides out of view.
35461 * @param {Roo.LayoutRegion} this
35463 "slidehide" : true,
35465 * @event panelactivated
35466 * Fires when a panel is activated.
35467 * @param {Roo.LayoutRegion} this
35468 * @param {Roo.ContentPanel} panel The activated panel
35470 "panelactivated" : true,
35473 * Fires when the user resizes this region.
35474 * @param {Roo.LayoutRegion} this
35475 * @param {Number} newSize The new size (width for east/west, height for north/south)
35479 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35480 this.panels = new Roo.util.MixedCollection();
35481 this.panels.getKey = this.getPanelId.createDelegate(this);
35483 this.activePanel = null;
35484 // ensure listeners are added...
35486 if (config.listeners || config.events) {
35487 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35488 listeners : config.listeners || {},
35489 events : config.events || {}
35493 if(skipConfig !== true){
35494 this.applyConfig(config);
35498 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35500 getPanelId : function(p){
35504 applyConfig : function(config){
35505 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35506 this.config = config;
35511 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35512 * the width, for horizontal (north, south) the height.
35513 * @param {Number} newSize The new width or height
35515 resizeTo : function(newSize){
35516 var el = this.el ? this.el :
35517 (this.activePanel ? this.activePanel.getEl() : null);
35519 switch(this.position){
35522 el.setWidth(newSize);
35523 this.fireEvent("resized", this, newSize);
35527 el.setHeight(newSize);
35528 this.fireEvent("resized", this, newSize);
35534 getBox : function(){
35535 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35538 getMargins : function(){
35539 return this.margins;
35542 updateBox : function(box){
35544 var el = this.activePanel.getEl();
35545 el.dom.style.left = box.x + "px";
35546 el.dom.style.top = box.y + "px";
35547 this.activePanel.setSize(box.width, box.height);
35551 * Returns the container element for this region.
35552 * @return {Roo.Element}
35554 getEl : function(){
35555 return this.activePanel;
35559 * Returns true if this region is currently visible.
35560 * @return {Boolean}
35562 isVisible : function(){
35563 return this.activePanel ? true : false;
35566 setActivePanel : function(panel){
35567 panel = this.getPanel(panel);
35568 if(this.activePanel && this.activePanel != panel){
35569 this.activePanel.setActiveState(false);
35570 this.activePanel.getEl().setLeftTop(-10000,-10000);
35572 this.activePanel = panel;
35573 panel.setActiveState(true);
35575 panel.setSize(this.box.width, this.box.height);
35577 this.fireEvent("panelactivated", this, panel);
35578 this.fireEvent("invalidated");
35582 * Show the specified panel.
35583 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35584 * @return {Roo.ContentPanel} The shown panel or null
35586 showPanel : function(panel){
35587 panel = this.getPanel(panel);
35589 this.setActivePanel(panel);
35595 * Get the active panel for this region.
35596 * @return {Roo.ContentPanel} The active panel or null
35598 getActivePanel : function(){
35599 return this.activePanel;
35603 * Add the passed ContentPanel(s)
35604 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35605 * @return {Roo.ContentPanel} The panel added (if only one was added)
35607 add : function(panel){
35608 if(arguments.length > 1){
35609 for(var i = 0, len = arguments.length; i < len; i++) {
35610 this.add(arguments[i]);
35614 if(this.hasPanel(panel)){
35615 this.showPanel(panel);
35618 var el = panel.getEl();
35619 if(el.dom.parentNode != this.mgr.el.dom){
35620 this.mgr.el.dom.appendChild(el.dom);
35622 if(panel.setRegion){
35623 panel.setRegion(this);
35625 this.panels.add(panel);
35626 el.setStyle("position", "absolute");
35627 if(!panel.background){
35628 this.setActivePanel(panel);
35629 if(this.config.initialSize && this.panels.getCount()==1){
35630 this.resizeTo(this.config.initialSize);
35633 this.fireEvent("paneladded", this, panel);
35638 * Returns true if the panel is in this region.
35639 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35640 * @return {Boolean}
35642 hasPanel : function(panel){
35643 if(typeof panel == "object"){ // must be panel obj
35644 panel = panel.getId();
35646 return this.getPanel(panel) ? true : false;
35650 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35651 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35652 * @param {Boolean} preservePanel Overrides the config preservePanel option
35653 * @return {Roo.ContentPanel} The panel that was removed
35655 remove : function(panel, preservePanel){
35656 panel = this.getPanel(panel);
35661 this.fireEvent("beforeremove", this, panel, e);
35662 if(e.cancel === true){
35665 var panelId = panel.getId();
35666 this.panels.removeKey(panelId);
35671 * Returns the panel specified or null if it's not in this region.
35672 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35673 * @return {Roo.ContentPanel}
35675 getPanel : function(id){
35676 if(typeof id == "object"){ // must be panel obj
35679 return this.panels.get(id);
35683 * Returns this regions position (north/south/east/west/center).
35686 getPosition: function(){
35687 return this.position;
35691 * Ext JS Library 1.1.1
35692 * Copyright(c) 2006-2007, Ext JS, LLC.
35694 * Originally Released Under LGPL - original licence link has changed is not relivant.
35697 * <script type="text/javascript">
35701 * @class Roo.bootstrap.layout.Region
35702 * @extends Roo.bootstrap.layout.Basic
35703 * This class represents a region in a layout manager.
35705 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35706 * @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})
35707 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35708 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35709 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35710 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35711 * @cfg {String} title The title for the region (overrides panel titles)
35712 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35713 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35714 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35715 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35716 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35717 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35718 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35719 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35720 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35721 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35723 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35724 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35725 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35726 * @cfg {Number} width For East/West panels
35727 * @cfg {Number} height For North/South panels
35728 * @cfg {Boolean} split To show the splitter
35729 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35731 * @cfg {string} cls Extra CSS classes to add to region
35733 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35734 * @cfg {string} region the region that it inhabits..
35737 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35738 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35740 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35741 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35742 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35744 Roo.bootstrap.layout.Region = function(config)
35746 this.applyConfig(config);
35748 var mgr = config.mgr;
35749 var pos = config.region;
35750 config.skipConfig = true;
35751 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35754 this.onRender(mgr.el);
35757 this.visible = true;
35758 this.collapsed = false;
35759 this.unrendered_panels = [];
35762 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35764 position: '', // set by wrapper (eg. north/south etc..)
35765 unrendered_panels : null, // unrendered panels.
35766 createBody : function(){
35767 /** This region's body element
35768 * @type Roo.Element */
35769 this.bodyEl = this.el.createChild({
35771 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35775 onRender: function(ctr, pos)
35777 var dh = Roo.DomHelper;
35778 /** This region's container element
35779 * @type Roo.Element */
35780 this.el = dh.append(ctr.dom, {
35782 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35784 /** This region's title element
35785 * @type Roo.Element */
35787 this.titleEl = dh.append(this.el.dom,
35790 unselectable: "on",
35791 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35793 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35794 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35797 this.titleEl.enableDisplayMode();
35798 /** This region's title text element
35799 * @type HTMLElement */
35800 this.titleTextEl = this.titleEl.dom.firstChild;
35801 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35803 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35804 this.closeBtn.enableDisplayMode();
35805 this.closeBtn.on("click", this.closeClicked, this);
35806 this.closeBtn.hide();
35808 this.createBody(this.config);
35809 if(this.config.hideWhenEmpty){
35811 this.on("paneladded", this.validateVisibility, this);
35812 this.on("panelremoved", this.validateVisibility, this);
35814 if(this.autoScroll){
35815 this.bodyEl.setStyle("overflow", "auto");
35817 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35819 //if(c.titlebar !== false){
35820 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35821 this.titleEl.hide();
35823 this.titleEl.show();
35824 if(this.config.title){
35825 this.titleTextEl.innerHTML = this.config.title;
35829 if(this.config.collapsed){
35830 this.collapse(true);
35832 if(this.config.hidden){
35836 if (this.unrendered_panels && this.unrendered_panels.length) {
35837 for (var i =0;i< this.unrendered_panels.length; i++) {
35838 this.add(this.unrendered_panels[i]);
35840 this.unrendered_panels = null;
35846 applyConfig : function(c)
35849 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35850 var dh = Roo.DomHelper;
35851 if(c.titlebar !== false){
35852 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35853 this.collapseBtn.on("click", this.collapse, this);
35854 this.collapseBtn.enableDisplayMode();
35856 if(c.showPin === true || this.showPin){
35857 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35858 this.stickBtn.enableDisplayMode();
35859 this.stickBtn.on("click", this.expand, this);
35860 this.stickBtn.hide();
35865 /** This region's collapsed element
35866 * @type Roo.Element */
35869 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35870 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35873 if(c.floatable !== false){
35874 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35875 this.collapsedEl.on("click", this.collapseClick, this);
35878 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35879 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35880 id: "message", unselectable: "on", style:{"float":"left"}});
35881 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35883 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35884 this.expandBtn.on("click", this.expand, this);
35888 if(this.collapseBtn){
35889 this.collapseBtn.setVisible(c.collapsible == true);
35892 this.cmargins = c.cmargins || this.cmargins ||
35893 (this.position == "west" || this.position == "east" ?
35894 {top: 0, left: 2, right:2, bottom: 0} :
35895 {top: 2, left: 0, right:0, bottom: 2});
35897 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35900 this.bottomTabs = c.tabPosition != "top";
35902 this.autoScroll = c.autoScroll || false;
35907 this.duration = c.duration || .30;
35908 this.slideDuration = c.slideDuration || .45;
35913 * Returns true if this region is currently visible.
35914 * @return {Boolean}
35916 isVisible : function(){
35917 return this.visible;
35921 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35922 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35924 //setCollapsedTitle : function(title){
35925 // title = title || " ";
35926 // if(this.collapsedTitleTextEl){
35927 // this.collapsedTitleTextEl.innerHTML = title;
35931 getBox : function(){
35933 // if(!this.collapsed){
35934 b = this.el.getBox(false, true);
35936 // b = this.collapsedEl.getBox(false, true);
35941 getMargins : function(){
35942 return this.margins;
35943 //return this.collapsed ? this.cmargins : this.margins;
35946 highlight : function(){
35947 this.el.addClass("x-layout-panel-dragover");
35950 unhighlight : function(){
35951 this.el.removeClass("x-layout-panel-dragover");
35954 updateBox : function(box)
35956 if (!this.bodyEl) {
35957 return; // not rendered yet..
35961 if(!this.collapsed){
35962 this.el.dom.style.left = box.x + "px";
35963 this.el.dom.style.top = box.y + "px";
35964 this.updateBody(box.width, box.height);
35966 this.collapsedEl.dom.style.left = box.x + "px";
35967 this.collapsedEl.dom.style.top = box.y + "px";
35968 this.collapsedEl.setSize(box.width, box.height);
35971 this.tabs.autoSizeTabs();
35975 updateBody : function(w, h)
35978 this.el.setWidth(w);
35979 w -= this.el.getBorderWidth("rl");
35980 if(this.config.adjustments){
35981 w += this.config.adjustments[0];
35984 if(h !== null && h > 0){
35985 this.el.setHeight(h);
35986 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35987 h -= this.el.getBorderWidth("tb");
35988 if(this.config.adjustments){
35989 h += this.config.adjustments[1];
35991 this.bodyEl.setHeight(h);
35993 h = this.tabs.syncHeight(h);
35996 if(this.panelSize){
35997 w = w !== null ? w : this.panelSize.width;
35998 h = h !== null ? h : this.panelSize.height;
36000 if(this.activePanel){
36001 var el = this.activePanel.getEl();
36002 w = w !== null ? w : el.getWidth();
36003 h = h !== null ? h : el.getHeight();
36004 this.panelSize = {width: w, height: h};
36005 this.activePanel.setSize(w, h);
36007 if(Roo.isIE && this.tabs){
36008 this.tabs.el.repaint();
36013 * Returns the container element for this region.
36014 * @return {Roo.Element}
36016 getEl : function(){
36021 * Hides this region.
36024 //if(!this.collapsed){
36025 this.el.dom.style.left = "-2000px";
36028 // this.collapsedEl.dom.style.left = "-2000px";
36029 // this.collapsedEl.hide();
36031 this.visible = false;
36032 this.fireEvent("visibilitychange", this, false);
36036 * Shows this region if it was previously hidden.
36039 //if(!this.collapsed){
36042 // this.collapsedEl.show();
36044 this.visible = true;
36045 this.fireEvent("visibilitychange", this, true);
36048 closeClicked : function(){
36049 if(this.activePanel){
36050 this.remove(this.activePanel);
36054 collapseClick : function(e){
36056 e.stopPropagation();
36059 e.stopPropagation();
36065 * Collapses this region.
36066 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36069 collapse : function(skipAnim, skipCheck = false){
36070 if(this.collapsed) {
36074 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36076 this.collapsed = true;
36078 this.split.el.hide();
36080 if(this.config.animate && skipAnim !== true){
36081 this.fireEvent("invalidated", this);
36082 this.animateCollapse();
36084 this.el.setLocation(-20000,-20000);
36086 this.collapsedEl.show();
36087 this.fireEvent("collapsed", this);
36088 this.fireEvent("invalidated", this);
36094 animateCollapse : function(){
36099 * Expands this region if it was previously collapsed.
36100 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36101 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36104 expand : function(e, skipAnim){
36106 e.stopPropagation();
36108 if(!this.collapsed || this.el.hasActiveFx()) {
36112 this.afterSlideIn();
36115 this.collapsed = false;
36116 if(this.config.animate && skipAnim !== true){
36117 this.animateExpand();
36121 this.split.el.show();
36123 this.collapsedEl.setLocation(-2000,-2000);
36124 this.collapsedEl.hide();
36125 this.fireEvent("invalidated", this);
36126 this.fireEvent("expanded", this);
36130 animateExpand : function(){
36134 initTabs : function()
36136 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36138 var ts = new Roo.bootstrap.panel.Tabs({
36139 el: this.bodyEl.dom,
36140 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36141 disableTooltips: this.config.disableTabTips,
36142 toolbar : this.config.toolbar
36145 if(this.config.hideTabs){
36146 ts.stripWrap.setDisplayed(false);
36149 ts.resizeTabs = this.config.resizeTabs === true;
36150 ts.minTabWidth = this.config.minTabWidth || 40;
36151 ts.maxTabWidth = this.config.maxTabWidth || 250;
36152 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36153 ts.monitorResize = false;
36154 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36155 ts.bodyEl.addClass('roo-layout-tabs-body');
36156 this.panels.each(this.initPanelAsTab, this);
36159 initPanelAsTab : function(panel){
36160 var ti = this.tabs.addTab(
36164 this.config.closeOnTab && panel.isClosable(),
36167 if(panel.tabTip !== undefined){
36168 ti.setTooltip(panel.tabTip);
36170 ti.on("activate", function(){
36171 this.setActivePanel(panel);
36174 if(this.config.closeOnTab){
36175 ti.on("beforeclose", function(t, e){
36177 this.remove(panel);
36181 panel.tabItem = ti;
36186 updatePanelTitle : function(panel, title)
36188 if(this.activePanel == panel){
36189 this.updateTitle(title);
36192 var ti = this.tabs.getTab(panel.getEl().id);
36194 if(panel.tabTip !== undefined){
36195 ti.setTooltip(panel.tabTip);
36200 updateTitle : function(title){
36201 if(this.titleTextEl && !this.config.title){
36202 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36206 setActivePanel : function(panel)
36208 panel = this.getPanel(panel);
36209 if(this.activePanel && this.activePanel != panel){
36210 if(this.activePanel.setActiveState(false) === false){
36214 this.activePanel = panel;
36215 panel.setActiveState(true);
36216 if(this.panelSize){
36217 panel.setSize(this.panelSize.width, this.panelSize.height);
36220 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36222 this.updateTitle(panel.getTitle());
36224 this.fireEvent("invalidated", this);
36226 this.fireEvent("panelactivated", this, panel);
36230 * Shows the specified panel.
36231 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36232 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36234 showPanel : function(panel)
36236 panel = this.getPanel(panel);
36239 var tab = this.tabs.getTab(panel.getEl().id);
36240 if(tab.isHidden()){
36241 this.tabs.unhideTab(tab.id);
36245 this.setActivePanel(panel);
36252 * Get the active panel for this region.
36253 * @return {Roo.ContentPanel} The active panel or null
36255 getActivePanel : function(){
36256 return this.activePanel;
36259 validateVisibility : function(){
36260 if(this.panels.getCount() < 1){
36261 this.updateTitle(" ");
36262 this.closeBtn.hide();
36265 if(!this.isVisible()){
36272 * Adds the passed ContentPanel(s) to this region.
36273 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36274 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36276 add : function(panel)
36278 if(arguments.length > 1){
36279 for(var i = 0, len = arguments.length; i < len; i++) {
36280 this.add(arguments[i]);
36285 // if we have not been rendered yet, then we can not really do much of this..
36286 if (!this.bodyEl) {
36287 this.unrendered_panels.push(panel);
36294 if(this.hasPanel(panel)){
36295 this.showPanel(panel);
36298 panel.setRegion(this);
36299 this.panels.add(panel);
36300 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36301 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36302 // and hide them... ???
36303 this.bodyEl.dom.appendChild(panel.getEl().dom);
36304 if(panel.background !== true){
36305 this.setActivePanel(panel);
36307 this.fireEvent("paneladded", this, panel);
36314 this.initPanelAsTab(panel);
36318 if(panel.background !== true){
36319 this.tabs.activate(panel.getEl().id);
36321 this.fireEvent("paneladded", this, panel);
36326 * Hides the tab for the specified panel.
36327 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36329 hidePanel : function(panel){
36330 if(this.tabs && (panel = this.getPanel(panel))){
36331 this.tabs.hideTab(panel.getEl().id);
36336 * Unhides the tab for a previously hidden panel.
36337 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36339 unhidePanel : function(panel){
36340 if(this.tabs && (panel = this.getPanel(panel))){
36341 this.tabs.unhideTab(panel.getEl().id);
36345 clearPanels : function(){
36346 while(this.panels.getCount() > 0){
36347 this.remove(this.panels.first());
36352 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36353 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36354 * @param {Boolean} preservePanel Overrides the config preservePanel option
36355 * @return {Roo.ContentPanel} The panel that was removed
36357 remove : function(panel, preservePanel)
36359 panel = this.getPanel(panel);
36364 this.fireEvent("beforeremove", this, panel, e);
36365 if(e.cancel === true){
36368 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36369 var panelId = panel.getId();
36370 this.panels.removeKey(panelId);
36372 document.body.appendChild(panel.getEl().dom);
36375 this.tabs.removeTab(panel.getEl().id);
36376 }else if (!preservePanel){
36377 this.bodyEl.dom.removeChild(panel.getEl().dom);
36379 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36380 var p = this.panels.first();
36381 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36382 tempEl.appendChild(p.getEl().dom);
36383 this.bodyEl.update("");
36384 this.bodyEl.dom.appendChild(p.getEl().dom);
36386 this.updateTitle(p.getTitle());
36388 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36389 this.setActivePanel(p);
36391 panel.setRegion(null);
36392 if(this.activePanel == panel){
36393 this.activePanel = null;
36395 if(this.config.autoDestroy !== false && preservePanel !== true){
36396 try{panel.destroy();}catch(e){}
36398 this.fireEvent("panelremoved", this, panel);
36403 * Returns the TabPanel component used by this region
36404 * @return {Roo.TabPanel}
36406 getTabs : function(){
36410 createTool : function(parentEl, className){
36411 var btn = Roo.DomHelper.append(parentEl, {
36413 cls: "x-layout-tools-button",
36416 cls: "roo-layout-tools-button-inner " + className,
36420 btn.addClassOnOver("roo-layout-tools-button-over");
36425 * Ext JS Library 1.1.1
36426 * Copyright(c) 2006-2007, Ext JS, LLC.
36428 * Originally Released Under LGPL - original licence link has changed is not relivant.
36431 * <script type="text/javascript">
36437 * @class Roo.SplitLayoutRegion
36438 * @extends Roo.LayoutRegion
36439 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36441 Roo.bootstrap.layout.Split = function(config){
36442 this.cursor = config.cursor;
36443 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36446 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36448 splitTip : "Drag to resize.",
36449 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36450 useSplitTips : false,
36452 applyConfig : function(config){
36453 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36456 onRender : function(ctr,pos) {
36458 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36459 if(!this.config.split){
36464 var splitEl = Roo.DomHelper.append(ctr.dom, {
36466 id: this.el.id + "-split",
36467 cls: "roo-layout-split roo-layout-split-"+this.position,
36470 /** The SplitBar for this region
36471 * @type Roo.SplitBar */
36472 // does not exist yet...
36473 Roo.log([this.position, this.orientation]);
36475 this.split = new Roo.bootstrap.SplitBar({
36476 dragElement : splitEl,
36477 resizingElement: this.el,
36478 orientation : this.orientation
36481 this.split.on("moved", this.onSplitMove, this);
36482 this.split.useShim = this.config.useShim === true;
36483 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36484 if(this.useSplitTips){
36485 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36487 //if(config.collapsible){
36488 // this.split.el.on("dblclick", this.collapse, this);
36491 if(typeof this.config.minSize != "undefined"){
36492 this.split.minSize = this.config.minSize;
36494 if(typeof this.config.maxSize != "undefined"){
36495 this.split.maxSize = this.config.maxSize;
36497 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36498 this.hideSplitter();
36503 getHMaxSize : function(){
36504 var cmax = this.config.maxSize || 10000;
36505 var center = this.mgr.getRegion("center");
36506 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36509 getVMaxSize : function(){
36510 var cmax = this.config.maxSize || 10000;
36511 var center = this.mgr.getRegion("center");
36512 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36515 onSplitMove : function(split, newSize){
36516 this.fireEvent("resized", this, newSize);
36520 * Returns the {@link Roo.SplitBar} for this region.
36521 * @return {Roo.SplitBar}
36523 getSplitBar : function(){
36528 this.hideSplitter();
36529 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36532 hideSplitter : function(){
36534 this.split.el.setLocation(-2000,-2000);
36535 this.split.el.hide();
36541 this.split.el.show();
36543 Roo.bootstrap.layout.Split.superclass.show.call(this);
36546 beforeSlide: function(){
36547 if(Roo.isGecko){// firefox overflow auto bug workaround
36548 this.bodyEl.clip();
36550 this.tabs.bodyEl.clip();
36552 if(this.activePanel){
36553 this.activePanel.getEl().clip();
36555 if(this.activePanel.beforeSlide){
36556 this.activePanel.beforeSlide();
36562 afterSlide : function(){
36563 if(Roo.isGecko){// firefox overflow auto bug workaround
36564 this.bodyEl.unclip();
36566 this.tabs.bodyEl.unclip();
36568 if(this.activePanel){
36569 this.activePanel.getEl().unclip();
36570 if(this.activePanel.afterSlide){
36571 this.activePanel.afterSlide();
36577 initAutoHide : function(){
36578 if(this.autoHide !== false){
36579 if(!this.autoHideHd){
36580 var st = new Roo.util.DelayedTask(this.slideIn, this);
36581 this.autoHideHd = {
36582 "mouseout": function(e){
36583 if(!e.within(this.el, true)){
36587 "mouseover" : function(e){
36593 this.el.on(this.autoHideHd);
36597 clearAutoHide : function(){
36598 if(this.autoHide !== false){
36599 this.el.un("mouseout", this.autoHideHd.mouseout);
36600 this.el.un("mouseover", this.autoHideHd.mouseover);
36604 clearMonitor : function(){
36605 Roo.get(document).un("click", this.slideInIf, this);
36608 // these names are backwards but not changed for compat
36609 slideOut : function(){
36610 if(this.isSlid || this.el.hasActiveFx()){
36613 this.isSlid = true;
36614 if(this.collapseBtn){
36615 this.collapseBtn.hide();
36617 this.closeBtnState = this.closeBtn.getStyle('display');
36618 this.closeBtn.hide();
36620 this.stickBtn.show();
36623 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36624 this.beforeSlide();
36625 this.el.setStyle("z-index", 10001);
36626 this.el.slideIn(this.getSlideAnchor(), {
36627 callback: function(){
36629 this.initAutoHide();
36630 Roo.get(document).on("click", this.slideInIf, this);
36631 this.fireEvent("slideshow", this);
36638 afterSlideIn : function(){
36639 this.clearAutoHide();
36640 this.isSlid = false;
36641 this.clearMonitor();
36642 this.el.setStyle("z-index", "");
36643 if(this.collapseBtn){
36644 this.collapseBtn.show();
36646 this.closeBtn.setStyle('display', this.closeBtnState);
36648 this.stickBtn.hide();
36650 this.fireEvent("slidehide", this);
36653 slideIn : function(cb){
36654 if(!this.isSlid || this.el.hasActiveFx()){
36658 this.isSlid = false;
36659 this.beforeSlide();
36660 this.el.slideOut(this.getSlideAnchor(), {
36661 callback: function(){
36662 this.el.setLeftTop(-10000, -10000);
36664 this.afterSlideIn();
36672 slideInIf : function(e){
36673 if(!e.within(this.el)){
36678 animateCollapse : function(){
36679 this.beforeSlide();
36680 this.el.setStyle("z-index", 20000);
36681 var anchor = this.getSlideAnchor();
36682 this.el.slideOut(anchor, {
36683 callback : function(){
36684 this.el.setStyle("z-index", "");
36685 this.collapsedEl.slideIn(anchor, {duration:.3});
36687 this.el.setLocation(-10000,-10000);
36689 this.fireEvent("collapsed", this);
36696 animateExpand : function(){
36697 this.beforeSlide();
36698 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36699 this.el.setStyle("z-index", 20000);
36700 this.collapsedEl.hide({
36703 this.el.slideIn(this.getSlideAnchor(), {
36704 callback : function(){
36705 this.el.setStyle("z-index", "");
36708 this.split.el.show();
36710 this.fireEvent("invalidated", this);
36711 this.fireEvent("expanded", this);
36739 getAnchor : function(){
36740 return this.anchors[this.position];
36743 getCollapseAnchor : function(){
36744 return this.canchors[this.position];
36747 getSlideAnchor : function(){
36748 return this.sanchors[this.position];
36751 getAlignAdj : function(){
36752 var cm = this.cmargins;
36753 switch(this.position){
36769 getExpandAdj : function(){
36770 var c = this.collapsedEl, cm = this.cmargins;
36771 switch(this.position){
36773 return [-(cm.right+c.getWidth()+cm.left), 0];
36776 return [cm.right+c.getWidth()+cm.left, 0];
36779 return [0, -(cm.top+cm.bottom+c.getHeight())];
36782 return [0, cm.top+cm.bottom+c.getHeight()];
36788 * Ext JS Library 1.1.1
36789 * Copyright(c) 2006-2007, Ext JS, LLC.
36791 * Originally Released Under LGPL - original licence link has changed is not relivant.
36794 * <script type="text/javascript">
36797 * These classes are private internal classes
36799 Roo.bootstrap.layout.Center = function(config){
36800 config.region = "center";
36801 Roo.bootstrap.layout.Region.call(this, config);
36802 this.visible = true;
36803 this.minWidth = config.minWidth || 20;
36804 this.minHeight = config.minHeight || 20;
36807 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36809 // center panel can't be hidden
36813 // center panel can't be hidden
36816 getMinWidth: function(){
36817 return this.minWidth;
36820 getMinHeight: function(){
36821 return this.minHeight;
36834 Roo.bootstrap.layout.North = function(config)
36836 config.region = 'north';
36837 config.cursor = 'n-resize';
36839 Roo.bootstrap.layout.Split.call(this, config);
36843 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36844 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36845 this.split.el.addClass("roo-layout-split-v");
36847 var size = config.initialSize || config.height;
36848 if(typeof size != "undefined"){
36849 this.el.setHeight(size);
36852 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36854 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36858 getBox : function(){
36859 if(this.collapsed){
36860 return this.collapsedEl.getBox();
36862 var box = this.el.getBox();
36864 box.height += this.split.el.getHeight();
36869 updateBox : function(box){
36870 if(this.split && !this.collapsed){
36871 box.height -= this.split.el.getHeight();
36872 this.split.el.setLeft(box.x);
36873 this.split.el.setTop(box.y+box.height);
36874 this.split.el.setWidth(box.width);
36876 if(this.collapsed){
36877 this.updateBody(box.width, null);
36879 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36887 Roo.bootstrap.layout.South = function(config){
36888 config.region = 'south';
36889 config.cursor = 's-resize';
36890 Roo.bootstrap.layout.Split.call(this, config);
36892 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36893 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36894 this.split.el.addClass("roo-layout-split-v");
36896 var size = config.initialSize || config.height;
36897 if(typeof size != "undefined"){
36898 this.el.setHeight(size);
36902 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36903 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36904 getBox : function(){
36905 if(this.collapsed){
36906 return this.collapsedEl.getBox();
36908 var box = this.el.getBox();
36910 var sh = this.split.el.getHeight();
36917 updateBox : function(box){
36918 if(this.split && !this.collapsed){
36919 var sh = this.split.el.getHeight();
36922 this.split.el.setLeft(box.x);
36923 this.split.el.setTop(box.y-sh);
36924 this.split.el.setWidth(box.width);
36926 if(this.collapsed){
36927 this.updateBody(box.width, null);
36929 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36933 Roo.bootstrap.layout.East = function(config){
36934 config.region = "east";
36935 config.cursor = "e-resize";
36936 Roo.bootstrap.layout.Split.call(this, config);
36938 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36939 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36940 this.split.el.addClass("roo-layout-split-h");
36942 var size = config.initialSize || config.width;
36943 if(typeof size != "undefined"){
36944 this.el.setWidth(size);
36947 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36948 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36949 getBox : function(){
36950 if(this.collapsed){
36951 return this.collapsedEl.getBox();
36953 var box = this.el.getBox();
36955 var sw = this.split.el.getWidth();
36962 updateBox : function(box){
36963 if(this.split && !this.collapsed){
36964 var sw = this.split.el.getWidth();
36966 this.split.el.setLeft(box.x);
36967 this.split.el.setTop(box.y);
36968 this.split.el.setHeight(box.height);
36971 if(this.collapsed){
36972 this.updateBody(null, box.height);
36974 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36978 Roo.bootstrap.layout.West = function(config){
36979 config.region = "west";
36980 config.cursor = "w-resize";
36982 Roo.bootstrap.layout.Split.call(this, config);
36984 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36985 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36986 this.split.el.addClass("roo-layout-split-h");
36990 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36991 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36993 onRender: function(ctr, pos)
36995 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36996 var size = this.config.initialSize || this.config.width;
36997 if(typeof size != "undefined"){
36998 this.el.setWidth(size);
37002 getBox : function(){
37003 if(this.collapsed){
37004 return this.collapsedEl.getBox();
37006 var box = this.el.getBox();
37008 box.width += this.split.el.getWidth();
37013 updateBox : function(box){
37014 if(this.split && !this.collapsed){
37015 var sw = this.split.el.getWidth();
37017 this.split.el.setLeft(box.x+box.width);
37018 this.split.el.setTop(box.y);
37019 this.split.el.setHeight(box.height);
37021 if(this.collapsed){
37022 this.updateBody(null, box.height);
37024 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37027 Roo.namespace("Roo.bootstrap.panel");/*
37029 * Ext JS Library 1.1.1
37030 * Copyright(c) 2006-2007, Ext JS, LLC.
37032 * Originally Released Under LGPL - original licence link has changed is not relivant.
37035 * <script type="text/javascript">
37038 * @class Roo.ContentPanel
37039 * @extends Roo.util.Observable
37040 * A basic ContentPanel element.
37041 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
37042 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
37043 * @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
37044 * @cfg {Boolean} closable True if the panel can be closed/removed
37045 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
37046 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37047 * @cfg {Toolbar} toolbar A toolbar for this panel
37048 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37049 * @cfg {String} title The title for this panel
37050 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37051 * @cfg {String} url Calls {@link #setUrl} with this value
37052 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37053 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37054 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37055 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37056 * @cfg {Boolean} badges render the badges
37059 * Create a new ContentPanel.
37060 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37061 * @param {String/Object} config A string to set only the title or a config object
37062 * @param {String} content (optional) Set the HTML content for this panel
37063 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37065 Roo.bootstrap.panel.Content = function( config){
37067 this.tpl = config.tpl || false;
37069 var el = config.el;
37070 var content = config.content;
37072 if(config.autoCreate){ // xtype is available if this is called from factory
37075 this.el = Roo.get(el);
37076 if(!this.el && config && config.autoCreate){
37077 if(typeof config.autoCreate == "object"){
37078 if(!config.autoCreate.id){
37079 config.autoCreate.id = config.id||el;
37081 this.el = Roo.DomHelper.append(document.body,
37082 config.autoCreate, true);
37084 var elcfg = { tag: "div",
37085 cls: "roo-layout-inactive-content",
37089 elcfg.html = config.html;
37093 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37096 this.closable = false;
37097 this.loaded = false;
37098 this.active = false;
37101 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37103 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37105 this.wrapEl = this.el; //this.el.wrap();
37107 if (config.toolbar.items) {
37108 ti = config.toolbar.items ;
37109 delete config.toolbar.items ;
37113 this.toolbar.render(this.wrapEl, 'before');
37114 for(var i =0;i < ti.length;i++) {
37115 // Roo.log(['add child', items[i]]);
37116 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37118 this.toolbar.items = nitems;
37119 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37120 delete config.toolbar;
37124 // xtype created footer. - not sure if will work as we normally have to render first..
37125 if (this.footer && !this.footer.el && this.footer.xtype) {
37126 if (!this.wrapEl) {
37127 this.wrapEl = this.el.wrap();
37130 this.footer.container = this.wrapEl.createChild();
37132 this.footer = Roo.factory(this.footer, Roo);
37137 if(typeof config == "string"){
37138 this.title = config;
37140 Roo.apply(this, config);
37144 this.resizeEl = Roo.get(this.resizeEl, true);
37146 this.resizeEl = this.el;
37148 // handle view.xtype
37156 * Fires when this panel is activated.
37157 * @param {Roo.ContentPanel} this
37161 * @event deactivate
37162 * Fires when this panel is activated.
37163 * @param {Roo.ContentPanel} this
37165 "deactivate" : true,
37169 * Fires when this panel is resized if fitToFrame is true.
37170 * @param {Roo.ContentPanel} this
37171 * @param {Number} width The width after any component adjustments
37172 * @param {Number} height The height after any component adjustments
37178 * Fires when this tab is created
37179 * @param {Roo.ContentPanel} this
37190 if(this.autoScroll){
37191 this.resizeEl.setStyle("overflow", "auto");
37193 // fix randome scrolling
37194 //this.el.on('scroll', function() {
37195 // Roo.log('fix random scolling');
37196 // this.scrollTo('top',0);
37199 content = content || this.content;
37201 this.setContent(content);
37203 if(config && config.url){
37204 this.setUrl(this.url, this.params, this.loadOnce);
37209 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37211 if (this.view && typeof(this.view.xtype) != 'undefined') {
37212 this.view.el = this.el.appendChild(document.createElement("div"));
37213 this.view = Roo.factory(this.view);
37214 this.view.render && this.view.render(false, '');
37218 this.fireEvent('render', this);
37221 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37225 setRegion : function(region){
37226 this.region = region;
37227 this.setActiveClass(region && !this.background);
37231 setActiveClass: function(state)
37234 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37235 this.el.setStyle('position','relative');
37237 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37238 this.el.setStyle('position', 'absolute');
37243 * Returns the toolbar for this Panel if one was configured.
37244 * @return {Roo.Toolbar}
37246 getToolbar : function(){
37247 return this.toolbar;
37250 setActiveState : function(active)
37252 this.active = active;
37253 this.setActiveClass(active);
37255 if(this.fireEvent("deactivate", this) === false){
37260 this.fireEvent("activate", this);
37264 * Updates this panel's element
37265 * @param {String} content The new content
37266 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37268 setContent : function(content, loadScripts){
37269 this.el.update(content, loadScripts);
37272 ignoreResize : function(w, h){
37273 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37276 this.lastSize = {width: w, height: h};
37281 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37282 * @return {Roo.UpdateManager} The UpdateManager
37284 getUpdateManager : function(){
37285 return this.el.getUpdateManager();
37288 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37289 * @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:
37292 url: "your-url.php",
37293 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37294 callback: yourFunction,
37295 scope: yourObject, //(optional scope)
37298 text: "Loading...",
37303 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37304 * 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.
37305 * @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}
37306 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37307 * @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.
37308 * @return {Roo.ContentPanel} this
37311 var um = this.el.getUpdateManager();
37312 um.update.apply(um, arguments);
37318 * 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.
37319 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37320 * @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)
37321 * @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)
37322 * @return {Roo.UpdateManager} The UpdateManager
37324 setUrl : function(url, params, loadOnce){
37325 if(this.refreshDelegate){
37326 this.removeListener("activate", this.refreshDelegate);
37328 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37329 this.on("activate", this.refreshDelegate);
37330 return this.el.getUpdateManager();
37333 _handleRefresh : function(url, params, loadOnce){
37334 if(!loadOnce || !this.loaded){
37335 var updater = this.el.getUpdateManager();
37336 updater.update(url, params, this._setLoaded.createDelegate(this));
37340 _setLoaded : function(){
37341 this.loaded = true;
37345 * Returns this panel's id
37348 getId : function(){
37353 * Returns this panel's element - used by regiosn to add.
37354 * @return {Roo.Element}
37356 getEl : function(){
37357 return this.wrapEl || this.el;
37362 adjustForComponents : function(width, height)
37364 //Roo.log('adjustForComponents ');
37365 if(this.resizeEl != this.el){
37366 width -= this.el.getFrameWidth('lr');
37367 height -= this.el.getFrameWidth('tb');
37370 var te = this.toolbar.getEl();
37371 te.setWidth(width);
37372 height -= te.getHeight();
37375 var te = this.footer.getEl();
37376 te.setWidth(width);
37377 height -= te.getHeight();
37381 if(this.adjustments){
37382 width += this.adjustments[0];
37383 height += this.adjustments[1];
37385 return {"width": width, "height": height};
37388 setSize : function(width, height){
37389 if(this.fitToFrame && !this.ignoreResize(width, height)){
37390 if(this.fitContainer && this.resizeEl != this.el){
37391 this.el.setSize(width, height);
37393 var size = this.adjustForComponents(width, height);
37394 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37395 this.fireEvent('resize', this, size.width, size.height);
37400 * Returns this panel's title
37403 getTitle : function(){
37405 if (typeof(this.title) != 'object') {
37410 for (var k in this.title) {
37411 if (!this.title.hasOwnProperty(k)) {
37415 if (k.indexOf('-') >= 0) {
37416 var s = k.split('-');
37417 for (var i = 0; i<s.length; i++) {
37418 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37421 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37428 * Set this panel's title
37429 * @param {String} title
37431 setTitle : function(title){
37432 this.title = title;
37434 this.region.updatePanelTitle(this, title);
37439 * Returns true is this panel was configured to be closable
37440 * @return {Boolean}
37442 isClosable : function(){
37443 return this.closable;
37446 beforeSlide : function(){
37448 this.resizeEl.clip();
37451 afterSlide : function(){
37453 this.resizeEl.unclip();
37457 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37458 * Will fail silently if the {@link #setUrl} method has not been called.
37459 * This does not activate the panel, just updates its content.
37461 refresh : function(){
37462 if(this.refreshDelegate){
37463 this.loaded = false;
37464 this.refreshDelegate();
37469 * Destroys this panel
37471 destroy : function(){
37472 this.el.removeAllListeners();
37473 var tempEl = document.createElement("span");
37474 tempEl.appendChild(this.el.dom);
37475 tempEl.innerHTML = "";
37481 * form - if the content panel contains a form - this is a reference to it.
37482 * @type {Roo.form.Form}
37486 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37487 * This contains a reference to it.
37493 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37503 * @param {Object} cfg Xtype definition of item to add.
37507 getChildContainer: function () {
37508 return this.getEl();
37513 var ret = new Roo.factory(cfg);
37518 if (cfg.xtype.match(/^Form$/)) {
37521 //if (this.footer) {
37522 // el = this.footer.container.insertSibling(false, 'before');
37524 el = this.el.createChild();
37527 this.form = new Roo.form.Form(cfg);
37530 if ( this.form.allItems.length) {
37531 this.form.render(el.dom);
37535 // should only have one of theses..
37536 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37537 // views.. should not be just added - used named prop 'view''
37539 cfg.el = this.el.appendChild(document.createElement("div"));
37542 var ret = new Roo.factory(cfg);
37544 ret.render && ret.render(false, ''); // render blank..
37554 * @class Roo.bootstrap.panel.Grid
37555 * @extends Roo.bootstrap.panel.Content
37557 * Create a new GridPanel.
37558 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37559 * @param {Object} config A the config object
37565 Roo.bootstrap.panel.Grid = function(config)
37569 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37570 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37572 config.el = this.wrapper;
37573 //this.el = this.wrapper;
37575 if (config.container) {
37576 // ctor'ed from a Border/panel.grid
37579 this.wrapper.setStyle("overflow", "hidden");
37580 this.wrapper.addClass('roo-grid-container');
37585 if(config.toolbar){
37586 var tool_el = this.wrapper.createChild();
37587 this.toolbar = Roo.factory(config.toolbar);
37589 if (config.toolbar.items) {
37590 ti = config.toolbar.items ;
37591 delete config.toolbar.items ;
37595 this.toolbar.render(tool_el);
37596 for(var i =0;i < ti.length;i++) {
37597 // Roo.log(['add child', items[i]]);
37598 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37600 this.toolbar.items = nitems;
37602 delete config.toolbar;
37605 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37606 config.grid.scrollBody = true;;
37607 config.grid.monitorWindowResize = false; // turn off autosizing
37608 config.grid.autoHeight = false;
37609 config.grid.autoWidth = false;
37611 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37613 if (config.background) {
37614 // render grid on panel activation (if panel background)
37615 this.on('activate', function(gp) {
37616 if (!gp.grid.rendered) {
37617 gp.grid.render(this.wrapper);
37618 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37623 this.grid.render(this.wrapper);
37624 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37627 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37628 // ??? needed ??? config.el = this.wrapper;
37633 // xtype created footer. - not sure if will work as we normally have to render first..
37634 if (this.footer && !this.footer.el && this.footer.xtype) {
37636 var ctr = this.grid.getView().getFooterPanel(true);
37637 this.footer.dataSource = this.grid.dataSource;
37638 this.footer = Roo.factory(this.footer, Roo);
37639 this.footer.render(ctr);
37649 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37650 getId : function(){
37651 return this.grid.id;
37655 * Returns the grid for this panel
37656 * @return {Roo.bootstrap.Table}
37658 getGrid : function(){
37662 setSize : function(width, height){
37663 if(!this.ignoreResize(width, height)){
37664 var grid = this.grid;
37665 var size = this.adjustForComponents(width, height);
37666 var gridel = grid.getGridEl();
37667 gridel.setSize(size.width, size.height);
37669 var thd = grid.getGridEl().select('thead',true).first();
37670 var tbd = grid.getGridEl().select('tbody', true).first();
37672 tbd.setSize(width, height - thd.getHeight());
37681 beforeSlide : function(){
37682 this.grid.getView().scroller.clip();
37685 afterSlide : function(){
37686 this.grid.getView().scroller.unclip();
37689 destroy : function(){
37690 this.grid.destroy();
37692 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37697 * @class Roo.bootstrap.panel.Nest
37698 * @extends Roo.bootstrap.panel.Content
37700 * Create a new Panel, that can contain a layout.Border.
37703 * @param {Roo.BorderLayout} layout The layout for this panel
37704 * @param {String/Object} config A string to set only the title or a config object
37706 Roo.bootstrap.panel.Nest = function(config)
37708 // construct with only one argument..
37709 /* FIXME - implement nicer consturctors
37710 if (layout.layout) {
37712 layout = config.layout;
37713 delete config.layout;
37715 if (layout.xtype && !layout.getEl) {
37716 // then layout needs constructing..
37717 layout = Roo.factory(layout, Roo);
37721 config.el = config.layout.getEl();
37723 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37725 config.layout.monitorWindowResize = false; // turn off autosizing
37726 this.layout = config.layout;
37727 this.layout.getEl().addClass("roo-layout-nested-layout");
37734 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37736 setSize : function(width, height){
37737 if(!this.ignoreResize(width, height)){
37738 var size = this.adjustForComponents(width, height);
37739 var el = this.layout.getEl();
37740 if (size.height < 1) {
37741 el.setWidth(size.width);
37743 el.setSize(size.width, size.height);
37745 var touch = el.dom.offsetWidth;
37746 this.layout.layout();
37747 // ie requires a double layout on the first pass
37748 if(Roo.isIE && !this.initialized){
37749 this.initialized = true;
37750 this.layout.layout();
37755 // activate all subpanels if not currently active..
37757 setActiveState : function(active){
37758 this.active = active;
37759 this.setActiveClass(active);
37762 this.fireEvent("deactivate", this);
37766 this.fireEvent("activate", this);
37767 // not sure if this should happen before or after..
37768 if (!this.layout) {
37769 return; // should not happen..
37772 for (var r in this.layout.regions) {
37773 reg = this.layout.getRegion(r);
37774 if (reg.getActivePanel()) {
37775 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37776 reg.setActivePanel(reg.getActivePanel());
37779 if (!reg.panels.length) {
37782 reg.showPanel(reg.getPanel(0));
37791 * Returns the nested BorderLayout for this panel
37792 * @return {Roo.BorderLayout}
37794 getLayout : function(){
37795 return this.layout;
37799 * Adds a xtype elements to the layout of the nested panel
37803 xtype : 'ContentPanel',
37810 xtype : 'NestedLayoutPanel',
37816 items : [ ... list of content panels or nested layout panels.. ]
37820 * @param {Object} cfg Xtype definition of item to add.
37822 addxtype : function(cfg) {
37823 return this.layout.addxtype(cfg);
37828 * Ext JS Library 1.1.1
37829 * Copyright(c) 2006-2007, Ext JS, LLC.
37831 * Originally Released Under LGPL - original licence link has changed is not relivant.
37834 * <script type="text/javascript">
37837 * @class Roo.TabPanel
37838 * @extends Roo.util.Observable
37839 * A lightweight tab container.
37843 // basic tabs 1, built from existing content
37844 var tabs = new Roo.TabPanel("tabs1");
37845 tabs.addTab("script", "View Script");
37846 tabs.addTab("markup", "View Markup");
37847 tabs.activate("script");
37849 // more advanced tabs, built from javascript
37850 var jtabs = new Roo.TabPanel("jtabs");
37851 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37853 // set up the UpdateManager
37854 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37855 var updater = tab2.getUpdateManager();
37856 updater.setDefaultUrl("ajax1.htm");
37857 tab2.on('activate', updater.refresh, updater, true);
37859 // Use setUrl for Ajax loading
37860 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37861 tab3.setUrl("ajax2.htm", null, true);
37864 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37867 jtabs.activate("jtabs-1");
37870 * Create a new TabPanel.
37871 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37872 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37874 Roo.bootstrap.panel.Tabs = function(config){
37876 * The container element for this TabPanel.
37877 * @type Roo.Element
37879 this.el = Roo.get(config.el);
37882 if(typeof config == "boolean"){
37883 this.tabPosition = config ? "bottom" : "top";
37885 Roo.apply(this, config);
37889 if(this.tabPosition == "bottom"){
37890 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37891 this.el.addClass("roo-tabs-bottom");
37893 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37894 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37895 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37897 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37899 if(this.tabPosition != "bottom"){
37900 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37901 * @type Roo.Element
37903 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37904 this.el.addClass("roo-tabs-top");
37908 this.bodyEl.setStyle("position", "relative");
37910 this.active = null;
37911 this.activateDelegate = this.activate.createDelegate(this);
37916 * Fires when the active tab changes
37917 * @param {Roo.TabPanel} this
37918 * @param {Roo.TabPanelItem} activePanel The new active tab
37922 * @event beforetabchange
37923 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37924 * @param {Roo.TabPanel} this
37925 * @param {Object} e Set cancel to true on this object to cancel the tab change
37926 * @param {Roo.TabPanelItem} tab The tab being changed to
37928 "beforetabchange" : true
37931 Roo.EventManager.onWindowResize(this.onResize, this);
37932 this.cpad = this.el.getPadding("lr");
37933 this.hiddenCount = 0;
37936 // toolbar on the tabbar support...
37937 if (this.toolbar) {
37938 alert("no toolbar support yet");
37939 this.toolbar = false;
37941 var tcfg = this.toolbar;
37942 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37943 this.toolbar = new Roo.Toolbar(tcfg);
37944 if (Roo.isSafari) {
37945 var tbl = tcfg.container.child('table', true);
37946 tbl.setAttribute('width', '100%');
37954 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37957 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37959 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37961 tabPosition : "top",
37963 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37965 currentTabWidth : 0,
37967 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37971 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37975 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37977 preferredTabWidth : 175,
37979 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37981 resizeTabs : false,
37983 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37985 monitorResize : true,
37987 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37992 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37993 * @param {String} id The id of the div to use <b>or create</b>
37994 * @param {String} text The text for the tab
37995 * @param {String} content (optional) Content to put in the TabPanelItem body
37996 * @param {Boolean} closable (optional) True to create a close icon on the tab
37997 * @return {Roo.TabPanelItem} The created TabPanelItem
37999 addTab : function(id, text, content, closable, tpl)
38001 var item = new Roo.bootstrap.panel.TabItem({
38005 closable : closable,
38008 this.addTabItem(item);
38010 item.setContent(content);
38016 * Returns the {@link Roo.TabPanelItem} with the specified id/index
38017 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38018 * @return {Roo.TabPanelItem}
38020 getTab : function(id){
38021 return this.items[id];
38025 * Hides the {@link Roo.TabPanelItem} with the specified id/index
38026 * @param {String/Number} id The id or index of the TabPanelItem to hide.
38028 hideTab : function(id){
38029 var t = this.items[id];
38032 this.hiddenCount++;
38033 this.autoSizeTabs();
38038 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38039 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38041 unhideTab : function(id){
38042 var t = this.items[id];
38044 t.setHidden(false);
38045 this.hiddenCount--;
38046 this.autoSizeTabs();
38051 * Adds an existing {@link Roo.TabPanelItem}.
38052 * @param {Roo.TabPanelItem} item The TabPanelItem to add
38054 addTabItem : function(item){
38055 this.items[item.id] = item;
38056 this.items.push(item);
38057 // if(this.resizeTabs){
38058 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38059 // this.autoSizeTabs();
38061 // item.autoSize();
38066 * Removes a {@link Roo.TabPanelItem}.
38067 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38069 removeTab : function(id){
38070 var items = this.items;
38071 var tab = items[id];
38072 if(!tab) { return; }
38073 var index = items.indexOf(tab);
38074 if(this.active == tab && items.length > 1){
38075 var newTab = this.getNextAvailable(index);
38080 this.stripEl.dom.removeChild(tab.pnode.dom);
38081 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38082 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38084 items.splice(index, 1);
38085 delete this.items[tab.id];
38086 tab.fireEvent("close", tab);
38087 tab.purgeListeners();
38088 this.autoSizeTabs();
38091 getNextAvailable : function(start){
38092 var items = this.items;
38094 // look for a next tab that will slide over to
38095 // replace the one being removed
38096 while(index < items.length){
38097 var item = items[++index];
38098 if(item && !item.isHidden()){
38102 // if one isn't found select the previous tab (on the left)
38105 var item = items[--index];
38106 if(item && !item.isHidden()){
38114 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38115 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38117 disableTab : function(id){
38118 var tab = this.items[id];
38119 if(tab && this.active != tab){
38125 * Enables a {@link Roo.TabPanelItem} that is disabled.
38126 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38128 enableTab : function(id){
38129 var tab = this.items[id];
38134 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38135 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38136 * @return {Roo.TabPanelItem} The TabPanelItem.
38138 activate : function(id){
38139 var tab = this.items[id];
38143 if(tab == this.active || tab.disabled){
38147 this.fireEvent("beforetabchange", this, e, tab);
38148 if(e.cancel !== true && !tab.disabled){
38150 this.active.hide();
38152 this.active = this.items[id];
38153 this.active.show();
38154 this.fireEvent("tabchange", this, this.active);
38160 * Gets the active {@link Roo.TabPanelItem}.
38161 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38163 getActiveTab : function(){
38164 return this.active;
38168 * Updates the tab body element to fit the height of the container element
38169 * for overflow scrolling
38170 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38172 syncHeight : function(targetHeight){
38173 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38174 var bm = this.bodyEl.getMargins();
38175 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38176 this.bodyEl.setHeight(newHeight);
38180 onResize : function(){
38181 if(this.monitorResize){
38182 this.autoSizeTabs();
38187 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38189 beginUpdate : function(){
38190 this.updating = true;
38194 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38196 endUpdate : function(){
38197 this.updating = false;
38198 this.autoSizeTabs();
38202 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38204 autoSizeTabs : function(){
38205 var count = this.items.length;
38206 var vcount = count - this.hiddenCount;
38207 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38210 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38211 var availWidth = Math.floor(w / vcount);
38212 var b = this.stripBody;
38213 if(b.getWidth() > w){
38214 var tabs = this.items;
38215 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38216 if(availWidth < this.minTabWidth){
38217 /*if(!this.sleft){ // incomplete scrolling code
38218 this.createScrollButtons();
38221 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38224 if(this.currentTabWidth < this.preferredTabWidth){
38225 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38231 * Returns the number of tabs in this TabPanel.
38234 getCount : function(){
38235 return this.items.length;
38239 * Resizes all the tabs to the passed width
38240 * @param {Number} The new width
38242 setTabWidth : function(width){
38243 this.currentTabWidth = width;
38244 for(var i = 0, len = this.items.length; i < len; i++) {
38245 if(!this.items[i].isHidden()) {
38246 this.items[i].setWidth(width);
38252 * Destroys this TabPanel
38253 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38255 destroy : function(removeEl){
38256 Roo.EventManager.removeResizeListener(this.onResize, this);
38257 for(var i = 0, len = this.items.length; i < len; i++){
38258 this.items[i].purgeListeners();
38260 if(removeEl === true){
38261 this.el.update("");
38266 createStrip : function(container)
38268 var strip = document.createElement("nav");
38269 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38270 container.appendChild(strip);
38274 createStripList : function(strip)
38276 // div wrapper for retard IE
38277 // returns the "tr" element.
38278 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38279 //'<div class="x-tabs-strip-wrap">'+
38280 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38281 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38282 return strip.firstChild; //.firstChild.firstChild.firstChild;
38284 createBody : function(container)
38286 var body = document.createElement("div");
38287 Roo.id(body, "tab-body");
38288 //Roo.fly(body).addClass("x-tabs-body");
38289 Roo.fly(body).addClass("tab-content");
38290 container.appendChild(body);
38293 createItemBody :function(bodyEl, id){
38294 var body = Roo.getDom(id);
38296 body = document.createElement("div");
38299 //Roo.fly(body).addClass("x-tabs-item-body");
38300 Roo.fly(body).addClass("tab-pane");
38301 bodyEl.insertBefore(body, bodyEl.firstChild);
38305 createStripElements : function(stripEl, text, closable, tpl)
38307 var td = document.createElement("li"); // was td..
38310 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38313 stripEl.appendChild(td);
38315 td.className = "x-tabs-closable";
38316 if(!this.closeTpl){
38317 this.closeTpl = new Roo.Template(
38318 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38319 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38320 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38323 var el = this.closeTpl.overwrite(td, {"text": text});
38324 var close = el.getElementsByTagName("div")[0];
38325 var inner = el.getElementsByTagName("em")[0];
38326 return {"el": el, "close": close, "inner": inner};
38329 // not sure what this is..
38330 // if(!this.tabTpl){
38331 //this.tabTpl = new Roo.Template(
38332 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38333 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38335 // this.tabTpl = new Roo.Template(
38336 // '<a href="#">' +
38337 // '<span unselectable="on"' +
38338 // (this.disableTooltips ? '' : ' title="{text}"') +
38339 // ' >{text}</span></a>'
38345 var template = tpl || this.tabTpl || false;
38349 template = new Roo.Template(
38351 '<span unselectable="on"' +
38352 (this.disableTooltips ? '' : ' title="{text}"') +
38353 ' >{text}</span></a>'
38357 switch (typeof(template)) {
38361 template = new Roo.Template(template);
38367 var el = template.overwrite(td, {"text": text});
38369 var inner = el.getElementsByTagName("span")[0];
38371 return {"el": el, "inner": inner};
38379 * @class Roo.TabPanelItem
38380 * @extends Roo.util.Observable
38381 * Represents an individual item (tab plus body) in a TabPanel.
38382 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38383 * @param {String} id The id of this TabPanelItem
38384 * @param {String} text The text for the tab of this TabPanelItem
38385 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38387 Roo.bootstrap.panel.TabItem = function(config){
38389 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38390 * @type Roo.TabPanel
38392 this.tabPanel = config.panel;
38394 * The id for this TabPanelItem
38397 this.id = config.id;
38399 this.disabled = false;
38401 this.text = config.text;
38403 this.loaded = false;
38404 this.closable = config.closable;
38407 * The body element for this TabPanelItem.
38408 * @type Roo.Element
38410 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38411 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38412 this.bodyEl.setStyle("display", "block");
38413 this.bodyEl.setStyle("zoom", "1");
38414 //this.hideAction();
38416 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38418 this.el = Roo.get(els.el);
38419 this.inner = Roo.get(els.inner, true);
38420 this.textEl = Roo.get(this.el.dom.firstChild, true);
38421 this.pnode = Roo.get(els.el.parentNode, true);
38422 // this.el.on("mousedown", this.onTabMouseDown, this);
38423 this.el.on("click", this.onTabClick, this);
38425 if(config.closable){
38426 var c = Roo.get(els.close, true);
38427 c.dom.title = this.closeText;
38428 c.addClassOnOver("close-over");
38429 c.on("click", this.closeClick, this);
38435 * Fires when this tab becomes the active tab.
38436 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38437 * @param {Roo.TabPanelItem} this
38441 * @event beforeclose
38442 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38443 * @param {Roo.TabPanelItem} this
38444 * @param {Object} e Set cancel to true on this object to cancel the close.
38446 "beforeclose": true,
38449 * Fires when this tab is closed.
38450 * @param {Roo.TabPanelItem} this
38454 * @event deactivate
38455 * Fires when this tab is no longer the active tab.
38456 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38457 * @param {Roo.TabPanelItem} this
38459 "deactivate" : true
38461 this.hidden = false;
38463 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38466 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38468 purgeListeners : function(){
38469 Roo.util.Observable.prototype.purgeListeners.call(this);
38470 this.el.removeAllListeners();
38473 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38476 this.pnode.addClass("active");
38479 this.tabPanel.stripWrap.repaint();
38481 this.fireEvent("activate", this.tabPanel, this);
38485 * Returns true if this tab is the active tab.
38486 * @return {Boolean}
38488 isActive : function(){
38489 return this.tabPanel.getActiveTab() == this;
38493 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38496 this.pnode.removeClass("active");
38498 this.fireEvent("deactivate", this.tabPanel, this);
38501 hideAction : function(){
38502 this.bodyEl.hide();
38503 this.bodyEl.setStyle("position", "absolute");
38504 this.bodyEl.setLeft("-20000px");
38505 this.bodyEl.setTop("-20000px");
38508 showAction : function(){
38509 this.bodyEl.setStyle("position", "relative");
38510 this.bodyEl.setTop("");
38511 this.bodyEl.setLeft("");
38512 this.bodyEl.show();
38516 * Set the tooltip for the tab.
38517 * @param {String} tooltip The tab's tooltip
38519 setTooltip : function(text){
38520 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38521 this.textEl.dom.qtip = text;
38522 this.textEl.dom.removeAttribute('title');
38524 this.textEl.dom.title = text;
38528 onTabClick : function(e){
38529 e.preventDefault();
38530 this.tabPanel.activate(this.id);
38533 onTabMouseDown : function(e){
38534 e.preventDefault();
38535 this.tabPanel.activate(this.id);
38538 getWidth : function(){
38539 return this.inner.getWidth();
38542 setWidth : function(width){
38543 var iwidth = width - this.pnode.getPadding("lr");
38544 this.inner.setWidth(iwidth);
38545 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38546 this.pnode.setWidth(width);
38550 * Show or hide the tab
38551 * @param {Boolean} hidden True to hide or false to show.
38553 setHidden : function(hidden){
38554 this.hidden = hidden;
38555 this.pnode.setStyle("display", hidden ? "none" : "");
38559 * Returns true if this tab is "hidden"
38560 * @return {Boolean}
38562 isHidden : function(){
38563 return this.hidden;
38567 * Returns the text for this tab
38570 getText : function(){
38574 autoSize : function(){
38575 //this.el.beginMeasure();
38576 this.textEl.setWidth(1);
38578 * #2804 [new] Tabs in Roojs
38579 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38581 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38582 //this.el.endMeasure();
38586 * Sets the text for the tab (Note: this also sets the tooltip text)
38587 * @param {String} text The tab's text and tooltip
38589 setText : function(text){
38591 this.textEl.update(text);
38592 this.setTooltip(text);
38593 //if(!this.tabPanel.resizeTabs){
38594 // this.autoSize();
38598 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38600 activate : function(){
38601 this.tabPanel.activate(this.id);
38605 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38607 disable : function(){
38608 if(this.tabPanel.active != this){
38609 this.disabled = true;
38610 this.pnode.addClass("disabled");
38615 * Enables this TabPanelItem if it was previously disabled.
38617 enable : function(){
38618 this.disabled = false;
38619 this.pnode.removeClass("disabled");
38623 * Sets the content for this TabPanelItem.
38624 * @param {String} content The content
38625 * @param {Boolean} loadScripts true to look for and load scripts
38627 setContent : function(content, loadScripts){
38628 this.bodyEl.update(content, loadScripts);
38632 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38633 * @return {Roo.UpdateManager} The UpdateManager
38635 getUpdateManager : function(){
38636 return this.bodyEl.getUpdateManager();
38640 * Set a URL to be used to load the content for this TabPanelItem.
38641 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38642 * @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)
38643 * @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)
38644 * @return {Roo.UpdateManager} The UpdateManager
38646 setUrl : function(url, params, loadOnce){
38647 if(this.refreshDelegate){
38648 this.un('activate', this.refreshDelegate);
38650 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38651 this.on("activate", this.refreshDelegate);
38652 return this.bodyEl.getUpdateManager();
38656 _handleRefresh : function(url, params, loadOnce){
38657 if(!loadOnce || !this.loaded){
38658 var updater = this.bodyEl.getUpdateManager();
38659 updater.update(url, params, this._setLoaded.createDelegate(this));
38664 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38665 * Will fail silently if the setUrl method has not been called.
38666 * This does not activate the panel, just updates its content.
38668 refresh : function(){
38669 if(this.refreshDelegate){
38670 this.loaded = false;
38671 this.refreshDelegate();
38676 _setLoaded : function(){
38677 this.loaded = true;
38681 closeClick : function(e){
38684 this.fireEvent("beforeclose", this, o);
38685 if(o.cancel !== true){
38686 this.tabPanel.removeTab(this.id);
38690 * The text displayed in the tooltip for the close icon.
38693 closeText : "Close this tab"
38696 * This script refer to:
38697 * Title: International Telephone Input
38698 * Author: Jack O'Connor
38699 * Code version: v12.1.12
38700 * Availability: https://github.com/jackocnr/intl-tel-input.git
38703 Roo.bootstrap.PhoneInputData = function() {
38706 "Afghanistan (افغانستان)",
38711 "Albania (Shqipëri)",
38716 "Algeria (الجزائر)",
38741 "Antigua and Barbuda",
38751 "Armenia (Հայաստան)",
38767 "Austria (Österreich)",
38772 "Azerbaijan (Azərbaycan)",
38782 "Bahrain (البحرين)",
38787 "Bangladesh (বাংলাদেশ)",
38797 "Belarus (Беларусь)",
38802 "Belgium (België)",
38832 "Bosnia and Herzegovina (Босна и Херцеговина)",
38847 "British Indian Ocean Territory",
38852 "British Virgin Islands",
38862 "Bulgaria (България)",
38872 "Burundi (Uburundi)",
38877 "Cambodia (កម្ពុជា)",
38882 "Cameroon (Cameroun)",
38891 ["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"]
38894 "Cape Verde (Kabu Verdi)",
38899 "Caribbean Netherlands",
38910 "Central African Republic (République centrafricaine)",
38930 "Christmas Island",
38936 "Cocos (Keeling) Islands",
38947 "Comoros (جزر القمر)",
38952 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38957 "Congo (Republic) (Congo-Brazzaville)",
38977 "Croatia (Hrvatska)",
38998 "Czech Republic (Česká republika)",
39003 "Denmark (Danmark)",
39018 "Dominican Republic (República Dominicana)",
39022 ["809", "829", "849"]
39040 "Equatorial Guinea (Guinea Ecuatorial)",
39060 "Falkland Islands (Islas Malvinas)",
39065 "Faroe Islands (Føroyar)",
39086 "French Guiana (Guyane française)",
39091 "French Polynesia (Polynésie française)",
39106 "Georgia (საქართველო)",
39111 "Germany (Deutschland)",
39131 "Greenland (Kalaallit Nunaat)",
39168 "Guinea-Bissau (Guiné Bissau)",
39193 "Hungary (Magyarország)",
39198 "Iceland (Ísland)",
39218 "Iraq (العراق)",
39234 "Israel (ישראל)",
39261 "Jordan (الأردن)",
39266 "Kazakhstan (Казахстан)",
39287 "Kuwait (الكويت)",
39292 "Kyrgyzstan (Кыргызстан)",
39302 "Latvia (Latvija)",
39307 "Lebanon (لبنان)",
39322 "Libya (ليبيا)",
39332 "Lithuania (Lietuva)",
39347 "Macedonia (FYROM) (Македонија)",
39352 "Madagascar (Madagasikara)",
39382 "Marshall Islands",
39392 "Mauritania (موريتانيا)",
39397 "Mauritius (Moris)",
39418 "Moldova (Republica Moldova)",
39428 "Mongolia (Монгол)",
39433 "Montenegro (Crna Gora)",
39443 "Morocco (المغرب)",
39449 "Mozambique (Moçambique)",
39454 "Myanmar (Burma) (မြန်မာ)",
39459 "Namibia (Namibië)",
39474 "Netherlands (Nederland)",
39479 "New Caledonia (Nouvelle-Calédonie)",
39514 "North Korea (조선 민주주의 인민 공화국)",
39519 "Northern Mariana Islands",
39535 "Pakistan (پاکستان)",
39545 "Palestine (فلسطين)",
39555 "Papua New Guinea",
39597 "Réunion (La Réunion)",
39603 "Romania (România)",
39619 "Saint Barthélemy",
39630 "Saint Kitts and Nevis",
39640 "Saint Martin (Saint-Martin (partie française))",
39646 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39651 "Saint Vincent and the Grenadines",
39666 "São Tomé and Príncipe (São Tomé e Príncipe)",
39671 "Saudi Arabia (المملكة العربية السعودية)",
39676 "Senegal (Sénégal)",
39706 "Slovakia (Slovensko)",
39711 "Slovenia (Slovenija)",
39721 "Somalia (Soomaaliya)",
39731 "South Korea (대한민국)",
39736 "South Sudan (جنوب السودان)",
39746 "Sri Lanka (ශ්රී ලංකාව)",
39751 "Sudan (السودان)",
39761 "Svalbard and Jan Mayen",
39772 "Sweden (Sverige)",
39777 "Switzerland (Schweiz)",
39782 "Syria (سوريا)",
39827 "Trinidad and Tobago",
39832 "Tunisia (تونس)",
39837 "Turkey (Türkiye)",
39847 "Turks and Caicos Islands",
39857 "U.S. Virgin Islands",
39867 "Ukraine (Україна)",
39872 "United Arab Emirates (الإمارات العربية المتحدة)",
39894 "Uzbekistan (Oʻzbekiston)",
39904 "Vatican City (Città del Vaticano)",
39915 "Vietnam (Việt Nam)",
39920 "Wallis and Futuna (Wallis-et-Futuna)",
39925 "Western Sahara (الصحراء الغربية)",
39931 "Yemen (اليمن)",
39955 * This script refer to:
39956 * Title: International Telephone Input
39957 * Author: Jack O'Connor
39958 * Code version: v12.1.12
39959 * Availability: https://github.com/jackocnr/intl-tel-input.git
39963 * @class Roo.bootstrap.PhoneInput
39964 * @extends Roo.bootstrap.TriggerField
39965 * An input with International dial-code selection
39967 * @cfg {String} defaultDialCode default '+852'
39968 * @cfg {Array} preferedCountries default []
39971 * Create a new PhoneInput.
39972 * @param {Object} config Configuration options
39975 Roo.bootstrap.PhoneInput = function(config) {
39976 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39979 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39981 listWidth: undefined,
39983 selectedClass: 'active',
39985 invalidClass : "has-warning",
39987 validClass: 'has-success',
39989 allowed: '0123456789',
39994 * @cfg {String} defaultDialCode The default dial code when initializing the input
39996 defaultDialCode: '+852',
39999 * @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
40001 preferedCountries: false,
40003 getAutoCreate : function()
40005 var data = Roo.bootstrap.PhoneInputData();
40006 var align = this.labelAlign || this.parentLabelAlign();
40009 this.allCountries = [];
40010 this.dialCodeMapping = [];
40012 for (var i = 0; i < data.length; i++) {
40014 this.allCountries[i] = {
40018 priority: c[3] || 0,
40019 areaCodes: c[4] || null
40021 this.dialCodeMapping[c[2]] = {
40024 priority: c[3] || 0,
40025 areaCodes: c[4] || null
40037 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40038 maxlength: this.max_length,
40039 cls : 'form-control tel-input',
40040 autocomplete: 'new-password'
40043 var hiddenInput = {
40046 cls: 'hidden-tel-input'
40050 hiddenInput.name = this.name;
40053 if (this.disabled) {
40054 input.disabled = true;
40057 var flag_container = {
40074 cls: this.hasFeedback ? 'has-feedback' : '',
40080 cls: 'dial-code-holder',
40087 cls: 'roo-select2-container input-group',
40094 if (this.fieldLabel.length) {
40097 tooltip: 'This field is required'
40103 cls: 'control-label',
40109 html: this.fieldLabel
40112 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40118 if(this.indicatorpos == 'right') {
40119 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40126 if(align == 'left') {
40134 if(this.labelWidth > 12){
40135 label.style = "width: " + this.labelWidth + 'px';
40137 if(this.labelWidth < 13 && this.labelmd == 0){
40138 this.labelmd = this.labelWidth;
40140 if(this.labellg > 0){
40141 label.cls += ' col-lg-' + this.labellg;
40142 input.cls += ' col-lg-' + (12 - this.labellg);
40144 if(this.labelmd > 0){
40145 label.cls += ' col-md-' + this.labelmd;
40146 container.cls += ' col-md-' + (12 - this.labelmd);
40148 if(this.labelsm > 0){
40149 label.cls += ' col-sm-' + this.labelsm;
40150 container.cls += ' col-sm-' + (12 - this.labelsm);
40152 if(this.labelxs > 0){
40153 label.cls += ' col-xs-' + this.labelxs;
40154 container.cls += ' col-xs-' + (12 - this.labelxs);
40164 var settings = this;
40166 ['xs','sm','md','lg'].map(function(size){
40167 if (settings[size]) {
40168 cfg.cls += ' col-' + size + '-' + settings[size];
40172 this.store = new Roo.data.Store({
40173 proxy : new Roo.data.MemoryProxy({}),
40174 reader : new Roo.data.JsonReader({
40185 'name' : 'dialCode',
40189 'name' : 'priority',
40193 'name' : 'areaCodes',
40200 if(!this.preferedCountries) {
40201 this.preferedCountries = [
40208 var p = this.preferedCountries.reverse();
40211 for (var i = 0; i < p.length; i++) {
40212 for (var j = 0; j < this.allCountries.length; j++) {
40213 if(this.allCountries[j].iso2 == p[i]) {
40214 var t = this.allCountries[j];
40215 this.allCountries.splice(j,1);
40216 this.allCountries.unshift(t);
40222 this.store.proxy.data = {
40224 data: this.allCountries
40230 initEvents : function()
40233 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40235 this.indicator = this.indicatorEl();
40236 this.flag = this.flagEl();
40237 this.dialCodeHolder = this.dialCodeHolderEl();
40239 this.trigger = this.el.select('div.flag-box',true).first();
40240 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40245 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40246 _this.list.setWidth(lw);
40249 this.list.on('mouseover', this.onViewOver, this);
40250 this.list.on('mousemove', this.onViewMove, this);
40251 this.inputEl().on("keyup", this.onKeyUp, this);
40252 this.inputEl().on("keypress", this.onKeyPress, this);
40254 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40256 this.view = new Roo.View(this.list, this.tpl, {
40257 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40260 this.view.on('click', this.onViewClick, this);
40261 this.setValue(this.defaultDialCode);
40264 onTriggerClick : function(e)
40266 Roo.log('trigger click');
40271 if(this.isExpanded()){
40273 this.hasFocus = false;
40275 this.store.load({});
40276 this.hasFocus = true;
40281 isExpanded : function()
40283 return this.list.isVisible();
40286 collapse : function()
40288 if(!this.isExpanded()){
40292 Roo.get(document).un('mousedown', this.collapseIf, this);
40293 Roo.get(document).un('mousewheel', this.collapseIf, this);
40294 this.fireEvent('collapse', this);
40298 expand : function()
40302 if(this.isExpanded() || !this.hasFocus){
40306 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40307 this.list.setWidth(lw);
40310 this.restrictHeight();
40312 Roo.get(document).on('mousedown', this.collapseIf, this);
40313 Roo.get(document).on('mousewheel', this.collapseIf, this);
40315 this.fireEvent('expand', this);
40318 restrictHeight : function()
40320 this.list.alignTo(this.inputEl(), this.listAlign);
40321 this.list.alignTo(this.inputEl(), this.listAlign);
40324 onViewOver : function(e, t)
40326 if(this.inKeyMode){
40329 var item = this.view.findItemFromChild(t);
40332 var index = this.view.indexOf(item);
40333 this.select(index, false);
40338 onViewClick : function(view, doFocus, el, e)
40340 var index = this.view.getSelectedIndexes()[0];
40342 var r = this.store.getAt(index);
40345 this.onSelect(r, index);
40347 if(doFocus !== false && !this.blockFocus){
40348 this.inputEl().focus();
40352 onViewMove : function(e, t)
40354 this.inKeyMode = false;
40357 select : function(index, scrollIntoView)
40359 this.selectedIndex = index;
40360 this.view.select(index);
40361 if(scrollIntoView !== false){
40362 var el = this.view.getNode(index);
40364 this.list.scrollChildIntoView(el, false);
40369 createList : function()
40371 this.list = Roo.get(document.body).createChild({
40373 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40374 style: 'display:none'
40377 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40380 collapseIf : function(e)
40382 var in_combo = e.within(this.el);
40383 var in_list = e.within(this.list);
40384 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40386 if (in_combo || in_list || is_list) {
40392 onSelect : function(record, index)
40394 if(this.fireEvent('beforeselect', this, record, index) !== false){
40396 this.setFlagClass(record.data.iso2);
40397 this.setDialCode(record.data.dialCode);
40398 this.hasFocus = false;
40400 this.fireEvent('select', this, record, index);
40404 flagEl : function()
40406 var flag = this.el.select('div.flag',true).first();
40413 dialCodeHolderEl : function()
40415 var d = this.el.select('input.dial-code-holder',true).first();
40422 setDialCode : function(v)
40424 this.dialCodeHolder.dom.value = '+'+v;
40427 setFlagClass : function(n)
40429 this.flag.dom.className = 'flag '+n;
40432 getValue : function()
40434 var v = this.inputEl().getValue();
40435 if(this.dialCodeHolder) {
40436 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40441 setValue : function(v)
40443 var d = this.getDialCode(v);
40445 //invalid dial code
40446 if(v.length == 0 || !d || d.length == 0) {
40448 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40449 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40455 this.setFlagClass(this.dialCodeMapping[d].iso2);
40456 this.setDialCode(d);
40457 this.inputEl().dom.value = v.replace('+'+d,'');
40458 this.hiddenEl().dom.value = this.getValue();
40463 getDialCode : function(v)
40467 if (v.length == 0) {
40468 return this.dialCodeHolder.dom.value;
40472 if (v.charAt(0) != "+") {
40475 var numericChars = "";
40476 for (var i = 1; i < v.length; i++) {
40477 var c = v.charAt(i);
40480 if (this.dialCodeMapping[numericChars]) {
40481 dialCode = v.substr(1, i);
40483 if (numericChars.length == 4) {
40493 this.setValue(this.defaultDialCode);
40497 hiddenEl : function()
40499 return this.el.select('input.hidden-tel-input',true).first();
40502 // after setting val
40503 onKeyUp : function(e){
40504 this.setValue(this.getValue());
40507 onKeyPress : function(e){
40508 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40515 * @class Roo.bootstrap.MoneyField
40516 * @extends Roo.bootstrap.ComboBox
40517 * Bootstrap MoneyField class
40520 * Create a new MoneyField.
40521 * @param {Object} config Configuration options
40524 Roo.bootstrap.MoneyField = function(config) {
40526 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40530 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40533 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40535 allowDecimals : true,
40537 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40539 decimalSeparator : ".",
40541 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40543 decimalPrecision : 0,
40545 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40547 allowNegative : true,
40549 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40553 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40555 minValue : Number.NEGATIVE_INFINITY,
40557 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40559 maxValue : Number.MAX_VALUE,
40561 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40563 minText : "The minimum value for this field is {0}",
40565 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40567 maxText : "The maximum value for this field is {0}",
40569 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40570 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40572 nanText : "{0} is not a valid number",
40574 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40578 * @cfg {String} defaults currency of the MoneyField
40579 * value should be in lkey
40581 defaultCurrency : false,
40583 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40585 thousandsDelimiter : false,
40587 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40598 getAutoCreate : function()
40600 var align = this.labelAlign || this.parentLabelAlign();
40612 cls : 'form-control roo-money-amount-input',
40613 autocomplete: 'new-password'
40616 var hiddenInput = {
40620 cls: 'hidden-number-input'
40623 if(this.max_length) {
40624 input.maxlength = this.max_length;
40628 hiddenInput.name = this.name;
40631 if (this.disabled) {
40632 input.disabled = true;
40635 var clg = 12 - this.inputlg;
40636 var cmd = 12 - this.inputmd;
40637 var csm = 12 - this.inputsm;
40638 var cxs = 12 - this.inputxs;
40642 cls : 'row roo-money-field',
40646 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40650 cls: 'roo-select2-container input-group',
40654 cls : 'form-control roo-money-currency-input',
40655 autocomplete: 'new-password',
40657 name : this.currencyName
40661 cls : 'input-group-addon',
40675 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40679 cls: this.hasFeedback ? 'has-feedback' : '',
40690 if (this.fieldLabel.length) {
40693 tooltip: 'This field is required'
40699 cls: 'control-label',
40705 html: this.fieldLabel
40708 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40714 if(this.indicatorpos == 'right') {
40715 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40722 if(align == 'left') {
40730 if(this.labelWidth > 12){
40731 label.style = "width: " + this.labelWidth + 'px';
40733 if(this.labelWidth < 13 && this.labelmd == 0){
40734 this.labelmd = this.labelWidth;
40736 if(this.labellg > 0){
40737 label.cls += ' col-lg-' + this.labellg;
40738 input.cls += ' col-lg-' + (12 - this.labellg);
40740 if(this.labelmd > 0){
40741 label.cls += ' col-md-' + this.labelmd;
40742 container.cls += ' col-md-' + (12 - this.labelmd);
40744 if(this.labelsm > 0){
40745 label.cls += ' col-sm-' + this.labelsm;
40746 container.cls += ' col-sm-' + (12 - this.labelsm);
40748 if(this.labelxs > 0){
40749 label.cls += ' col-xs-' + this.labelxs;
40750 container.cls += ' col-xs-' + (12 - this.labelxs);
40761 var settings = this;
40763 ['xs','sm','md','lg'].map(function(size){
40764 if (settings[size]) {
40765 cfg.cls += ' col-' + size + '-' + settings[size];
40772 initEvents : function()
40774 this.indicator = this.indicatorEl();
40776 this.initCurrencyEvent();
40778 this.initNumberEvent();
40781 initCurrencyEvent : function()
40784 throw "can not find store for combo";
40787 this.store = Roo.factory(this.store, Roo.data);
40788 this.store.parent = this;
40792 this.triggerEl = this.el.select('.input-group-addon', true).first();
40794 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40799 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40800 _this.list.setWidth(lw);
40803 this.list.on('mouseover', this.onViewOver, this);
40804 this.list.on('mousemove', this.onViewMove, this);
40805 this.list.on('scroll', this.onViewScroll, this);
40808 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40811 this.view = new Roo.View(this.list, this.tpl, {
40812 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40815 this.view.on('click', this.onViewClick, this);
40817 this.store.on('beforeload', this.onBeforeLoad, this);
40818 this.store.on('load', this.onLoad, this);
40819 this.store.on('loadexception', this.onLoadException, this);
40821 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40822 "up" : function(e){
40823 this.inKeyMode = true;
40827 "down" : function(e){
40828 if(!this.isExpanded()){
40829 this.onTriggerClick();
40831 this.inKeyMode = true;
40836 "enter" : function(e){
40839 if(this.fireEvent("specialkey", this, e)){
40840 this.onViewClick(false);
40846 "esc" : function(e){
40850 "tab" : function(e){
40853 if(this.fireEvent("specialkey", this, e)){
40854 this.onViewClick(false);
40862 doRelay : function(foo, bar, hname){
40863 if(hname == 'down' || this.scope.isExpanded()){
40864 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40872 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40876 initNumberEvent : function(e)
40878 this.inputEl().on("keydown" , this.fireKey, this);
40879 this.inputEl().on("focus", this.onFocus, this);
40880 this.inputEl().on("blur", this.onBlur, this);
40882 this.inputEl().relayEvent('keyup', this);
40884 if(this.indicator){
40885 this.indicator.addClass('invisible');
40888 this.originalValue = this.getValue();
40890 if(this.validationEvent == 'keyup'){
40891 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40892 this.inputEl().on('keyup', this.filterValidation, this);
40894 else if(this.validationEvent !== false){
40895 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40898 if(this.selectOnFocus){
40899 this.on("focus", this.preFocus, this);
40902 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40903 this.inputEl().on("keypress", this.filterKeys, this);
40905 this.inputEl().relayEvent('keypress', this);
40908 var allowed = "0123456789";
40910 if(this.allowDecimals){
40911 allowed += this.decimalSeparator;
40914 if(this.allowNegative){
40918 if(this.thousandsDelimiter) {
40922 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40924 var keyPress = function(e){
40926 var k = e.getKey();
40928 var c = e.getCharCode();
40931 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40932 allowed.indexOf(String.fromCharCode(c)) === -1
40938 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40942 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40947 this.inputEl().on("keypress", keyPress, this);
40951 onTriggerClick : function(e)
40958 this.loadNext = false;
40960 if(this.isExpanded()){
40965 this.hasFocus = true;
40967 if(this.triggerAction == 'all') {
40968 this.doQuery(this.allQuery, true);
40972 this.doQuery(this.getRawValue());
40975 getCurrency : function()
40977 var v = this.currencyEl().getValue();
40982 restrictHeight : function()
40984 this.list.alignTo(this.currencyEl(), this.listAlign);
40985 this.list.alignTo(this.currencyEl(), this.listAlign);
40988 onViewClick : function(view, doFocus, el, e)
40990 var index = this.view.getSelectedIndexes()[0];
40992 var r = this.store.getAt(index);
40995 this.onSelect(r, index);
40999 onSelect : function(record, index){
41001 if(this.fireEvent('beforeselect', this, record, index) !== false){
41003 this.setFromCurrencyData(index > -1 ? record.data : false);
41007 this.fireEvent('select', this, record, index);
41011 setFromCurrencyData : function(o)
41015 this.lastCurrency = o;
41017 if (this.currencyField) {
41018 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41020 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
41023 this.lastSelectionText = currency;
41025 //setting default currency
41026 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41027 this.setCurrency(this.defaultCurrency);
41031 this.setCurrency(currency);
41034 setFromData : function(o)
41038 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41040 this.setFromCurrencyData(c);
41045 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41047 Roo.log('no value set for '+ (this.name ? this.name : this.id));
41050 this.setValue(value);
41054 setCurrency : function(v)
41056 this.currencyValue = v;
41059 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41064 setValue : function(v)
41066 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41072 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41074 this.inputEl().dom.value = (v == '') ? '' :
41075 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41077 if(!this.allowZero && v === '0') {
41078 this.hiddenEl().dom.value = '';
41079 this.inputEl().dom.value = '';
41086 getRawValue : function()
41088 var v = this.inputEl().getValue();
41093 getValue : function()
41095 return this.fixPrecision(this.parseValue(this.getRawValue()));
41098 parseValue : function(value)
41100 if(this.thousandsDelimiter) {
41102 r = new RegExp(",", "g");
41103 value = value.replace(r, "");
41106 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41107 return isNaN(value) ? '' : value;
41111 fixPrecision : function(value)
41113 if(this.thousandsDelimiter) {
41115 r = new RegExp(",", "g");
41116 value = value.replace(r, "");
41119 var nan = isNaN(value);
41121 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41122 return nan ? '' : value;
41124 return parseFloat(value).toFixed(this.decimalPrecision);
41127 decimalPrecisionFcn : function(v)
41129 return Math.floor(v);
41132 validateValue : function(value)
41134 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41138 var num = this.parseValue(value);
41141 this.markInvalid(String.format(this.nanText, value));
41145 if(num < this.minValue){
41146 this.markInvalid(String.format(this.minText, this.minValue));
41150 if(num > this.maxValue){
41151 this.markInvalid(String.format(this.maxText, this.maxValue));
41158 validate : function()
41160 if(this.disabled || this.allowBlank){
41165 var currency = this.getCurrency();
41167 if(this.validateValue(this.getRawValue()) && currency.length){
41172 this.markInvalid();
41176 getName: function()
41181 beforeBlur : function()
41187 var v = this.parseValue(this.getRawValue());
41194 onBlur : function()
41198 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41199 //this.el.removeClass(this.focusClass);
41202 this.hasFocus = false;
41204 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41208 var v = this.getValue();
41210 if(String(v) !== String(this.startValue)){
41211 this.fireEvent('change', this, v, this.startValue);
41214 this.fireEvent("blur", this);
41217 inputEl : function()
41219 return this.el.select('.roo-money-amount-input', true).first();
41222 currencyEl : function()
41224 return this.el.select('.roo-money-currency-input', true).first();
41227 hiddenEl : function()
41229 return this.el.select('input.hidden-number-input',true).first();