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');
3870 ce.setHeight(ce.getHeight()); // resize it ...
3871 ce.removeClass('collapsing');
3872 ce.addClass('collapsing');
3875 // now flag it as moving..
3878 (function() { ce.removeClass('collapsing'); }).defer(100);
3880 ce.addClass('collapsing');
3881 ce.removeClass('show');
3883 ce.removeClass('collapsing');
3884 ce.addClass('collapse');
3897 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3899 var size = this.el.getSize();
3900 this.maskEl.setSize(size.width, size.height);
3901 this.maskEl.enableDisplayMode("block");
3910 getChildContainer : function()
3912 if (this.el.select('.collapse').getCount()) {
3913 return this.el.select('.collapse',true).first();
3946 * @class Roo.bootstrap.NavSimplebar
3947 * @extends Roo.bootstrap.Navbar
3948 * Bootstrap Sidebar class
3950 * @cfg {Boolean} inverse is inverted color
3952 * @cfg {String} type (nav | pills | tabs)
3953 * @cfg {Boolean} arrangement stacked | justified
3954 * @cfg {String} align (left | right) alignment
3956 * @cfg {Boolean} main (true|false) main nav bar? default false
3957 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3959 * @cfg {String} tag (header|footer|nav|div) default is nav
3961 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3965 * Create a new Sidebar
3966 * @param {Object} config The config object
3970 Roo.bootstrap.NavSimplebar = function(config){
3971 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3974 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3990 getAutoCreate : function(){
3994 tag : this.tag || 'div',
3995 cls : 'navbar navbar-expand-lg'
3997 if (['light','white'].indexOf(this.weight) > -1) {
3998 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4000 cfg.cls += ' bg-' + this.weight;
4012 this.type = this.type || 'nav';
4013 if (['tabs','pills'].indexOf(this.type)!==-1) {
4014 cfg.cn[0].cls += ' nav-' + this.type
4018 if (this.type!=='nav') {
4019 Roo.log('nav type must be nav/tabs/pills')
4021 cfg.cn[0].cls += ' navbar-nav'
4027 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4028 cfg.cn[0].cls += ' nav-' + this.arrangement;
4032 if (this.align === 'right') {
4033 cfg.cn[0].cls += ' navbar-right';
4037 cfg.cls += ' navbar-inverse';
4061 * navbar-expand-md fixed-top
4065 * @class Roo.bootstrap.NavHeaderbar
4066 * @extends Roo.bootstrap.NavSimplebar
4067 * Bootstrap Sidebar class
4069 * @cfg {String} brand what is brand
4070 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4071 * @cfg {String} brand_href href of the brand
4072 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
4073 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4074 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4075 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4078 * Create a new Sidebar
4079 * @param {Object} config The config object
4083 Roo.bootstrap.NavHeaderbar = function(config){
4084 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4088 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
4095 desktopCenter : false,
4098 getAutoCreate : function(){
4101 tag: this.nav || 'nav',
4102 cls: 'navbar navbar-expand-md',
4108 if (this.desktopCenter) {
4109 cn.push({cls : 'container', cn : []});
4117 cls: 'navbar-toggle navbar-toggler',
4118 'data-toggle': 'collapse',
4123 html: 'Toggle navigation'
4127 cls: 'icon-bar navbar-toggler-icon'
4140 cn.push( Roo.bootstrap.version == 4 ? btn : {
4142 cls: 'navbar-header',
4151 cls: 'collapse navbar-collapse',
4155 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4157 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4158 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4160 // tag can override this..
4162 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4165 if (this.brand !== '') {
4166 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4167 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4169 href: this.brand_href ? this.brand_href : '#',
4170 cls: 'navbar-brand',
4178 cfg.cls += ' main-nav';
4186 getHeaderChildContainer : function()
4188 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4189 return this.el.select('.navbar-header',true).first();
4192 return this.getChildContainer();
4196 initEvents : function()
4198 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4200 if (this.autohide) {
4205 Roo.get(document).on('scroll',function(e) {
4206 var ns = Roo.get(document).getScroll().top;
4207 var os = prevScroll;
4211 ft.removeClass('slideDown');
4212 ft.addClass('slideUp');
4215 ft.removeClass('slideUp');
4216 ft.addClass('slideDown');
4237 * @class Roo.bootstrap.NavSidebar
4238 * @extends Roo.bootstrap.Navbar
4239 * Bootstrap Sidebar class
4242 * Create a new Sidebar
4243 * @param {Object} config The config object
4247 Roo.bootstrap.NavSidebar = function(config){
4248 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4251 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4253 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4255 getAutoCreate : function(){
4260 cls: 'sidebar sidebar-nav'
4282 * @class Roo.bootstrap.NavGroup
4283 * @extends Roo.bootstrap.Component
4284 * Bootstrap NavGroup class
4285 * @cfg {String} align (left|right)
4286 * @cfg {Boolean} inverse
4287 * @cfg {String} type (nav|pills|tab) default nav
4288 * @cfg {String} navId - reference Id for navbar.
4292 * Create a new nav group
4293 * @param {Object} config The config object
4296 Roo.bootstrap.NavGroup = function(config){
4297 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4300 Roo.bootstrap.NavGroup.register(this);
4304 * Fires when the active item changes
4305 * @param {Roo.bootstrap.NavGroup} this
4306 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4307 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4314 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4325 getAutoCreate : function()
4327 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4334 if (['tabs','pills'].indexOf(this.type)!==-1) {
4335 cfg.cls += ' nav-' + this.type
4337 if (this.type!=='nav') {
4338 Roo.log('nav type must be nav/tabs/pills')
4340 cfg.cls += ' navbar-nav'
4343 if (this.parent() && this.parent().sidebar) {
4346 cls: 'dashboard-menu sidebar-menu'
4352 if (this.form === true) {
4358 if (this.align === 'right') {
4359 cfg.cls += ' navbar-right ml-md-auto';
4361 cfg.cls += ' navbar-left';
4365 if (this.align === 'right') {
4366 cfg.cls += ' navbar-right ml-md-auto';
4368 cfg.cls += ' mr-auto';
4372 cfg.cls += ' navbar-inverse';
4380 * sets the active Navigation item
4381 * @param {Roo.bootstrap.NavItem} the new current navitem
4383 setActiveItem : function(item)
4386 Roo.each(this.navItems, function(v){
4391 v.setActive(false, true);
4398 item.setActive(true, true);
4399 this.fireEvent('changed', this, item, prev);
4404 * gets the active Navigation item
4405 * @return {Roo.bootstrap.NavItem} the current navitem
4407 getActive : function()
4411 Roo.each(this.navItems, function(v){
4422 indexOfNav : function()
4426 Roo.each(this.navItems, function(v,i){
4437 * adds a Navigation item
4438 * @param {Roo.bootstrap.NavItem} the navitem to add
4440 addItem : function(cfg)
4442 var cn = new Roo.bootstrap.NavItem(cfg);
4444 cn.parentId = this.id;
4445 cn.onRender(this.el, null);
4449 * register a Navigation item
4450 * @param {Roo.bootstrap.NavItem} the navitem to add
4452 register : function(item)
4454 this.navItems.push( item);
4455 item.navId = this.navId;
4460 * clear all the Navigation item
4463 clearAll : function()
4466 this.el.dom.innerHTML = '';
4469 getNavItem: function(tabId)
4472 Roo.each(this.navItems, function(e) {
4473 if (e.tabId == tabId) {
4483 setActiveNext : function()
4485 var i = this.indexOfNav(this.getActive());
4486 if (i > this.navItems.length) {
4489 this.setActiveItem(this.navItems[i+1]);
4491 setActivePrev : function()
4493 var i = this.indexOfNav(this.getActive());
4497 this.setActiveItem(this.navItems[i-1]);
4499 clearWasActive : function(except) {
4500 Roo.each(this.navItems, function(e) {
4501 if (e.tabId != except.tabId && e.was_active) {
4502 e.was_active = false;
4509 getWasActive : function ()
4512 Roo.each(this.navItems, function(e) {
4527 Roo.apply(Roo.bootstrap.NavGroup, {
4531 * register a Navigation Group
4532 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4534 register : function(navgrp)
4536 this.groups[navgrp.navId] = navgrp;
4540 * fetch a Navigation Group based on the navigation ID
4541 * @param {string} the navgroup to add
4542 * @returns {Roo.bootstrap.NavGroup} the navgroup
4544 get: function(navId) {
4545 if (typeof(this.groups[navId]) == 'undefined') {
4547 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4549 return this.groups[navId] ;
4564 * @class Roo.bootstrap.NavItem
4565 * @extends Roo.bootstrap.Component
4566 * Bootstrap Navbar.NavItem class
4567 * @cfg {String} href link to
4568 * @cfg {String} html content of button
4569 * @cfg {String} badge text inside badge
4570 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4571 * @cfg {String} glyphicon DEPRICATED - use fa
4572 * @cfg {String} icon DEPRICATED - use fa
4573 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4574 * @cfg {Boolean} active Is item active
4575 * @cfg {Boolean} disabled Is item disabled
4577 * @cfg {Boolean} preventDefault (true | false) default false
4578 * @cfg {String} tabId the tab that this item activates.
4579 * @cfg {String} tagtype (a|span) render as a href or span?
4580 * @cfg {Boolean} animateRef (true|false) link to element default false
4583 * Create a new Navbar Item
4584 * @param {Object} config The config object
4586 Roo.bootstrap.NavItem = function(config){
4587 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4592 * The raw click event for the entire grid.
4593 * @param {Roo.EventObject} e
4598 * Fires when the active item active state changes
4599 * @param {Roo.bootstrap.NavItem} this
4600 * @param {boolean} state the new state
4606 * Fires when scroll to element
4607 * @param {Roo.bootstrap.NavItem} this
4608 * @param {Object} options
4609 * @param {Roo.EventObject} e
4617 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4626 preventDefault : false,
4633 getAutoCreate : function(){
4642 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4644 if (this.disabled) {
4645 cfg.cls += ' disabled';
4648 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4652 href : this.href || "#",
4653 html: this.html || ''
4656 if (this.tagtype == 'a') {
4657 cfg.cn[0].cls = 'nav-link';
4660 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4663 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4665 if(this.glyphicon) {
4666 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4671 cfg.cn[0].html += " <span class='caret'></span>";
4675 if (this.badge !== '') {
4677 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4685 initEvents: function()
4687 if (typeof (this.menu) != 'undefined') {
4688 this.menu.parentType = this.xtype;
4689 this.menu.triggerEl = this.el;
4690 this.menu = this.addxtype(Roo.apply({}, this.menu));
4693 this.el.select('a',true).on('click', this.onClick, this);
4695 if(this.tagtype == 'span'){
4696 this.el.select('span',true).on('click', this.onClick, this);
4699 // at this point parent should be available..
4700 this.parent().register(this);
4703 onClick : function(e)
4705 if (e.getTarget('.dropdown-menu-item')) {
4706 // did you click on a menu itemm.... - then don't trigger onclick..
4711 this.preventDefault ||
4714 Roo.log("NavItem - prevent Default?");
4718 if (this.disabled) {
4722 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4723 if (tg && tg.transition) {
4724 Roo.log("waiting for the transitionend");
4730 //Roo.log("fire event clicked");
4731 if(this.fireEvent('click', this, e) === false){
4735 if(this.tagtype == 'span'){
4739 //Roo.log(this.href);
4740 var ael = this.el.select('a',true).first();
4743 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4744 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4745 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4746 return; // ignore... - it's a 'hash' to another page.
4748 Roo.log("NavItem - prevent Default?");
4750 this.scrollToElement(e);
4754 var p = this.parent();
4756 if (['tabs','pills'].indexOf(p.type)!==-1) {
4757 if (typeof(p.setActiveItem) !== 'undefined') {
4758 p.setActiveItem(this);
4762 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4763 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4764 // remove the collapsed menu expand...
4765 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4769 isActive: function () {
4772 setActive : function(state, fire, is_was_active)
4774 if (this.active && !state && this.navId) {
4775 this.was_active = true;
4776 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4778 nv.clearWasActive(this);
4782 this.active = state;
4785 this.el.removeClass('active');
4786 } else if (!this.el.hasClass('active')) {
4787 this.el.addClass('active');
4790 this.fireEvent('changed', this, state);
4793 // show a panel if it's registered and related..
4795 if (!this.navId || !this.tabId || !state || is_was_active) {
4799 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4803 var pan = tg.getPanelByName(this.tabId);
4807 // if we can not flip to new panel - go back to old nav highlight..
4808 if (false == tg.showPanel(pan)) {
4809 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4811 var onav = nv.getWasActive();
4813 onav.setActive(true, false, true);
4822 // this should not be here...
4823 setDisabled : function(state)
4825 this.disabled = state;
4827 this.el.removeClass('disabled');
4828 } else if (!this.el.hasClass('disabled')) {
4829 this.el.addClass('disabled');
4835 * Fetch the element to display the tooltip on.
4836 * @return {Roo.Element} defaults to this.el
4838 tooltipEl : function()
4840 return this.el.select('' + this.tagtype + '', true).first();
4843 scrollToElement : function(e)
4845 var c = document.body;
4848 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4850 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4851 c = document.documentElement;
4854 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4860 var o = target.calcOffsetsTo(c);
4867 this.fireEvent('scrollto', this, options, e);
4869 Roo.get(c).scrollTo('top', options.value, true);
4882 * <span> icon </span>
4883 * <span> text </span>
4884 * <span>badge </span>
4888 * @class Roo.bootstrap.NavSidebarItem
4889 * @extends Roo.bootstrap.NavItem
4890 * Bootstrap Navbar.NavSidebarItem class
4891 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4892 * {Boolean} open is the menu open
4893 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4894 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4895 * {String} buttonSize (sm|md|lg)the extra classes for the button
4896 * {Boolean} showArrow show arrow next to the text (default true)
4898 * Create a new Navbar Button
4899 * @param {Object} config The config object
4901 Roo.bootstrap.NavSidebarItem = function(config){
4902 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4907 * The raw click event for the entire grid.
4908 * @param {Roo.EventObject} e
4913 * Fires when the active item active state changes
4914 * @param {Roo.bootstrap.NavSidebarItem} this
4915 * @param {boolean} state the new state
4923 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4925 badgeWeight : 'default',
4931 buttonWeight : 'default',
4937 getAutoCreate : function(){
4942 href : this.href || '#',
4948 if(this.buttonView){
4951 href : this.href || '#',
4952 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4965 cfg.cls += ' active';
4968 if (this.disabled) {
4969 cfg.cls += ' disabled';
4972 cfg.cls += ' open x-open';
4975 if (this.glyphicon || this.icon) {
4976 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4977 a.cn.push({ tag : 'i', cls : c }) ;
4980 if(!this.buttonView){
4983 html : this.html || ''
4990 if (this.badge !== '') {
4991 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4997 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5000 a.cls += ' dropdown-toggle treeview' ;
5006 initEvents : function()
5008 if (typeof (this.menu) != 'undefined') {
5009 this.menu.parentType = this.xtype;
5010 this.menu.triggerEl = this.el;
5011 this.menu = this.addxtype(Roo.apply({}, this.menu));
5014 this.el.on('click', this.onClick, this);
5016 if(this.badge !== ''){
5017 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5022 onClick : function(e)
5029 if(this.preventDefault){
5033 this.fireEvent('click', this);
5036 disable : function()
5038 this.setDisabled(true);
5043 this.setDisabled(false);
5046 setDisabled : function(state)
5048 if(this.disabled == state){
5052 this.disabled = state;
5055 this.el.addClass('disabled');
5059 this.el.removeClass('disabled');
5064 setActive : function(state)
5066 if(this.active == state){
5070 this.active = state;
5073 this.el.addClass('active');
5077 this.el.removeClass('active');
5082 isActive: function ()
5087 setBadge : function(str)
5093 this.badgeEl.dom.innerHTML = str;
5110 * @class Roo.bootstrap.Row
5111 * @extends Roo.bootstrap.Component
5112 * Bootstrap Row class (contains columns...)
5116 * @param {Object} config The config object
5119 Roo.bootstrap.Row = function(config){
5120 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5123 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5125 getAutoCreate : function(){
5144 * @class Roo.bootstrap.Element
5145 * @extends Roo.bootstrap.Component
5146 * Bootstrap Element class
5147 * @cfg {String} html contents of the element
5148 * @cfg {String} tag tag of the element
5149 * @cfg {String} cls class of the element
5150 * @cfg {Boolean} preventDefault (true|false) default false
5151 * @cfg {Boolean} clickable (true|false) default false
5154 * Create a new Element
5155 * @param {Object} config The config object
5158 Roo.bootstrap.Element = function(config){
5159 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5165 * When a element is chick
5166 * @param {Roo.bootstrap.Element} this
5167 * @param {Roo.EventObject} e
5173 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5178 preventDefault: false,
5181 getAutoCreate : function(){
5185 // cls: this.cls, double assign in parent class Component.js :: onRender
5192 initEvents: function()
5194 Roo.bootstrap.Element.superclass.initEvents.call(this);
5197 this.el.on('click', this.onClick, this);
5202 onClick : function(e)
5204 if(this.preventDefault){
5208 this.fireEvent('click', this, e);
5211 getValue : function()
5213 return this.el.dom.innerHTML;
5216 setValue : function(value)
5218 this.el.dom.innerHTML = value;
5233 * @class Roo.bootstrap.Pagination
5234 * @extends Roo.bootstrap.Component
5235 * Bootstrap Pagination class
5236 * @cfg {String} size xs | sm | md | lg
5237 * @cfg {Boolean} inverse false | true
5240 * Create a new Pagination
5241 * @param {Object} config The config object
5244 Roo.bootstrap.Pagination = function(config){
5245 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5248 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5254 getAutoCreate : function(){
5260 cfg.cls += ' inverse';
5266 cfg.cls += " " + this.cls;
5284 * @class Roo.bootstrap.PaginationItem
5285 * @extends Roo.bootstrap.Component
5286 * Bootstrap PaginationItem class
5287 * @cfg {String} html text
5288 * @cfg {String} href the link
5289 * @cfg {Boolean} preventDefault (true | false) default true
5290 * @cfg {Boolean} active (true | false) default false
5291 * @cfg {Boolean} disabled default false
5295 * Create a new PaginationItem
5296 * @param {Object} config The config object
5300 Roo.bootstrap.PaginationItem = function(config){
5301 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5306 * The raw click event for the entire grid.
5307 * @param {Roo.EventObject} e
5313 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5317 preventDefault: true,
5322 getAutoCreate : function(){
5328 href : this.href ? this.href : '#',
5329 html : this.html ? this.html : ''
5339 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5343 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5349 initEvents: function() {
5351 this.el.on('click', this.onClick, this);
5354 onClick : function(e)
5356 Roo.log('PaginationItem on click ');
5357 if(this.preventDefault){
5365 this.fireEvent('click', this, e);
5381 * @class Roo.bootstrap.Slider
5382 * @extends Roo.bootstrap.Component
5383 * Bootstrap Slider class
5386 * Create a new Slider
5387 * @param {Object} config The config object
5390 Roo.bootstrap.Slider = function(config){
5391 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5394 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5396 getAutoCreate : function(){
5400 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5404 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5416 * Ext JS Library 1.1.1
5417 * Copyright(c) 2006-2007, Ext JS, LLC.
5419 * Originally Released Under LGPL - original licence link has changed is not relivant.
5422 * <script type="text/javascript">
5427 * @class Roo.grid.ColumnModel
5428 * @extends Roo.util.Observable
5429 * This is the default implementation of a ColumnModel used by the Grid. It defines
5430 * the columns in the grid.
5433 var colModel = new Roo.grid.ColumnModel([
5434 {header: "Ticker", width: 60, sortable: true, locked: true},
5435 {header: "Company Name", width: 150, sortable: true},
5436 {header: "Market Cap.", width: 100, sortable: true},
5437 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5438 {header: "Employees", width: 100, sortable: true, resizable: false}
5443 * The config options listed for this class are options which may appear in each
5444 * individual column definition.
5445 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5447 * @param {Object} config An Array of column config objects. See this class's
5448 * config objects for details.
5450 Roo.grid.ColumnModel = function(config){
5452 * The config passed into the constructor
5454 this.config = config;
5457 // if no id, create one
5458 // if the column does not have a dataIndex mapping,
5459 // map it to the order it is in the config
5460 for(var i = 0, len = config.length; i < len; i++){
5462 if(typeof c.dataIndex == "undefined"){
5465 if(typeof c.renderer == "string"){
5466 c.renderer = Roo.util.Format[c.renderer];
5468 if(typeof c.id == "undefined"){
5471 if(c.editor && c.editor.xtype){
5472 c.editor = Roo.factory(c.editor, Roo.grid);
5474 if(c.editor && c.editor.isFormField){
5475 c.editor = new Roo.grid.GridEditor(c.editor);
5477 this.lookup[c.id] = c;
5481 * The width of columns which have no width specified (defaults to 100)
5484 this.defaultWidth = 100;
5487 * Default sortable of columns which have no sortable specified (defaults to false)
5490 this.defaultSortable = false;
5494 * @event widthchange
5495 * Fires when the width of a column changes.
5496 * @param {ColumnModel} this
5497 * @param {Number} columnIndex The column index
5498 * @param {Number} newWidth The new width
5500 "widthchange": true,
5502 * @event headerchange
5503 * Fires when the text of a header changes.
5504 * @param {ColumnModel} this
5505 * @param {Number} columnIndex The column index
5506 * @param {Number} newText The new header text
5508 "headerchange": true,
5510 * @event hiddenchange
5511 * Fires when a column is hidden or "unhidden".
5512 * @param {ColumnModel} this
5513 * @param {Number} columnIndex The column index
5514 * @param {Boolean} hidden true if hidden, false otherwise
5516 "hiddenchange": true,
5518 * @event columnmoved
5519 * Fires when a column is moved.
5520 * @param {ColumnModel} this
5521 * @param {Number} oldIndex
5522 * @param {Number} newIndex
5524 "columnmoved" : true,
5526 * @event columlockchange
5527 * Fires when a column's locked state is changed
5528 * @param {ColumnModel} this
5529 * @param {Number} colIndex
5530 * @param {Boolean} locked true if locked
5532 "columnlockchange" : true
5534 Roo.grid.ColumnModel.superclass.constructor.call(this);
5536 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5538 * @cfg {String} header The header text to display in the Grid view.
5541 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5542 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5543 * specified, the column's index is used as an index into the Record's data Array.
5546 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5547 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5550 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5551 * Defaults to the value of the {@link #defaultSortable} property.
5552 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5555 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5558 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5561 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5564 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5567 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5568 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5569 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5570 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5573 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5576 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5579 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5582 * @cfg {String} cursor (Optional)
5585 * @cfg {String} tooltip (Optional)
5588 * @cfg {Number} xs (Optional)
5591 * @cfg {Number} sm (Optional)
5594 * @cfg {Number} md (Optional)
5597 * @cfg {Number} lg (Optional)
5600 * Returns the id of the column at the specified index.
5601 * @param {Number} index The column index
5602 * @return {String} the id
5604 getColumnId : function(index){
5605 return this.config[index].id;
5609 * Returns the column for a specified id.
5610 * @param {String} id The column id
5611 * @return {Object} the column
5613 getColumnById : function(id){
5614 return this.lookup[id];
5619 * Returns the column for a specified dataIndex.
5620 * @param {String} dataIndex The column dataIndex
5621 * @return {Object|Boolean} the column or false if not found
5623 getColumnByDataIndex: function(dataIndex){
5624 var index = this.findColumnIndex(dataIndex);
5625 return index > -1 ? this.config[index] : false;
5629 * Returns the index for a specified column id.
5630 * @param {String} id The column id
5631 * @return {Number} the index, or -1 if not found
5633 getIndexById : function(id){
5634 for(var i = 0, len = this.config.length; i < len; i++){
5635 if(this.config[i].id == id){
5643 * Returns the index for a specified column dataIndex.
5644 * @param {String} dataIndex The column dataIndex
5645 * @return {Number} the index, or -1 if not found
5648 findColumnIndex : function(dataIndex){
5649 for(var i = 0, len = this.config.length; i < len; i++){
5650 if(this.config[i].dataIndex == dataIndex){
5658 moveColumn : function(oldIndex, newIndex){
5659 var c = this.config[oldIndex];
5660 this.config.splice(oldIndex, 1);
5661 this.config.splice(newIndex, 0, c);
5662 this.dataMap = null;
5663 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5666 isLocked : function(colIndex){
5667 return this.config[colIndex].locked === true;
5670 setLocked : function(colIndex, value, suppressEvent){
5671 if(this.isLocked(colIndex) == value){
5674 this.config[colIndex].locked = value;
5676 this.fireEvent("columnlockchange", this, colIndex, value);
5680 getTotalLockedWidth : function(){
5682 for(var i = 0; i < this.config.length; i++){
5683 if(this.isLocked(i) && !this.isHidden(i)){
5684 this.totalWidth += this.getColumnWidth(i);
5690 getLockedCount : function(){
5691 for(var i = 0, len = this.config.length; i < len; i++){
5692 if(!this.isLocked(i)){
5697 return this.config.length;
5701 * Returns the number of columns.
5704 getColumnCount : function(visibleOnly){
5705 if(visibleOnly === true){
5707 for(var i = 0, len = this.config.length; i < len; i++){
5708 if(!this.isHidden(i)){
5714 return this.config.length;
5718 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5719 * @param {Function} fn
5720 * @param {Object} scope (optional)
5721 * @return {Array} result
5723 getColumnsBy : function(fn, scope){
5725 for(var i = 0, len = this.config.length; i < len; i++){
5726 var c = this.config[i];
5727 if(fn.call(scope||this, c, i) === true){
5735 * Returns true if the specified column is sortable.
5736 * @param {Number} col The column index
5739 isSortable : function(col){
5740 if(typeof this.config[col].sortable == "undefined"){
5741 return this.defaultSortable;
5743 return this.config[col].sortable;
5747 * Returns the rendering (formatting) function defined for the column.
5748 * @param {Number} col The column index.
5749 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5751 getRenderer : function(col){
5752 if(!this.config[col].renderer){
5753 return Roo.grid.ColumnModel.defaultRenderer;
5755 return this.config[col].renderer;
5759 * Sets the rendering (formatting) function for a column.
5760 * @param {Number} col The column index
5761 * @param {Function} fn The function to use to process the cell's raw data
5762 * to return HTML markup for the grid view. The render function is called with
5763 * the following parameters:<ul>
5764 * <li>Data value.</li>
5765 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5766 * <li>css A CSS style string to apply to the table cell.</li>
5767 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5768 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5769 * <li>Row index</li>
5770 * <li>Column index</li>
5771 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5773 setRenderer : function(col, fn){
5774 this.config[col].renderer = fn;
5778 * Returns the width for the specified column.
5779 * @param {Number} col The column index
5782 getColumnWidth : function(col){
5783 return this.config[col].width * 1 || this.defaultWidth;
5787 * Sets the width for a column.
5788 * @param {Number} col The column index
5789 * @param {Number} width The new width
5791 setColumnWidth : function(col, width, suppressEvent){
5792 this.config[col].width = width;
5793 this.totalWidth = null;
5795 this.fireEvent("widthchange", this, col, width);
5800 * Returns the total width of all columns.
5801 * @param {Boolean} includeHidden True to include hidden column widths
5804 getTotalWidth : function(includeHidden){
5805 if(!this.totalWidth){
5806 this.totalWidth = 0;
5807 for(var i = 0, len = this.config.length; i < len; i++){
5808 if(includeHidden || !this.isHidden(i)){
5809 this.totalWidth += this.getColumnWidth(i);
5813 return this.totalWidth;
5817 * Returns the header for the specified column.
5818 * @param {Number} col The column index
5821 getColumnHeader : function(col){
5822 return this.config[col].header;
5826 * Sets the header for a column.
5827 * @param {Number} col The column index
5828 * @param {String} header The new header
5830 setColumnHeader : function(col, header){
5831 this.config[col].header = header;
5832 this.fireEvent("headerchange", this, col, header);
5836 * Returns the tooltip for the specified column.
5837 * @param {Number} col The column index
5840 getColumnTooltip : function(col){
5841 return this.config[col].tooltip;
5844 * Sets the tooltip for a column.
5845 * @param {Number} col The column index
5846 * @param {String} tooltip The new tooltip
5848 setColumnTooltip : function(col, tooltip){
5849 this.config[col].tooltip = tooltip;
5853 * Returns the dataIndex for the specified column.
5854 * @param {Number} col The column index
5857 getDataIndex : function(col){
5858 return this.config[col].dataIndex;
5862 * Sets the dataIndex for a column.
5863 * @param {Number} col The column index
5864 * @param {Number} dataIndex The new dataIndex
5866 setDataIndex : function(col, dataIndex){
5867 this.config[col].dataIndex = dataIndex;
5873 * Returns true if the cell is editable.
5874 * @param {Number} colIndex The column index
5875 * @param {Number} rowIndex The row index - this is nto actually used..?
5878 isCellEditable : function(colIndex, rowIndex){
5879 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5883 * Returns the editor defined for the cell/column.
5884 * return false or null to disable editing.
5885 * @param {Number} colIndex The column index
5886 * @param {Number} rowIndex The row index
5889 getCellEditor : function(colIndex, rowIndex){
5890 return this.config[colIndex].editor;
5894 * Sets if a column is editable.
5895 * @param {Number} col The column index
5896 * @param {Boolean} editable True if the column is editable
5898 setEditable : function(col, editable){
5899 this.config[col].editable = editable;
5904 * Returns true if the column is hidden.
5905 * @param {Number} colIndex The column index
5908 isHidden : function(colIndex){
5909 return this.config[colIndex].hidden;
5914 * Returns true if the column width cannot be changed
5916 isFixed : function(colIndex){
5917 return this.config[colIndex].fixed;
5921 * Returns true if the column can be resized
5924 isResizable : function(colIndex){
5925 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5928 * Sets if a column is hidden.
5929 * @param {Number} colIndex The column index
5930 * @param {Boolean} hidden True if the column is hidden
5932 setHidden : function(colIndex, hidden){
5933 this.config[colIndex].hidden = hidden;
5934 this.totalWidth = null;
5935 this.fireEvent("hiddenchange", this, colIndex, hidden);
5939 * Sets the editor for a column.
5940 * @param {Number} col The column index
5941 * @param {Object} editor The editor object
5943 setEditor : function(col, editor){
5944 this.config[col].editor = editor;
5948 Roo.grid.ColumnModel.defaultRenderer = function(value)
5950 if(typeof value == "object") {
5953 if(typeof value == "string" && value.length < 1){
5957 return String.format("{0}", value);
5960 // Alias for backwards compatibility
5961 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5964 * Ext JS Library 1.1.1
5965 * Copyright(c) 2006-2007, Ext JS, LLC.
5967 * Originally Released Under LGPL - original licence link has changed is not relivant.
5970 * <script type="text/javascript">
5974 * @class Roo.LoadMask
5975 * A simple utility class for generically masking elements while loading data. If the element being masked has
5976 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5977 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5978 * element's UpdateManager load indicator and will be destroyed after the initial load.
5980 * Create a new LoadMask
5981 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5982 * @param {Object} config The config object
5984 Roo.LoadMask = function(el, config){
5985 this.el = Roo.get(el);
5986 Roo.apply(this, config);
5988 this.store.on('beforeload', this.onBeforeLoad, this);
5989 this.store.on('load', this.onLoad, this);
5990 this.store.on('loadexception', this.onLoadException, this);
5991 this.removeMask = false;
5993 var um = this.el.getUpdateManager();
5994 um.showLoadIndicator = false; // disable the default indicator
5995 um.on('beforeupdate', this.onBeforeLoad, this);
5996 um.on('update', this.onLoad, this);
5997 um.on('failure', this.onLoad, this);
5998 this.removeMask = true;
6002 Roo.LoadMask.prototype = {
6004 * @cfg {Boolean} removeMask
6005 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6006 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
6010 * The text to display in a centered loading message box (defaults to 'Loading...')
6014 * @cfg {String} msgCls
6015 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6017 msgCls : 'x-mask-loading',
6020 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6026 * Disables the mask to prevent it from being displayed
6028 disable : function(){
6029 this.disabled = true;
6033 * Enables the mask so that it can be displayed
6035 enable : function(){
6036 this.disabled = false;
6039 onLoadException : function()
6043 if (typeof(arguments[3]) != 'undefined') {
6044 Roo.MessageBox.alert("Error loading",arguments[3]);
6048 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6049 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6056 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6061 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6065 onBeforeLoad : function(){
6067 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6072 destroy : function(){
6074 this.store.un('beforeload', this.onBeforeLoad, this);
6075 this.store.un('load', this.onLoad, this);
6076 this.store.un('loadexception', this.onLoadException, this);
6078 var um = this.el.getUpdateManager();
6079 um.un('beforeupdate', this.onBeforeLoad, this);
6080 um.un('update', this.onLoad, this);
6081 um.un('failure', this.onLoad, this);
6092 * @class Roo.bootstrap.Table
6093 * @extends Roo.bootstrap.Component
6094 * Bootstrap Table class
6095 * @cfg {String} cls table class
6096 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6097 * @cfg {String} bgcolor Specifies the background color for a table
6098 * @cfg {Number} border Specifies whether the table cells should have borders or not
6099 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6100 * @cfg {Number} cellspacing Specifies the space between cells
6101 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6102 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6103 * @cfg {String} sortable Specifies that the table should be sortable
6104 * @cfg {String} summary Specifies a summary of the content of a table
6105 * @cfg {Number} width Specifies the width of a table
6106 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6108 * @cfg {boolean} striped Should the rows be alternative striped
6109 * @cfg {boolean} bordered Add borders to the table
6110 * @cfg {boolean} hover Add hover highlighting
6111 * @cfg {boolean} condensed Format condensed
6112 * @cfg {boolean} responsive Format condensed
6113 * @cfg {Boolean} loadMask (true|false) default false
6114 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6115 * @cfg {Boolean} headerShow (true|false) generate thead, default true
6116 * @cfg {Boolean} rowSelection (true|false) default false
6117 * @cfg {Boolean} cellSelection (true|false) default false
6118 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6119 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
6120 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
6121 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6125 * Create a new Table
6126 * @param {Object} config The config object
6129 Roo.bootstrap.Table = function(config){
6130 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6135 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6136 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6137 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6138 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6140 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6142 this.sm.grid = this;
6143 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6144 this.sm = this.selModel;
6145 this.sm.xmodule = this.xmodule || false;
6148 if (this.cm && typeof(this.cm.config) == 'undefined') {
6149 this.colModel = new Roo.grid.ColumnModel(this.cm);
6150 this.cm = this.colModel;
6151 this.cm.xmodule = this.xmodule || false;
6154 this.store= Roo.factory(this.store, Roo.data);
6155 this.ds = this.store;
6156 this.ds.xmodule = this.xmodule || false;
6159 if (this.footer && this.store) {
6160 this.footer.dataSource = this.ds;
6161 this.footer = Roo.factory(this.footer);
6168 * Fires when a cell is clicked
6169 * @param {Roo.bootstrap.Table} this
6170 * @param {Roo.Element} el
6171 * @param {Number} rowIndex
6172 * @param {Number} columnIndex
6173 * @param {Roo.EventObject} e
6177 * @event celldblclick
6178 * Fires when a cell is double clicked
6179 * @param {Roo.bootstrap.Table} this
6180 * @param {Roo.Element} el
6181 * @param {Number} rowIndex
6182 * @param {Number} columnIndex
6183 * @param {Roo.EventObject} e
6185 "celldblclick" : true,
6188 * Fires when a row is clicked
6189 * @param {Roo.bootstrap.Table} this
6190 * @param {Roo.Element} el
6191 * @param {Number} rowIndex
6192 * @param {Roo.EventObject} e
6196 * @event rowdblclick
6197 * Fires when a row is double clicked
6198 * @param {Roo.bootstrap.Table} this
6199 * @param {Roo.Element} el
6200 * @param {Number} rowIndex
6201 * @param {Roo.EventObject} e
6203 "rowdblclick" : true,
6206 * Fires when a mouseover occur
6207 * @param {Roo.bootstrap.Table} this
6208 * @param {Roo.Element} el
6209 * @param {Number} rowIndex
6210 * @param {Number} columnIndex
6211 * @param {Roo.EventObject} e
6216 * Fires when a mouseout occur
6217 * @param {Roo.bootstrap.Table} this
6218 * @param {Roo.Element} el
6219 * @param {Number} rowIndex
6220 * @param {Number} columnIndex
6221 * @param {Roo.EventObject} e
6226 * Fires when a row is rendered, so you can change add a style to it.
6227 * @param {Roo.bootstrap.Table} this
6228 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6232 * @event rowsrendered
6233 * Fires when all the rows have been rendered
6234 * @param {Roo.bootstrap.Table} this
6236 'rowsrendered' : true,
6238 * @event contextmenu
6239 * The raw contextmenu event for the entire grid.
6240 * @param {Roo.EventObject} e
6242 "contextmenu" : true,
6244 * @event rowcontextmenu
6245 * Fires when a row is right clicked
6246 * @param {Roo.bootstrap.Table} this
6247 * @param {Number} rowIndex
6248 * @param {Roo.EventObject} e
6250 "rowcontextmenu" : true,
6252 * @event cellcontextmenu
6253 * Fires when a cell is right clicked
6254 * @param {Roo.bootstrap.Table} this
6255 * @param {Number} rowIndex
6256 * @param {Number} cellIndex
6257 * @param {Roo.EventObject} e
6259 "cellcontextmenu" : true,
6261 * @event headercontextmenu
6262 * Fires when a header is right clicked
6263 * @param {Roo.bootstrap.Table} this
6264 * @param {Number} columnIndex
6265 * @param {Roo.EventObject} e
6267 "headercontextmenu" : true
6271 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6297 rowSelection : false,
6298 cellSelection : false,
6301 // Roo.Element - the tbody
6303 // Roo.Element - thead element
6306 container: false, // used by gridpanel...
6312 auto_hide_footer : false,
6314 getAutoCreate : function()
6316 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6323 if (this.scrollBody) {
6324 cfg.cls += ' table-body-fixed';
6327 cfg.cls += ' table-striped';
6331 cfg.cls += ' table-hover';
6333 if (this.bordered) {
6334 cfg.cls += ' table-bordered';
6336 if (this.condensed) {
6337 cfg.cls += ' table-condensed';
6339 if (this.responsive) {
6340 cfg.cls += ' table-responsive';
6344 cfg.cls+= ' ' +this.cls;
6347 // this lot should be simplifed...
6360 ].forEach(function(k) {
6368 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6371 if(this.store || this.cm){
6372 if(this.headerShow){
6373 cfg.cn.push(this.renderHeader());
6376 cfg.cn.push(this.renderBody());
6378 if(this.footerShow){
6379 cfg.cn.push(this.renderFooter());
6381 // where does this come from?
6382 //cfg.cls+= ' TableGrid';
6385 return { cn : [ cfg ] };
6388 initEvents : function()
6390 if(!this.store || !this.cm){
6393 if (this.selModel) {
6394 this.selModel.initEvents();
6398 //Roo.log('initEvents with ds!!!!');
6400 this.mainBody = this.el.select('tbody', true).first();
6401 this.mainHead = this.el.select('thead', true).first();
6402 this.mainFoot = this.el.select('tfoot', true).first();
6408 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6409 e.on('click', _this.sort, _this);
6412 this.mainBody.on("click", this.onClick, this);
6413 this.mainBody.on("dblclick", this.onDblClick, this);
6415 // why is this done????? = it breaks dialogs??
6416 //this.parent().el.setStyle('position', 'relative');
6420 this.footer.parentId = this.id;
6421 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6424 this.el.select('tfoot tr td').first().addClass('hide');
6429 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6432 this.store.on('load', this.onLoad, this);
6433 this.store.on('beforeload', this.onBeforeLoad, this);
6434 this.store.on('update', this.onUpdate, this);
6435 this.store.on('add', this.onAdd, this);
6436 this.store.on("clear", this.clear, this);
6438 this.el.on("contextmenu", this.onContextMenu, this);
6440 this.mainBody.on('scroll', this.onBodyScroll, this);
6442 this.cm.on("headerchange", this.onHeaderChange, this);
6444 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6448 onContextMenu : function(e, t)
6450 this.processEvent("contextmenu", e);
6453 processEvent : function(name, e)
6455 if (name != 'touchstart' ) {
6456 this.fireEvent(name, e);
6459 var t = e.getTarget();
6461 var cell = Roo.get(t);
6467 if(cell.findParent('tfoot', false, true)){
6471 if(cell.findParent('thead', false, true)){
6473 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6474 cell = Roo.get(t).findParent('th', false, true);
6476 Roo.log("failed to find th in thead?");
6477 Roo.log(e.getTarget());
6482 var cellIndex = cell.dom.cellIndex;
6484 var ename = name == 'touchstart' ? 'click' : name;
6485 this.fireEvent("header" + ename, this, cellIndex, e);
6490 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6491 cell = Roo.get(t).findParent('td', false, true);
6493 Roo.log("failed to find th in tbody?");
6494 Roo.log(e.getTarget());
6499 var row = cell.findParent('tr', false, true);
6500 var cellIndex = cell.dom.cellIndex;
6501 var rowIndex = row.dom.rowIndex - 1;
6505 this.fireEvent("row" + name, this, rowIndex, e);
6509 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6515 onMouseover : function(e, el)
6517 var cell = Roo.get(el);
6523 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6524 cell = cell.findParent('td', false, true);
6527 var row = cell.findParent('tr', false, true);
6528 var cellIndex = cell.dom.cellIndex;
6529 var rowIndex = row.dom.rowIndex - 1; // start from 0
6531 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6535 onMouseout : function(e, el)
6537 var cell = Roo.get(el);
6543 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6544 cell = cell.findParent('td', false, true);
6547 var row = cell.findParent('tr', false, true);
6548 var cellIndex = cell.dom.cellIndex;
6549 var rowIndex = row.dom.rowIndex - 1; // start from 0
6551 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6555 onClick : function(e, el)
6557 var cell = Roo.get(el);
6559 if(!cell || (!this.cellSelection && !this.rowSelection)){
6563 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6564 cell = cell.findParent('td', false, true);
6567 if(!cell || typeof(cell) == 'undefined'){
6571 var row = cell.findParent('tr', false, true);
6573 if(!row || typeof(row) == 'undefined'){
6577 var cellIndex = cell.dom.cellIndex;
6578 var rowIndex = this.getRowIndex(row);
6580 // why??? - should these not be based on SelectionModel?
6581 if(this.cellSelection){
6582 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6585 if(this.rowSelection){
6586 this.fireEvent('rowclick', this, row, rowIndex, e);
6592 onDblClick : function(e,el)
6594 var cell = Roo.get(el);
6596 if(!cell || (!this.cellSelection && !this.rowSelection)){
6600 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6601 cell = cell.findParent('td', false, true);
6604 if(!cell || typeof(cell) == 'undefined'){
6608 var row = cell.findParent('tr', false, true);
6610 if(!row || typeof(row) == 'undefined'){
6614 var cellIndex = cell.dom.cellIndex;
6615 var rowIndex = this.getRowIndex(row);
6617 if(this.cellSelection){
6618 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6621 if(this.rowSelection){
6622 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6626 sort : function(e,el)
6628 var col = Roo.get(el);
6630 if(!col.hasClass('sortable')){
6634 var sort = col.attr('sort');
6637 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6641 this.store.sortInfo = {field : sort, direction : dir};
6644 Roo.log("calling footer first");
6645 this.footer.onClick('first');
6648 this.store.load({ params : { start : 0 } });
6652 renderHeader : function()
6660 this.totalWidth = 0;
6662 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6664 var config = cm.config[i];
6668 cls : 'x-hcol-' + i,
6670 html: cm.getColumnHeader(i)
6675 if(typeof(config.sortable) != 'undefined' && config.sortable){
6677 c.html = '<i class="glyphicon"></i>' + c.html;
6680 if(typeof(config.lgHeader) != 'undefined'){
6681 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6684 if(typeof(config.mdHeader) != 'undefined'){
6685 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6688 if(typeof(config.smHeader) != 'undefined'){
6689 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6692 if(typeof(config.xsHeader) != 'undefined'){
6693 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6700 if(typeof(config.tooltip) != 'undefined'){
6701 c.tooltip = config.tooltip;
6704 if(typeof(config.colspan) != 'undefined'){
6705 c.colspan = config.colspan;
6708 if(typeof(config.hidden) != 'undefined' && config.hidden){
6709 c.style += ' display:none;';
6712 if(typeof(config.dataIndex) != 'undefined'){
6713 c.sort = config.dataIndex;
6718 if(typeof(config.align) != 'undefined' && config.align.length){
6719 c.style += ' text-align:' + config.align + ';';
6722 if(typeof(config.width) != 'undefined'){
6723 c.style += ' width:' + config.width + 'px;';
6724 this.totalWidth += config.width;
6726 this.totalWidth += 100; // assume minimum of 100 per column?
6729 if(typeof(config.cls) != 'undefined'){
6730 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6733 ['xs','sm','md','lg'].map(function(size){
6735 if(typeof(config[size]) == 'undefined'){
6739 if (!config[size]) { // 0 = hidden
6740 c.cls += ' hidden-' + size;
6744 c.cls += ' col-' + size + '-' + config[size];
6754 renderBody : function()
6764 colspan : this.cm.getColumnCount()
6774 renderFooter : function()
6784 colspan : this.cm.getColumnCount()
6798 // Roo.log('ds onload');
6803 var ds = this.store;
6805 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6806 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6807 if (_this.store.sortInfo) {
6809 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6810 e.select('i', true).addClass(['glyphicon-arrow-up']);
6813 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6814 e.select('i', true).addClass(['glyphicon-arrow-down']);
6819 var tbody = this.mainBody;
6821 if(ds.getCount() > 0){
6822 ds.data.each(function(d,rowIndex){
6823 var row = this.renderRow(cm, ds, rowIndex);
6825 tbody.createChild(row);
6829 if(row.cellObjects.length){
6830 Roo.each(row.cellObjects, function(r){
6831 _this.renderCellObject(r);
6838 var tfoot = this.el.select('tfoot', true).first();
6840 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6842 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6844 var total = this.ds.getTotalCount();
6846 if(this.footer.pageSize < total){
6847 this.mainFoot.show();
6851 Roo.each(this.el.select('tbody td', true).elements, function(e){
6852 e.on('mouseover', _this.onMouseover, _this);
6855 Roo.each(this.el.select('tbody td', true).elements, function(e){
6856 e.on('mouseout', _this.onMouseout, _this);
6858 this.fireEvent('rowsrendered', this);
6864 onUpdate : function(ds,record)
6866 this.refreshRow(record);
6870 onRemove : function(ds, record, index, isUpdate){
6871 if(isUpdate !== true){
6872 this.fireEvent("beforerowremoved", this, index, record);
6874 var bt = this.mainBody.dom;
6876 var rows = this.el.select('tbody > tr', true).elements;
6878 if(typeof(rows[index]) != 'undefined'){
6879 bt.removeChild(rows[index].dom);
6882 // if(bt.rows[index]){
6883 // bt.removeChild(bt.rows[index]);
6886 if(isUpdate !== true){
6887 //this.stripeRows(index);
6888 //this.syncRowHeights(index, index);
6890 this.fireEvent("rowremoved", this, index, record);
6894 onAdd : function(ds, records, rowIndex)
6896 //Roo.log('on Add called');
6897 // - note this does not handle multiple adding very well..
6898 var bt = this.mainBody.dom;
6899 for (var i =0 ; i < records.length;i++) {
6900 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6901 //Roo.log(records[i]);
6902 //Roo.log(this.store.getAt(rowIndex+i));
6903 this.insertRow(this.store, rowIndex + i, false);
6910 refreshRow : function(record){
6911 var ds = this.store, index;
6912 if(typeof record == 'number'){
6914 record = ds.getAt(index);
6916 index = ds.indexOf(record);
6918 this.insertRow(ds, index, true);
6920 this.onRemove(ds, record, index+1, true);
6922 //this.syncRowHeights(index, index);
6924 this.fireEvent("rowupdated", this, index, record);
6927 insertRow : function(dm, rowIndex, isUpdate){
6930 this.fireEvent("beforerowsinserted", this, rowIndex);
6932 //var s = this.getScrollState();
6933 var row = this.renderRow(this.cm, this.store, rowIndex);
6934 // insert before rowIndex..
6935 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6939 if(row.cellObjects.length){
6940 Roo.each(row.cellObjects, function(r){
6941 _this.renderCellObject(r);
6946 this.fireEvent("rowsinserted", this, rowIndex);
6947 //this.syncRowHeights(firstRow, lastRow);
6948 //this.stripeRows(firstRow);
6955 getRowDom : function(rowIndex)
6957 var rows = this.el.select('tbody > tr', true).elements;
6959 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6962 // returns the object tree for a tr..
6965 renderRow : function(cm, ds, rowIndex)
6967 var d = ds.getAt(rowIndex);
6971 cls : 'x-row-' + rowIndex,
6975 var cellObjects = [];
6977 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6978 var config = cm.config[i];
6980 var renderer = cm.getRenderer(i);
6984 if(typeof(renderer) !== 'undefined'){
6985 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6987 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6988 // and are rendered into the cells after the row is rendered - using the id for the element.
6990 if(typeof(value) === 'object'){
7000 rowIndex : rowIndex,
7005 this.fireEvent('rowclass', this, rowcfg);
7009 cls : rowcfg.rowClass + ' x-col-' + i,
7011 html: (typeof(value) === 'object') ? '' : value
7018 if(typeof(config.colspan) != 'undefined'){
7019 td.colspan = config.colspan;
7022 if(typeof(config.hidden) != 'undefined' && config.hidden){
7023 td.style += ' display:none;';
7026 if(typeof(config.align) != 'undefined' && config.align.length){
7027 td.style += ' text-align:' + config.align + ';';
7029 if(typeof(config.valign) != 'undefined' && config.valign.length){
7030 td.style += ' vertical-align:' + config.valign + ';';
7033 if(typeof(config.width) != 'undefined'){
7034 td.style += ' width:' + config.width + 'px;';
7037 if(typeof(config.cursor) != 'undefined'){
7038 td.style += ' cursor:' + config.cursor + ';';
7041 if(typeof(config.cls) != 'undefined'){
7042 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7045 ['xs','sm','md','lg'].map(function(size){
7047 if(typeof(config[size]) == 'undefined'){
7051 if (!config[size]) { // 0 = hidden
7052 td.cls += ' hidden-' + size;
7056 td.cls += ' col-' + size + '-' + config[size];
7064 row.cellObjects = cellObjects;
7072 onBeforeLoad : function()
7081 this.el.select('tbody', true).first().dom.innerHTML = '';
7084 * Show or hide a row.
7085 * @param {Number} rowIndex to show or hide
7086 * @param {Boolean} state hide
7088 setRowVisibility : function(rowIndex, state)
7090 var bt = this.mainBody.dom;
7092 var rows = this.el.select('tbody > tr', true).elements;
7094 if(typeof(rows[rowIndex]) == 'undefined'){
7097 rows[rowIndex].dom.style.display = state ? '' : 'none';
7101 getSelectionModel : function(){
7103 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7105 return this.selModel;
7108 * Render the Roo.bootstrap object from renderder
7110 renderCellObject : function(r)
7114 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7116 var t = r.cfg.render(r.container);
7119 Roo.each(r.cfg.cn, function(c){
7121 container: t.getChildContainer(),
7124 _this.renderCellObject(child);
7129 getRowIndex : function(row)
7133 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7144 * Returns the grid's underlying element = used by panel.Grid
7145 * @return {Element} The element
7147 getGridEl : function(){
7151 * Forces a resize - used by panel.Grid
7152 * @return {Element} The element
7154 autoSize : function()
7156 //var ctr = Roo.get(this.container.dom.parentElement);
7157 var ctr = Roo.get(this.el.dom);
7159 var thd = this.getGridEl().select('thead',true).first();
7160 var tbd = this.getGridEl().select('tbody', true).first();
7161 var tfd = this.getGridEl().select('tfoot', true).first();
7163 var cw = ctr.getWidth();
7167 tbd.setSize(ctr.getWidth(),
7168 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7170 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7173 cw = Math.max(cw, this.totalWidth);
7174 this.getGridEl().select('tr',true).setWidth(cw);
7175 // resize 'expandable coloumn?
7177 return; // we doe not have a view in this design..
7180 onBodyScroll: function()
7182 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7184 this.mainHead.setStyle({
7185 'position' : 'relative',
7186 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7192 var scrollHeight = this.mainBody.dom.scrollHeight;
7194 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7196 var height = this.mainBody.getHeight();
7198 if(scrollHeight - height == scrollTop) {
7200 var total = this.ds.getTotalCount();
7202 if(this.footer.cursor + this.footer.pageSize < total){
7204 this.footer.ds.load({
7206 start : this.footer.cursor + this.footer.pageSize,
7207 limit : this.footer.pageSize
7217 onHeaderChange : function()
7219 var header = this.renderHeader();
7220 var table = this.el.select('table', true).first();
7222 this.mainHead.remove();
7223 this.mainHead = table.createChild(header, this.mainBody, false);
7226 onHiddenChange : function(colModel, colIndex, hidden)
7228 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7229 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7231 this.CSS.updateRule(thSelector, "display", "");
7232 this.CSS.updateRule(tdSelector, "display", "");
7235 this.CSS.updateRule(thSelector, "display", "none");
7236 this.CSS.updateRule(tdSelector, "display", "none");
7239 this.onHeaderChange();
7243 setColumnWidth: function(col_index, width)
7245 // width = "md-2 xs-2..."
7246 if(!this.colModel.config[col_index]) {
7250 var w = width.split(" ");
7252 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7254 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7257 for(var j = 0; j < w.length; j++) {
7263 var size_cls = w[j].split("-");
7265 if(!Number.isInteger(size_cls[1] * 1)) {
7269 if(!this.colModel.config[col_index][size_cls[0]]) {
7273 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7277 h_row[0].classList.replace(
7278 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7279 "col-"+size_cls[0]+"-"+size_cls[1]
7282 for(var i = 0; i < rows.length; i++) {
7284 var size_cls = w[j].split("-");
7286 if(!Number.isInteger(size_cls[1] * 1)) {
7290 if(!this.colModel.config[col_index][size_cls[0]]) {
7294 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7298 rows[i].classList.replace(
7299 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7300 "col-"+size_cls[0]+"-"+size_cls[1]
7304 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7319 * @class Roo.bootstrap.TableCell
7320 * @extends Roo.bootstrap.Component
7321 * Bootstrap TableCell class
7322 * @cfg {String} html cell contain text
7323 * @cfg {String} cls cell class
7324 * @cfg {String} tag cell tag (td|th) default td
7325 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7326 * @cfg {String} align Aligns the content in a cell
7327 * @cfg {String} axis Categorizes cells
7328 * @cfg {String} bgcolor Specifies the background color of a cell
7329 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7330 * @cfg {Number} colspan Specifies the number of columns a cell should span
7331 * @cfg {String} headers Specifies one or more header cells a cell is related to
7332 * @cfg {Number} height Sets the height of a cell
7333 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7334 * @cfg {Number} rowspan Sets the number of rows a cell should span
7335 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7336 * @cfg {String} valign Vertical aligns the content in a cell
7337 * @cfg {Number} width Specifies the width of a cell
7340 * Create a new TableCell
7341 * @param {Object} config The config object
7344 Roo.bootstrap.TableCell = function(config){
7345 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7348 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7368 getAutoCreate : function(){
7369 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7389 cfg.align=this.align
7395 cfg.bgcolor=this.bgcolor
7398 cfg.charoff=this.charoff
7401 cfg.colspan=this.colspan
7404 cfg.headers=this.headers
7407 cfg.height=this.height
7410 cfg.nowrap=this.nowrap
7413 cfg.rowspan=this.rowspan
7416 cfg.scope=this.scope
7419 cfg.valign=this.valign
7422 cfg.width=this.width
7441 * @class Roo.bootstrap.TableRow
7442 * @extends Roo.bootstrap.Component
7443 * Bootstrap TableRow class
7444 * @cfg {String} cls row class
7445 * @cfg {String} align Aligns the content in a table row
7446 * @cfg {String} bgcolor Specifies a background color for a table row
7447 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7448 * @cfg {String} valign Vertical aligns the content in a table row
7451 * Create a new TableRow
7452 * @param {Object} config The config object
7455 Roo.bootstrap.TableRow = function(config){
7456 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7459 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7467 getAutoCreate : function(){
7468 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7478 cfg.align = this.align;
7481 cfg.bgcolor = this.bgcolor;
7484 cfg.charoff = this.charoff;
7487 cfg.valign = this.valign;
7505 * @class Roo.bootstrap.TableBody
7506 * @extends Roo.bootstrap.Component
7507 * Bootstrap TableBody class
7508 * @cfg {String} cls element class
7509 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7510 * @cfg {String} align Aligns the content inside the element
7511 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7512 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7515 * Create a new TableBody
7516 * @param {Object} config The config object
7519 Roo.bootstrap.TableBody = function(config){
7520 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7523 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7531 getAutoCreate : function(){
7532 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7546 cfg.align = this.align;
7549 cfg.charoff = this.charoff;
7552 cfg.valign = this.valign;
7559 // initEvents : function()
7566 // this.store = Roo.factory(this.store, Roo.data);
7567 // this.store.on('load', this.onLoad, this);
7569 // this.store.load();
7573 // onLoad: function ()
7575 // this.fireEvent('load', this);
7585 * Ext JS Library 1.1.1
7586 * Copyright(c) 2006-2007, Ext JS, LLC.
7588 * Originally Released Under LGPL - original licence link has changed is not relivant.
7591 * <script type="text/javascript">
7594 // as we use this in bootstrap.
7595 Roo.namespace('Roo.form');
7597 * @class Roo.form.Action
7598 * Internal Class used to handle form actions
7600 * @param {Roo.form.BasicForm} el The form element or its id
7601 * @param {Object} config Configuration options
7606 // define the action interface
7607 Roo.form.Action = function(form, options){
7609 this.options = options || {};
7612 * Client Validation Failed
7615 Roo.form.Action.CLIENT_INVALID = 'client';
7617 * Server Validation Failed
7620 Roo.form.Action.SERVER_INVALID = 'server';
7622 * Connect to Server Failed
7625 Roo.form.Action.CONNECT_FAILURE = 'connect';
7627 * Reading Data from Server Failed
7630 Roo.form.Action.LOAD_FAILURE = 'load';
7632 Roo.form.Action.prototype = {
7634 failureType : undefined,
7635 response : undefined,
7639 run : function(options){
7644 success : function(response){
7649 handleResponse : function(response){
7653 // default connection failure
7654 failure : function(response){
7656 this.response = response;
7657 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7658 this.form.afterAction(this, false);
7661 processResponse : function(response){
7662 this.response = response;
7663 if(!response.responseText){
7666 this.result = this.handleResponse(response);
7670 // utility functions used internally
7671 getUrl : function(appendParams){
7672 var url = this.options.url || this.form.url || this.form.el.dom.action;
7674 var p = this.getParams();
7676 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7682 getMethod : function(){
7683 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7686 getParams : function(){
7687 var bp = this.form.baseParams;
7688 var p = this.options.params;
7690 if(typeof p == "object"){
7691 p = Roo.urlEncode(Roo.applyIf(p, bp));
7692 }else if(typeof p == 'string' && bp){
7693 p += '&' + Roo.urlEncode(bp);
7696 p = Roo.urlEncode(bp);
7701 createCallback : function(){
7703 success: this.success,
7704 failure: this.failure,
7706 timeout: (this.form.timeout*1000),
7707 upload: this.form.fileUpload ? this.success : undefined
7712 Roo.form.Action.Submit = function(form, options){
7713 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7716 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7719 haveProgress : false,
7720 uploadComplete : false,
7722 // uploadProgress indicator.
7723 uploadProgress : function()
7725 if (!this.form.progressUrl) {
7729 if (!this.haveProgress) {
7730 Roo.MessageBox.progress("Uploading", "Uploading");
7732 if (this.uploadComplete) {
7733 Roo.MessageBox.hide();
7737 this.haveProgress = true;
7739 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7741 var c = new Roo.data.Connection();
7743 url : this.form.progressUrl,
7748 success : function(req){
7749 //console.log(data);
7753 rdata = Roo.decode(req.responseText)
7755 Roo.log("Invalid data from server..");
7759 if (!rdata || !rdata.success) {
7761 Roo.MessageBox.alert(Roo.encode(rdata));
7764 var data = rdata.data;
7766 if (this.uploadComplete) {
7767 Roo.MessageBox.hide();
7772 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7773 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7776 this.uploadProgress.defer(2000,this);
7779 failure: function(data) {
7780 Roo.log('progress url failed ');
7791 // run get Values on the form, so it syncs any secondary forms.
7792 this.form.getValues();
7794 var o = this.options;
7795 var method = this.getMethod();
7796 var isPost = method == 'POST';
7797 if(o.clientValidation === false || this.form.isValid()){
7799 if (this.form.progressUrl) {
7800 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7801 (new Date() * 1) + '' + Math.random());
7806 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7807 form:this.form.el.dom,
7808 url:this.getUrl(!isPost),
7810 params:isPost ? this.getParams() : null,
7811 isUpload: this.form.fileUpload
7814 this.uploadProgress();
7816 }else if (o.clientValidation !== false){ // client validation failed
7817 this.failureType = Roo.form.Action.CLIENT_INVALID;
7818 this.form.afterAction(this, false);
7822 success : function(response)
7824 this.uploadComplete= true;
7825 if (this.haveProgress) {
7826 Roo.MessageBox.hide();
7830 var result = this.processResponse(response);
7831 if(result === true || result.success){
7832 this.form.afterAction(this, true);
7836 this.form.markInvalid(result.errors);
7837 this.failureType = Roo.form.Action.SERVER_INVALID;
7839 this.form.afterAction(this, false);
7841 failure : function(response)
7843 this.uploadComplete= true;
7844 if (this.haveProgress) {
7845 Roo.MessageBox.hide();
7848 this.response = response;
7849 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7850 this.form.afterAction(this, false);
7853 handleResponse : function(response){
7854 if(this.form.errorReader){
7855 var rs = this.form.errorReader.read(response);
7858 for(var i = 0, len = rs.records.length; i < len; i++) {
7859 var r = rs.records[i];
7863 if(errors.length < 1){
7867 success : rs.success,
7873 ret = Roo.decode(response.responseText);
7877 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7887 Roo.form.Action.Load = function(form, options){
7888 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7889 this.reader = this.form.reader;
7892 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7897 Roo.Ajax.request(Roo.apply(
7898 this.createCallback(), {
7899 method:this.getMethod(),
7900 url:this.getUrl(false),
7901 params:this.getParams()
7905 success : function(response){
7907 var result = this.processResponse(response);
7908 if(result === true || !result.success || !result.data){
7909 this.failureType = Roo.form.Action.LOAD_FAILURE;
7910 this.form.afterAction(this, false);
7913 this.form.clearInvalid();
7914 this.form.setValues(result.data);
7915 this.form.afterAction(this, true);
7918 handleResponse : function(response){
7919 if(this.form.reader){
7920 var rs = this.form.reader.read(response);
7921 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7923 success : rs.success,
7927 return Roo.decode(response.responseText);
7931 Roo.form.Action.ACTION_TYPES = {
7932 'load' : Roo.form.Action.Load,
7933 'submit' : Roo.form.Action.Submit
7942 * @class Roo.bootstrap.Form
7943 * @extends Roo.bootstrap.Component
7944 * Bootstrap Form class
7945 * @cfg {String} method GET | POST (default POST)
7946 * @cfg {String} labelAlign top | left (default top)
7947 * @cfg {String} align left | right - for navbars
7948 * @cfg {Boolean} loadMask load mask when submit (default true)
7953 * @param {Object} config The config object
7957 Roo.bootstrap.Form = function(config){
7959 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7961 Roo.bootstrap.Form.popover.apply();
7965 * @event clientvalidation
7966 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7967 * @param {Form} this
7968 * @param {Boolean} valid true if the form has passed client-side validation
7970 clientvalidation: true,
7972 * @event beforeaction
7973 * Fires before any action is performed. Return false to cancel the action.
7974 * @param {Form} this
7975 * @param {Action} action The action to be performed
7979 * @event actionfailed
7980 * Fires when an action fails.
7981 * @param {Form} this
7982 * @param {Action} action The action that failed
7984 actionfailed : true,
7986 * @event actioncomplete
7987 * Fires when an action is completed.
7988 * @param {Form} this
7989 * @param {Action} action The action that completed
7991 actioncomplete : true
7995 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7998 * @cfg {String} method
7999 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8004 * The URL to use for form actions if one isn't supplied in the action options.
8007 * @cfg {Boolean} fileUpload
8008 * Set to true if this form is a file upload.
8012 * @cfg {Object} baseParams
8013 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8017 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8021 * @cfg {Sting} align (left|right) for navbar forms
8026 activeAction : null,
8029 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8030 * element by passing it or its id or mask the form itself by passing in true.
8033 waitMsgTarget : false,
8038 * @cfg {Boolean} errorMask (true|false) default false
8043 * @cfg {Number} maskOffset Default 100
8048 * @cfg {Boolean} maskBody
8052 getAutoCreate : function(){
8056 method : this.method || 'POST',
8057 id : this.id || Roo.id(),
8060 if (this.parent().xtype.match(/^Nav/)) {
8061 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8065 if (this.labelAlign == 'left' ) {
8066 cfg.cls += ' form-horizontal';
8072 initEvents : function()
8074 this.el.on('submit', this.onSubmit, this);
8075 // this was added as random key presses on the form where triggering form submit.
8076 this.el.on('keypress', function(e) {
8077 if (e.getCharCode() != 13) {
8080 // we might need to allow it for textareas.. and some other items.
8081 // check e.getTarget().
8083 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8087 Roo.log("keypress blocked");
8095 onSubmit : function(e){
8100 * Returns true if client-side validation on the form is successful.
8103 isValid : function(){
8104 var items = this.getItems();
8108 items.each(function(f){
8114 Roo.log('invalid field: ' + f.name);
8118 if(!target && f.el.isVisible(true)){
8124 if(this.errorMask && !valid){
8125 Roo.bootstrap.Form.popover.mask(this, target);
8132 * Returns true if any fields in this form have changed since their original load.
8135 isDirty : function(){
8137 var items = this.getItems();
8138 items.each(function(f){
8148 * Performs a predefined action (submit or load) or custom actions you define on this form.
8149 * @param {String} actionName The name of the action type
8150 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8151 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8152 * accept other config options):
8154 Property Type Description
8155 ---------------- --------------- ----------------------------------------------------------------------------------
8156 url String The url for the action (defaults to the form's url)
8157 method String The form method to use (defaults to the form's method, or POST if not defined)
8158 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8159 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8160 validate the form on the client (defaults to false)
8162 * @return {BasicForm} this
8164 doAction : function(action, options){
8165 if(typeof action == 'string'){
8166 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8168 if(this.fireEvent('beforeaction', this, action) !== false){
8169 this.beforeAction(action);
8170 action.run.defer(100, action);
8176 beforeAction : function(action){
8177 var o = action.options;
8182 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8184 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8187 // not really supported yet.. ??
8189 //if(this.waitMsgTarget === true){
8190 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8191 //}else if(this.waitMsgTarget){
8192 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8193 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8195 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8201 afterAction : function(action, success){
8202 this.activeAction = null;
8203 var o = action.options;
8208 Roo.get(document.body).unmask();
8214 //if(this.waitMsgTarget === true){
8215 // this.el.unmask();
8216 //}else if(this.waitMsgTarget){
8217 // this.waitMsgTarget.unmask();
8219 // Roo.MessageBox.updateProgress(1);
8220 // Roo.MessageBox.hide();
8227 Roo.callback(o.success, o.scope, [this, action]);
8228 this.fireEvent('actioncomplete', this, action);
8232 // failure condition..
8233 // we have a scenario where updates need confirming.
8234 // eg. if a locking scenario exists..
8235 // we look for { errors : { needs_confirm : true }} in the response.
8237 (typeof(action.result) != 'undefined') &&
8238 (typeof(action.result.errors) != 'undefined') &&
8239 (typeof(action.result.errors.needs_confirm) != 'undefined')
8242 Roo.log("not supported yet");
8245 Roo.MessageBox.confirm(
8246 "Change requires confirmation",
8247 action.result.errorMsg,
8252 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8262 Roo.callback(o.failure, o.scope, [this, action]);
8263 // show an error message if no failed handler is set..
8264 if (!this.hasListener('actionfailed')) {
8265 Roo.log("need to add dialog support");
8267 Roo.MessageBox.alert("Error",
8268 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8269 action.result.errorMsg :
8270 "Saving Failed, please check your entries or try again"
8275 this.fireEvent('actionfailed', this, action);
8280 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8281 * @param {String} id The value to search for
8284 findField : function(id){
8285 var items = this.getItems();
8286 var field = items.get(id);
8288 items.each(function(f){
8289 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8296 return field || null;
8299 * Mark fields in this form invalid in bulk.
8300 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8301 * @return {BasicForm} this
8303 markInvalid : function(errors){
8304 if(errors instanceof Array){
8305 for(var i = 0, len = errors.length; i < len; i++){
8306 var fieldError = errors[i];
8307 var f = this.findField(fieldError.id);
8309 f.markInvalid(fieldError.msg);
8315 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8316 field.markInvalid(errors[id]);
8320 //Roo.each(this.childForms || [], function (f) {
8321 // f.markInvalid(errors);
8328 * Set values for fields in this form in bulk.
8329 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8330 * @return {BasicForm} this
8332 setValues : function(values){
8333 if(values instanceof Array){ // array of objects
8334 for(var i = 0, len = values.length; i < len; i++){
8336 var f = this.findField(v.id);
8338 f.setValue(v.value);
8339 if(this.trackResetOnLoad){
8340 f.originalValue = f.getValue();
8344 }else{ // object hash
8347 if(typeof values[id] != 'function' && (field = this.findField(id))){
8349 if (field.setFromData &&
8351 field.displayField &&
8352 // combos' with local stores can
8353 // be queried via setValue()
8354 // to set their value..
8355 (field.store && !field.store.isLocal)
8359 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8360 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8361 field.setFromData(sd);
8363 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8365 field.setFromData(values);
8368 field.setValue(values[id]);
8372 if(this.trackResetOnLoad){
8373 field.originalValue = field.getValue();
8379 //Roo.each(this.childForms || [], function (f) {
8380 // f.setValues(values);
8387 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8388 * they are returned as an array.
8389 * @param {Boolean} asString
8392 getValues : function(asString){
8393 //if (this.childForms) {
8394 // copy values from the child forms
8395 // Roo.each(this.childForms, function (f) {
8396 // this.setValues(f.getValues());
8402 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8403 if(asString === true){
8406 return Roo.urlDecode(fs);
8410 * Returns the fields in this form as an object with key/value pairs.
8411 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8414 getFieldValues : function(with_hidden)
8416 var items = this.getItems();
8418 items.each(function(f){
8424 var v = f.getValue();
8426 if (f.inputType =='radio') {
8427 if (typeof(ret[f.getName()]) == 'undefined') {
8428 ret[f.getName()] = ''; // empty..
8431 if (!f.el.dom.checked) {
8439 if(f.xtype == 'MoneyField'){
8440 ret[f.currencyName] = f.getCurrency();
8443 // not sure if this supported any more..
8444 if ((typeof(v) == 'object') && f.getRawValue) {
8445 v = f.getRawValue() ; // dates..
8447 // combo boxes where name != hiddenName...
8448 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8449 ret[f.name] = f.getRawValue();
8451 ret[f.getName()] = v;
8458 * Clears all invalid messages in this form.
8459 * @return {BasicForm} this
8461 clearInvalid : function(){
8462 var items = this.getItems();
8464 items.each(function(f){
8473 * @return {BasicForm} this
8476 var items = this.getItems();
8477 items.each(function(f){
8481 Roo.each(this.childForms || [], function (f) {
8489 getItems : function()
8491 var r=new Roo.util.MixedCollection(false, function(o){
8492 return o.id || (o.id = Roo.id());
8494 var iter = function(el) {
8501 Roo.each(el.items,function(e) {
8510 hideFields : function(items)
8512 Roo.each(items, function(i){
8514 var f = this.findField(i);
8525 showFields : function(items)
8527 Roo.each(items, function(i){
8529 var f = this.findField(i);
8542 Roo.apply(Roo.bootstrap.Form, {
8569 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8570 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8571 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8572 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8575 this.maskEl.top.enableDisplayMode("block");
8576 this.maskEl.left.enableDisplayMode("block");
8577 this.maskEl.bottom.enableDisplayMode("block");
8578 this.maskEl.right.enableDisplayMode("block");
8580 this.toolTip = new Roo.bootstrap.Tooltip({
8581 cls : 'roo-form-error-popover',
8583 'left' : ['r-l', [-2,0], 'right'],
8584 'right' : ['l-r', [2,0], 'left'],
8585 'bottom' : ['tl-bl', [0,2], 'top'],
8586 'top' : [ 'bl-tl', [0,-2], 'bottom']
8590 this.toolTip.render(Roo.get(document.body));
8592 this.toolTip.el.enableDisplayMode("block");
8594 Roo.get(document.body).on('click', function(){
8598 Roo.get(document.body).on('touchstart', function(){
8602 this.isApplied = true
8605 mask : function(form, target)
8609 this.target = target;
8611 if(!this.form.errorMask || !target.el){
8615 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8617 Roo.log(scrollable);
8619 var ot = this.target.el.calcOffsetsTo(scrollable);
8621 var scrollTo = ot[1] - this.form.maskOffset;
8623 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8625 scrollable.scrollTo('top', scrollTo);
8627 var box = this.target.el.getBox();
8629 var zIndex = Roo.bootstrap.Modal.zIndex++;
8632 this.maskEl.top.setStyle('position', 'absolute');
8633 this.maskEl.top.setStyle('z-index', zIndex);
8634 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8635 this.maskEl.top.setLeft(0);
8636 this.maskEl.top.setTop(0);
8637 this.maskEl.top.show();
8639 this.maskEl.left.setStyle('position', 'absolute');
8640 this.maskEl.left.setStyle('z-index', zIndex);
8641 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8642 this.maskEl.left.setLeft(0);
8643 this.maskEl.left.setTop(box.y - this.padding);
8644 this.maskEl.left.show();
8646 this.maskEl.bottom.setStyle('position', 'absolute');
8647 this.maskEl.bottom.setStyle('z-index', zIndex);
8648 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8649 this.maskEl.bottom.setLeft(0);
8650 this.maskEl.bottom.setTop(box.bottom + this.padding);
8651 this.maskEl.bottom.show();
8653 this.maskEl.right.setStyle('position', 'absolute');
8654 this.maskEl.right.setStyle('z-index', zIndex);
8655 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8656 this.maskEl.right.setLeft(box.right + this.padding);
8657 this.maskEl.right.setTop(box.y - this.padding);
8658 this.maskEl.right.show();
8660 this.toolTip.bindEl = this.target.el;
8662 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8664 var tip = this.target.blankText;
8666 if(this.target.getValue() !== '' ) {
8668 if (this.target.invalidText.length) {
8669 tip = this.target.invalidText;
8670 } else if (this.target.regexText.length){
8671 tip = this.target.regexText;
8675 this.toolTip.show(tip);
8677 this.intervalID = window.setInterval(function() {
8678 Roo.bootstrap.Form.popover.unmask();
8681 window.onwheel = function(){ return false;};
8683 (function(){ this.isMasked = true; }).defer(500, this);
8689 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8693 this.maskEl.top.setStyle('position', 'absolute');
8694 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8695 this.maskEl.top.hide();
8697 this.maskEl.left.setStyle('position', 'absolute');
8698 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8699 this.maskEl.left.hide();
8701 this.maskEl.bottom.setStyle('position', 'absolute');
8702 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8703 this.maskEl.bottom.hide();
8705 this.maskEl.right.setStyle('position', 'absolute');
8706 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8707 this.maskEl.right.hide();
8709 this.toolTip.hide();
8711 this.toolTip.el.hide();
8713 window.onwheel = function(){ return true;};
8715 if(this.intervalID){
8716 window.clearInterval(this.intervalID);
8717 this.intervalID = false;
8720 this.isMasked = false;
8730 * Ext JS Library 1.1.1
8731 * Copyright(c) 2006-2007, Ext JS, LLC.
8733 * Originally Released Under LGPL - original licence link has changed is not relivant.
8736 * <script type="text/javascript">
8739 * @class Roo.form.VTypes
8740 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8743 Roo.form.VTypes = function(){
8744 // closure these in so they are only created once.
8745 var alpha = /^[a-zA-Z_]+$/;
8746 var alphanum = /^[a-zA-Z0-9_]+$/;
8747 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8748 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8750 // All these messages and functions are configurable
8753 * The function used to validate email addresses
8754 * @param {String} value The email address
8756 'email' : function(v){
8757 return email.test(v);
8760 * The error text to display when the email validation function returns false
8763 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8765 * The keystroke filter mask to be applied on email input
8768 'emailMask' : /[a-z0-9_\.\-@]/i,
8771 * The function used to validate URLs
8772 * @param {String} value The URL
8774 'url' : function(v){
8778 * The error text to display when the url validation function returns false
8781 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8784 * The function used to validate alpha values
8785 * @param {String} value The value
8787 'alpha' : function(v){
8788 return alpha.test(v);
8791 * The error text to display when the alpha validation function returns false
8794 'alphaText' : 'This field should only contain letters and _',
8796 * The keystroke filter mask to be applied on alpha input
8799 'alphaMask' : /[a-z_]/i,
8802 * The function used to validate alphanumeric values
8803 * @param {String} value The value
8805 'alphanum' : function(v){
8806 return alphanum.test(v);
8809 * The error text to display when the alphanumeric validation function returns false
8812 'alphanumText' : 'This field should only contain letters, numbers and _',
8814 * The keystroke filter mask to be applied on alphanumeric input
8817 'alphanumMask' : /[a-z0-9_]/i
8827 * @class Roo.bootstrap.Input
8828 * @extends Roo.bootstrap.Component
8829 * Bootstrap Input class
8830 * @cfg {Boolean} disabled is it disabled
8831 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8832 * @cfg {String} name name of the input
8833 * @cfg {string} fieldLabel - the label associated
8834 * @cfg {string} placeholder - placeholder to put in text.
8835 * @cfg {string} before - input group add on before
8836 * @cfg {string} after - input group add on after
8837 * @cfg {string} size - (lg|sm) or leave empty..
8838 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8839 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8840 * @cfg {Number} md colspan out of 12 for computer-sized screens
8841 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8842 * @cfg {string} value default value of the input
8843 * @cfg {Number} labelWidth set the width of label
8844 * @cfg {Number} labellg set the width of label (1-12)
8845 * @cfg {Number} labelmd set the width of label (1-12)
8846 * @cfg {Number} labelsm set the width of label (1-12)
8847 * @cfg {Number} labelxs set the width of label (1-12)
8848 * @cfg {String} labelAlign (top|left)
8849 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8850 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8851 * @cfg {String} indicatorpos (left|right) default left
8852 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8853 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8855 * @cfg {String} align (left|center|right) Default left
8856 * @cfg {Boolean} forceFeedback (true|false) Default false
8859 * Create a new Input
8860 * @param {Object} config The config object
8863 Roo.bootstrap.Input = function(config){
8865 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8870 * Fires when this field receives input focus.
8871 * @param {Roo.form.Field} this
8876 * Fires when this field loses input focus.
8877 * @param {Roo.form.Field} this
8882 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8883 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8884 * @param {Roo.form.Field} this
8885 * @param {Roo.EventObject} e The event object
8890 * Fires just before the field blurs if the field value has changed.
8891 * @param {Roo.form.Field} this
8892 * @param {Mixed} newValue The new value
8893 * @param {Mixed} oldValue The original value
8898 * Fires after the field has been marked as invalid.
8899 * @param {Roo.form.Field} this
8900 * @param {String} msg The validation message
8905 * Fires after the field has been validated with no errors.
8906 * @param {Roo.form.Field} this
8911 * Fires after the key up
8912 * @param {Roo.form.Field} this
8913 * @param {Roo.EventObject} e The event Object
8919 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8921 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8922 automatic validation (defaults to "keyup").
8924 validationEvent : "keyup",
8926 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8928 validateOnBlur : true,
8930 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8932 validationDelay : 250,
8934 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8936 focusClass : "x-form-focus", // not needed???
8940 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8942 invalidClass : "has-warning",
8945 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8947 validClass : "has-success",
8950 * @cfg {Boolean} hasFeedback (true|false) default true
8955 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8957 invalidFeedbackClass : "glyphicon-warning-sign",
8960 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8962 validFeedbackClass : "glyphicon-ok",
8965 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8967 selectOnFocus : false,
8970 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8974 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8979 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8981 disableKeyFilter : false,
8984 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8988 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8992 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8994 blankText : "Please complete this mandatory field",
8997 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9001 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9003 maxLength : Number.MAX_VALUE,
9005 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9007 minLengthText : "The minimum length for this field is {0}",
9009 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9011 maxLengthText : "The maximum length for this field is {0}",
9015 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9016 * If available, this function will be called only after the basic validators all return true, and will be passed the
9017 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9021 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9022 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9023 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
9027 * @cfg {String} regexText -- Depricated - use Invalid Text
9032 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9038 autocomplete: false,
9057 formatedValue : false,
9058 forceFeedback : false,
9060 indicatorpos : 'left',
9070 parentLabelAlign : function()
9073 while (parent.parent()) {
9074 parent = parent.parent();
9075 if (typeof(parent.labelAlign) !='undefined') {
9076 return parent.labelAlign;
9083 getAutoCreate : function()
9085 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9091 if(this.inputType != 'hidden'){
9092 cfg.cls = 'form-group' //input-group
9098 type : this.inputType,
9100 cls : 'form-control',
9101 placeholder : this.placeholder || '',
9102 autocomplete : this.autocomplete || 'new-password'
9105 if(this.capture.length){
9106 input.capture = this.capture;
9109 if(this.accept.length){
9110 input.accept = this.accept + "/*";
9114 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9117 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9118 input.maxLength = this.maxLength;
9121 if (this.disabled) {
9122 input.disabled=true;
9125 if (this.readOnly) {
9126 input.readonly=true;
9130 input.name = this.name;
9134 input.cls += ' input-' + this.size;
9138 ['xs','sm','md','lg'].map(function(size){
9139 if (settings[size]) {
9140 cfg.cls += ' col-' + size + '-' + settings[size];
9144 var inputblock = input;
9148 cls: 'glyphicon form-control-feedback'
9151 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9154 cls : 'has-feedback',
9162 if (this.before || this.after) {
9165 cls : 'input-group',
9169 if (this.before && typeof(this.before) == 'string') {
9171 inputblock.cn.push({
9173 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9177 if (this.before && typeof(this.before) == 'object') {
9178 this.before = Roo.factory(this.before);
9180 inputblock.cn.push({
9182 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9183 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9187 inputblock.cn.push(input);
9189 if (this.after && typeof(this.after) == 'string') {
9190 inputblock.cn.push({
9192 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9196 if (this.after && typeof(this.after) == 'object') {
9197 this.after = Roo.factory(this.after);
9199 inputblock.cn.push({
9201 cls : 'roo-input-after input-group-append input-group-text input-group-' +
9202 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9206 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9207 inputblock.cls += ' has-feedback';
9208 inputblock.cn.push(feedback);
9213 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9214 tooltip : 'This field is required'
9216 if (Roo.bootstrap.version == 4) {
9219 style : 'display-none'
9222 if (align ==='left' && this.fieldLabel.length) {
9224 cfg.cls += ' roo-form-group-label-left row';
9231 cls : 'control-label col-form-label',
9232 html : this.fieldLabel
9243 var labelCfg = cfg.cn[1];
9244 var contentCfg = cfg.cn[2];
9246 if(this.indicatorpos == 'right'){
9251 cls : 'control-label col-form-label',
9255 html : this.fieldLabel
9269 labelCfg = cfg.cn[0];
9270 contentCfg = cfg.cn[1];
9274 if(this.labelWidth > 12){
9275 labelCfg.style = "width: " + this.labelWidth + 'px';
9278 if(this.labelWidth < 13 && this.labelmd == 0){
9279 this.labelmd = this.labelWidth;
9282 if(this.labellg > 0){
9283 labelCfg.cls += ' col-lg-' + this.labellg;
9284 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9287 if(this.labelmd > 0){
9288 labelCfg.cls += ' col-md-' + this.labelmd;
9289 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9292 if(this.labelsm > 0){
9293 labelCfg.cls += ' col-sm-' + this.labelsm;
9294 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9297 if(this.labelxs > 0){
9298 labelCfg.cls += ' col-xs-' + this.labelxs;
9299 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9303 } else if ( this.fieldLabel.length) {
9308 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9309 tooltip : 'This field is required'
9313 //cls : 'input-group-addon',
9314 html : this.fieldLabel
9322 if(this.indicatorpos == 'right'){
9327 //cls : 'input-group-addon',
9328 html : this.fieldLabel
9333 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9334 tooltip : 'This field is required'
9354 if (this.parentType === 'Navbar' && this.parent().bar) {
9355 cfg.cls += ' navbar-form';
9358 if (this.parentType === 'NavGroup') {
9359 cfg.cls += ' navbar-form';
9367 * return the real input element.
9369 inputEl: function ()
9371 return this.el.select('input.form-control',true).first();
9374 tooltipEl : function()
9376 return this.inputEl();
9379 indicatorEl : function()
9381 if (Roo.bootstrap.version == 4) {
9382 return false; // not enabled in v4 yet.
9385 var indicator = this.el.select('i.roo-required-indicator',true).first();
9395 setDisabled : function(v)
9397 var i = this.inputEl().dom;
9399 i.removeAttribute('disabled');
9403 i.setAttribute('disabled','true');
9405 initEvents : function()
9408 this.inputEl().on("keydown" , this.fireKey, this);
9409 this.inputEl().on("focus", this.onFocus, this);
9410 this.inputEl().on("blur", this.onBlur, this);
9412 this.inputEl().relayEvent('keyup', this);
9414 this.indicator = this.indicatorEl();
9417 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9420 // reference to original value for reset
9421 this.originalValue = this.getValue();
9422 //Roo.form.TextField.superclass.initEvents.call(this);
9423 if(this.validationEvent == 'keyup'){
9424 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9425 this.inputEl().on('keyup', this.filterValidation, this);
9427 else if(this.validationEvent !== false){
9428 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9431 if(this.selectOnFocus){
9432 this.on("focus", this.preFocus, this);
9435 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9436 this.inputEl().on("keypress", this.filterKeys, this);
9438 this.inputEl().relayEvent('keypress', this);
9441 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9442 this.el.on("click", this.autoSize, this);
9445 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9446 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9449 if (typeof(this.before) == 'object') {
9450 this.before.render(this.el.select('.roo-input-before',true).first());
9452 if (typeof(this.after) == 'object') {
9453 this.after.render(this.el.select('.roo-input-after',true).first());
9456 this.inputEl().on('change', this.onChange, this);
9459 filterValidation : function(e){
9460 if(!e.isNavKeyPress()){
9461 this.validationTask.delay(this.validationDelay);
9465 * Validates the field value
9466 * @return {Boolean} True if the value is valid, else false
9468 validate : function(){
9469 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9470 if(this.disabled || this.validateValue(this.getRawValue())){
9481 * Validates a value according to the field's validation rules and marks the field as invalid
9482 * if the validation fails
9483 * @param {Mixed} value The value to validate
9484 * @return {Boolean} True if the value is valid, else false
9486 validateValue : function(value)
9488 if(this.getVisibilityEl().hasClass('hidden')){
9492 if(value.length < 1) { // if it's blank
9493 if(this.allowBlank){
9499 if(value.length < this.minLength){
9502 if(value.length > this.maxLength){
9506 var vt = Roo.form.VTypes;
9507 if(!vt[this.vtype](value, this)){
9511 if(typeof this.validator == "function"){
9512 var msg = this.validator(value);
9516 if (typeof(msg) == 'string') {
9517 this.invalidText = msg;
9521 if(this.regex && !this.regex.test(value)){
9529 fireKey : function(e){
9530 //Roo.log('field ' + e.getKey());
9531 if(e.isNavKeyPress()){
9532 this.fireEvent("specialkey", this, e);
9535 focus : function (selectText){
9537 this.inputEl().focus();
9538 if(selectText === true){
9539 this.inputEl().dom.select();
9545 onFocus : function(){
9546 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9547 // this.el.addClass(this.focusClass);
9550 this.hasFocus = true;
9551 this.startValue = this.getValue();
9552 this.fireEvent("focus", this);
9556 beforeBlur : Roo.emptyFn,
9560 onBlur : function(){
9562 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9563 //this.el.removeClass(this.focusClass);
9565 this.hasFocus = false;
9566 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9569 var v = this.getValue();
9570 if(String(v) !== String(this.startValue)){
9571 this.fireEvent('change', this, v, this.startValue);
9573 this.fireEvent("blur", this);
9576 onChange : function(e)
9578 var v = this.getValue();
9579 if(String(v) !== String(this.startValue)){
9580 this.fireEvent('change', this, v, this.startValue);
9586 * Resets the current field value to the originally loaded value and clears any validation messages
9589 this.setValue(this.originalValue);
9593 * Returns the name of the field
9594 * @return {Mixed} name The name field
9596 getName: function(){
9600 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9601 * @return {Mixed} value The field value
9603 getValue : function(){
9605 var v = this.inputEl().getValue();
9610 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9611 * @return {Mixed} value The field value
9613 getRawValue : function(){
9614 var v = this.inputEl().getValue();
9620 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9621 * @param {Mixed} value The value to set
9623 setRawValue : function(v){
9624 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9627 selectText : function(start, end){
9628 var v = this.getRawValue();
9630 start = start === undefined ? 0 : start;
9631 end = end === undefined ? v.length : end;
9632 var d = this.inputEl().dom;
9633 if(d.setSelectionRange){
9634 d.setSelectionRange(start, end);
9635 }else if(d.createTextRange){
9636 var range = d.createTextRange();
9637 range.moveStart("character", start);
9638 range.moveEnd("character", v.length-end);
9645 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9646 * @param {Mixed} value The value to set
9648 setValue : function(v){
9651 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9657 processValue : function(value){
9658 if(this.stripCharsRe){
9659 var newValue = value.replace(this.stripCharsRe, '');
9660 if(newValue !== value){
9661 this.setRawValue(newValue);
9668 preFocus : function(){
9670 if(this.selectOnFocus){
9671 this.inputEl().dom.select();
9674 filterKeys : function(e){
9676 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9679 var c = e.getCharCode(), cc = String.fromCharCode(c);
9680 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9683 if(!this.maskRe.test(cc)){
9688 * Clear any invalid styles/messages for this field
9690 clearInvalid : function(){
9692 if(!this.el || this.preventMark){ // not rendered
9697 this.el.removeClass(this.invalidClass);
9699 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9701 var feedback = this.el.select('.form-control-feedback', true).first();
9704 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9710 this.indicator.removeClass('visible');
9711 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9714 this.fireEvent('valid', this);
9718 * Mark this field as valid
9720 markValid : function()
9722 if(!this.el || this.preventMark){ // not rendered...
9726 this.el.removeClass([this.invalidClass, this.validClass]);
9728 var feedback = this.el.select('.form-control-feedback', true).first();
9731 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9735 this.indicator.removeClass('visible');
9736 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9743 if(this.allowBlank && !this.getRawValue().length){
9747 this.el.addClass(this.validClass);
9749 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9751 var feedback = this.el.select('.form-control-feedback', true).first();
9754 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9755 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9760 this.fireEvent('valid', this);
9764 * Mark this field as invalid
9765 * @param {String} msg The validation message
9767 markInvalid : function(msg)
9769 if(!this.el || this.preventMark){ // not rendered
9773 this.el.removeClass([this.invalidClass, this.validClass]);
9775 var feedback = this.el.select('.form-control-feedback', true).first();
9778 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9785 if(this.allowBlank && !this.getRawValue().length){
9790 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9791 this.indicator.addClass('visible');
9794 this.el.addClass(this.invalidClass);
9796 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9798 var feedback = this.el.select('.form-control-feedback', true).first();
9801 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9803 if(this.getValue().length || this.forceFeedback){
9804 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9811 this.fireEvent('invalid', this, msg);
9814 SafariOnKeyDown : function(event)
9816 // this is a workaround for a password hang bug on chrome/ webkit.
9817 if (this.inputEl().dom.type != 'password') {
9821 var isSelectAll = false;
9823 if(this.inputEl().dom.selectionEnd > 0){
9824 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9826 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9827 event.preventDefault();
9832 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9834 event.preventDefault();
9835 // this is very hacky as keydown always get's upper case.
9837 var cc = String.fromCharCode(event.getCharCode());
9838 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9842 adjustWidth : function(tag, w){
9843 tag = tag.toLowerCase();
9844 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9845 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9849 if(tag == 'textarea'){
9852 }else if(Roo.isOpera){
9856 if(tag == 'textarea'){
9864 setFieldLabel : function(v)
9870 if(this.indicatorEl()){
9871 var ar = this.el.select('label > span',true);
9873 if (ar.elements.length) {
9874 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9875 this.fieldLabel = v;
9879 var br = this.el.select('label',true);
9881 if(br.elements.length) {
9882 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9883 this.fieldLabel = v;
9887 Roo.log('Cannot Found any of label > span || label in input');
9891 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9892 this.fieldLabel = v;
9907 * @class Roo.bootstrap.TextArea
9908 * @extends Roo.bootstrap.Input
9909 * Bootstrap TextArea class
9910 * @cfg {Number} cols Specifies the visible width of a text area
9911 * @cfg {Number} rows Specifies the visible number of lines in a text area
9912 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9913 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9914 * @cfg {string} html text
9917 * Create a new TextArea
9918 * @param {Object} config The config object
9921 Roo.bootstrap.TextArea = function(config){
9922 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9926 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9936 getAutoCreate : function(){
9938 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9944 if(this.inputType != 'hidden'){
9945 cfg.cls = 'form-group' //input-group
9953 value : this.value || '',
9954 html: this.html || '',
9955 cls : 'form-control',
9956 placeholder : this.placeholder || ''
9960 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9961 input.maxLength = this.maxLength;
9965 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9969 input.cols = this.cols;
9972 if (this.readOnly) {
9973 input.readonly = true;
9977 input.name = this.name;
9981 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9985 ['xs','sm','md','lg'].map(function(size){
9986 if (settings[size]) {
9987 cfg.cls += ' col-' + size + '-' + settings[size];
9991 var inputblock = input;
9993 if(this.hasFeedback && !this.allowBlank){
9997 cls: 'glyphicon form-control-feedback'
10001 cls : 'has-feedback',
10010 if (this.before || this.after) {
10013 cls : 'input-group',
10017 inputblock.cn.push({
10019 cls : 'input-group-addon',
10024 inputblock.cn.push(input);
10026 if(this.hasFeedback && !this.allowBlank){
10027 inputblock.cls += ' has-feedback';
10028 inputblock.cn.push(feedback);
10032 inputblock.cn.push({
10034 cls : 'input-group-addon',
10041 if (align ==='left' && this.fieldLabel.length) {
10046 cls : 'control-label',
10047 html : this.fieldLabel
10058 if(this.labelWidth > 12){
10059 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10062 if(this.labelWidth < 13 && this.labelmd == 0){
10063 this.labelmd = this.labelWidth;
10066 if(this.labellg > 0){
10067 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10068 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10071 if(this.labelmd > 0){
10072 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10073 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10076 if(this.labelsm > 0){
10077 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10078 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10081 if(this.labelxs > 0){
10082 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10083 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10086 } else if ( this.fieldLabel.length) {
10091 //cls : 'input-group-addon',
10092 html : this.fieldLabel
10110 if (this.disabled) {
10111 input.disabled=true;
10118 * return the real textarea element.
10120 inputEl: function ()
10122 return this.el.select('textarea.form-control',true).first();
10126 * Clear any invalid styles/messages for this field
10128 clearInvalid : function()
10131 if(!this.el || this.preventMark){ // not rendered
10135 var label = this.el.select('label', true).first();
10136 var icon = this.el.select('i.fa-star', true).first();
10142 this.el.removeClass(this.invalidClass);
10144 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10146 var feedback = this.el.select('.form-control-feedback', true).first();
10149 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10154 this.fireEvent('valid', this);
10158 * Mark this field as valid
10160 markValid : function()
10162 if(!this.el || this.preventMark){ // not rendered
10166 this.el.removeClass([this.invalidClass, this.validClass]);
10168 var feedback = this.el.select('.form-control-feedback', true).first();
10171 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10174 if(this.disabled || this.allowBlank){
10178 var label = this.el.select('label', true).first();
10179 var icon = this.el.select('i.fa-star', true).first();
10185 this.el.addClass(this.validClass);
10187 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10189 var feedback = this.el.select('.form-control-feedback', true).first();
10192 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10193 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10198 this.fireEvent('valid', this);
10202 * Mark this field as invalid
10203 * @param {String} msg The validation message
10205 markInvalid : function(msg)
10207 if(!this.el || this.preventMark){ // not rendered
10211 this.el.removeClass([this.invalidClass, this.validClass]);
10213 var feedback = this.el.select('.form-control-feedback', true).first();
10216 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10219 if(this.disabled || this.allowBlank){
10223 var label = this.el.select('label', true).first();
10224 var icon = this.el.select('i.fa-star', true).first();
10226 if(!this.getValue().length && label && !icon){
10227 this.el.createChild({
10229 cls : 'text-danger fa fa-lg fa-star',
10230 tooltip : 'This field is required',
10231 style : 'margin-right:5px;'
10235 this.el.addClass(this.invalidClass);
10237 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10239 var feedback = this.el.select('.form-control-feedback', true).first();
10242 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10244 if(this.getValue().length || this.forceFeedback){
10245 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10252 this.fireEvent('invalid', this, msg);
10260 * trigger field - base class for combo..
10265 * @class Roo.bootstrap.TriggerField
10266 * @extends Roo.bootstrap.Input
10267 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10268 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10269 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10270 * for which you can provide a custom implementation. For example:
10272 var trigger = new Roo.bootstrap.TriggerField();
10273 trigger.onTriggerClick = myTriggerFn;
10274 trigger.applyTo('my-field');
10277 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10278 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10279 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10280 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10281 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10284 * Create a new TriggerField.
10285 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10286 * to the base TextField)
10288 Roo.bootstrap.TriggerField = function(config){
10289 this.mimicing = false;
10290 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10293 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10295 * @cfg {String} triggerClass A CSS class to apply to the trigger
10298 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10303 * @cfg {Boolean} removable (true|false) special filter default false
10307 /** @cfg {Boolean} grow @hide */
10308 /** @cfg {Number} growMin @hide */
10309 /** @cfg {Number} growMax @hide */
10315 autoSize: Roo.emptyFn,
10319 deferHeight : true,
10322 actionMode : 'wrap',
10327 getAutoCreate : function(){
10329 var align = this.labelAlign || this.parentLabelAlign();
10334 cls: 'form-group' //input-group
10341 type : this.inputType,
10342 cls : 'form-control',
10343 autocomplete: 'new-password',
10344 placeholder : this.placeholder || ''
10348 input.name = this.name;
10351 input.cls += ' input-' + this.size;
10354 if (this.disabled) {
10355 input.disabled=true;
10358 var inputblock = input;
10360 if(this.hasFeedback && !this.allowBlank){
10364 cls: 'glyphicon form-control-feedback'
10367 if(this.removable && !this.editable && !this.tickable){
10369 cls : 'has-feedback',
10375 cls : 'roo-combo-removable-btn close'
10382 cls : 'has-feedback',
10391 if(this.removable && !this.editable && !this.tickable){
10393 cls : 'roo-removable',
10399 cls : 'roo-combo-removable-btn close'
10406 if (this.before || this.after) {
10409 cls : 'input-group',
10413 inputblock.cn.push({
10415 cls : 'input-group-addon input-group-prepend input-group-text',
10420 inputblock.cn.push(input);
10422 if(this.hasFeedback && !this.allowBlank){
10423 inputblock.cls += ' has-feedback';
10424 inputblock.cn.push(feedback);
10428 inputblock.cn.push({
10430 cls : 'input-group-addon input-group-append input-group-text',
10439 var ibwrap = inputblock;
10444 cls: 'roo-select2-choices',
10448 cls: 'roo-select2-search-field',
10460 cls: 'roo-select2-container input-group',
10465 cls: 'form-hidden-field'
10471 if(!this.multiple && this.showToggleBtn){
10477 if (this.caret != false) {
10480 cls: 'fa fa-' + this.caret
10487 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10492 cls: 'combobox-clear',
10506 combobox.cls += ' roo-select2-container-multi';
10510 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10511 tooltip : 'This field is required'
10513 if (Roo.bootstrap.version == 4) {
10516 style : 'display:none'
10521 if (align ==='left' && this.fieldLabel.length) {
10523 cfg.cls += ' roo-form-group-label-left row';
10530 cls : 'control-label',
10531 html : this.fieldLabel
10543 var labelCfg = cfg.cn[1];
10544 var contentCfg = cfg.cn[2];
10546 if(this.indicatorpos == 'right'){
10551 cls : 'control-label',
10555 html : this.fieldLabel
10569 labelCfg = cfg.cn[0];
10570 contentCfg = cfg.cn[1];
10573 if(this.labelWidth > 12){
10574 labelCfg.style = "width: " + this.labelWidth + 'px';
10577 if(this.labelWidth < 13 && this.labelmd == 0){
10578 this.labelmd = this.labelWidth;
10581 if(this.labellg > 0){
10582 labelCfg.cls += ' col-lg-' + this.labellg;
10583 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10586 if(this.labelmd > 0){
10587 labelCfg.cls += ' col-md-' + this.labelmd;
10588 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10591 if(this.labelsm > 0){
10592 labelCfg.cls += ' col-sm-' + this.labelsm;
10593 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10596 if(this.labelxs > 0){
10597 labelCfg.cls += ' col-xs-' + this.labelxs;
10598 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10601 } else if ( this.fieldLabel.length) {
10602 // Roo.log(" label");
10607 //cls : 'input-group-addon',
10608 html : this.fieldLabel
10616 if(this.indicatorpos == 'right'){
10624 html : this.fieldLabel
10638 // Roo.log(" no label && no align");
10645 ['xs','sm','md','lg'].map(function(size){
10646 if (settings[size]) {
10647 cfg.cls += ' col-' + size + '-' + settings[size];
10658 onResize : function(w, h){
10659 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10660 // if(typeof w == 'number'){
10661 // var x = w - this.trigger.getWidth();
10662 // this.inputEl().setWidth(this.adjustWidth('input', x));
10663 // this.trigger.setStyle('left', x+'px');
10668 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10671 getResizeEl : function(){
10672 return this.inputEl();
10676 getPositionEl : function(){
10677 return this.inputEl();
10681 alignErrorIcon : function(){
10682 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10686 initEvents : function(){
10690 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10691 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10692 if(!this.multiple && this.showToggleBtn){
10693 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10694 if(this.hideTrigger){
10695 this.trigger.setDisplayed(false);
10697 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10701 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10704 if(this.removable && !this.editable && !this.tickable){
10705 var close = this.closeTriggerEl();
10708 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10709 close.on('click', this.removeBtnClick, this, close);
10713 //this.trigger.addClassOnOver('x-form-trigger-over');
10714 //this.trigger.addClassOnClick('x-form-trigger-click');
10717 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10721 closeTriggerEl : function()
10723 var close = this.el.select('.roo-combo-removable-btn', true).first();
10724 return close ? close : false;
10727 removeBtnClick : function(e, h, el)
10729 e.preventDefault();
10731 if(this.fireEvent("remove", this) !== false){
10733 this.fireEvent("afterremove", this)
10737 createList : function()
10739 this.list = Roo.get(document.body).createChild({
10740 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10741 cls: 'typeahead typeahead-long dropdown-menu',
10742 style: 'display:none'
10745 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10750 initTrigger : function(){
10755 onDestroy : function(){
10757 this.trigger.removeAllListeners();
10758 // this.trigger.remove();
10761 // this.wrap.remove();
10763 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10767 onFocus : function(){
10768 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10770 if(!this.mimicing){
10771 this.wrap.addClass('x-trigger-wrap-focus');
10772 this.mimicing = true;
10773 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10774 if(this.monitorTab){
10775 this.el.on("keydown", this.checkTab, this);
10782 checkTab : function(e){
10783 if(e.getKey() == e.TAB){
10784 this.triggerBlur();
10789 onBlur : function(){
10794 mimicBlur : function(e, t){
10796 if(!this.wrap.contains(t) && this.validateBlur()){
10797 this.triggerBlur();
10803 triggerBlur : function(){
10804 this.mimicing = false;
10805 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10806 if(this.monitorTab){
10807 this.el.un("keydown", this.checkTab, this);
10809 //this.wrap.removeClass('x-trigger-wrap-focus');
10810 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10814 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10815 validateBlur : function(e, t){
10820 onDisable : function(){
10821 this.inputEl().dom.disabled = true;
10822 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10824 // this.wrap.addClass('x-item-disabled');
10829 onEnable : function(){
10830 this.inputEl().dom.disabled = false;
10831 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10833 // this.el.removeClass('x-item-disabled');
10838 onShow : function(){
10839 var ae = this.getActionEl();
10842 ae.dom.style.display = '';
10843 ae.dom.style.visibility = 'visible';
10849 onHide : function(){
10850 var ae = this.getActionEl();
10851 ae.dom.style.display = 'none';
10855 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10856 * by an implementing function.
10858 * @param {EventObject} e
10860 onTriggerClick : Roo.emptyFn
10864 * Ext JS Library 1.1.1
10865 * Copyright(c) 2006-2007, Ext JS, LLC.
10867 * Originally Released Under LGPL - original licence link has changed is not relivant.
10870 * <script type="text/javascript">
10875 * @class Roo.data.SortTypes
10877 * Defines the default sorting (casting?) comparison functions used when sorting data.
10879 Roo.data.SortTypes = {
10881 * Default sort that does nothing
10882 * @param {Mixed} s The value being converted
10883 * @return {Mixed} The comparison value
10885 none : function(s){
10890 * The regular expression used to strip tags
10894 stripTagsRE : /<\/?[^>]+>/gi,
10897 * Strips all HTML tags to sort on text only
10898 * @param {Mixed} s The value being converted
10899 * @return {String} The comparison value
10901 asText : function(s){
10902 return String(s).replace(this.stripTagsRE, "");
10906 * Strips all HTML tags to sort on text only - Case insensitive
10907 * @param {Mixed} s The value being converted
10908 * @return {String} The comparison value
10910 asUCText : function(s){
10911 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10915 * Case insensitive string
10916 * @param {Mixed} s The value being converted
10917 * @return {String} The comparison value
10919 asUCString : function(s) {
10920 return String(s).toUpperCase();
10925 * @param {Mixed} s The value being converted
10926 * @return {Number} The comparison value
10928 asDate : function(s) {
10932 if(s instanceof Date){
10933 return s.getTime();
10935 return Date.parse(String(s));
10940 * @param {Mixed} s The value being converted
10941 * @return {Float} The comparison value
10943 asFloat : function(s) {
10944 var val = parseFloat(String(s).replace(/,/g, ""));
10953 * @param {Mixed} s The value being converted
10954 * @return {Number} The comparison value
10956 asInt : function(s) {
10957 var val = parseInt(String(s).replace(/,/g, ""));
10965 * Ext JS Library 1.1.1
10966 * Copyright(c) 2006-2007, Ext JS, LLC.
10968 * Originally Released Under LGPL - original licence link has changed is not relivant.
10971 * <script type="text/javascript">
10975 * @class Roo.data.Record
10976 * Instances of this class encapsulate both record <em>definition</em> information, and record
10977 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10978 * to access Records cached in an {@link Roo.data.Store} object.<br>
10980 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10981 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10984 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10986 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10987 * {@link #create}. The parameters are the same.
10988 * @param {Array} data An associative Array of data values keyed by the field name.
10989 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10990 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10991 * not specified an integer id is generated.
10993 Roo.data.Record = function(data, id){
10994 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10999 * Generate a constructor for a specific record layout.
11000 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11001 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11002 * Each field definition object may contain the following properties: <ul>
11003 * <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,
11004 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11005 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11006 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11007 * is being used, then this is a string containing the javascript expression to reference the data relative to
11008 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11009 * to the data item relative to the record element. If the mapping expression is the same as the field name,
11010 * this may be omitted.</p></li>
11011 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11012 * <ul><li>auto (Default, implies no conversion)</li>
11017 * <li>date</li></ul></p></li>
11018 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11019 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11020 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11021 * by the Reader into an object that will be stored in the Record. It is passed the
11022 * following parameters:<ul>
11023 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11025 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11027 * <br>usage:<br><pre><code>
11028 var TopicRecord = Roo.data.Record.create(
11029 {name: 'title', mapping: 'topic_title'},
11030 {name: 'author', mapping: 'username'},
11031 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11032 {name: 'lastPost', mapping: 'post_time', type: 'date'},
11033 {name: 'lastPoster', mapping: 'user2'},
11034 {name: 'excerpt', mapping: 'post_text'}
11037 var myNewRecord = new TopicRecord({
11038 title: 'Do my job please',
11041 lastPost: new Date(),
11042 lastPoster: 'Animal',
11043 excerpt: 'No way dude!'
11045 myStore.add(myNewRecord);
11050 Roo.data.Record.create = function(o){
11051 var f = function(){
11052 f.superclass.constructor.apply(this, arguments);
11054 Roo.extend(f, Roo.data.Record);
11055 var p = f.prototype;
11056 p.fields = new Roo.util.MixedCollection(false, function(field){
11059 for(var i = 0, len = o.length; i < len; i++){
11060 p.fields.add(new Roo.data.Field(o[i]));
11062 f.getField = function(name){
11063 return p.fields.get(name);
11068 Roo.data.Record.AUTO_ID = 1000;
11069 Roo.data.Record.EDIT = 'edit';
11070 Roo.data.Record.REJECT = 'reject';
11071 Roo.data.Record.COMMIT = 'commit';
11073 Roo.data.Record.prototype = {
11075 * Readonly flag - true if this record has been modified.
11084 join : function(store){
11085 this.store = store;
11089 * Set the named field to the specified value.
11090 * @param {String} name The name of the field to set.
11091 * @param {Object} value The value to set the field to.
11093 set : function(name, value){
11094 if(this.data[name] == value){
11098 if(!this.modified){
11099 this.modified = {};
11101 if(typeof this.modified[name] == 'undefined'){
11102 this.modified[name] = this.data[name];
11104 this.data[name] = value;
11105 if(!this.editing && this.store){
11106 this.store.afterEdit(this);
11111 * Get the value of the named field.
11112 * @param {String} name The name of the field to get the value of.
11113 * @return {Object} The value of the field.
11115 get : function(name){
11116 return this.data[name];
11120 beginEdit : function(){
11121 this.editing = true;
11122 this.modified = {};
11126 cancelEdit : function(){
11127 this.editing = false;
11128 delete this.modified;
11132 endEdit : function(){
11133 this.editing = false;
11134 if(this.dirty && this.store){
11135 this.store.afterEdit(this);
11140 * Usually called by the {@link Roo.data.Store} which owns the Record.
11141 * Rejects all changes made to the Record since either creation, or the last commit operation.
11142 * Modified fields are reverted to their original values.
11144 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11145 * of reject operations.
11147 reject : function(){
11148 var m = this.modified;
11150 if(typeof m[n] != "function"){
11151 this.data[n] = m[n];
11154 this.dirty = false;
11155 delete this.modified;
11156 this.editing = false;
11158 this.store.afterReject(this);
11163 * Usually called by the {@link Roo.data.Store} which owns the Record.
11164 * Commits all changes made to the Record since either creation, or the last commit operation.
11166 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11167 * of commit operations.
11169 commit : function(){
11170 this.dirty = false;
11171 delete this.modified;
11172 this.editing = false;
11174 this.store.afterCommit(this);
11179 hasError : function(){
11180 return this.error != null;
11184 clearError : function(){
11189 * Creates a copy of this record.
11190 * @param {String} id (optional) A new record id if you don't want to use this record's id
11193 copy : function(newId) {
11194 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11198 * Ext JS Library 1.1.1
11199 * Copyright(c) 2006-2007, Ext JS, LLC.
11201 * Originally Released Under LGPL - original licence link has changed is not relivant.
11204 * <script type="text/javascript">
11210 * @class Roo.data.Store
11211 * @extends Roo.util.Observable
11212 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11213 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11215 * 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
11216 * has no knowledge of the format of the data returned by the Proxy.<br>
11218 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11219 * instances from the data object. These records are cached and made available through accessor functions.
11221 * Creates a new Store.
11222 * @param {Object} config A config object containing the objects needed for the Store to access data,
11223 * and read the data into Records.
11225 Roo.data.Store = function(config){
11226 this.data = new Roo.util.MixedCollection(false);
11227 this.data.getKey = function(o){
11230 this.baseParams = {};
11232 this.paramNames = {
11237 "multisort" : "_multisort"
11240 if(config && config.data){
11241 this.inlineData = config.data;
11242 delete config.data;
11245 Roo.apply(this, config);
11247 if(this.reader){ // reader passed
11248 this.reader = Roo.factory(this.reader, Roo.data);
11249 this.reader.xmodule = this.xmodule || false;
11250 if(!this.recordType){
11251 this.recordType = this.reader.recordType;
11253 if(this.reader.onMetaChange){
11254 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11258 if(this.recordType){
11259 this.fields = this.recordType.prototype.fields;
11261 this.modified = [];
11265 * @event datachanged
11266 * Fires when the data cache has changed, and a widget which is using this Store
11267 * as a Record cache should refresh its view.
11268 * @param {Store} this
11270 datachanged : true,
11272 * @event metachange
11273 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11274 * @param {Store} this
11275 * @param {Object} meta The JSON metadata
11280 * Fires when Records have been added to the Store
11281 * @param {Store} this
11282 * @param {Roo.data.Record[]} records The array of Records added
11283 * @param {Number} index The index at which the record(s) were added
11288 * Fires when a Record has been removed from the Store
11289 * @param {Store} this
11290 * @param {Roo.data.Record} record The Record that was removed
11291 * @param {Number} index The index at which the record was removed
11296 * Fires when a Record has been updated
11297 * @param {Store} this
11298 * @param {Roo.data.Record} record The Record that was updated
11299 * @param {String} operation The update operation being performed. Value may be one of:
11301 Roo.data.Record.EDIT
11302 Roo.data.Record.REJECT
11303 Roo.data.Record.COMMIT
11309 * Fires when the data cache has been cleared.
11310 * @param {Store} this
11314 * @event beforeload
11315 * Fires before a request is made for a new data object. If the beforeload handler returns false
11316 * the load action will be canceled.
11317 * @param {Store} this
11318 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11322 * @event beforeloadadd
11323 * Fires after a new set of Records has been loaded.
11324 * @param {Store} this
11325 * @param {Roo.data.Record[]} records The Records that were loaded
11326 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11328 beforeloadadd : true,
11331 * Fires after a new set of Records has been loaded, before they are added to the store.
11332 * @param {Store} this
11333 * @param {Roo.data.Record[]} records The Records that were loaded
11334 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11335 * @params {Object} return from reader
11339 * @event loadexception
11340 * Fires if an exception occurs in the Proxy during loading.
11341 * Called with the signature of the Proxy's "loadexception" event.
11342 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11345 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11346 * @param {Object} load options
11347 * @param {Object} jsonData from your request (normally this contains the Exception)
11349 loadexception : true
11353 this.proxy = Roo.factory(this.proxy, Roo.data);
11354 this.proxy.xmodule = this.xmodule || false;
11355 this.relayEvents(this.proxy, ["loadexception"]);
11357 this.sortToggle = {};
11358 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11360 Roo.data.Store.superclass.constructor.call(this);
11362 if(this.inlineData){
11363 this.loadData(this.inlineData);
11364 delete this.inlineData;
11368 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11370 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11371 * without a remote query - used by combo/forms at present.
11375 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11378 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11381 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11382 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11385 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11386 * on any HTTP request
11389 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11392 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11396 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11397 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11399 remoteSort : false,
11402 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11403 * loaded or when a record is removed. (defaults to false).
11405 pruneModifiedRecords : false,
11408 lastOptions : null,
11411 * Add Records to the Store and fires the add event.
11412 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11414 add : function(records){
11415 records = [].concat(records);
11416 for(var i = 0, len = records.length; i < len; i++){
11417 records[i].join(this);
11419 var index = this.data.length;
11420 this.data.addAll(records);
11421 this.fireEvent("add", this, records, index);
11425 * Remove a Record from the Store and fires the remove event.
11426 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11428 remove : function(record){
11429 var index = this.data.indexOf(record);
11430 this.data.removeAt(index);
11432 if(this.pruneModifiedRecords){
11433 this.modified.remove(record);
11435 this.fireEvent("remove", this, record, index);
11439 * Remove all Records from the Store and fires the clear event.
11441 removeAll : function(){
11443 if(this.pruneModifiedRecords){
11444 this.modified = [];
11446 this.fireEvent("clear", this);
11450 * Inserts Records to the Store at the given index and fires the add event.
11451 * @param {Number} index The start index at which to insert the passed Records.
11452 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11454 insert : function(index, records){
11455 records = [].concat(records);
11456 for(var i = 0, len = records.length; i < len; i++){
11457 this.data.insert(index, records[i]);
11458 records[i].join(this);
11460 this.fireEvent("add", this, records, index);
11464 * Get the index within the cache of the passed Record.
11465 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11466 * @return {Number} The index of the passed Record. Returns -1 if not found.
11468 indexOf : function(record){
11469 return this.data.indexOf(record);
11473 * Get the index within the cache of the Record with the passed id.
11474 * @param {String} id The id of the Record to find.
11475 * @return {Number} The index of the Record. Returns -1 if not found.
11477 indexOfId : function(id){
11478 return this.data.indexOfKey(id);
11482 * Get the Record with the specified id.
11483 * @param {String} id The id of the Record to find.
11484 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11486 getById : function(id){
11487 return this.data.key(id);
11491 * Get the Record at the specified index.
11492 * @param {Number} index The index of the Record to find.
11493 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11495 getAt : function(index){
11496 return this.data.itemAt(index);
11500 * Returns a range of Records between specified indices.
11501 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11502 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11503 * @return {Roo.data.Record[]} An array of Records
11505 getRange : function(start, end){
11506 return this.data.getRange(start, end);
11510 storeOptions : function(o){
11511 o = Roo.apply({}, o);
11514 this.lastOptions = o;
11518 * Loads the Record cache from the configured Proxy using the configured Reader.
11520 * If using remote paging, then the first load call must specify the <em>start</em>
11521 * and <em>limit</em> properties in the options.params property to establish the initial
11522 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11524 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11525 * and this call will return before the new data has been loaded. Perform any post-processing
11526 * in a callback function, or in a "load" event handler.</strong>
11528 * @param {Object} options An object containing properties which control loading options:<ul>
11529 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11530 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11531 * passed the following arguments:<ul>
11532 * <li>r : Roo.data.Record[]</li>
11533 * <li>options: Options object from the load call</li>
11534 * <li>success: Boolean success indicator</li></ul></li>
11535 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11536 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11539 load : function(options){
11540 options = options || {};
11541 if(this.fireEvent("beforeload", this, options) !== false){
11542 this.storeOptions(options);
11543 var p = Roo.apply(options.params || {}, this.baseParams);
11544 // if meta was not loaded from remote source.. try requesting it.
11545 if (!this.reader.metaFromRemote) {
11546 p._requestMeta = 1;
11548 if(this.sortInfo && this.remoteSort){
11549 var pn = this.paramNames;
11550 p[pn["sort"]] = this.sortInfo.field;
11551 p[pn["dir"]] = this.sortInfo.direction;
11553 if (this.multiSort) {
11554 var pn = this.paramNames;
11555 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11558 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11563 * Reloads the Record cache from the configured Proxy using the configured Reader and
11564 * the options from the last load operation performed.
11565 * @param {Object} options (optional) An object containing properties which may override the options
11566 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11567 * the most recently used options are reused).
11569 reload : function(options){
11570 this.load(Roo.applyIf(options||{}, this.lastOptions));
11574 // Called as a callback by the Reader during a load operation.
11575 loadRecords : function(o, options, success){
11576 if(!o || success === false){
11577 if(success !== false){
11578 this.fireEvent("load", this, [], options, o);
11580 if(options.callback){
11581 options.callback.call(options.scope || this, [], options, false);
11585 // if data returned failure - throw an exception.
11586 if (o.success === false) {
11587 // show a message if no listener is registered.
11588 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11589 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11591 // loadmask wil be hooked into this..
11592 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11595 var r = o.records, t = o.totalRecords || r.length;
11597 this.fireEvent("beforeloadadd", this, r, options, o);
11599 if(!options || options.add !== true){
11600 if(this.pruneModifiedRecords){
11601 this.modified = [];
11603 for(var i = 0, len = r.length; i < len; i++){
11607 this.data = this.snapshot;
11608 delete this.snapshot;
11611 this.data.addAll(r);
11612 this.totalLength = t;
11614 this.fireEvent("datachanged", this);
11616 this.totalLength = Math.max(t, this.data.length+r.length);
11620 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11622 var e = new Roo.data.Record({});
11624 e.set(this.parent.displayField, this.parent.emptyTitle);
11625 e.set(this.parent.valueField, '');
11630 this.fireEvent("load", this, r, options, o);
11631 if(options.callback){
11632 options.callback.call(options.scope || this, r, options, true);
11638 * Loads data from a passed data block. A Reader which understands the format of the data
11639 * must have been configured in the constructor.
11640 * @param {Object} data The data block from which to read the Records. The format of the data expected
11641 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11642 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11644 loadData : function(o, append){
11645 var r = this.reader.readRecords(o);
11646 this.loadRecords(r, {add: append}, true);
11650 * Gets the number of cached records.
11652 * <em>If using paging, this may not be the total size of the dataset. If the data object
11653 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11654 * the data set size</em>
11656 getCount : function(){
11657 return this.data.length || 0;
11661 * Gets the total number of records in the dataset as returned by the server.
11663 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11664 * the dataset size</em>
11666 getTotalCount : function(){
11667 return this.totalLength || 0;
11671 * Returns the sort state of the Store as an object with two properties:
11673 field {String} The name of the field by which the Records are sorted
11674 direction {String} The sort order, "ASC" or "DESC"
11677 getSortState : function(){
11678 return this.sortInfo;
11682 applySort : function(){
11683 if(this.sortInfo && !this.remoteSort){
11684 var s = this.sortInfo, f = s.field;
11685 var st = this.fields.get(f).sortType;
11686 var fn = function(r1, r2){
11687 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11688 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11690 this.data.sort(s.direction, fn);
11691 if(this.snapshot && this.snapshot != this.data){
11692 this.snapshot.sort(s.direction, fn);
11698 * Sets the default sort column and order to be used by the next load operation.
11699 * @param {String} fieldName The name of the field to sort by.
11700 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11702 setDefaultSort : function(field, dir){
11703 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11707 * Sort the Records.
11708 * If remote sorting is used, the sort is performed on the server, and the cache is
11709 * reloaded. If local sorting is used, the cache is sorted internally.
11710 * @param {String} fieldName The name of the field to sort by.
11711 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11713 sort : function(fieldName, dir){
11714 var f = this.fields.get(fieldName);
11716 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11718 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11719 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11724 this.sortToggle[f.name] = dir;
11725 this.sortInfo = {field: f.name, direction: dir};
11726 if(!this.remoteSort){
11728 this.fireEvent("datachanged", this);
11730 this.load(this.lastOptions);
11735 * Calls the specified function for each of the Records in the cache.
11736 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11737 * Returning <em>false</em> aborts and exits the iteration.
11738 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11740 each : function(fn, scope){
11741 this.data.each(fn, scope);
11745 * Gets all records modified since the last commit. Modified records are persisted across load operations
11746 * (e.g., during paging).
11747 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11749 getModifiedRecords : function(){
11750 return this.modified;
11754 createFilterFn : function(property, value, anyMatch){
11755 if(!value.exec){ // not a regex
11756 value = String(value);
11757 if(value.length == 0){
11760 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11762 return function(r){
11763 return value.test(r.data[property]);
11768 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11769 * @param {String} property A field on your records
11770 * @param {Number} start The record index to start at (defaults to 0)
11771 * @param {Number} end The last record index to include (defaults to length - 1)
11772 * @return {Number} The sum
11774 sum : function(property, start, end){
11775 var rs = this.data.items, v = 0;
11776 start = start || 0;
11777 end = (end || end === 0) ? end : rs.length-1;
11779 for(var i = start; i <= end; i++){
11780 v += (rs[i].data[property] || 0);
11786 * Filter the records by a specified property.
11787 * @param {String} field A field on your records
11788 * @param {String/RegExp} value Either a string that the field
11789 * should start with or a RegExp to test against the field
11790 * @param {Boolean} anyMatch True to match any part not just the beginning
11792 filter : function(property, value, anyMatch){
11793 var fn = this.createFilterFn(property, value, anyMatch);
11794 return fn ? this.filterBy(fn) : this.clearFilter();
11798 * Filter by a function. The specified function will be called with each
11799 * record in this data source. If the function returns true the record is included,
11800 * otherwise it is filtered.
11801 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11802 * @param {Object} scope (optional) The scope of the function (defaults to this)
11804 filterBy : function(fn, scope){
11805 this.snapshot = this.snapshot || this.data;
11806 this.data = this.queryBy(fn, scope||this);
11807 this.fireEvent("datachanged", this);
11811 * Query the records by a specified property.
11812 * @param {String} field A field on your records
11813 * @param {String/RegExp} value Either a string that the field
11814 * should start with or a RegExp to test against the field
11815 * @param {Boolean} anyMatch True to match any part not just the beginning
11816 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11818 query : function(property, value, anyMatch){
11819 var fn = this.createFilterFn(property, value, anyMatch);
11820 return fn ? this.queryBy(fn) : this.data.clone();
11824 * Query by a function. The specified function will be called with each
11825 * record in this data source. If the function returns true the record is included
11827 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11828 * @param {Object} scope (optional) The scope of the function (defaults to this)
11829 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11831 queryBy : function(fn, scope){
11832 var data = this.snapshot || this.data;
11833 return data.filterBy(fn, scope||this);
11837 * Collects unique values for a particular dataIndex from this store.
11838 * @param {String} dataIndex The property to collect
11839 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11840 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11841 * @return {Array} An array of the unique values
11843 collect : function(dataIndex, allowNull, bypassFilter){
11844 var d = (bypassFilter === true && this.snapshot) ?
11845 this.snapshot.items : this.data.items;
11846 var v, sv, r = [], l = {};
11847 for(var i = 0, len = d.length; i < len; i++){
11848 v = d[i].data[dataIndex];
11850 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11859 * Revert to a view of the Record cache with no filtering applied.
11860 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11862 clearFilter : function(suppressEvent){
11863 if(this.snapshot && this.snapshot != this.data){
11864 this.data = this.snapshot;
11865 delete this.snapshot;
11866 if(suppressEvent !== true){
11867 this.fireEvent("datachanged", this);
11873 afterEdit : function(record){
11874 if(this.modified.indexOf(record) == -1){
11875 this.modified.push(record);
11877 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11881 afterReject : function(record){
11882 this.modified.remove(record);
11883 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11887 afterCommit : function(record){
11888 this.modified.remove(record);
11889 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11893 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11894 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11896 commitChanges : function(){
11897 var m = this.modified.slice(0);
11898 this.modified = [];
11899 for(var i = 0, len = m.length; i < len; i++){
11905 * Cancel outstanding changes on all changed records.
11907 rejectChanges : function(){
11908 var m = this.modified.slice(0);
11909 this.modified = [];
11910 for(var i = 0, len = m.length; i < len; i++){
11915 onMetaChange : function(meta, rtype, o){
11916 this.recordType = rtype;
11917 this.fields = rtype.prototype.fields;
11918 delete this.snapshot;
11919 this.sortInfo = meta.sortInfo || this.sortInfo;
11920 this.modified = [];
11921 this.fireEvent('metachange', this, this.reader.meta);
11924 moveIndex : function(data, type)
11926 var index = this.indexOf(data);
11928 var newIndex = index + type;
11932 this.insert(newIndex, data);
11937 * Ext JS Library 1.1.1
11938 * Copyright(c) 2006-2007, Ext JS, LLC.
11940 * Originally Released Under LGPL - original licence link has changed is not relivant.
11943 * <script type="text/javascript">
11947 * @class Roo.data.SimpleStore
11948 * @extends Roo.data.Store
11949 * Small helper class to make creating Stores from Array data easier.
11950 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11951 * @cfg {Array} fields An array of field definition objects, or field name strings.
11952 * @cfg {Array} data The multi-dimensional array of data
11954 * @param {Object} config
11956 Roo.data.SimpleStore = function(config){
11957 Roo.data.SimpleStore.superclass.constructor.call(this, {
11959 reader: new Roo.data.ArrayReader({
11962 Roo.data.Record.create(config.fields)
11964 proxy : new Roo.data.MemoryProxy(config.data)
11968 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11970 * Ext JS Library 1.1.1
11971 * Copyright(c) 2006-2007, Ext JS, LLC.
11973 * Originally Released Under LGPL - original licence link has changed is not relivant.
11976 * <script type="text/javascript">
11981 * @extends Roo.data.Store
11982 * @class Roo.data.JsonStore
11983 * Small helper class to make creating Stores for JSON data easier. <br/>
11985 var store = new Roo.data.JsonStore({
11986 url: 'get-images.php',
11988 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11991 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11992 * JsonReader and HttpProxy (unless inline data is provided).</b>
11993 * @cfg {Array} fields An array of field definition objects, or field name strings.
11995 * @param {Object} config
11997 Roo.data.JsonStore = function(c){
11998 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11999 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12000 reader: new Roo.data.JsonReader(c, c.fields)
12003 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12005 * Ext JS Library 1.1.1
12006 * Copyright(c) 2006-2007, Ext JS, LLC.
12008 * Originally Released Under LGPL - original licence link has changed is not relivant.
12011 * <script type="text/javascript">
12015 Roo.data.Field = function(config){
12016 if(typeof config == "string"){
12017 config = {name: config};
12019 Roo.apply(this, config);
12022 this.type = "auto";
12025 var st = Roo.data.SortTypes;
12026 // named sortTypes are supported, here we look them up
12027 if(typeof this.sortType == "string"){
12028 this.sortType = st[this.sortType];
12031 // set default sortType for strings and dates
12032 if(!this.sortType){
12035 this.sortType = st.asUCString;
12038 this.sortType = st.asDate;
12041 this.sortType = st.none;
12046 var stripRe = /[\$,%]/g;
12048 // prebuilt conversion function for this field, instead of
12049 // switching every time we're reading a value
12051 var cv, dateFormat = this.dateFormat;
12056 cv = function(v){ return v; };
12059 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12063 return v !== undefined && v !== null && v !== '' ?
12064 parseInt(String(v).replace(stripRe, ""), 10) : '';
12069 return v !== undefined && v !== null && v !== '' ?
12070 parseFloat(String(v).replace(stripRe, ""), 10) : '';
12075 cv = function(v){ return v === true || v === "true" || v == 1; };
12082 if(v instanceof Date){
12086 if(dateFormat == "timestamp"){
12087 return new Date(v*1000);
12089 return Date.parseDate(v, dateFormat);
12091 var parsed = Date.parse(v);
12092 return parsed ? new Date(parsed) : null;
12101 Roo.data.Field.prototype = {
12109 * Ext JS Library 1.1.1
12110 * Copyright(c) 2006-2007, Ext JS, LLC.
12112 * Originally Released Under LGPL - original licence link has changed is not relivant.
12115 * <script type="text/javascript">
12118 // Base class for reading structured data from a data source. This class is intended to be
12119 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12122 * @class Roo.data.DataReader
12123 * Base class for reading structured data from a data source. This class is intended to be
12124 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12127 Roo.data.DataReader = function(meta, recordType){
12131 this.recordType = recordType instanceof Array ?
12132 Roo.data.Record.create(recordType) : recordType;
12135 Roo.data.DataReader.prototype = {
12137 * Create an empty record
12138 * @param {Object} data (optional) - overlay some values
12139 * @return {Roo.data.Record} record created.
12141 newRow : function(d) {
12143 this.recordType.prototype.fields.each(function(c) {
12145 case 'int' : da[c.name] = 0; break;
12146 case 'date' : da[c.name] = new Date(); break;
12147 case 'float' : da[c.name] = 0.0; break;
12148 case 'boolean' : da[c.name] = false; break;
12149 default : da[c.name] = ""; break;
12153 return new this.recordType(Roo.apply(da, d));
12158 * Ext JS Library 1.1.1
12159 * Copyright(c) 2006-2007, Ext JS, LLC.
12161 * Originally Released Under LGPL - original licence link has changed is not relivant.
12164 * <script type="text/javascript">
12168 * @class Roo.data.DataProxy
12169 * @extends Roo.data.Observable
12170 * This class is an abstract base class for implementations which provide retrieval of
12171 * unformatted data objects.<br>
12173 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12174 * (of the appropriate type which knows how to parse the data object) to provide a block of
12175 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12177 * Custom implementations must implement the load method as described in
12178 * {@link Roo.data.HttpProxy#load}.
12180 Roo.data.DataProxy = function(){
12183 * @event beforeload
12184 * Fires before a network request is made to retrieve a data object.
12185 * @param {Object} This DataProxy object.
12186 * @param {Object} params The params parameter to the load function.
12191 * Fires before the load method's callback is called.
12192 * @param {Object} This DataProxy object.
12193 * @param {Object} o The data object.
12194 * @param {Object} arg The callback argument object passed to the load function.
12198 * @event loadexception
12199 * Fires if an Exception occurs during data retrieval.
12200 * @param {Object} This DataProxy object.
12201 * @param {Object} o The data object.
12202 * @param {Object} arg The callback argument object passed to the load function.
12203 * @param {Object} e The Exception.
12205 loadexception : true
12207 Roo.data.DataProxy.superclass.constructor.call(this);
12210 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12213 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12217 * Ext JS Library 1.1.1
12218 * Copyright(c) 2006-2007, Ext JS, LLC.
12220 * Originally Released Under LGPL - original licence link has changed is not relivant.
12223 * <script type="text/javascript">
12226 * @class Roo.data.MemoryProxy
12227 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12228 * to the Reader when its load method is called.
12230 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12232 Roo.data.MemoryProxy = function(data){
12236 Roo.data.MemoryProxy.superclass.constructor.call(this);
12240 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12243 * Load data from the requested source (in this case an in-memory
12244 * data object passed to the constructor), read the data object into
12245 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12246 * process that block using the passed callback.
12247 * @param {Object} params This parameter is not used by the MemoryProxy class.
12248 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12249 * object into a block of Roo.data.Records.
12250 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12251 * The function must be passed <ul>
12252 * <li>The Record block object</li>
12253 * <li>The "arg" argument from the load function</li>
12254 * <li>A boolean success indicator</li>
12256 * @param {Object} scope The scope in which to call the callback
12257 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12259 load : function(params, reader, callback, scope, arg){
12260 params = params || {};
12263 result = reader.readRecords(this.data);
12265 this.fireEvent("loadexception", this, arg, null, e);
12266 callback.call(scope, null, arg, false);
12269 callback.call(scope, result, arg, true);
12273 update : function(params, records){
12278 * Ext JS Library 1.1.1
12279 * Copyright(c) 2006-2007, Ext JS, LLC.
12281 * Originally Released Under LGPL - original licence link has changed is not relivant.
12284 * <script type="text/javascript">
12287 * @class Roo.data.HttpProxy
12288 * @extends Roo.data.DataProxy
12289 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12290 * configured to reference a certain URL.<br><br>
12292 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12293 * from which the running page was served.<br><br>
12295 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12297 * Be aware that to enable the browser to parse an XML document, the server must set
12298 * the Content-Type header in the HTTP response to "text/xml".
12300 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12301 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12302 * will be used to make the request.
12304 Roo.data.HttpProxy = function(conn){
12305 Roo.data.HttpProxy.superclass.constructor.call(this);
12306 // is conn a conn config or a real conn?
12308 this.useAjax = !conn || !conn.events;
12312 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12313 // thse are take from connection...
12316 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12319 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12320 * extra parameters to each request made by this object. (defaults to undefined)
12323 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12324 * to each request made by this object. (defaults to undefined)
12327 * @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)
12330 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12333 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12339 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12343 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12344 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12345 * a finer-grained basis than the DataProxy events.
12347 getConnection : function(){
12348 return this.useAjax ? Roo.Ajax : this.conn;
12352 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12353 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12354 * process that block using the passed callback.
12355 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12356 * for the request to the remote server.
12357 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12358 * object into a block of Roo.data.Records.
12359 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12360 * The function must be passed <ul>
12361 * <li>The Record block object</li>
12362 * <li>The "arg" argument from the load function</li>
12363 * <li>A boolean success indicator</li>
12365 * @param {Object} scope The scope in which to call the callback
12366 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12368 load : function(params, reader, callback, scope, arg){
12369 if(this.fireEvent("beforeload", this, params) !== false){
12371 params : params || {},
12373 callback : callback,
12378 callback : this.loadResponse,
12382 Roo.applyIf(o, this.conn);
12383 if(this.activeRequest){
12384 Roo.Ajax.abort(this.activeRequest);
12386 this.activeRequest = Roo.Ajax.request(o);
12388 this.conn.request(o);
12391 callback.call(scope||this, null, arg, false);
12396 loadResponse : function(o, success, response){
12397 delete this.activeRequest;
12399 this.fireEvent("loadexception", this, o, response);
12400 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12405 result = o.reader.read(response);
12407 this.fireEvent("loadexception", this, o, response, e);
12408 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12412 this.fireEvent("load", this, o, o.request.arg);
12413 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12417 update : function(dataSet){
12422 updateResponse : function(dataSet){
12427 * Ext JS Library 1.1.1
12428 * Copyright(c) 2006-2007, Ext JS, LLC.
12430 * Originally Released Under LGPL - original licence link has changed is not relivant.
12433 * <script type="text/javascript">
12437 * @class Roo.data.ScriptTagProxy
12438 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12439 * other than the originating domain of the running page.<br><br>
12441 * <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
12442 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12444 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12445 * source code that is used as the source inside a <script> tag.<br><br>
12447 * In order for the browser to process the returned data, the server must wrap the data object
12448 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12449 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12450 * depending on whether the callback name was passed:
12453 boolean scriptTag = false;
12454 String cb = request.getParameter("callback");
12457 response.setContentType("text/javascript");
12459 response.setContentType("application/x-json");
12461 Writer out = response.getWriter();
12463 out.write(cb + "(");
12465 out.print(dataBlock.toJsonString());
12472 * @param {Object} config A configuration object.
12474 Roo.data.ScriptTagProxy = function(config){
12475 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12476 Roo.apply(this, config);
12477 this.head = document.getElementsByTagName("head")[0];
12480 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12482 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12484 * @cfg {String} url The URL from which to request the data object.
12487 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12491 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12492 * the server the name of the callback function set up by the load call to process the returned data object.
12493 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12494 * javascript output which calls this named function passing the data object as its only parameter.
12496 callbackParam : "callback",
12498 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12499 * name to the request.
12504 * Load data from the configured URL, read the data object into
12505 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12506 * process that block using the passed callback.
12507 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12508 * for the request to the remote server.
12509 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12510 * object into a block of Roo.data.Records.
12511 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12512 * The function must be passed <ul>
12513 * <li>The Record block object</li>
12514 * <li>The "arg" argument from the load function</li>
12515 * <li>A boolean success indicator</li>
12517 * @param {Object} scope The scope in which to call the callback
12518 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12520 load : function(params, reader, callback, scope, arg){
12521 if(this.fireEvent("beforeload", this, params) !== false){
12523 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12525 var url = this.url;
12526 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12528 url += "&_dc=" + (new Date().getTime());
12530 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12533 cb : "stcCallback"+transId,
12534 scriptId : "stcScript"+transId,
12538 callback : callback,
12544 window[trans.cb] = function(o){
12545 conn.handleResponse(o, trans);
12548 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12550 if(this.autoAbort !== false){
12554 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12556 var script = document.createElement("script");
12557 script.setAttribute("src", url);
12558 script.setAttribute("type", "text/javascript");
12559 script.setAttribute("id", trans.scriptId);
12560 this.head.appendChild(script);
12562 this.trans = trans;
12564 callback.call(scope||this, null, arg, false);
12569 isLoading : function(){
12570 return this.trans ? true : false;
12574 * Abort the current server request.
12576 abort : function(){
12577 if(this.isLoading()){
12578 this.destroyTrans(this.trans);
12583 destroyTrans : function(trans, isLoaded){
12584 this.head.removeChild(document.getElementById(trans.scriptId));
12585 clearTimeout(trans.timeoutId);
12587 window[trans.cb] = undefined;
12589 delete window[trans.cb];
12592 // if hasn't been loaded, wait for load to remove it to prevent script error
12593 window[trans.cb] = function(){
12594 window[trans.cb] = undefined;
12596 delete window[trans.cb];
12603 handleResponse : function(o, trans){
12604 this.trans = false;
12605 this.destroyTrans(trans, true);
12608 result = trans.reader.readRecords(o);
12610 this.fireEvent("loadexception", this, o, trans.arg, e);
12611 trans.callback.call(trans.scope||window, null, trans.arg, false);
12614 this.fireEvent("load", this, o, trans.arg);
12615 trans.callback.call(trans.scope||window, result, trans.arg, true);
12619 handleFailure : function(trans){
12620 this.trans = false;
12621 this.destroyTrans(trans, false);
12622 this.fireEvent("loadexception", this, null, trans.arg);
12623 trans.callback.call(trans.scope||window, null, trans.arg, false);
12627 * Ext JS Library 1.1.1
12628 * Copyright(c) 2006-2007, Ext JS, LLC.
12630 * Originally Released Under LGPL - original licence link has changed is not relivant.
12633 * <script type="text/javascript">
12637 * @class Roo.data.JsonReader
12638 * @extends Roo.data.DataReader
12639 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12640 * based on mappings in a provided Roo.data.Record constructor.
12642 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12643 * in the reply previously.
12648 var RecordDef = Roo.data.Record.create([
12649 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12650 {name: 'occupation'} // This field will use "occupation" as the mapping.
12652 var myReader = new Roo.data.JsonReader({
12653 totalProperty: "results", // The property which contains the total dataset size (optional)
12654 root: "rows", // The property which contains an Array of row objects
12655 id: "id" // The property within each row object that provides an ID for the record (optional)
12659 * This would consume a JSON file like this:
12661 { 'results': 2, 'rows': [
12662 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12663 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12666 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12667 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12668 * paged from the remote server.
12669 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12670 * @cfg {String} root name of the property which contains the Array of row objects.
12671 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12672 * @cfg {Array} fields Array of field definition objects
12674 * Create a new JsonReader
12675 * @param {Object} meta Metadata configuration options
12676 * @param {Object} recordType Either an Array of field definition objects,
12677 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12679 Roo.data.JsonReader = function(meta, recordType){
12682 // set some defaults:
12683 Roo.applyIf(meta, {
12684 totalProperty: 'total',
12685 successProperty : 'success',
12690 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12692 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12695 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12696 * Used by Store query builder to append _requestMeta to params.
12699 metaFromRemote : false,
12701 * This method is only used by a DataProxy which has retrieved data from a remote server.
12702 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12703 * @return {Object} data A data block which is used by an Roo.data.Store object as
12704 * a cache of Roo.data.Records.
12706 read : function(response){
12707 var json = response.responseText;
12709 var o = /* eval:var:o */ eval("("+json+")");
12711 throw {message: "JsonReader.read: Json object not found"};
12717 this.metaFromRemote = true;
12718 this.meta = o.metaData;
12719 this.recordType = Roo.data.Record.create(o.metaData.fields);
12720 this.onMetaChange(this.meta, this.recordType, o);
12722 return this.readRecords(o);
12725 // private function a store will implement
12726 onMetaChange : function(meta, recordType, o){
12733 simpleAccess: function(obj, subsc) {
12740 getJsonAccessor: function(){
12742 return function(expr) {
12744 return(re.test(expr))
12745 ? new Function("obj", "return obj." + expr)
12750 return Roo.emptyFn;
12755 * Create a data block containing Roo.data.Records from an XML document.
12756 * @param {Object} o An object which contains an Array of row objects in the property specified
12757 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12758 * which contains the total size of the dataset.
12759 * @return {Object} data A data block which is used by an Roo.data.Store object as
12760 * a cache of Roo.data.Records.
12762 readRecords : function(o){
12764 * After any data loads, the raw JSON data is available for further custom processing.
12768 var s = this.meta, Record = this.recordType,
12769 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12771 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12773 if(s.totalProperty) {
12774 this.getTotal = this.getJsonAccessor(s.totalProperty);
12776 if(s.successProperty) {
12777 this.getSuccess = this.getJsonAccessor(s.successProperty);
12779 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12781 var g = this.getJsonAccessor(s.id);
12782 this.getId = function(rec) {
12784 return (r === undefined || r === "") ? null : r;
12787 this.getId = function(){return null;};
12790 for(var jj = 0; jj < fl; jj++){
12792 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12793 this.ef[jj] = this.getJsonAccessor(map);
12797 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12798 if(s.totalProperty){
12799 var vt = parseInt(this.getTotal(o), 10);
12804 if(s.successProperty){
12805 var vs = this.getSuccess(o);
12806 if(vs === false || vs === 'false'){
12811 for(var i = 0; i < c; i++){
12814 var id = this.getId(n);
12815 for(var j = 0; j < fl; j++){
12817 var v = this.ef[j](n);
12819 Roo.log('missing convert for ' + f.name);
12823 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12825 var record = new Record(values, id);
12827 records[i] = record;
12833 totalRecords : totalRecords
12838 * Ext JS Library 1.1.1
12839 * Copyright(c) 2006-2007, Ext JS, LLC.
12841 * Originally Released Under LGPL - original licence link has changed is not relivant.
12844 * <script type="text/javascript">
12848 * @class Roo.data.ArrayReader
12849 * @extends Roo.data.DataReader
12850 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12851 * Each element of that Array represents a row of data fields. The
12852 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12853 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12857 var RecordDef = Roo.data.Record.create([
12858 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12859 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12861 var myReader = new Roo.data.ArrayReader({
12862 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12866 * This would consume an Array like this:
12868 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12870 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12872 * Create a new JsonReader
12873 * @param {Object} meta Metadata configuration options.
12874 * @param {Object} recordType Either an Array of field definition objects
12875 * as specified to {@link Roo.data.Record#create},
12876 * or an {@link Roo.data.Record} object
12877 * created using {@link Roo.data.Record#create}.
12879 Roo.data.ArrayReader = function(meta, recordType){
12880 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12883 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12885 * Create a data block containing Roo.data.Records from an XML document.
12886 * @param {Object} o An Array of row objects which represents the dataset.
12887 * @return {Object} data A data block which is used by an Roo.data.Store object as
12888 * a cache of Roo.data.Records.
12890 readRecords : function(o){
12891 var sid = this.meta ? this.meta.id : null;
12892 var recordType = this.recordType, fields = recordType.prototype.fields;
12895 for(var i = 0; i < root.length; i++){
12898 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12899 for(var j = 0, jlen = fields.length; j < jlen; j++){
12900 var f = fields.items[j];
12901 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12902 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12904 values[f.name] = v;
12906 var record = new recordType(values, id);
12908 records[records.length] = record;
12912 totalRecords : records.length
12921 * @class Roo.bootstrap.ComboBox
12922 * @extends Roo.bootstrap.TriggerField
12923 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12924 * @cfg {Boolean} append (true|false) default false
12925 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12926 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12927 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12928 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12929 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12930 * @cfg {Boolean} animate default true
12931 * @cfg {Boolean} emptyResultText only for touch device
12932 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12933 * @cfg {String} emptyTitle default ''
12935 * Create a new ComboBox.
12936 * @param {Object} config Configuration options
12938 Roo.bootstrap.ComboBox = function(config){
12939 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12943 * Fires when the dropdown list is expanded
12944 * @param {Roo.bootstrap.ComboBox} combo This combo box
12949 * Fires when the dropdown list is collapsed
12950 * @param {Roo.bootstrap.ComboBox} combo This combo box
12954 * @event beforeselect
12955 * Fires before a list item is selected. Return false to cancel the selection.
12956 * @param {Roo.bootstrap.ComboBox} combo This combo box
12957 * @param {Roo.data.Record} record The data record returned from the underlying store
12958 * @param {Number} index The index of the selected item in the dropdown list
12960 'beforeselect' : true,
12963 * Fires when a list item is selected
12964 * @param {Roo.bootstrap.ComboBox} combo This combo box
12965 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12966 * @param {Number} index The index of the selected item in the dropdown list
12970 * @event beforequery
12971 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12972 * The event object passed has these properties:
12973 * @param {Roo.bootstrap.ComboBox} combo This combo box
12974 * @param {String} query The query
12975 * @param {Boolean} forceAll true to force "all" query
12976 * @param {Boolean} cancel true to cancel the query
12977 * @param {Object} e The query event object
12979 'beforequery': true,
12982 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12983 * @param {Roo.bootstrap.ComboBox} combo This combo box
12988 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12989 * @param {Roo.bootstrap.ComboBox} combo This combo box
12990 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12995 * Fires when the remove value from the combobox array
12996 * @param {Roo.bootstrap.ComboBox} combo This combo box
13000 * @event afterremove
13001 * Fires when the remove value from the combobox array
13002 * @param {Roo.bootstrap.ComboBox} combo This combo box
13004 'afterremove' : true,
13006 * @event specialfilter
13007 * Fires when specialfilter
13008 * @param {Roo.bootstrap.ComboBox} combo This combo box
13010 'specialfilter' : true,
13013 * Fires when tick the element
13014 * @param {Roo.bootstrap.ComboBox} combo This combo box
13018 * @event touchviewdisplay
13019 * Fires when touch view require special display (default is using displayField)
13020 * @param {Roo.bootstrap.ComboBox} combo This combo box
13021 * @param {Object} cfg set html .
13023 'touchviewdisplay' : true
13028 this.tickItems = [];
13030 this.selectedIndex = -1;
13031 if(this.mode == 'local'){
13032 if(config.queryDelay === undefined){
13033 this.queryDelay = 10;
13035 if(config.minChars === undefined){
13041 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13044 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13045 * rendering into an Roo.Editor, defaults to false)
13048 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13049 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13052 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13055 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13056 * the dropdown list (defaults to undefined, with no header element)
13060 * @cfg {String/Roo.Template} tpl The template to use to render the output
13064 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13066 listWidth: undefined,
13068 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13069 * mode = 'remote' or 'text' if mode = 'local')
13071 displayField: undefined,
13074 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13075 * mode = 'remote' or 'value' if mode = 'local').
13076 * Note: use of a valueField requires the user make a selection
13077 * in order for a value to be mapped.
13079 valueField: undefined,
13081 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13086 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13087 * field's data value (defaults to the underlying DOM element's name)
13089 hiddenName: undefined,
13091 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13095 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13097 selectedClass: 'active',
13100 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13104 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13105 * anchor positions (defaults to 'tl-bl')
13107 listAlign: 'tl-bl?',
13109 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13113 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13114 * query specified by the allQuery config option (defaults to 'query')
13116 triggerAction: 'query',
13118 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13119 * (defaults to 4, does not apply if editable = false)
13123 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13124 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13128 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13129 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13133 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13134 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13138 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13139 * when editable = true (defaults to false)
13141 selectOnFocus:false,
13143 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13145 queryParam: 'query',
13147 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13148 * when mode = 'remote' (defaults to 'Loading...')
13150 loadingText: 'Loading...',
13152 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13156 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13160 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13161 * traditional select (defaults to true)
13165 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13169 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13173 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13174 * listWidth has a higher value)
13178 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13179 * allow the user to set arbitrary text into the field (defaults to false)
13181 forceSelection:false,
13183 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13184 * if typeAhead = true (defaults to 250)
13186 typeAheadDelay : 250,
13188 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13189 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13191 valueNotFoundText : undefined,
13193 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13195 blockFocus : false,
13198 * @cfg {Boolean} disableClear Disable showing of clear button.
13200 disableClear : false,
13202 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13204 alwaysQuery : false,
13207 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13212 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13214 invalidClass : "has-warning",
13217 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13219 validClass : "has-success",
13222 * @cfg {Boolean} specialFilter (true|false) special filter default false
13224 specialFilter : false,
13227 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13229 mobileTouchView : true,
13232 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13234 useNativeIOS : false,
13237 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13239 mobile_restrict_height : false,
13241 ios_options : false,
13253 btnPosition : 'right',
13254 triggerList : true,
13255 showToggleBtn : true,
13257 emptyResultText: 'Empty',
13258 triggerText : 'Select',
13261 // element that contains real text value.. (when hidden is used..)
13263 getAutoCreate : function()
13268 * Render classic select for iso
13271 if(Roo.isIOS && this.useNativeIOS){
13272 cfg = this.getAutoCreateNativeIOS();
13280 if(Roo.isTouch && this.mobileTouchView){
13281 cfg = this.getAutoCreateTouchView();
13288 if(!this.tickable){
13289 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13294 * ComboBox with tickable selections
13297 var align = this.labelAlign || this.parentLabelAlign();
13300 cls : 'form-group roo-combobox-tickable' //input-group
13303 var btn_text_select = '';
13304 var btn_text_done = '';
13305 var btn_text_cancel = '';
13307 if (this.btn_text_show) {
13308 btn_text_select = 'Select';
13309 btn_text_done = 'Done';
13310 btn_text_cancel = 'Cancel';
13315 cls : 'tickable-buttons',
13320 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13321 //html : this.triggerText
13322 html: btn_text_select
13328 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13330 html: btn_text_done
13336 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13338 html: btn_text_cancel
13344 buttons.cn.unshift({
13346 cls: 'roo-select2-search-field-input'
13352 Roo.each(buttons.cn, function(c){
13354 c.cls += ' btn-' + _this.size;
13357 if (_this.disabled) {
13368 cls: 'form-hidden-field'
13372 cls: 'roo-select2-choices',
13376 cls: 'roo-select2-search-field',
13387 cls: 'roo-select2-container input-group roo-select2-container-multi',
13393 // cls: 'typeahead typeahead-long dropdown-menu',
13394 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13399 if(this.hasFeedback && !this.allowBlank){
13403 cls: 'glyphicon form-control-feedback'
13406 combobox.cn.push(feedback);
13411 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13412 tooltip : 'This field is required'
13414 if (Roo.bootstrap.version == 4) {
13417 style : 'display:none'
13420 if (align ==='left' && this.fieldLabel.length) {
13422 cfg.cls += ' roo-form-group-label-left row';
13429 cls : 'control-label col-form-label',
13430 html : this.fieldLabel
13442 var labelCfg = cfg.cn[1];
13443 var contentCfg = cfg.cn[2];
13446 if(this.indicatorpos == 'right'){
13452 cls : 'control-label col-form-label',
13456 html : this.fieldLabel
13472 labelCfg = cfg.cn[0];
13473 contentCfg = cfg.cn[1];
13477 if(this.labelWidth > 12){
13478 labelCfg.style = "width: " + this.labelWidth + 'px';
13481 if(this.labelWidth < 13 && this.labelmd == 0){
13482 this.labelmd = this.labelWidth;
13485 if(this.labellg > 0){
13486 labelCfg.cls += ' col-lg-' + this.labellg;
13487 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13490 if(this.labelmd > 0){
13491 labelCfg.cls += ' col-md-' + this.labelmd;
13492 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13495 if(this.labelsm > 0){
13496 labelCfg.cls += ' col-sm-' + this.labelsm;
13497 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13500 if(this.labelxs > 0){
13501 labelCfg.cls += ' col-xs-' + this.labelxs;
13502 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13506 } else if ( this.fieldLabel.length) {
13507 // Roo.log(" label");
13512 //cls : 'input-group-addon',
13513 html : this.fieldLabel
13518 if(this.indicatorpos == 'right'){
13522 //cls : 'input-group-addon',
13523 html : this.fieldLabel
13533 // Roo.log(" no label && no align");
13540 ['xs','sm','md','lg'].map(function(size){
13541 if (settings[size]) {
13542 cfg.cls += ' col-' + size + '-' + settings[size];
13550 _initEventsCalled : false,
13553 initEvents: function()
13555 if (this._initEventsCalled) { // as we call render... prevent looping...
13558 this._initEventsCalled = true;
13561 throw "can not find store for combo";
13564 this.indicator = this.indicatorEl();
13566 this.store = Roo.factory(this.store, Roo.data);
13567 this.store.parent = this;
13569 // if we are building from html. then this element is so complex, that we can not really
13570 // use the rendered HTML.
13571 // so we have to trash and replace the previous code.
13572 if (Roo.XComponent.build_from_html) {
13573 // remove this element....
13574 var e = this.el.dom, k=0;
13575 while (e ) { e = e.previousSibling; ++k;}
13580 this.rendered = false;
13582 this.render(this.parent().getChildContainer(true), k);
13585 if(Roo.isIOS && this.useNativeIOS){
13586 this.initIOSView();
13594 if(Roo.isTouch && this.mobileTouchView){
13595 this.initTouchView();
13600 this.initTickableEvents();
13604 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13606 if(this.hiddenName){
13608 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13610 this.hiddenField.dom.value =
13611 this.hiddenValue !== undefined ? this.hiddenValue :
13612 this.value !== undefined ? this.value : '';
13614 // prevent input submission
13615 this.el.dom.removeAttribute('name');
13616 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13621 // this.el.dom.setAttribute('autocomplete', 'off');
13624 var cls = 'x-combo-list';
13626 //this.list = new Roo.Layer({
13627 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13633 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13634 _this.list.setWidth(lw);
13637 this.list.on('mouseover', this.onViewOver, this);
13638 this.list.on('mousemove', this.onViewMove, this);
13639 this.list.on('scroll', this.onViewScroll, this);
13642 this.list.swallowEvent('mousewheel');
13643 this.assetHeight = 0;
13646 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13647 this.assetHeight += this.header.getHeight();
13650 this.innerList = this.list.createChild({cls:cls+'-inner'});
13651 this.innerList.on('mouseover', this.onViewOver, this);
13652 this.innerList.on('mousemove', this.onViewMove, this);
13653 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13655 if(this.allowBlank && !this.pageSize && !this.disableClear){
13656 this.footer = this.list.createChild({cls:cls+'-ft'});
13657 this.pageTb = new Roo.Toolbar(this.footer);
13661 this.footer = this.list.createChild({cls:cls+'-ft'});
13662 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13663 {pageSize: this.pageSize});
13667 if (this.pageTb && this.allowBlank && !this.disableClear) {
13669 this.pageTb.add(new Roo.Toolbar.Fill(), {
13670 cls: 'x-btn-icon x-btn-clear',
13672 handler: function()
13675 _this.clearValue();
13676 _this.onSelect(false, -1);
13681 this.assetHeight += this.footer.getHeight();
13686 this.tpl = Roo.bootstrap.version == 4 ?
13687 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
13688 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13691 this.view = new Roo.View(this.list, this.tpl, {
13692 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13694 //this.view.wrapEl.setDisplayed(false);
13695 this.view.on('click', this.onViewClick, this);
13698 this.store.on('beforeload', this.onBeforeLoad, this);
13699 this.store.on('load', this.onLoad, this);
13700 this.store.on('loadexception', this.onLoadException, this);
13702 if(this.resizable){
13703 this.resizer = new Roo.Resizable(this.list, {
13704 pinned:true, handles:'se'
13706 this.resizer.on('resize', function(r, w, h){
13707 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13708 this.listWidth = w;
13709 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13710 this.restrictHeight();
13712 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13715 if(!this.editable){
13716 this.editable = true;
13717 this.setEditable(false);
13722 if (typeof(this.events.add.listeners) != 'undefined') {
13724 this.addicon = this.wrap.createChild(
13725 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13727 this.addicon.on('click', function(e) {
13728 this.fireEvent('add', this);
13731 if (typeof(this.events.edit.listeners) != 'undefined') {
13733 this.editicon = this.wrap.createChild(
13734 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13735 if (this.addicon) {
13736 this.editicon.setStyle('margin-left', '40px');
13738 this.editicon.on('click', function(e) {
13740 // we fire even if inothing is selected..
13741 this.fireEvent('edit', this, this.lastData );
13747 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13748 "up" : function(e){
13749 this.inKeyMode = true;
13753 "down" : function(e){
13754 if(!this.isExpanded()){
13755 this.onTriggerClick();
13757 this.inKeyMode = true;
13762 "enter" : function(e){
13763 // this.onViewClick();
13767 if(this.fireEvent("specialkey", this, e)){
13768 this.onViewClick(false);
13774 "esc" : function(e){
13778 "tab" : function(e){
13781 if(this.fireEvent("specialkey", this, e)){
13782 this.onViewClick(false);
13790 doRelay : function(foo, bar, hname){
13791 if(hname == 'down' || this.scope.isExpanded()){
13792 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13801 this.queryDelay = Math.max(this.queryDelay || 10,
13802 this.mode == 'local' ? 10 : 250);
13805 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13807 if(this.typeAhead){
13808 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13810 if(this.editable !== false){
13811 this.inputEl().on("keyup", this.onKeyUp, this);
13813 if(this.forceSelection){
13814 this.inputEl().on('blur', this.doForce, this);
13818 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13819 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13823 initTickableEvents: function()
13827 if(this.hiddenName){
13829 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13831 this.hiddenField.dom.value =
13832 this.hiddenValue !== undefined ? this.hiddenValue :
13833 this.value !== undefined ? this.value : '';
13835 // prevent input submission
13836 this.el.dom.removeAttribute('name');
13837 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13842 // this.list = this.el.select('ul.dropdown-menu',true).first();
13844 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13845 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13846 if(this.triggerList){
13847 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13850 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13851 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13853 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13854 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13856 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13857 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13859 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13860 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13861 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13864 this.cancelBtn.hide();
13869 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13870 _this.list.setWidth(lw);
13873 this.list.on('mouseover', this.onViewOver, this);
13874 this.list.on('mousemove', this.onViewMove, this);
13876 this.list.on('scroll', this.onViewScroll, this);
13879 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13880 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13883 this.view = new Roo.View(this.list, this.tpl, {
13888 selectedClass: this.selectedClass
13891 //this.view.wrapEl.setDisplayed(false);
13892 this.view.on('click', this.onViewClick, this);
13896 this.store.on('beforeload', this.onBeforeLoad, this);
13897 this.store.on('load', this.onLoad, this);
13898 this.store.on('loadexception', this.onLoadException, this);
13901 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13902 "up" : function(e){
13903 this.inKeyMode = true;
13907 "down" : function(e){
13908 this.inKeyMode = true;
13912 "enter" : function(e){
13913 if(this.fireEvent("specialkey", this, e)){
13914 this.onViewClick(false);
13920 "esc" : function(e){
13921 this.onTickableFooterButtonClick(e, false, false);
13924 "tab" : function(e){
13925 this.fireEvent("specialkey", this, e);
13927 this.onTickableFooterButtonClick(e, false, false);
13934 doRelay : function(e, fn, key){
13935 if(this.scope.isExpanded()){
13936 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13945 this.queryDelay = Math.max(this.queryDelay || 10,
13946 this.mode == 'local' ? 10 : 250);
13949 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13951 if(this.typeAhead){
13952 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13955 if(this.editable !== false){
13956 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13959 this.indicator = this.indicatorEl();
13961 if(this.indicator){
13962 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13963 this.indicator.hide();
13968 onDestroy : function(){
13970 this.view.setStore(null);
13971 this.view.el.removeAllListeners();
13972 this.view.el.remove();
13973 this.view.purgeListeners();
13976 this.list.dom.innerHTML = '';
13980 this.store.un('beforeload', this.onBeforeLoad, this);
13981 this.store.un('load', this.onLoad, this);
13982 this.store.un('loadexception', this.onLoadException, this);
13984 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13988 fireKey : function(e){
13989 if(e.isNavKeyPress() && !this.list.isVisible()){
13990 this.fireEvent("specialkey", this, e);
13995 onResize: function(w, h){
13996 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13998 // if(typeof w != 'number'){
13999 // // we do not handle it!?!?
14002 // var tw = this.trigger.getWidth();
14003 // // tw += this.addicon ? this.addicon.getWidth() : 0;
14004 // // tw += this.editicon ? this.editicon.getWidth() : 0;
14006 // this.inputEl().setWidth( this.adjustWidth('input', x));
14008 // //this.trigger.setStyle('left', x+'px');
14010 // if(this.list && this.listWidth === undefined){
14011 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14012 // this.list.setWidth(lw);
14013 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14021 * Allow or prevent the user from directly editing the field text. If false is passed,
14022 * the user will only be able to select from the items defined in the dropdown list. This method
14023 * is the runtime equivalent of setting the 'editable' config option at config time.
14024 * @param {Boolean} value True to allow the user to directly edit the field text
14026 setEditable : function(value){
14027 if(value == this.editable){
14030 this.editable = value;
14032 this.inputEl().dom.setAttribute('readOnly', true);
14033 this.inputEl().on('mousedown', this.onTriggerClick, this);
14034 this.inputEl().addClass('x-combo-noedit');
14036 this.inputEl().dom.setAttribute('readOnly', false);
14037 this.inputEl().un('mousedown', this.onTriggerClick, this);
14038 this.inputEl().removeClass('x-combo-noedit');
14044 onBeforeLoad : function(combo,opts){
14045 if(!this.hasFocus){
14049 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14051 this.restrictHeight();
14052 this.selectedIndex = -1;
14056 onLoad : function(){
14058 this.hasQuery = false;
14060 if(!this.hasFocus){
14064 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14065 this.loading.hide();
14068 if(this.store.getCount() > 0){
14071 this.restrictHeight();
14072 if(this.lastQuery == this.allQuery){
14073 if(this.editable && !this.tickable){
14074 this.inputEl().dom.select();
14078 !this.selectByValue(this.value, true) &&
14081 !this.store.lastOptions ||
14082 typeof(this.store.lastOptions.add) == 'undefined' ||
14083 this.store.lastOptions.add != true
14086 this.select(0, true);
14089 if(this.autoFocus){
14092 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14093 this.taTask.delay(this.typeAheadDelay);
14097 this.onEmptyResults();
14103 onLoadException : function()
14105 this.hasQuery = false;
14107 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14108 this.loading.hide();
14111 if(this.tickable && this.editable){
14116 // only causes errors at present
14117 //Roo.log(this.store.reader.jsonData);
14118 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14120 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14126 onTypeAhead : function(){
14127 if(this.store.getCount() > 0){
14128 var r = this.store.getAt(0);
14129 var newValue = r.data[this.displayField];
14130 var len = newValue.length;
14131 var selStart = this.getRawValue().length;
14133 if(selStart != len){
14134 this.setRawValue(newValue);
14135 this.selectText(selStart, newValue.length);
14141 onSelect : function(record, index){
14143 if(this.fireEvent('beforeselect', this, record, index) !== false){
14145 this.setFromData(index > -1 ? record.data : false);
14148 this.fireEvent('select', this, record, index);
14153 * Returns the currently selected field value or empty string if no value is set.
14154 * @return {String} value The selected value
14156 getValue : function()
14158 if(Roo.isIOS && this.useNativeIOS){
14159 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14163 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14166 if(this.valueField){
14167 return typeof this.value != 'undefined' ? this.value : '';
14169 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14173 getRawValue : function()
14175 if(Roo.isIOS && this.useNativeIOS){
14176 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14179 var v = this.inputEl().getValue();
14185 * Clears any text/value currently set in the field
14187 clearValue : function(){
14189 if(this.hiddenField){
14190 this.hiddenField.dom.value = '';
14193 this.setRawValue('');
14194 this.lastSelectionText = '';
14195 this.lastData = false;
14197 var close = this.closeTriggerEl();
14208 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14209 * will be displayed in the field. If the value does not match the data value of an existing item,
14210 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14211 * Otherwise the field will be blank (although the value will still be set).
14212 * @param {String} value The value to match
14214 setValue : function(v)
14216 if(Roo.isIOS && this.useNativeIOS){
14217 this.setIOSValue(v);
14227 if(this.valueField){
14228 var r = this.findRecord(this.valueField, v);
14230 text = r.data[this.displayField];
14231 }else if(this.valueNotFoundText !== undefined){
14232 text = this.valueNotFoundText;
14235 this.lastSelectionText = text;
14236 if(this.hiddenField){
14237 this.hiddenField.dom.value = v;
14239 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14242 var close = this.closeTriggerEl();
14245 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14251 * @property {Object} the last set data for the element
14256 * Sets the value of the field based on a object which is related to the record format for the store.
14257 * @param {Object} value the value to set as. or false on reset?
14259 setFromData : function(o){
14266 var dv = ''; // display value
14267 var vv = ''; // value value..
14269 if (this.displayField) {
14270 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14272 // this is an error condition!!!
14273 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14276 if(this.valueField){
14277 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14280 var close = this.closeTriggerEl();
14283 if(dv.length || vv * 1 > 0){
14285 this.blockFocus=true;
14291 if(this.hiddenField){
14292 this.hiddenField.dom.value = vv;
14294 this.lastSelectionText = dv;
14295 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14299 // no hidden field.. - we store the value in 'value', but still display
14300 // display field!!!!
14301 this.lastSelectionText = dv;
14302 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14309 reset : function(){
14310 // overridden so that last data is reset..
14317 this.setValue(this.originalValue);
14318 //this.clearInvalid();
14319 this.lastData = false;
14321 this.view.clearSelections();
14327 findRecord : function(prop, value){
14329 if(this.store.getCount() > 0){
14330 this.store.each(function(r){
14331 if(r.data[prop] == value){
14341 getName: function()
14343 // returns hidden if it's set..
14344 if (!this.rendered) {return ''};
14345 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14349 onViewMove : function(e, t){
14350 this.inKeyMode = false;
14354 onViewOver : function(e, t){
14355 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14358 var item = this.view.findItemFromChild(t);
14361 var index = this.view.indexOf(item);
14362 this.select(index, false);
14367 onViewClick : function(view, doFocus, el, e)
14369 var index = this.view.getSelectedIndexes()[0];
14371 var r = this.store.getAt(index);
14375 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14382 Roo.each(this.tickItems, function(v,k){
14384 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14386 _this.tickItems.splice(k, 1);
14388 if(typeof(e) == 'undefined' && view == false){
14389 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14401 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14402 this.tickItems.push(r.data);
14405 if(typeof(e) == 'undefined' && view == false){
14406 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14413 this.onSelect(r, index);
14415 if(doFocus !== false && !this.blockFocus){
14416 this.inputEl().focus();
14421 restrictHeight : function(){
14422 //this.innerList.dom.style.height = '';
14423 //var inner = this.innerList.dom;
14424 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14425 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14426 //this.list.beginUpdate();
14427 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14428 this.list.alignTo(this.inputEl(), this.listAlign);
14429 this.list.alignTo(this.inputEl(), this.listAlign);
14430 //this.list.endUpdate();
14434 onEmptyResults : function(){
14436 if(this.tickable && this.editable){
14437 this.hasFocus = false;
14438 this.restrictHeight();
14446 * Returns true if the dropdown list is expanded, else false.
14448 isExpanded : function(){
14449 return this.list.isVisible();
14453 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14454 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14455 * @param {String} value The data value of the item to select
14456 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14457 * selected item if it is not currently in view (defaults to true)
14458 * @return {Boolean} True if the value matched an item in the list, else false
14460 selectByValue : function(v, scrollIntoView){
14461 if(v !== undefined && v !== null){
14462 var r = this.findRecord(this.valueField || this.displayField, v);
14464 this.select(this.store.indexOf(r), scrollIntoView);
14472 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14473 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14474 * @param {Number} index The zero-based index of the list item to select
14475 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14476 * selected item if it is not currently in view (defaults to true)
14478 select : function(index, scrollIntoView){
14479 this.selectedIndex = index;
14480 this.view.select(index);
14481 if(scrollIntoView !== false){
14482 var el = this.view.getNode(index);
14484 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14487 this.list.scrollChildIntoView(el, false);
14493 selectNext : function(){
14494 var ct = this.store.getCount();
14496 if(this.selectedIndex == -1){
14498 }else if(this.selectedIndex < ct-1){
14499 this.select(this.selectedIndex+1);
14505 selectPrev : function(){
14506 var ct = this.store.getCount();
14508 if(this.selectedIndex == -1){
14510 }else if(this.selectedIndex != 0){
14511 this.select(this.selectedIndex-1);
14517 onKeyUp : function(e){
14518 if(this.editable !== false && !e.isSpecialKey()){
14519 this.lastKey = e.getKey();
14520 this.dqTask.delay(this.queryDelay);
14525 validateBlur : function(){
14526 return !this.list || !this.list.isVisible();
14530 initQuery : function(){
14532 var v = this.getRawValue();
14534 if(this.tickable && this.editable){
14535 v = this.tickableInputEl().getValue();
14542 doForce : function(){
14543 if(this.inputEl().dom.value.length > 0){
14544 this.inputEl().dom.value =
14545 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14551 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14552 * query allowing the query action to be canceled if needed.
14553 * @param {String} query The SQL query to execute
14554 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14555 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14556 * saved in the current store (defaults to false)
14558 doQuery : function(q, forceAll){
14560 if(q === undefined || q === null){
14565 forceAll: forceAll,
14569 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14574 forceAll = qe.forceAll;
14575 if(forceAll === true || (q.length >= this.minChars)){
14577 this.hasQuery = true;
14579 if(this.lastQuery != q || this.alwaysQuery){
14580 this.lastQuery = q;
14581 if(this.mode == 'local'){
14582 this.selectedIndex = -1;
14584 this.store.clearFilter();
14587 if(this.specialFilter){
14588 this.fireEvent('specialfilter', this);
14593 this.store.filter(this.displayField, q);
14596 this.store.fireEvent("datachanged", this.store);
14603 this.store.baseParams[this.queryParam] = q;
14605 var options = {params : this.getParams(q)};
14608 options.add = true;
14609 options.params.start = this.page * this.pageSize;
14612 this.store.load(options);
14615 * this code will make the page width larger, at the beginning, the list not align correctly,
14616 * we should expand the list on onLoad
14617 * so command out it
14622 this.selectedIndex = -1;
14627 this.loadNext = false;
14631 getParams : function(q){
14633 //p[this.queryParam] = q;
14637 p.limit = this.pageSize;
14643 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14645 collapse : function(){
14646 if(!this.isExpanded()){
14652 this.hasFocus = false;
14656 this.cancelBtn.hide();
14657 this.trigger.show();
14660 this.tickableInputEl().dom.value = '';
14661 this.tickableInputEl().blur();
14666 Roo.get(document).un('mousedown', this.collapseIf, this);
14667 Roo.get(document).un('mousewheel', this.collapseIf, this);
14668 if (!this.editable) {
14669 Roo.get(document).un('keydown', this.listKeyPress, this);
14671 this.fireEvent('collapse', this);
14677 collapseIf : function(e){
14678 var in_combo = e.within(this.el);
14679 var in_list = e.within(this.list);
14680 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14682 if (in_combo || in_list || is_list) {
14683 //e.stopPropagation();
14688 this.onTickableFooterButtonClick(e, false, false);
14696 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14698 expand : function(){
14700 if(this.isExpanded() || !this.hasFocus){
14704 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14705 this.list.setWidth(lw);
14711 this.restrictHeight();
14715 this.tickItems = Roo.apply([], this.item);
14718 this.cancelBtn.show();
14719 this.trigger.hide();
14722 this.tickableInputEl().focus();
14727 Roo.get(document).on('mousedown', this.collapseIf, this);
14728 Roo.get(document).on('mousewheel', this.collapseIf, this);
14729 if (!this.editable) {
14730 Roo.get(document).on('keydown', this.listKeyPress, this);
14733 this.fireEvent('expand', this);
14737 // Implements the default empty TriggerField.onTriggerClick function
14738 onTriggerClick : function(e)
14740 Roo.log('trigger click');
14742 if(this.disabled || !this.triggerList){
14747 this.loadNext = false;
14749 if(this.isExpanded()){
14751 if (!this.blockFocus) {
14752 this.inputEl().focus();
14756 this.hasFocus = true;
14757 if(this.triggerAction == 'all') {
14758 this.doQuery(this.allQuery, true);
14760 this.doQuery(this.getRawValue());
14762 if (!this.blockFocus) {
14763 this.inputEl().focus();
14768 onTickableTriggerClick : function(e)
14775 this.loadNext = false;
14776 this.hasFocus = true;
14778 if(this.triggerAction == 'all') {
14779 this.doQuery(this.allQuery, true);
14781 this.doQuery(this.getRawValue());
14785 onSearchFieldClick : function(e)
14787 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14788 this.onTickableFooterButtonClick(e, false, false);
14792 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14797 this.loadNext = false;
14798 this.hasFocus = true;
14800 if(this.triggerAction == 'all') {
14801 this.doQuery(this.allQuery, true);
14803 this.doQuery(this.getRawValue());
14807 listKeyPress : function(e)
14809 //Roo.log('listkeypress');
14810 // scroll to first matching element based on key pres..
14811 if (e.isSpecialKey()) {
14814 var k = String.fromCharCode(e.getKey()).toUpperCase();
14817 var csel = this.view.getSelectedNodes();
14818 var cselitem = false;
14820 var ix = this.view.indexOf(csel[0]);
14821 cselitem = this.store.getAt(ix);
14822 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14828 this.store.each(function(v) {
14830 // start at existing selection.
14831 if (cselitem.id == v.id) {
14837 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14838 match = this.store.indexOf(v);
14844 if (match === false) {
14845 return true; // no more action?
14848 this.view.select(match);
14849 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14850 sn.scrollIntoView(sn.dom.parentNode, false);
14853 onViewScroll : function(e, t){
14855 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){
14859 this.hasQuery = true;
14861 this.loading = this.list.select('.loading', true).first();
14863 if(this.loading === null){
14864 this.list.createChild({
14866 cls: 'loading roo-select2-more-results roo-select2-active',
14867 html: 'Loading more results...'
14870 this.loading = this.list.select('.loading', true).first();
14872 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14874 this.loading.hide();
14877 this.loading.show();
14882 this.loadNext = true;
14884 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14889 addItem : function(o)
14891 var dv = ''; // display value
14893 if (this.displayField) {
14894 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14896 // this is an error condition!!!
14897 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14904 var choice = this.choices.createChild({
14906 cls: 'roo-select2-search-choice',
14915 cls: 'roo-select2-search-choice-close fa fa-times',
14920 }, this.searchField);
14922 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14924 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14932 this.inputEl().dom.value = '';
14937 onRemoveItem : function(e, _self, o)
14939 e.preventDefault();
14941 this.lastItem = Roo.apply([], this.item);
14943 var index = this.item.indexOf(o.data) * 1;
14946 Roo.log('not this item?!');
14950 this.item.splice(index, 1);
14955 this.fireEvent('remove', this, e);
14961 syncValue : function()
14963 if(!this.item.length){
14970 Roo.each(this.item, function(i){
14971 if(_this.valueField){
14972 value.push(i[_this.valueField]);
14979 this.value = value.join(',');
14981 if(this.hiddenField){
14982 this.hiddenField.dom.value = this.value;
14985 this.store.fireEvent("datachanged", this.store);
14990 clearItem : function()
14992 if(!this.multiple){
14998 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15006 if(this.tickable && !Roo.isTouch){
15007 this.view.refresh();
15011 inputEl: function ()
15013 if(Roo.isIOS && this.useNativeIOS){
15014 return this.el.select('select.roo-ios-select', true).first();
15017 if(Roo.isTouch && this.mobileTouchView){
15018 return this.el.select('input.form-control',true).first();
15022 return this.searchField;
15025 return this.el.select('input.form-control',true).first();
15028 onTickableFooterButtonClick : function(e, btn, el)
15030 e.preventDefault();
15032 this.lastItem = Roo.apply([], this.item);
15034 if(btn && btn.name == 'cancel'){
15035 this.tickItems = Roo.apply([], this.item);
15044 Roo.each(this.tickItems, function(o){
15052 validate : function()
15054 if(this.getVisibilityEl().hasClass('hidden')){
15058 var v = this.getRawValue();
15061 v = this.getValue();
15064 if(this.disabled || this.allowBlank || v.length){
15069 this.markInvalid();
15073 tickableInputEl : function()
15075 if(!this.tickable || !this.editable){
15076 return this.inputEl();
15079 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15083 getAutoCreateTouchView : function()
15088 cls: 'form-group' //input-group
15094 type : this.inputType,
15095 cls : 'form-control x-combo-noedit',
15096 autocomplete: 'new-password',
15097 placeholder : this.placeholder || '',
15102 input.name = this.name;
15106 input.cls += ' input-' + this.size;
15109 if (this.disabled) {
15110 input.disabled = true;
15121 inputblock.cls += ' input-group';
15123 inputblock.cn.unshift({
15125 cls : 'input-group-addon input-group-prepend input-group-text',
15130 if(this.removable && !this.multiple){
15131 inputblock.cls += ' roo-removable';
15133 inputblock.cn.push({
15136 cls : 'roo-combo-removable-btn close'
15140 if(this.hasFeedback && !this.allowBlank){
15142 inputblock.cls += ' has-feedback';
15144 inputblock.cn.push({
15146 cls: 'glyphicon form-control-feedback'
15153 inputblock.cls += (this.before) ? '' : ' input-group';
15155 inputblock.cn.push({
15157 cls : 'input-group-addon input-group-append input-group-text',
15163 var ibwrap = inputblock;
15168 cls: 'roo-select2-choices',
15172 cls: 'roo-select2-search-field',
15185 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15190 cls: 'form-hidden-field'
15196 if(!this.multiple && this.showToggleBtn){
15203 if (this.caret != false) {
15206 cls: 'fa fa-' + this.caret
15213 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15218 cls: 'combobox-clear',
15232 combobox.cls += ' roo-select2-container-multi';
15235 var align = this.labelAlign || this.parentLabelAlign();
15237 if (align ==='left' && this.fieldLabel.length) {
15242 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15243 tooltip : 'This field is required'
15247 cls : 'control-label col-form-label',
15248 html : this.fieldLabel
15259 var labelCfg = cfg.cn[1];
15260 var contentCfg = cfg.cn[2];
15263 if(this.indicatorpos == 'right'){
15268 cls : 'control-label col-form-label',
15272 html : this.fieldLabel
15276 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15277 tooltip : 'This field is required'
15290 labelCfg = cfg.cn[0];
15291 contentCfg = cfg.cn[1];
15296 if(this.labelWidth > 12){
15297 labelCfg.style = "width: " + this.labelWidth + 'px';
15300 if(this.labelWidth < 13 && this.labelmd == 0){
15301 this.labelmd = this.labelWidth;
15304 if(this.labellg > 0){
15305 labelCfg.cls += ' col-lg-' + this.labellg;
15306 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15309 if(this.labelmd > 0){
15310 labelCfg.cls += ' col-md-' + this.labelmd;
15311 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15314 if(this.labelsm > 0){
15315 labelCfg.cls += ' col-sm-' + this.labelsm;
15316 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15319 if(this.labelxs > 0){
15320 labelCfg.cls += ' col-xs-' + this.labelxs;
15321 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15325 } else if ( this.fieldLabel.length) {
15329 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15330 tooltip : 'This field is required'
15334 cls : 'control-label',
15335 html : this.fieldLabel
15346 if(this.indicatorpos == 'right'){
15350 cls : 'control-label',
15351 html : this.fieldLabel,
15355 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15356 tooltip : 'This field is required'
15373 var settings = this;
15375 ['xs','sm','md','lg'].map(function(size){
15376 if (settings[size]) {
15377 cfg.cls += ' col-' + size + '-' + settings[size];
15384 initTouchView : function()
15386 this.renderTouchView();
15388 this.touchViewEl.on('scroll', function(){
15389 this.el.dom.scrollTop = 0;
15392 this.originalValue = this.getValue();
15394 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15396 this.inputEl().on("click", this.showTouchView, this);
15397 if (this.triggerEl) {
15398 this.triggerEl.on("click", this.showTouchView, this);
15402 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15403 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15405 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15407 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15408 this.store.on('load', this.onTouchViewLoad, this);
15409 this.store.on('loadexception', this.onTouchViewLoadException, this);
15411 if(this.hiddenName){
15413 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15415 this.hiddenField.dom.value =
15416 this.hiddenValue !== undefined ? this.hiddenValue :
15417 this.value !== undefined ? this.value : '';
15419 this.el.dom.removeAttribute('name');
15420 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15424 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15425 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15428 if(this.removable && !this.multiple){
15429 var close = this.closeTriggerEl();
15431 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15432 close.on('click', this.removeBtnClick, this, close);
15436 * fix the bug in Safari iOS8
15438 this.inputEl().on("focus", function(e){
15439 document.activeElement.blur();
15442 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15449 renderTouchView : function()
15451 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15452 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15454 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15455 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15457 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15458 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15459 this.touchViewBodyEl.setStyle('overflow', 'auto');
15461 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15462 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15464 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15465 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15469 showTouchView : function()
15475 this.touchViewHeaderEl.hide();
15477 if(this.modalTitle.length){
15478 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15479 this.touchViewHeaderEl.show();
15482 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15483 this.touchViewEl.show();
15485 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15487 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15488 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15490 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15492 if(this.modalTitle.length){
15493 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15496 this.touchViewBodyEl.setHeight(bodyHeight);
15500 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15502 this.touchViewEl.addClass('in');
15505 if(this._touchViewMask){
15506 Roo.get(document.body).addClass("x-body-masked");
15507 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15508 this._touchViewMask.setStyle('z-index', 10000);
15509 this._touchViewMask.addClass('show');
15512 this.doTouchViewQuery();
15516 hideTouchView : function()
15518 this.touchViewEl.removeClass('in');
15522 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15524 this.touchViewEl.setStyle('display', 'none');
15527 if(this._touchViewMask){
15528 this._touchViewMask.removeClass('show');
15529 Roo.get(document.body).removeClass("x-body-masked");
15533 setTouchViewValue : function()
15540 Roo.each(this.tickItems, function(o){
15545 this.hideTouchView();
15548 doTouchViewQuery : function()
15557 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15561 if(!this.alwaysQuery || this.mode == 'local'){
15562 this.onTouchViewLoad();
15569 onTouchViewBeforeLoad : function(combo,opts)
15575 onTouchViewLoad : function()
15577 if(this.store.getCount() < 1){
15578 this.onTouchViewEmptyResults();
15582 this.clearTouchView();
15584 var rawValue = this.getRawValue();
15586 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15588 this.tickItems = [];
15590 this.store.data.each(function(d, rowIndex){
15591 var row = this.touchViewListGroup.createChild(template);
15593 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15594 row.addClass(d.data.cls);
15597 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15600 html : d.data[this.displayField]
15603 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15604 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15607 row.removeClass('selected');
15608 if(!this.multiple && this.valueField &&
15609 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15612 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15613 row.addClass('selected');
15616 if(this.multiple && this.valueField &&
15617 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15621 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15622 this.tickItems.push(d.data);
15625 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15629 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15631 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15633 if(this.modalTitle.length){
15634 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15637 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15639 if(this.mobile_restrict_height && listHeight < bodyHeight){
15640 this.touchViewBodyEl.setHeight(listHeight);
15645 if(firstChecked && listHeight > bodyHeight){
15646 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15651 onTouchViewLoadException : function()
15653 this.hideTouchView();
15656 onTouchViewEmptyResults : function()
15658 this.clearTouchView();
15660 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15662 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15666 clearTouchView : function()
15668 this.touchViewListGroup.dom.innerHTML = '';
15671 onTouchViewClick : function(e, el, o)
15673 e.preventDefault();
15676 var rowIndex = o.rowIndex;
15678 var r = this.store.getAt(rowIndex);
15680 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15682 if(!this.multiple){
15683 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15684 c.dom.removeAttribute('checked');
15687 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15689 this.setFromData(r.data);
15691 var close = this.closeTriggerEl();
15697 this.hideTouchView();
15699 this.fireEvent('select', this, r, rowIndex);
15704 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15705 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15706 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15710 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15711 this.addItem(r.data);
15712 this.tickItems.push(r.data);
15716 getAutoCreateNativeIOS : function()
15719 cls: 'form-group' //input-group,
15724 cls : 'roo-ios-select'
15728 combobox.name = this.name;
15731 if (this.disabled) {
15732 combobox.disabled = true;
15735 var settings = this;
15737 ['xs','sm','md','lg'].map(function(size){
15738 if (settings[size]) {
15739 cfg.cls += ' col-' + size + '-' + settings[size];
15749 initIOSView : function()
15751 this.store.on('load', this.onIOSViewLoad, this);
15756 onIOSViewLoad : function()
15758 if(this.store.getCount() < 1){
15762 this.clearIOSView();
15764 if(this.allowBlank) {
15766 var default_text = '-- SELECT --';
15768 if(this.placeholder.length){
15769 default_text = this.placeholder;
15772 if(this.emptyTitle.length){
15773 default_text += ' - ' + this.emptyTitle + ' -';
15776 var opt = this.inputEl().createChild({
15779 html : default_text
15783 o[this.valueField] = 0;
15784 o[this.displayField] = default_text;
15786 this.ios_options.push({
15793 this.store.data.each(function(d, rowIndex){
15797 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15798 html = d.data[this.displayField];
15803 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15804 value = d.data[this.valueField];
15813 if(this.value == d.data[this.valueField]){
15814 option['selected'] = true;
15817 var opt = this.inputEl().createChild(option);
15819 this.ios_options.push({
15826 this.inputEl().on('change', function(){
15827 this.fireEvent('select', this);
15832 clearIOSView: function()
15834 this.inputEl().dom.innerHTML = '';
15836 this.ios_options = [];
15839 setIOSValue: function(v)
15843 if(!this.ios_options){
15847 Roo.each(this.ios_options, function(opts){
15849 opts.el.dom.removeAttribute('selected');
15851 if(opts.data[this.valueField] != v){
15855 opts.el.dom.setAttribute('selected', true);
15861 * @cfg {Boolean} grow
15865 * @cfg {Number} growMin
15869 * @cfg {Number} growMax
15878 Roo.apply(Roo.bootstrap.ComboBox, {
15882 cls: 'modal-header',
15904 cls: 'list-group-item',
15908 cls: 'roo-combobox-list-group-item-value'
15912 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15926 listItemCheckbox : {
15928 cls: 'list-group-item',
15932 cls: 'roo-combobox-list-group-item-value'
15936 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15952 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15957 cls: 'modal-footer',
15965 cls: 'col-xs-6 text-left',
15968 cls: 'btn btn-danger roo-touch-view-cancel',
15974 cls: 'col-xs-6 text-right',
15977 cls: 'btn btn-success roo-touch-view-ok',
15988 Roo.apply(Roo.bootstrap.ComboBox, {
15990 touchViewTemplate : {
15992 cls: 'modal fade roo-combobox-touch-view',
15996 cls: 'modal-dialog',
15997 style : 'position:fixed', // we have to fix position....
16001 cls: 'modal-content',
16003 Roo.bootstrap.ComboBox.header,
16004 Roo.bootstrap.ComboBox.body,
16005 Roo.bootstrap.ComboBox.footer
16014 * Ext JS Library 1.1.1
16015 * Copyright(c) 2006-2007, Ext JS, LLC.
16017 * Originally Released Under LGPL - original licence link has changed is not relivant.
16020 * <script type="text/javascript">
16025 * @extends Roo.util.Observable
16026 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
16027 * This class also supports single and multi selection modes. <br>
16028 * Create a data model bound view:
16030 var store = new Roo.data.Store(...);
16032 var view = new Roo.View({
16034 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
16036 singleSelect: true,
16037 selectedClass: "ydataview-selected",
16041 // listen for node click?
16042 view.on("click", function(vw, index, node, e){
16043 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16047 dataModel.load("foobar.xml");
16049 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16051 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16052 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16054 * Note: old style constructor is still suported (container, template, config)
16057 * Create a new View
16058 * @param {Object} config The config object
16061 Roo.View = function(config, depreciated_tpl, depreciated_config){
16063 this.parent = false;
16065 if (typeof(depreciated_tpl) == 'undefined') {
16066 // new way.. - universal constructor.
16067 Roo.apply(this, config);
16068 this.el = Roo.get(this.el);
16071 this.el = Roo.get(config);
16072 this.tpl = depreciated_tpl;
16073 Roo.apply(this, depreciated_config);
16075 this.wrapEl = this.el.wrap().wrap();
16076 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16079 if(typeof(this.tpl) == "string"){
16080 this.tpl = new Roo.Template(this.tpl);
16082 // support xtype ctors..
16083 this.tpl = new Roo.factory(this.tpl, Roo);
16087 this.tpl.compile();
16092 * @event beforeclick
16093 * Fires before a click is processed. Returns false to cancel the default action.
16094 * @param {Roo.View} this
16095 * @param {Number} index The index of the target node
16096 * @param {HTMLElement} node The target node
16097 * @param {Roo.EventObject} e The raw event object
16099 "beforeclick" : true,
16102 * Fires when a template node is clicked.
16103 * @param {Roo.View} this
16104 * @param {Number} index The index of the target node
16105 * @param {HTMLElement} node The target node
16106 * @param {Roo.EventObject} e The raw event object
16111 * Fires when a template node is double clicked.
16112 * @param {Roo.View} this
16113 * @param {Number} index The index of the target node
16114 * @param {HTMLElement} node The target node
16115 * @param {Roo.EventObject} e The raw event object
16119 * @event contextmenu
16120 * Fires when a template node is right clicked.
16121 * @param {Roo.View} this
16122 * @param {Number} index The index of the target node
16123 * @param {HTMLElement} node The target node
16124 * @param {Roo.EventObject} e The raw event object
16126 "contextmenu" : true,
16128 * @event selectionchange
16129 * Fires when the selected nodes change.
16130 * @param {Roo.View} this
16131 * @param {Array} selections Array of the selected nodes
16133 "selectionchange" : true,
16136 * @event beforeselect
16137 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16138 * @param {Roo.View} this
16139 * @param {HTMLElement} node The node to be selected
16140 * @param {Array} selections Array of currently selected nodes
16142 "beforeselect" : true,
16144 * @event preparedata
16145 * Fires on every row to render, to allow you to change the data.
16146 * @param {Roo.View} this
16147 * @param {Object} data to be rendered (change this)
16149 "preparedata" : true
16157 "click": this.onClick,
16158 "dblclick": this.onDblClick,
16159 "contextmenu": this.onContextMenu,
16163 this.selections = [];
16165 this.cmp = new Roo.CompositeElementLite([]);
16167 this.store = Roo.factory(this.store, Roo.data);
16168 this.setStore(this.store, true);
16171 if ( this.footer && this.footer.xtype) {
16173 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16175 this.footer.dataSource = this.store;
16176 this.footer.container = fctr;
16177 this.footer = Roo.factory(this.footer, Roo);
16178 fctr.insertFirst(this.el);
16180 // this is a bit insane - as the paging toolbar seems to detach the el..
16181 // dom.parentNode.parentNode.parentNode
16182 // they get detached?
16186 Roo.View.superclass.constructor.call(this);
16191 Roo.extend(Roo.View, Roo.util.Observable, {
16194 * @cfg {Roo.data.Store} store Data store to load data from.
16199 * @cfg {String|Roo.Element} el The container element.
16204 * @cfg {String|Roo.Template} tpl The template used by this View
16208 * @cfg {String} dataName the named area of the template to use as the data area
16209 * Works with domtemplates roo-name="name"
16213 * @cfg {String} selectedClass The css class to add to selected nodes
16215 selectedClass : "x-view-selected",
16217 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16222 * @cfg {String} text to display on mask (default Loading)
16226 * @cfg {Boolean} multiSelect Allow multiple selection
16228 multiSelect : false,
16230 * @cfg {Boolean} singleSelect Allow single selection
16232 singleSelect: false,
16235 * @cfg {Boolean} toggleSelect - selecting
16237 toggleSelect : false,
16240 * @cfg {Boolean} tickable - selecting
16245 * Returns the element this view is bound to.
16246 * @return {Roo.Element}
16248 getEl : function(){
16249 return this.wrapEl;
16255 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16257 refresh : function(){
16258 //Roo.log('refresh');
16261 // if we are using something like 'domtemplate', then
16262 // the what gets used is:
16263 // t.applySubtemplate(NAME, data, wrapping data..)
16264 // the outer template then get' applied with
16265 // the store 'extra data'
16266 // and the body get's added to the
16267 // roo-name="data" node?
16268 // <span class='roo-tpl-{name}'></span> ?????
16272 this.clearSelections();
16273 this.el.update("");
16275 var records = this.store.getRange();
16276 if(records.length < 1) {
16278 // is this valid?? = should it render a template??
16280 this.el.update(this.emptyText);
16284 if (this.dataName) {
16285 this.el.update(t.apply(this.store.meta)); //????
16286 el = this.el.child('.roo-tpl-' + this.dataName);
16289 for(var i = 0, len = records.length; i < len; i++){
16290 var data = this.prepareData(records[i].data, i, records[i]);
16291 this.fireEvent("preparedata", this, data, i, records[i]);
16293 var d = Roo.apply({}, data);
16296 Roo.apply(d, {'roo-id' : Roo.id()});
16300 Roo.each(this.parent.item, function(item){
16301 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16304 Roo.apply(d, {'roo-data-checked' : 'checked'});
16308 html[html.length] = Roo.util.Format.trim(
16310 t.applySubtemplate(this.dataName, d, this.store.meta) :
16317 el.update(html.join(""));
16318 this.nodes = el.dom.childNodes;
16319 this.updateIndexes(0);
16324 * Function to override to reformat the data that is sent to
16325 * the template for each node.
16326 * DEPRICATED - use the preparedata event handler.
16327 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16328 * a JSON object for an UpdateManager bound view).
16330 prepareData : function(data, index, record)
16332 this.fireEvent("preparedata", this, data, index, record);
16336 onUpdate : function(ds, record){
16337 // Roo.log('on update');
16338 this.clearSelections();
16339 var index = this.store.indexOf(record);
16340 var n = this.nodes[index];
16341 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16342 n.parentNode.removeChild(n);
16343 this.updateIndexes(index, index);
16349 onAdd : function(ds, records, index)
16351 //Roo.log(['on Add', ds, records, index] );
16352 this.clearSelections();
16353 if(this.nodes.length == 0){
16357 var n = this.nodes[index];
16358 for(var i = 0, len = records.length; i < len; i++){
16359 var d = this.prepareData(records[i].data, i, records[i]);
16361 this.tpl.insertBefore(n, d);
16364 this.tpl.append(this.el, d);
16367 this.updateIndexes(index);
16370 onRemove : function(ds, record, index){
16371 // Roo.log('onRemove');
16372 this.clearSelections();
16373 var el = this.dataName ?
16374 this.el.child('.roo-tpl-' + this.dataName) :
16377 el.dom.removeChild(this.nodes[index]);
16378 this.updateIndexes(index);
16382 * Refresh an individual node.
16383 * @param {Number} index
16385 refreshNode : function(index){
16386 this.onUpdate(this.store, this.store.getAt(index));
16389 updateIndexes : function(startIndex, endIndex){
16390 var ns = this.nodes;
16391 startIndex = startIndex || 0;
16392 endIndex = endIndex || ns.length - 1;
16393 for(var i = startIndex; i <= endIndex; i++){
16394 ns[i].nodeIndex = i;
16399 * Changes the data store this view uses and refresh the view.
16400 * @param {Store} store
16402 setStore : function(store, initial){
16403 if(!initial && this.store){
16404 this.store.un("datachanged", this.refresh);
16405 this.store.un("add", this.onAdd);
16406 this.store.un("remove", this.onRemove);
16407 this.store.un("update", this.onUpdate);
16408 this.store.un("clear", this.refresh);
16409 this.store.un("beforeload", this.onBeforeLoad);
16410 this.store.un("load", this.onLoad);
16411 this.store.un("loadexception", this.onLoad);
16415 store.on("datachanged", this.refresh, this);
16416 store.on("add", this.onAdd, this);
16417 store.on("remove", this.onRemove, this);
16418 store.on("update", this.onUpdate, this);
16419 store.on("clear", this.refresh, this);
16420 store.on("beforeload", this.onBeforeLoad, this);
16421 store.on("load", this.onLoad, this);
16422 store.on("loadexception", this.onLoad, this);
16430 * onbeforeLoad - masks the loading area.
16433 onBeforeLoad : function(store,opts)
16435 //Roo.log('onBeforeLoad');
16437 this.el.update("");
16439 this.el.mask(this.mask ? this.mask : "Loading" );
16441 onLoad : function ()
16448 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16449 * @param {HTMLElement} node
16450 * @return {HTMLElement} The template node
16452 findItemFromChild : function(node){
16453 var el = this.dataName ?
16454 this.el.child('.roo-tpl-' + this.dataName,true) :
16457 if(!node || node.parentNode == el){
16460 var p = node.parentNode;
16461 while(p && p != el){
16462 if(p.parentNode == el){
16471 onClick : function(e){
16472 var item = this.findItemFromChild(e.getTarget());
16474 var index = this.indexOf(item);
16475 if(this.onItemClick(item, index, e) !== false){
16476 this.fireEvent("click", this, index, item, e);
16479 this.clearSelections();
16484 onContextMenu : function(e){
16485 var item = this.findItemFromChild(e.getTarget());
16487 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16492 onDblClick : function(e){
16493 var item = this.findItemFromChild(e.getTarget());
16495 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16499 onItemClick : function(item, index, e)
16501 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16504 if (this.toggleSelect) {
16505 var m = this.isSelected(item) ? 'unselect' : 'select';
16508 _t[m](item, true, false);
16511 if(this.multiSelect || this.singleSelect){
16512 if(this.multiSelect && e.shiftKey && this.lastSelection){
16513 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16515 this.select(item, this.multiSelect && e.ctrlKey);
16516 this.lastSelection = item;
16519 if(!this.tickable){
16520 e.preventDefault();
16528 * Get the number of selected nodes.
16531 getSelectionCount : function(){
16532 return this.selections.length;
16536 * Get the currently selected nodes.
16537 * @return {Array} An array of HTMLElements
16539 getSelectedNodes : function(){
16540 return this.selections;
16544 * Get the indexes of the selected nodes.
16547 getSelectedIndexes : function(){
16548 var indexes = [], s = this.selections;
16549 for(var i = 0, len = s.length; i < len; i++){
16550 indexes.push(s[i].nodeIndex);
16556 * Clear all selections
16557 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16559 clearSelections : function(suppressEvent){
16560 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16561 this.cmp.elements = this.selections;
16562 this.cmp.removeClass(this.selectedClass);
16563 this.selections = [];
16564 if(!suppressEvent){
16565 this.fireEvent("selectionchange", this, this.selections);
16571 * Returns true if the passed node is selected
16572 * @param {HTMLElement/Number} node The node or node index
16573 * @return {Boolean}
16575 isSelected : function(node){
16576 var s = this.selections;
16580 node = this.getNode(node);
16581 return s.indexOf(node) !== -1;
16586 * @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
16587 * @param {Boolean} keepExisting (optional) true to keep existing selections
16588 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16590 select : function(nodeInfo, keepExisting, suppressEvent){
16591 if(nodeInfo instanceof Array){
16593 this.clearSelections(true);
16595 for(var i = 0, len = nodeInfo.length; i < len; i++){
16596 this.select(nodeInfo[i], true, true);
16600 var node = this.getNode(nodeInfo);
16601 if(!node || this.isSelected(node)){
16602 return; // already selected.
16605 this.clearSelections(true);
16608 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16609 Roo.fly(node).addClass(this.selectedClass);
16610 this.selections.push(node);
16611 if(!suppressEvent){
16612 this.fireEvent("selectionchange", this, this.selections);
16620 * @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
16621 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16622 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16624 unselect : function(nodeInfo, keepExisting, suppressEvent)
16626 if(nodeInfo instanceof Array){
16627 Roo.each(this.selections, function(s) {
16628 this.unselect(s, nodeInfo);
16632 var node = this.getNode(nodeInfo);
16633 if(!node || !this.isSelected(node)){
16634 //Roo.log("not selected");
16635 return; // not selected.
16639 Roo.each(this.selections, function(s) {
16641 Roo.fly(node).removeClass(this.selectedClass);
16648 this.selections= ns;
16649 this.fireEvent("selectionchange", this, this.selections);
16653 * Gets a template node.
16654 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16655 * @return {HTMLElement} The node or null if it wasn't found
16657 getNode : function(nodeInfo){
16658 if(typeof nodeInfo == "string"){
16659 return document.getElementById(nodeInfo);
16660 }else if(typeof nodeInfo == "number"){
16661 return this.nodes[nodeInfo];
16667 * Gets a range template nodes.
16668 * @param {Number} startIndex
16669 * @param {Number} endIndex
16670 * @return {Array} An array of nodes
16672 getNodes : function(start, end){
16673 var ns = this.nodes;
16674 start = start || 0;
16675 end = typeof end == "undefined" ? ns.length - 1 : end;
16678 for(var i = start; i <= end; i++){
16682 for(var i = start; i >= end; i--){
16690 * Finds the index of the passed node
16691 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16692 * @return {Number} The index of the node or -1
16694 indexOf : function(node){
16695 node = this.getNode(node);
16696 if(typeof node.nodeIndex == "number"){
16697 return node.nodeIndex;
16699 var ns = this.nodes;
16700 for(var i = 0, len = ns.length; i < len; i++){
16711 * based on jquery fullcalendar
16715 Roo.bootstrap = Roo.bootstrap || {};
16717 * @class Roo.bootstrap.Calendar
16718 * @extends Roo.bootstrap.Component
16719 * Bootstrap Calendar class
16720 * @cfg {Boolean} loadMask (true|false) default false
16721 * @cfg {Object} header generate the user specific header of the calendar, default false
16724 * Create a new Container
16725 * @param {Object} config The config object
16730 Roo.bootstrap.Calendar = function(config){
16731 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16735 * Fires when a date is selected
16736 * @param {DatePicker} this
16737 * @param {Date} date The selected date
16741 * @event monthchange
16742 * Fires when the displayed month changes
16743 * @param {DatePicker} this
16744 * @param {Date} date The selected month
16746 'monthchange': true,
16748 * @event evententer
16749 * Fires when mouse over an event
16750 * @param {Calendar} this
16751 * @param {event} Event
16753 'evententer': true,
16755 * @event eventleave
16756 * Fires when the mouse leaves an
16757 * @param {Calendar} this
16760 'eventleave': true,
16762 * @event eventclick
16763 * Fires when the mouse click an
16764 * @param {Calendar} this
16773 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16776 * @cfg {Number} startDay
16777 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16785 getAutoCreate : function(){
16788 var fc_button = function(name, corner, style, content ) {
16789 return Roo.apply({},{
16791 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16793 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16796 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16807 style : 'width:100%',
16814 cls : 'fc-header-left',
16816 fc_button('prev', 'left', 'arrow', '‹' ),
16817 fc_button('next', 'right', 'arrow', '›' ),
16818 { tag: 'span', cls: 'fc-header-space' },
16819 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16827 cls : 'fc-header-center',
16831 cls: 'fc-header-title',
16834 html : 'month / year'
16842 cls : 'fc-header-right',
16844 /* fc_button('month', 'left', '', 'month' ),
16845 fc_button('week', '', '', 'week' ),
16846 fc_button('day', 'right', '', 'day' )
16858 header = this.header;
16861 var cal_heads = function() {
16863 // fixme - handle this.
16865 for (var i =0; i < Date.dayNames.length; i++) {
16866 var d = Date.dayNames[i];
16869 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16870 html : d.substring(0,3)
16874 ret[0].cls += ' fc-first';
16875 ret[6].cls += ' fc-last';
16878 var cal_cell = function(n) {
16881 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16886 cls: 'fc-day-number',
16890 cls: 'fc-day-content',
16894 style: 'position: relative;' // height: 17px;
16906 var cal_rows = function() {
16909 for (var r = 0; r < 6; r++) {
16916 for (var i =0; i < Date.dayNames.length; i++) {
16917 var d = Date.dayNames[i];
16918 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16921 row.cn[0].cls+=' fc-first';
16922 row.cn[0].cn[0].style = 'min-height:90px';
16923 row.cn[6].cls+=' fc-last';
16927 ret[0].cls += ' fc-first';
16928 ret[4].cls += ' fc-prev-last';
16929 ret[5].cls += ' fc-last';
16936 cls: 'fc-border-separate',
16937 style : 'width:100%',
16945 cls : 'fc-first fc-last',
16963 cls : 'fc-content',
16964 style : "position: relative;",
16967 cls : 'fc-view fc-view-month fc-grid',
16968 style : 'position: relative',
16969 unselectable : 'on',
16972 cls : 'fc-event-container',
16973 style : 'position:absolute;z-index:8;top:0;left:0;'
16991 initEvents : function()
16994 throw "can not find store for calendar";
17000 style: "text-align:center",
17004 style: "background-color:white;width:50%;margin:250 auto",
17008 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
17019 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17021 var size = this.el.select('.fc-content', true).first().getSize();
17022 this.maskEl.setSize(size.width, size.height);
17023 this.maskEl.enableDisplayMode("block");
17024 if(!this.loadMask){
17025 this.maskEl.hide();
17028 this.store = Roo.factory(this.store, Roo.data);
17029 this.store.on('load', this.onLoad, this);
17030 this.store.on('beforeload', this.onBeforeLoad, this);
17034 this.cells = this.el.select('.fc-day',true);
17035 //Roo.log(this.cells);
17036 this.textNodes = this.el.query('.fc-day-number');
17037 this.cells.addClassOnOver('fc-state-hover');
17039 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17040 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17041 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17042 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17044 this.on('monthchange', this.onMonthChange, this);
17046 this.update(new Date().clearTime());
17049 resize : function() {
17050 var sz = this.el.getSize();
17052 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17053 this.el.select('.fc-day-content div',true).setHeight(34);
17058 showPrevMonth : function(e){
17059 this.update(this.activeDate.add("mo", -1));
17061 showToday : function(e){
17062 this.update(new Date().clearTime());
17065 showNextMonth : function(e){
17066 this.update(this.activeDate.add("mo", 1));
17070 showPrevYear : function(){
17071 this.update(this.activeDate.add("y", -1));
17075 showNextYear : function(){
17076 this.update(this.activeDate.add("y", 1));
17081 update : function(date)
17083 var vd = this.activeDate;
17084 this.activeDate = date;
17085 // if(vd && this.el){
17086 // var t = date.getTime();
17087 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17088 // Roo.log('using add remove');
17090 // this.fireEvent('monthchange', this, date);
17092 // this.cells.removeClass("fc-state-highlight");
17093 // this.cells.each(function(c){
17094 // if(c.dateValue == t){
17095 // c.addClass("fc-state-highlight");
17096 // setTimeout(function(){
17097 // try{c.dom.firstChild.focus();}catch(e){}
17107 var days = date.getDaysInMonth();
17109 var firstOfMonth = date.getFirstDateOfMonth();
17110 var startingPos = firstOfMonth.getDay()-this.startDay;
17112 if(startingPos < this.startDay){
17116 var pm = date.add(Date.MONTH, -1);
17117 var prevStart = pm.getDaysInMonth()-startingPos;
17119 this.cells = this.el.select('.fc-day',true);
17120 this.textNodes = this.el.query('.fc-day-number');
17121 this.cells.addClassOnOver('fc-state-hover');
17123 var cells = this.cells.elements;
17124 var textEls = this.textNodes;
17126 Roo.each(cells, function(cell){
17127 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17130 days += startingPos;
17132 // convert everything to numbers so it's fast
17133 var day = 86400000;
17134 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17137 //Roo.log(prevStart);
17139 var today = new Date().clearTime().getTime();
17140 var sel = date.clearTime().getTime();
17141 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17142 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17143 var ddMatch = this.disabledDatesRE;
17144 var ddText = this.disabledDatesText;
17145 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17146 var ddaysText = this.disabledDaysText;
17147 var format = this.format;
17149 var setCellClass = function(cal, cell){
17153 //Roo.log('set Cell Class');
17155 var t = d.getTime();
17159 cell.dateValue = t;
17161 cell.className += " fc-today";
17162 cell.className += " fc-state-highlight";
17163 cell.title = cal.todayText;
17166 // disable highlight in other month..
17167 //cell.className += " fc-state-highlight";
17172 cell.className = " fc-state-disabled";
17173 cell.title = cal.minText;
17177 cell.className = " fc-state-disabled";
17178 cell.title = cal.maxText;
17182 if(ddays.indexOf(d.getDay()) != -1){
17183 cell.title = ddaysText;
17184 cell.className = " fc-state-disabled";
17187 if(ddMatch && format){
17188 var fvalue = d.dateFormat(format);
17189 if(ddMatch.test(fvalue)){
17190 cell.title = ddText.replace("%0", fvalue);
17191 cell.className = " fc-state-disabled";
17195 if (!cell.initialClassName) {
17196 cell.initialClassName = cell.dom.className;
17199 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17204 for(; i < startingPos; i++) {
17205 textEls[i].innerHTML = (++prevStart);
17206 d.setDate(d.getDate()+1);
17208 cells[i].className = "fc-past fc-other-month";
17209 setCellClass(this, cells[i]);
17214 for(; i < days; i++){
17215 intDay = i - startingPos + 1;
17216 textEls[i].innerHTML = (intDay);
17217 d.setDate(d.getDate()+1);
17219 cells[i].className = ''; // "x-date-active";
17220 setCellClass(this, cells[i]);
17224 for(; i < 42; i++) {
17225 textEls[i].innerHTML = (++extraDays);
17226 d.setDate(d.getDate()+1);
17228 cells[i].className = "fc-future fc-other-month";
17229 setCellClass(this, cells[i]);
17232 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17234 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17236 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17237 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17239 if(totalRows != 6){
17240 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17241 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17244 this.fireEvent('monthchange', this, date);
17248 if(!this.internalRender){
17249 var main = this.el.dom.firstChild;
17250 var w = main.offsetWidth;
17251 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17252 Roo.fly(main).setWidth(w);
17253 this.internalRender = true;
17254 // opera does not respect the auto grow header center column
17255 // then, after it gets a width opera refuses to recalculate
17256 // without a second pass
17257 if(Roo.isOpera && !this.secondPass){
17258 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17259 this.secondPass = true;
17260 this.update.defer(10, this, [date]);
17267 findCell : function(dt) {
17268 dt = dt.clearTime().getTime();
17270 this.cells.each(function(c){
17271 //Roo.log("check " +c.dateValue + '?=' + dt);
17272 if(c.dateValue == dt){
17282 findCells : function(ev) {
17283 var s = ev.start.clone().clearTime().getTime();
17285 var e= ev.end.clone().clearTime().getTime();
17288 this.cells.each(function(c){
17289 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17291 if(c.dateValue > e){
17294 if(c.dateValue < s){
17303 // findBestRow: function(cells)
17307 // for (var i =0 ; i < cells.length;i++) {
17308 // ret = Math.max(cells[i].rows || 0,ret);
17315 addItem : function(ev)
17317 // look for vertical location slot in
17318 var cells = this.findCells(ev);
17320 // ev.row = this.findBestRow(cells);
17322 // work out the location.
17326 for(var i =0; i < cells.length; i++) {
17328 cells[i].row = cells[0].row;
17331 cells[i].row = cells[i].row + 1;
17341 if (crow.start.getY() == cells[i].getY()) {
17343 crow.end = cells[i];
17360 cells[0].events.push(ev);
17362 this.calevents.push(ev);
17365 clearEvents: function() {
17367 if(!this.calevents){
17371 Roo.each(this.cells.elements, function(c){
17377 Roo.each(this.calevents, function(e) {
17378 Roo.each(e.els, function(el) {
17379 el.un('mouseenter' ,this.onEventEnter, this);
17380 el.un('mouseleave' ,this.onEventLeave, this);
17385 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17391 renderEvents: function()
17395 this.cells.each(function(c) {
17404 if(c.row != c.events.length){
17405 r = 4 - (4 - (c.row - c.events.length));
17408 c.events = ev.slice(0, r);
17409 c.more = ev.slice(r);
17411 if(c.more.length && c.more.length == 1){
17412 c.events.push(c.more.pop());
17415 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17419 this.cells.each(function(c) {
17421 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17424 for (var e = 0; e < c.events.length; e++){
17425 var ev = c.events[e];
17426 var rows = ev.rows;
17428 for(var i = 0; i < rows.length; i++) {
17430 // how many rows should it span..
17433 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17434 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17436 unselectable : "on",
17439 cls: 'fc-event-inner',
17443 // cls: 'fc-event-time',
17444 // html : cells.length > 1 ? '' : ev.time
17448 cls: 'fc-event-title',
17449 html : String.format('{0}', ev.title)
17456 cls: 'ui-resizable-handle ui-resizable-e',
17457 html : '  '
17464 cfg.cls += ' fc-event-start';
17466 if ((i+1) == rows.length) {
17467 cfg.cls += ' fc-event-end';
17470 var ctr = _this.el.select('.fc-event-container',true).first();
17471 var cg = ctr.createChild(cfg);
17473 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17474 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17476 var r = (c.more.length) ? 1 : 0;
17477 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17478 cg.setWidth(ebox.right - sbox.x -2);
17480 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17481 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17482 cg.on('click', _this.onEventClick, _this, ev);
17493 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17494 style : 'position: absolute',
17495 unselectable : "on",
17498 cls: 'fc-event-inner',
17502 cls: 'fc-event-title',
17510 cls: 'ui-resizable-handle ui-resizable-e',
17511 html : '  '
17517 var ctr = _this.el.select('.fc-event-container',true).first();
17518 var cg = ctr.createChild(cfg);
17520 var sbox = c.select('.fc-day-content',true).first().getBox();
17521 var ebox = c.select('.fc-day-content',true).first().getBox();
17523 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17524 cg.setWidth(ebox.right - sbox.x -2);
17526 cg.on('click', _this.onMoreEventClick, _this, c.more);
17536 onEventEnter: function (e, el,event,d) {
17537 this.fireEvent('evententer', this, el, event);
17540 onEventLeave: function (e, el,event,d) {
17541 this.fireEvent('eventleave', this, el, event);
17544 onEventClick: function (e, el,event,d) {
17545 this.fireEvent('eventclick', this, el, event);
17548 onMonthChange: function () {
17552 onMoreEventClick: function(e, el, more)
17556 this.calpopover.placement = 'right';
17557 this.calpopover.setTitle('More');
17559 this.calpopover.setContent('');
17561 var ctr = this.calpopover.el.select('.popover-content', true).first();
17563 Roo.each(more, function(m){
17565 cls : 'fc-event-hori fc-event-draggable',
17568 var cg = ctr.createChild(cfg);
17570 cg.on('click', _this.onEventClick, _this, m);
17573 this.calpopover.show(el);
17578 onLoad: function ()
17580 this.calevents = [];
17583 if(this.store.getCount() > 0){
17584 this.store.data.each(function(d){
17587 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17588 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17589 time : d.data.start_time,
17590 title : d.data.title,
17591 description : d.data.description,
17592 venue : d.data.venue
17597 this.renderEvents();
17599 if(this.calevents.length && this.loadMask){
17600 this.maskEl.hide();
17604 onBeforeLoad: function()
17606 this.clearEvents();
17608 this.maskEl.show();
17622 * @class Roo.bootstrap.Popover
17623 * @extends Roo.bootstrap.Component
17624 * Bootstrap Popover class
17625 * @cfg {String} html contents of the popover (or false to use children..)
17626 * @cfg {String} title of popover (or false to hide)
17627 * @cfg {String} placement how it is placed
17628 * @cfg {String} trigger click || hover (or false to trigger manually)
17629 * @cfg {String} over what (parent or false to trigger manually.)
17630 * @cfg {Number} delay - delay before showing
17633 * Create a new Popover
17634 * @param {Object} config The config object
17637 Roo.bootstrap.Popover = function(config){
17638 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17644 * After the popover show
17646 * @param {Roo.bootstrap.Popover} this
17651 * After the popover hide
17653 * @param {Roo.bootstrap.Popover} this
17659 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17661 title: 'Fill in a title',
17664 placement : 'right',
17665 trigger : 'hover', // hover
17671 can_build_overlaid : false,
17673 getChildContainer : function()
17675 return this.el.select('.popover-content',true).first();
17678 getAutoCreate : function(){
17681 cls : 'popover roo-dynamic',
17682 style: 'display:block',
17688 cls : 'popover-inner',
17692 cls: 'popover-title popover-header',
17696 cls : 'popover-content popover-body',
17707 setTitle: function(str)
17710 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17712 setContent: function(str)
17715 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17717 // as it get's added to the bottom of the page.
17718 onRender : function(ct, position)
17720 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17722 var cfg = Roo.apply({}, this.getAutoCreate());
17726 cfg.cls += ' ' + this.cls;
17729 cfg.style = this.style;
17731 //Roo.log("adding to ");
17732 this.el = Roo.get(document.body).createChild(cfg, position);
17733 // Roo.log(this.el);
17738 initEvents : function()
17740 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17741 this.el.enableDisplayMode('block');
17743 if (this.over === false) {
17746 if (this.triggers === false) {
17749 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17750 var triggers = this.trigger ? this.trigger.split(' ') : [];
17751 Roo.each(triggers, function(trigger) {
17753 if (trigger == 'click') {
17754 on_el.on('click', this.toggle, this);
17755 } else if (trigger != 'manual') {
17756 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17757 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17759 on_el.on(eventIn ,this.enter, this);
17760 on_el.on(eventOut, this.leave, this);
17771 toggle : function () {
17772 this.hoverState == 'in' ? this.leave() : this.enter();
17775 enter : function () {
17777 clearTimeout(this.timeout);
17779 this.hoverState = 'in';
17781 if (!this.delay || !this.delay.show) {
17786 this.timeout = setTimeout(function () {
17787 if (_t.hoverState == 'in') {
17790 }, this.delay.show)
17793 leave : function() {
17794 clearTimeout(this.timeout);
17796 this.hoverState = 'out';
17798 if (!this.delay || !this.delay.hide) {
17803 this.timeout = setTimeout(function () {
17804 if (_t.hoverState == 'out') {
17807 }, this.delay.hide)
17810 show : function (on_el)
17813 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17817 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17818 if (this.html !== false) {
17819 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17821 this.el.removeClass([
17822 'fade','top','bottom', 'left', 'right','in',
17823 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17825 if (!this.title.length) {
17826 this.el.select('.popover-title',true).hide();
17829 var placement = typeof this.placement == 'function' ?
17830 this.placement.call(this, this.el, on_el) :
17833 var autoToken = /\s?auto?\s?/i;
17834 var autoPlace = autoToken.test(placement);
17836 placement = placement.replace(autoToken, '') || 'top';
17840 //this.el.setXY([0,0]);
17842 this.el.dom.style.display='block';
17843 this.el.addClass(placement);
17845 //this.el.appendTo(on_el);
17847 var p = this.getPosition();
17848 var box = this.el.getBox();
17853 var align = Roo.bootstrap.Popover.alignment[placement];
17856 this.el.alignTo(on_el, align[0],align[1]);
17857 //var arrow = this.el.select('.arrow',true).first();
17858 //arrow.set(align[2],
17860 this.el.addClass('in');
17863 if (this.el.hasClass('fade')) {
17867 this.hoverState = 'in';
17869 this.fireEvent('show', this);
17874 this.el.setXY([0,0]);
17875 this.el.removeClass('in');
17877 this.hoverState = null;
17879 this.fireEvent('hide', this);
17884 Roo.bootstrap.Popover.alignment = {
17885 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17886 'right' : ['l-r', [10,0], 'left bs-popover-left'],
17887 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17888 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17899 * @class Roo.bootstrap.Progress
17900 * @extends Roo.bootstrap.Component
17901 * Bootstrap Progress class
17902 * @cfg {Boolean} striped striped of the progress bar
17903 * @cfg {Boolean} active animated of the progress bar
17907 * Create a new Progress
17908 * @param {Object} config The config object
17911 Roo.bootstrap.Progress = function(config){
17912 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17915 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17920 getAutoCreate : function(){
17928 cfg.cls += ' progress-striped';
17932 cfg.cls += ' active';
17951 * @class Roo.bootstrap.ProgressBar
17952 * @extends Roo.bootstrap.Component
17953 * Bootstrap ProgressBar class
17954 * @cfg {Number} aria_valuenow aria-value now
17955 * @cfg {Number} aria_valuemin aria-value min
17956 * @cfg {Number} aria_valuemax aria-value max
17957 * @cfg {String} label label for the progress bar
17958 * @cfg {String} panel (success | info | warning | danger )
17959 * @cfg {String} role role of the progress bar
17960 * @cfg {String} sr_only text
17964 * Create a new ProgressBar
17965 * @param {Object} config The config object
17968 Roo.bootstrap.ProgressBar = function(config){
17969 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17972 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17976 aria_valuemax : 100,
17982 getAutoCreate : function()
17987 cls: 'progress-bar',
17988 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18000 cfg.role = this.role;
18003 if(this.aria_valuenow){
18004 cfg['aria-valuenow'] = this.aria_valuenow;
18007 if(this.aria_valuemin){
18008 cfg['aria-valuemin'] = this.aria_valuemin;
18011 if(this.aria_valuemax){
18012 cfg['aria-valuemax'] = this.aria_valuemax;
18015 if(this.label && !this.sr_only){
18016 cfg.html = this.label;
18020 cfg.cls += ' progress-bar-' + this.panel;
18026 update : function(aria_valuenow)
18028 this.aria_valuenow = aria_valuenow;
18030 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18045 * @class Roo.bootstrap.TabGroup
18046 * @extends Roo.bootstrap.Column
18047 * Bootstrap Column class
18048 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18049 * @cfg {Boolean} carousel true to make the group behave like a carousel
18050 * @cfg {Boolean} bullets show bullets for the panels
18051 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18052 * @cfg {Number} timer auto slide timer .. default 0 millisecond
18053 * @cfg {Boolean} showarrow (true|false) show arrow default true
18056 * Create a new TabGroup
18057 * @param {Object} config The config object
18060 Roo.bootstrap.TabGroup = function(config){
18061 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18063 this.navId = Roo.id();
18066 Roo.bootstrap.TabGroup.register(this);
18070 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18073 transition : false,
18078 slideOnTouch : false,
18081 getAutoCreate : function()
18083 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18085 cfg.cls += ' tab-content';
18087 if (this.carousel) {
18088 cfg.cls += ' carousel slide';
18091 cls : 'carousel-inner',
18095 if(this.bullets && !Roo.isTouch){
18098 cls : 'carousel-bullets',
18102 if(this.bullets_cls){
18103 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18110 cfg.cn[0].cn.push(bullets);
18113 if(this.showarrow){
18114 cfg.cn[0].cn.push({
18116 class : 'carousel-arrow',
18120 class : 'carousel-prev',
18124 class : 'fa fa-chevron-left'
18130 class : 'carousel-next',
18134 class : 'fa fa-chevron-right'
18147 initEvents: function()
18149 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18150 // this.el.on("touchstart", this.onTouchStart, this);
18153 if(this.autoslide){
18156 this.slideFn = window.setInterval(function() {
18157 _this.showPanelNext();
18161 if(this.showarrow){
18162 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18163 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18169 // onTouchStart : function(e, el, o)
18171 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18175 // this.showPanelNext();
18179 getChildContainer : function()
18181 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18185 * register a Navigation item
18186 * @param {Roo.bootstrap.NavItem} the navitem to add
18188 register : function(item)
18190 this.tabs.push( item);
18191 item.navId = this.navId; // not really needed..
18196 getActivePanel : function()
18199 Roo.each(this.tabs, function(t) {
18209 getPanelByName : function(n)
18212 Roo.each(this.tabs, function(t) {
18213 if (t.tabId == n) {
18221 indexOfPanel : function(p)
18224 Roo.each(this.tabs, function(t,i) {
18225 if (t.tabId == p.tabId) {
18234 * show a specific panel
18235 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18236 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18238 showPanel : function (pan)
18240 if(this.transition || typeof(pan) == 'undefined'){
18241 Roo.log("waiting for the transitionend");
18245 if (typeof(pan) == 'number') {
18246 pan = this.tabs[pan];
18249 if (typeof(pan) == 'string') {
18250 pan = this.getPanelByName(pan);
18253 var cur = this.getActivePanel();
18256 Roo.log('pan or acitve pan is undefined');
18260 if (pan.tabId == this.getActivePanel().tabId) {
18264 if (false === cur.fireEvent('beforedeactivate')) {
18268 if(this.bullets > 0 && !Roo.isTouch){
18269 this.setActiveBullet(this.indexOfPanel(pan));
18272 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18274 this.transition = true;
18275 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18276 var lr = dir == 'next' ? 'left' : 'right';
18277 pan.el.addClass(dir); // or prev
18278 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18279 cur.el.addClass(lr); // or right
18280 pan.el.addClass(lr);
18283 cur.el.on('transitionend', function() {
18284 Roo.log("trans end?");
18286 pan.el.removeClass([lr,dir]);
18287 pan.setActive(true);
18289 cur.el.removeClass([lr]);
18290 cur.setActive(false);
18292 _this.transition = false;
18294 }, this, { single: true } );
18299 cur.setActive(false);
18300 pan.setActive(true);
18305 showPanelNext : function()
18307 var i = this.indexOfPanel(this.getActivePanel());
18309 if (i >= this.tabs.length - 1 && !this.autoslide) {
18313 if (i >= this.tabs.length - 1 && this.autoslide) {
18317 this.showPanel(this.tabs[i+1]);
18320 showPanelPrev : function()
18322 var i = this.indexOfPanel(this.getActivePanel());
18324 if (i < 1 && !this.autoslide) {
18328 if (i < 1 && this.autoslide) {
18329 i = this.tabs.length;
18332 this.showPanel(this.tabs[i-1]);
18336 addBullet: function()
18338 if(!this.bullets || Roo.isTouch){
18341 var ctr = this.el.select('.carousel-bullets',true).first();
18342 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18343 var bullet = ctr.createChild({
18344 cls : 'bullet bullet-' + i
18345 },ctr.dom.lastChild);
18350 bullet.on('click', (function(e, el, o, ii, t){
18352 e.preventDefault();
18354 this.showPanel(ii);
18356 if(this.autoslide && this.slideFn){
18357 clearInterval(this.slideFn);
18358 this.slideFn = window.setInterval(function() {
18359 _this.showPanelNext();
18363 }).createDelegate(this, [i, bullet], true));
18368 setActiveBullet : function(i)
18374 Roo.each(this.el.select('.bullet', true).elements, function(el){
18375 el.removeClass('selected');
18378 var bullet = this.el.select('.bullet-' + i, true).first();
18384 bullet.addClass('selected');
18395 Roo.apply(Roo.bootstrap.TabGroup, {
18399 * register a Navigation Group
18400 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18402 register : function(navgrp)
18404 this.groups[navgrp.navId] = navgrp;
18408 * fetch a Navigation Group based on the navigation ID
18409 * if one does not exist , it will get created.
18410 * @param {string} the navgroup to add
18411 * @returns {Roo.bootstrap.NavGroup} the navgroup
18413 get: function(navId) {
18414 if (typeof(this.groups[navId]) == 'undefined') {
18415 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18417 return this.groups[navId] ;
18432 * @class Roo.bootstrap.TabPanel
18433 * @extends Roo.bootstrap.Component
18434 * Bootstrap TabPanel class
18435 * @cfg {Boolean} active panel active
18436 * @cfg {String} html panel content
18437 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18438 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18439 * @cfg {String} href click to link..
18443 * Create a new TabPanel
18444 * @param {Object} config The config object
18447 Roo.bootstrap.TabPanel = function(config){
18448 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18452 * Fires when the active status changes
18453 * @param {Roo.bootstrap.TabPanel} this
18454 * @param {Boolean} state the new state
18459 * @event beforedeactivate
18460 * Fires before a tab is de-activated - can be used to do validation on a form.
18461 * @param {Roo.bootstrap.TabPanel} this
18462 * @return {Boolean} false if there is an error
18465 'beforedeactivate': true
18468 this.tabId = this.tabId || Roo.id();
18472 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18480 getAutoCreate : function(){
18483 // item is needed for carousel - not sure if it has any effect otherwise
18484 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18485 html: this.html || ''
18489 cfg.cls += ' active';
18493 cfg.tabId = this.tabId;
18500 initEvents: function()
18502 var p = this.parent();
18504 this.navId = this.navId || p.navId;
18506 if (typeof(this.navId) != 'undefined') {
18507 // not really needed.. but just in case.. parent should be a NavGroup.
18508 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18512 var i = tg.tabs.length - 1;
18514 if(this.active && tg.bullets > 0 && i < tg.bullets){
18515 tg.setActiveBullet(i);
18519 this.el.on('click', this.onClick, this);
18522 this.el.on("touchstart", this.onTouchStart, this);
18523 this.el.on("touchmove", this.onTouchMove, this);
18524 this.el.on("touchend", this.onTouchEnd, this);
18529 onRender : function(ct, position)
18531 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18534 setActive : function(state)
18536 Roo.log("panel - set active " + this.tabId + "=" + state);
18538 this.active = state;
18540 this.el.removeClass('active');
18542 } else if (!this.el.hasClass('active')) {
18543 this.el.addClass('active');
18546 this.fireEvent('changed', this, state);
18549 onClick : function(e)
18551 e.preventDefault();
18553 if(!this.href.length){
18557 window.location.href = this.href;
18566 onTouchStart : function(e)
18568 this.swiping = false;
18570 this.startX = e.browserEvent.touches[0].clientX;
18571 this.startY = e.browserEvent.touches[0].clientY;
18574 onTouchMove : function(e)
18576 this.swiping = true;
18578 this.endX = e.browserEvent.touches[0].clientX;
18579 this.endY = e.browserEvent.touches[0].clientY;
18582 onTouchEnd : function(e)
18589 var tabGroup = this.parent();
18591 if(this.endX > this.startX){ // swiping right
18592 tabGroup.showPanelPrev();
18596 if(this.startX > this.endX){ // swiping left
18597 tabGroup.showPanelNext();
18616 * @class Roo.bootstrap.DateField
18617 * @extends Roo.bootstrap.Input
18618 * Bootstrap DateField class
18619 * @cfg {Number} weekStart default 0
18620 * @cfg {String} viewMode default empty, (months|years)
18621 * @cfg {String} minViewMode default empty, (months|years)
18622 * @cfg {Number} startDate default -Infinity
18623 * @cfg {Number} endDate default Infinity
18624 * @cfg {Boolean} todayHighlight default false
18625 * @cfg {Boolean} todayBtn default false
18626 * @cfg {Boolean} calendarWeeks default false
18627 * @cfg {Object} daysOfWeekDisabled default empty
18628 * @cfg {Boolean} singleMode default false (true | false)
18630 * @cfg {Boolean} keyboardNavigation default true
18631 * @cfg {String} language default en
18634 * Create a new DateField
18635 * @param {Object} config The config object
18638 Roo.bootstrap.DateField = function(config){
18639 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18643 * Fires when this field show.
18644 * @param {Roo.bootstrap.DateField} this
18645 * @param {Mixed} date The date value
18650 * Fires when this field hide.
18651 * @param {Roo.bootstrap.DateField} this
18652 * @param {Mixed} date The date value
18657 * Fires when select a date.
18658 * @param {Roo.bootstrap.DateField} this
18659 * @param {Mixed} date The date value
18663 * @event beforeselect
18664 * Fires when before select a date.
18665 * @param {Roo.bootstrap.DateField} this
18666 * @param {Mixed} date The date value
18668 beforeselect : true
18672 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18675 * @cfg {String} format
18676 * The default date format string which can be overriden for localization support. The format must be
18677 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18681 * @cfg {String} altFormats
18682 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18683 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18685 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18693 todayHighlight : false,
18699 keyboardNavigation: true,
18701 calendarWeeks: false,
18703 startDate: -Infinity,
18707 daysOfWeekDisabled: [],
18711 singleMode : false,
18713 UTCDate: function()
18715 return new Date(Date.UTC.apply(Date, arguments));
18718 UTCToday: function()
18720 var today = new Date();
18721 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18724 getDate: function() {
18725 var d = this.getUTCDate();
18726 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18729 getUTCDate: function() {
18733 setDate: function(d) {
18734 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18737 setUTCDate: function(d) {
18739 this.setValue(this.formatDate(this.date));
18742 onRender: function(ct, position)
18745 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18747 this.language = this.language || 'en';
18748 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18749 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18751 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18752 this.format = this.format || 'm/d/y';
18753 this.isInline = false;
18754 this.isInput = true;
18755 this.component = this.el.select('.add-on', true).first() || false;
18756 this.component = (this.component && this.component.length === 0) ? false : this.component;
18757 this.hasInput = this.component && this.inputEl().length;
18759 if (typeof(this.minViewMode === 'string')) {
18760 switch (this.minViewMode) {
18762 this.minViewMode = 1;
18765 this.minViewMode = 2;
18768 this.minViewMode = 0;
18773 if (typeof(this.viewMode === 'string')) {
18774 switch (this.viewMode) {
18787 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18789 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18791 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18793 this.picker().on('mousedown', this.onMousedown, this);
18794 this.picker().on('click', this.onClick, this);
18796 this.picker().addClass('datepicker-dropdown');
18798 this.startViewMode = this.viewMode;
18800 if(this.singleMode){
18801 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18802 v.setVisibilityMode(Roo.Element.DISPLAY);
18806 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18807 v.setStyle('width', '189px');
18811 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18812 if(!this.calendarWeeks){
18817 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18818 v.attr('colspan', function(i, val){
18819 return parseInt(val) + 1;
18824 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18826 this.setStartDate(this.startDate);
18827 this.setEndDate(this.endDate);
18829 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18836 if(this.isInline) {
18841 picker : function()
18843 return this.pickerEl;
18844 // return this.el.select('.datepicker', true).first();
18847 fillDow: function()
18849 var dowCnt = this.weekStart;
18858 if(this.calendarWeeks){
18866 while (dowCnt < this.weekStart + 7) {
18870 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18874 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18877 fillMonths: function()
18880 var months = this.picker().select('>.datepicker-months td', true).first();
18882 months.dom.innerHTML = '';
18888 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18891 months.createChild(month);
18898 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;
18900 if (this.date < this.startDate) {
18901 this.viewDate = new Date(this.startDate);
18902 } else if (this.date > this.endDate) {
18903 this.viewDate = new Date(this.endDate);
18905 this.viewDate = new Date(this.date);
18913 var d = new Date(this.viewDate),
18914 year = d.getUTCFullYear(),
18915 month = d.getUTCMonth(),
18916 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18917 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18918 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18919 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18920 currentDate = this.date && this.date.valueOf(),
18921 today = this.UTCToday();
18923 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18925 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18927 // this.picker.select('>tfoot th.today').
18928 // .text(dates[this.language].today)
18929 // .toggle(this.todayBtn !== false);
18931 this.updateNavArrows();
18934 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18936 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18938 prevMonth.setUTCDate(day);
18940 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18942 var nextMonth = new Date(prevMonth);
18944 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18946 nextMonth = nextMonth.valueOf();
18948 var fillMonths = false;
18950 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18952 while(prevMonth.valueOf() <= nextMonth) {
18955 if (prevMonth.getUTCDay() === this.weekStart) {
18957 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18965 if(this.calendarWeeks){
18966 // ISO 8601: First week contains first thursday.
18967 // ISO also states week starts on Monday, but we can be more abstract here.
18969 // Start of current week: based on weekstart/current date
18970 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18971 // Thursday of this week
18972 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18973 // First Thursday of year, year from thursday
18974 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18975 // Calendar week: ms between thursdays, div ms per day, div 7 days
18976 calWeek = (th - yth) / 864e5 / 7 + 1;
18978 fillMonths.cn.push({
18986 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18988 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18991 if (this.todayHighlight &&
18992 prevMonth.getUTCFullYear() == today.getFullYear() &&
18993 prevMonth.getUTCMonth() == today.getMonth() &&
18994 prevMonth.getUTCDate() == today.getDate()) {
18995 clsName += ' today';
18998 if (currentDate && prevMonth.valueOf() === currentDate) {
18999 clsName += ' active';
19002 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19003 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19004 clsName += ' disabled';
19007 fillMonths.cn.push({
19009 cls: 'day ' + clsName,
19010 html: prevMonth.getDate()
19013 prevMonth.setDate(prevMonth.getDate()+1);
19016 var currentYear = this.date && this.date.getUTCFullYear();
19017 var currentMonth = this.date && this.date.getUTCMonth();
19019 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19021 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19022 v.removeClass('active');
19024 if(currentYear === year && k === currentMonth){
19025 v.addClass('active');
19028 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19029 v.addClass('disabled');
19035 year = parseInt(year/10, 10) * 10;
19037 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19039 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19042 for (var i = -1; i < 11; i++) {
19043 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19045 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19053 showMode: function(dir)
19056 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19059 Roo.each(this.picker().select('>div',true).elements, function(v){
19060 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19063 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19068 if(this.isInline) {
19072 this.picker().removeClass(['bottom', 'top']);
19074 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19076 * place to the top of element!
19080 this.picker().addClass('top');
19081 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19086 this.picker().addClass('bottom');
19088 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19091 parseDate : function(value)
19093 if(!value || value instanceof Date){
19096 var v = Date.parseDate(value, this.format);
19097 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19098 v = Date.parseDate(value, 'Y-m-d');
19100 if(!v && this.altFormats){
19101 if(!this.altFormatsArray){
19102 this.altFormatsArray = this.altFormats.split("|");
19104 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19105 v = Date.parseDate(value, this.altFormatsArray[i]);
19111 formatDate : function(date, fmt)
19113 return (!date || !(date instanceof Date)) ?
19114 date : date.dateFormat(fmt || this.format);
19117 onFocus : function()
19119 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19123 onBlur : function()
19125 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19127 var d = this.inputEl().getValue();
19134 showPopup : function()
19136 this.picker().show();
19140 this.fireEvent('showpopup', this, this.date);
19143 hidePopup : function()
19145 if(this.isInline) {
19148 this.picker().hide();
19149 this.viewMode = this.startViewMode;
19152 this.fireEvent('hidepopup', this, this.date);
19156 onMousedown: function(e)
19158 e.stopPropagation();
19159 e.preventDefault();
19164 Roo.bootstrap.DateField.superclass.keyup.call(this);
19168 setValue: function(v)
19170 if(this.fireEvent('beforeselect', this, v) !== false){
19171 var d = new Date(this.parseDate(v) ).clearTime();
19173 if(isNaN(d.getTime())){
19174 this.date = this.viewDate = '';
19175 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19179 v = this.formatDate(d);
19181 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19183 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19187 this.fireEvent('select', this, this.date);
19191 getValue: function()
19193 return this.formatDate(this.date);
19196 fireKey: function(e)
19198 if (!this.picker().isVisible()){
19199 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19205 var dateChanged = false,
19207 newDate, newViewDate;
19212 e.preventDefault();
19216 if (!this.keyboardNavigation) {
19219 dir = e.keyCode == 37 ? -1 : 1;
19222 newDate = this.moveYear(this.date, dir);
19223 newViewDate = this.moveYear(this.viewDate, dir);
19224 } else if (e.shiftKey){
19225 newDate = this.moveMonth(this.date, dir);
19226 newViewDate = this.moveMonth(this.viewDate, dir);
19228 newDate = new Date(this.date);
19229 newDate.setUTCDate(this.date.getUTCDate() + dir);
19230 newViewDate = new Date(this.viewDate);
19231 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19233 if (this.dateWithinRange(newDate)){
19234 this.date = newDate;
19235 this.viewDate = newViewDate;
19236 this.setValue(this.formatDate(this.date));
19238 e.preventDefault();
19239 dateChanged = true;
19244 if (!this.keyboardNavigation) {
19247 dir = e.keyCode == 38 ? -1 : 1;
19249 newDate = this.moveYear(this.date, dir);
19250 newViewDate = this.moveYear(this.viewDate, dir);
19251 } else if (e.shiftKey){
19252 newDate = this.moveMonth(this.date, dir);
19253 newViewDate = this.moveMonth(this.viewDate, dir);
19255 newDate = new Date(this.date);
19256 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19257 newViewDate = new Date(this.viewDate);
19258 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19260 if (this.dateWithinRange(newDate)){
19261 this.date = newDate;
19262 this.viewDate = newViewDate;
19263 this.setValue(this.formatDate(this.date));
19265 e.preventDefault();
19266 dateChanged = true;
19270 this.setValue(this.formatDate(this.date));
19272 e.preventDefault();
19275 this.setValue(this.formatDate(this.date));
19289 onClick: function(e)
19291 e.stopPropagation();
19292 e.preventDefault();
19294 var target = e.getTarget();
19296 if(target.nodeName.toLowerCase() === 'i'){
19297 target = Roo.get(target).dom.parentNode;
19300 var nodeName = target.nodeName;
19301 var className = target.className;
19302 var html = target.innerHTML;
19303 //Roo.log(nodeName);
19305 switch(nodeName.toLowerCase()) {
19307 switch(className) {
19313 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19314 switch(this.viewMode){
19316 this.viewDate = this.moveMonth(this.viewDate, dir);
19320 this.viewDate = this.moveYear(this.viewDate, dir);
19326 var date = new Date();
19327 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19329 this.setValue(this.formatDate(this.date));
19336 if (className.indexOf('disabled') < 0) {
19337 this.viewDate.setUTCDate(1);
19338 if (className.indexOf('month') > -1) {
19339 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19341 var year = parseInt(html, 10) || 0;
19342 this.viewDate.setUTCFullYear(year);
19346 if(this.singleMode){
19347 this.setValue(this.formatDate(this.viewDate));
19358 //Roo.log(className);
19359 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19360 var day = parseInt(html, 10) || 1;
19361 var year = this.viewDate.getUTCFullYear(),
19362 month = this.viewDate.getUTCMonth();
19364 if (className.indexOf('old') > -1) {
19371 } else if (className.indexOf('new') > -1) {
19379 //Roo.log([year,month,day]);
19380 this.date = this.UTCDate(year, month, day,0,0,0,0);
19381 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19383 //Roo.log(this.formatDate(this.date));
19384 this.setValue(this.formatDate(this.date));
19391 setStartDate: function(startDate)
19393 this.startDate = startDate || -Infinity;
19394 if (this.startDate !== -Infinity) {
19395 this.startDate = this.parseDate(this.startDate);
19398 this.updateNavArrows();
19401 setEndDate: function(endDate)
19403 this.endDate = endDate || Infinity;
19404 if (this.endDate !== Infinity) {
19405 this.endDate = this.parseDate(this.endDate);
19408 this.updateNavArrows();
19411 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19413 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19414 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19415 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19417 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19418 return parseInt(d, 10);
19421 this.updateNavArrows();
19424 updateNavArrows: function()
19426 if(this.singleMode){
19430 var d = new Date(this.viewDate),
19431 year = d.getUTCFullYear(),
19432 month = d.getUTCMonth();
19434 Roo.each(this.picker().select('.prev', true).elements, function(v){
19436 switch (this.viewMode) {
19439 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19445 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19452 Roo.each(this.picker().select('.next', true).elements, function(v){
19454 switch (this.viewMode) {
19457 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19463 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19471 moveMonth: function(date, dir)
19476 var new_date = new Date(date.valueOf()),
19477 day = new_date.getUTCDate(),
19478 month = new_date.getUTCMonth(),
19479 mag = Math.abs(dir),
19481 dir = dir > 0 ? 1 : -1;
19484 // If going back one month, make sure month is not current month
19485 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19487 return new_date.getUTCMonth() == month;
19489 // If going forward one month, make sure month is as expected
19490 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19492 return new_date.getUTCMonth() != new_month;
19494 new_month = month + dir;
19495 new_date.setUTCMonth(new_month);
19496 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19497 if (new_month < 0 || new_month > 11) {
19498 new_month = (new_month + 12) % 12;
19501 // For magnitudes >1, move one month at a time...
19502 for (var i=0; i<mag; i++) {
19503 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19504 new_date = this.moveMonth(new_date, dir);
19506 // ...then reset the day, keeping it in the new month
19507 new_month = new_date.getUTCMonth();
19508 new_date.setUTCDate(day);
19510 return new_month != new_date.getUTCMonth();
19513 // Common date-resetting loop -- if date is beyond end of month, make it
19516 new_date.setUTCDate(--day);
19517 new_date.setUTCMonth(new_month);
19522 moveYear: function(date, dir)
19524 return this.moveMonth(date, dir*12);
19527 dateWithinRange: function(date)
19529 return date >= this.startDate && date <= this.endDate;
19535 this.picker().remove();
19538 validateValue : function(value)
19540 if(this.getVisibilityEl().hasClass('hidden')){
19544 if(value.length < 1) {
19545 if(this.allowBlank){
19551 if(value.length < this.minLength){
19554 if(value.length > this.maxLength){
19558 var vt = Roo.form.VTypes;
19559 if(!vt[this.vtype](value, this)){
19563 if(typeof this.validator == "function"){
19564 var msg = this.validator(value);
19570 if(this.regex && !this.regex.test(value)){
19574 if(typeof(this.parseDate(value)) == 'undefined'){
19578 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19582 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19592 this.date = this.viewDate = '';
19594 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19599 Roo.apply(Roo.bootstrap.DateField, {
19610 html: '<i class="fa fa-arrow-left"/>'
19620 html: '<i class="fa fa-arrow-right"/>'
19662 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19663 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19664 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19665 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19666 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19679 navFnc: 'FullYear',
19684 navFnc: 'FullYear',
19689 Roo.apply(Roo.bootstrap.DateField, {
19693 cls: 'datepicker dropdown-menu roo-dynamic',
19697 cls: 'datepicker-days',
19701 cls: 'table-condensed',
19703 Roo.bootstrap.DateField.head,
19707 Roo.bootstrap.DateField.footer
19714 cls: 'datepicker-months',
19718 cls: 'table-condensed',
19720 Roo.bootstrap.DateField.head,
19721 Roo.bootstrap.DateField.content,
19722 Roo.bootstrap.DateField.footer
19729 cls: 'datepicker-years',
19733 cls: 'table-condensed',
19735 Roo.bootstrap.DateField.head,
19736 Roo.bootstrap.DateField.content,
19737 Roo.bootstrap.DateField.footer
19756 * @class Roo.bootstrap.TimeField
19757 * @extends Roo.bootstrap.Input
19758 * Bootstrap DateField class
19762 * Create a new TimeField
19763 * @param {Object} config The config object
19766 Roo.bootstrap.TimeField = function(config){
19767 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19771 * Fires when this field show.
19772 * @param {Roo.bootstrap.DateField} thisthis
19773 * @param {Mixed} date The date value
19778 * Fires when this field hide.
19779 * @param {Roo.bootstrap.DateField} this
19780 * @param {Mixed} date The date value
19785 * Fires when select a date.
19786 * @param {Roo.bootstrap.DateField} this
19787 * @param {Mixed} date The date value
19793 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19796 * @cfg {String} format
19797 * The default time format string which can be overriden for localization support. The format must be
19798 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19802 onRender: function(ct, position)
19805 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19807 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19809 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19811 this.pop = this.picker().select('>.datepicker-time',true).first();
19812 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19814 this.picker().on('mousedown', this.onMousedown, this);
19815 this.picker().on('click', this.onClick, this);
19817 this.picker().addClass('datepicker-dropdown');
19822 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19823 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19824 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19825 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19826 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19827 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19831 fireKey: function(e){
19832 if (!this.picker().isVisible()){
19833 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19839 e.preventDefault();
19847 this.onTogglePeriod();
19850 this.onIncrementMinutes();
19853 this.onDecrementMinutes();
19862 onClick: function(e) {
19863 e.stopPropagation();
19864 e.preventDefault();
19867 picker : function()
19869 return this.el.select('.datepicker', true).first();
19872 fillTime: function()
19874 var time = this.pop.select('tbody', true).first();
19876 time.dom.innerHTML = '';
19891 cls: 'hours-up glyphicon glyphicon-chevron-up'
19911 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19932 cls: 'timepicker-hour',
19947 cls: 'timepicker-minute',
19962 cls: 'btn btn-primary period',
19984 cls: 'hours-down glyphicon glyphicon-chevron-down'
20004 cls: 'minutes-down glyphicon glyphicon-chevron-down'
20022 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20029 var hours = this.time.getHours();
20030 var minutes = this.time.getMinutes();
20043 hours = hours - 12;
20047 hours = '0' + hours;
20051 minutes = '0' + minutes;
20054 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20055 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20056 this.pop.select('button', true).first().dom.innerHTML = period;
20062 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20064 var cls = ['bottom'];
20066 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20073 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20078 this.picker().addClass(cls.join('-'));
20082 Roo.each(cls, function(c){
20084 _this.picker().setTop(_this.inputEl().getHeight());
20088 _this.picker().setTop(0 - _this.picker().getHeight());
20093 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20097 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20104 onFocus : function()
20106 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20110 onBlur : function()
20112 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20118 this.picker().show();
20123 this.fireEvent('show', this, this.date);
20128 this.picker().hide();
20131 this.fireEvent('hide', this, this.date);
20134 setTime : function()
20137 this.setValue(this.time.format(this.format));
20139 this.fireEvent('select', this, this.date);
20144 onMousedown: function(e){
20145 e.stopPropagation();
20146 e.preventDefault();
20149 onIncrementHours: function()
20151 Roo.log('onIncrementHours');
20152 this.time = this.time.add(Date.HOUR, 1);
20157 onDecrementHours: function()
20159 Roo.log('onDecrementHours');
20160 this.time = this.time.add(Date.HOUR, -1);
20164 onIncrementMinutes: function()
20166 Roo.log('onIncrementMinutes');
20167 this.time = this.time.add(Date.MINUTE, 1);
20171 onDecrementMinutes: function()
20173 Roo.log('onDecrementMinutes');
20174 this.time = this.time.add(Date.MINUTE, -1);
20178 onTogglePeriod: function()
20180 Roo.log('onTogglePeriod');
20181 this.time = this.time.add(Date.HOUR, 12);
20188 Roo.apply(Roo.bootstrap.TimeField, {
20218 cls: 'btn btn-info ok',
20230 Roo.apply(Roo.bootstrap.TimeField, {
20234 cls: 'datepicker dropdown-menu',
20238 cls: 'datepicker-time',
20242 cls: 'table-condensed',
20244 Roo.bootstrap.TimeField.content,
20245 Roo.bootstrap.TimeField.footer
20264 * @class Roo.bootstrap.MonthField
20265 * @extends Roo.bootstrap.Input
20266 * Bootstrap MonthField class
20268 * @cfg {String} language default en
20271 * Create a new MonthField
20272 * @param {Object} config The config object
20275 Roo.bootstrap.MonthField = function(config){
20276 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20281 * Fires when this field show.
20282 * @param {Roo.bootstrap.MonthField} this
20283 * @param {Mixed} date The date value
20288 * Fires when this field hide.
20289 * @param {Roo.bootstrap.MonthField} this
20290 * @param {Mixed} date The date value
20295 * Fires when select a date.
20296 * @param {Roo.bootstrap.MonthField} this
20297 * @param {String} oldvalue The old value
20298 * @param {String} newvalue The new value
20304 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20306 onRender: function(ct, position)
20309 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20311 this.language = this.language || 'en';
20312 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20313 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20315 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20316 this.isInline = false;
20317 this.isInput = true;
20318 this.component = this.el.select('.add-on', true).first() || false;
20319 this.component = (this.component && this.component.length === 0) ? false : this.component;
20320 this.hasInput = this.component && this.inputEL().length;
20322 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20324 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20326 this.picker().on('mousedown', this.onMousedown, this);
20327 this.picker().on('click', this.onClick, this);
20329 this.picker().addClass('datepicker-dropdown');
20331 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20332 v.setStyle('width', '189px');
20339 if(this.isInline) {
20345 setValue: function(v, suppressEvent)
20347 var o = this.getValue();
20349 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20353 if(suppressEvent !== true){
20354 this.fireEvent('select', this, o, v);
20359 getValue: function()
20364 onClick: function(e)
20366 e.stopPropagation();
20367 e.preventDefault();
20369 var target = e.getTarget();
20371 if(target.nodeName.toLowerCase() === 'i'){
20372 target = Roo.get(target).dom.parentNode;
20375 var nodeName = target.nodeName;
20376 var className = target.className;
20377 var html = target.innerHTML;
20379 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20383 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20385 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20391 picker : function()
20393 return this.pickerEl;
20396 fillMonths: function()
20399 var months = this.picker().select('>.datepicker-months td', true).first();
20401 months.dom.innerHTML = '';
20407 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20410 months.createChild(month);
20419 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20420 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20423 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20424 e.removeClass('active');
20426 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20427 e.addClass('active');
20434 if(this.isInline) {
20438 this.picker().removeClass(['bottom', 'top']);
20440 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20442 * place to the top of element!
20446 this.picker().addClass('top');
20447 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20452 this.picker().addClass('bottom');
20454 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20457 onFocus : function()
20459 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20463 onBlur : function()
20465 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20467 var d = this.inputEl().getValue();
20476 this.picker().show();
20477 this.picker().select('>.datepicker-months', true).first().show();
20481 this.fireEvent('show', this, this.date);
20486 if(this.isInline) {
20489 this.picker().hide();
20490 this.fireEvent('hide', this, this.date);
20494 onMousedown: function(e)
20496 e.stopPropagation();
20497 e.preventDefault();
20502 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20506 fireKey: function(e)
20508 if (!this.picker().isVisible()){
20509 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20520 e.preventDefault();
20524 dir = e.keyCode == 37 ? -1 : 1;
20526 this.vIndex = this.vIndex + dir;
20528 if(this.vIndex < 0){
20532 if(this.vIndex > 11){
20536 if(isNaN(this.vIndex)){
20540 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20546 dir = e.keyCode == 38 ? -1 : 1;
20548 this.vIndex = this.vIndex + dir * 4;
20550 if(this.vIndex < 0){
20554 if(this.vIndex > 11){
20558 if(isNaN(this.vIndex)){
20562 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20567 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20568 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20572 e.preventDefault();
20575 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20576 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20592 this.picker().remove();
20597 Roo.apply(Roo.bootstrap.MonthField, {
20616 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20617 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20622 Roo.apply(Roo.bootstrap.MonthField, {
20626 cls: 'datepicker dropdown-menu roo-dynamic',
20630 cls: 'datepicker-months',
20634 cls: 'table-condensed',
20636 Roo.bootstrap.DateField.content
20656 * @class Roo.bootstrap.CheckBox
20657 * @extends Roo.bootstrap.Input
20658 * Bootstrap CheckBox class
20660 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20661 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20662 * @cfg {String} boxLabel The text that appears beside the checkbox
20663 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20664 * @cfg {Boolean} checked initnal the element
20665 * @cfg {Boolean} inline inline the element (default false)
20666 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20667 * @cfg {String} tooltip label tooltip
20670 * Create a new CheckBox
20671 * @param {Object} config The config object
20674 Roo.bootstrap.CheckBox = function(config){
20675 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20680 * Fires when the element is checked or unchecked.
20681 * @param {Roo.bootstrap.CheckBox} this This input
20682 * @param {Boolean} checked The new checked value
20687 * Fires when the element is click.
20688 * @param {Roo.bootstrap.CheckBox} this This input
20695 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20697 inputType: 'checkbox',
20706 getAutoCreate : function()
20708 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20714 cfg.cls = 'form-group ' + this.inputType; //input-group
20717 cfg.cls += ' ' + this.inputType + '-inline';
20723 type : this.inputType,
20724 value : this.inputValue,
20725 cls : 'roo-' + this.inputType, //'form-box',
20726 placeholder : this.placeholder || ''
20730 if(this.inputType != 'radio'){
20734 cls : 'roo-hidden-value',
20735 value : this.checked ? this.inputValue : this.valueOff
20740 if (this.weight) { // Validity check?
20741 cfg.cls += " " + this.inputType + "-" + this.weight;
20744 if (this.disabled) {
20745 input.disabled=true;
20749 input.checked = this.checked;
20754 input.name = this.name;
20756 if(this.inputType != 'radio'){
20757 hidden.name = this.name;
20758 input.name = '_hidden_' + this.name;
20763 input.cls += ' input-' + this.size;
20768 ['xs','sm','md','lg'].map(function(size){
20769 if (settings[size]) {
20770 cfg.cls += ' col-' + size + '-' + settings[size];
20774 var inputblock = input;
20776 if (this.before || this.after) {
20779 cls : 'input-group',
20784 inputblock.cn.push({
20786 cls : 'input-group-addon',
20791 inputblock.cn.push(input);
20793 if(this.inputType != 'radio'){
20794 inputblock.cn.push(hidden);
20798 inputblock.cn.push({
20800 cls : 'input-group-addon',
20807 if (align ==='left' && this.fieldLabel.length) {
20808 // Roo.log("left and has label");
20813 cls : 'control-label',
20814 html : this.fieldLabel
20824 if(this.labelWidth > 12){
20825 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20828 if(this.labelWidth < 13 && this.labelmd == 0){
20829 this.labelmd = this.labelWidth;
20832 if(this.labellg > 0){
20833 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20834 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20837 if(this.labelmd > 0){
20838 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20839 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20842 if(this.labelsm > 0){
20843 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20844 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20847 if(this.labelxs > 0){
20848 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20849 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20852 } else if ( this.fieldLabel.length) {
20853 // Roo.log(" label");
20857 tag: this.boxLabel ? 'span' : 'label',
20859 cls: 'control-label box-input-label',
20860 //cls : 'input-group-addon',
20861 html : this.fieldLabel
20870 // Roo.log(" no label && no align");
20871 cfg.cn = [ inputblock ] ;
20877 var boxLabelCfg = {
20879 //'for': id, // box label is handled by onclick - so no for...
20881 html: this.boxLabel
20885 boxLabelCfg.tooltip = this.tooltip;
20888 cfg.cn.push(boxLabelCfg);
20891 if(this.inputType != 'radio'){
20892 cfg.cn.push(hidden);
20900 * return the real input element.
20902 inputEl: function ()
20904 return this.el.select('input.roo-' + this.inputType,true).first();
20906 hiddenEl: function ()
20908 return this.el.select('input.roo-hidden-value',true).first();
20911 labelEl: function()
20913 return this.el.select('label.control-label',true).first();
20915 /* depricated... */
20919 return this.labelEl();
20922 boxLabelEl: function()
20924 return this.el.select('label.box-label',true).first();
20927 initEvents : function()
20929 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20931 this.inputEl().on('click', this.onClick, this);
20933 if (this.boxLabel) {
20934 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20937 this.startValue = this.getValue();
20940 Roo.bootstrap.CheckBox.register(this);
20944 onClick : function(e)
20946 if(this.fireEvent('click', this, e) !== false){
20947 this.setChecked(!this.checked);
20952 setChecked : function(state,suppressEvent)
20954 this.startValue = this.getValue();
20956 if(this.inputType == 'radio'){
20958 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20959 e.dom.checked = false;
20962 this.inputEl().dom.checked = true;
20964 this.inputEl().dom.value = this.inputValue;
20966 if(suppressEvent !== true){
20967 this.fireEvent('check', this, true);
20975 this.checked = state;
20977 this.inputEl().dom.checked = state;
20980 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20982 if(suppressEvent !== true){
20983 this.fireEvent('check', this, state);
20989 getValue : function()
20991 if(this.inputType == 'radio'){
20992 return this.getGroupValue();
20995 return this.hiddenEl().dom.value;
20999 getGroupValue : function()
21001 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21005 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21008 setValue : function(v,suppressEvent)
21010 if(this.inputType == 'radio'){
21011 this.setGroupValue(v, suppressEvent);
21015 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21020 setGroupValue : function(v, suppressEvent)
21022 this.startValue = this.getValue();
21024 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21025 e.dom.checked = false;
21027 if(e.dom.value == v){
21028 e.dom.checked = true;
21032 if(suppressEvent !== true){
21033 this.fireEvent('check', this, true);
21041 validate : function()
21043 if(this.getVisibilityEl().hasClass('hidden')){
21049 (this.inputType == 'radio' && this.validateRadio()) ||
21050 (this.inputType == 'checkbox' && this.validateCheckbox())
21056 this.markInvalid();
21060 validateRadio : function()
21062 if(this.getVisibilityEl().hasClass('hidden')){
21066 if(this.allowBlank){
21072 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21073 if(!e.dom.checked){
21085 validateCheckbox : function()
21088 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21089 //return (this.getValue() == this.inputValue) ? true : false;
21092 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21100 for(var i in group){
21101 if(group[i].el.isVisible(true)){
21109 for(var i in group){
21114 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21121 * Mark this field as valid
21123 markValid : function()
21127 this.fireEvent('valid', this);
21129 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21132 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21139 if(this.inputType == 'radio'){
21140 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21141 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21142 e.findParent('.form-group', false, true).addClass(_this.validClass);
21149 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21150 this.el.findParent('.form-group', false, true).addClass(this.validClass);
21154 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21160 for(var i in group){
21161 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21162 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21167 * Mark this field as invalid
21168 * @param {String} msg The validation message
21170 markInvalid : function(msg)
21172 if(this.allowBlank){
21178 this.fireEvent('invalid', this, msg);
21180 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21183 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21187 label.markInvalid();
21190 if(this.inputType == 'radio'){
21191 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21192 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21193 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21200 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21201 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21205 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21211 for(var i in group){
21212 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21213 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21218 clearInvalid : function()
21220 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21222 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21224 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21226 if (label && label.iconEl) {
21227 label.iconEl.removeClass(label.validClass);
21228 label.iconEl.removeClass(label.invalidClass);
21232 disable : function()
21234 if(this.inputType != 'radio'){
21235 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21242 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21243 _this.getActionEl().addClass(this.disabledClass);
21244 e.dom.disabled = true;
21248 this.disabled = true;
21249 this.fireEvent("disable", this);
21253 enable : function()
21255 if(this.inputType != 'radio'){
21256 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21263 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21264 _this.getActionEl().removeClass(this.disabledClass);
21265 e.dom.disabled = false;
21269 this.disabled = false;
21270 this.fireEvent("enable", this);
21274 setBoxLabel : function(v)
21279 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21285 Roo.apply(Roo.bootstrap.CheckBox, {
21290 * register a CheckBox Group
21291 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21293 register : function(checkbox)
21295 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21296 this.groups[checkbox.groupId] = {};
21299 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21303 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21307 * fetch a CheckBox Group based on the group ID
21308 * @param {string} the group ID
21309 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21311 get: function(groupId) {
21312 if (typeof(this.groups[groupId]) == 'undefined') {
21316 return this.groups[groupId] ;
21329 * @class Roo.bootstrap.Radio
21330 * @extends Roo.bootstrap.Component
21331 * Bootstrap Radio class
21332 * @cfg {String} boxLabel - the label associated
21333 * @cfg {String} value - the value of radio
21336 * Create a new Radio
21337 * @param {Object} config The config object
21339 Roo.bootstrap.Radio = function(config){
21340 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21344 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21350 getAutoCreate : function()
21354 cls : 'form-group radio',
21359 html : this.boxLabel
21367 initEvents : function()
21369 this.parent().register(this);
21371 this.el.on('click', this.onClick, this);
21375 onClick : function(e)
21377 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21378 this.setChecked(true);
21382 setChecked : function(state, suppressEvent)
21384 this.parent().setValue(this.value, suppressEvent);
21388 setBoxLabel : function(v)
21393 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21408 * @class Roo.bootstrap.SecurePass
21409 * @extends Roo.bootstrap.Input
21410 * Bootstrap SecurePass class
21414 * Create a new SecurePass
21415 * @param {Object} config The config object
21418 Roo.bootstrap.SecurePass = function (config) {
21419 // these go here, so the translation tool can replace them..
21421 PwdEmpty: "Please type a password, and then retype it to confirm.",
21422 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21423 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21424 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21425 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21426 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21427 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21428 TooWeak: "Your password is Too Weak."
21430 this.meterLabel = "Password strength:";
21431 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21432 this.meterClass = [
21433 "roo-password-meter-tooweak",
21434 "roo-password-meter-weak",
21435 "roo-password-meter-medium",
21436 "roo-password-meter-strong",
21437 "roo-password-meter-grey"
21442 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21445 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21447 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21449 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21450 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21451 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21452 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21453 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21454 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21455 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21465 * @cfg {String/Object} Label for the strength meter (defaults to
21466 * 'Password strength:')
21471 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21472 * ['Weak', 'Medium', 'Strong'])
21475 pwdStrengths: false,
21488 initEvents: function ()
21490 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21492 if (this.el.is('input[type=password]') && Roo.isSafari) {
21493 this.el.on('keydown', this.SafariOnKeyDown, this);
21496 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21499 onRender: function (ct, position)
21501 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21502 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21503 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21505 this.trigger.createChild({
21510 cls: 'roo-password-meter-grey col-xs-12',
21513 //width: this.meterWidth + 'px'
21517 cls: 'roo-password-meter-text'
21523 if (this.hideTrigger) {
21524 this.trigger.setDisplayed(false);
21526 this.setSize(this.width || '', this.height || '');
21529 onDestroy: function ()
21531 if (this.trigger) {
21532 this.trigger.removeAllListeners();
21533 this.trigger.remove();
21536 this.wrap.remove();
21538 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21541 checkStrength: function ()
21543 var pwd = this.inputEl().getValue();
21544 if (pwd == this._lastPwd) {
21549 if (this.ClientSideStrongPassword(pwd)) {
21551 } else if (this.ClientSideMediumPassword(pwd)) {
21553 } else if (this.ClientSideWeakPassword(pwd)) {
21559 Roo.log('strength1: ' + strength);
21561 //var pm = this.trigger.child('div/div/div').dom;
21562 var pm = this.trigger.child('div/div');
21563 pm.removeClass(this.meterClass);
21564 pm.addClass(this.meterClass[strength]);
21567 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21569 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21571 this._lastPwd = pwd;
21575 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21577 this._lastPwd = '';
21579 var pm = this.trigger.child('div/div');
21580 pm.removeClass(this.meterClass);
21581 pm.addClass('roo-password-meter-grey');
21584 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21587 this.inputEl().dom.type='password';
21590 validateValue: function (value)
21593 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21596 if (value.length == 0) {
21597 if (this.allowBlank) {
21598 this.clearInvalid();
21602 this.markInvalid(this.errors.PwdEmpty);
21603 this.errorMsg = this.errors.PwdEmpty;
21611 if ('[\x21-\x7e]*'.match(value)) {
21612 this.markInvalid(this.errors.PwdBadChar);
21613 this.errorMsg = this.errors.PwdBadChar;
21616 if (value.length < 6) {
21617 this.markInvalid(this.errors.PwdShort);
21618 this.errorMsg = this.errors.PwdShort;
21621 if (value.length > 16) {
21622 this.markInvalid(this.errors.PwdLong);
21623 this.errorMsg = this.errors.PwdLong;
21627 if (this.ClientSideStrongPassword(value)) {
21629 } else if (this.ClientSideMediumPassword(value)) {
21631 } else if (this.ClientSideWeakPassword(value)) {
21638 if (strength < 2) {
21639 //this.markInvalid(this.errors.TooWeak);
21640 this.errorMsg = this.errors.TooWeak;
21645 console.log('strength2: ' + strength);
21647 //var pm = this.trigger.child('div/div/div').dom;
21649 var pm = this.trigger.child('div/div');
21650 pm.removeClass(this.meterClass);
21651 pm.addClass(this.meterClass[strength]);
21653 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21655 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21657 this.errorMsg = '';
21661 CharacterSetChecks: function (type)
21664 this.fResult = false;
21667 isctype: function (character, type)
21670 case this.kCapitalLetter:
21671 if (character >= 'A' && character <= 'Z') {
21676 case this.kSmallLetter:
21677 if (character >= 'a' && character <= 'z') {
21683 if (character >= '0' && character <= '9') {
21688 case this.kPunctuation:
21689 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21700 IsLongEnough: function (pwd, size)
21702 return !(pwd == null || isNaN(size) || pwd.length < size);
21705 SpansEnoughCharacterSets: function (word, nb)
21707 if (!this.IsLongEnough(word, nb))
21712 var characterSetChecks = new Array(
21713 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21714 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21717 for (var index = 0; index < word.length; ++index) {
21718 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21719 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21720 characterSetChecks[nCharSet].fResult = true;
21727 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21728 if (characterSetChecks[nCharSet].fResult) {
21733 if (nCharSets < nb) {
21739 ClientSideStrongPassword: function (pwd)
21741 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21744 ClientSideMediumPassword: function (pwd)
21746 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21749 ClientSideWeakPassword: function (pwd)
21751 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21754 })//<script type="text/javascript">
21757 * Based Ext JS Library 1.1.1
21758 * Copyright(c) 2006-2007, Ext JS, LLC.
21764 * @class Roo.HtmlEditorCore
21765 * @extends Roo.Component
21766 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21768 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21771 Roo.HtmlEditorCore = function(config){
21774 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21779 * @event initialize
21780 * Fires when the editor is fully initialized (including the iframe)
21781 * @param {Roo.HtmlEditorCore} this
21786 * Fires when the editor is first receives the focus. Any insertion must wait
21787 * until after this event.
21788 * @param {Roo.HtmlEditorCore} this
21792 * @event beforesync
21793 * Fires before the textarea is updated with content from the editor iframe. Return false
21794 * to cancel the sync.
21795 * @param {Roo.HtmlEditorCore} this
21796 * @param {String} html
21800 * @event beforepush
21801 * Fires before the iframe editor is updated with content from the textarea. Return false
21802 * to cancel the push.
21803 * @param {Roo.HtmlEditorCore} this
21804 * @param {String} html
21809 * Fires when the textarea is updated with content from the editor iframe.
21810 * @param {Roo.HtmlEditorCore} this
21811 * @param {String} html
21816 * Fires when the iframe editor is updated with content from the textarea.
21817 * @param {Roo.HtmlEditorCore} this
21818 * @param {String} html
21823 * @event editorevent
21824 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21825 * @param {Roo.HtmlEditorCore} this
21831 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21833 // defaults : white / black...
21834 this.applyBlacklists();
21841 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21845 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21851 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21856 * @cfg {Number} height (in pixels)
21860 * @cfg {Number} width (in pixels)
21865 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21868 stylesheets: false,
21873 // private properties
21874 validationEvent : false,
21876 initialized : false,
21878 sourceEditMode : false,
21879 onFocus : Roo.emptyFn,
21881 hideMode:'offsets',
21885 // blacklist + whitelisted elements..
21892 * Protected method that will not generally be called directly. It
21893 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21894 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21896 getDocMarkup : function(){
21900 // inherit styels from page...??
21901 if (this.stylesheets === false) {
21903 Roo.get(document.head).select('style').each(function(node) {
21904 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21907 Roo.get(document.head).select('link').each(function(node) {
21908 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21911 } else if (!this.stylesheets.length) {
21913 st = '<style type="text/css">' +
21914 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21917 st = '<style type="text/css">' +
21922 st += '<style type="text/css">' +
21923 'IMG { cursor: pointer } ' +
21926 var cls = 'roo-htmleditor-body';
21928 if(this.bodyCls.length){
21929 cls += ' ' + this.bodyCls;
21932 return '<html><head>' + st +
21933 //<style type="text/css">' +
21934 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21936 ' </head><body class="' + cls + '"></body></html>';
21940 onRender : function(ct, position)
21943 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21944 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21947 this.el.dom.style.border = '0 none';
21948 this.el.dom.setAttribute('tabIndex', -1);
21949 this.el.addClass('x-hidden hide');
21953 if(Roo.isIE){ // fix IE 1px bogus margin
21954 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21958 this.frameId = Roo.id();
21962 var iframe = this.owner.wrap.createChild({
21964 cls: 'form-control', // bootstrap..
21966 name: this.frameId,
21967 frameBorder : 'no',
21968 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21973 this.iframe = iframe.dom;
21975 this.assignDocWin();
21977 this.doc.designMode = 'on';
21980 this.doc.write(this.getDocMarkup());
21984 var task = { // must defer to wait for browser to be ready
21986 //console.log("run task?" + this.doc.readyState);
21987 this.assignDocWin();
21988 if(this.doc.body || this.doc.readyState == 'complete'){
21990 this.doc.designMode="on";
21994 Roo.TaskMgr.stop(task);
21995 this.initEditor.defer(10, this);
22002 Roo.TaskMgr.start(task);
22007 onResize : function(w, h)
22009 Roo.log('resize: ' +w + ',' + h );
22010 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22014 if(typeof w == 'number'){
22016 this.iframe.style.width = w + 'px';
22018 if(typeof h == 'number'){
22020 this.iframe.style.height = h + 'px';
22022 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22029 * Toggles the editor between standard and source edit mode.
22030 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22032 toggleSourceEdit : function(sourceEditMode){
22034 this.sourceEditMode = sourceEditMode === true;
22036 if(this.sourceEditMode){
22038 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
22041 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22042 //this.iframe.className = '';
22045 //this.setSize(this.owner.wrap.getSize());
22046 //this.fireEvent('editmodechange', this, this.sourceEditMode);
22053 * Protected method that will not generally be called directly. If you need/want
22054 * custom HTML cleanup, this is the method you should override.
22055 * @param {String} html The HTML to be cleaned
22056 * return {String} The cleaned HTML
22058 cleanHtml : function(html){
22059 html = String(html);
22060 if(html.length > 5){
22061 if(Roo.isSafari){ // strip safari nonsense
22062 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22065 if(html == ' '){
22072 * HTML Editor -> Textarea
22073 * Protected method that will not generally be called directly. Syncs the contents
22074 * of the editor iframe with the textarea.
22076 syncValue : function(){
22077 if(this.initialized){
22078 var bd = (this.doc.body || this.doc.documentElement);
22079 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22080 var html = bd.innerHTML;
22082 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22083 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22085 html = '<div style="'+m[0]+'">' + html + '</div>';
22088 html = this.cleanHtml(html);
22089 // fix up the special chars.. normaly like back quotes in word...
22090 // however we do not want to do this with chinese..
22091 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22092 var cc = b.charCodeAt();
22094 (cc >= 0x4E00 && cc < 0xA000 ) ||
22095 (cc >= 0x3400 && cc < 0x4E00 ) ||
22096 (cc >= 0xf900 && cc < 0xfb00 )
22102 if(this.owner.fireEvent('beforesync', this, html) !== false){
22103 this.el.dom.value = html;
22104 this.owner.fireEvent('sync', this, html);
22110 * Protected method that will not generally be called directly. Pushes the value of the textarea
22111 * into the iframe editor.
22113 pushValue : function(){
22114 if(this.initialized){
22115 var v = this.el.dom.value.trim();
22117 // if(v.length < 1){
22121 if(this.owner.fireEvent('beforepush', this, v) !== false){
22122 var d = (this.doc.body || this.doc.documentElement);
22124 this.cleanUpPaste();
22125 this.el.dom.value = d.innerHTML;
22126 this.owner.fireEvent('push', this, v);
22132 deferFocus : function(){
22133 this.focus.defer(10, this);
22137 focus : function(){
22138 if(this.win && !this.sourceEditMode){
22145 assignDocWin: function()
22147 var iframe = this.iframe;
22150 this.doc = iframe.contentWindow.document;
22151 this.win = iframe.contentWindow;
22153 // if (!Roo.get(this.frameId)) {
22156 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22157 // this.win = Roo.get(this.frameId).dom.contentWindow;
22159 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22163 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22164 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22169 initEditor : function(){
22170 //console.log("INIT EDITOR");
22171 this.assignDocWin();
22175 this.doc.designMode="on";
22177 this.doc.write(this.getDocMarkup());
22180 var dbody = (this.doc.body || this.doc.documentElement);
22181 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22182 // this copies styles from the containing element into thsi one..
22183 // not sure why we need all of this..
22184 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22186 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22187 //ss['background-attachment'] = 'fixed'; // w3c
22188 dbody.bgProperties = 'fixed'; // ie
22189 //Roo.DomHelper.applyStyles(dbody, ss);
22190 Roo.EventManager.on(this.doc, {
22191 //'mousedown': this.onEditorEvent,
22192 'mouseup': this.onEditorEvent,
22193 'dblclick': this.onEditorEvent,
22194 'click': this.onEditorEvent,
22195 'keyup': this.onEditorEvent,
22200 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22202 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22203 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22205 this.initialized = true;
22207 this.owner.fireEvent('initialize', this);
22212 onDestroy : function(){
22218 //for (var i =0; i < this.toolbars.length;i++) {
22219 // // fixme - ask toolbars for heights?
22220 // this.toolbars[i].onDestroy();
22223 //this.wrap.dom.innerHTML = '';
22224 //this.wrap.remove();
22229 onFirstFocus : function(){
22231 this.assignDocWin();
22234 this.activated = true;
22237 if(Roo.isGecko){ // prevent silly gecko errors
22239 var s = this.win.getSelection();
22240 if(!s.focusNode || s.focusNode.nodeType != 3){
22241 var r = s.getRangeAt(0);
22242 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22247 this.execCmd('useCSS', true);
22248 this.execCmd('styleWithCSS', false);
22251 this.owner.fireEvent('activate', this);
22255 adjustFont: function(btn){
22256 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22257 //if(Roo.isSafari){ // safari
22260 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22261 if(Roo.isSafari){ // safari
22262 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22263 v = (v < 10) ? 10 : v;
22264 v = (v > 48) ? 48 : v;
22265 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22270 v = Math.max(1, v+adjust);
22272 this.execCmd('FontSize', v );
22275 onEditorEvent : function(e)
22277 this.owner.fireEvent('editorevent', this, e);
22278 // this.updateToolbar();
22279 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22282 insertTag : function(tg)
22284 // could be a bit smarter... -> wrap the current selected tRoo..
22285 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22287 range = this.createRange(this.getSelection());
22288 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22289 wrappingNode.appendChild(range.extractContents());
22290 range.insertNode(wrappingNode);
22297 this.execCmd("formatblock", tg);
22301 insertText : function(txt)
22305 var range = this.createRange();
22306 range.deleteContents();
22307 //alert(Sender.getAttribute('label'));
22309 range.insertNode(this.doc.createTextNode(txt));
22315 * Executes a Midas editor command on the editor document and performs necessary focus and
22316 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22317 * @param {String} cmd The Midas command
22318 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22320 relayCmd : function(cmd, value){
22322 this.execCmd(cmd, value);
22323 this.owner.fireEvent('editorevent', this);
22324 //this.updateToolbar();
22325 this.owner.deferFocus();
22329 * Executes a Midas editor command directly on the editor document.
22330 * For visual commands, you should use {@link #relayCmd} instead.
22331 * <b>This should only be called after the editor is initialized.</b>
22332 * @param {String} cmd The Midas command
22333 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22335 execCmd : function(cmd, value){
22336 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22343 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22345 * @param {String} text | dom node..
22347 insertAtCursor : function(text)
22350 if(!this.activated){
22356 var r = this.doc.selection.createRange();
22367 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22371 // from jquery ui (MIT licenced)
22373 var win = this.win;
22375 if (win.getSelection && win.getSelection().getRangeAt) {
22376 range = win.getSelection().getRangeAt(0);
22377 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22378 range.insertNode(node);
22379 } else if (win.document.selection && win.document.selection.createRange) {
22380 // no firefox support
22381 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22382 win.document.selection.createRange().pasteHTML(txt);
22384 // no firefox support
22385 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22386 this.execCmd('InsertHTML', txt);
22395 mozKeyPress : function(e){
22397 var c = e.getCharCode(), cmd;
22400 c = String.fromCharCode(c).toLowerCase();
22414 this.cleanUpPaste.defer(100, this);
22422 e.preventDefault();
22430 fixKeys : function(){ // load time branching for fastest keydown performance
22432 return function(e){
22433 var k = e.getKey(), r;
22436 r = this.doc.selection.createRange();
22439 r.pasteHTML('    ');
22446 r = this.doc.selection.createRange();
22448 var target = r.parentElement();
22449 if(!target || target.tagName.toLowerCase() != 'li'){
22451 r.pasteHTML('<br />');
22457 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22458 this.cleanUpPaste.defer(100, this);
22464 }else if(Roo.isOpera){
22465 return function(e){
22466 var k = e.getKey();
22470 this.execCmd('InsertHTML','    ');
22473 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22474 this.cleanUpPaste.defer(100, this);
22479 }else if(Roo.isSafari){
22480 return function(e){
22481 var k = e.getKey();
22485 this.execCmd('InsertText','\t');
22489 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22490 this.cleanUpPaste.defer(100, this);
22498 getAllAncestors: function()
22500 var p = this.getSelectedNode();
22503 a.push(p); // push blank onto stack..
22504 p = this.getParentElement();
22508 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22512 a.push(this.doc.body);
22516 lastSelNode : false,
22519 getSelection : function()
22521 this.assignDocWin();
22522 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22525 getSelectedNode: function()
22527 // this may only work on Gecko!!!
22529 // should we cache this!!!!
22534 var range = this.createRange(this.getSelection()).cloneRange();
22537 var parent = range.parentElement();
22539 var testRange = range.duplicate();
22540 testRange.moveToElementText(parent);
22541 if (testRange.inRange(range)) {
22544 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22547 parent = parent.parentElement;
22552 // is ancestor a text element.
22553 var ac = range.commonAncestorContainer;
22554 if (ac.nodeType == 3) {
22555 ac = ac.parentNode;
22558 var ar = ac.childNodes;
22561 var other_nodes = [];
22562 var has_other_nodes = false;
22563 for (var i=0;i<ar.length;i++) {
22564 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22567 // fullly contained node.
22569 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22574 // probably selected..
22575 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22576 other_nodes.push(ar[i]);
22580 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22585 has_other_nodes = true;
22587 if (!nodes.length && other_nodes.length) {
22588 nodes= other_nodes;
22590 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22596 createRange: function(sel)
22598 // this has strange effects when using with
22599 // top toolbar - not sure if it's a great idea.
22600 //this.editor.contentWindow.focus();
22601 if (typeof sel != "undefined") {
22603 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22605 return this.doc.createRange();
22608 return this.doc.createRange();
22611 getParentElement: function()
22614 this.assignDocWin();
22615 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22617 var range = this.createRange(sel);
22620 var p = range.commonAncestorContainer;
22621 while (p.nodeType == 3) { // text node
22632 * Range intersection.. the hard stuff...
22636 * [ -- selected range --- ]
22640 * if end is before start or hits it. fail.
22641 * if start is after end or hits it fail.
22643 * if either hits (but other is outside. - then it's not
22649 // @see http://www.thismuchiknow.co.uk/?p=64.
22650 rangeIntersectsNode : function(range, node)
22652 var nodeRange = node.ownerDocument.createRange();
22654 nodeRange.selectNode(node);
22656 nodeRange.selectNodeContents(node);
22659 var rangeStartRange = range.cloneRange();
22660 rangeStartRange.collapse(true);
22662 var rangeEndRange = range.cloneRange();
22663 rangeEndRange.collapse(false);
22665 var nodeStartRange = nodeRange.cloneRange();
22666 nodeStartRange.collapse(true);
22668 var nodeEndRange = nodeRange.cloneRange();
22669 nodeEndRange.collapse(false);
22671 return rangeStartRange.compareBoundaryPoints(
22672 Range.START_TO_START, nodeEndRange) == -1 &&
22673 rangeEndRange.compareBoundaryPoints(
22674 Range.START_TO_START, nodeStartRange) == 1;
22678 rangeCompareNode : function(range, node)
22680 var nodeRange = node.ownerDocument.createRange();
22682 nodeRange.selectNode(node);
22684 nodeRange.selectNodeContents(node);
22688 range.collapse(true);
22690 nodeRange.collapse(true);
22692 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22693 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22695 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22697 var nodeIsBefore = ss == 1;
22698 var nodeIsAfter = ee == -1;
22700 if (nodeIsBefore && nodeIsAfter) {
22703 if (!nodeIsBefore && nodeIsAfter) {
22704 return 1; //right trailed.
22707 if (nodeIsBefore && !nodeIsAfter) {
22708 return 2; // left trailed.
22714 // private? - in a new class?
22715 cleanUpPaste : function()
22717 // cleans up the whole document..
22718 Roo.log('cleanuppaste');
22720 this.cleanUpChildren(this.doc.body);
22721 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22722 if (clean != this.doc.body.innerHTML) {
22723 this.doc.body.innerHTML = clean;
22728 cleanWordChars : function(input) {// change the chars to hex code
22729 var he = Roo.HtmlEditorCore;
22731 var output = input;
22732 Roo.each(he.swapCodes, function(sw) {
22733 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22735 output = output.replace(swapper, sw[1]);
22742 cleanUpChildren : function (n)
22744 if (!n.childNodes.length) {
22747 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22748 this.cleanUpChild(n.childNodes[i]);
22755 cleanUpChild : function (node)
22758 //console.log(node);
22759 if (node.nodeName == "#text") {
22760 // clean up silly Windows -- stuff?
22763 if (node.nodeName == "#comment") {
22764 node.parentNode.removeChild(node);
22765 // clean up silly Windows -- stuff?
22768 var lcname = node.tagName.toLowerCase();
22769 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22770 // whitelist of tags..
22772 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22774 node.parentNode.removeChild(node);
22779 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22781 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22782 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22784 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22785 // remove_keep_children = true;
22788 if (remove_keep_children) {
22789 this.cleanUpChildren(node);
22790 // inserts everything just before this node...
22791 while (node.childNodes.length) {
22792 var cn = node.childNodes[0];
22793 node.removeChild(cn);
22794 node.parentNode.insertBefore(cn, node);
22796 node.parentNode.removeChild(node);
22800 if (!node.attributes || !node.attributes.length) {
22801 this.cleanUpChildren(node);
22805 function cleanAttr(n,v)
22808 if (v.match(/^\./) || v.match(/^\//)) {
22811 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22814 if (v.match(/^#/)) {
22817 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22818 node.removeAttribute(n);
22822 var cwhite = this.cwhite;
22823 var cblack = this.cblack;
22825 function cleanStyle(n,v)
22827 if (v.match(/expression/)) { //XSS?? should we even bother..
22828 node.removeAttribute(n);
22832 var parts = v.split(/;/);
22835 Roo.each(parts, function(p) {
22836 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22840 var l = p.split(':').shift().replace(/\s+/g,'');
22841 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22843 if ( cwhite.length && cblack.indexOf(l) > -1) {
22844 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22845 //node.removeAttribute(n);
22849 // only allow 'c whitelisted system attributes'
22850 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22851 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22852 //node.removeAttribute(n);
22862 if (clean.length) {
22863 node.setAttribute(n, clean.join(';'));
22865 node.removeAttribute(n);
22871 for (var i = node.attributes.length-1; i > -1 ; i--) {
22872 var a = node.attributes[i];
22875 if (a.name.toLowerCase().substr(0,2)=='on') {
22876 node.removeAttribute(a.name);
22879 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22880 node.removeAttribute(a.name);
22883 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22884 cleanAttr(a.name,a.value); // fixme..
22887 if (a.name == 'style') {
22888 cleanStyle(a.name,a.value);
22891 /// clean up MS crap..
22892 // tecnically this should be a list of valid class'es..
22895 if (a.name == 'class') {
22896 if (a.value.match(/^Mso/)) {
22897 node.className = '';
22900 if (a.value.match(/^body$/)) {
22901 node.className = '';
22912 this.cleanUpChildren(node);
22918 * Clean up MS wordisms...
22920 cleanWord : function(node)
22925 this.cleanWord(this.doc.body);
22928 if (node.nodeName == "#text") {
22929 // clean up silly Windows -- stuff?
22932 if (node.nodeName == "#comment") {
22933 node.parentNode.removeChild(node);
22934 // clean up silly Windows -- stuff?
22938 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22939 node.parentNode.removeChild(node);
22943 // remove - but keep children..
22944 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22945 while (node.childNodes.length) {
22946 var cn = node.childNodes[0];
22947 node.removeChild(cn);
22948 node.parentNode.insertBefore(cn, node);
22950 node.parentNode.removeChild(node);
22951 this.iterateChildren(node, this.cleanWord);
22955 if (node.className.length) {
22957 var cn = node.className.split(/\W+/);
22959 Roo.each(cn, function(cls) {
22960 if (cls.match(/Mso[a-zA-Z]+/)) {
22965 node.className = cna.length ? cna.join(' ') : '';
22967 node.removeAttribute("class");
22971 if (node.hasAttribute("lang")) {
22972 node.removeAttribute("lang");
22975 if (node.hasAttribute("style")) {
22977 var styles = node.getAttribute("style").split(";");
22979 Roo.each(styles, function(s) {
22980 if (!s.match(/:/)) {
22983 var kv = s.split(":");
22984 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22987 // what ever is left... we allow.
22990 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22991 if (!nstyle.length) {
22992 node.removeAttribute('style');
22995 this.iterateChildren(node, this.cleanWord);
23001 * iterateChildren of a Node, calling fn each time, using this as the scole..
23002 * @param {DomNode} node node to iterate children of.
23003 * @param {Function} fn method of this class to call on each item.
23005 iterateChildren : function(node, fn)
23007 if (!node.childNodes.length) {
23010 for (var i = node.childNodes.length-1; i > -1 ; i--) {
23011 fn.call(this, node.childNodes[i])
23017 * cleanTableWidths.
23019 * Quite often pasting from word etc.. results in tables with column and widths.
23020 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23023 cleanTableWidths : function(node)
23028 this.cleanTableWidths(this.doc.body);
23033 if (node.nodeName == "#text" || node.nodeName == "#comment") {
23036 Roo.log(node.tagName);
23037 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23038 this.iterateChildren(node, this.cleanTableWidths);
23041 if (node.hasAttribute('width')) {
23042 node.removeAttribute('width');
23046 if (node.hasAttribute("style")) {
23049 var styles = node.getAttribute("style").split(";");
23051 Roo.each(styles, function(s) {
23052 if (!s.match(/:/)) {
23055 var kv = s.split(":");
23056 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23059 // what ever is left... we allow.
23062 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23063 if (!nstyle.length) {
23064 node.removeAttribute('style');
23068 this.iterateChildren(node, this.cleanTableWidths);
23076 domToHTML : function(currentElement, depth, nopadtext) {
23078 depth = depth || 0;
23079 nopadtext = nopadtext || false;
23081 if (!currentElement) {
23082 return this.domToHTML(this.doc.body);
23085 //Roo.log(currentElement);
23087 var allText = false;
23088 var nodeName = currentElement.nodeName;
23089 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23091 if (nodeName == '#text') {
23093 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23098 if (nodeName != 'BODY') {
23101 // Prints the node tagName, such as <A>, <IMG>, etc
23104 for(i = 0; i < currentElement.attributes.length;i++) {
23106 var aname = currentElement.attributes.item(i).name;
23107 if (!currentElement.attributes.item(i).value.length) {
23110 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23113 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23122 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23125 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23130 // Traverse the tree
23132 var currentElementChild = currentElement.childNodes.item(i);
23133 var allText = true;
23134 var innerHTML = '';
23136 while (currentElementChild) {
23137 // Formatting code (indent the tree so it looks nice on the screen)
23138 var nopad = nopadtext;
23139 if (lastnode == 'SPAN') {
23143 if (currentElementChild.nodeName == '#text') {
23144 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23145 toadd = nopadtext ? toadd : toadd.trim();
23146 if (!nopad && toadd.length > 80) {
23147 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23149 innerHTML += toadd;
23152 currentElementChild = currentElement.childNodes.item(i);
23158 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23160 // Recursively traverse the tree structure of the child node
23161 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23162 lastnode = currentElementChild.nodeName;
23164 currentElementChild=currentElement.childNodes.item(i);
23170 // The remaining code is mostly for formatting the tree
23171 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23176 ret+= "</"+tagName+">";
23182 applyBlacklists : function()
23184 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23185 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23189 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23190 if (b.indexOf(tag) > -1) {
23193 this.white.push(tag);
23197 Roo.each(w, function(tag) {
23198 if (b.indexOf(tag) > -1) {
23201 if (this.white.indexOf(tag) > -1) {
23204 this.white.push(tag);
23209 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23210 if (w.indexOf(tag) > -1) {
23213 this.black.push(tag);
23217 Roo.each(b, function(tag) {
23218 if (w.indexOf(tag) > -1) {
23221 if (this.black.indexOf(tag) > -1) {
23224 this.black.push(tag);
23229 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23230 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23234 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23235 if (b.indexOf(tag) > -1) {
23238 this.cwhite.push(tag);
23242 Roo.each(w, function(tag) {
23243 if (b.indexOf(tag) > -1) {
23246 if (this.cwhite.indexOf(tag) > -1) {
23249 this.cwhite.push(tag);
23254 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23255 if (w.indexOf(tag) > -1) {
23258 this.cblack.push(tag);
23262 Roo.each(b, function(tag) {
23263 if (w.indexOf(tag) > -1) {
23266 if (this.cblack.indexOf(tag) > -1) {
23269 this.cblack.push(tag);
23274 setStylesheets : function(stylesheets)
23276 if(typeof(stylesheets) == 'string'){
23277 Roo.get(this.iframe.contentDocument.head).createChild({
23279 rel : 'stylesheet',
23288 Roo.each(stylesheets, function(s) {
23293 Roo.get(_this.iframe.contentDocument.head).createChild({
23295 rel : 'stylesheet',
23304 removeStylesheets : function()
23308 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23313 setStyle : function(style)
23315 Roo.get(this.iframe.contentDocument.head).createChild({
23324 // hide stuff that is not compatible
23338 * @event specialkey
23342 * @cfg {String} fieldClass @hide
23345 * @cfg {String} focusClass @hide
23348 * @cfg {String} autoCreate @hide
23351 * @cfg {String} inputType @hide
23354 * @cfg {String} invalidClass @hide
23357 * @cfg {String} invalidText @hide
23360 * @cfg {String} msgFx @hide
23363 * @cfg {String} validateOnBlur @hide
23367 Roo.HtmlEditorCore.white = [
23368 'area', 'br', 'img', 'input', 'hr', 'wbr',
23370 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23371 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23372 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23373 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23374 'table', 'ul', 'xmp',
23376 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23379 'dir', 'menu', 'ol', 'ul', 'dl',
23385 Roo.HtmlEditorCore.black = [
23386 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23388 'base', 'basefont', 'bgsound', 'blink', 'body',
23389 'frame', 'frameset', 'head', 'html', 'ilayer',
23390 'iframe', 'layer', 'link', 'meta', 'object',
23391 'script', 'style' ,'title', 'xml' // clean later..
23393 Roo.HtmlEditorCore.clean = [
23394 'script', 'style', 'title', 'xml'
23396 Roo.HtmlEditorCore.remove = [
23401 Roo.HtmlEditorCore.ablack = [
23405 Roo.HtmlEditorCore.aclean = [
23406 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23410 Roo.HtmlEditorCore.pwhite= [
23411 'http', 'https', 'mailto'
23414 // white listed style attributes.
23415 Roo.HtmlEditorCore.cwhite= [
23416 // 'text-align', /// default is to allow most things..
23422 // black listed style attributes.
23423 Roo.HtmlEditorCore.cblack= [
23424 // 'font-size' -- this can be set by the project
23428 Roo.HtmlEditorCore.swapCodes =[
23447 * @class Roo.bootstrap.HtmlEditor
23448 * @extends Roo.bootstrap.TextArea
23449 * Bootstrap HtmlEditor class
23452 * Create a new HtmlEditor
23453 * @param {Object} config The config object
23456 Roo.bootstrap.HtmlEditor = function(config){
23457 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23458 if (!this.toolbars) {
23459 this.toolbars = [];
23462 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23465 * @event initialize
23466 * Fires when the editor is fully initialized (including the iframe)
23467 * @param {HtmlEditor} this
23472 * Fires when the editor is first receives the focus. Any insertion must wait
23473 * until after this event.
23474 * @param {HtmlEditor} this
23478 * @event beforesync
23479 * Fires before the textarea is updated with content from the editor iframe. Return false
23480 * to cancel the sync.
23481 * @param {HtmlEditor} this
23482 * @param {String} html
23486 * @event beforepush
23487 * Fires before the iframe editor is updated with content from the textarea. Return false
23488 * to cancel the push.
23489 * @param {HtmlEditor} this
23490 * @param {String} html
23495 * Fires when the textarea is updated with content from the editor iframe.
23496 * @param {HtmlEditor} this
23497 * @param {String} html
23502 * Fires when the iframe editor is updated with content from the textarea.
23503 * @param {HtmlEditor} this
23504 * @param {String} html
23508 * @event editmodechange
23509 * Fires when the editor switches edit modes
23510 * @param {HtmlEditor} this
23511 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23513 editmodechange: true,
23515 * @event editorevent
23516 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23517 * @param {HtmlEditor} this
23521 * @event firstfocus
23522 * Fires when on first focus - needed by toolbars..
23523 * @param {HtmlEditor} this
23528 * Auto save the htmlEditor value as a file into Events
23529 * @param {HtmlEditor} this
23533 * @event savedpreview
23534 * preview the saved version of htmlEditor
23535 * @param {HtmlEditor} this
23542 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23546 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23551 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23556 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23561 * @cfg {Number} height (in pixels)
23565 * @cfg {Number} width (in pixels)
23570 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23573 stylesheets: false,
23578 // private properties
23579 validationEvent : false,
23581 initialized : false,
23584 onFocus : Roo.emptyFn,
23586 hideMode:'offsets',
23588 tbContainer : false,
23592 toolbarContainer :function() {
23593 return this.wrap.select('.x-html-editor-tb',true).first();
23597 * Protected method that will not generally be called directly. It
23598 * is called when the editor creates its toolbar. Override this method if you need to
23599 * add custom toolbar buttons.
23600 * @param {HtmlEditor} editor
23602 createToolbar : function(){
23603 Roo.log('renewing');
23604 Roo.log("create toolbars");
23606 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23607 this.toolbars[0].render(this.toolbarContainer());
23611 // if (!editor.toolbars || !editor.toolbars.length) {
23612 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23615 // for (var i =0 ; i < editor.toolbars.length;i++) {
23616 // editor.toolbars[i] = Roo.factory(
23617 // typeof(editor.toolbars[i]) == 'string' ?
23618 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23619 // Roo.bootstrap.HtmlEditor);
23620 // editor.toolbars[i].init(editor);
23626 onRender : function(ct, position)
23628 // Roo.log("Call onRender: " + this.xtype);
23630 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23632 this.wrap = this.inputEl().wrap({
23633 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23636 this.editorcore.onRender(ct, position);
23638 if (this.resizable) {
23639 this.resizeEl = new Roo.Resizable(this.wrap, {
23643 minHeight : this.height,
23644 height: this.height,
23645 handles : this.resizable,
23648 resize : function(r, w, h) {
23649 _t.onResize(w,h); // -something
23655 this.createToolbar(this);
23658 if(!this.width && this.resizable){
23659 this.setSize(this.wrap.getSize());
23661 if (this.resizeEl) {
23662 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23663 // should trigger onReize..
23669 onResize : function(w, h)
23671 Roo.log('resize: ' +w + ',' + h );
23672 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23676 if(this.inputEl() ){
23677 if(typeof w == 'number'){
23678 var aw = w - this.wrap.getFrameWidth('lr');
23679 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23682 if(typeof h == 'number'){
23683 var tbh = -11; // fixme it needs to tool bar size!
23684 for (var i =0; i < this.toolbars.length;i++) {
23685 // fixme - ask toolbars for heights?
23686 tbh += this.toolbars[i].el.getHeight();
23687 //if (this.toolbars[i].footer) {
23688 // tbh += this.toolbars[i].footer.el.getHeight();
23696 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23697 ah -= 5; // knock a few pixes off for look..
23698 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23702 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23703 this.editorcore.onResize(ew,eh);
23708 * Toggles the editor between standard and source edit mode.
23709 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23711 toggleSourceEdit : function(sourceEditMode)
23713 this.editorcore.toggleSourceEdit(sourceEditMode);
23715 if(this.editorcore.sourceEditMode){
23716 Roo.log('editor - showing textarea');
23719 // Roo.log(this.syncValue());
23721 this.inputEl().removeClass(['hide', 'x-hidden']);
23722 this.inputEl().dom.removeAttribute('tabIndex');
23723 this.inputEl().focus();
23725 Roo.log('editor - hiding textarea');
23727 // Roo.log(this.pushValue());
23730 this.inputEl().addClass(['hide', 'x-hidden']);
23731 this.inputEl().dom.setAttribute('tabIndex', -1);
23732 //this.deferFocus();
23735 if(this.resizable){
23736 this.setSize(this.wrap.getSize());
23739 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23742 // private (for BoxComponent)
23743 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23745 // private (for BoxComponent)
23746 getResizeEl : function(){
23750 // private (for BoxComponent)
23751 getPositionEl : function(){
23756 initEvents : function(){
23757 this.originalValue = this.getValue();
23761 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23764 // markInvalid : Roo.emptyFn,
23766 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23769 // clearInvalid : Roo.emptyFn,
23771 setValue : function(v){
23772 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23773 this.editorcore.pushValue();
23778 deferFocus : function(){
23779 this.focus.defer(10, this);
23783 focus : function(){
23784 this.editorcore.focus();
23790 onDestroy : function(){
23796 for (var i =0; i < this.toolbars.length;i++) {
23797 // fixme - ask toolbars for heights?
23798 this.toolbars[i].onDestroy();
23801 this.wrap.dom.innerHTML = '';
23802 this.wrap.remove();
23807 onFirstFocus : function(){
23808 //Roo.log("onFirstFocus");
23809 this.editorcore.onFirstFocus();
23810 for (var i =0; i < this.toolbars.length;i++) {
23811 this.toolbars[i].onFirstFocus();
23817 syncValue : function()
23819 this.editorcore.syncValue();
23822 pushValue : function()
23824 this.editorcore.pushValue();
23828 // hide stuff that is not compatible
23842 * @event specialkey
23846 * @cfg {String} fieldClass @hide
23849 * @cfg {String} focusClass @hide
23852 * @cfg {String} autoCreate @hide
23855 * @cfg {String} inputType @hide
23858 * @cfg {String} invalidClass @hide
23861 * @cfg {String} invalidText @hide
23864 * @cfg {String} msgFx @hide
23867 * @cfg {String} validateOnBlur @hide
23876 Roo.namespace('Roo.bootstrap.htmleditor');
23878 * @class Roo.bootstrap.HtmlEditorToolbar1
23883 new Roo.bootstrap.HtmlEditor({
23886 new Roo.bootstrap.HtmlEditorToolbar1({
23887 disable : { fonts: 1 , format: 1, ..., ... , ...],
23893 * @cfg {Object} disable List of elements to disable..
23894 * @cfg {Array} btns List of additional buttons.
23898 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23901 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23904 Roo.apply(this, config);
23906 // default disabled, based on 'good practice'..
23907 this.disable = this.disable || {};
23908 Roo.applyIf(this.disable, {
23911 specialElements : true
23913 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23915 this.editor = config.editor;
23916 this.editorcore = config.editor.editorcore;
23918 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23920 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23921 // dont call parent... till later.
23923 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23928 editorcore : false,
23933 "h1","h2","h3","h4","h5","h6",
23935 "abbr", "acronym", "address", "cite", "samp", "var",
23939 onRender : function(ct, position)
23941 // Roo.log("Call onRender: " + this.xtype);
23943 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23945 this.el.dom.style.marginBottom = '0';
23947 var editorcore = this.editorcore;
23948 var editor= this.editor;
23951 var btn = function(id,cmd , toggle, handler, html){
23953 var event = toggle ? 'toggle' : 'click';
23958 xns: Roo.bootstrap,
23961 enableToggle:toggle !== false,
23963 pressed : toggle ? false : null,
23966 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23967 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23973 // var cb_box = function...
23978 xns: Roo.bootstrap,
23979 glyphicon : 'font',
23983 xns: Roo.bootstrap,
23987 Roo.each(this.formats, function(f) {
23988 style.menu.items.push({
23990 xns: Roo.bootstrap,
23991 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23996 editorcore.insertTag(this.tagname);
24003 children.push(style);
24005 btn('bold',false,true);
24006 btn('italic',false,true);
24007 btn('align-left', 'justifyleft',true);
24008 btn('align-center', 'justifycenter',true);
24009 btn('align-right' , 'justifyright',true);
24010 btn('link', false, false, function(btn) {
24011 //Roo.log("create link?");
24012 var url = prompt(this.createLinkText, this.defaultLinkValue);
24013 if(url && url != 'http:/'+'/'){
24014 this.editorcore.relayCmd('createlink', url);
24017 btn('list','insertunorderedlist',true);
24018 btn('pencil', false,true, function(btn){
24020 this.toggleSourceEdit(btn.pressed);
24023 if (this.editor.btns.length > 0) {
24024 for (var i = 0; i<this.editor.btns.length; i++) {
24025 children.push(this.editor.btns[i]);
24033 xns: Roo.bootstrap,
24038 xns: Roo.bootstrap,
24043 cog.menu.items.push({
24045 xns: Roo.bootstrap,
24046 html : Clean styles,
24051 editorcore.insertTag(this.tagname);
24060 this.xtype = 'NavSimplebar';
24062 for(var i=0;i< children.length;i++) {
24064 this.buttons.add(this.addxtypeChild(children[i]));
24068 editor.on('editorevent', this.updateToolbar, this);
24070 onBtnClick : function(id)
24072 this.editorcore.relayCmd(id);
24073 this.editorcore.focus();
24077 * Protected method that will not generally be called directly. It triggers
24078 * a toolbar update by reading the markup state of the current selection in the editor.
24080 updateToolbar: function(){
24082 if(!this.editorcore.activated){
24083 this.editor.onFirstFocus(); // is this neeed?
24087 var btns = this.buttons;
24088 var doc = this.editorcore.doc;
24089 btns.get('bold').setActive(doc.queryCommandState('bold'));
24090 btns.get('italic').setActive(doc.queryCommandState('italic'));
24091 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24093 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24094 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24095 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24097 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24098 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24101 var ans = this.editorcore.getAllAncestors();
24102 if (this.formatCombo) {
24105 var store = this.formatCombo.store;
24106 this.formatCombo.setValue("");
24107 for (var i =0; i < ans.length;i++) {
24108 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24110 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24118 // hides menus... - so this cant be on a menu...
24119 Roo.bootstrap.MenuMgr.hideAll();
24121 Roo.bootstrap.MenuMgr.hideAll();
24122 //this.editorsyncValue();
24124 onFirstFocus: function() {
24125 this.buttons.each(function(item){
24129 toggleSourceEdit : function(sourceEditMode){
24132 if(sourceEditMode){
24133 Roo.log("disabling buttons");
24134 this.buttons.each( function(item){
24135 if(item.cmd != 'pencil'){
24141 Roo.log("enabling buttons");
24142 if(this.editorcore.initialized){
24143 this.buttons.each( function(item){
24149 Roo.log("calling toggole on editor");
24150 // tell the editor that it's been pressed..
24151 this.editor.toggleSourceEdit(sourceEditMode);
24161 * @class Roo.bootstrap.Table.AbstractSelectionModel
24162 * @extends Roo.util.Observable
24163 * Abstract base class for grid SelectionModels. It provides the interface that should be
24164 * implemented by descendant classes. This class should not be directly instantiated.
24167 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24168 this.locked = false;
24169 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24173 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24174 /** @ignore Called by the grid automatically. Do not call directly. */
24175 init : function(grid){
24181 * Locks the selections.
24184 this.locked = true;
24188 * Unlocks the selections.
24190 unlock : function(){
24191 this.locked = false;
24195 * Returns true if the selections are locked.
24196 * @return {Boolean}
24198 isLocked : function(){
24199 return this.locked;
24203 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24204 * @class Roo.bootstrap.Table.RowSelectionModel
24205 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24206 * It supports multiple selections and keyboard selection/navigation.
24208 * @param {Object} config
24211 Roo.bootstrap.Table.RowSelectionModel = function(config){
24212 Roo.apply(this, config);
24213 this.selections = new Roo.util.MixedCollection(false, function(o){
24218 this.lastActive = false;
24222 * @event selectionchange
24223 * Fires when the selection changes
24224 * @param {SelectionModel} this
24226 "selectionchange" : true,
24228 * @event afterselectionchange
24229 * Fires after the selection changes (eg. by key press or clicking)
24230 * @param {SelectionModel} this
24232 "afterselectionchange" : true,
24234 * @event beforerowselect
24235 * Fires when a row is selected being selected, return false to cancel.
24236 * @param {SelectionModel} this
24237 * @param {Number} rowIndex The selected index
24238 * @param {Boolean} keepExisting False if other selections will be cleared
24240 "beforerowselect" : true,
24243 * Fires when a row is selected.
24244 * @param {SelectionModel} this
24245 * @param {Number} rowIndex The selected index
24246 * @param {Roo.data.Record} r The record
24248 "rowselect" : true,
24250 * @event rowdeselect
24251 * Fires when a row is deselected.
24252 * @param {SelectionModel} this
24253 * @param {Number} rowIndex The selected index
24255 "rowdeselect" : true
24257 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24258 this.locked = false;
24261 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24263 * @cfg {Boolean} singleSelect
24264 * True to allow selection of only one row at a time (defaults to false)
24266 singleSelect : false,
24269 initEvents : function()
24272 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24273 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24274 //}else{ // allow click to work like normal
24275 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24277 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24278 this.grid.on("rowclick", this.handleMouseDown, this);
24280 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24281 "up" : function(e){
24283 this.selectPrevious(e.shiftKey);
24284 }else if(this.last !== false && this.lastActive !== false){
24285 var last = this.last;
24286 this.selectRange(this.last, this.lastActive-1);
24287 this.grid.getView().focusRow(this.lastActive);
24288 if(last !== false){
24292 this.selectFirstRow();
24294 this.fireEvent("afterselectionchange", this);
24296 "down" : function(e){
24298 this.selectNext(e.shiftKey);
24299 }else if(this.last !== false && this.lastActive !== false){
24300 var last = this.last;
24301 this.selectRange(this.last, this.lastActive+1);
24302 this.grid.getView().focusRow(this.lastActive);
24303 if(last !== false){
24307 this.selectFirstRow();
24309 this.fireEvent("afterselectionchange", this);
24313 this.grid.store.on('load', function(){
24314 this.selections.clear();
24317 var view = this.grid.view;
24318 view.on("refresh", this.onRefresh, this);
24319 view.on("rowupdated", this.onRowUpdated, this);
24320 view.on("rowremoved", this.onRemove, this);
24325 onRefresh : function()
24327 var ds = this.grid.store, i, v = this.grid.view;
24328 var s = this.selections;
24329 s.each(function(r){
24330 if((i = ds.indexOfId(r.id)) != -1){
24339 onRemove : function(v, index, r){
24340 this.selections.remove(r);
24344 onRowUpdated : function(v, index, r){
24345 if(this.isSelected(r)){
24346 v.onRowSelect(index);
24352 * @param {Array} records The records to select
24353 * @param {Boolean} keepExisting (optional) True to keep existing selections
24355 selectRecords : function(records, keepExisting)
24358 this.clearSelections();
24360 var ds = this.grid.store;
24361 for(var i = 0, len = records.length; i < len; i++){
24362 this.selectRow(ds.indexOf(records[i]), true);
24367 * Gets the number of selected rows.
24370 getCount : function(){
24371 return this.selections.length;
24375 * Selects the first row in the grid.
24377 selectFirstRow : function(){
24382 * Select the last row.
24383 * @param {Boolean} keepExisting (optional) True to keep existing selections
24385 selectLastRow : function(keepExisting){
24386 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24387 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24391 * Selects the row immediately following the last selected row.
24392 * @param {Boolean} keepExisting (optional) True to keep existing selections
24394 selectNext : function(keepExisting)
24396 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24397 this.selectRow(this.last+1, keepExisting);
24398 this.grid.getView().focusRow(this.last);
24403 * Selects the row that precedes the last selected row.
24404 * @param {Boolean} keepExisting (optional) True to keep existing selections
24406 selectPrevious : function(keepExisting){
24408 this.selectRow(this.last-1, keepExisting);
24409 this.grid.getView().focusRow(this.last);
24414 * Returns the selected records
24415 * @return {Array} Array of selected records
24417 getSelections : function(){
24418 return [].concat(this.selections.items);
24422 * Returns the first selected record.
24425 getSelected : function(){
24426 return this.selections.itemAt(0);
24431 * Clears all selections.
24433 clearSelections : function(fast)
24439 var ds = this.grid.store;
24440 var s = this.selections;
24441 s.each(function(r){
24442 this.deselectRow(ds.indexOfId(r.id));
24446 this.selections.clear();
24453 * Selects all rows.
24455 selectAll : function(){
24459 this.selections.clear();
24460 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24461 this.selectRow(i, true);
24466 * Returns True if there is a selection.
24467 * @return {Boolean}
24469 hasSelection : function(){
24470 return this.selections.length > 0;
24474 * Returns True if the specified row is selected.
24475 * @param {Number/Record} record The record or index of the record to check
24476 * @return {Boolean}
24478 isSelected : function(index){
24479 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24480 return (r && this.selections.key(r.id) ? true : false);
24484 * Returns True if the specified record id is selected.
24485 * @param {String} id The id of record to check
24486 * @return {Boolean}
24488 isIdSelected : function(id){
24489 return (this.selections.key(id) ? true : false);
24494 handleMouseDBClick : function(e, t){
24498 handleMouseDown : function(e, t)
24500 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24501 if(this.isLocked() || rowIndex < 0 ){
24504 if(e.shiftKey && this.last !== false){
24505 var last = this.last;
24506 this.selectRange(last, rowIndex, e.ctrlKey);
24507 this.last = last; // reset the last
24511 var isSelected = this.isSelected(rowIndex);
24512 //Roo.log("select row:" + rowIndex);
24514 this.deselectRow(rowIndex);
24516 this.selectRow(rowIndex, true);
24520 if(e.button !== 0 && isSelected){
24521 alert('rowIndex 2: ' + rowIndex);
24522 view.focusRow(rowIndex);
24523 }else if(e.ctrlKey && isSelected){
24524 this.deselectRow(rowIndex);
24525 }else if(!isSelected){
24526 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24527 view.focusRow(rowIndex);
24531 this.fireEvent("afterselectionchange", this);
24534 handleDragableRowClick : function(grid, rowIndex, e)
24536 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24537 this.selectRow(rowIndex, false);
24538 grid.view.focusRow(rowIndex);
24539 this.fireEvent("afterselectionchange", this);
24544 * Selects multiple rows.
24545 * @param {Array} rows Array of the indexes of the row to select
24546 * @param {Boolean} keepExisting (optional) True to keep existing selections
24548 selectRows : function(rows, keepExisting){
24550 this.clearSelections();
24552 for(var i = 0, len = rows.length; i < len; i++){
24553 this.selectRow(rows[i], true);
24558 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24559 * @param {Number} startRow The index of the first row in the range
24560 * @param {Number} endRow The index of the last row in the range
24561 * @param {Boolean} keepExisting (optional) True to retain existing selections
24563 selectRange : function(startRow, endRow, keepExisting){
24568 this.clearSelections();
24570 if(startRow <= endRow){
24571 for(var i = startRow; i <= endRow; i++){
24572 this.selectRow(i, true);
24575 for(var i = startRow; i >= endRow; i--){
24576 this.selectRow(i, true);
24582 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24583 * @param {Number} startRow The index of the first row in the range
24584 * @param {Number} endRow The index of the last row in the range
24586 deselectRange : function(startRow, endRow, preventViewNotify){
24590 for(var i = startRow; i <= endRow; i++){
24591 this.deselectRow(i, preventViewNotify);
24597 * @param {Number} row The index of the row to select
24598 * @param {Boolean} keepExisting (optional) True to keep existing selections
24600 selectRow : function(index, keepExisting, preventViewNotify)
24602 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24605 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24606 if(!keepExisting || this.singleSelect){
24607 this.clearSelections();
24610 var r = this.grid.store.getAt(index);
24611 //console.log('selectRow - record id :' + r.id);
24613 this.selections.add(r);
24614 this.last = this.lastActive = index;
24615 if(!preventViewNotify){
24616 var proxy = new Roo.Element(
24617 this.grid.getRowDom(index)
24619 proxy.addClass('bg-info info');
24621 this.fireEvent("rowselect", this, index, r);
24622 this.fireEvent("selectionchange", this);
24628 * @param {Number} row The index of the row to deselect
24630 deselectRow : function(index, preventViewNotify)
24635 if(this.last == index){
24638 if(this.lastActive == index){
24639 this.lastActive = false;
24642 var r = this.grid.store.getAt(index);
24647 this.selections.remove(r);
24648 //.console.log('deselectRow - record id :' + r.id);
24649 if(!preventViewNotify){
24651 var proxy = new Roo.Element(
24652 this.grid.getRowDom(index)
24654 proxy.removeClass('bg-info info');
24656 this.fireEvent("rowdeselect", this, index);
24657 this.fireEvent("selectionchange", this);
24661 restoreLast : function(){
24663 this.last = this._last;
24668 acceptsNav : function(row, col, cm){
24669 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24673 onEditorKey : function(field, e){
24674 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24679 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24681 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24683 }else if(k == e.ENTER && !e.ctrlKey){
24687 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24689 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24691 }else if(k == e.ESC){
24695 g.startEditing(newCell[0], newCell[1]);
24701 * Ext JS Library 1.1.1
24702 * Copyright(c) 2006-2007, Ext JS, LLC.
24704 * Originally Released Under LGPL - original licence link has changed is not relivant.
24707 * <script type="text/javascript">
24711 * @class Roo.bootstrap.PagingToolbar
24712 * @extends Roo.bootstrap.NavSimplebar
24713 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24715 * Create a new PagingToolbar
24716 * @param {Object} config The config object
24717 * @param {Roo.data.Store} store
24719 Roo.bootstrap.PagingToolbar = function(config)
24721 // old args format still supported... - xtype is prefered..
24722 // created from xtype...
24724 this.ds = config.dataSource;
24726 if (config.store && !this.ds) {
24727 this.store= Roo.factory(config.store, Roo.data);
24728 this.ds = this.store;
24729 this.ds.xmodule = this.xmodule || false;
24732 this.toolbarItems = [];
24733 if (config.items) {
24734 this.toolbarItems = config.items;
24737 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24742 this.bind(this.ds);
24745 if (Roo.bootstrap.version == 4) {
24746 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24748 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24753 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24755 * @cfg {Roo.data.Store} dataSource
24756 * The underlying data store providing the paged data
24759 * @cfg {String/HTMLElement/Element} container
24760 * container The id or element that will contain the toolbar
24763 * @cfg {Boolean} displayInfo
24764 * True to display the displayMsg (defaults to false)
24767 * @cfg {Number} pageSize
24768 * The number of records to display per page (defaults to 20)
24772 * @cfg {String} displayMsg
24773 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24775 displayMsg : 'Displaying {0} - {1} of {2}',
24777 * @cfg {String} emptyMsg
24778 * The message to display when no records are found (defaults to "No data to display")
24780 emptyMsg : 'No data to display',
24782 * Customizable piece of the default paging text (defaults to "Page")
24785 beforePageText : "Page",
24787 * Customizable piece of the default paging text (defaults to "of %0")
24790 afterPageText : "of {0}",
24792 * Customizable piece of the default paging text (defaults to "First Page")
24795 firstText : "First Page",
24797 * Customizable piece of the default paging text (defaults to "Previous Page")
24800 prevText : "Previous Page",
24802 * Customizable piece of the default paging text (defaults to "Next Page")
24805 nextText : "Next Page",
24807 * Customizable piece of the default paging text (defaults to "Last Page")
24810 lastText : "Last Page",
24812 * Customizable piece of the default paging text (defaults to "Refresh")
24815 refreshText : "Refresh",
24819 onRender : function(ct, position)
24821 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24822 this.navgroup.parentId = this.id;
24823 this.navgroup.onRender(this.el, null);
24824 // add the buttons to the navgroup
24826 if(this.displayInfo){
24827 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24828 this.displayEl = this.el.select('.x-paging-info', true).first();
24829 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24830 // this.displayEl = navel.el.select('span',true).first();
24836 Roo.each(_this.buttons, function(e){ // this might need to use render????
24837 Roo.factory(e).render(_this.el);
24841 Roo.each(_this.toolbarItems, function(e) {
24842 _this.navgroup.addItem(e);
24846 this.first = this.navgroup.addItem({
24847 tooltip: this.firstText,
24848 cls: "prev btn-outline-secondary",
24849 html : ' <i class="fa fa-step-backward"></i>',
24851 preventDefault: true,
24852 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24855 this.prev = this.navgroup.addItem({
24856 tooltip: this.prevText,
24857 cls: "prev btn-outline-secondary",
24858 html : ' <i class="fa fa-backward"></i>',
24860 preventDefault: true,
24861 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24863 //this.addSeparator();
24866 var field = this.navgroup.addItem( {
24868 cls : 'x-paging-position btn-outline-secondary',
24870 html : this.beforePageText +
24871 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24872 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24875 this.field = field.el.select('input', true).first();
24876 this.field.on("keydown", this.onPagingKeydown, this);
24877 this.field.on("focus", function(){this.dom.select();});
24880 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24881 //this.field.setHeight(18);
24882 //this.addSeparator();
24883 this.next = this.navgroup.addItem({
24884 tooltip: this.nextText,
24885 cls: "next btn-outline-secondary",
24886 html : ' <i class="fa fa-forward"></i>',
24888 preventDefault: true,
24889 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24891 this.last = this.navgroup.addItem({
24892 tooltip: this.lastText,
24893 html : ' <i class="fa fa-step-forward"></i>',
24894 cls: "next btn-outline-secondary",
24896 preventDefault: true,
24897 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24899 //this.addSeparator();
24900 this.loading = this.navgroup.addItem({
24901 tooltip: this.refreshText,
24902 cls: "btn-outline-secondary",
24903 html : ' <i class="fa fa-refresh"></i>',
24904 preventDefault: true,
24905 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24911 updateInfo : function(){
24912 if(this.displayEl){
24913 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24914 var msg = count == 0 ?
24918 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24920 this.displayEl.update(msg);
24925 onLoad : function(ds, r, o)
24927 this.cursor = o.params.start ? o.params.start : 0;
24929 var d = this.getPageData(),
24934 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24935 this.field.dom.value = ap;
24936 this.first.setDisabled(ap == 1);
24937 this.prev.setDisabled(ap == 1);
24938 this.next.setDisabled(ap == ps);
24939 this.last.setDisabled(ap == ps);
24940 this.loading.enable();
24945 getPageData : function(){
24946 var total = this.ds.getTotalCount();
24949 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24950 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24955 onLoadError : function(){
24956 this.loading.enable();
24960 onPagingKeydown : function(e){
24961 var k = e.getKey();
24962 var d = this.getPageData();
24964 var v = this.field.dom.value, pageNum;
24965 if(!v || isNaN(pageNum = parseInt(v, 10))){
24966 this.field.dom.value = d.activePage;
24969 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24970 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24973 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))
24975 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24976 this.field.dom.value = pageNum;
24977 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24980 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24982 var v = this.field.dom.value, pageNum;
24983 var increment = (e.shiftKey) ? 10 : 1;
24984 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24987 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24988 this.field.dom.value = d.activePage;
24991 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24993 this.field.dom.value = parseInt(v, 10) + increment;
24994 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24995 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25002 beforeLoad : function(){
25004 this.loading.disable();
25009 onClick : function(which){
25018 ds.load({params:{start: 0, limit: this.pageSize}});
25021 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25024 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25027 var total = ds.getTotalCount();
25028 var extra = total % this.pageSize;
25029 var lastStart = extra ? (total - extra) : total-this.pageSize;
25030 ds.load({params:{start: lastStart, limit: this.pageSize}});
25033 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25039 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25040 * @param {Roo.data.Store} store The data store to unbind
25042 unbind : function(ds){
25043 ds.un("beforeload", this.beforeLoad, this);
25044 ds.un("load", this.onLoad, this);
25045 ds.un("loadexception", this.onLoadError, this);
25046 ds.un("remove", this.updateInfo, this);
25047 ds.un("add", this.updateInfo, this);
25048 this.ds = undefined;
25052 * Binds the paging toolbar to the specified {@link Roo.data.Store}
25053 * @param {Roo.data.Store} store The data store to bind
25055 bind : function(ds){
25056 ds.on("beforeload", this.beforeLoad, this);
25057 ds.on("load", this.onLoad, this);
25058 ds.on("loadexception", this.onLoadError, this);
25059 ds.on("remove", this.updateInfo, this);
25060 ds.on("add", this.updateInfo, this);
25071 * @class Roo.bootstrap.MessageBar
25072 * @extends Roo.bootstrap.Component
25073 * Bootstrap MessageBar class
25074 * @cfg {String} html contents of the MessageBar
25075 * @cfg {String} weight (info | success | warning | danger) default info
25076 * @cfg {String} beforeClass insert the bar before the given class
25077 * @cfg {Boolean} closable (true | false) default false
25078 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25081 * Create a new Element
25082 * @param {Object} config The config object
25085 Roo.bootstrap.MessageBar = function(config){
25086 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25089 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25095 beforeClass: 'bootstrap-sticky-wrap',
25097 getAutoCreate : function(){
25101 cls: 'alert alert-dismissable alert-' + this.weight,
25106 html: this.html || ''
25112 cfg.cls += ' alert-messages-fixed';
25126 onRender : function(ct, position)
25128 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25131 var cfg = Roo.apply({}, this.getAutoCreate());
25135 cfg.cls += ' ' + this.cls;
25138 cfg.style = this.style;
25140 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25142 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25145 this.el.select('>button.close').on('click', this.hide, this);
25151 if (!this.rendered) {
25157 this.fireEvent('show', this);
25163 if (!this.rendered) {
25169 this.fireEvent('hide', this);
25172 update : function()
25174 // var e = this.el.dom.firstChild;
25176 // if(this.closable){
25177 // e = e.nextSibling;
25180 // e.data = this.html || '';
25182 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25198 * @class Roo.bootstrap.Graph
25199 * @extends Roo.bootstrap.Component
25200 * Bootstrap Graph class
25204 @cfg {String} graphtype bar | vbar | pie
25205 @cfg {number} g_x coodinator | centre x (pie)
25206 @cfg {number} g_y coodinator | centre y (pie)
25207 @cfg {number} g_r radius (pie)
25208 @cfg {number} g_height height of the chart (respected by all elements in the set)
25209 @cfg {number} g_width width of the chart (respected by all elements in the set)
25210 @cfg {Object} title The title of the chart
25213 -opts (object) options for the chart
25215 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25216 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25218 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.
25219 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25221 o stretch (boolean)
25223 -opts (object) options for the pie
25226 o startAngle (number)
25227 o endAngle (number)
25231 * Create a new Input
25232 * @param {Object} config The config object
25235 Roo.bootstrap.Graph = function(config){
25236 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25242 * The img click event for the img.
25243 * @param {Roo.EventObject} e
25249 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25260 //g_colors: this.colors,
25267 getAutoCreate : function(){
25278 onRender : function(ct,position){
25281 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25283 if (typeof(Raphael) == 'undefined') {
25284 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25288 this.raphael = Raphael(this.el.dom);
25290 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25291 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25292 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25293 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25295 r.text(160, 10, "Single Series Chart").attr(txtattr);
25296 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25297 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25298 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25300 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25301 r.barchart(330, 10, 300, 220, data1);
25302 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25303 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25306 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25307 // r.barchart(30, 30, 560, 250, xdata, {
25308 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25309 // axis : "0 0 1 1",
25310 // axisxlabels : xdata
25311 // //yvalues : cols,
25314 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25316 // this.load(null,xdata,{
25317 // axis : "0 0 1 1",
25318 // axisxlabels : xdata
25323 load : function(graphtype,xdata,opts)
25325 this.raphael.clear();
25327 graphtype = this.graphtype;
25332 var r = this.raphael,
25333 fin = function () {
25334 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25336 fout = function () {
25337 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25339 pfin = function() {
25340 this.sector.stop();
25341 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25344 this.label[0].stop();
25345 this.label[0].attr({ r: 7.5 });
25346 this.label[1].attr({ "font-weight": 800 });
25349 pfout = function() {
25350 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25353 this.label[0].animate({ r: 5 }, 500, "bounce");
25354 this.label[1].attr({ "font-weight": 400 });
25360 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25363 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25366 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25367 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25369 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25376 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25381 setTitle: function(o)
25386 initEvents: function() {
25389 this.el.on('click', this.onClick, this);
25393 onClick : function(e)
25395 Roo.log('img onclick');
25396 this.fireEvent('click', this, e);
25408 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25411 * @class Roo.bootstrap.dash.NumberBox
25412 * @extends Roo.bootstrap.Component
25413 * Bootstrap NumberBox class
25414 * @cfg {String} headline Box headline
25415 * @cfg {String} content Box content
25416 * @cfg {String} icon Box icon
25417 * @cfg {String} footer Footer text
25418 * @cfg {String} fhref Footer href
25421 * Create a new NumberBox
25422 * @param {Object} config The config object
25426 Roo.bootstrap.dash.NumberBox = function(config){
25427 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25431 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25440 getAutoCreate : function(){
25444 cls : 'small-box ',
25452 cls : 'roo-headline',
25453 html : this.headline
25457 cls : 'roo-content',
25458 html : this.content
25472 cls : 'ion ' + this.icon
25481 cls : 'small-box-footer',
25482 href : this.fhref || '#',
25486 cfg.cn.push(footer);
25493 onRender : function(ct,position){
25494 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25501 setHeadline: function (value)
25503 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25506 setFooter: function (value, href)
25508 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25511 this.el.select('a.small-box-footer',true).first().attr('href', href);
25516 setContent: function (value)
25518 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25521 initEvents: function()
25535 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25538 * @class Roo.bootstrap.dash.TabBox
25539 * @extends Roo.bootstrap.Component
25540 * Bootstrap TabBox class
25541 * @cfg {String} title Title of the TabBox
25542 * @cfg {String} icon Icon of the TabBox
25543 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25544 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25547 * Create a new TabBox
25548 * @param {Object} config The config object
25552 Roo.bootstrap.dash.TabBox = function(config){
25553 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25558 * When a pane is added
25559 * @param {Roo.bootstrap.dash.TabPane} pane
25563 * @event activatepane
25564 * When a pane is activated
25565 * @param {Roo.bootstrap.dash.TabPane} pane
25567 "activatepane" : true
25575 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25580 tabScrollable : false,
25582 getChildContainer : function()
25584 return this.el.select('.tab-content', true).first();
25587 getAutoCreate : function(){
25591 cls: 'pull-left header',
25599 cls: 'fa ' + this.icon
25605 cls: 'nav nav-tabs pull-right',
25611 if(this.tabScrollable){
25618 cls: 'nav nav-tabs pull-right',
25629 cls: 'nav-tabs-custom',
25634 cls: 'tab-content no-padding',
25642 initEvents : function()
25644 //Roo.log('add add pane handler');
25645 this.on('addpane', this.onAddPane, this);
25648 * Updates the box title
25649 * @param {String} html to set the title to.
25651 setTitle : function(value)
25653 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25655 onAddPane : function(pane)
25657 this.panes.push(pane);
25658 //Roo.log('addpane');
25660 // tabs are rendere left to right..
25661 if(!this.showtabs){
25665 var ctr = this.el.select('.nav-tabs', true).first();
25668 var existing = ctr.select('.nav-tab',true);
25669 var qty = existing.getCount();;
25672 var tab = ctr.createChild({
25674 cls : 'nav-tab' + (qty ? '' : ' active'),
25682 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25685 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25687 pane.el.addClass('active');
25692 onTabClick : function(ev,un,ob,pane)
25694 //Roo.log('tab - prev default');
25695 ev.preventDefault();
25698 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25699 pane.tab.addClass('active');
25700 //Roo.log(pane.title);
25701 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25702 // technically we should have a deactivate event.. but maybe add later.
25703 // and it should not de-activate the selected tab...
25704 this.fireEvent('activatepane', pane);
25705 pane.el.addClass('active');
25706 pane.fireEvent('activate');
25711 getActivePane : function()
25714 Roo.each(this.panes, function(p) {
25715 if(p.el.hasClass('active')){
25736 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25738 * @class Roo.bootstrap.TabPane
25739 * @extends Roo.bootstrap.Component
25740 * Bootstrap TabPane class
25741 * @cfg {Boolean} active (false | true) Default false
25742 * @cfg {String} title title of panel
25746 * Create a new TabPane
25747 * @param {Object} config The config object
25750 Roo.bootstrap.dash.TabPane = function(config){
25751 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25757 * When a pane is activated
25758 * @param {Roo.bootstrap.dash.TabPane} pane
25765 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25770 // the tabBox that this is attached to.
25773 getAutoCreate : function()
25781 cfg.cls += ' active';
25786 initEvents : function()
25788 //Roo.log('trigger add pane handler');
25789 this.parent().fireEvent('addpane', this)
25793 * Updates the tab title
25794 * @param {String} html to set the title to.
25796 setTitle: function(str)
25802 this.tab.select('a', true).first().dom.innerHTML = str;
25819 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25822 * @class Roo.bootstrap.menu.Menu
25823 * @extends Roo.bootstrap.Component
25824 * Bootstrap Menu class - container for Menu
25825 * @cfg {String} html Text of the menu
25826 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25827 * @cfg {String} icon Font awesome icon
25828 * @cfg {String} pos Menu align to (top | bottom) default bottom
25832 * Create a new Menu
25833 * @param {Object} config The config object
25837 Roo.bootstrap.menu.Menu = function(config){
25838 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25842 * @event beforeshow
25843 * Fires before this menu is displayed
25844 * @param {Roo.bootstrap.menu.Menu} this
25848 * @event beforehide
25849 * Fires before this menu is hidden
25850 * @param {Roo.bootstrap.menu.Menu} this
25855 * Fires after this menu is displayed
25856 * @param {Roo.bootstrap.menu.Menu} this
25861 * Fires after this menu is hidden
25862 * @param {Roo.bootstrap.menu.Menu} this
25867 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25868 * @param {Roo.bootstrap.menu.Menu} this
25869 * @param {Roo.EventObject} e
25876 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25880 weight : 'default',
25885 getChildContainer : function() {
25886 if(this.isSubMenu){
25890 return this.el.select('ul.dropdown-menu', true).first();
25893 getAutoCreate : function()
25898 cls : 'roo-menu-text',
25906 cls : 'fa ' + this.icon
25917 cls : 'dropdown-button btn btn-' + this.weight,
25922 cls : 'dropdown-toggle btn btn-' + this.weight,
25932 cls : 'dropdown-menu'
25938 if(this.pos == 'top'){
25939 cfg.cls += ' dropup';
25942 if(this.isSubMenu){
25945 cls : 'dropdown-menu'
25952 onRender : function(ct, position)
25954 this.isSubMenu = ct.hasClass('dropdown-submenu');
25956 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25959 initEvents : function()
25961 if(this.isSubMenu){
25965 this.hidden = true;
25967 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25968 this.triggerEl.on('click', this.onTriggerPress, this);
25970 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25971 this.buttonEl.on('click', this.onClick, this);
25977 if(this.isSubMenu){
25981 return this.el.select('ul.dropdown-menu', true).first();
25984 onClick : function(e)
25986 this.fireEvent("click", this, e);
25989 onTriggerPress : function(e)
25991 if (this.isVisible()) {
25998 isVisible : function(){
25999 return !this.hidden;
26004 this.fireEvent("beforeshow", this);
26006 this.hidden = false;
26007 this.el.addClass('open');
26009 Roo.get(document).on("mouseup", this.onMouseUp, this);
26011 this.fireEvent("show", this);
26018 this.fireEvent("beforehide", this);
26020 this.hidden = true;
26021 this.el.removeClass('open');
26023 Roo.get(document).un("mouseup", this.onMouseUp);
26025 this.fireEvent("hide", this);
26028 onMouseUp : function()
26042 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26045 * @class Roo.bootstrap.menu.Item
26046 * @extends Roo.bootstrap.Component
26047 * Bootstrap MenuItem class
26048 * @cfg {Boolean} submenu (true | false) default false
26049 * @cfg {String} html text of the item
26050 * @cfg {String} href the link
26051 * @cfg {Boolean} disable (true | false) default false
26052 * @cfg {Boolean} preventDefault (true | false) default true
26053 * @cfg {String} icon Font awesome icon
26054 * @cfg {String} pos Submenu align to (left | right) default right
26058 * Create a new Item
26059 * @param {Object} config The config object
26063 Roo.bootstrap.menu.Item = function(config){
26064 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26068 * Fires when the mouse is hovering over this menu
26069 * @param {Roo.bootstrap.menu.Item} this
26070 * @param {Roo.EventObject} e
26075 * Fires when the mouse exits this menu
26076 * @param {Roo.bootstrap.menu.Item} this
26077 * @param {Roo.EventObject} e
26083 * The raw click event for the entire grid.
26084 * @param {Roo.EventObject} e
26090 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26095 preventDefault: true,
26100 getAutoCreate : function()
26105 cls : 'roo-menu-item-text',
26113 cls : 'fa ' + this.icon
26122 href : this.href || '#',
26129 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26133 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26135 if(this.pos == 'left'){
26136 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26143 initEvents : function()
26145 this.el.on('mouseover', this.onMouseOver, this);
26146 this.el.on('mouseout', this.onMouseOut, this);
26148 this.el.select('a', true).first().on('click', this.onClick, this);
26152 onClick : function(e)
26154 if(this.preventDefault){
26155 e.preventDefault();
26158 this.fireEvent("click", this, e);
26161 onMouseOver : function(e)
26163 if(this.submenu && this.pos == 'left'){
26164 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26167 this.fireEvent("mouseover", this, e);
26170 onMouseOut : function(e)
26172 this.fireEvent("mouseout", this, e);
26184 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26187 * @class Roo.bootstrap.menu.Separator
26188 * @extends Roo.bootstrap.Component
26189 * Bootstrap Separator class
26192 * Create a new Separator
26193 * @param {Object} config The config object
26197 Roo.bootstrap.menu.Separator = function(config){
26198 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26201 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26203 getAutoCreate : function(){
26224 * @class Roo.bootstrap.Tooltip
26225 * Bootstrap Tooltip class
26226 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26227 * to determine which dom element triggers the tooltip.
26229 * It needs to add support for additional attributes like tooltip-position
26232 * Create a new Toolti
26233 * @param {Object} config The config object
26236 Roo.bootstrap.Tooltip = function(config){
26237 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26239 this.alignment = Roo.bootstrap.Tooltip.alignment;
26241 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26242 this.alignment = config.alignment;
26247 Roo.apply(Roo.bootstrap.Tooltip, {
26249 * @function init initialize tooltip monitoring.
26253 currentTip : false,
26254 currentRegion : false,
26260 Roo.get(document).on('mouseover', this.enter ,this);
26261 Roo.get(document).on('mouseout', this.leave, this);
26264 this.currentTip = new Roo.bootstrap.Tooltip();
26267 enter : function(ev)
26269 var dom = ev.getTarget();
26271 //Roo.log(['enter',dom]);
26272 var el = Roo.fly(dom);
26273 if (this.currentEl) {
26275 //Roo.log(this.currentEl);
26276 //Roo.log(this.currentEl.contains(dom));
26277 if (this.currentEl == el) {
26280 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26286 if (this.currentTip.el) {
26287 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26291 if(!el || el.dom == document){
26297 // you can not look for children, as if el is the body.. then everythign is the child..
26298 if (!el.attr('tooltip')) { //
26299 if (!el.select("[tooltip]").elements.length) {
26302 // is the mouse over this child...?
26303 bindEl = el.select("[tooltip]").first();
26304 var xy = ev.getXY();
26305 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26306 //Roo.log("not in region.");
26309 //Roo.log("child element over..");
26312 this.currentEl = bindEl;
26313 this.currentTip.bind(bindEl);
26314 this.currentRegion = Roo.lib.Region.getRegion(dom);
26315 this.currentTip.enter();
26318 leave : function(ev)
26320 var dom = ev.getTarget();
26321 //Roo.log(['leave',dom]);
26322 if (!this.currentEl) {
26327 if (dom != this.currentEl.dom) {
26330 var xy = ev.getXY();
26331 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26334 // only activate leave if mouse cursor is outside... bounding box..
26339 if (this.currentTip) {
26340 this.currentTip.leave();
26342 //Roo.log('clear currentEl');
26343 this.currentEl = false;
26348 'left' : ['r-l', [-2,0], 'right'],
26349 'right' : ['l-r', [2,0], 'left'],
26350 'bottom' : ['t-b', [0,2], 'top'],
26351 'top' : [ 'b-t', [0,-2], 'bottom']
26357 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26362 delay : null, // can be { show : 300 , hide: 500}
26366 hoverState : null, //???
26368 placement : 'bottom',
26372 getAutoCreate : function(){
26379 cls : 'tooltip-arrow'
26382 cls : 'tooltip-inner'
26389 bind : function(el)
26395 enter : function () {
26397 if (this.timeout != null) {
26398 clearTimeout(this.timeout);
26401 this.hoverState = 'in';
26402 //Roo.log("enter - show");
26403 if (!this.delay || !this.delay.show) {
26408 this.timeout = setTimeout(function () {
26409 if (_t.hoverState == 'in') {
26412 }, this.delay.show);
26416 clearTimeout(this.timeout);
26418 this.hoverState = 'out';
26419 if (!this.delay || !this.delay.hide) {
26425 this.timeout = setTimeout(function () {
26426 //Roo.log("leave - timeout");
26428 if (_t.hoverState == 'out') {
26430 Roo.bootstrap.Tooltip.currentEl = false;
26435 show : function (msg)
26438 this.render(document.body);
26441 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26443 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26445 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26447 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26449 var placement = typeof this.placement == 'function' ?
26450 this.placement.call(this, this.el, on_el) :
26453 var autoToken = /\s?auto?\s?/i;
26454 var autoPlace = autoToken.test(placement);
26456 placement = placement.replace(autoToken, '') || 'top';
26460 //this.el.setXY([0,0]);
26462 //this.el.dom.style.display='block';
26464 //this.el.appendTo(on_el);
26466 var p = this.getPosition();
26467 var box = this.el.getBox();
26473 var align = this.alignment[placement];
26475 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26477 if(placement == 'top' || placement == 'bottom'){
26479 placement = 'right';
26482 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26483 placement = 'left';
26486 var scroll = Roo.select('body', true).first().getScroll();
26488 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26492 align = this.alignment[placement];
26495 this.el.alignTo(this.bindEl, align[0],align[1]);
26496 //var arrow = this.el.select('.arrow',true).first();
26497 //arrow.set(align[2],
26499 this.el.addClass(placement);
26501 this.el.addClass('in fade');
26503 this.hoverState = null;
26505 if (this.el.hasClass('fade')) {
26516 //this.el.setXY([0,0]);
26517 this.el.removeClass('in');
26533 * @class Roo.bootstrap.LocationPicker
26534 * @extends Roo.bootstrap.Component
26535 * Bootstrap LocationPicker class
26536 * @cfg {Number} latitude Position when init default 0
26537 * @cfg {Number} longitude Position when init default 0
26538 * @cfg {Number} zoom default 15
26539 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26540 * @cfg {Boolean} mapTypeControl default false
26541 * @cfg {Boolean} disableDoubleClickZoom default false
26542 * @cfg {Boolean} scrollwheel default true
26543 * @cfg {Boolean} streetViewControl default false
26544 * @cfg {Number} radius default 0
26545 * @cfg {String} locationName
26546 * @cfg {Boolean} draggable default true
26547 * @cfg {Boolean} enableAutocomplete default false
26548 * @cfg {Boolean} enableReverseGeocode default true
26549 * @cfg {String} markerTitle
26552 * Create a new LocationPicker
26553 * @param {Object} config The config object
26557 Roo.bootstrap.LocationPicker = function(config){
26559 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26564 * Fires when the picker initialized.
26565 * @param {Roo.bootstrap.LocationPicker} this
26566 * @param {Google Location} location
26570 * @event positionchanged
26571 * Fires when the picker position changed.
26572 * @param {Roo.bootstrap.LocationPicker} this
26573 * @param {Google Location} location
26575 positionchanged : true,
26578 * Fires when the map resize.
26579 * @param {Roo.bootstrap.LocationPicker} this
26584 * Fires when the map show.
26585 * @param {Roo.bootstrap.LocationPicker} this
26590 * Fires when the map hide.
26591 * @param {Roo.bootstrap.LocationPicker} this
26596 * Fires when click the map.
26597 * @param {Roo.bootstrap.LocationPicker} this
26598 * @param {Map event} e
26602 * @event mapRightClick
26603 * Fires when right click the map.
26604 * @param {Roo.bootstrap.LocationPicker} this
26605 * @param {Map event} e
26607 mapRightClick : true,
26609 * @event markerClick
26610 * Fires when click the marker.
26611 * @param {Roo.bootstrap.LocationPicker} this
26612 * @param {Map event} e
26614 markerClick : true,
26616 * @event markerRightClick
26617 * Fires when right click the marker.
26618 * @param {Roo.bootstrap.LocationPicker} this
26619 * @param {Map event} e
26621 markerRightClick : true,
26623 * @event OverlayViewDraw
26624 * Fires when OverlayView Draw
26625 * @param {Roo.bootstrap.LocationPicker} this
26627 OverlayViewDraw : true,
26629 * @event OverlayViewOnAdd
26630 * Fires when OverlayView Draw
26631 * @param {Roo.bootstrap.LocationPicker} this
26633 OverlayViewOnAdd : true,
26635 * @event OverlayViewOnRemove
26636 * Fires when OverlayView Draw
26637 * @param {Roo.bootstrap.LocationPicker} this
26639 OverlayViewOnRemove : true,
26641 * @event OverlayViewShow
26642 * Fires when OverlayView Draw
26643 * @param {Roo.bootstrap.LocationPicker} this
26644 * @param {Pixel} cpx
26646 OverlayViewShow : true,
26648 * @event OverlayViewHide
26649 * Fires when OverlayView Draw
26650 * @param {Roo.bootstrap.LocationPicker} this
26652 OverlayViewHide : true,
26654 * @event loadexception
26655 * Fires when load google lib failed.
26656 * @param {Roo.bootstrap.LocationPicker} this
26658 loadexception : true
26663 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26665 gMapContext: false,
26671 mapTypeControl: false,
26672 disableDoubleClickZoom: false,
26674 streetViewControl: false,
26678 enableAutocomplete: false,
26679 enableReverseGeocode: true,
26682 getAutoCreate: function()
26687 cls: 'roo-location-picker'
26693 initEvents: function(ct, position)
26695 if(!this.el.getWidth() || this.isApplied()){
26699 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26704 initial: function()
26706 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26707 this.fireEvent('loadexception', this);
26711 if(!this.mapTypeId){
26712 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26715 this.gMapContext = this.GMapContext();
26717 this.initOverlayView();
26719 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26723 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26724 _this.setPosition(_this.gMapContext.marker.position);
26727 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26728 _this.fireEvent('mapClick', this, event);
26732 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26733 _this.fireEvent('mapRightClick', this, event);
26737 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26738 _this.fireEvent('markerClick', this, event);
26742 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26743 _this.fireEvent('markerRightClick', this, event);
26747 this.setPosition(this.gMapContext.location);
26749 this.fireEvent('initial', this, this.gMapContext.location);
26752 initOverlayView: function()
26756 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26760 _this.fireEvent('OverlayViewDraw', _this);
26765 _this.fireEvent('OverlayViewOnAdd', _this);
26768 onRemove: function()
26770 _this.fireEvent('OverlayViewOnRemove', _this);
26773 show: function(cpx)
26775 _this.fireEvent('OverlayViewShow', _this, cpx);
26780 _this.fireEvent('OverlayViewHide', _this);
26786 fromLatLngToContainerPixel: function(event)
26788 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26791 isApplied: function()
26793 return this.getGmapContext() == false ? false : true;
26796 getGmapContext: function()
26798 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26801 GMapContext: function()
26803 var position = new google.maps.LatLng(this.latitude, this.longitude);
26805 var _map = new google.maps.Map(this.el.dom, {
26808 mapTypeId: this.mapTypeId,
26809 mapTypeControl: this.mapTypeControl,
26810 disableDoubleClickZoom: this.disableDoubleClickZoom,
26811 scrollwheel: this.scrollwheel,
26812 streetViewControl: this.streetViewControl,
26813 locationName: this.locationName,
26814 draggable: this.draggable,
26815 enableAutocomplete: this.enableAutocomplete,
26816 enableReverseGeocode: this.enableReverseGeocode
26819 var _marker = new google.maps.Marker({
26820 position: position,
26822 title: this.markerTitle,
26823 draggable: this.draggable
26830 location: position,
26831 radius: this.radius,
26832 locationName: this.locationName,
26833 addressComponents: {
26834 formatted_address: null,
26835 addressLine1: null,
26836 addressLine2: null,
26838 streetNumber: null,
26842 stateOrProvince: null
26845 domContainer: this.el.dom,
26846 geodecoder: new google.maps.Geocoder()
26850 drawCircle: function(center, radius, options)
26852 if (this.gMapContext.circle != null) {
26853 this.gMapContext.circle.setMap(null);
26857 options = Roo.apply({}, options, {
26858 strokeColor: "#0000FF",
26859 strokeOpacity: .35,
26861 fillColor: "#0000FF",
26865 options.map = this.gMapContext.map;
26866 options.radius = radius;
26867 options.center = center;
26868 this.gMapContext.circle = new google.maps.Circle(options);
26869 return this.gMapContext.circle;
26875 setPosition: function(location)
26877 this.gMapContext.location = location;
26878 this.gMapContext.marker.setPosition(location);
26879 this.gMapContext.map.panTo(location);
26880 this.drawCircle(location, this.gMapContext.radius, {});
26884 if (this.gMapContext.settings.enableReverseGeocode) {
26885 this.gMapContext.geodecoder.geocode({
26886 latLng: this.gMapContext.location
26887 }, function(results, status) {
26889 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26890 _this.gMapContext.locationName = results[0].formatted_address;
26891 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26893 _this.fireEvent('positionchanged', this, location);
26900 this.fireEvent('positionchanged', this, location);
26905 google.maps.event.trigger(this.gMapContext.map, "resize");
26907 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26909 this.fireEvent('resize', this);
26912 setPositionByLatLng: function(latitude, longitude)
26914 this.setPosition(new google.maps.LatLng(latitude, longitude));
26917 getCurrentPosition: function()
26920 latitude: this.gMapContext.location.lat(),
26921 longitude: this.gMapContext.location.lng()
26925 getAddressName: function()
26927 return this.gMapContext.locationName;
26930 getAddressComponents: function()
26932 return this.gMapContext.addressComponents;
26935 address_component_from_google_geocode: function(address_components)
26939 for (var i = 0; i < address_components.length; i++) {
26940 var component = address_components[i];
26941 if (component.types.indexOf("postal_code") >= 0) {
26942 result.postalCode = component.short_name;
26943 } else if (component.types.indexOf("street_number") >= 0) {
26944 result.streetNumber = component.short_name;
26945 } else if (component.types.indexOf("route") >= 0) {
26946 result.streetName = component.short_name;
26947 } else if (component.types.indexOf("neighborhood") >= 0) {
26948 result.city = component.short_name;
26949 } else if (component.types.indexOf("locality") >= 0) {
26950 result.city = component.short_name;
26951 } else if (component.types.indexOf("sublocality") >= 0) {
26952 result.district = component.short_name;
26953 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26954 result.stateOrProvince = component.short_name;
26955 } else if (component.types.indexOf("country") >= 0) {
26956 result.country = component.short_name;
26960 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26961 result.addressLine2 = "";
26965 setZoomLevel: function(zoom)
26967 this.gMapContext.map.setZoom(zoom);
26980 this.fireEvent('show', this);
26991 this.fireEvent('hide', this);
26996 Roo.apply(Roo.bootstrap.LocationPicker, {
26998 OverlayView : function(map, options)
27000 options = options || {};
27014 * @class Roo.bootstrap.Alert
27015 * @extends Roo.bootstrap.Component
27016 * Bootstrap Alert class
27017 * @cfg {String} title The title of alert
27018 * @cfg {String} html The content of alert
27019 * @cfg {String} weight ( success | info | warning | danger )
27020 * @cfg {String} faicon font-awesomeicon
27023 * Create a new alert
27024 * @param {Object} config The config object
27028 Roo.bootstrap.Alert = function(config){
27029 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27033 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
27040 getAutoCreate : function()
27049 cls : 'roo-alert-icon'
27054 cls : 'roo-alert-title',
27059 cls : 'roo-alert-text',
27066 cfg.cn[0].cls += ' fa ' + this.faicon;
27070 cfg.cls += ' alert-' + this.weight;
27076 initEvents: function()
27078 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27081 setTitle : function(str)
27083 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27086 setText : function(str)
27088 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27091 setWeight : function(weight)
27094 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27097 this.weight = weight;
27099 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27102 setIcon : function(icon)
27105 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27108 this.faicon = icon;
27110 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27131 * @class Roo.bootstrap.UploadCropbox
27132 * @extends Roo.bootstrap.Component
27133 * Bootstrap UploadCropbox class
27134 * @cfg {String} emptyText show when image has been loaded
27135 * @cfg {String} rotateNotify show when image too small to rotate
27136 * @cfg {Number} errorTimeout default 3000
27137 * @cfg {Number} minWidth default 300
27138 * @cfg {Number} minHeight default 300
27139 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27140 * @cfg {Boolean} isDocument (true|false) default false
27141 * @cfg {String} url action url
27142 * @cfg {String} paramName default 'imageUpload'
27143 * @cfg {String} method default POST
27144 * @cfg {Boolean} loadMask (true|false) default true
27145 * @cfg {Boolean} loadingText default 'Loading...'
27148 * Create a new UploadCropbox
27149 * @param {Object} config The config object
27152 Roo.bootstrap.UploadCropbox = function(config){
27153 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27157 * @event beforeselectfile
27158 * Fire before select file
27159 * @param {Roo.bootstrap.UploadCropbox} this
27161 "beforeselectfile" : true,
27164 * Fire after initEvent
27165 * @param {Roo.bootstrap.UploadCropbox} this
27170 * Fire after initEvent
27171 * @param {Roo.bootstrap.UploadCropbox} this
27172 * @param {String} data
27177 * Fire when preparing the file data
27178 * @param {Roo.bootstrap.UploadCropbox} this
27179 * @param {Object} file
27184 * Fire when get exception
27185 * @param {Roo.bootstrap.UploadCropbox} this
27186 * @param {XMLHttpRequest} xhr
27188 "exception" : true,
27190 * @event beforeloadcanvas
27191 * Fire before load the canvas
27192 * @param {Roo.bootstrap.UploadCropbox} this
27193 * @param {String} src
27195 "beforeloadcanvas" : true,
27198 * Fire when trash image
27199 * @param {Roo.bootstrap.UploadCropbox} this
27204 * Fire when download the image
27205 * @param {Roo.bootstrap.UploadCropbox} this
27209 * @event footerbuttonclick
27210 * Fire when footerbuttonclick
27211 * @param {Roo.bootstrap.UploadCropbox} this
27212 * @param {String} type
27214 "footerbuttonclick" : true,
27218 * @param {Roo.bootstrap.UploadCropbox} this
27223 * Fire when rotate the image
27224 * @param {Roo.bootstrap.UploadCropbox} this
27225 * @param {String} pos
27230 * Fire when inspect the file
27231 * @param {Roo.bootstrap.UploadCropbox} this
27232 * @param {Object} file
27237 * Fire when xhr upload the file
27238 * @param {Roo.bootstrap.UploadCropbox} this
27239 * @param {Object} data
27244 * Fire when arrange the file data
27245 * @param {Roo.bootstrap.UploadCropbox} this
27246 * @param {Object} formData
27251 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27254 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27256 emptyText : 'Click to upload image',
27257 rotateNotify : 'Image is too small to rotate',
27258 errorTimeout : 3000,
27272 cropType : 'image/jpeg',
27274 canvasLoaded : false,
27275 isDocument : false,
27277 paramName : 'imageUpload',
27279 loadingText : 'Loading...',
27282 getAutoCreate : function()
27286 cls : 'roo-upload-cropbox',
27290 cls : 'roo-upload-cropbox-selector',
27295 cls : 'roo-upload-cropbox-body',
27296 style : 'cursor:pointer',
27300 cls : 'roo-upload-cropbox-preview'
27304 cls : 'roo-upload-cropbox-thumb'
27308 cls : 'roo-upload-cropbox-empty-notify',
27309 html : this.emptyText
27313 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27314 html : this.rotateNotify
27320 cls : 'roo-upload-cropbox-footer',
27323 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27333 onRender : function(ct, position)
27335 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27337 if (this.buttons.length) {
27339 Roo.each(this.buttons, function(bb) {
27341 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27343 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27349 this.maskEl = this.el;
27353 initEvents : function()
27355 this.urlAPI = (window.createObjectURL && window) ||
27356 (window.URL && URL.revokeObjectURL && URL) ||
27357 (window.webkitURL && webkitURL);
27359 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27360 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27362 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27363 this.selectorEl.hide();
27365 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27366 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27368 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27369 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27370 this.thumbEl.hide();
27372 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27373 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27375 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27376 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27377 this.errorEl.hide();
27379 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27380 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27381 this.footerEl.hide();
27383 this.setThumbBoxSize();
27389 this.fireEvent('initial', this);
27396 window.addEventListener("resize", function() { _this.resize(); } );
27398 this.bodyEl.on('click', this.beforeSelectFile, this);
27401 this.bodyEl.on('touchstart', this.onTouchStart, this);
27402 this.bodyEl.on('touchmove', this.onTouchMove, this);
27403 this.bodyEl.on('touchend', this.onTouchEnd, this);
27407 this.bodyEl.on('mousedown', this.onMouseDown, this);
27408 this.bodyEl.on('mousemove', this.onMouseMove, this);
27409 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27410 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27411 Roo.get(document).on('mouseup', this.onMouseUp, this);
27414 this.selectorEl.on('change', this.onFileSelected, this);
27420 this.baseScale = 1;
27422 this.baseRotate = 1;
27423 this.dragable = false;
27424 this.pinching = false;
27427 this.cropData = false;
27428 this.notifyEl.dom.innerHTML = this.emptyText;
27430 this.selectorEl.dom.value = '';
27434 resize : function()
27436 if(this.fireEvent('resize', this) != false){
27437 this.setThumbBoxPosition();
27438 this.setCanvasPosition();
27442 onFooterButtonClick : function(e, el, o, type)
27445 case 'rotate-left' :
27446 this.onRotateLeft(e);
27448 case 'rotate-right' :
27449 this.onRotateRight(e);
27452 this.beforeSelectFile(e);
27467 this.fireEvent('footerbuttonclick', this, type);
27470 beforeSelectFile : function(e)
27472 e.preventDefault();
27474 if(this.fireEvent('beforeselectfile', this) != false){
27475 this.selectorEl.dom.click();
27479 onFileSelected : function(e)
27481 e.preventDefault();
27483 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27487 var file = this.selectorEl.dom.files[0];
27489 if(this.fireEvent('inspect', this, file) != false){
27490 this.prepare(file);
27495 trash : function(e)
27497 this.fireEvent('trash', this);
27500 download : function(e)
27502 this.fireEvent('download', this);
27505 loadCanvas : function(src)
27507 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27511 this.imageEl = document.createElement('img');
27515 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27517 this.imageEl.src = src;
27521 onLoadCanvas : function()
27523 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27524 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27526 this.bodyEl.un('click', this.beforeSelectFile, this);
27528 this.notifyEl.hide();
27529 this.thumbEl.show();
27530 this.footerEl.show();
27532 this.baseRotateLevel();
27534 if(this.isDocument){
27535 this.setThumbBoxSize();
27538 this.setThumbBoxPosition();
27540 this.baseScaleLevel();
27546 this.canvasLoaded = true;
27549 this.maskEl.unmask();
27554 setCanvasPosition : function()
27556 if(!this.canvasEl){
27560 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27561 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27563 this.previewEl.setLeft(pw);
27564 this.previewEl.setTop(ph);
27568 onMouseDown : function(e)
27572 this.dragable = true;
27573 this.pinching = false;
27575 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27576 this.dragable = false;
27580 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27581 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27585 onMouseMove : function(e)
27589 if(!this.canvasLoaded){
27593 if (!this.dragable){
27597 var minX = Math.ceil(this.thumbEl.getLeft(true));
27598 var minY = Math.ceil(this.thumbEl.getTop(true));
27600 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27601 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27603 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27604 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27606 x = x - this.mouseX;
27607 y = y - this.mouseY;
27609 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27610 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27612 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27613 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27615 this.previewEl.setLeft(bgX);
27616 this.previewEl.setTop(bgY);
27618 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27619 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27622 onMouseUp : function(e)
27626 this.dragable = false;
27629 onMouseWheel : function(e)
27633 this.startScale = this.scale;
27635 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27637 if(!this.zoomable()){
27638 this.scale = this.startScale;
27647 zoomable : function()
27649 var minScale = this.thumbEl.getWidth() / this.minWidth;
27651 if(this.minWidth < this.minHeight){
27652 minScale = this.thumbEl.getHeight() / this.minHeight;
27655 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27656 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27660 (this.rotate == 0 || this.rotate == 180) &&
27662 width > this.imageEl.OriginWidth ||
27663 height > this.imageEl.OriginHeight ||
27664 (width < this.minWidth && height < this.minHeight)
27672 (this.rotate == 90 || this.rotate == 270) &&
27674 width > this.imageEl.OriginWidth ||
27675 height > this.imageEl.OriginHeight ||
27676 (width < this.minHeight && height < this.minWidth)
27683 !this.isDocument &&
27684 (this.rotate == 0 || this.rotate == 180) &&
27686 width < this.minWidth ||
27687 width > this.imageEl.OriginWidth ||
27688 height < this.minHeight ||
27689 height > this.imageEl.OriginHeight
27696 !this.isDocument &&
27697 (this.rotate == 90 || this.rotate == 270) &&
27699 width < this.minHeight ||
27700 width > this.imageEl.OriginWidth ||
27701 height < this.minWidth ||
27702 height > this.imageEl.OriginHeight
27712 onRotateLeft : function(e)
27714 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27716 var minScale = this.thumbEl.getWidth() / this.minWidth;
27718 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27719 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27721 this.startScale = this.scale;
27723 while (this.getScaleLevel() < minScale){
27725 this.scale = this.scale + 1;
27727 if(!this.zoomable()){
27732 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27733 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27738 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27745 this.scale = this.startScale;
27747 this.onRotateFail();
27752 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27754 if(this.isDocument){
27755 this.setThumbBoxSize();
27756 this.setThumbBoxPosition();
27757 this.setCanvasPosition();
27762 this.fireEvent('rotate', this, 'left');
27766 onRotateRight : function(e)
27768 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27770 var minScale = this.thumbEl.getWidth() / this.minWidth;
27772 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27773 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27775 this.startScale = this.scale;
27777 while (this.getScaleLevel() < minScale){
27779 this.scale = this.scale + 1;
27781 if(!this.zoomable()){
27786 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27787 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27792 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27799 this.scale = this.startScale;
27801 this.onRotateFail();
27806 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27808 if(this.isDocument){
27809 this.setThumbBoxSize();
27810 this.setThumbBoxPosition();
27811 this.setCanvasPosition();
27816 this.fireEvent('rotate', this, 'right');
27819 onRotateFail : function()
27821 this.errorEl.show(true);
27825 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27830 this.previewEl.dom.innerHTML = '';
27832 var canvasEl = document.createElement("canvas");
27834 var contextEl = canvasEl.getContext("2d");
27836 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27837 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27838 var center = this.imageEl.OriginWidth / 2;
27840 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27841 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27842 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27843 center = this.imageEl.OriginHeight / 2;
27846 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27848 contextEl.translate(center, center);
27849 contextEl.rotate(this.rotate * Math.PI / 180);
27851 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27853 this.canvasEl = document.createElement("canvas");
27855 this.contextEl = this.canvasEl.getContext("2d");
27857 switch (this.rotate) {
27860 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27861 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27863 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27868 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27869 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27871 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27872 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);
27876 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27881 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27882 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27884 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27885 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);
27889 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);
27894 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27895 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27897 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27898 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27902 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);
27909 this.previewEl.appendChild(this.canvasEl);
27911 this.setCanvasPosition();
27916 if(!this.canvasLoaded){
27920 var imageCanvas = document.createElement("canvas");
27922 var imageContext = imageCanvas.getContext("2d");
27924 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27925 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27927 var center = imageCanvas.width / 2;
27929 imageContext.translate(center, center);
27931 imageContext.rotate(this.rotate * Math.PI / 180);
27933 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27935 var canvas = document.createElement("canvas");
27937 var context = canvas.getContext("2d");
27939 canvas.width = this.minWidth;
27940 canvas.height = this.minHeight;
27942 switch (this.rotate) {
27945 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27946 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27948 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27949 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27951 var targetWidth = this.minWidth - 2 * x;
27952 var targetHeight = this.minHeight - 2 * y;
27956 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27957 scale = targetWidth / width;
27960 if(x > 0 && y == 0){
27961 scale = targetHeight / height;
27964 if(x > 0 && y > 0){
27965 scale = targetWidth / width;
27967 if(width < height){
27968 scale = targetHeight / height;
27972 context.scale(scale, scale);
27974 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27975 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27977 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27978 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27980 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27985 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27986 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27988 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27989 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27991 var targetWidth = this.minWidth - 2 * x;
27992 var targetHeight = this.minHeight - 2 * y;
27996 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27997 scale = targetWidth / width;
28000 if(x > 0 && y == 0){
28001 scale = targetHeight / height;
28004 if(x > 0 && y > 0){
28005 scale = targetWidth / width;
28007 if(width < height){
28008 scale = targetHeight / height;
28012 context.scale(scale, scale);
28014 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28015 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28017 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28018 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28020 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28022 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28027 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28028 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28030 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28031 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28033 var targetWidth = this.minWidth - 2 * x;
28034 var targetHeight = this.minHeight - 2 * y;
28038 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28039 scale = targetWidth / width;
28042 if(x > 0 && y == 0){
28043 scale = targetHeight / height;
28046 if(x > 0 && y > 0){
28047 scale = targetWidth / width;
28049 if(width < height){
28050 scale = targetHeight / height;
28054 context.scale(scale, scale);
28056 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28057 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28059 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28060 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28062 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28063 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28065 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28070 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28071 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28073 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28074 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28076 var targetWidth = this.minWidth - 2 * x;
28077 var targetHeight = this.minHeight - 2 * y;
28081 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28082 scale = targetWidth / width;
28085 if(x > 0 && y == 0){
28086 scale = targetHeight / height;
28089 if(x > 0 && y > 0){
28090 scale = targetWidth / width;
28092 if(width < height){
28093 scale = targetHeight / height;
28097 context.scale(scale, scale);
28099 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28100 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28102 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28103 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28105 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28107 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28114 this.cropData = canvas.toDataURL(this.cropType);
28116 if(this.fireEvent('crop', this, this.cropData) !== false){
28117 this.process(this.file, this.cropData);
28124 setThumbBoxSize : function()
28128 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28129 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28130 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28132 this.minWidth = width;
28133 this.minHeight = height;
28135 if(this.rotate == 90 || this.rotate == 270){
28136 this.minWidth = height;
28137 this.minHeight = width;
28142 width = Math.ceil(this.minWidth * height / this.minHeight);
28144 if(this.minWidth > this.minHeight){
28146 height = Math.ceil(this.minHeight * width / this.minWidth);
28149 this.thumbEl.setStyle({
28150 width : width + 'px',
28151 height : height + 'px'
28158 setThumbBoxPosition : function()
28160 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28161 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28163 this.thumbEl.setLeft(x);
28164 this.thumbEl.setTop(y);
28168 baseRotateLevel : function()
28170 this.baseRotate = 1;
28173 typeof(this.exif) != 'undefined' &&
28174 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28175 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28177 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28180 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28184 baseScaleLevel : function()
28188 if(this.isDocument){
28190 if(this.baseRotate == 6 || this.baseRotate == 8){
28192 height = this.thumbEl.getHeight();
28193 this.baseScale = height / this.imageEl.OriginWidth;
28195 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28196 width = this.thumbEl.getWidth();
28197 this.baseScale = width / this.imageEl.OriginHeight;
28203 height = this.thumbEl.getHeight();
28204 this.baseScale = height / this.imageEl.OriginHeight;
28206 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28207 width = this.thumbEl.getWidth();
28208 this.baseScale = width / this.imageEl.OriginWidth;
28214 if(this.baseRotate == 6 || this.baseRotate == 8){
28216 width = this.thumbEl.getHeight();
28217 this.baseScale = width / this.imageEl.OriginHeight;
28219 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28220 height = this.thumbEl.getWidth();
28221 this.baseScale = height / this.imageEl.OriginHeight;
28224 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28225 height = this.thumbEl.getWidth();
28226 this.baseScale = height / this.imageEl.OriginHeight;
28228 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28229 width = this.thumbEl.getHeight();
28230 this.baseScale = width / this.imageEl.OriginWidth;
28237 width = this.thumbEl.getWidth();
28238 this.baseScale = width / this.imageEl.OriginWidth;
28240 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28241 height = this.thumbEl.getHeight();
28242 this.baseScale = height / this.imageEl.OriginHeight;
28245 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28247 height = this.thumbEl.getHeight();
28248 this.baseScale = height / this.imageEl.OriginHeight;
28250 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28251 width = this.thumbEl.getWidth();
28252 this.baseScale = width / this.imageEl.OriginWidth;
28260 getScaleLevel : function()
28262 return this.baseScale * Math.pow(1.1, this.scale);
28265 onTouchStart : function(e)
28267 if(!this.canvasLoaded){
28268 this.beforeSelectFile(e);
28272 var touches = e.browserEvent.touches;
28278 if(touches.length == 1){
28279 this.onMouseDown(e);
28283 if(touches.length != 2){
28289 for(var i = 0, finger; finger = touches[i]; i++){
28290 coords.push(finger.pageX, finger.pageY);
28293 var x = Math.pow(coords[0] - coords[2], 2);
28294 var y = Math.pow(coords[1] - coords[3], 2);
28296 this.startDistance = Math.sqrt(x + y);
28298 this.startScale = this.scale;
28300 this.pinching = true;
28301 this.dragable = false;
28305 onTouchMove : function(e)
28307 if(!this.pinching && !this.dragable){
28311 var touches = e.browserEvent.touches;
28318 this.onMouseMove(e);
28324 for(var i = 0, finger; finger = touches[i]; i++){
28325 coords.push(finger.pageX, finger.pageY);
28328 var x = Math.pow(coords[0] - coords[2], 2);
28329 var y = Math.pow(coords[1] - coords[3], 2);
28331 this.endDistance = Math.sqrt(x + y);
28333 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28335 if(!this.zoomable()){
28336 this.scale = this.startScale;
28344 onTouchEnd : function(e)
28346 this.pinching = false;
28347 this.dragable = false;
28351 process : function(file, crop)
28354 this.maskEl.mask(this.loadingText);
28357 this.xhr = new XMLHttpRequest();
28359 file.xhr = this.xhr;
28361 this.xhr.open(this.method, this.url, true);
28364 "Accept": "application/json",
28365 "Cache-Control": "no-cache",
28366 "X-Requested-With": "XMLHttpRequest"
28369 for (var headerName in headers) {
28370 var headerValue = headers[headerName];
28372 this.xhr.setRequestHeader(headerName, headerValue);
28378 this.xhr.onload = function()
28380 _this.xhrOnLoad(_this.xhr);
28383 this.xhr.onerror = function()
28385 _this.xhrOnError(_this.xhr);
28388 var formData = new FormData();
28390 formData.append('returnHTML', 'NO');
28393 formData.append('crop', crop);
28396 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28397 formData.append(this.paramName, file, file.name);
28400 if(typeof(file.filename) != 'undefined'){
28401 formData.append('filename', file.filename);
28404 if(typeof(file.mimetype) != 'undefined'){
28405 formData.append('mimetype', file.mimetype);
28408 if(this.fireEvent('arrange', this, formData) != false){
28409 this.xhr.send(formData);
28413 xhrOnLoad : function(xhr)
28416 this.maskEl.unmask();
28419 if (xhr.readyState !== 4) {
28420 this.fireEvent('exception', this, xhr);
28424 var response = Roo.decode(xhr.responseText);
28426 if(!response.success){
28427 this.fireEvent('exception', this, xhr);
28431 var response = Roo.decode(xhr.responseText);
28433 this.fireEvent('upload', this, response);
28437 xhrOnError : function()
28440 this.maskEl.unmask();
28443 Roo.log('xhr on error');
28445 var response = Roo.decode(xhr.responseText);
28451 prepare : function(file)
28454 this.maskEl.mask(this.loadingText);
28460 if(typeof(file) === 'string'){
28461 this.loadCanvas(file);
28465 if(!file || !this.urlAPI){
28470 this.cropType = file.type;
28474 if(this.fireEvent('prepare', this, this.file) != false){
28476 var reader = new FileReader();
28478 reader.onload = function (e) {
28479 if (e.target.error) {
28480 Roo.log(e.target.error);
28484 var buffer = e.target.result,
28485 dataView = new DataView(buffer),
28487 maxOffset = dataView.byteLength - 4,
28491 if (dataView.getUint16(0) === 0xffd8) {
28492 while (offset < maxOffset) {
28493 markerBytes = dataView.getUint16(offset);
28495 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28496 markerLength = dataView.getUint16(offset + 2) + 2;
28497 if (offset + markerLength > dataView.byteLength) {
28498 Roo.log('Invalid meta data: Invalid segment size.');
28502 if(markerBytes == 0xffe1){
28503 _this.parseExifData(
28510 offset += markerLength;
28520 var url = _this.urlAPI.createObjectURL(_this.file);
28522 _this.loadCanvas(url);
28527 reader.readAsArrayBuffer(this.file);
28533 parseExifData : function(dataView, offset, length)
28535 var tiffOffset = offset + 10,
28539 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28540 // No Exif data, might be XMP data instead
28544 // Check for the ASCII code for "Exif" (0x45786966):
28545 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28546 // No Exif data, might be XMP data instead
28549 if (tiffOffset + 8 > dataView.byteLength) {
28550 Roo.log('Invalid Exif data: Invalid segment size.');
28553 // Check for the two null bytes:
28554 if (dataView.getUint16(offset + 8) !== 0x0000) {
28555 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28558 // Check the byte alignment:
28559 switch (dataView.getUint16(tiffOffset)) {
28561 littleEndian = true;
28564 littleEndian = false;
28567 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28570 // Check for the TIFF tag marker (0x002A):
28571 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28572 Roo.log('Invalid Exif data: Missing TIFF marker.');
28575 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28576 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28578 this.parseExifTags(
28581 tiffOffset + dirOffset,
28586 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28591 if (dirOffset + 6 > dataView.byteLength) {
28592 Roo.log('Invalid Exif data: Invalid directory offset.');
28595 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28596 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28597 if (dirEndOffset + 4 > dataView.byteLength) {
28598 Roo.log('Invalid Exif data: Invalid directory size.');
28601 for (i = 0; i < tagsNumber; i += 1) {
28605 dirOffset + 2 + 12 * i, // tag offset
28609 // Return the offset to the next directory:
28610 return dataView.getUint32(dirEndOffset, littleEndian);
28613 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28615 var tag = dataView.getUint16(offset, littleEndian);
28617 this.exif[tag] = this.getExifValue(
28621 dataView.getUint16(offset + 2, littleEndian), // tag type
28622 dataView.getUint32(offset + 4, littleEndian), // tag length
28627 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28629 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28638 Roo.log('Invalid Exif data: Invalid tag type.');
28642 tagSize = tagType.size * length;
28643 // Determine if the value is contained in the dataOffset bytes,
28644 // or if the value at the dataOffset is a pointer to the actual data:
28645 dataOffset = tagSize > 4 ?
28646 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28647 if (dataOffset + tagSize > dataView.byteLength) {
28648 Roo.log('Invalid Exif data: Invalid data offset.');
28651 if (length === 1) {
28652 return tagType.getValue(dataView, dataOffset, littleEndian);
28655 for (i = 0; i < length; i += 1) {
28656 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28659 if (tagType.ascii) {
28661 // Concatenate the chars:
28662 for (i = 0; i < values.length; i += 1) {
28664 // Ignore the terminating NULL byte(s):
28665 if (c === '\u0000') {
28677 Roo.apply(Roo.bootstrap.UploadCropbox, {
28679 'Orientation': 0x0112
28683 1: 0, //'top-left',
28685 3: 180, //'bottom-right',
28686 // 4: 'bottom-left',
28688 6: 90, //'right-top',
28689 // 7: 'right-bottom',
28690 8: 270 //'left-bottom'
28694 // byte, 8-bit unsigned int:
28696 getValue: function (dataView, dataOffset) {
28697 return dataView.getUint8(dataOffset);
28701 // ascii, 8-bit byte:
28703 getValue: function (dataView, dataOffset) {
28704 return String.fromCharCode(dataView.getUint8(dataOffset));
28709 // short, 16 bit int:
28711 getValue: function (dataView, dataOffset, littleEndian) {
28712 return dataView.getUint16(dataOffset, littleEndian);
28716 // long, 32 bit int:
28718 getValue: function (dataView, dataOffset, littleEndian) {
28719 return dataView.getUint32(dataOffset, littleEndian);
28723 // rational = two long values, first is numerator, second is denominator:
28725 getValue: function (dataView, dataOffset, littleEndian) {
28726 return dataView.getUint32(dataOffset, littleEndian) /
28727 dataView.getUint32(dataOffset + 4, littleEndian);
28731 // slong, 32 bit signed int:
28733 getValue: function (dataView, dataOffset, littleEndian) {
28734 return dataView.getInt32(dataOffset, littleEndian);
28738 // srational, two slongs, first is numerator, second is denominator:
28740 getValue: function (dataView, dataOffset, littleEndian) {
28741 return dataView.getInt32(dataOffset, littleEndian) /
28742 dataView.getInt32(dataOffset + 4, littleEndian);
28752 cls : 'btn-group roo-upload-cropbox-rotate-left',
28753 action : 'rotate-left',
28757 cls : 'btn btn-default',
28758 html : '<i class="fa fa-undo"></i>'
28764 cls : 'btn-group roo-upload-cropbox-picture',
28765 action : 'picture',
28769 cls : 'btn btn-default',
28770 html : '<i class="fa fa-picture-o"></i>'
28776 cls : 'btn-group roo-upload-cropbox-rotate-right',
28777 action : 'rotate-right',
28781 cls : 'btn btn-default',
28782 html : '<i class="fa fa-repeat"></i>'
28790 cls : 'btn-group roo-upload-cropbox-rotate-left',
28791 action : 'rotate-left',
28795 cls : 'btn btn-default',
28796 html : '<i class="fa fa-undo"></i>'
28802 cls : 'btn-group roo-upload-cropbox-download',
28803 action : 'download',
28807 cls : 'btn btn-default',
28808 html : '<i class="fa fa-download"></i>'
28814 cls : 'btn-group roo-upload-cropbox-crop',
28819 cls : 'btn btn-default',
28820 html : '<i class="fa fa-crop"></i>'
28826 cls : 'btn-group roo-upload-cropbox-trash',
28831 cls : 'btn btn-default',
28832 html : '<i class="fa fa-trash"></i>'
28838 cls : 'btn-group roo-upload-cropbox-rotate-right',
28839 action : 'rotate-right',
28843 cls : 'btn btn-default',
28844 html : '<i class="fa fa-repeat"></i>'
28852 cls : 'btn-group roo-upload-cropbox-rotate-left',
28853 action : 'rotate-left',
28857 cls : 'btn btn-default',
28858 html : '<i class="fa fa-undo"></i>'
28864 cls : 'btn-group roo-upload-cropbox-rotate-right',
28865 action : 'rotate-right',
28869 cls : 'btn btn-default',
28870 html : '<i class="fa fa-repeat"></i>'
28883 * @class Roo.bootstrap.DocumentManager
28884 * @extends Roo.bootstrap.Component
28885 * Bootstrap DocumentManager class
28886 * @cfg {String} paramName default 'imageUpload'
28887 * @cfg {String} toolTipName default 'filename'
28888 * @cfg {String} method default POST
28889 * @cfg {String} url action url
28890 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28891 * @cfg {Boolean} multiple multiple upload default true
28892 * @cfg {Number} thumbSize default 300
28893 * @cfg {String} fieldLabel
28894 * @cfg {Number} labelWidth default 4
28895 * @cfg {String} labelAlign (left|top) default left
28896 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28897 * @cfg {Number} labellg set the width of label (1-12)
28898 * @cfg {Number} labelmd set the width of label (1-12)
28899 * @cfg {Number} labelsm set the width of label (1-12)
28900 * @cfg {Number} labelxs set the width of label (1-12)
28903 * Create a new DocumentManager
28904 * @param {Object} config The config object
28907 Roo.bootstrap.DocumentManager = function(config){
28908 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28911 this.delegates = [];
28916 * Fire when initial the DocumentManager
28917 * @param {Roo.bootstrap.DocumentManager} this
28922 * inspect selected file
28923 * @param {Roo.bootstrap.DocumentManager} this
28924 * @param {File} file
28929 * Fire when xhr load exception
28930 * @param {Roo.bootstrap.DocumentManager} this
28931 * @param {XMLHttpRequest} xhr
28933 "exception" : true,
28935 * @event afterupload
28936 * Fire when xhr load exception
28937 * @param {Roo.bootstrap.DocumentManager} this
28938 * @param {XMLHttpRequest} xhr
28940 "afterupload" : true,
28943 * prepare the form data
28944 * @param {Roo.bootstrap.DocumentManager} this
28945 * @param {Object} formData
28950 * Fire when remove the file
28951 * @param {Roo.bootstrap.DocumentManager} this
28952 * @param {Object} file
28957 * Fire after refresh the file
28958 * @param {Roo.bootstrap.DocumentManager} this
28963 * Fire after click the image
28964 * @param {Roo.bootstrap.DocumentManager} this
28965 * @param {Object} file
28970 * Fire when upload a image and editable set to true
28971 * @param {Roo.bootstrap.DocumentManager} this
28972 * @param {Object} file
28976 * @event beforeselectfile
28977 * Fire before select file
28978 * @param {Roo.bootstrap.DocumentManager} this
28980 "beforeselectfile" : true,
28983 * Fire before process file
28984 * @param {Roo.bootstrap.DocumentManager} this
28985 * @param {Object} file
28989 * @event previewrendered
28990 * Fire when preview rendered
28991 * @param {Roo.bootstrap.DocumentManager} this
28992 * @param {Object} file
28994 "previewrendered" : true,
28997 "previewResize" : true
29002 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
29011 paramName : 'imageUpload',
29012 toolTipName : 'filename',
29015 labelAlign : 'left',
29025 getAutoCreate : function()
29027 var managerWidget = {
29029 cls : 'roo-document-manager',
29033 cls : 'roo-document-manager-selector',
29038 cls : 'roo-document-manager-uploader',
29042 cls : 'roo-document-manager-upload-btn',
29043 html : '<i class="fa fa-plus"></i>'
29054 cls : 'column col-md-12',
29059 if(this.fieldLabel.length){
29064 cls : 'column col-md-12',
29065 html : this.fieldLabel
29069 cls : 'column col-md-12',
29074 if(this.labelAlign == 'left'){
29079 html : this.fieldLabel
29088 if(this.labelWidth > 12){
29089 content[0].style = "width: " + this.labelWidth + 'px';
29092 if(this.labelWidth < 13 && this.labelmd == 0){
29093 this.labelmd = this.labelWidth;
29096 if(this.labellg > 0){
29097 content[0].cls += ' col-lg-' + this.labellg;
29098 content[1].cls += ' col-lg-' + (12 - this.labellg);
29101 if(this.labelmd > 0){
29102 content[0].cls += ' col-md-' + this.labelmd;
29103 content[1].cls += ' col-md-' + (12 - this.labelmd);
29106 if(this.labelsm > 0){
29107 content[0].cls += ' col-sm-' + this.labelsm;
29108 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29111 if(this.labelxs > 0){
29112 content[0].cls += ' col-xs-' + this.labelxs;
29113 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29121 cls : 'row clearfix',
29129 initEvents : function()
29131 this.managerEl = this.el.select('.roo-document-manager', true).first();
29132 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29134 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29135 this.selectorEl.hide();
29138 this.selectorEl.attr('multiple', 'multiple');
29141 this.selectorEl.on('change', this.onFileSelected, this);
29143 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29144 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29146 this.uploader.on('click', this.onUploaderClick, this);
29148 this.renderProgressDialog();
29152 window.addEventListener("resize", function() { _this.refresh(); } );
29154 this.fireEvent('initial', this);
29157 renderProgressDialog : function()
29161 this.progressDialog = new Roo.bootstrap.Modal({
29162 cls : 'roo-document-manager-progress-dialog',
29163 allow_close : false,
29173 btnclick : function() {
29174 _this.uploadCancel();
29180 this.progressDialog.render(Roo.get(document.body));
29182 this.progress = new Roo.bootstrap.Progress({
29183 cls : 'roo-document-manager-progress',
29188 this.progress.render(this.progressDialog.getChildContainer());
29190 this.progressBar = new Roo.bootstrap.ProgressBar({
29191 cls : 'roo-document-manager-progress-bar',
29194 aria_valuemax : 12,
29198 this.progressBar.render(this.progress.getChildContainer());
29201 onUploaderClick : function(e)
29203 e.preventDefault();
29205 if(this.fireEvent('beforeselectfile', this) != false){
29206 this.selectorEl.dom.click();
29211 onFileSelected : function(e)
29213 e.preventDefault();
29215 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29219 Roo.each(this.selectorEl.dom.files, function(file){
29220 if(this.fireEvent('inspect', this, file) != false){
29221 this.files.push(file);
29231 this.selectorEl.dom.value = '';
29233 if(!this.files || !this.files.length){
29237 if(this.boxes > 0 && this.files.length > this.boxes){
29238 this.files = this.files.slice(0, this.boxes);
29241 this.uploader.show();
29243 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29244 this.uploader.hide();
29253 Roo.each(this.files, function(file){
29255 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29256 var f = this.renderPreview(file);
29261 if(file.type.indexOf('image') != -1){
29262 this.delegates.push(
29264 _this.process(file);
29265 }).createDelegate(this)
29273 _this.process(file);
29274 }).createDelegate(this)
29279 this.files = files;
29281 this.delegates = this.delegates.concat(docs);
29283 if(!this.delegates.length){
29288 this.progressBar.aria_valuemax = this.delegates.length;
29295 arrange : function()
29297 if(!this.delegates.length){
29298 this.progressDialog.hide();
29303 var delegate = this.delegates.shift();
29305 this.progressDialog.show();
29307 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29309 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29314 refresh : function()
29316 this.uploader.show();
29318 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29319 this.uploader.hide();
29322 Roo.isTouch ? this.closable(false) : this.closable(true);
29324 this.fireEvent('refresh', this);
29327 onRemove : function(e, el, o)
29329 e.preventDefault();
29331 this.fireEvent('remove', this, o);
29335 remove : function(o)
29339 Roo.each(this.files, function(file){
29340 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29349 this.files = files;
29356 Roo.each(this.files, function(file){
29361 file.target.remove();
29370 onClick : function(e, el, o)
29372 e.preventDefault();
29374 this.fireEvent('click', this, o);
29378 closable : function(closable)
29380 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29382 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29394 xhrOnLoad : function(xhr)
29396 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29400 if (xhr.readyState !== 4) {
29402 this.fireEvent('exception', this, xhr);
29406 var response = Roo.decode(xhr.responseText);
29408 if(!response.success){
29410 this.fireEvent('exception', this, xhr);
29414 var file = this.renderPreview(response.data);
29416 this.files.push(file);
29420 this.fireEvent('afterupload', this, xhr);
29424 xhrOnError : function(xhr)
29426 Roo.log('xhr on error');
29428 var response = Roo.decode(xhr.responseText);
29435 process : function(file)
29437 if(this.fireEvent('process', this, file) !== false){
29438 if(this.editable && file.type.indexOf('image') != -1){
29439 this.fireEvent('edit', this, file);
29443 this.uploadStart(file, false);
29450 uploadStart : function(file, crop)
29452 this.xhr = new XMLHttpRequest();
29454 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29459 file.xhr = this.xhr;
29461 this.managerEl.createChild({
29463 cls : 'roo-document-manager-loading',
29467 tooltip : file.name,
29468 cls : 'roo-document-manager-thumb',
29469 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29475 this.xhr.open(this.method, this.url, true);
29478 "Accept": "application/json",
29479 "Cache-Control": "no-cache",
29480 "X-Requested-With": "XMLHttpRequest"
29483 for (var headerName in headers) {
29484 var headerValue = headers[headerName];
29486 this.xhr.setRequestHeader(headerName, headerValue);
29492 this.xhr.onload = function()
29494 _this.xhrOnLoad(_this.xhr);
29497 this.xhr.onerror = function()
29499 _this.xhrOnError(_this.xhr);
29502 var formData = new FormData();
29504 formData.append('returnHTML', 'NO');
29507 formData.append('crop', crop);
29510 formData.append(this.paramName, file, file.name);
29517 if(this.fireEvent('prepare', this, formData, options) != false){
29519 if(options.manually){
29523 this.xhr.send(formData);
29527 this.uploadCancel();
29530 uploadCancel : function()
29536 this.delegates = [];
29538 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29545 renderPreview : function(file)
29547 if(typeof(file.target) != 'undefined' && file.target){
29551 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29553 var previewEl = this.managerEl.createChild({
29555 cls : 'roo-document-manager-preview',
29559 tooltip : file[this.toolTipName],
29560 cls : 'roo-document-manager-thumb',
29561 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29566 html : '<i class="fa fa-times-circle"></i>'
29571 var close = previewEl.select('button.close', true).first();
29573 close.on('click', this.onRemove, this, file);
29575 file.target = previewEl;
29577 var image = previewEl.select('img', true).first();
29581 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29583 image.on('click', this.onClick, this, file);
29585 this.fireEvent('previewrendered', this, file);
29591 onPreviewLoad : function(file, image)
29593 if(typeof(file.target) == 'undefined' || !file.target){
29597 var width = image.dom.naturalWidth || image.dom.width;
29598 var height = image.dom.naturalHeight || image.dom.height;
29600 if(!this.previewResize) {
29604 if(width > height){
29605 file.target.addClass('wide');
29609 file.target.addClass('tall');
29614 uploadFromSource : function(file, crop)
29616 this.xhr = new XMLHttpRequest();
29618 this.managerEl.createChild({
29620 cls : 'roo-document-manager-loading',
29624 tooltip : file.name,
29625 cls : 'roo-document-manager-thumb',
29626 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29632 this.xhr.open(this.method, this.url, true);
29635 "Accept": "application/json",
29636 "Cache-Control": "no-cache",
29637 "X-Requested-With": "XMLHttpRequest"
29640 for (var headerName in headers) {
29641 var headerValue = headers[headerName];
29643 this.xhr.setRequestHeader(headerName, headerValue);
29649 this.xhr.onload = function()
29651 _this.xhrOnLoad(_this.xhr);
29654 this.xhr.onerror = function()
29656 _this.xhrOnError(_this.xhr);
29659 var formData = new FormData();
29661 formData.append('returnHTML', 'NO');
29663 formData.append('crop', crop);
29665 if(typeof(file.filename) != 'undefined'){
29666 formData.append('filename', file.filename);
29669 if(typeof(file.mimetype) != 'undefined'){
29670 formData.append('mimetype', file.mimetype);
29675 if(this.fireEvent('prepare', this, formData) != false){
29676 this.xhr.send(formData);
29686 * @class Roo.bootstrap.DocumentViewer
29687 * @extends Roo.bootstrap.Component
29688 * Bootstrap DocumentViewer class
29689 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29690 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29693 * Create a new DocumentViewer
29694 * @param {Object} config The config object
29697 Roo.bootstrap.DocumentViewer = function(config){
29698 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29703 * Fire after initEvent
29704 * @param {Roo.bootstrap.DocumentViewer} this
29710 * @param {Roo.bootstrap.DocumentViewer} this
29715 * Fire after download button
29716 * @param {Roo.bootstrap.DocumentViewer} this
29721 * Fire after trash button
29722 * @param {Roo.bootstrap.DocumentViewer} this
29729 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29731 showDownload : true,
29735 getAutoCreate : function()
29739 cls : 'roo-document-viewer',
29743 cls : 'roo-document-viewer-body',
29747 cls : 'roo-document-viewer-thumb',
29751 cls : 'roo-document-viewer-image'
29759 cls : 'roo-document-viewer-footer',
29762 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29766 cls : 'btn-group roo-document-viewer-download',
29770 cls : 'btn btn-default',
29771 html : '<i class="fa fa-download"></i>'
29777 cls : 'btn-group roo-document-viewer-trash',
29781 cls : 'btn btn-default',
29782 html : '<i class="fa fa-trash"></i>'
29795 initEvents : function()
29797 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29798 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29800 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29801 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29803 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29804 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29806 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29807 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29809 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29810 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29812 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29813 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29815 this.bodyEl.on('click', this.onClick, this);
29816 this.downloadBtn.on('click', this.onDownload, this);
29817 this.trashBtn.on('click', this.onTrash, this);
29819 this.downloadBtn.hide();
29820 this.trashBtn.hide();
29822 if(this.showDownload){
29823 this.downloadBtn.show();
29826 if(this.showTrash){
29827 this.trashBtn.show();
29830 if(!this.showDownload && !this.showTrash) {
29831 this.footerEl.hide();
29836 initial : function()
29838 this.fireEvent('initial', this);
29842 onClick : function(e)
29844 e.preventDefault();
29846 this.fireEvent('click', this);
29849 onDownload : function(e)
29851 e.preventDefault();
29853 this.fireEvent('download', this);
29856 onTrash : function(e)
29858 e.preventDefault();
29860 this.fireEvent('trash', this);
29872 * @class Roo.bootstrap.NavProgressBar
29873 * @extends Roo.bootstrap.Component
29874 * Bootstrap NavProgressBar class
29877 * Create a new nav progress bar
29878 * @param {Object} config The config object
29881 Roo.bootstrap.NavProgressBar = function(config){
29882 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29884 this.bullets = this.bullets || [];
29886 // Roo.bootstrap.NavProgressBar.register(this);
29890 * Fires when the active item changes
29891 * @param {Roo.bootstrap.NavProgressBar} this
29892 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29893 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29900 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29905 getAutoCreate : function()
29907 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29911 cls : 'roo-navigation-bar-group',
29915 cls : 'roo-navigation-top-bar'
29919 cls : 'roo-navigation-bullets-bar',
29923 cls : 'roo-navigation-bar'
29930 cls : 'roo-navigation-bottom-bar'
29940 initEvents: function()
29945 onRender : function(ct, position)
29947 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29949 if(this.bullets.length){
29950 Roo.each(this.bullets, function(b){
29959 addItem : function(cfg)
29961 var item = new Roo.bootstrap.NavProgressItem(cfg);
29963 item.parentId = this.id;
29964 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29967 var top = new Roo.bootstrap.Element({
29969 cls : 'roo-navigation-bar-text'
29972 var bottom = new Roo.bootstrap.Element({
29974 cls : 'roo-navigation-bar-text'
29977 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29978 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29980 var topText = new Roo.bootstrap.Element({
29982 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29985 var bottomText = new Roo.bootstrap.Element({
29987 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29990 topText.onRender(top.el, null);
29991 bottomText.onRender(bottom.el, null);
29994 item.bottomEl = bottom;
29997 this.barItems.push(item);
30002 getActive : function()
30004 var active = false;
30006 Roo.each(this.barItems, function(v){
30008 if (!v.isActive()) {
30020 setActiveItem : function(item)
30024 Roo.each(this.barItems, function(v){
30025 if (v.rid == item.rid) {
30029 if (v.isActive()) {
30030 v.setActive(false);
30035 item.setActive(true);
30037 this.fireEvent('changed', this, item, prev);
30040 getBarItem: function(rid)
30044 Roo.each(this.barItems, function(e) {
30045 if (e.rid != rid) {
30056 indexOfItem : function(item)
30060 Roo.each(this.barItems, function(v, i){
30062 if (v.rid != item.rid) {
30073 setActiveNext : function()
30075 var i = this.indexOfItem(this.getActive());
30077 if (i > this.barItems.length) {
30081 this.setActiveItem(this.barItems[i+1]);
30084 setActivePrev : function()
30086 var i = this.indexOfItem(this.getActive());
30092 this.setActiveItem(this.barItems[i-1]);
30095 format : function()
30097 if(!this.barItems.length){
30101 var width = 100 / this.barItems.length;
30103 Roo.each(this.barItems, function(i){
30104 i.el.setStyle('width', width + '%');
30105 i.topEl.el.setStyle('width', width + '%');
30106 i.bottomEl.el.setStyle('width', width + '%');
30115 * Nav Progress Item
30120 * @class Roo.bootstrap.NavProgressItem
30121 * @extends Roo.bootstrap.Component
30122 * Bootstrap NavProgressItem class
30123 * @cfg {String} rid the reference id
30124 * @cfg {Boolean} active (true|false) Is item active default false
30125 * @cfg {Boolean} disabled (true|false) Is item active default false
30126 * @cfg {String} html
30127 * @cfg {String} position (top|bottom) text position default bottom
30128 * @cfg {String} icon show icon instead of number
30131 * Create a new NavProgressItem
30132 * @param {Object} config The config object
30134 Roo.bootstrap.NavProgressItem = function(config){
30135 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30140 * The raw click event for the entire grid.
30141 * @param {Roo.bootstrap.NavProgressItem} this
30142 * @param {Roo.EventObject} e
30149 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30155 position : 'bottom',
30158 getAutoCreate : function()
30160 var iconCls = 'roo-navigation-bar-item-icon';
30162 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30166 cls: 'roo-navigation-bar-item',
30176 cfg.cls += ' active';
30179 cfg.cls += ' disabled';
30185 disable : function()
30187 this.setDisabled(true);
30190 enable : function()
30192 this.setDisabled(false);
30195 initEvents: function()
30197 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30199 this.iconEl.on('click', this.onClick, this);
30202 onClick : function(e)
30204 e.preventDefault();
30210 if(this.fireEvent('click', this, e) === false){
30214 this.parent().setActiveItem(this);
30217 isActive: function ()
30219 return this.active;
30222 setActive : function(state)
30224 if(this.active == state){
30228 this.active = state;
30231 this.el.addClass('active');
30235 this.el.removeClass('active');
30240 setDisabled : function(state)
30242 if(this.disabled == state){
30246 this.disabled = state;
30249 this.el.addClass('disabled');
30253 this.el.removeClass('disabled');
30256 tooltipEl : function()
30258 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30271 * @class Roo.bootstrap.FieldLabel
30272 * @extends Roo.bootstrap.Component
30273 * Bootstrap FieldLabel class
30274 * @cfg {String} html contents of the element
30275 * @cfg {String} tag tag of the element default label
30276 * @cfg {String} cls class of the element
30277 * @cfg {String} target label target
30278 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30279 * @cfg {String} invalidClass default "text-warning"
30280 * @cfg {String} validClass default "text-success"
30281 * @cfg {String} iconTooltip default "This field is required"
30282 * @cfg {String} indicatorpos (left|right) default left
30285 * Create a new FieldLabel
30286 * @param {Object} config The config object
30289 Roo.bootstrap.FieldLabel = function(config){
30290 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30295 * Fires after the field has been marked as invalid.
30296 * @param {Roo.form.FieldLabel} this
30297 * @param {String} msg The validation message
30302 * Fires after the field has been validated with no errors.
30303 * @param {Roo.form.FieldLabel} this
30309 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30316 invalidClass : 'has-warning',
30317 validClass : 'has-success',
30318 iconTooltip : 'This field is required',
30319 indicatorpos : 'left',
30321 getAutoCreate : function(){
30324 if (!this.allowBlank) {
30330 cls : 'roo-bootstrap-field-label ' + this.cls,
30335 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30336 tooltip : this.iconTooltip
30345 if(this.indicatorpos == 'right'){
30348 cls : 'roo-bootstrap-field-label ' + this.cls,
30357 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30358 tooltip : this.iconTooltip
30367 initEvents: function()
30369 Roo.bootstrap.Element.superclass.initEvents.call(this);
30371 this.indicator = this.indicatorEl();
30373 if(this.indicator){
30374 this.indicator.removeClass('visible');
30375 this.indicator.addClass('invisible');
30378 Roo.bootstrap.FieldLabel.register(this);
30381 indicatorEl : function()
30383 var indicator = this.el.select('i.roo-required-indicator',true).first();
30394 * Mark this field as valid
30396 markValid : function()
30398 if(this.indicator){
30399 this.indicator.removeClass('visible');
30400 this.indicator.addClass('invisible');
30403 this.el.removeClass(this.invalidClass);
30405 this.el.addClass(this.validClass);
30407 this.fireEvent('valid', this);
30411 * Mark this field as invalid
30412 * @param {String} msg The validation message
30414 markInvalid : function(msg)
30416 if(this.indicator){
30417 this.indicator.removeClass('invisible');
30418 this.indicator.addClass('visible');
30421 this.el.removeClass(this.validClass);
30423 this.el.addClass(this.invalidClass);
30425 this.fireEvent('invalid', this, msg);
30431 Roo.apply(Roo.bootstrap.FieldLabel, {
30436 * register a FieldLabel Group
30437 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30439 register : function(label)
30441 if(this.groups.hasOwnProperty(label.target)){
30445 this.groups[label.target] = label;
30449 * fetch a FieldLabel Group based on the target
30450 * @param {string} target
30451 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30453 get: function(target) {
30454 if (typeof(this.groups[target]) == 'undefined') {
30458 return this.groups[target] ;
30467 * page DateSplitField.
30473 * @class Roo.bootstrap.DateSplitField
30474 * @extends Roo.bootstrap.Component
30475 * Bootstrap DateSplitField class
30476 * @cfg {string} fieldLabel - the label associated
30477 * @cfg {Number} labelWidth set the width of label (0-12)
30478 * @cfg {String} labelAlign (top|left)
30479 * @cfg {Boolean} dayAllowBlank (true|false) default false
30480 * @cfg {Boolean} monthAllowBlank (true|false) default false
30481 * @cfg {Boolean} yearAllowBlank (true|false) default false
30482 * @cfg {string} dayPlaceholder
30483 * @cfg {string} monthPlaceholder
30484 * @cfg {string} yearPlaceholder
30485 * @cfg {string} dayFormat default 'd'
30486 * @cfg {string} monthFormat default 'm'
30487 * @cfg {string} yearFormat default 'Y'
30488 * @cfg {Number} labellg set the width of label (1-12)
30489 * @cfg {Number} labelmd set the width of label (1-12)
30490 * @cfg {Number} labelsm set the width of label (1-12)
30491 * @cfg {Number} labelxs set the width of label (1-12)
30495 * Create a new DateSplitField
30496 * @param {Object} config The config object
30499 Roo.bootstrap.DateSplitField = function(config){
30500 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30506 * getting the data of years
30507 * @param {Roo.bootstrap.DateSplitField} this
30508 * @param {Object} years
30513 * getting the data of days
30514 * @param {Roo.bootstrap.DateSplitField} this
30515 * @param {Object} days
30520 * Fires after the field has been marked as invalid.
30521 * @param {Roo.form.Field} this
30522 * @param {String} msg The validation message
30527 * Fires after the field has been validated with no errors.
30528 * @param {Roo.form.Field} this
30534 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30537 labelAlign : 'top',
30539 dayAllowBlank : false,
30540 monthAllowBlank : false,
30541 yearAllowBlank : false,
30542 dayPlaceholder : '',
30543 monthPlaceholder : '',
30544 yearPlaceholder : '',
30548 isFormField : true,
30554 getAutoCreate : function()
30558 cls : 'row roo-date-split-field-group',
30563 cls : 'form-hidden-field roo-date-split-field-group-value',
30569 var labelCls = 'col-md-12';
30570 var contentCls = 'col-md-4';
30572 if(this.fieldLabel){
30576 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30580 html : this.fieldLabel
30585 if(this.labelAlign == 'left'){
30587 if(this.labelWidth > 12){
30588 label.style = "width: " + this.labelWidth + 'px';
30591 if(this.labelWidth < 13 && this.labelmd == 0){
30592 this.labelmd = this.labelWidth;
30595 if(this.labellg > 0){
30596 labelCls = ' col-lg-' + this.labellg;
30597 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30600 if(this.labelmd > 0){
30601 labelCls = ' col-md-' + this.labelmd;
30602 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30605 if(this.labelsm > 0){
30606 labelCls = ' col-sm-' + this.labelsm;
30607 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30610 if(this.labelxs > 0){
30611 labelCls = ' col-xs-' + this.labelxs;
30612 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30616 label.cls += ' ' + labelCls;
30618 cfg.cn.push(label);
30621 Roo.each(['day', 'month', 'year'], function(t){
30624 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30631 inputEl: function ()
30633 return this.el.select('.roo-date-split-field-group-value', true).first();
30636 onRender : function(ct, position)
30640 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30642 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30644 this.dayField = new Roo.bootstrap.ComboBox({
30645 allowBlank : this.dayAllowBlank,
30646 alwaysQuery : true,
30647 displayField : 'value',
30650 forceSelection : true,
30652 placeholder : this.dayPlaceholder,
30653 selectOnFocus : true,
30654 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30655 triggerAction : 'all',
30657 valueField : 'value',
30658 store : new Roo.data.SimpleStore({
30659 data : (function() {
30661 _this.fireEvent('days', _this, days);
30664 fields : [ 'value' ]
30667 select : function (_self, record, index)
30669 _this.setValue(_this.getValue());
30674 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30676 this.monthField = new Roo.bootstrap.MonthField({
30677 after : '<i class=\"fa fa-calendar\"></i>',
30678 allowBlank : this.monthAllowBlank,
30679 placeholder : this.monthPlaceholder,
30682 render : function (_self)
30684 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30685 e.preventDefault();
30689 select : function (_self, oldvalue, newvalue)
30691 _this.setValue(_this.getValue());
30696 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30698 this.yearField = new Roo.bootstrap.ComboBox({
30699 allowBlank : this.yearAllowBlank,
30700 alwaysQuery : true,
30701 displayField : 'value',
30704 forceSelection : true,
30706 placeholder : this.yearPlaceholder,
30707 selectOnFocus : true,
30708 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30709 triggerAction : 'all',
30711 valueField : 'value',
30712 store : new Roo.data.SimpleStore({
30713 data : (function() {
30715 _this.fireEvent('years', _this, years);
30718 fields : [ 'value' ]
30721 select : function (_self, record, index)
30723 _this.setValue(_this.getValue());
30728 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30731 setValue : function(v, format)
30733 this.inputEl.dom.value = v;
30735 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30737 var d = Date.parseDate(v, f);
30744 this.setDay(d.format(this.dayFormat));
30745 this.setMonth(d.format(this.monthFormat));
30746 this.setYear(d.format(this.yearFormat));
30753 setDay : function(v)
30755 this.dayField.setValue(v);
30756 this.inputEl.dom.value = this.getValue();
30761 setMonth : function(v)
30763 this.monthField.setValue(v, true);
30764 this.inputEl.dom.value = this.getValue();
30769 setYear : function(v)
30771 this.yearField.setValue(v);
30772 this.inputEl.dom.value = this.getValue();
30777 getDay : function()
30779 return this.dayField.getValue();
30782 getMonth : function()
30784 return this.monthField.getValue();
30787 getYear : function()
30789 return this.yearField.getValue();
30792 getValue : function()
30794 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30796 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30806 this.inputEl.dom.value = '';
30811 validate : function()
30813 var d = this.dayField.validate();
30814 var m = this.monthField.validate();
30815 var y = this.yearField.validate();
30820 (!this.dayAllowBlank && !d) ||
30821 (!this.monthAllowBlank && !m) ||
30822 (!this.yearAllowBlank && !y)
30827 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30836 this.markInvalid();
30841 markValid : function()
30844 var label = this.el.select('label', true).first();
30845 var icon = this.el.select('i.fa-star', true).first();
30851 this.fireEvent('valid', this);
30855 * Mark this field as invalid
30856 * @param {String} msg The validation message
30858 markInvalid : function(msg)
30861 var label = this.el.select('label', true).first();
30862 var icon = this.el.select('i.fa-star', true).first();
30864 if(label && !icon){
30865 this.el.select('.roo-date-split-field-label', true).createChild({
30867 cls : 'text-danger fa fa-lg fa-star',
30868 tooltip : 'This field is required',
30869 style : 'margin-right:5px;'
30873 this.fireEvent('invalid', this, msg);
30876 clearInvalid : function()
30878 var label = this.el.select('label', true).first();
30879 var icon = this.el.select('i.fa-star', true).first();
30885 this.fireEvent('valid', this);
30888 getName: function()
30898 * http://masonry.desandro.com
30900 * The idea is to render all the bricks based on vertical width...
30902 * The original code extends 'outlayer' - we might need to use that....
30908 * @class Roo.bootstrap.LayoutMasonry
30909 * @extends Roo.bootstrap.Component
30910 * Bootstrap Layout Masonry class
30913 * Create a new Element
30914 * @param {Object} config The config object
30917 Roo.bootstrap.LayoutMasonry = function(config){
30919 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30923 Roo.bootstrap.LayoutMasonry.register(this);
30929 * Fire after layout the items
30930 * @param {Roo.bootstrap.LayoutMasonry} this
30931 * @param {Roo.EventObject} e
30938 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30941 * @cfg {Boolean} isLayoutInstant = no animation?
30943 isLayoutInstant : false, // needed?
30946 * @cfg {Number} boxWidth width of the columns
30951 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30956 * @cfg {Number} padWidth padding below box..
30961 * @cfg {Number} gutter gutter width..
30966 * @cfg {Number} maxCols maximum number of columns
30972 * @cfg {Boolean} isAutoInitial defalut true
30974 isAutoInitial : true,
30979 * @cfg {Boolean} isHorizontal defalut false
30981 isHorizontal : false,
30983 currentSize : null,
30989 bricks: null, //CompositeElement
30993 _isLayoutInited : false,
30995 // isAlternative : false, // only use for vertical layout...
30998 * @cfg {Number} alternativePadWidth padding below box..
31000 alternativePadWidth : 50,
31002 selectedBrick : [],
31004 getAutoCreate : function(){
31006 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31010 cls: 'blog-masonary-wrapper ' + this.cls,
31012 cls : 'mas-boxes masonary'
31019 getChildContainer: function( )
31021 if (this.boxesEl) {
31022 return this.boxesEl;
31025 this.boxesEl = this.el.select('.mas-boxes').first();
31027 return this.boxesEl;
31031 initEvents : function()
31035 if(this.isAutoInitial){
31036 Roo.log('hook children rendered');
31037 this.on('childrenrendered', function() {
31038 Roo.log('children rendered');
31044 initial : function()
31046 this.selectedBrick = [];
31048 this.currentSize = this.el.getBox(true);
31050 Roo.EventManager.onWindowResize(this.resize, this);
31052 if(!this.isAutoInitial){
31060 //this.layout.defer(500,this);
31064 resize : function()
31066 var cs = this.el.getBox(true);
31069 this.currentSize.width == cs.width &&
31070 this.currentSize.x == cs.x &&
31071 this.currentSize.height == cs.height &&
31072 this.currentSize.y == cs.y
31074 Roo.log("no change in with or X or Y");
31078 this.currentSize = cs;
31084 layout : function()
31086 this._resetLayout();
31088 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31090 this.layoutItems( isInstant );
31092 this._isLayoutInited = true;
31094 this.fireEvent('layout', this);
31098 _resetLayout : function()
31100 if(this.isHorizontal){
31101 this.horizontalMeasureColumns();
31105 this.verticalMeasureColumns();
31109 verticalMeasureColumns : function()
31111 this.getContainerWidth();
31113 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31114 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31118 var boxWidth = this.boxWidth + this.padWidth;
31120 if(this.containerWidth < this.boxWidth){
31121 boxWidth = this.containerWidth
31124 var containerWidth = this.containerWidth;
31126 var cols = Math.floor(containerWidth / boxWidth);
31128 this.cols = Math.max( cols, 1 );
31130 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31132 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31134 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31136 this.colWidth = boxWidth + avail - this.padWidth;
31138 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31139 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31142 horizontalMeasureColumns : function()
31144 this.getContainerWidth();
31146 var boxWidth = this.boxWidth;
31148 if(this.containerWidth < boxWidth){
31149 boxWidth = this.containerWidth;
31152 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31154 this.el.setHeight(boxWidth);
31158 getContainerWidth : function()
31160 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31163 layoutItems : function( isInstant )
31165 Roo.log(this.bricks);
31167 var items = Roo.apply([], this.bricks);
31169 if(this.isHorizontal){
31170 this._horizontalLayoutItems( items , isInstant );
31174 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31175 // this._verticalAlternativeLayoutItems( items , isInstant );
31179 this._verticalLayoutItems( items , isInstant );
31183 _verticalLayoutItems : function ( items , isInstant)
31185 if ( !items || !items.length ) {
31190 ['xs', 'xs', 'xs', 'tall'],
31191 ['xs', 'xs', 'tall'],
31192 ['xs', 'xs', 'sm'],
31193 ['xs', 'xs', 'xs'],
31199 ['sm', 'xs', 'xs'],
31203 ['tall', 'xs', 'xs', 'xs'],
31204 ['tall', 'xs', 'xs'],
31216 Roo.each(items, function(item, k){
31218 switch (item.size) {
31219 // these layouts take up a full box,
31230 boxes.push([item]);
31253 var filterPattern = function(box, length)
31261 var pattern = box.slice(0, length);
31265 Roo.each(pattern, function(i){
31266 format.push(i.size);
31269 Roo.each(standard, function(s){
31271 if(String(s) != String(format)){
31280 if(!match && length == 1){
31285 filterPattern(box, length - 1);
31289 queue.push(pattern);
31291 box = box.slice(length, box.length);
31293 filterPattern(box, 4);
31299 Roo.each(boxes, function(box, k){
31305 if(box.length == 1){
31310 filterPattern(box, 4);
31314 this._processVerticalLayoutQueue( queue, isInstant );
31318 // _verticalAlternativeLayoutItems : function( items , isInstant )
31320 // if ( !items || !items.length ) {
31324 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31328 _horizontalLayoutItems : function ( items , isInstant)
31330 if ( !items || !items.length || items.length < 3) {
31336 var eItems = items.slice(0, 3);
31338 items = items.slice(3, items.length);
31341 ['xs', 'xs', 'xs', 'wide'],
31342 ['xs', 'xs', 'wide'],
31343 ['xs', 'xs', 'sm'],
31344 ['xs', 'xs', 'xs'],
31350 ['sm', 'xs', 'xs'],
31354 ['wide', 'xs', 'xs', 'xs'],
31355 ['wide', 'xs', 'xs'],
31368 Roo.each(items, function(item, k){
31370 switch (item.size) {
31381 boxes.push([item]);
31405 var filterPattern = function(box, length)
31413 var pattern = box.slice(0, length);
31417 Roo.each(pattern, function(i){
31418 format.push(i.size);
31421 Roo.each(standard, function(s){
31423 if(String(s) != String(format)){
31432 if(!match && length == 1){
31437 filterPattern(box, length - 1);
31441 queue.push(pattern);
31443 box = box.slice(length, box.length);
31445 filterPattern(box, 4);
31451 Roo.each(boxes, function(box, k){
31457 if(box.length == 1){
31462 filterPattern(box, 4);
31469 var pos = this.el.getBox(true);
31473 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31475 var hit_end = false;
31477 Roo.each(queue, function(box){
31481 Roo.each(box, function(b){
31483 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31493 Roo.each(box, function(b){
31495 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31498 mx = Math.max(mx, b.x);
31502 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31506 Roo.each(box, function(b){
31508 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31522 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31525 /** Sets position of item in DOM
31526 * @param {Element} item
31527 * @param {Number} x - horizontal position
31528 * @param {Number} y - vertical position
31529 * @param {Boolean} isInstant - disables transitions
31531 _processVerticalLayoutQueue : function( queue, isInstant )
31533 var pos = this.el.getBox(true);
31538 for (var i = 0; i < this.cols; i++){
31542 Roo.each(queue, function(box, k){
31544 var col = k % this.cols;
31546 Roo.each(box, function(b,kk){
31548 b.el.position('absolute');
31550 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31551 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31553 if(b.size == 'md-left' || b.size == 'md-right'){
31554 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31555 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31558 b.el.setWidth(width);
31559 b.el.setHeight(height);
31561 b.el.select('iframe',true).setSize(width,height);
31565 for (var i = 0; i < this.cols; i++){
31567 if(maxY[i] < maxY[col]){
31572 col = Math.min(col, i);
31576 x = pos.x + col * (this.colWidth + this.padWidth);
31580 var positions = [];
31582 switch (box.length){
31584 positions = this.getVerticalOneBoxColPositions(x, y, box);
31587 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31590 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31593 positions = this.getVerticalFourBoxColPositions(x, y, box);
31599 Roo.each(box, function(b,kk){
31601 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31603 var sz = b.el.getSize();
31605 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31613 for (var i = 0; i < this.cols; i++){
31614 mY = Math.max(mY, maxY[i]);
31617 this.el.setHeight(mY - pos.y);
31621 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31623 // var pos = this.el.getBox(true);
31626 // var maxX = pos.right;
31628 // var maxHeight = 0;
31630 // Roo.each(items, function(item, k){
31634 // item.el.position('absolute');
31636 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31638 // item.el.setWidth(width);
31640 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31642 // item.el.setHeight(height);
31645 // item.el.setXY([x, y], isInstant ? false : true);
31647 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31650 // y = y + height + this.alternativePadWidth;
31652 // maxHeight = maxHeight + height + this.alternativePadWidth;
31656 // this.el.setHeight(maxHeight);
31660 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31662 var pos = this.el.getBox(true);
31667 var maxX = pos.right;
31669 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31671 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31673 Roo.each(queue, function(box, k){
31675 Roo.each(box, function(b, kk){
31677 b.el.position('absolute');
31679 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31680 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31682 if(b.size == 'md-left' || b.size == 'md-right'){
31683 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31684 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31687 b.el.setWidth(width);
31688 b.el.setHeight(height);
31696 var positions = [];
31698 switch (box.length){
31700 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31703 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31706 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31709 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31715 Roo.each(box, function(b,kk){
31717 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31719 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31727 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31729 Roo.each(eItems, function(b,k){
31731 b.size = (k == 0) ? 'sm' : 'xs';
31732 b.x = (k == 0) ? 2 : 1;
31733 b.y = (k == 0) ? 2 : 1;
31735 b.el.position('absolute');
31737 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31739 b.el.setWidth(width);
31741 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31743 b.el.setHeight(height);
31747 var positions = [];
31750 x : maxX - this.unitWidth * 2 - this.gutter,
31755 x : maxX - this.unitWidth,
31756 y : minY + (this.unitWidth + this.gutter) * 2
31760 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31764 Roo.each(eItems, function(b,k){
31766 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31772 getVerticalOneBoxColPositions : function(x, y, box)
31776 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31778 if(box[0].size == 'md-left'){
31782 if(box[0].size == 'md-right'){
31787 x : x + (this.unitWidth + this.gutter) * rand,
31794 getVerticalTwoBoxColPositions : function(x, y, box)
31798 if(box[0].size == 'xs'){
31802 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31806 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31820 x : x + (this.unitWidth + this.gutter) * 2,
31821 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31828 getVerticalThreeBoxColPositions : function(x, y, box)
31832 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31840 x : x + (this.unitWidth + this.gutter) * 1,
31845 x : x + (this.unitWidth + this.gutter) * 2,
31853 if(box[0].size == 'xs' && box[1].size == 'xs'){
31862 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31866 x : x + (this.unitWidth + this.gutter) * 1,
31880 x : x + (this.unitWidth + this.gutter) * 2,
31885 x : x + (this.unitWidth + this.gutter) * 2,
31886 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31893 getVerticalFourBoxColPositions : function(x, y, box)
31897 if(box[0].size == 'xs'){
31906 y : y + (this.unitHeight + this.gutter) * 1
31911 y : y + (this.unitHeight + this.gutter) * 2
31915 x : x + (this.unitWidth + this.gutter) * 1,
31929 x : x + (this.unitWidth + this.gutter) * 2,
31934 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31935 y : y + (this.unitHeight + this.gutter) * 1
31939 x : x + (this.unitWidth + this.gutter) * 2,
31940 y : y + (this.unitWidth + this.gutter) * 2
31947 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31951 if(box[0].size == 'md-left'){
31953 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31960 if(box[0].size == 'md-right'){
31962 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31963 y : minY + (this.unitWidth + this.gutter) * 1
31969 var rand = Math.floor(Math.random() * (4 - box[0].y));
31972 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31973 y : minY + (this.unitWidth + this.gutter) * rand
31980 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31984 if(box[0].size == 'xs'){
31987 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31992 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31993 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32001 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32006 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32007 y : minY + (this.unitWidth + this.gutter) * 2
32014 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32018 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32021 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32026 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32027 y : minY + (this.unitWidth + this.gutter) * 1
32031 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32032 y : minY + (this.unitWidth + this.gutter) * 2
32039 if(box[0].size == 'xs' && box[1].size == 'xs'){
32042 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32047 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32052 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32053 y : minY + (this.unitWidth + this.gutter) * 1
32061 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32066 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32067 y : minY + (this.unitWidth + this.gutter) * 2
32071 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32072 y : minY + (this.unitWidth + this.gutter) * 2
32079 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32083 if(box[0].size == 'xs'){
32086 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].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),
32096 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32101 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32102 y : minY + (this.unitWidth + this.gutter) * 1
32110 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32115 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].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),
32121 y : minY + (this.unitWidth + this.gutter) * 2
32125 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32126 y : minY + (this.unitWidth + this.gutter) * 2
32134 * remove a Masonry Brick
32135 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32137 removeBrick : function(brick_id)
32143 for (var i = 0; i<this.bricks.length; i++) {
32144 if (this.bricks[i].id == brick_id) {
32145 this.bricks.splice(i,1);
32146 this.el.dom.removeChild(Roo.get(brick_id).dom);
32153 * adds a Masonry Brick
32154 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32156 addBrick : function(cfg)
32158 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32159 //this.register(cn);
32160 cn.parentId = this.id;
32161 cn.render(this.el);
32166 * register a Masonry Brick
32167 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32170 register : function(brick)
32172 this.bricks.push(brick);
32173 brick.masonryId = this.id;
32177 * clear all the Masonry Brick
32179 clearAll : function()
32182 //this.getChildContainer().dom.innerHTML = "";
32183 this.el.dom.innerHTML = '';
32186 getSelected : function()
32188 if (!this.selectedBrick) {
32192 return this.selectedBrick;
32196 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32200 * register a Masonry Layout
32201 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32204 register : function(layout)
32206 this.groups[layout.id] = layout;
32209 * fetch a Masonry Layout based on the masonry layout ID
32210 * @param {string} the masonry layout to add
32211 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32214 get: function(layout_id) {
32215 if (typeof(this.groups[layout_id]) == 'undefined') {
32218 return this.groups[layout_id] ;
32230 * http://masonry.desandro.com
32232 * The idea is to render all the bricks based on vertical width...
32234 * The original code extends 'outlayer' - we might need to use that....
32240 * @class Roo.bootstrap.LayoutMasonryAuto
32241 * @extends Roo.bootstrap.Component
32242 * Bootstrap Layout Masonry class
32245 * Create a new Element
32246 * @param {Object} config The config object
32249 Roo.bootstrap.LayoutMasonryAuto = function(config){
32250 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32253 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32256 * @cfg {Boolean} isFitWidth - resize the width..
32258 isFitWidth : false, // options..
32260 * @cfg {Boolean} isOriginLeft = left align?
32262 isOriginLeft : true,
32264 * @cfg {Boolean} isOriginTop = top align?
32266 isOriginTop : false,
32268 * @cfg {Boolean} isLayoutInstant = no animation?
32270 isLayoutInstant : false, // needed?
32272 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32274 isResizingContainer : true,
32276 * @cfg {Number} columnWidth width of the columns
32282 * @cfg {Number} maxCols maximum number of columns
32287 * @cfg {Number} padHeight padding below box..
32293 * @cfg {Boolean} isAutoInitial defalut true
32296 isAutoInitial : true,
32302 initialColumnWidth : 0,
32303 currentSize : null,
32305 colYs : null, // array.
32312 bricks: null, //CompositeElement
32313 cols : 0, // array?
32314 // element : null, // wrapped now this.el
32315 _isLayoutInited : null,
32318 getAutoCreate : function(){
32322 cls: 'blog-masonary-wrapper ' + this.cls,
32324 cls : 'mas-boxes masonary'
32331 getChildContainer: function( )
32333 if (this.boxesEl) {
32334 return this.boxesEl;
32337 this.boxesEl = this.el.select('.mas-boxes').first();
32339 return this.boxesEl;
32343 initEvents : function()
32347 if(this.isAutoInitial){
32348 Roo.log('hook children rendered');
32349 this.on('childrenrendered', function() {
32350 Roo.log('children rendered');
32357 initial : function()
32359 this.reloadItems();
32361 this.currentSize = this.el.getBox(true);
32363 /// was window resize... - let's see if this works..
32364 Roo.EventManager.onWindowResize(this.resize, this);
32366 if(!this.isAutoInitial){
32371 this.layout.defer(500,this);
32374 reloadItems: function()
32376 this.bricks = this.el.select('.masonry-brick', true);
32378 this.bricks.each(function(b) {
32379 //Roo.log(b.getSize());
32380 if (!b.attr('originalwidth')) {
32381 b.attr('originalwidth', b.getSize().width);
32386 Roo.log(this.bricks.elements.length);
32389 resize : function()
32392 var cs = this.el.getBox(true);
32394 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32395 Roo.log("no change in with or X");
32398 this.currentSize = cs;
32402 layout : function()
32405 this._resetLayout();
32406 //this._manageStamps();
32408 // don't animate first layout
32409 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32410 this.layoutItems( isInstant );
32412 // flag for initalized
32413 this._isLayoutInited = true;
32416 layoutItems : function( isInstant )
32418 //var items = this._getItemsForLayout( this.items );
32419 // original code supports filtering layout items.. we just ignore it..
32421 this._layoutItems( this.bricks , isInstant );
32423 this._postLayout();
32425 _layoutItems : function ( items , isInstant)
32427 //this.fireEvent( 'layout', this, items );
32430 if ( !items || !items.elements.length ) {
32431 // no items, emit event with empty array
32436 items.each(function(item) {
32437 Roo.log("layout item");
32439 // get x/y object from method
32440 var position = this._getItemLayoutPosition( item );
32442 position.item = item;
32443 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32444 queue.push( position );
32447 this._processLayoutQueue( queue );
32449 /** Sets position of item in DOM
32450 * @param {Element} item
32451 * @param {Number} x - horizontal position
32452 * @param {Number} y - vertical position
32453 * @param {Boolean} isInstant - disables transitions
32455 _processLayoutQueue : function( queue )
32457 for ( var i=0, len = queue.length; i < len; i++ ) {
32458 var obj = queue[i];
32459 obj.item.position('absolute');
32460 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32466 * Any logic you want to do after each layout,
32467 * i.e. size the container
32469 _postLayout : function()
32471 this.resizeContainer();
32474 resizeContainer : function()
32476 if ( !this.isResizingContainer ) {
32479 var size = this._getContainerSize();
32481 this.el.setSize(size.width,size.height);
32482 this.boxesEl.setSize(size.width,size.height);
32488 _resetLayout : function()
32490 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32491 this.colWidth = this.el.getWidth();
32492 //this.gutter = this.el.getWidth();
32494 this.measureColumns();
32500 this.colYs.push( 0 );
32506 measureColumns : function()
32508 this.getContainerWidth();
32509 // if columnWidth is 0, default to outerWidth of first item
32510 if ( !this.columnWidth ) {
32511 var firstItem = this.bricks.first();
32512 Roo.log(firstItem);
32513 this.columnWidth = this.containerWidth;
32514 if (firstItem && firstItem.attr('originalwidth') ) {
32515 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32517 // columnWidth fall back to item of first element
32518 Roo.log("set column width?");
32519 this.initialColumnWidth = this.columnWidth ;
32521 // if first elem has no width, default to size of container
32526 if (this.initialColumnWidth) {
32527 this.columnWidth = this.initialColumnWidth;
32532 // column width is fixed at the top - however if container width get's smaller we should
32535 // this bit calcs how man columns..
32537 var columnWidth = this.columnWidth += this.gutter;
32539 // calculate columns
32540 var containerWidth = this.containerWidth + this.gutter;
32542 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32543 // fix rounding errors, typically with gutters
32544 var excess = columnWidth - containerWidth % columnWidth;
32547 // if overshoot is less than a pixel, round up, otherwise floor it
32548 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32549 cols = Math[ mathMethod ]( cols );
32550 this.cols = Math.max( cols, 1 );
32551 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32553 // padding positioning..
32554 var totalColWidth = this.cols * this.columnWidth;
32555 var padavail = this.containerWidth - totalColWidth;
32556 // so for 2 columns - we need 3 'pads'
32558 var padNeeded = (1+this.cols) * this.padWidth;
32560 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32562 this.columnWidth += padExtra
32563 //this.padWidth = Math.floor(padavail / ( this.cols));
32565 // adjust colum width so that padding is fixed??
32567 // we have 3 columns ... total = width * 3
32568 // we have X left over... that should be used by
32570 //if (this.expandC) {
32578 getContainerWidth : function()
32580 /* // container is parent if fit width
32581 var container = this.isFitWidth ? this.element.parentNode : this.element;
32582 // check that this.size and size are there
32583 // IE8 triggers resize on body size change, so they might not be
32585 var size = getSize( container ); //FIXME
32586 this.containerWidth = size && size.innerWidth; //FIXME
32589 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32593 _getItemLayoutPosition : function( item ) // what is item?
32595 // we resize the item to our columnWidth..
32597 item.setWidth(this.columnWidth);
32598 item.autoBoxAdjust = false;
32600 var sz = item.getSize();
32602 // how many columns does this brick span
32603 var remainder = this.containerWidth % this.columnWidth;
32605 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32606 // round if off by 1 pixel, otherwise use ceil
32607 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32608 colSpan = Math.min( colSpan, this.cols );
32610 // normally this should be '1' as we dont' currently allow multi width columns..
32612 var colGroup = this._getColGroup( colSpan );
32613 // get the minimum Y value from the columns
32614 var minimumY = Math.min.apply( Math, colGroup );
32615 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32617 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32619 // position the brick
32621 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32622 y: this.currentSize.y + minimumY + this.padHeight
32626 // apply setHeight to necessary columns
32627 var setHeight = minimumY + sz.height + this.padHeight;
32628 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32630 var setSpan = this.cols + 1 - colGroup.length;
32631 for ( var i = 0; i < setSpan; i++ ) {
32632 this.colYs[ shortColIndex + i ] = setHeight ;
32639 * @param {Number} colSpan - number of columns the element spans
32640 * @returns {Array} colGroup
32642 _getColGroup : function( colSpan )
32644 if ( colSpan < 2 ) {
32645 // if brick spans only one column, use all the column Ys
32650 // how many different places could this brick fit horizontally
32651 var groupCount = this.cols + 1 - colSpan;
32652 // for each group potential horizontal position
32653 for ( var i = 0; i < groupCount; i++ ) {
32654 // make an array of colY values for that one group
32655 var groupColYs = this.colYs.slice( i, i + colSpan );
32656 // and get the max value of the array
32657 colGroup[i] = Math.max.apply( Math, groupColYs );
32662 _manageStamp : function( stamp )
32664 var stampSize = stamp.getSize();
32665 var offset = stamp.getBox();
32666 // get the columns that this stamp affects
32667 var firstX = this.isOriginLeft ? offset.x : offset.right;
32668 var lastX = firstX + stampSize.width;
32669 var firstCol = Math.floor( firstX / this.columnWidth );
32670 firstCol = Math.max( 0, firstCol );
32672 var lastCol = Math.floor( lastX / this.columnWidth );
32673 // lastCol should not go over if multiple of columnWidth #425
32674 lastCol -= lastX % this.columnWidth ? 0 : 1;
32675 lastCol = Math.min( this.cols - 1, lastCol );
32677 // set colYs to bottom of the stamp
32678 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32681 for ( var i = firstCol; i <= lastCol; i++ ) {
32682 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32687 _getContainerSize : function()
32689 this.maxY = Math.max.apply( Math, this.colYs );
32694 if ( this.isFitWidth ) {
32695 size.width = this._getContainerFitWidth();
32701 _getContainerFitWidth : function()
32703 var unusedCols = 0;
32704 // count unused columns
32707 if ( this.colYs[i] !== 0 ) {
32712 // fit container to columns that have been used
32713 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32716 needsResizeLayout : function()
32718 var previousWidth = this.containerWidth;
32719 this.getContainerWidth();
32720 return previousWidth !== this.containerWidth;
32735 * @class Roo.bootstrap.MasonryBrick
32736 * @extends Roo.bootstrap.Component
32737 * Bootstrap MasonryBrick class
32740 * Create a new MasonryBrick
32741 * @param {Object} config The config object
32744 Roo.bootstrap.MasonryBrick = function(config){
32746 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32748 Roo.bootstrap.MasonryBrick.register(this);
32754 * When a MasonryBrick is clcik
32755 * @param {Roo.bootstrap.MasonryBrick} this
32756 * @param {Roo.EventObject} e
32762 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32765 * @cfg {String} title
32769 * @cfg {String} html
32773 * @cfg {String} bgimage
32777 * @cfg {String} videourl
32781 * @cfg {String} cls
32785 * @cfg {String} href
32789 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32794 * @cfg {String} placetitle (center|bottom)
32799 * @cfg {Boolean} isFitContainer defalut true
32801 isFitContainer : true,
32804 * @cfg {Boolean} preventDefault defalut false
32806 preventDefault : false,
32809 * @cfg {Boolean} inverse defalut false
32811 maskInverse : false,
32813 getAutoCreate : function()
32815 if(!this.isFitContainer){
32816 return this.getSplitAutoCreate();
32819 var cls = 'masonry-brick masonry-brick-full';
32821 if(this.href.length){
32822 cls += ' masonry-brick-link';
32825 if(this.bgimage.length){
32826 cls += ' masonry-brick-image';
32829 if(this.maskInverse){
32830 cls += ' mask-inverse';
32833 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32834 cls += ' enable-mask';
32838 cls += ' masonry-' + this.size + '-brick';
32841 if(this.placetitle.length){
32843 switch (this.placetitle) {
32845 cls += ' masonry-center-title';
32848 cls += ' masonry-bottom-title';
32855 if(!this.html.length && !this.bgimage.length){
32856 cls += ' masonry-center-title';
32859 if(!this.html.length && this.bgimage.length){
32860 cls += ' masonry-bottom-title';
32865 cls += ' ' + this.cls;
32869 tag: (this.href.length) ? 'a' : 'div',
32874 cls: 'masonry-brick-mask'
32878 cls: 'masonry-brick-paragraph',
32884 if(this.href.length){
32885 cfg.href = this.href;
32888 var cn = cfg.cn[1].cn;
32890 if(this.title.length){
32893 cls: 'masonry-brick-title',
32898 if(this.html.length){
32901 cls: 'masonry-brick-text',
32906 if (!this.title.length && !this.html.length) {
32907 cfg.cn[1].cls += ' hide';
32910 if(this.bgimage.length){
32913 cls: 'masonry-brick-image-view',
32918 if(this.videourl.length){
32919 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32920 // youtube support only?
32923 cls: 'masonry-brick-image-view',
32926 allowfullscreen : true
32934 getSplitAutoCreate : function()
32936 var cls = 'masonry-brick masonry-brick-split';
32938 if(this.href.length){
32939 cls += ' masonry-brick-link';
32942 if(this.bgimage.length){
32943 cls += ' masonry-brick-image';
32947 cls += ' masonry-' + this.size + '-brick';
32950 switch (this.placetitle) {
32952 cls += ' masonry-center-title';
32955 cls += ' masonry-bottom-title';
32958 if(!this.bgimage.length){
32959 cls += ' masonry-center-title';
32962 if(this.bgimage.length){
32963 cls += ' masonry-bottom-title';
32969 cls += ' ' + this.cls;
32973 tag: (this.href.length) ? 'a' : 'div',
32978 cls: 'masonry-brick-split-head',
32982 cls: 'masonry-brick-paragraph',
32989 cls: 'masonry-brick-split-body',
32995 if(this.href.length){
32996 cfg.href = this.href;
32999 if(this.title.length){
33000 cfg.cn[0].cn[0].cn.push({
33002 cls: 'masonry-brick-title',
33007 if(this.html.length){
33008 cfg.cn[1].cn.push({
33010 cls: 'masonry-brick-text',
33015 if(this.bgimage.length){
33016 cfg.cn[0].cn.push({
33018 cls: 'masonry-brick-image-view',
33023 if(this.videourl.length){
33024 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33025 // youtube support only?
33026 cfg.cn[0].cn.cn.push({
33028 cls: 'masonry-brick-image-view',
33031 allowfullscreen : true
33038 initEvents: function()
33040 switch (this.size) {
33073 this.el.on('touchstart', this.onTouchStart, this);
33074 this.el.on('touchmove', this.onTouchMove, this);
33075 this.el.on('touchend', this.onTouchEnd, this);
33076 this.el.on('contextmenu', this.onContextMenu, this);
33078 this.el.on('mouseenter' ,this.enter, this);
33079 this.el.on('mouseleave', this.leave, this);
33080 this.el.on('click', this.onClick, this);
33083 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33084 this.parent().bricks.push(this);
33089 onClick: function(e, el)
33091 var time = this.endTimer - this.startTimer;
33092 // Roo.log(e.preventDefault());
33095 e.preventDefault();
33100 if(!this.preventDefault){
33104 e.preventDefault();
33106 if (this.activeClass != '') {
33107 this.selectBrick();
33110 this.fireEvent('click', this, e);
33113 enter: function(e, el)
33115 e.preventDefault();
33117 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33121 if(this.bgimage.length && this.html.length){
33122 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33126 leave: function(e, el)
33128 e.preventDefault();
33130 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33134 if(this.bgimage.length && this.html.length){
33135 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33139 onTouchStart: function(e, el)
33141 // e.preventDefault();
33143 this.touchmoved = false;
33145 if(!this.isFitContainer){
33149 if(!this.bgimage.length || !this.html.length){
33153 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33155 this.timer = new Date().getTime();
33159 onTouchMove: function(e, el)
33161 this.touchmoved = true;
33164 onContextMenu : function(e,el)
33166 e.preventDefault();
33167 e.stopPropagation();
33171 onTouchEnd: function(e, el)
33173 // e.preventDefault();
33175 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33182 if(!this.bgimage.length || !this.html.length){
33184 if(this.href.length){
33185 window.location.href = this.href;
33191 if(!this.isFitContainer){
33195 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33197 window.location.href = this.href;
33200 //selection on single brick only
33201 selectBrick : function() {
33203 if (!this.parentId) {
33207 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33208 var index = m.selectedBrick.indexOf(this.id);
33211 m.selectedBrick.splice(index,1);
33212 this.el.removeClass(this.activeClass);
33216 for(var i = 0; i < m.selectedBrick.length; i++) {
33217 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33218 b.el.removeClass(b.activeClass);
33221 m.selectedBrick = [];
33223 m.selectedBrick.push(this.id);
33224 this.el.addClass(this.activeClass);
33228 isSelected : function(){
33229 return this.el.hasClass(this.activeClass);
33234 Roo.apply(Roo.bootstrap.MasonryBrick, {
33237 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33239 * register a Masonry Brick
33240 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33243 register : function(brick)
33245 //this.groups[brick.id] = brick;
33246 this.groups.add(brick.id, brick);
33249 * fetch a masonry brick based on the masonry brick ID
33250 * @param {string} the masonry brick to add
33251 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33254 get: function(brick_id)
33256 // if (typeof(this.groups[brick_id]) == 'undefined') {
33259 // return this.groups[brick_id] ;
33261 if(this.groups.key(brick_id)) {
33262 return this.groups.key(brick_id);
33280 * @class Roo.bootstrap.Brick
33281 * @extends Roo.bootstrap.Component
33282 * Bootstrap Brick class
33285 * Create a new Brick
33286 * @param {Object} config The config object
33289 Roo.bootstrap.Brick = function(config){
33290 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33296 * When a Brick is click
33297 * @param {Roo.bootstrap.Brick} this
33298 * @param {Roo.EventObject} e
33304 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33307 * @cfg {String} title
33311 * @cfg {String} html
33315 * @cfg {String} bgimage
33319 * @cfg {String} cls
33323 * @cfg {String} href
33327 * @cfg {String} video
33331 * @cfg {Boolean} square
33335 getAutoCreate : function()
33337 var cls = 'roo-brick';
33339 if(this.href.length){
33340 cls += ' roo-brick-link';
33343 if(this.bgimage.length){
33344 cls += ' roo-brick-image';
33347 if(!this.html.length && !this.bgimage.length){
33348 cls += ' roo-brick-center-title';
33351 if(!this.html.length && this.bgimage.length){
33352 cls += ' roo-brick-bottom-title';
33356 cls += ' ' + this.cls;
33360 tag: (this.href.length) ? 'a' : 'div',
33365 cls: 'roo-brick-paragraph',
33371 if(this.href.length){
33372 cfg.href = this.href;
33375 var cn = cfg.cn[0].cn;
33377 if(this.title.length){
33380 cls: 'roo-brick-title',
33385 if(this.html.length){
33388 cls: 'roo-brick-text',
33395 if(this.bgimage.length){
33398 cls: 'roo-brick-image-view',
33406 initEvents: function()
33408 if(this.title.length || this.html.length){
33409 this.el.on('mouseenter' ,this.enter, this);
33410 this.el.on('mouseleave', this.leave, this);
33413 Roo.EventManager.onWindowResize(this.resize, this);
33415 if(this.bgimage.length){
33416 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33417 this.imageEl.on('load', this.onImageLoad, this);
33424 onImageLoad : function()
33429 resize : function()
33431 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33433 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33435 if(this.bgimage.length){
33436 var image = this.el.select('.roo-brick-image-view', true).first();
33438 image.setWidth(paragraph.getWidth());
33441 image.setHeight(paragraph.getWidth());
33444 this.el.setHeight(image.getHeight());
33445 paragraph.setHeight(image.getHeight());
33451 enter: function(e, el)
33453 e.preventDefault();
33455 if(this.bgimage.length){
33456 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33457 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33461 leave: function(e, el)
33463 e.preventDefault();
33465 if(this.bgimage.length){
33466 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33467 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33482 * @class Roo.bootstrap.NumberField
33483 * @extends Roo.bootstrap.Input
33484 * Bootstrap NumberField class
33490 * Create a new NumberField
33491 * @param {Object} config The config object
33494 Roo.bootstrap.NumberField = function(config){
33495 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33498 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33501 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33503 allowDecimals : true,
33505 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33507 decimalSeparator : ".",
33509 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33511 decimalPrecision : 2,
33513 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33515 allowNegative : true,
33518 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33522 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33524 minValue : Number.NEGATIVE_INFINITY,
33526 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33528 maxValue : Number.MAX_VALUE,
33530 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33532 minText : "The minimum value for this field is {0}",
33534 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33536 maxText : "The maximum value for this field is {0}",
33538 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33539 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33541 nanText : "{0} is not a valid number",
33543 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33545 thousandsDelimiter : false,
33547 * @cfg {String} valueAlign alignment of value
33549 valueAlign : "left",
33551 getAutoCreate : function()
33553 var hiddenInput = {
33557 cls: 'hidden-number-input'
33561 hiddenInput.name = this.name;
33566 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33568 this.name = hiddenInput.name;
33570 if(cfg.cn.length > 0) {
33571 cfg.cn.push(hiddenInput);
33578 initEvents : function()
33580 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33582 var allowed = "0123456789";
33584 if(this.allowDecimals){
33585 allowed += this.decimalSeparator;
33588 if(this.allowNegative){
33592 if(this.thousandsDelimiter) {
33596 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33598 var keyPress = function(e){
33600 var k = e.getKey();
33602 var c = e.getCharCode();
33605 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33606 allowed.indexOf(String.fromCharCode(c)) === -1
33612 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33616 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33621 this.el.on("keypress", keyPress, this);
33624 validateValue : function(value)
33627 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33631 var num = this.parseValue(value);
33634 this.markInvalid(String.format(this.nanText, value));
33638 if(num < this.minValue){
33639 this.markInvalid(String.format(this.minText, this.minValue));
33643 if(num > this.maxValue){
33644 this.markInvalid(String.format(this.maxText, this.maxValue));
33651 getValue : function()
33653 var v = this.hiddenEl().getValue();
33655 return this.fixPrecision(this.parseValue(v));
33658 parseValue : function(value)
33660 if(this.thousandsDelimiter) {
33662 r = new RegExp(",", "g");
33663 value = value.replace(r, "");
33666 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33667 return isNaN(value) ? '' : value;
33670 fixPrecision : function(value)
33672 if(this.thousandsDelimiter) {
33674 r = new RegExp(",", "g");
33675 value = value.replace(r, "");
33678 var nan = isNaN(value);
33680 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33681 return nan ? '' : value;
33683 return parseFloat(value).toFixed(this.decimalPrecision);
33686 setValue : function(v)
33688 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33694 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33696 this.inputEl().dom.value = (v == '') ? '' :
33697 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33699 if(!this.allowZero && v === '0') {
33700 this.hiddenEl().dom.value = '';
33701 this.inputEl().dom.value = '';
33708 decimalPrecisionFcn : function(v)
33710 return Math.floor(v);
33713 beforeBlur : function()
33715 var v = this.parseValue(this.getRawValue());
33717 if(v || v === 0 || v === ''){
33722 hiddenEl : function()
33724 return this.el.select('input.hidden-number-input',true).first();
33736 * @class Roo.bootstrap.DocumentSlider
33737 * @extends Roo.bootstrap.Component
33738 * Bootstrap DocumentSlider class
33741 * Create a new DocumentViewer
33742 * @param {Object} config The config object
33745 Roo.bootstrap.DocumentSlider = function(config){
33746 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33753 * Fire after initEvent
33754 * @param {Roo.bootstrap.DocumentSlider} this
33759 * Fire after update
33760 * @param {Roo.bootstrap.DocumentSlider} this
33766 * @param {Roo.bootstrap.DocumentSlider} this
33772 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33778 getAutoCreate : function()
33782 cls : 'roo-document-slider',
33786 cls : 'roo-document-slider-header',
33790 cls : 'roo-document-slider-header-title'
33796 cls : 'roo-document-slider-body',
33800 cls : 'roo-document-slider-prev',
33804 cls : 'fa fa-chevron-left'
33810 cls : 'roo-document-slider-thumb',
33814 cls : 'roo-document-slider-image'
33820 cls : 'roo-document-slider-next',
33824 cls : 'fa fa-chevron-right'
33836 initEvents : function()
33838 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33839 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33841 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33842 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33844 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33845 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33847 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33848 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33850 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33851 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33853 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33854 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33856 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33857 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33859 this.thumbEl.on('click', this.onClick, this);
33861 this.prevIndicator.on('click', this.prev, this);
33863 this.nextIndicator.on('click', this.next, this);
33867 initial : function()
33869 if(this.files.length){
33870 this.indicator = 1;
33874 this.fireEvent('initial', this);
33877 update : function()
33879 this.imageEl.attr('src', this.files[this.indicator - 1]);
33881 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33883 this.prevIndicator.show();
33885 if(this.indicator == 1){
33886 this.prevIndicator.hide();
33889 this.nextIndicator.show();
33891 if(this.indicator == this.files.length){
33892 this.nextIndicator.hide();
33895 this.thumbEl.scrollTo('top');
33897 this.fireEvent('update', this);
33900 onClick : function(e)
33902 e.preventDefault();
33904 this.fireEvent('click', this);
33909 e.preventDefault();
33911 this.indicator = Math.max(1, this.indicator - 1);
33918 e.preventDefault();
33920 this.indicator = Math.min(this.files.length, this.indicator + 1);
33934 * @class Roo.bootstrap.RadioSet
33935 * @extends Roo.bootstrap.Input
33936 * Bootstrap RadioSet class
33937 * @cfg {String} indicatorpos (left|right) default left
33938 * @cfg {Boolean} inline (true|false) inline the element (default true)
33939 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33941 * Create a new RadioSet
33942 * @param {Object} config The config object
33945 Roo.bootstrap.RadioSet = function(config){
33947 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33951 Roo.bootstrap.RadioSet.register(this);
33956 * Fires when the element is checked or unchecked.
33957 * @param {Roo.bootstrap.RadioSet} this This radio
33958 * @param {Roo.bootstrap.Radio} item The checked item
33963 * Fires when the element is click.
33964 * @param {Roo.bootstrap.RadioSet} this This radio set
33965 * @param {Roo.bootstrap.Radio} item The checked item
33966 * @param {Roo.EventObject} e The event object
33973 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33981 indicatorpos : 'left',
33983 getAutoCreate : function()
33987 cls : 'roo-radio-set-label',
33991 html : this.fieldLabel
33996 if(this.indicatorpos == 'left'){
33999 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34000 tooltip : 'This field is required'
34005 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34006 tooltip : 'This field is required'
34012 cls : 'roo-radio-set-items'
34015 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34017 if (align === 'left' && this.fieldLabel.length) {
34020 cls : "roo-radio-set-right",
34026 if(this.labelWidth > 12){
34027 label.style = "width: " + this.labelWidth + 'px';
34030 if(this.labelWidth < 13 && this.labelmd == 0){
34031 this.labelmd = this.labelWidth;
34034 if(this.labellg > 0){
34035 label.cls += ' col-lg-' + this.labellg;
34036 items.cls += ' col-lg-' + (12 - this.labellg);
34039 if(this.labelmd > 0){
34040 label.cls += ' col-md-' + this.labelmd;
34041 items.cls += ' col-md-' + (12 - this.labelmd);
34044 if(this.labelsm > 0){
34045 label.cls += ' col-sm-' + this.labelsm;
34046 items.cls += ' col-sm-' + (12 - this.labelsm);
34049 if(this.labelxs > 0){
34050 label.cls += ' col-xs-' + this.labelxs;
34051 items.cls += ' col-xs-' + (12 - this.labelxs);
34057 cls : 'roo-radio-set',
34061 cls : 'roo-radio-set-input',
34064 value : this.value ? this.value : ''
34071 if(this.weight.length){
34072 cfg.cls += ' roo-radio-' + this.weight;
34076 cfg.cls += ' roo-radio-set-inline';
34080 ['xs','sm','md','lg'].map(function(size){
34081 if (settings[size]) {
34082 cfg.cls += ' col-' + size + '-' + settings[size];
34090 initEvents : function()
34092 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34093 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34095 if(!this.fieldLabel.length){
34096 this.labelEl.hide();
34099 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34100 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34102 this.indicator = this.indicatorEl();
34104 if(this.indicator){
34105 this.indicator.addClass('invisible');
34108 this.originalValue = this.getValue();
34112 inputEl: function ()
34114 return this.el.select('.roo-radio-set-input', true).first();
34117 getChildContainer : function()
34119 return this.itemsEl;
34122 register : function(item)
34124 this.radioes.push(item);
34128 validate : function()
34130 if(this.getVisibilityEl().hasClass('hidden')){
34136 Roo.each(this.radioes, function(i){
34145 if(this.allowBlank) {
34149 if(this.disabled || valid){
34154 this.markInvalid();
34159 markValid : function()
34161 if(this.labelEl.isVisible(true)){
34162 this.indicatorEl().removeClass('visible');
34163 this.indicatorEl().addClass('invisible');
34166 this.el.removeClass([this.invalidClass, this.validClass]);
34167 this.el.addClass(this.validClass);
34169 this.fireEvent('valid', this);
34172 markInvalid : function(msg)
34174 if(this.allowBlank || this.disabled){
34178 if(this.labelEl.isVisible(true)){
34179 this.indicatorEl().removeClass('invisible');
34180 this.indicatorEl().addClass('visible');
34183 this.el.removeClass([this.invalidClass, this.validClass]);
34184 this.el.addClass(this.invalidClass);
34186 this.fireEvent('invalid', this, msg);
34190 setValue : function(v, suppressEvent)
34192 if(this.value === v){
34199 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34202 Roo.each(this.radioes, function(i){
34204 i.el.removeClass('checked');
34207 Roo.each(this.radioes, function(i){
34209 if(i.value === v || i.value.toString() === v.toString()){
34211 i.el.addClass('checked');
34213 if(suppressEvent !== true){
34214 this.fireEvent('check', this, i);
34225 clearInvalid : function(){
34227 if(!this.el || this.preventMark){
34231 this.el.removeClass([this.invalidClass]);
34233 this.fireEvent('valid', this);
34238 Roo.apply(Roo.bootstrap.RadioSet, {
34242 register : function(set)
34244 this.groups[set.name] = set;
34247 get: function(name)
34249 if (typeof(this.groups[name]) == 'undefined') {
34253 return this.groups[name] ;
34259 * Ext JS Library 1.1.1
34260 * Copyright(c) 2006-2007, Ext JS, LLC.
34262 * Originally Released Under LGPL - original licence link has changed is not relivant.
34265 * <script type="text/javascript">
34270 * @class Roo.bootstrap.SplitBar
34271 * @extends Roo.util.Observable
34272 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34276 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34277 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34278 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34279 split.minSize = 100;
34280 split.maxSize = 600;
34281 split.animate = true;
34282 split.on('moved', splitterMoved);
34285 * Create a new SplitBar
34286 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34287 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34288 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34289 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34290 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34291 position of the SplitBar).
34293 Roo.bootstrap.SplitBar = function(cfg){
34298 // dragElement : elm
34299 // resizingElement: el,
34301 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34302 // placement : Roo.bootstrap.SplitBar.LEFT ,
34303 // existingProxy ???
34306 this.el = Roo.get(cfg.dragElement, true);
34307 this.el.dom.unselectable = "on";
34309 this.resizingEl = Roo.get(cfg.resizingElement, true);
34313 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34314 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34317 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34320 * The minimum size of the resizing element. (Defaults to 0)
34326 * The maximum size of the resizing element. (Defaults to 2000)
34329 this.maxSize = 2000;
34332 * Whether to animate the transition to the new size
34335 this.animate = false;
34338 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34341 this.useShim = false;
34346 if(!cfg.existingProxy){
34348 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34350 this.proxy = Roo.get(cfg.existingProxy).dom;
34353 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34356 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34359 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34362 this.dragSpecs = {};
34365 * @private The adapter to use to positon and resize elements
34367 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34368 this.adapter.init(this);
34370 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34372 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34373 this.el.addClass("roo-splitbar-h");
34376 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34377 this.el.addClass("roo-splitbar-v");
34383 * Fires when the splitter is moved (alias for {@link #event-moved})
34384 * @param {Roo.bootstrap.SplitBar} this
34385 * @param {Number} newSize the new width or height
34390 * Fires when the splitter is moved
34391 * @param {Roo.bootstrap.SplitBar} this
34392 * @param {Number} newSize the new width or height
34396 * @event beforeresize
34397 * Fires before the splitter is dragged
34398 * @param {Roo.bootstrap.SplitBar} this
34400 "beforeresize" : true,
34402 "beforeapply" : true
34405 Roo.util.Observable.call(this);
34408 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34409 onStartProxyDrag : function(x, y){
34410 this.fireEvent("beforeresize", this);
34412 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34414 o.enableDisplayMode("block");
34415 // all splitbars share the same overlay
34416 Roo.bootstrap.SplitBar.prototype.overlay = o;
34418 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34419 this.overlay.show();
34420 Roo.get(this.proxy).setDisplayed("block");
34421 var size = this.adapter.getElementSize(this);
34422 this.activeMinSize = this.getMinimumSize();;
34423 this.activeMaxSize = this.getMaximumSize();;
34424 var c1 = size - this.activeMinSize;
34425 var c2 = Math.max(this.activeMaxSize - size, 0);
34426 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34427 this.dd.resetConstraints();
34428 this.dd.setXConstraint(
34429 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34430 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34432 this.dd.setYConstraint(0, 0);
34434 this.dd.resetConstraints();
34435 this.dd.setXConstraint(0, 0);
34436 this.dd.setYConstraint(
34437 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34438 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34441 this.dragSpecs.startSize = size;
34442 this.dragSpecs.startPoint = [x, y];
34443 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34447 * @private Called after the drag operation by the DDProxy
34449 onEndProxyDrag : function(e){
34450 Roo.get(this.proxy).setDisplayed(false);
34451 var endPoint = Roo.lib.Event.getXY(e);
34453 this.overlay.hide();
34456 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34457 newSize = this.dragSpecs.startSize +
34458 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34459 endPoint[0] - this.dragSpecs.startPoint[0] :
34460 this.dragSpecs.startPoint[0] - endPoint[0]
34463 newSize = this.dragSpecs.startSize +
34464 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34465 endPoint[1] - this.dragSpecs.startPoint[1] :
34466 this.dragSpecs.startPoint[1] - endPoint[1]
34469 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34470 if(newSize != this.dragSpecs.startSize){
34471 if(this.fireEvent('beforeapply', this, newSize) !== false){
34472 this.adapter.setElementSize(this, newSize);
34473 this.fireEvent("moved", this, newSize);
34474 this.fireEvent("resize", this, newSize);
34480 * Get the adapter this SplitBar uses
34481 * @return The adapter object
34483 getAdapter : function(){
34484 return this.adapter;
34488 * Set the adapter this SplitBar uses
34489 * @param {Object} adapter A SplitBar adapter object
34491 setAdapter : function(adapter){
34492 this.adapter = adapter;
34493 this.adapter.init(this);
34497 * Gets the minimum size for the resizing element
34498 * @return {Number} The minimum size
34500 getMinimumSize : function(){
34501 return this.minSize;
34505 * Sets the minimum size for the resizing element
34506 * @param {Number} minSize The minimum size
34508 setMinimumSize : function(minSize){
34509 this.minSize = minSize;
34513 * Gets the maximum size for the resizing element
34514 * @return {Number} The maximum size
34516 getMaximumSize : function(){
34517 return this.maxSize;
34521 * Sets the maximum size for the resizing element
34522 * @param {Number} maxSize The maximum size
34524 setMaximumSize : function(maxSize){
34525 this.maxSize = maxSize;
34529 * Sets the initialize size for the resizing element
34530 * @param {Number} size The initial size
34532 setCurrentSize : function(size){
34533 var oldAnimate = this.animate;
34534 this.animate = false;
34535 this.adapter.setElementSize(this, size);
34536 this.animate = oldAnimate;
34540 * Destroy this splitbar.
34541 * @param {Boolean} removeEl True to remove the element
34543 destroy : function(removeEl){
34545 this.shim.remove();
34548 this.proxy.parentNode.removeChild(this.proxy);
34556 * @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.
34558 Roo.bootstrap.SplitBar.createProxy = function(dir){
34559 var proxy = new Roo.Element(document.createElement("div"));
34560 proxy.unselectable();
34561 var cls = 'roo-splitbar-proxy';
34562 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34563 document.body.appendChild(proxy.dom);
34568 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34569 * Default Adapter. It assumes the splitter and resizing element are not positioned
34570 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34572 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34575 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34576 // do nothing for now
34577 init : function(s){
34581 * Called before drag operations to get the current size of the resizing element.
34582 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34584 getElementSize : function(s){
34585 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34586 return s.resizingEl.getWidth();
34588 return s.resizingEl.getHeight();
34593 * Called after drag operations to set the size of the resizing element.
34594 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34595 * @param {Number} newSize The new size to set
34596 * @param {Function} onComplete A function to be invoked when resizing is complete
34598 setElementSize : function(s, newSize, onComplete){
34599 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34601 s.resizingEl.setWidth(newSize);
34603 onComplete(s, newSize);
34606 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34611 s.resizingEl.setHeight(newSize);
34613 onComplete(s, newSize);
34616 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34623 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34624 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34625 * Adapter that moves the splitter element to align with the resized sizing element.
34626 * Used with an absolute positioned SplitBar.
34627 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34628 * document.body, make sure you assign an id to the body element.
34630 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34631 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34632 this.container = Roo.get(container);
34635 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34636 init : function(s){
34637 this.basic.init(s);
34640 getElementSize : function(s){
34641 return this.basic.getElementSize(s);
34644 setElementSize : function(s, newSize, onComplete){
34645 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34648 moveSplitter : function(s){
34649 var yes = Roo.bootstrap.SplitBar;
34650 switch(s.placement){
34652 s.el.setX(s.resizingEl.getRight());
34655 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34658 s.el.setY(s.resizingEl.getBottom());
34661 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34668 * Orientation constant - Create a vertical SplitBar
34672 Roo.bootstrap.SplitBar.VERTICAL = 1;
34675 * Orientation constant - Create a horizontal SplitBar
34679 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34682 * Placement constant - The resizing element is to the left of the splitter element
34686 Roo.bootstrap.SplitBar.LEFT = 1;
34689 * Placement constant - The resizing element is to the right of the splitter element
34693 Roo.bootstrap.SplitBar.RIGHT = 2;
34696 * Placement constant - The resizing element is positioned above the splitter element
34700 Roo.bootstrap.SplitBar.TOP = 3;
34703 * Placement constant - The resizing element is positioned under splitter element
34707 Roo.bootstrap.SplitBar.BOTTOM = 4;
34708 Roo.namespace("Roo.bootstrap.layout");/*
34710 * Ext JS Library 1.1.1
34711 * Copyright(c) 2006-2007, Ext JS, LLC.
34713 * Originally Released Under LGPL - original licence link has changed is not relivant.
34716 * <script type="text/javascript">
34720 * @class Roo.bootstrap.layout.Manager
34721 * @extends Roo.bootstrap.Component
34722 * Base class for layout managers.
34724 Roo.bootstrap.layout.Manager = function(config)
34726 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34732 /** false to disable window resize monitoring @type Boolean */
34733 this.monitorWindowResize = true;
34738 * Fires when a layout is performed.
34739 * @param {Roo.LayoutManager} this
34743 * @event regionresized
34744 * Fires when the user resizes a region.
34745 * @param {Roo.LayoutRegion} region The resized region
34746 * @param {Number} newSize The new size (width for east/west, height for north/south)
34748 "regionresized" : true,
34750 * @event regioncollapsed
34751 * Fires when a region is collapsed.
34752 * @param {Roo.LayoutRegion} region The collapsed region
34754 "regioncollapsed" : true,
34756 * @event regionexpanded
34757 * Fires when a region is expanded.
34758 * @param {Roo.LayoutRegion} region The expanded region
34760 "regionexpanded" : true
34762 this.updating = false;
34765 this.el = Roo.get(config.el);
34771 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34776 monitorWindowResize : true,
34782 onRender : function(ct, position)
34785 this.el = Roo.get(ct);
34788 //this.fireEvent('render',this);
34792 initEvents: function()
34796 // ie scrollbar fix
34797 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34798 document.body.scroll = "no";
34799 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34800 this.el.position('relative');
34802 this.id = this.el.id;
34803 this.el.addClass("roo-layout-container");
34804 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34805 if(this.el.dom != document.body ) {
34806 this.el.on('resize', this.layout,this);
34807 this.el.on('show', this.layout,this);
34813 * Returns true if this layout is currently being updated
34814 * @return {Boolean}
34816 isUpdating : function(){
34817 return this.updating;
34821 * Suspend the LayoutManager from doing auto-layouts while
34822 * making multiple add or remove calls
34824 beginUpdate : function(){
34825 this.updating = true;
34829 * Restore auto-layouts and optionally disable the manager from performing a layout
34830 * @param {Boolean} noLayout true to disable a layout update
34832 endUpdate : function(noLayout){
34833 this.updating = false;
34839 layout: function(){
34843 onRegionResized : function(region, newSize){
34844 this.fireEvent("regionresized", region, newSize);
34848 onRegionCollapsed : function(region){
34849 this.fireEvent("regioncollapsed", region);
34852 onRegionExpanded : function(region){
34853 this.fireEvent("regionexpanded", region);
34857 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34858 * performs box-model adjustments.
34859 * @return {Object} The size as an object {width: (the width), height: (the height)}
34861 getViewSize : function()
34864 if(this.el.dom != document.body){
34865 size = this.el.getSize();
34867 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34869 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34870 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34875 * Returns the Element this layout is bound to.
34876 * @return {Roo.Element}
34878 getEl : function(){
34883 * Returns the specified region.
34884 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34885 * @return {Roo.LayoutRegion}
34887 getRegion : function(target){
34888 return this.regions[target.toLowerCase()];
34891 onWindowResize : function(){
34892 if(this.monitorWindowResize){
34899 * Ext JS Library 1.1.1
34900 * Copyright(c) 2006-2007, Ext JS, LLC.
34902 * Originally Released Under LGPL - original licence link has changed is not relivant.
34905 * <script type="text/javascript">
34908 * @class Roo.bootstrap.layout.Border
34909 * @extends Roo.bootstrap.layout.Manager
34910 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34911 * please see: examples/bootstrap/nested.html<br><br>
34913 <b>The container the layout is rendered into can be either the body element or any other element.
34914 If it is not the body element, the container needs to either be an absolute positioned element,
34915 or you will need to add "position:relative" to the css of the container. You will also need to specify
34916 the container size if it is not the body element.</b>
34919 * Create a new Border
34920 * @param {Object} config Configuration options
34922 Roo.bootstrap.layout.Border = function(config){
34923 config = config || {};
34924 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34928 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34929 if(config[region]){
34930 config[region].region = region;
34931 this.addRegion(config[region]);
34937 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34939 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34941 * Creates and adds a new region if it doesn't already exist.
34942 * @param {String} target The target region key (north, south, east, west or center).
34943 * @param {Object} config The regions config object
34944 * @return {BorderLayoutRegion} The new region
34946 addRegion : function(config)
34948 if(!this.regions[config.region]){
34949 var r = this.factory(config);
34950 this.bindRegion(r);
34952 return this.regions[config.region];
34956 bindRegion : function(r){
34957 this.regions[r.config.region] = r;
34959 r.on("visibilitychange", this.layout, this);
34960 r.on("paneladded", this.layout, this);
34961 r.on("panelremoved", this.layout, this);
34962 r.on("invalidated", this.layout, this);
34963 r.on("resized", this.onRegionResized, this);
34964 r.on("collapsed", this.onRegionCollapsed, this);
34965 r.on("expanded", this.onRegionExpanded, this);
34969 * Performs a layout update.
34971 layout : function()
34973 if(this.updating) {
34977 // render all the rebions if they have not been done alreayd?
34978 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34979 if(this.regions[region] && !this.regions[region].bodyEl){
34980 this.regions[region].onRender(this.el)
34984 var size = this.getViewSize();
34985 var w = size.width;
34986 var h = size.height;
34991 //var x = 0, y = 0;
34993 var rs = this.regions;
34994 var north = rs["north"];
34995 var south = rs["south"];
34996 var west = rs["west"];
34997 var east = rs["east"];
34998 var center = rs["center"];
34999 //if(this.hideOnLayout){ // not supported anymore
35000 //c.el.setStyle("display", "none");
35002 if(north && north.isVisible()){
35003 var b = north.getBox();
35004 var m = north.getMargins();
35005 b.width = w - (m.left+m.right);
35008 centerY = b.height + b.y + m.bottom;
35009 centerH -= centerY;
35010 north.updateBox(this.safeBox(b));
35012 if(south && south.isVisible()){
35013 var b = south.getBox();
35014 var m = south.getMargins();
35015 b.width = w - (m.left+m.right);
35017 var totalHeight = (b.height + m.top + m.bottom);
35018 b.y = h - totalHeight + m.top;
35019 centerH -= totalHeight;
35020 south.updateBox(this.safeBox(b));
35022 if(west && west.isVisible()){
35023 var b = west.getBox();
35024 var m = west.getMargins();
35025 b.height = centerH - (m.top+m.bottom);
35027 b.y = centerY + m.top;
35028 var totalWidth = (b.width + m.left + m.right);
35029 centerX += totalWidth;
35030 centerW -= totalWidth;
35031 west.updateBox(this.safeBox(b));
35033 if(east && east.isVisible()){
35034 var b = east.getBox();
35035 var m = east.getMargins();
35036 b.height = centerH - (m.top+m.bottom);
35037 var totalWidth = (b.width + m.left + m.right);
35038 b.x = w - totalWidth + m.left;
35039 b.y = centerY + m.top;
35040 centerW -= totalWidth;
35041 east.updateBox(this.safeBox(b));
35044 var m = center.getMargins();
35046 x: centerX + m.left,
35047 y: centerY + m.top,
35048 width: centerW - (m.left+m.right),
35049 height: centerH - (m.top+m.bottom)
35051 //if(this.hideOnLayout){
35052 //center.el.setStyle("display", "block");
35054 center.updateBox(this.safeBox(centerBox));
35057 this.fireEvent("layout", this);
35061 safeBox : function(box){
35062 box.width = Math.max(0, box.width);
35063 box.height = Math.max(0, box.height);
35068 * Adds a ContentPanel (or subclass) to this layout.
35069 * @param {String} target The target region key (north, south, east, west or center).
35070 * @param {Roo.ContentPanel} panel The panel to add
35071 * @return {Roo.ContentPanel} The added panel
35073 add : function(target, panel){
35075 target = target.toLowerCase();
35076 return this.regions[target].add(panel);
35080 * Remove a ContentPanel (or subclass) to this layout.
35081 * @param {String} target The target region key (north, south, east, west or center).
35082 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35083 * @return {Roo.ContentPanel} The removed panel
35085 remove : function(target, panel){
35086 target = target.toLowerCase();
35087 return this.regions[target].remove(panel);
35091 * Searches all regions for a panel with the specified id
35092 * @param {String} panelId
35093 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35095 findPanel : function(panelId){
35096 var rs = this.regions;
35097 for(var target in rs){
35098 if(typeof rs[target] != "function"){
35099 var p = rs[target].getPanel(panelId);
35109 * Searches all regions for a panel with the specified id and activates (shows) it.
35110 * @param {String/ContentPanel} panelId The panels id or the panel itself
35111 * @return {Roo.ContentPanel} The shown panel or null
35113 showPanel : function(panelId) {
35114 var rs = this.regions;
35115 for(var target in rs){
35116 var r = rs[target];
35117 if(typeof r != "function"){
35118 if(r.hasPanel(panelId)){
35119 return r.showPanel(panelId);
35127 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35128 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35131 restoreState : function(provider){
35133 provider = Roo.state.Manager;
35135 var sm = new Roo.LayoutStateManager();
35136 sm.init(this, provider);
35142 * Adds a xtype elements to the layout.
35146 xtype : 'ContentPanel',
35153 xtype : 'NestedLayoutPanel',
35159 items : [ ... list of content panels or nested layout panels.. ]
35163 * @param {Object} cfg Xtype definition of item to add.
35165 addxtype : function(cfg)
35167 // basically accepts a pannel...
35168 // can accept a layout region..!?!?
35169 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35172 // theory? children can only be panels??
35174 //if (!cfg.xtype.match(/Panel$/)) {
35179 if (typeof(cfg.region) == 'undefined') {
35180 Roo.log("Failed to add Panel, region was not set");
35184 var region = cfg.region;
35190 xitems = cfg.items;
35197 case 'Content': // ContentPanel (el, cfg)
35198 case 'Scroll': // ContentPanel (el, cfg)
35200 cfg.autoCreate = true;
35201 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35203 // var el = this.el.createChild();
35204 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35207 this.add(region, ret);
35211 case 'TreePanel': // our new panel!
35212 cfg.el = this.el.createChild();
35213 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35214 this.add(region, ret);
35219 // create a new Layout (which is a Border Layout...
35221 var clayout = cfg.layout;
35222 clayout.el = this.el.createChild();
35223 clayout.items = clayout.items || [];
35227 // replace this exitems with the clayout ones..
35228 xitems = clayout.items;
35230 // force background off if it's in center...
35231 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35232 cfg.background = false;
35234 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35237 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35238 //console.log('adding nested layout panel ' + cfg.toSource());
35239 this.add(region, ret);
35240 nb = {}; /// find first...
35245 // needs grid and region
35247 //var el = this.getRegion(region).el.createChild();
35249 *var el = this.el.createChild();
35250 // create the grid first...
35251 cfg.grid.container = el;
35252 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35255 if (region == 'center' && this.active ) {
35256 cfg.background = false;
35259 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35261 this.add(region, ret);
35263 if (cfg.background) {
35264 // render grid on panel activation (if panel background)
35265 ret.on('activate', function(gp) {
35266 if (!gp.grid.rendered) {
35267 // gp.grid.render(el);
35271 // cfg.grid.render(el);
35277 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35278 // it was the old xcomponent building that caused this before.
35279 // espeically if border is the top element in the tree.
35289 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35291 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35292 this.add(region, ret);
35296 throw "Can not add '" + cfg.xtype + "' to Border";
35302 this.beginUpdate();
35306 Roo.each(xitems, function(i) {
35307 region = nb && i.region ? i.region : false;
35309 var add = ret.addxtype(i);
35312 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35313 if (!i.background) {
35314 abn[region] = nb[region] ;
35321 // make the last non-background panel active..
35322 //if (nb) { Roo.log(abn); }
35325 for(var r in abn) {
35326 region = this.getRegion(r);
35328 // tried using nb[r], but it does not work..
35330 region.showPanel(abn[r]);
35341 factory : function(cfg)
35344 var validRegions = Roo.bootstrap.layout.Border.regions;
35346 var target = cfg.region;
35349 var r = Roo.bootstrap.layout;
35353 return new r.North(cfg);
35355 return new r.South(cfg);
35357 return new r.East(cfg);
35359 return new r.West(cfg);
35361 return new r.Center(cfg);
35363 throw 'Layout region "'+target+'" not supported.';
35370 * Ext JS Library 1.1.1
35371 * Copyright(c) 2006-2007, Ext JS, LLC.
35373 * Originally Released Under LGPL - original licence link has changed is not relivant.
35376 * <script type="text/javascript">
35380 * @class Roo.bootstrap.layout.Basic
35381 * @extends Roo.util.Observable
35382 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35383 * and does not have a titlebar, tabs or any other features. All it does is size and position
35384 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35385 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35386 * @cfg {string} region the region that it inhabits..
35387 * @cfg {bool} skipConfig skip config?
35391 Roo.bootstrap.layout.Basic = function(config){
35393 this.mgr = config.mgr;
35395 this.position = config.region;
35397 var skipConfig = config.skipConfig;
35401 * @scope Roo.BasicLayoutRegion
35405 * @event beforeremove
35406 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35407 * @param {Roo.LayoutRegion} this
35408 * @param {Roo.ContentPanel} panel The panel
35409 * @param {Object} e The cancel event object
35411 "beforeremove" : true,
35413 * @event invalidated
35414 * Fires when the layout for this region is changed.
35415 * @param {Roo.LayoutRegion} this
35417 "invalidated" : true,
35419 * @event visibilitychange
35420 * Fires when this region is shown or hidden
35421 * @param {Roo.LayoutRegion} this
35422 * @param {Boolean} visibility true or false
35424 "visibilitychange" : true,
35426 * @event paneladded
35427 * Fires when a panel is added.
35428 * @param {Roo.LayoutRegion} this
35429 * @param {Roo.ContentPanel} panel The panel
35431 "paneladded" : true,
35433 * @event panelremoved
35434 * Fires when a panel is removed.
35435 * @param {Roo.LayoutRegion} this
35436 * @param {Roo.ContentPanel} panel The panel
35438 "panelremoved" : true,
35440 * @event beforecollapse
35441 * Fires when this region before collapse.
35442 * @param {Roo.LayoutRegion} this
35444 "beforecollapse" : true,
35447 * Fires when this region is collapsed.
35448 * @param {Roo.LayoutRegion} this
35450 "collapsed" : true,
35453 * Fires when this region is expanded.
35454 * @param {Roo.LayoutRegion} this
35459 * Fires when this region is slid into view.
35460 * @param {Roo.LayoutRegion} this
35462 "slideshow" : true,
35465 * Fires when this region slides out of view.
35466 * @param {Roo.LayoutRegion} this
35468 "slidehide" : true,
35470 * @event panelactivated
35471 * Fires when a panel is activated.
35472 * @param {Roo.LayoutRegion} this
35473 * @param {Roo.ContentPanel} panel The activated panel
35475 "panelactivated" : true,
35478 * Fires when the user resizes this region.
35479 * @param {Roo.LayoutRegion} this
35480 * @param {Number} newSize The new size (width for east/west, height for north/south)
35484 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35485 this.panels = new Roo.util.MixedCollection();
35486 this.panels.getKey = this.getPanelId.createDelegate(this);
35488 this.activePanel = null;
35489 // ensure listeners are added...
35491 if (config.listeners || config.events) {
35492 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35493 listeners : config.listeners || {},
35494 events : config.events || {}
35498 if(skipConfig !== true){
35499 this.applyConfig(config);
35503 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35505 getPanelId : function(p){
35509 applyConfig : function(config){
35510 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35511 this.config = config;
35516 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35517 * the width, for horizontal (north, south) the height.
35518 * @param {Number} newSize The new width or height
35520 resizeTo : function(newSize){
35521 var el = this.el ? this.el :
35522 (this.activePanel ? this.activePanel.getEl() : null);
35524 switch(this.position){
35527 el.setWidth(newSize);
35528 this.fireEvent("resized", this, newSize);
35532 el.setHeight(newSize);
35533 this.fireEvent("resized", this, newSize);
35539 getBox : function(){
35540 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35543 getMargins : function(){
35544 return this.margins;
35547 updateBox : function(box){
35549 var el = this.activePanel.getEl();
35550 el.dom.style.left = box.x + "px";
35551 el.dom.style.top = box.y + "px";
35552 this.activePanel.setSize(box.width, box.height);
35556 * Returns the container element for this region.
35557 * @return {Roo.Element}
35559 getEl : function(){
35560 return this.activePanel;
35564 * Returns true if this region is currently visible.
35565 * @return {Boolean}
35567 isVisible : function(){
35568 return this.activePanel ? true : false;
35571 setActivePanel : function(panel){
35572 panel = this.getPanel(panel);
35573 if(this.activePanel && this.activePanel != panel){
35574 this.activePanel.setActiveState(false);
35575 this.activePanel.getEl().setLeftTop(-10000,-10000);
35577 this.activePanel = panel;
35578 panel.setActiveState(true);
35580 panel.setSize(this.box.width, this.box.height);
35582 this.fireEvent("panelactivated", this, panel);
35583 this.fireEvent("invalidated");
35587 * Show the specified panel.
35588 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35589 * @return {Roo.ContentPanel} The shown panel or null
35591 showPanel : function(panel){
35592 panel = this.getPanel(panel);
35594 this.setActivePanel(panel);
35600 * Get the active panel for this region.
35601 * @return {Roo.ContentPanel} The active panel or null
35603 getActivePanel : function(){
35604 return this.activePanel;
35608 * Add the passed ContentPanel(s)
35609 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35610 * @return {Roo.ContentPanel} The panel added (if only one was added)
35612 add : function(panel){
35613 if(arguments.length > 1){
35614 for(var i = 0, len = arguments.length; i < len; i++) {
35615 this.add(arguments[i]);
35619 if(this.hasPanel(panel)){
35620 this.showPanel(panel);
35623 var el = panel.getEl();
35624 if(el.dom.parentNode != this.mgr.el.dom){
35625 this.mgr.el.dom.appendChild(el.dom);
35627 if(panel.setRegion){
35628 panel.setRegion(this);
35630 this.panels.add(panel);
35631 el.setStyle("position", "absolute");
35632 if(!panel.background){
35633 this.setActivePanel(panel);
35634 if(this.config.initialSize && this.panels.getCount()==1){
35635 this.resizeTo(this.config.initialSize);
35638 this.fireEvent("paneladded", this, panel);
35643 * Returns true if the panel is in this region.
35644 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35645 * @return {Boolean}
35647 hasPanel : function(panel){
35648 if(typeof panel == "object"){ // must be panel obj
35649 panel = panel.getId();
35651 return this.getPanel(panel) ? true : false;
35655 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35656 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35657 * @param {Boolean} preservePanel Overrides the config preservePanel option
35658 * @return {Roo.ContentPanel} The panel that was removed
35660 remove : function(panel, preservePanel){
35661 panel = this.getPanel(panel);
35666 this.fireEvent("beforeremove", this, panel, e);
35667 if(e.cancel === true){
35670 var panelId = panel.getId();
35671 this.panels.removeKey(panelId);
35676 * Returns the panel specified or null if it's not in this region.
35677 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35678 * @return {Roo.ContentPanel}
35680 getPanel : function(id){
35681 if(typeof id == "object"){ // must be panel obj
35684 return this.panels.get(id);
35688 * Returns this regions position (north/south/east/west/center).
35691 getPosition: function(){
35692 return this.position;
35696 * Ext JS Library 1.1.1
35697 * Copyright(c) 2006-2007, Ext JS, LLC.
35699 * Originally Released Under LGPL - original licence link has changed is not relivant.
35702 * <script type="text/javascript">
35706 * @class Roo.bootstrap.layout.Region
35707 * @extends Roo.bootstrap.layout.Basic
35708 * This class represents a region in a layout manager.
35710 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35711 * @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})
35712 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35713 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35714 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35715 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35716 * @cfg {String} title The title for the region (overrides panel titles)
35717 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35718 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35719 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35720 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35721 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35722 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35723 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35724 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35725 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35726 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35728 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35729 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35730 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35731 * @cfg {Number} width For East/West panels
35732 * @cfg {Number} height For North/South panels
35733 * @cfg {Boolean} split To show the splitter
35734 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35736 * @cfg {string} cls Extra CSS classes to add to region
35738 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35739 * @cfg {string} region the region that it inhabits..
35742 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35743 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35745 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35746 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35747 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35749 Roo.bootstrap.layout.Region = function(config)
35751 this.applyConfig(config);
35753 var mgr = config.mgr;
35754 var pos = config.region;
35755 config.skipConfig = true;
35756 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35759 this.onRender(mgr.el);
35762 this.visible = true;
35763 this.collapsed = false;
35764 this.unrendered_panels = [];
35767 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35769 position: '', // set by wrapper (eg. north/south etc..)
35770 unrendered_panels : null, // unrendered panels.
35771 createBody : function(){
35772 /** This region's body element
35773 * @type Roo.Element */
35774 this.bodyEl = this.el.createChild({
35776 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35780 onRender: function(ctr, pos)
35782 var dh = Roo.DomHelper;
35783 /** This region's container element
35784 * @type Roo.Element */
35785 this.el = dh.append(ctr.dom, {
35787 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35789 /** This region's title element
35790 * @type Roo.Element */
35792 this.titleEl = dh.append(this.el.dom,
35795 unselectable: "on",
35796 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35798 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35799 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35802 this.titleEl.enableDisplayMode();
35803 /** This region's title text element
35804 * @type HTMLElement */
35805 this.titleTextEl = this.titleEl.dom.firstChild;
35806 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35808 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35809 this.closeBtn.enableDisplayMode();
35810 this.closeBtn.on("click", this.closeClicked, this);
35811 this.closeBtn.hide();
35813 this.createBody(this.config);
35814 if(this.config.hideWhenEmpty){
35816 this.on("paneladded", this.validateVisibility, this);
35817 this.on("panelremoved", this.validateVisibility, this);
35819 if(this.autoScroll){
35820 this.bodyEl.setStyle("overflow", "auto");
35822 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35824 //if(c.titlebar !== false){
35825 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35826 this.titleEl.hide();
35828 this.titleEl.show();
35829 if(this.config.title){
35830 this.titleTextEl.innerHTML = this.config.title;
35834 if(this.config.collapsed){
35835 this.collapse(true);
35837 if(this.config.hidden){
35841 if (this.unrendered_panels && this.unrendered_panels.length) {
35842 for (var i =0;i< this.unrendered_panels.length; i++) {
35843 this.add(this.unrendered_panels[i]);
35845 this.unrendered_panels = null;
35851 applyConfig : function(c)
35854 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35855 var dh = Roo.DomHelper;
35856 if(c.titlebar !== false){
35857 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35858 this.collapseBtn.on("click", this.collapse, this);
35859 this.collapseBtn.enableDisplayMode();
35861 if(c.showPin === true || this.showPin){
35862 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35863 this.stickBtn.enableDisplayMode();
35864 this.stickBtn.on("click", this.expand, this);
35865 this.stickBtn.hide();
35870 /** This region's collapsed element
35871 * @type Roo.Element */
35874 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35875 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35878 if(c.floatable !== false){
35879 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35880 this.collapsedEl.on("click", this.collapseClick, this);
35883 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35884 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35885 id: "message", unselectable: "on", style:{"float":"left"}});
35886 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35888 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35889 this.expandBtn.on("click", this.expand, this);
35893 if(this.collapseBtn){
35894 this.collapseBtn.setVisible(c.collapsible == true);
35897 this.cmargins = c.cmargins || this.cmargins ||
35898 (this.position == "west" || this.position == "east" ?
35899 {top: 0, left: 2, right:2, bottom: 0} :
35900 {top: 2, left: 0, right:0, bottom: 2});
35902 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35905 this.bottomTabs = c.tabPosition != "top";
35907 this.autoScroll = c.autoScroll || false;
35912 this.duration = c.duration || .30;
35913 this.slideDuration = c.slideDuration || .45;
35918 * Returns true if this region is currently visible.
35919 * @return {Boolean}
35921 isVisible : function(){
35922 return this.visible;
35926 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35927 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35929 //setCollapsedTitle : function(title){
35930 // title = title || " ";
35931 // if(this.collapsedTitleTextEl){
35932 // this.collapsedTitleTextEl.innerHTML = title;
35936 getBox : function(){
35938 // if(!this.collapsed){
35939 b = this.el.getBox(false, true);
35941 // b = this.collapsedEl.getBox(false, true);
35946 getMargins : function(){
35947 return this.margins;
35948 //return this.collapsed ? this.cmargins : this.margins;
35951 highlight : function(){
35952 this.el.addClass("x-layout-panel-dragover");
35955 unhighlight : function(){
35956 this.el.removeClass("x-layout-panel-dragover");
35959 updateBox : function(box)
35961 if (!this.bodyEl) {
35962 return; // not rendered yet..
35966 if(!this.collapsed){
35967 this.el.dom.style.left = box.x + "px";
35968 this.el.dom.style.top = box.y + "px";
35969 this.updateBody(box.width, box.height);
35971 this.collapsedEl.dom.style.left = box.x + "px";
35972 this.collapsedEl.dom.style.top = box.y + "px";
35973 this.collapsedEl.setSize(box.width, box.height);
35976 this.tabs.autoSizeTabs();
35980 updateBody : function(w, h)
35983 this.el.setWidth(w);
35984 w -= this.el.getBorderWidth("rl");
35985 if(this.config.adjustments){
35986 w += this.config.adjustments[0];
35989 if(h !== null && h > 0){
35990 this.el.setHeight(h);
35991 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35992 h -= this.el.getBorderWidth("tb");
35993 if(this.config.adjustments){
35994 h += this.config.adjustments[1];
35996 this.bodyEl.setHeight(h);
35998 h = this.tabs.syncHeight(h);
36001 if(this.panelSize){
36002 w = w !== null ? w : this.panelSize.width;
36003 h = h !== null ? h : this.panelSize.height;
36005 if(this.activePanel){
36006 var el = this.activePanel.getEl();
36007 w = w !== null ? w : el.getWidth();
36008 h = h !== null ? h : el.getHeight();
36009 this.panelSize = {width: w, height: h};
36010 this.activePanel.setSize(w, h);
36012 if(Roo.isIE && this.tabs){
36013 this.tabs.el.repaint();
36018 * Returns the container element for this region.
36019 * @return {Roo.Element}
36021 getEl : function(){
36026 * Hides this region.
36029 //if(!this.collapsed){
36030 this.el.dom.style.left = "-2000px";
36033 // this.collapsedEl.dom.style.left = "-2000px";
36034 // this.collapsedEl.hide();
36036 this.visible = false;
36037 this.fireEvent("visibilitychange", this, false);
36041 * Shows this region if it was previously hidden.
36044 //if(!this.collapsed){
36047 // this.collapsedEl.show();
36049 this.visible = true;
36050 this.fireEvent("visibilitychange", this, true);
36053 closeClicked : function(){
36054 if(this.activePanel){
36055 this.remove(this.activePanel);
36059 collapseClick : function(e){
36061 e.stopPropagation();
36064 e.stopPropagation();
36070 * Collapses this region.
36071 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36074 collapse : function(skipAnim, skipCheck = false){
36075 if(this.collapsed) {
36079 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36081 this.collapsed = true;
36083 this.split.el.hide();
36085 if(this.config.animate && skipAnim !== true){
36086 this.fireEvent("invalidated", this);
36087 this.animateCollapse();
36089 this.el.setLocation(-20000,-20000);
36091 this.collapsedEl.show();
36092 this.fireEvent("collapsed", this);
36093 this.fireEvent("invalidated", this);
36099 animateCollapse : function(){
36104 * Expands this region if it was previously collapsed.
36105 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36106 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36109 expand : function(e, skipAnim){
36111 e.stopPropagation();
36113 if(!this.collapsed || this.el.hasActiveFx()) {
36117 this.afterSlideIn();
36120 this.collapsed = false;
36121 if(this.config.animate && skipAnim !== true){
36122 this.animateExpand();
36126 this.split.el.show();
36128 this.collapsedEl.setLocation(-2000,-2000);
36129 this.collapsedEl.hide();
36130 this.fireEvent("invalidated", this);
36131 this.fireEvent("expanded", this);
36135 animateExpand : function(){
36139 initTabs : function()
36141 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36143 var ts = new Roo.bootstrap.panel.Tabs({
36144 el: this.bodyEl.dom,
36145 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36146 disableTooltips: this.config.disableTabTips,
36147 toolbar : this.config.toolbar
36150 if(this.config.hideTabs){
36151 ts.stripWrap.setDisplayed(false);
36154 ts.resizeTabs = this.config.resizeTabs === true;
36155 ts.minTabWidth = this.config.minTabWidth || 40;
36156 ts.maxTabWidth = this.config.maxTabWidth || 250;
36157 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36158 ts.monitorResize = false;
36159 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36160 ts.bodyEl.addClass('roo-layout-tabs-body');
36161 this.panels.each(this.initPanelAsTab, this);
36164 initPanelAsTab : function(panel){
36165 var ti = this.tabs.addTab(
36169 this.config.closeOnTab && panel.isClosable(),
36172 if(panel.tabTip !== undefined){
36173 ti.setTooltip(panel.tabTip);
36175 ti.on("activate", function(){
36176 this.setActivePanel(panel);
36179 if(this.config.closeOnTab){
36180 ti.on("beforeclose", function(t, e){
36182 this.remove(panel);
36186 panel.tabItem = ti;
36191 updatePanelTitle : function(panel, title)
36193 if(this.activePanel == panel){
36194 this.updateTitle(title);
36197 var ti = this.tabs.getTab(panel.getEl().id);
36199 if(panel.tabTip !== undefined){
36200 ti.setTooltip(panel.tabTip);
36205 updateTitle : function(title){
36206 if(this.titleTextEl && !this.config.title){
36207 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36211 setActivePanel : function(panel)
36213 panel = this.getPanel(panel);
36214 if(this.activePanel && this.activePanel != panel){
36215 if(this.activePanel.setActiveState(false) === false){
36219 this.activePanel = panel;
36220 panel.setActiveState(true);
36221 if(this.panelSize){
36222 panel.setSize(this.panelSize.width, this.panelSize.height);
36225 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36227 this.updateTitle(panel.getTitle());
36229 this.fireEvent("invalidated", this);
36231 this.fireEvent("panelactivated", this, panel);
36235 * Shows the specified panel.
36236 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36237 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36239 showPanel : function(panel)
36241 panel = this.getPanel(panel);
36244 var tab = this.tabs.getTab(panel.getEl().id);
36245 if(tab.isHidden()){
36246 this.tabs.unhideTab(tab.id);
36250 this.setActivePanel(panel);
36257 * Get the active panel for this region.
36258 * @return {Roo.ContentPanel} The active panel or null
36260 getActivePanel : function(){
36261 return this.activePanel;
36264 validateVisibility : function(){
36265 if(this.panels.getCount() < 1){
36266 this.updateTitle(" ");
36267 this.closeBtn.hide();
36270 if(!this.isVisible()){
36277 * Adds the passed ContentPanel(s) to this region.
36278 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36279 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36281 add : function(panel)
36283 if(arguments.length > 1){
36284 for(var i = 0, len = arguments.length; i < len; i++) {
36285 this.add(arguments[i]);
36290 // if we have not been rendered yet, then we can not really do much of this..
36291 if (!this.bodyEl) {
36292 this.unrendered_panels.push(panel);
36299 if(this.hasPanel(panel)){
36300 this.showPanel(panel);
36303 panel.setRegion(this);
36304 this.panels.add(panel);
36305 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36306 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36307 // and hide them... ???
36308 this.bodyEl.dom.appendChild(panel.getEl().dom);
36309 if(panel.background !== true){
36310 this.setActivePanel(panel);
36312 this.fireEvent("paneladded", this, panel);
36319 this.initPanelAsTab(panel);
36323 if(panel.background !== true){
36324 this.tabs.activate(panel.getEl().id);
36326 this.fireEvent("paneladded", this, panel);
36331 * Hides the tab for the specified panel.
36332 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36334 hidePanel : function(panel){
36335 if(this.tabs && (panel = this.getPanel(panel))){
36336 this.tabs.hideTab(panel.getEl().id);
36341 * Unhides the tab for a previously hidden panel.
36342 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36344 unhidePanel : function(panel){
36345 if(this.tabs && (panel = this.getPanel(panel))){
36346 this.tabs.unhideTab(panel.getEl().id);
36350 clearPanels : function(){
36351 while(this.panels.getCount() > 0){
36352 this.remove(this.panels.first());
36357 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36358 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36359 * @param {Boolean} preservePanel Overrides the config preservePanel option
36360 * @return {Roo.ContentPanel} The panel that was removed
36362 remove : function(panel, preservePanel)
36364 panel = this.getPanel(panel);
36369 this.fireEvent("beforeremove", this, panel, e);
36370 if(e.cancel === true){
36373 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36374 var panelId = panel.getId();
36375 this.panels.removeKey(panelId);
36377 document.body.appendChild(panel.getEl().dom);
36380 this.tabs.removeTab(panel.getEl().id);
36381 }else if (!preservePanel){
36382 this.bodyEl.dom.removeChild(panel.getEl().dom);
36384 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36385 var p = this.panels.first();
36386 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36387 tempEl.appendChild(p.getEl().dom);
36388 this.bodyEl.update("");
36389 this.bodyEl.dom.appendChild(p.getEl().dom);
36391 this.updateTitle(p.getTitle());
36393 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36394 this.setActivePanel(p);
36396 panel.setRegion(null);
36397 if(this.activePanel == panel){
36398 this.activePanel = null;
36400 if(this.config.autoDestroy !== false && preservePanel !== true){
36401 try{panel.destroy();}catch(e){}
36403 this.fireEvent("panelremoved", this, panel);
36408 * Returns the TabPanel component used by this region
36409 * @return {Roo.TabPanel}
36411 getTabs : function(){
36415 createTool : function(parentEl, className){
36416 var btn = Roo.DomHelper.append(parentEl, {
36418 cls: "x-layout-tools-button",
36421 cls: "roo-layout-tools-button-inner " + className,
36425 btn.addClassOnOver("roo-layout-tools-button-over");
36430 * Ext JS Library 1.1.1
36431 * Copyright(c) 2006-2007, Ext JS, LLC.
36433 * Originally Released Under LGPL - original licence link has changed is not relivant.
36436 * <script type="text/javascript">
36442 * @class Roo.SplitLayoutRegion
36443 * @extends Roo.LayoutRegion
36444 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36446 Roo.bootstrap.layout.Split = function(config){
36447 this.cursor = config.cursor;
36448 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36451 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36453 splitTip : "Drag to resize.",
36454 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36455 useSplitTips : false,
36457 applyConfig : function(config){
36458 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36461 onRender : function(ctr,pos) {
36463 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36464 if(!this.config.split){
36469 var splitEl = Roo.DomHelper.append(ctr.dom, {
36471 id: this.el.id + "-split",
36472 cls: "roo-layout-split roo-layout-split-"+this.position,
36475 /** The SplitBar for this region
36476 * @type Roo.SplitBar */
36477 // does not exist yet...
36478 Roo.log([this.position, this.orientation]);
36480 this.split = new Roo.bootstrap.SplitBar({
36481 dragElement : splitEl,
36482 resizingElement: this.el,
36483 orientation : this.orientation
36486 this.split.on("moved", this.onSplitMove, this);
36487 this.split.useShim = this.config.useShim === true;
36488 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36489 if(this.useSplitTips){
36490 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36492 //if(config.collapsible){
36493 // this.split.el.on("dblclick", this.collapse, this);
36496 if(typeof this.config.minSize != "undefined"){
36497 this.split.minSize = this.config.minSize;
36499 if(typeof this.config.maxSize != "undefined"){
36500 this.split.maxSize = this.config.maxSize;
36502 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36503 this.hideSplitter();
36508 getHMaxSize : function(){
36509 var cmax = this.config.maxSize || 10000;
36510 var center = this.mgr.getRegion("center");
36511 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36514 getVMaxSize : function(){
36515 var cmax = this.config.maxSize || 10000;
36516 var center = this.mgr.getRegion("center");
36517 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36520 onSplitMove : function(split, newSize){
36521 this.fireEvent("resized", this, newSize);
36525 * Returns the {@link Roo.SplitBar} for this region.
36526 * @return {Roo.SplitBar}
36528 getSplitBar : function(){
36533 this.hideSplitter();
36534 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36537 hideSplitter : function(){
36539 this.split.el.setLocation(-2000,-2000);
36540 this.split.el.hide();
36546 this.split.el.show();
36548 Roo.bootstrap.layout.Split.superclass.show.call(this);
36551 beforeSlide: function(){
36552 if(Roo.isGecko){// firefox overflow auto bug workaround
36553 this.bodyEl.clip();
36555 this.tabs.bodyEl.clip();
36557 if(this.activePanel){
36558 this.activePanel.getEl().clip();
36560 if(this.activePanel.beforeSlide){
36561 this.activePanel.beforeSlide();
36567 afterSlide : function(){
36568 if(Roo.isGecko){// firefox overflow auto bug workaround
36569 this.bodyEl.unclip();
36571 this.tabs.bodyEl.unclip();
36573 if(this.activePanel){
36574 this.activePanel.getEl().unclip();
36575 if(this.activePanel.afterSlide){
36576 this.activePanel.afterSlide();
36582 initAutoHide : function(){
36583 if(this.autoHide !== false){
36584 if(!this.autoHideHd){
36585 var st = new Roo.util.DelayedTask(this.slideIn, this);
36586 this.autoHideHd = {
36587 "mouseout": function(e){
36588 if(!e.within(this.el, true)){
36592 "mouseover" : function(e){
36598 this.el.on(this.autoHideHd);
36602 clearAutoHide : function(){
36603 if(this.autoHide !== false){
36604 this.el.un("mouseout", this.autoHideHd.mouseout);
36605 this.el.un("mouseover", this.autoHideHd.mouseover);
36609 clearMonitor : function(){
36610 Roo.get(document).un("click", this.slideInIf, this);
36613 // these names are backwards but not changed for compat
36614 slideOut : function(){
36615 if(this.isSlid || this.el.hasActiveFx()){
36618 this.isSlid = true;
36619 if(this.collapseBtn){
36620 this.collapseBtn.hide();
36622 this.closeBtnState = this.closeBtn.getStyle('display');
36623 this.closeBtn.hide();
36625 this.stickBtn.show();
36628 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36629 this.beforeSlide();
36630 this.el.setStyle("z-index", 10001);
36631 this.el.slideIn(this.getSlideAnchor(), {
36632 callback: function(){
36634 this.initAutoHide();
36635 Roo.get(document).on("click", this.slideInIf, this);
36636 this.fireEvent("slideshow", this);
36643 afterSlideIn : function(){
36644 this.clearAutoHide();
36645 this.isSlid = false;
36646 this.clearMonitor();
36647 this.el.setStyle("z-index", "");
36648 if(this.collapseBtn){
36649 this.collapseBtn.show();
36651 this.closeBtn.setStyle('display', this.closeBtnState);
36653 this.stickBtn.hide();
36655 this.fireEvent("slidehide", this);
36658 slideIn : function(cb){
36659 if(!this.isSlid || this.el.hasActiveFx()){
36663 this.isSlid = false;
36664 this.beforeSlide();
36665 this.el.slideOut(this.getSlideAnchor(), {
36666 callback: function(){
36667 this.el.setLeftTop(-10000, -10000);
36669 this.afterSlideIn();
36677 slideInIf : function(e){
36678 if(!e.within(this.el)){
36683 animateCollapse : function(){
36684 this.beforeSlide();
36685 this.el.setStyle("z-index", 20000);
36686 var anchor = this.getSlideAnchor();
36687 this.el.slideOut(anchor, {
36688 callback : function(){
36689 this.el.setStyle("z-index", "");
36690 this.collapsedEl.slideIn(anchor, {duration:.3});
36692 this.el.setLocation(-10000,-10000);
36694 this.fireEvent("collapsed", this);
36701 animateExpand : function(){
36702 this.beforeSlide();
36703 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36704 this.el.setStyle("z-index", 20000);
36705 this.collapsedEl.hide({
36708 this.el.slideIn(this.getSlideAnchor(), {
36709 callback : function(){
36710 this.el.setStyle("z-index", "");
36713 this.split.el.show();
36715 this.fireEvent("invalidated", this);
36716 this.fireEvent("expanded", this);
36744 getAnchor : function(){
36745 return this.anchors[this.position];
36748 getCollapseAnchor : function(){
36749 return this.canchors[this.position];
36752 getSlideAnchor : function(){
36753 return this.sanchors[this.position];
36756 getAlignAdj : function(){
36757 var cm = this.cmargins;
36758 switch(this.position){
36774 getExpandAdj : function(){
36775 var c = this.collapsedEl, cm = this.cmargins;
36776 switch(this.position){
36778 return [-(cm.right+c.getWidth()+cm.left), 0];
36781 return [cm.right+c.getWidth()+cm.left, 0];
36784 return [0, -(cm.top+cm.bottom+c.getHeight())];
36787 return [0, cm.top+cm.bottom+c.getHeight()];
36793 * Ext JS Library 1.1.1
36794 * Copyright(c) 2006-2007, Ext JS, LLC.
36796 * Originally Released Under LGPL - original licence link has changed is not relivant.
36799 * <script type="text/javascript">
36802 * These classes are private internal classes
36804 Roo.bootstrap.layout.Center = function(config){
36805 config.region = "center";
36806 Roo.bootstrap.layout.Region.call(this, config);
36807 this.visible = true;
36808 this.minWidth = config.minWidth || 20;
36809 this.minHeight = config.minHeight || 20;
36812 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36814 // center panel can't be hidden
36818 // center panel can't be hidden
36821 getMinWidth: function(){
36822 return this.minWidth;
36825 getMinHeight: function(){
36826 return this.minHeight;
36839 Roo.bootstrap.layout.North = function(config)
36841 config.region = 'north';
36842 config.cursor = 'n-resize';
36844 Roo.bootstrap.layout.Split.call(this, config);
36848 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36849 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36850 this.split.el.addClass("roo-layout-split-v");
36852 var size = config.initialSize || config.height;
36853 if(typeof size != "undefined"){
36854 this.el.setHeight(size);
36857 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36859 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36863 getBox : function(){
36864 if(this.collapsed){
36865 return this.collapsedEl.getBox();
36867 var box = this.el.getBox();
36869 box.height += this.split.el.getHeight();
36874 updateBox : function(box){
36875 if(this.split && !this.collapsed){
36876 box.height -= this.split.el.getHeight();
36877 this.split.el.setLeft(box.x);
36878 this.split.el.setTop(box.y+box.height);
36879 this.split.el.setWidth(box.width);
36881 if(this.collapsed){
36882 this.updateBody(box.width, null);
36884 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36892 Roo.bootstrap.layout.South = function(config){
36893 config.region = 'south';
36894 config.cursor = 's-resize';
36895 Roo.bootstrap.layout.Split.call(this, config);
36897 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36898 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36899 this.split.el.addClass("roo-layout-split-v");
36901 var size = config.initialSize || config.height;
36902 if(typeof size != "undefined"){
36903 this.el.setHeight(size);
36907 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36908 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36909 getBox : function(){
36910 if(this.collapsed){
36911 return this.collapsedEl.getBox();
36913 var box = this.el.getBox();
36915 var sh = this.split.el.getHeight();
36922 updateBox : function(box){
36923 if(this.split && !this.collapsed){
36924 var sh = this.split.el.getHeight();
36927 this.split.el.setLeft(box.x);
36928 this.split.el.setTop(box.y-sh);
36929 this.split.el.setWidth(box.width);
36931 if(this.collapsed){
36932 this.updateBody(box.width, null);
36934 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36938 Roo.bootstrap.layout.East = function(config){
36939 config.region = "east";
36940 config.cursor = "e-resize";
36941 Roo.bootstrap.layout.Split.call(this, config);
36943 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36944 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36945 this.split.el.addClass("roo-layout-split-h");
36947 var size = config.initialSize || config.width;
36948 if(typeof size != "undefined"){
36949 this.el.setWidth(size);
36952 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36953 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36954 getBox : function(){
36955 if(this.collapsed){
36956 return this.collapsedEl.getBox();
36958 var box = this.el.getBox();
36960 var sw = this.split.el.getWidth();
36967 updateBox : function(box){
36968 if(this.split && !this.collapsed){
36969 var sw = this.split.el.getWidth();
36971 this.split.el.setLeft(box.x);
36972 this.split.el.setTop(box.y);
36973 this.split.el.setHeight(box.height);
36976 if(this.collapsed){
36977 this.updateBody(null, box.height);
36979 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36983 Roo.bootstrap.layout.West = function(config){
36984 config.region = "west";
36985 config.cursor = "w-resize";
36987 Roo.bootstrap.layout.Split.call(this, config);
36989 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36990 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36991 this.split.el.addClass("roo-layout-split-h");
36995 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36996 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36998 onRender: function(ctr, pos)
37000 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37001 var size = this.config.initialSize || this.config.width;
37002 if(typeof size != "undefined"){
37003 this.el.setWidth(size);
37007 getBox : function(){
37008 if(this.collapsed){
37009 return this.collapsedEl.getBox();
37011 var box = this.el.getBox();
37013 box.width += this.split.el.getWidth();
37018 updateBox : function(box){
37019 if(this.split && !this.collapsed){
37020 var sw = this.split.el.getWidth();
37022 this.split.el.setLeft(box.x+box.width);
37023 this.split.el.setTop(box.y);
37024 this.split.el.setHeight(box.height);
37026 if(this.collapsed){
37027 this.updateBody(null, box.height);
37029 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37032 Roo.namespace("Roo.bootstrap.panel");/*
37034 * Ext JS Library 1.1.1
37035 * Copyright(c) 2006-2007, Ext JS, LLC.
37037 * Originally Released Under LGPL - original licence link has changed is not relivant.
37040 * <script type="text/javascript">
37043 * @class Roo.ContentPanel
37044 * @extends Roo.util.Observable
37045 * A basic ContentPanel element.
37046 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
37047 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
37048 * @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
37049 * @cfg {Boolean} closable True if the panel can be closed/removed
37050 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
37051 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37052 * @cfg {Toolbar} toolbar A toolbar for this panel
37053 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37054 * @cfg {String} title The title for this panel
37055 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37056 * @cfg {String} url Calls {@link #setUrl} with this value
37057 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37058 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37059 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37060 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37061 * @cfg {Boolean} badges render the badges
37064 * Create a new ContentPanel.
37065 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37066 * @param {String/Object} config A string to set only the title or a config object
37067 * @param {String} content (optional) Set the HTML content for this panel
37068 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37070 Roo.bootstrap.panel.Content = function( config){
37072 this.tpl = config.tpl || false;
37074 var el = config.el;
37075 var content = config.content;
37077 if(config.autoCreate){ // xtype is available if this is called from factory
37080 this.el = Roo.get(el);
37081 if(!this.el && config && config.autoCreate){
37082 if(typeof config.autoCreate == "object"){
37083 if(!config.autoCreate.id){
37084 config.autoCreate.id = config.id||el;
37086 this.el = Roo.DomHelper.append(document.body,
37087 config.autoCreate, true);
37089 var elcfg = { tag: "div",
37090 cls: "roo-layout-inactive-content",
37094 elcfg.html = config.html;
37098 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37101 this.closable = false;
37102 this.loaded = false;
37103 this.active = false;
37106 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37108 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37110 this.wrapEl = this.el; //this.el.wrap();
37112 if (config.toolbar.items) {
37113 ti = config.toolbar.items ;
37114 delete config.toolbar.items ;
37118 this.toolbar.render(this.wrapEl, 'before');
37119 for(var i =0;i < ti.length;i++) {
37120 // Roo.log(['add child', items[i]]);
37121 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37123 this.toolbar.items = nitems;
37124 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37125 delete config.toolbar;
37129 // xtype created footer. - not sure if will work as we normally have to render first..
37130 if (this.footer && !this.footer.el && this.footer.xtype) {
37131 if (!this.wrapEl) {
37132 this.wrapEl = this.el.wrap();
37135 this.footer.container = this.wrapEl.createChild();
37137 this.footer = Roo.factory(this.footer, Roo);
37142 if(typeof config == "string"){
37143 this.title = config;
37145 Roo.apply(this, config);
37149 this.resizeEl = Roo.get(this.resizeEl, true);
37151 this.resizeEl = this.el;
37153 // handle view.xtype
37161 * Fires when this panel is activated.
37162 * @param {Roo.ContentPanel} this
37166 * @event deactivate
37167 * Fires when this panel is activated.
37168 * @param {Roo.ContentPanel} this
37170 "deactivate" : true,
37174 * Fires when this panel is resized if fitToFrame is true.
37175 * @param {Roo.ContentPanel} this
37176 * @param {Number} width The width after any component adjustments
37177 * @param {Number} height The height after any component adjustments
37183 * Fires when this tab is created
37184 * @param {Roo.ContentPanel} this
37195 if(this.autoScroll){
37196 this.resizeEl.setStyle("overflow", "auto");
37198 // fix randome scrolling
37199 //this.el.on('scroll', function() {
37200 // Roo.log('fix random scolling');
37201 // this.scrollTo('top',0);
37204 content = content || this.content;
37206 this.setContent(content);
37208 if(config && config.url){
37209 this.setUrl(this.url, this.params, this.loadOnce);
37214 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37216 if (this.view && typeof(this.view.xtype) != 'undefined') {
37217 this.view.el = this.el.appendChild(document.createElement("div"));
37218 this.view = Roo.factory(this.view);
37219 this.view.render && this.view.render(false, '');
37223 this.fireEvent('render', this);
37226 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37230 setRegion : function(region){
37231 this.region = region;
37232 this.setActiveClass(region && !this.background);
37236 setActiveClass: function(state)
37239 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37240 this.el.setStyle('position','relative');
37242 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37243 this.el.setStyle('position', 'absolute');
37248 * Returns the toolbar for this Panel if one was configured.
37249 * @return {Roo.Toolbar}
37251 getToolbar : function(){
37252 return this.toolbar;
37255 setActiveState : function(active)
37257 this.active = active;
37258 this.setActiveClass(active);
37260 if(this.fireEvent("deactivate", this) === false){
37265 this.fireEvent("activate", this);
37269 * Updates this panel's element
37270 * @param {String} content The new content
37271 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37273 setContent : function(content, loadScripts){
37274 this.el.update(content, loadScripts);
37277 ignoreResize : function(w, h){
37278 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37281 this.lastSize = {width: w, height: h};
37286 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37287 * @return {Roo.UpdateManager} The UpdateManager
37289 getUpdateManager : function(){
37290 return this.el.getUpdateManager();
37293 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37294 * @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:
37297 url: "your-url.php",
37298 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37299 callback: yourFunction,
37300 scope: yourObject, //(optional scope)
37303 text: "Loading...",
37308 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37309 * 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.
37310 * @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}
37311 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37312 * @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.
37313 * @return {Roo.ContentPanel} this
37316 var um = this.el.getUpdateManager();
37317 um.update.apply(um, arguments);
37323 * 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.
37324 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37325 * @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)
37326 * @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)
37327 * @return {Roo.UpdateManager} The UpdateManager
37329 setUrl : function(url, params, loadOnce){
37330 if(this.refreshDelegate){
37331 this.removeListener("activate", this.refreshDelegate);
37333 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37334 this.on("activate", this.refreshDelegate);
37335 return this.el.getUpdateManager();
37338 _handleRefresh : function(url, params, loadOnce){
37339 if(!loadOnce || !this.loaded){
37340 var updater = this.el.getUpdateManager();
37341 updater.update(url, params, this._setLoaded.createDelegate(this));
37345 _setLoaded : function(){
37346 this.loaded = true;
37350 * Returns this panel's id
37353 getId : function(){
37358 * Returns this panel's element - used by regiosn to add.
37359 * @return {Roo.Element}
37361 getEl : function(){
37362 return this.wrapEl || this.el;
37367 adjustForComponents : function(width, height)
37369 //Roo.log('adjustForComponents ');
37370 if(this.resizeEl != this.el){
37371 width -= this.el.getFrameWidth('lr');
37372 height -= this.el.getFrameWidth('tb');
37375 var te = this.toolbar.getEl();
37376 te.setWidth(width);
37377 height -= te.getHeight();
37380 var te = this.footer.getEl();
37381 te.setWidth(width);
37382 height -= te.getHeight();
37386 if(this.adjustments){
37387 width += this.adjustments[0];
37388 height += this.adjustments[1];
37390 return {"width": width, "height": height};
37393 setSize : function(width, height){
37394 if(this.fitToFrame && !this.ignoreResize(width, height)){
37395 if(this.fitContainer && this.resizeEl != this.el){
37396 this.el.setSize(width, height);
37398 var size = this.adjustForComponents(width, height);
37399 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37400 this.fireEvent('resize', this, size.width, size.height);
37405 * Returns this panel's title
37408 getTitle : function(){
37410 if (typeof(this.title) != 'object') {
37415 for (var k in this.title) {
37416 if (!this.title.hasOwnProperty(k)) {
37420 if (k.indexOf('-') >= 0) {
37421 var s = k.split('-');
37422 for (var i = 0; i<s.length; i++) {
37423 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37426 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37433 * Set this panel's title
37434 * @param {String} title
37436 setTitle : function(title){
37437 this.title = title;
37439 this.region.updatePanelTitle(this, title);
37444 * Returns true is this panel was configured to be closable
37445 * @return {Boolean}
37447 isClosable : function(){
37448 return this.closable;
37451 beforeSlide : function(){
37453 this.resizeEl.clip();
37456 afterSlide : function(){
37458 this.resizeEl.unclip();
37462 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37463 * Will fail silently if the {@link #setUrl} method has not been called.
37464 * This does not activate the panel, just updates its content.
37466 refresh : function(){
37467 if(this.refreshDelegate){
37468 this.loaded = false;
37469 this.refreshDelegate();
37474 * Destroys this panel
37476 destroy : function(){
37477 this.el.removeAllListeners();
37478 var tempEl = document.createElement("span");
37479 tempEl.appendChild(this.el.dom);
37480 tempEl.innerHTML = "";
37486 * form - if the content panel contains a form - this is a reference to it.
37487 * @type {Roo.form.Form}
37491 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37492 * This contains a reference to it.
37498 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37508 * @param {Object} cfg Xtype definition of item to add.
37512 getChildContainer: function () {
37513 return this.getEl();
37518 var ret = new Roo.factory(cfg);
37523 if (cfg.xtype.match(/^Form$/)) {
37526 //if (this.footer) {
37527 // el = this.footer.container.insertSibling(false, 'before');
37529 el = this.el.createChild();
37532 this.form = new Roo.form.Form(cfg);
37535 if ( this.form.allItems.length) {
37536 this.form.render(el.dom);
37540 // should only have one of theses..
37541 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37542 // views.. should not be just added - used named prop 'view''
37544 cfg.el = this.el.appendChild(document.createElement("div"));
37547 var ret = new Roo.factory(cfg);
37549 ret.render && ret.render(false, ''); // render blank..
37559 * @class Roo.bootstrap.panel.Grid
37560 * @extends Roo.bootstrap.panel.Content
37562 * Create a new GridPanel.
37563 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37564 * @param {Object} config A the config object
37570 Roo.bootstrap.panel.Grid = function(config)
37574 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37575 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37577 config.el = this.wrapper;
37578 //this.el = this.wrapper;
37580 if (config.container) {
37581 // ctor'ed from a Border/panel.grid
37584 this.wrapper.setStyle("overflow", "hidden");
37585 this.wrapper.addClass('roo-grid-container');
37590 if(config.toolbar){
37591 var tool_el = this.wrapper.createChild();
37592 this.toolbar = Roo.factory(config.toolbar);
37594 if (config.toolbar.items) {
37595 ti = config.toolbar.items ;
37596 delete config.toolbar.items ;
37600 this.toolbar.render(tool_el);
37601 for(var i =0;i < ti.length;i++) {
37602 // Roo.log(['add child', items[i]]);
37603 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37605 this.toolbar.items = nitems;
37607 delete config.toolbar;
37610 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37611 config.grid.scrollBody = true;;
37612 config.grid.monitorWindowResize = false; // turn off autosizing
37613 config.grid.autoHeight = false;
37614 config.grid.autoWidth = false;
37616 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37618 if (config.background) {
37619 // render grid on panel activation (if panel background)
37620 this.on('activate', function(gp) {
37621 if (!gp.grid.rendered) {
37622 gp.grid.render(this.wrapper);
37623 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37628 this.grid.render(this.wrapper);
37629 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37632 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37633 // ??? needed ??? config.el = this.wrapper;
37638 // xtype created footer. - not sure if will work as we normally have to render first..
37639 if (this.footer && !this.footer.el && this.footer.xtype) {
37641 var ctr = this.grid.getView().getFooterPanel(true);
37642 this.footer.dataSource = this.grid.dataSource;
37643 this.footer = Roo.factory(this.footer, Roo);
37644 this.footer.render(ctr);
37654 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37655 getId : function(){
37656 return this.grid.id;
37660 * Returns the grid for this panel
37661 * @return {Roo.bootstrap.Table}
37663 getGrid : function(){
37667 setSize : function(width, height){
37668 if(!this.ignoreResize(width, height)){
37669 var grid = this.grid;
37670 var size = this.adjustForComponents(width, height);
37671 var gridel = grid.getGridEl();
37672 gridel.setSize(size.width, size.height);
37674 var thd = grid.getGridEl().select('thead',true).first();
37675 var tbd = grid.getGridEl().select('tbody', true).first();
37677 tbd.setSize(width, height - thd.getHeight());
37686 beforeSlide : function(){
37687 this.grid.getView().scroller.clip();
37690 afterSlide : function(){
37691 this.grid.getView().scroller.unclip();
37694 destroy : function(){
37695 this.grid.destroy();
37697 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37702 * @class Roo.bootstrap.panel.Nest
37703 * @extends Roo.bootstrap.panel.Content
37705 * Create a new Panel, that can contain a layout.Border.
37708 * @param {Roo.BorderLayout} layout The layout for this panel
37709 * @param {String/Object} config A string to set only the title or a config object
37711 Roo.bootstrap.panel.Nest = function(config)
37713 // construct with only one argument..
37714 /* FIXME - implement nicer consturctors
37715 if (layout.layout) {
37717 layout = config.layout;
37718 delete config.layout;
37720 if (layout.xtype && !layout.getEl) {
37721 // then layout needs constructing..
37722 layout = Roo.factory(layout, Roo);
37726 config.el = config.layout.getEl();
37728 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37730 config.layout.monitorWindowResize = false; // turn off autosizing
37731 this.layout = config.layout;
37732 this.layout.getEl().addClass("roo-layout-nested-layout");
37739 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37741 setSize : function(width, height){
37742 if(!this.ignoreResize(width, height)){
37743 var size = this.adjustForComponents(width, height);
37744 var el = this.layout.getEl();
37745 if (size.height < 1) {
37746 el.setWidth(size.width);
37748 el.setSize(size.width, size.height);
37750 var touch = el.dom.offsetWidth;
37751 this.layout.layout();
37752 // ie requires a double layout on the first pass
37753 if(Roo.isIE && !this.initialized){
37754 this.initialized = true;
37755 this.layout.layout();
37760 // activate all subpanels if not currently active..
37762 setActiveState : function(active){
37763 this.active = active;
37764 this.setActiveClass(active);
37767 this.fireEvent("deactivate", this);
37771 this.fireEvent("activate", this);
37772 // not sure if this should happen before or after..
37773 if (!this.layout) {
37774 return; // should not happen..
37777 for (var r in this.layout.regions) {
37778 reg = this.layout.getRegion(r);
37779 if (reg.getActivePanel()) {
37780 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37781 reg.setActivePanel(reg.getActivePanel());
37784 if (!reg.panels.length) {
37787 reg.showPanel(reg.getPanel(0));
37796 * Returns the nested BorderLayout for this panel
37797 * @return {Roo.BorderLayout}
37799 getLayout : function(){
37800 return this.layout;
37804 * Adds a xtype elements to the layout of the nested panel
37808 xtype : 'ContentPanel',
37815 xtype : 'NestedLayoutPanel',
37821 items : [ ... list of content panels or nested layout panels.. ]
37825 * @param {Object} cfg Xtype definition of item to add.
37827 addxtype : function(cfg) {
37828 return this.layout.addxtype(cfg);
37833 * Ext JS Library 1.1.1
37834 * Copyright(c) 2006-2007, Ext JS, LLC.
37836 * Originally Released Under LGPL - original licence link has changed is not relivant.
37839 * <script type="text/javascript">
37842 * @class Roo.TabPanel
37843 * @extends Roo.util.Observable
37844 * A lightweight tab container.
37848 // basic tabs 1, built from existing content
37849 var tabs = new Roo.TabPanel("tabs1");
37850 tabs.addTab("script", "View Script");
37851 tabs.addTab("markup", "View Markup");
37852 tabs.activate("script");
37854 // more advanced tabs, built from javascript
37855 var jtabs = new Roo.TabPanel("jtabs");
37856 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37858 // set up the UpdateManager
37859 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37860 var updater = tab2.getUpdateManager();
37861 updater.setDefaultUrl("ajax1.htm");
37862 tab2.on('activate', updater.refresh, updater, true);
37864 // Use setUrl for Ajax loading
37865 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37866 tab3.setUrl("ajax2.htm", null, true);
37869 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37872 jtabs.activate("jtabs-1");
37875 * Create a new TabPanel.
37876 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37877 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37879 Roo.bootstrap.panel.Tabs = function(config){
37881 * The container element for this TabPanel.
37882 * @type Roo.Element
37884 this.el = Roo.get(config.el);
37887 if(typeof config == "boolean"){
37888 this.tabPosition = config ? "bottom" : "top";
37890 Roo.apply(this, config);
37894 if(this.tabPosition == "bottom"){
37895 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37896 this.el.addClass("roo-tabs-bottom");
37898 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37899 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37900 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37902 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37904 if(this.tabPosition != "bottom"){
37905 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37906 * @type Roo.Element
37908 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37909 this.el.addClass("roo-tabs-top");
37913 this.bodyEl.setStyle("position", "relative");
37915 this.active = null;
37916 this.activateDelegate = this.activate.createDelegate(this);
37921 * Fires when the active tab changes
37922 * @param {Roo.TabPanel} this
37923 * @param {Roo.TabPanelItem} activePanel The new active tab
37927 * @event beforetabchange
37928 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37929 * @param {Roo.TabPanel} this
37930 * @param {Object} e Set cancel to true on this object to cancel the tab change
37931 * @param {Roo.TabPanelItem} tab The tab being changed to
37933 "beforetabchange" : true
37936 Roo.EventManager.onWindowResize(this.onResize, this);
37937 this.cpad = this.el.getPadding("lr");
37938 this.hiddenCount = 0;
37941 // toolbar on the tabbar support...
37942 if (this.toolbar) {
37943 alert("no toolbar support yet");
37944 this.toolbar = false;
37946 var tcfg = this.toolbar;
37947 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37948 this.toolbar = new Roo.Toolbar(tcfg);
37949 if (Roo.isSafari) {
37950 var tbl = tcfg.container.child('table', true);
37951 tbl.setAttribute('width', '100%');
37959 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37962 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37964 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37966 tabPosition : "top",
37968 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37970 currentTabWidth : 0,
37972 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37976 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37980 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37982 preferredTabWidth : 175,
37984 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37986 resizeTabs : false,
37988 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37990 monitorResize : true,
37992 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37997 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37998 * @param {String} id The id of the div to use <b>or create</b>
37999 * @param {String} text The text for the tab
38000 * @param {String} content (optional) Content to put in the TabPanelItem body
38001 * @param {Boolean} closable (optional) True to create a close icon on the tab
38002 * @return {Roo.TabPanelItem} The created TabPanelItem
38004 addTab : function(id, text, content, closable, tpl)
38006 var item = new Roo.bootstrap.panel.TabItem({
38010 closable : closable,
38013 this.addTabItem(item);
38015 item.setContent(content);
38021 * Returns the {@link Roo.TabPanelItem} with the specified id/index
38022 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38023 * @return {Roo.TabPanelItem}
38025 getTab : function(id){
38026 return this.items[id];
38030 * Hides the {@link Roo.TabPanelItem} with the specified id/index
38031 * @param {String/Number} id The id or index of the TabPanelItem to hide.
38033 hideTab : function(id){
38034 var t = this.items[id];
38037 this.hiddenCount++;
38038 this.autoSizeTabs();
38043 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38044 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38046 unhideTab : function(id){
38047 var t = this.items[id];
38049 t.setHidden(false);
38050 this.hiddenCount--;
38051 this.autoSizeTabs();
38056 * Adds an existing {@link Roo.TabPanelItem}.
38057 * @param {Roo.TabPanelItem} item The TabPanelItem to add
38059 addTabItem : function(item){
38060 this.items[item.id] = item;
38061 this.items.push(item);
38062 // if(this.resizeTabs){
38063 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38064 // this.autoSizeTabs();
38066 // item.autoSize();
38071 * Removes a {@link Roo.TabPanelItem}.
38072 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38074 removeTab : function(id){
38075 var items = this.items;
38076 var tab = items[id];
38077 if(!tab) { return; }
38078 var index = items.indexOf(tab);
38079 if(this.active == tab && items.length > 1){
38080 var newTab = this.getNextAvailable(index);
38085 this.stripEl.dom.removeChild(tab.pnode.dom);
38086 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38087 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38089 items.splice(index, 1);
38090 delete this.items[tab.id];
38091 tab.fireEvent("close", tab);
38092 tab.purgeListeners();
38093 this.autoSizeTabs();
38096 getNextAvailable : function(start){
38097 var items = this.items;
38099 // look for a next tab that will slide over to
38100 // replace the one being removed
38101 while(index < items.length){
38102 var item = items[++index];
38103 if(item && !item.isHidden()){
38107 // if one isn't found select the previous tab (on the left)
38110 var item = items[--index];
38111 if(item && !item.isHidden()){
38119 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38120 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38122 disableTab : function(id){
38123 var tab = this.items[id];
38124 if(tab && this.active != tab){
38130 * Enables a {@link Roo.TabPanelItem} that is disabled.
38131 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38133 enableTab : function(id){
38134 var tab = this.items[id];
38139 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38140 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38141 * @return {Roo.TabPanelItem} The TabPanelItem.
38143 activate : function(id){
38144 var tab = this.items[id];
38148 if(tab == this.active || tab.disabled){
38152 this.fireEvent("beforetabchange", this, e, tab);
38153 if(e.cancel !== true && !tab.disabled){
38155 this.active.hide();
38157 this.active = this.items[id];
38158 this.active.show();
38159 this.fireEvent("tabchange", this, this.active);
38165 * Gets the active {@link Roo.TabPanelItem}.
38166 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38168 getActiveTab : function(){
38169 return this.active;
38173 * Updates the tab body element to fit the height of the container element
38174 * for overflow scrolling
38175 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38177 syncHeight : function(targetHeight){
38178 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38179 var bm = this.bodyEl.getMargins();
38180 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38181 this.bodyEl.setHeight(newHeight);
38185 onResize : function(){
38186 if(this.monitorResize){
38187 this.autoSizeTabs();
38192 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38194 beginUpdate : function(){
38195 this.updating = true;
38199 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38201 endUpdate : function(){
38202 this.updating = false;
38203 this.autoSizeTabs();
38207 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38209 autoSizeTabs : function(){
38210 var count = this.items.length;
38211 var vcount = count - this.hiddenCount;
38212 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38215 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38216 var availWidth = Math.floor(w / vcount);
38217 var b = this.stripBody;
38218 if(b.getWidth() > w){
38219 var tabs = this.items;
38220 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38221 if(availWidth < this.minTabWidth){
38222 /*if(!this.sleft){ // incomplete scrolling code
38223 this.createScrollButtons();
38226 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38229 if(this.currentTabWidth < this.preferredTabWidth){
38230 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38236 * Returns the number of tabs in this TabPanel.
38239 getCount : function(){
38240 return this.items.length;
38244 * Resizes all the tabs to the passed width
38245 * @param {Number} The new width
38247 setTabWidth : function(width){
38248 this.currentTabWidth = width;
38249 for(var i = 0, len = this.items.length; i < len; i++) {
38250 if(!this.items[i].isHidden()) {
38251 this.items[i].setWidth(width);
38257 * Destroys this TabPanel
38258 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38260 destroy : function(removeEl){
38261 Roo.EventManager.removeResizeListener(this.onResize, this);
38262 for(var i = 0, len = this.items.length; i < len; i++){
38263 this.items[i].purgeListeners();
38265 if(removeEl === true){
38266 this.el.update("");
38271 createStrip : function(container)
38273 var strip = document.createElement("nav");
38274 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38275 container.appendChild(strip);
38279 createStripList : function(strip)
38281 // div wrapper for retard IE
38282 // returns the "tr" element.
38283 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38284 //'<div class="x-tabs-strip-wrap">'+
38285 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38286 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38287 return strip.firstChild; //.firstChild.firstChild.firstChild;
38289 createBody : function(container)
38291 var body = document.createElement("div");
38292 Roo.id(body, "tab-body");
38293 //Roo.fly(body).addClass("x-tabs-body");
38294 Roo.fly(body).addClass("tab-content");
38295 container.appendChild(body);
38298 createItemBody :function(bodyEl, id){
38299 var body = Roo.getDom(id);
38301 body = document.createElement("div");
38304 //Roo.fly(body).addClass("x-tabs-item-body");
38305 Roo.fly(body).addClass("tab-pane");
38306 bodyEl.insertBefore(body, bodyEl.firstChild);
38310 createStripElements : function(stripEl, text, closable, tpl)
38312 var td = document.createElement("li"); // was td..
38315 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38318 stripEl.appendChild(td);
38320 td.className = "x-tabs-closable";
38321 if(!this.closeTpl){
38322 this.closeTpl = new Roo.Template(
38323 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38324 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38325 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38328 var el = this.closeTpl.overwrite(td, {"text": text});
38329 var close = el.getElementsByTagName("div")[0];
38330 var inner = el.getElementsByTagName("em")[0];
38331 return {"el": el, "close": close, "inner": inner};
38334 // not sure what this is..
38335 // if(!this.tabTpl){
38336 //this.tabTpl = new Roo.Template(
38337 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38338 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38340 // this.tabTpl = new Roo.Template(
38341 // '<a href="#">' +
38342 // '<span unselectable="on"' +
38343 // (this.disableTooltips ? '' : ' title="{text}"') +
38344 // ' >{text}</span></a>'
38350 var template = tpl || this.tabTpl || false;
38354 template = new Roo.Template(
38356 '<span unselectable="on"' +
38357 (this.disableTooltips ? '' : ' title="{text}"') +
38358 ' >{text}</span></a>'
38362 switch (typeof(template)) {
38366 template = new Roo.Template(template);
38372 var el = template.overwrite(td, {"text": text});
38374 var inner = el.getElementsByTagName("span")[0];
38376 return {"el": el, "inner": inner};
38384 * @class Roo.TabPanelItem
38385 * @extends Roo.util.Observable
38386 * Represents an individual item (tab plus body) in a TabPanel.
38387 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38388 * @param {String} id The id of this TabPanelItem
38389 * @param {String} text The text for the tab of this TabPanelItem
38390 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38392 Roo.bootstrap.panel.TabItem = function(config){
38394 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38395 * @type Roo.TabPanel
38397 this.tabPanel = config.panel;
38399 * The id for this TabPanelItem
38402 this.id = config.id;
38404 this.disabled = false;
38406 this.text = config.text;
38408 this.loaded = false;
38409 this.closable = config.closable;
38412 * The body element for this TabPanelItem.
38413 * @type Roo.Element
38415 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38416 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38417 this.bodyEl.setStyle("display", "block");
38418 this.bodyEl.setStyle("zoom", "1");
38419 //this.hideAction();
38421 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38423 this.el = Roo.get(els.el);
38424 this.inner = Roo.get(els.inner, true);
38425 this.textEl = Roo.get(this.el.dom.firstChild, true);
38426 this.pnode = Roo.get(els.el.parentNode, true);
38427 // this.el.on("mousedown", this.onTabMouseDown, this);
38428 this.el.on("click", this.onTabClick, this);
38430 if(config.closable){
38431 var c = Roo.get(els.close, true);
38432 c.dom.title = this.closeText;
38433 c.addClassOnOver("close-over");
38434 c.on("click", this.closeClick, this);
38440 * Fires when this tab becomes the active tab.
38441 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38442 * @param {Roo.TabPanelItem} this
38446 * @event beforeclose
38447 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38448 * @param {Roo.TabPanelItem} this
38449 * @param {Object} e Set cancel to true on this object to cancel the close.
38451 "beforeclose": true,
38454 * Fires when this tab is closed.
38455 * @param {Roo.TabPanelItem} this
38459 * @event deactivate
38460 * Fires when this tab is no longer the active tab.
38461 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38462 * @param {Roo.TabPanelItem} this
38464 "deactivate" : true
38466 this.hidden = false;
38468 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38471 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38473 purgeListeners : function(){
38474 Roo.util.Observable.prototype.purgeListeners.call(this);
38475 this.el.removeAllListeners();
38478 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38481 this.pnode.addClass("active");
38484 this.tabPanel.stripWrap.repaint();
38486 this.fireEvent("activate", this.tabPanel, this);
38490 * Returns true if this tab is the active tab.
38491 * @return {Boolean}
38493 isActive : function(){
38494 return this.tabPanel.getActiveTab() == this;
38498 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38501 this.pnode.removeClass("active");
38503 this.fireEvent("deactivate", this.tabPanel, this);
38506 hideAction : function(){
38507 this.bodyEl.hide();
38508 this.bodyEl.setStyle("position", "absolute");
38509 this.bodyEl.setLeft("-20000px");
38510 this.bodyEl.setTop("-20000px");
38513 showAction : function(){
38514 this.bodyEl.setStyle("position", "relative");
38515 this.bodyEl.setTop("");
38516 this.bodyEl.setLeft("");
38517 this.bodyEl.show();
38521 * Set the tooltip for the tab.
38522 * @param {String} tooltip The tab's tooltip
38524 setTooltip : function(text){
38525 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38526 this.textEl.dom.qtip = text;
38527 this.textEl.dom.removeAttribute('title');
38529 this.textEl.dom.title = text;
38533 onTabClick : function(e){
38534 e.preventDefault();
38535 this.tabPanel.activate(this.id);
38538 onTabMouseDown : function(e){
38539 e.preventDefault();
38540 this.tabPanel.activate(this.id);
38543 getWidth : function(){
38544 return this.inner.getWidth();
38547 setWidth : function(width){
38548 var iwidth = width - this.pnode.getPadding("lr");
38549 this.inner.setWidth(iwidth);
38550 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38551 this.pnode.setWidth(width);
38555 * Show or hide the tab
38556 * @param {Boolean} hidden True to hide or false to show.
38558 setHidden : function(hidden){
38559 this.hidden = hidden;
38560 this.pnode.setStyle("display", hidden ? "none" : "");
38564 * Returns true if this tab is "hidden"
38565 * @return {Boolean}
38567 isHidden : function(){
38568 return this.hidden;
38572 * Returns the text for this tab
38575 getText : function(){
38579 autoSize : function(){
38580 //this.el.beginMeasure();
38581 this.textEl.setWidth(1);
38583 * #2804 [new] Tabs in Roojs
38584 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38586 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38587 //this.el.endMeasure();
38591 * Sets the text for the tab (Note: this also sets the tooltip text)
38592 * @param {String} text The tab's text and tooltip
38594 setText : function(text){
38596 this.textEl.update(text);
38597 this.setTooltip(text);
38598 //if(!this.tabPanel.resizeTabs){
38599 // this.autoSize();
38603 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38605 activate : function(){
38606 this.tabPanel.activate(this.id);
38610 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38612 disable : function(){
38613 if(this.tabPanel.active != this){
38614 this.disabled = true;
38615 this.pnode.addClass("disabled");
38620 * Enables this TabPanelItem if it was previously disabled.
38622 enable : function(){
38623 this.disabled = false;
38624 this.pnode.removeClass("disabled");
38628 * Sets the content for this TabPanelItem.
38629 * @param {String} content The content
38630 * @param {Boolean} loadScripts true to look for and load scripts
38632 setContent : function(content, loadScripts){
38633 this.bodyEl.update(content, loadScripts);
38637 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38638 * @return {Roo.UpdateManager} The UpdateManager
38640 getUpdateManager : function(){
38641 return this.bodyEl.getUpdateManager();
38645 * Set a URL to be used to load the content for this TabPanelItem.
38646 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38647 * @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)
38648 * @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)
38649 * @return {Roo.UpdateManager} The UpdateManager
38651 setUrl : function(url, params, loadOnce){
38652 if(this.refreshDelegate){
38653 this.un('activate', this.refreshDelegate);
38655 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38656 this.on("activate", this.refreshDelegate);
38657 return this.bodyEl.getUpdateManager();
38661 _handleRefresh : function(url, params, loadOnce){
38662 if(!loadOnce || !this.loaded){
38663 var updater = this.bodyEl.getUpdateManager();
38664 updater.update(url, params, this._setLoaded.createDelegate(this));
38669 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38670 * Will fail silently if the setUrl method has not been called.
38671 * This does not activate the panel, just updates its content.
38673 refresh : function(){
38674 if(this.refreshDelegate){
38675 this.loaded = false;
38676 this.refreshDelegate();
38681 _setLoaded : function(){
38682 this.loaded = true;
38686 closeClick : function(e){
38689 this.fireEvent("beforeclose", this, o);
38690 if(o.cancel !== true){
38691 this.tabPanel.removeTab(this.id);
38695 * The text displayed in the tooltip for the close icon.
38698 closeText : "Close this tab"
38701 * This script refer to:
38702 * Title: International Telephone Input
38703 * Author: Jack O'Connor
38704 * Code version: v12.1.12
38705 * Availability: https://github.com/jackocnr/intl-tel-input.git
38708 Roo.bootstrap.PhoneInputData = function() {
38711 "Afghanistan (افغانستان)",
38716 "Albania (Shqipëri)",
38721 "Algeria (الجزائر)",
38746 "Antigua and Barbuda",
38756 "Armenia (Հայաստան)",
38772 "Austria (Österreich)",
38777 "Azerbaijan (Azərbaycan)",
38787 "Bahrain (البحرين)",
38792 "Bangladesh (বাংলাদেশ)",
38802 "Belarus (Беларусь)",
38807 "Belgium (België)",
38837 "Bosnia and Herzegovina (Босна и Херцеговина)",
38852 "British Indian Ocean Territory",
38857 "British Virgin Islands",
38867 "Bulgaria (България)",
38877 "Burundi (Uburundi)",
38882 "Cambodia (កម្ពុជា)",
38887 "Cameroon (Cameroun)",
38896 ["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"]
38899 "Cape Verde (Kabu Verdi)",
38904 "Caribbean Netherlands",
38915 "Central African Republic (République centrafricaine)",
38935 "Christmas Island",
38941 "Cocos (Keeling) Islands",
38952 "Comoros (جزر القمر)",
38957 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38962 "Congo (Republic) (Congo-Brazzaville)",
38982 "Croatia (Hrvatska)",
39003 "Czech Republic (Česká republika)",
39008 "Denmark (Danmark)",
39023 "Dominican Republic (República Dominicana)",
39027 ["809", "829", "849"]
39045 "Equatorial Guinea (Guinea Ecuatorial)",
39065 "Falkland Islands (Islas Malvinas)",
39070 "Faroe Islands (Føroyar)",
39091 "French Guiana (Guyane française)",
39096 "French Polynesia (Polynésie française)",
39111 "Georgia (საქართველო)",
39116 "Germany (Deutschland)",
39136 "Greenland (Kalaallit Nunaat)",
39173 "Guinea-Bissau (Guiné Bissau)",
39198 "Hungary (Magyarország)",
39203 "Iceland (Ísland)",
39223 "Iraq (العراق)",
39239 "Israel (ישראל)",
39266 "Jordan (الأردن)",
39271 "Kazakhstan (Казахстан)",
39292 "Kuwait (الكويت)",
39297 "Kyrgyzstan (Кыргызстан)",
39307 "Latvia (Latvija)",
39312 "Lebanon (لبنان)",
39327 "Libya (ليبيا)",
39337 "Lithuania (Lietuva)",
39352 "Macedonia (FYROM) (Македонија)",
39357 "Madagascar (Madagasikara)",
39387 "Marshall Islands",
39397 "Mauritania (موريتانيا)",
39402 "Mauritius (Moris)",
39423 "Moldova (Republica Moldova)",
39433 "Mongolia (Монгол)",
39438 "Montenegro (Crna Gora)",
39448 "Morocco (المغرب)",
39454 "Mozambique (Moçambique)",
39459 "Myanmar (Burma) (မြန်မာ)",
39464 "Namibia (Namibië)",
39479 "Netherlands (Nederland)",
39484 "New Caledonia (Nouvelle-Calédonie)",
39519 "North Korea (조선 민주주의 인민 공화국)",
39524 "Northern Mariana Islands",
39540 "Pakistan (پاکستان)",
39550 "Palestine (فلسطين)",
39560 "Papua New Guinea",
39602 "Réunion (La Réunion)",
39608 "Romania (România)",
39624 "Saint Barthélemy",
39635 "Saint Kitts and Nevis",
39645 "Saint Martin (Saint-Martin (partie française))",
39651 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39656 "Saint Vincent and the Grenadines",
39671 "São Tomé and Príncipe (São Tomé e Príncipe)",
39676 "Saudi Arabia (المملكة العربية السعودية)",
39681 "Senegal (Sénégal)",
39711 "Slovakia (Slovensko)",
39716 "Slovenia (Slovenija)",
39726 "Somalia (Soomaaliya)",
39736 "South Korea (대한민국)",
39741 "South Sudan (جنوب السودان)",
39751 "Sri Lanka (ශ්රී ලංකාව)",
39756 "Sudan (السودان)",
39766 "Svalbard and Jan Mayen",
39777 "Sweden (Sverige)",
39782 "Switzerland (Schweiz)",
39787 "Syria (سوريا)",
39832 "Trinidad and Tobago",
39837 "Tunisia (تونس)",
39842 "Turkey (Türkiye)",
39852 "Turks and Caicos Islands",
39862 "U.S. Virgin Islands",
39872 "Ukraine (Україна)",
39877 "United Arab Emirates (الإمارات العربية المتحدة)",
39899 "Uzbekistan (Oʻzbekiston)",
39909 "Vatican City (Città del Vaticano)",
39920 "Vietnam (Việt Nam)",
39925 "Wallis and Futuna (Wallis-et-Futuna)",
39930 "Western Sahara (الصحراء الغربية)",
39936 "Yemen (اليمن)",
39960 * This script refer to:
39961 * Title: International Telephone Input
39962 * Author: Jack O'Connor
39963 * Code version: v12.1.12
39964 * Availability: https://github.com/jackocnr/intl-tel-input.git
39968 * @class Roo.bootstrap.PhoneInput
39969 * @extends Roo.bootstrap.TriggerField
39970 * An input with International dial-code selection
39972 * @cfg {String} defaultDialCode default '+852'
39973 * @cfg {Array} preferedCountries default []
39976 * Create a new PhoneInput.
39977 * @param {Object} config Configuration options
39980 Roo.bootstrap.PhoneInput = function(config) {
39981 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39984 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39986 listWidth: undefined,
39988 selectedClass: 'active',
39990 invalidClass : "has-warning",
39992 validClass: 'has-success',
39994 allowed: '0123456789',
39999 * @cfg {String} defaultDialCode The default dial code when initializing the input
40001 defaultDialCode: '+852',
40004 * @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
40006 preferedCountries: false,
40008 getAutoCreate : function()
40010 var data = Roo.bootstrap.PhoneInputData();
40011 var align = this.labelAlign || this.parentLabelAlign();
40014 this.allCountries = [];
40015 this.dialCodeMapping = [];
40017 for (var i = 0; i < data.length; i++) {
40019 this.allCountries[i] = {
40023 priority: c[3] || 0,
40024 areaCodes: c[4] || null
40026 this.dialCodeMapping[c[2]] = {
40029 priority: c[3] || 0,
40030 areaCodes: c[4] || null
40042 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40043 maxlength: this.max_length,
40044 cls : 'form-control tel-input',
40045 autocomplete: 'new-password'
40048 var hiddenInput = {
40051 cls: 'hidden-tel-input'
40055 hiddenInput.name = this.name;
40058 if (this.disabled) {
40059 input.disabled = true;
40062 var flag_container = {
40079 cls: this.hasFeedback ? 'has-feedback' : '',
40085 cls: 'dial-code-holder',
40092 cls: 'roo-select2-container input-group',
40099 if (this.fieldLabel.length) {
40102 tooltip: 'This field is required'
40108 cls: 'control-label',
40114 html: this.fieldLabel
40117 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40123 if(this.indicatorpos == 'right') {
40124 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40131 if(align == 'left') {
40139 if(this.labelWidth > 12){
40140 label.style = "width: " + this.labelWidth + 'px';
40142 if(this.labelWidth < 13 && this.labelmd == 0){
40143 this.labelmd = this.labelWidth;
40145 if(this.labellg > 0){
40146 label.cls += ' col-lg-' + this.labellg;
40147 input.cls += ' col-lg-' + (12 - this.labellg);
40149 if(this.labelmd > 0){
40150 label.cls += ' col-md-' + this.labelmd;
40151 container.cls += ' col-md-' + (12 - this.labelmd);
40153 if(this.labelsm > 0){
40154 label.cls += ' col-sm-' + this.labelsm;
40155 container.cls += ' col-sm-' + (12 - this.labelsm);
40157 if(this.labelxs > 0){
40158 label.cls += ' col-xs-' + this.labelxs;
40159 container.cls += ' col-xs-' + (12 - this.labelxs);
40169 var settings = this;
40171 ['xs','sm','md','lg'].map(function(size){
40172 if (settings[size]) {
40173 cfg.cls += ' col-' + size + '-' + settings[size];
40177 this.store = new Roo.data.Store({
40178 proxy : new Roo.data.MemoryProxy({}),
40179 reader : new Roo.data.JsonReader({
40190 'name' : 'dialCode',
40194 'name' : 'priority',
40198 'name' : 'areaCodes',
40205 if(!this.preferedCountries) {
40206 this.preferedCountries = [
40213 var p = this.preferedCountries.reverse();
40216 for (var i = 0; i < p.length; i++) {
40217 for (var j = 0; j < this.allCountries.length; j++) {
40218 if(this.allCountries[j].iso2 == p[i]) {
40219 var t = this.allCountries[j];
40220 this.allCountries.splice(j,1);
40221 this.allCountries.unshift(t);
40227 this.store.proxy.data = {
40229 data: this.allCountries
40235 initEvents : function()
40238 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40240 this.indicator = this.indicatorEl();
40241 this.flag = this.flagEl();
40242 this.dialCodeHolder = this.dialCodeHolderEl();
40244 this.trigger = this.el.select('div.flag-box',true).first();
40245 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40250 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40251 _this.list.setWidth(lw);
40254 this.list.on('mouseover', this.onViewOver, this);
40255 this.list.on('mousemove', this.onViewMove, this);
40256 this.inputEl().on("keyup", this.onKeyUp, this);
40257 this.inputEl().on("keypress", this.onKeyPress, this);
40259 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40261 this.view = new Roo.View(this.list, this.tpl, {
40262 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40265 this.view.on('click', this.onViewClick, this);
40266 this.setValue(this.defaultDialCode);
40269 onTriggerClick : function(e)
40271 Roo.log('trigger click');
40276 if(this.isExpanded()){
40278 this.hasFocus = false;
40280 this.store.load({});
40281 this.hasFocus = true;
40286 isExpanded : function()
40288 return this.list.isVisible();
40291 collapse : function()
40293 if(!this.isExpanded()){
40297 Roo.get(document).un('mousedown', this.collapseIf, this);
40298 Roo.get(document).un('mousewheel', this.collapseIf, this);
40299 this.fireEvent('collapse', this);
40303 expand : function()
40307 if(this.isExpanded() || !this.hasFocus){
40311 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40312 this.list.setWidth(lw);
40315 this.restrictHeight();
40317 Roo.get(document).on('mousedown', this.collapseIf, this);
40318 Roo.get(document).on('mousewheel', this.collapseIf, this);
40320 this.fireEvent('expand', this);
40323 restrictHeight : function()
40325 this.list.alignTo(this.inputEl(), this.listAlign);
40326 this.list.alignTo(this.inputEl(), this.listAlign);
40329 onViewOver : function(e, t)
40331 if(this.inKeyMode){
40334 var item = this.view.findItemFromChild(t);
40337 var index = this.view.indexOf(item);
40338 this.select(index, false);
40343 onViewClick : function(view, doFocus, el, e)
40345 var index = this.view.getSelectedIndexes()[0];
40347 var r = this.store.getAt(index);
40350 this.onSelect(r, index);
40352 if(doFocus !== false && !this.blockFocus){
40353 this.inputEl().focus();
40357 onViewMove : function(e, t)
40359 this.inKeyMode = false;
40362 select : function(index, scrollIntoView)
40364 this.selectedIndex = index;
40365 this.view.select(index);
40366 if(scrollIntoView !== false){
40367 var el = this.view.getNode(index);
40369 this.list.scrollChildIntoView(el, false);
40374 createList : function()
40376 this.list = Roo.get(document.body).createChild({
40378 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40379 style: 'display:none'
40382 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40385 collapseIf : function(e)
40387 var in_combo = e.within(this.el);
40388 var in_list = e.within(this.list);
40389 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40391 if (in_combo || in_list || is_list) {
40397 onSelect : function(record, index)
40399 if(this.fireEvent('beforeselect', this, record, index) !== false){
40401 this.setFlagClass(record.data.iso2);
40402 this.setDialCode(record.data.dialCode);
40403 this.hasFocus = false;
40405 this.fireEvent('select', this, record, index);
40409 flagEl : function()
40411 var flag = this.el.select('div.flag',true).first();
40418 dialCodeHolderEl : function()
40420 var d = this.el.select('input.dial-code-holder',true).first();
40427 setDialCode : function(v)
40429 this.dialCodeHolder.dom.value = '+'+v;
40432 setFlagClass : function(n)
40434 this.flag.dom.className = 'flag '+n;
40437 getValue : function()
40439 var v = this.inputEl().getValue();
40440 if(this.dialCodeHolder) {
40441 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40446 setValue : function(v)
40448 var d = this.getDialCode(v);
40450 //invalid dial code
40451 if(v.length == 0 || !d || d.length == 0) {
40453 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40454 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40460 this.setFlagClass(this.dialCodeMapping[d].iso2);
40461 this.setDialCode(d);
40462 this.inputEl().dom.value = v.replace('+'+d,'');
40463 this.hiddenEl().dom.value = this.getValue();
40468 getDialCode : function(v)
40472 if (v.length == 0) {
40473 return this.dialCodeHolder.dom.value;
40477 if (v.charAt(0) != "+") {
40480 var numericChars = "";
40481 for (var i = 1; i < v.length; i++) {
40482 var c = v.charAt(i);
40485 if (this.dialCodeMapping[numericChars]) {
40486 dialCode = v.substr(1, i);
40488 if (numericChars.length == 4) {
40498 this.setValue(this.defaultDialCode);
40502 hiddenEl : function()
40504 return this.el.select('input.hidden-tel-input',true).first();
40507 // after setting val
40508 onKeyUp : function(e){
40509 this.setValue(this.getValue());
40512 onKeyPress : function(e){
40513 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40520 * @class Roo.bootstrap.MoneyField
40521 * @extends Roo.bootstrap.ComboBox
40522 * Bootstrap MoneyField class
40525 * Create a new MoneyField.
40526 * @param {Object} config Configuration options
40529 Roo.bootstrap.MoneyField = function(config) {
40531 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40535 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40538 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40540 allowDecimals : true,
40542 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40544 decimalSeparator : ".",
40546 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40548 decimalPrecision : 0,
40550 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40552 allowNegative : true,
40554 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40558 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40560 minValue : Number.NEGATIVE_INFINITY,
40562 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40564 maxValue : Number.MAX_VALUE,
40566 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40568 minText : "The minimum value for this field is {0}",
40570 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40572 maxText : "The maximum value for this field is {0}",
40574 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40575 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40577 nanText : "{0} is not a valid number",
40579 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40583 * @cfg {String} defaults currency of the MoneyField
40584 * value should be in lkey
40586 defaultCurrency : false,
40588 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40590 thousandsDelimiter : false,
40592 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40603 getAutoCreate : function()
40605 var align = this.labelAlign || this.parentLabelAlign();
40617 cls : 'form-control roo-money-amount-input',
40618 autocomplete: 'new-password'
40621 var hiddenInput = {
40625 cls: 'hidden-number-input'
40628 if(this.max_length) {
40629 input.maxlength = this.max_length;
40633 hiddenInput.name = this.name;
40636 if (this.disabled) {
40637 input.disabled = true;
40640 var clg = 12 - this.inputlg;
40641 var cmd = 12 - this.inputmd;
40642 var csm = 12 - this.inputsm;
40643 var cxs = 12 - this.inputxs;
40647 cls : 'row roo-money-field',
40651 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40655 cls: 'roo-select2-container input-group',
40659 cls : 'form-control roo-money-currency-input',
40660 autocomplete: 'new-password',
40662 name : this.currencyName
40666 cls : 'input-group-addon',
40680 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40684 cls: this.hasFeedback ? 'has-feedback' : '',
40695 if (this.fieldLabel.length) {
40698 tooltip: 'This field is required'
40704 cls: 'control-label',
40710 html: this.fieldLabel
40713 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40719 if(this.indicatorpos == 'right') {
40720 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40727 if(align == 'left') {
40735 if(this.labelWidth > 12){
40736 label.style = "width: " + this.labelWidth + 'px';
40738 if(this.labelWidth < 13 && this.labelmd == 0){
40739 this.labelmd = this.labelWidth;
40741 if(this.labellg > 0){
40742 label.cls += ' col-lg-' + this.labellg;
40743 input.cls += ' col-lg-' + (12 - this.labellg);
40745 if(this.labelmd > 0){
40746 label.cls += ' col-md-' + this.labelmd;
40747 container.cls += ' col-md-' + (12 - this.labelmd);
40749 if(this.labelsm > 0){
40750 label.cls += ' col-sm-' + this.labelsm;
40751 container.cls += ' col-sm-' + (12 - this.labelsm);
40753 if(this.labelxs > 0){
40754 label.cls += ' col-xs-' + this.labelxs;
40755 container.cls += ' col-xs-' + (12 - this.labelxs);
40766 var settings = this;
40768 ['xs','sm','md','lg'].map(function(size){
40769 if (settings[size]) {
40770 cfg.cls += ' col-' + size + '-' + settings[size];
40777 initEvents : function()
40779 this.indicator = this.indicatorEl();
40781 this.initCurrencyEvent();
40783 this.initNumberEvent();
40786 initCurrencyEvent : function()
40789 throw "can not find store for combo";
40792 this.store = Roo.factory(this.store, Roo.data);
40793 this.store.parent = this;
40797 this.triggerEl = this.el.select('.input-group-addon', true).first();
40799 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40804 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40805 _this.list.setWidth(lw);
40808 this.list.on('mouseover', this.onViewOver, this);
40809 this.list.on('mousemove', this.onViewMove, this);
40810 this.list.on('scroll', this.onViewScroll, this);
40813 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40816 this.view = new Roo.View(this.list, this.tpl, {
40817 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40820 this.view.on('click', this.onViewClick, this);
40822 this.store.on('beforeload', this.onBeforeLoad, this);
40823 this.store.on('load', this.onLoad, this);
40824 this.store.on('loadexception', this.onLoadException, this);
40826 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40827 "up" : function(e){
40828 this.inKeyMode = true;
40832 "down" : function(e){
40833 if(!this.isExpanded()){
40834 this.onTriggerClick();
40836 this.inKeyMode = true;
40841 "enter" : function(e){
40844 if(this.fireEvent("specialkey", this, e)){
40845 this.onViewClick(false);
40851 "esc" : function(e){
40855 "tab" : function(e){
40858 if(this.fireEvent("specialkey", this, e)){
40859 this.onViewClick(false);
40867 doRelay : function(foo, bar, hname){
40868 if(hname == 'down' || this.scope.isExpanded()){
40869 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40877 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40881 initNumberEvent : function(e)
40883 this.inputEl().on("keydown" , this.fireKey, this);
40884 this.inputEl().on("focus", this.onFocus, this);
40885 this.inputEl().on("blur", this.onBlur, this);
40887 this.inputEl().relayEvent('keyup', this);
40889 if(this.indicator){
40890 this.indicator.addClass('invisible');
40893 this.originalValue = this.getValue();
40895 if(this.validationEvent == 'keyup'){
40896 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40897 this.inputEl().on('keyup', this.filterValidation, this);
40899 else if(this.validationEvent !== false){
40900 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40903 if(this.selectOnFocus){
40904 this.on("focus", this.preFocus, this);
40907 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40908 this.inputEl().on("keypress", this.filterKeys, this);
40910 this.inputEl().relayEvent('keypress', this);
40913 var allowed = "0123456789";
40915 if(this.allowDecimals){
40916 allowed += this.decimalSeparator;
40919 if(this.allowNegative){
40923 if(this.thousandsDelimiter) {
40927 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40929 var keyPress = function(e){
40931 var k = e.getKey();
40933 var c = e.getCharCode();
40936 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40937 allowed.indexOf(String.fromCharCode(c)) === -1
40943 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40947 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40952 this.inputEl().on("keypress", keyPress, this);
40956 onTriggerClick : function(e)
40963 this.loadNext = false;
40965 if(this.isExpanded()){
40970 this.hasFocus = true;
40972 if(this.triggerAction == 'all') {
40973 this.doQuery(this.allQuery, true);
40977 this.doQuery(this.getRawValue());
40980 getCurrency : function()
40982 var v = this.currencyEl().getValue();
40987 restrictHeight : function()
40989 this.list.alignTo(this.currencyEl(), this.listAlign);
40990 this.list.alignTo(this.currencyEl(), this.listAlign);
40993 onViewClick : function(view, doFocus, el, e)
40995 var index = this.view.getSelectedIndexes()[0];
40997 var r = this.store.getAt(index);
41000 this.onSelect(r, index);
41004 onSelect : function(record, index){
41006 if(this.fireEvent('beforeselect', this, record, index) !== false){
41008 this.setFromCurrencyData(index > -1 ? record.data : false);
41012 this.fireEvent('select', this, record, index);
41016 setFromCurrencyData : function(o)
41020 this.lastCurrency = o;
41022 if (this.currencyField) {
41023 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41025 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
41028 this.lastSelectionText = currency;
41030 //setting default currency
41031 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41032 this.setCurrency(this.defaultCurrency);
41036 this.setCurrency(currency);
41039 setFromData : function(o)
41043 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41045 this.setFromCurrencyData(c);
41050 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41052 Roo.log('no value set for '+ (this.name ? this.name : this.id));
41055 this.setValue(value);
41059 setCurrency : function(v)
41061 this.currencyValue = v;
41064 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41069 setValue : function(v)
41071 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41077 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41079 this.inputEl().dom.value = (v == '') ? '' :
41080 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41082 if(!this.allowZero && v === '0') {
41083 this.hiddenEl().dom.value = '';
41084 this.inputEl().dom.value = '';
41091 getRawValue : function()
41093 var v = this.inputEl().getValue();
41098 getValue : function()
41100 return this.fixPrecision(this.parseValue(this.getRawValue()));
41103 parseValue : function(value)
41105 if(this.thousandsDelimiter) {
41107 r = new RegExp(",", "g");
41108 value = value.replace(r, "");
41111 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41112 return isNaN(value) ? '' : value;
41116 fixPrecision : function(value)
41118 if(this.thousandsDelimiter) {
41120 r = new RegExp(",", "g");
41121 value = value.replace(r, "");
41124 var nan = isNaN(value);
41126 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41127 return nan ? '' : value;
41129 return parseFloat(value).toFixed(this.decimalPrecision);
41132 decimalPrecisionFcn : function(v)
41134 return Math.floor(v);
41137 validateValue : function(value)
41139 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41143 var num = this.parseValue(value);
41146 this.markInvalid(String.format(this.nanText, value));
41150 if(num < this.minValue){
41151 this.markInvalid(String.format(this.minText, this.minValue));
41155 if(num > this.maxValue){
41156 this.markInvalid(String.format(this.maxText, this.maxValue));
41163 validate : function()
41165 if(this.disabled || this.allowBlank){
41170 var currency = this.getCurrency();
41172 if(this.validateValue(this.getRawValue()) && currency.length){
41177 this.markInvalid();
41181 getName: function()
41186 beforeBlur : function()
41192 var v = this.parseValue(this.getRawValue());
41199 onBlur : function()
41203 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41204 //this.el.removeClass(this.focusClass);
41207 this.hasFocus = false;
41209 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41213 var v = this.getValue();
41215 if(String(v) !== String(this.startValue)){
41216 this.fireEvent('change', this, v, this.startValue);
41219 this.fireEvent("blur", this);
41222 inputEl : function()
41224 return this.el.select('.roo-money-amount-input', true).first();
41227 currencyEl : function()
41229 return this.el.select('.roo-money-currency-input', true).first();
41232 hiddenEl : function()
41234 return this.el.select('input.hidden-number-input',true).first();