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 fa
593 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
594 * @cfg {String} badge text for badge
595 * @cfg {String} theme (default|glow)
596 * @cfg {Boolean} inverse dark themed version
597 * @cfg {Boolean} toggle is it a slidy toggle button
598 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
599 * @cfg {String} ontext text for on slidy toggle state
600 * @cfg {String} offtext text for off slidy toggle state
601 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
602 * @cfg {Boolean} removeClass remove the standard class..
603 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
606 * Create a new button
607 * @param {Object} config The config object
611 Roo.bootstrap.Button = function(config){
612 Roo.bootstrap.Button.superclass.constructor.call(this, config);
613 this.weightClass = ["btn-default btn-outline-secondary",
625 * When a butotn is pressed
626 * @param {Roo.bootstrap.Button} btn
627 * @param {Roo.EventObject} e
632 * After the button has been toggles
633 * @param {Roo.bootstrap.Button} btn
634 * @param {Roo.EventObject} e
635 * @param {boolean} pressed (also available as button.pressed)
641 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
662 preventDefault: true,
670 getAutoCreate : function(){
678 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
679 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
684 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
686 if (this.toggle == true) {
689 cls: 'slider-frame roo-button',
694 'data-off-text':'OFF',
695 cls: 'slider-button',
701 if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
702 cfg.cls += ' '+this.weight;
711 cfg["aria-hidden"] = true;
713 cfg.html = "×";
719 if (this.theme==='default') {
720 cfg.cls = 'btn roo-button';
722 //if (this.parentType != 'Navbar') {
723 this.weight = this.weight.length ? this.weight : 'default';
725 if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
727 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
728 var weight = this.weight == 'default' ? 'secondary' : this.weight;
729 cfg.cls += ' btn-' + outline + weight;
730 if (this.weight == 'default') {
732 cfg.cls += ' btn-' + this.weight;
735 } else if (this.theme==='glow') {
738 cfg.cls = 'btn-glow roo-button';
740 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
742 cfg.cls += ' ' + this.weight;
748 this.cls += ' inverse';
752 if (this.active || this.pressed === true) {
753 cfg.cls += ' active';
757 cfg.disabled = 'disabled';
761 Roo.log('changing to ul' );
763 this.glyphicon = 'caret';
764 if (Roo.bootstrap.version == 4) {
765 this.fa = 'caret-down';
770 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
772 //gsRoo.log(this.parentType);
773 if (this.parentType === 'Navbar' && !this.parent().bar) {
774 Roo.log('changing to li?');
783 href : this.href || '#'
786 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
787 cfg.cls += ' dropdown';
794 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
796 if (this.glyphicon) {
797 cfg.html = ' ' + cfg.html;
802 cls: 'glyphicon glyphicon-' + this.glyphicon
807 cfg.html = ' ' + cfg.html;
812 cls: 'fa fas fa-' + this.fa
822 // cfg.cls='btn roo-button';
826 var value = cfg.html;
831 cls: 'glyphicon glyphicon-' + this.glyphicon,
838 cls: 'fa fas fa-' + this.fa,
843 var bw = this.badge_weight.length ? this.badge_weight :
844 (this.weight.length ? this.weight : 'secondary');
845 bw = bw == 'default' ? 'secondary' : bw;
851 cls: 'badge badge-' + bw,
860 cfg.cls += ' dropdown';
861 cfg.html = typeof(cfg.html) != 'undefined' ?
862 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
865 if (cfg.tag !== 'a' && this.href !== '') {
866 throw "Tag must be a to set href.";
867 } else if (this.href.length > 0) {
868 cfg.href = this.href;
871 if(this.removeClass){
876 cfg.target = this.target;
881 initEvents: function() {
882 // Roo.log('init events?');
883 // Roo.log(this.el.dom);
886 if (typeof (this.menu) != 'undefined') {
887 this.menu.parentType = this.xtype;
888 this.menu.triggerEl = this.el;
889 this.addxtype(Roo.apply({}, this.menu));
893 if (this.el.hasClass('roo-button')) {
894 this.el.on('click', this.onClick, this);
896 this.el.select('.roo-button').on('click', this.onClick, this);
899 if(this.removeClass){
900 this.el.on('click', this.onClick, this);
903 this.el.enableDisplayMode();
906 onClick : function(e)
912 Roo.log('button on click ');
913 if(this.preventDefault){
917 if (this.pressed === true || this.pressed === false) {
918 this.toggleActive(e);
922 this.fireEvent('click', this, e);
926 * Enables this button
930 this.disabled = false;
931 this.el.removeClass('disabled');
935 * Disable this button
939 this.disabled = true;
940 this.el.addClass('disabled');
943 * sets the active state on/off,
944 * @param {Boolean} state (optional) Force a particular state
946 setActive : function(v) {
948 this.el[v ? 'addClass' : 'removeClass']('active');
952 * toggles the current active state
954 toggleActive : function(e)
956 this.setActive(!this.pressed);
957 this.fireEvent('toggle', this, e, !this.pressed);
960 * get the current active state
961 * @return {boolean} true if it's active
963 isActive : function()
965 return this.el.hasClass('active');
968 * set the text of the first selected button
970 setText : function(str)
972 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
975 * get the text of the first selected button
979 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
982 setWeight : function(str)
984 this.el.removeClass(this.weightClass);
986 var outline = this.outline ? 'outline-' : '';
987 if (str == 'default') {
988 this.el.addClass('btn-default btn-outline-secondary');
991 this.el.addClass('btn-' + outline + str);
1005 * @class Roo.bootstrap.Column
1006 * @extends Roo.bootstrap.Component
1007 * Bootstrap Column class
1008 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1009 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1010 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1011 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1012 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1013 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1014 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1015 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1018 * @cfg {Boolean} hidden (true|false) hide the element
1019 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1020 * @cfg {String} fa (ban|check|...) font awesome icon
1021 * @cfg {Number} fasize (1|2|....) font awsome size
1023 * @cfg {String} icon (info-sign|check|...) glyphicon name
1025 * @cfg {String} html content of column.
1028 * Create a new Column
1029 * @param {Object} config The config object
1032 Roo.bootstrap.Column = function(config){
1033 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1036 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1054 getAutoCreate : function(){
1055 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1063 ['xs','sm','md','lg'].map(function(size){
1064 //Roo.log( size + ':' + settings[size]);
1066 if (settings[size+'off'] !== false) {
1067 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1070 if (settings[size] === false) {
1074 if (!settings[size]) { // 0 = hidden
1075 cfg.cls += ' hidden-' + size;
1078 cfg.cls += ' col-' + size + '-' + settings[size];
1083 cfg.cls += ' hidden';
1086 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1087 cfg.cls +=' alert alert-' + this.alert;
1091 if (this.html.length) {
1092 cfg.html = this.html;
1096 if (this.fasize > 1) {
1097 fasize = ' fa-' + this.fasize + 'x';
1099 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1104 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1123 * @class Roo.bootstrap.Container
1124 * @extends Roo.bootstrap.Component
1125 * Bootstrap Container class
1126 * @cfg {Boolean} jumbotron is it a jumbotron element
1127 * @cfg {String} html content of element
1128 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1129 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1130 * @cfg {String} header content of header (for panel)
1131 * @cfg {String} footer content of footer (for panel)
1132 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1133 * @cfg {String} tag (header|aside|section) type of HTML tag.
1134 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1135 * @cfg {String} fa font awesome icon
1136 * @cfg {String} icon (info-sign|check|...) glyphicon name
1137 * @cfg {Boolean} hidden (true|false) hide the element
1138 * @cfg {Boolean} expandable (true|false) default false
1139 * @cfg {Boolean} expanded (true|false) default true
1140 * @cfg {String} rheader contet on the right of header
1141 * @cfg {Boolean} clickable (true|false) default false
1145 * Create a new Container
1146 * @param {Object} config The config object
1149 Roo.bootstrap.Container = function(config){
1150 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1156 * After the panel has been expand
1158 * @param {Roo.bootstrap.Container} this
1163 * After the panel has been collapsed
1165 * @param {Roo.bootstrap.Container} this
1170 * When a element is chick
1171 * @param {Roo.bootstrap.Container} this
1172 * @param {Roo.EventObject} e
1178 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1196 getChildContainer : function() {
1202 if (this.panel.length) {
1203 return this.el.select('.panel-body',true).first();
1210 getAutoCreate : function(){
1213 tag : this.tag || 'div',
1217 if (this.jumbotron) {
1218 cfg.cls = 'jumbotron';
1223 // - this is applied by the parent..
1225 // cfg.cls = this.cls + '';
1228 if (this.sticky.length) {
1230 var bd = Roo.get(document.body);
1231 if (!bd.hasClass('bootstrap-sticky')) {
1232 bd.addClass('bootstrap-sticky');
1233 Roo.select('html',true).setStyle('height', '100%');
1236 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1240 if (this.well.length) {
1241 switch (this.well) {
1244 cfg.cls +=' well well-' +this.well;
1253 cfg.cls += ' hidden';
1257 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1258 cfg.cls +=' alert alert-' + this.alert;
1263 if (this.panel.length) {
1264 cfg.cls += ' panel panel-' + this.panel;
1266 if (this.header.length) {
1270 if(this.expandable){
1272 cfg.cls = cfg.cls + ' expandable';
1276 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1284 cls : 'panel-title',
1285 html : (this.expandable ? ' ' : '') + this.header
1289 cls: 'panel-header-right',
1295 cls : 'panel-heading',
1296 style : this.expandable ? 'cursor: pointer' : '',
1304 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1309 if (this.footer.length) {
1311 cls : 'panel-footer',
1320 body.html = this.html || cfg.html;
1321 // prefix with the icons..
1323 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1326 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1331 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1332 cfg.cls = 'container';
1338 initEvents: function()
1340 if(this.expandable){
1341 var headerEl = this.headerEl();
1344 headerEl.on('click', this.onToggleClick, this);
1349 this.el.on('click', this.onClick, this);
1354 onToggleClick : function()
1356 var headerEl = this.headerEl();
1372 if(this.fireEvent('expand', this)) {
1374 this.expanded = true;
1376 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1378 this.el.select('.panel-body',true).first().removeClass('hide');
1380 var toggleEl = this.toggleEl();
1386 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1391 collapse : function()
1393 if(this.fireEvent('collapse', this)) {
1395 this.expanded = false;
1397 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1398 this.el.select('.panel-body',true).first().addClass('hide');
1400 var toggleEl = this.toggleEl();
1406 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1410 toggleEl : function()
1412 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1416 return this.el.select('.panel-heading .fa',true).first();
1419 headerEl : function()
1421 if(!this.el || !this.panel.length || !this.header.length){
1425 return this.el.select('.panel-heading',true).first()
1430 if(!this.el || !this.panel.length){
1434 return this.el.select('.panel-body',true).first()
1437 titleEl : function()
1439 if(!this.el || !this.panel.length || !this.header.length){
1443 return this.el.select('.panel-title',true).first();
1446 setTitle : function(v)
1448 var titleEl = this.titleEl();
1454 titleEl.dom.innerHTML = v;
1457 getTitle : function()
1460 var titleEl = this.titleEl();
1466 return titleEl.dom.innerHTML;
1469 setRightTitle : function(v)
1471 var t = this.el.select('.panel-header-right',true).first();
1477 t.dom.innerHTML = v;
1480 onClick : function(e)
1484 this.fireEvent('click', this, e);
1497 * @class Roo.bootstrap.Img
1498 * @extends Roo.bootstrap.Component
1499 * Bootstrap Img class
1500 * @cfg {Boolean} imgResponsive false | true
1501 * @cfg {String} border rounded | circle | thumbnail
1502 * @cfg {String} src image source
1503 * @cfg {String} alt image alternative text
1504 * @cfg {String} href a tag href
1505 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1506 * @cfg {String} xsUrl xs image source
1507 * @cfg {String} smUrl sm image source
1508 * @cfg {String} mdUrl md image source
1509 * @cfg {String} lgUrl lg image source
1512 * Create a new Input
1513 * @param {Object} config The config object
1516 Roo.bootstrap.Img = function(config){
1517 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1523 * The img click event for the img.
1524 * @param {Roo.EventObject} e
1530 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1532 imgResponsive: true,
1542 getAutoCreate : function()
1544 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1545 return this.createSingleImg();
1550 cls: 'roo-image-responsive-group',
1555 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1557 if(!_this[size + 'Url']){
1563 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1564 html: _this.html || cfg.html,
1565 src: _this[size + 'Url']
1568 img.cls += ' roo-image-responsive-' + size;
1570 var s = ['xs', 'sm', 'md', 'lg'];
1572 s.splice(s.indexOf(size), 1);
1574 Roo.each(s, function(ss){
1575 img.cls += ' hidden-' + ss;
1578 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1579 cfg.cls += ' img-' + _this.border;
1583 cfg.alt = _this.alt;
1596 a.target = _this.target;
1600 cfg.cn.push((_this.href) ? a : img);
1607 createSingleImg : function()
1611 cls: (this.imgResponsive) ? 'img-responsive' : '',
1613 src : 'about:blank' // just incase src get's set to undefined?!?
1616 cfg.html = this.html || cfg.html;
1618 cfg.src = this.src || cfg.src;
1620 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1621 cfg.cls += ' img-' + this.border;
1638 a.target = this.target;
1643 return (this.href) ? a : cfg;
1646 initEvents: function()
1649 this.el.on('click', this.onClick, this);
1654 onClick : function(e)
1656 Roo.log('img onclick');
1657 this.fireEvent('click', this, e);
1660 * Sets the url of the image - used to update it
1661 * @param {String} url the url of the image
1664 setSrc : function(url)
1668 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1669 this.el.dom.src = url;
1673 this.el.select('img', true).first().dom.src = url;
1689 * @class Roo.bootstrap.Link
1690 * @extends Roo.bootstrap.Component
1691 * Bootstrap Link Class
1692 * @cfg {String} alt image alternative text
1693 * @cfg {String} href a tag href
1694 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1695 * @cfg {String} html the content of the link.
1696 * @cfg {String} anchor name for the anchor link
1697 * @cfg {String} fa - favicon
1699 * @cfg {Boolean} preventDefault (true | false) default false
1703 * Create a new Input
1704 * @param {Object} config The config object
1707 Roo.bootstrap.Link = function(config){
1708 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1714 * The img click event for the img.
1715 * @param {Roo.EventObject} e
1721 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1725 preventDefault: false,
1731 getAutoCreate : function()
1733 var html = this.html || '';
1735 if (this.fa !== false) {
1736 html = '<i class="fa fa-' + this.fa + '"></i>';
1741 // anchor's do not require html/href...
1742 if (this.anchor === false) {
1744 cfg.href = this.href || '#';
1746 cfg.name = this.anchor;
1747 if (this.html !== false || this.fa !== false) {
1750 if (this.href !== false) {
1751 cfg.href = this.href;
1755 if(this.alt !== false){
1760 if(this.target !== false) {
1761 cfg.target = this.target;
1767 initEvents: function() {
1769 if(!this.href || this.preventDefault){
1770 this.el.on('click', this.onClick, this);
1774 onClick : function(e)
1776 if(this.preventDefault){
1779 //Roo.log('img onclick');
1780 this.fireEvent('click', this, e);
1793 * @class Roo.bootstrap.Header
1794 * @extends Roo.bootstrap.Component
1795 * Bootstrap Header class
1796 * @cfg {String} html content of header
1797 * @cfg {Number} level (1|2|3|4|5|6) default 1
1800 * Create a new Header
1801 * @param {Object} config The config object
1805 Roo.bootstrap.Header = function(config){
1806 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1809 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1817 getAutoCreate : function(){
1822 tag: 'h' + (1 *this.level),
1823 html: this.html || ''
1835 * Ext JS Library 1.1.1
1836 * Copyright(c) 2006-2007, Ext JS, LLC.
1838 * Originally Released Under LGPL - original licence link has changed is not relivant.
1841 * <script type="text/javascript">
1845 * @class Roo.bootstrap.MenuMgr
1846 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1849 Roo.bootstrap.MenuMgr = function(){
1850 var menus, active, groups = {}, attached = false, lastShow = new Date();
1852 // private - called when first menu is created
1855 active = new Roo.util.MixedCollection();
1856 Roo.get(document).addKeyListener(27, function(){
1857 if(active.length > 0){
1865 if(active && active.length > 0){
1866 var c = active.clone();
1876 if(active.length < 1){
1877 Roo.get(document).un("mouseup", onMouseDown);
1885 var last = active.last();
1886 lastShow = new Date();
1889 Roo.get(document).on("mouseup", onMouseDown);
1894 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1895 m.parentMenu.activeChild = m;
1896 }else if(last && last.isVisible()){
1897 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1902 function onBeforeHide(m){
1904 m.activeChild.hide();
1906 if(m.autoHideTimer){
1907 clearTimeout(m.autoHideTimer);
1908 delete m.autoHideTimer;
1913 function onBeforeShow(m){
1914 var pm = m.parentMenu;
1915 if(!pm && !m.allowOtherMenus){
1917 }else if(pm && pm.activeChild && active != m){
1918 pm.activeChild.hide();
1922 // private this should really trigger on mouseup..
1923 function onMouseDown(e){
1924 Roo.log("on Mouse Up");
1926 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1927 Roo.log("MenuManager hideAll");
1936 function onBeforeCheck(mi, state){
1938 var g = groups[mi.group];
1939 for(var i = 0, l = g.length; i < l; i++){
1941 g[i].setChecked(false);
1950 * Hides all menus that are currently visible
1952 hideAll : function(){
1957 register : function(menu){
1961 menus[menu.id] = menu;
1962 menu.on("beforehide", onBeforeHide);
1963 menu.on("hide", onHide);
1964 menu.on("beforeshow", onBeforeShow);
1965 menu.on("show", onShow);
1967 if(g && menu.events["checkchange"]){
1971 groups[g].push(menu);
1972 menu.on("checkchange", onCheck);
1977 * Returns a {@link Roo.menu.Menu} object
1978 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1979 * be used to generate and return a new Menu instance.
1981 get : function(menu){
1982 if(typeof menu == "string"){ // menu id
1984 }else if(menu.events){ // menu instance
1987 /*else if(typeof menu.length == 'number'){ // array of menu items?
1988 return new Roo.bootstrap.Menu({items:menu});
1989 }else{ // otherwise, must be a config
1990 return new Roo.bootstrap.Menu(menu);
1997 unregister : function(menu){
1998 delete menus[menu.id];
1999 menu.un("beforehide", onBeforeHide);
2000 menu.un("hide", onHide);
2001 menu.un("beforeshow", onBeforeShow);
2002 menu.un("show", onShow);
2004 if(g && menu.events["checkchange"]){
2005 groups[g].remove(menu);
2006 menu.un("checkchange", onCheck);
2011 registerCheckable : function(menuItem){
2012 var g = menuItem.group;
2017 groups[g].push(menuItem);
2018 menuItem.on("beforecheckchange", onBeforeCheck);
2023 unregisterCheckable : function(menuItem){
2024 var g = menuItem.group;
2026 groups[g].remove(menuItem);
2027 menuItem.un("beforecheckchange", onBeforeCheck);
2039 * @class Roo.bootstrap.Menu
2040 * @extends Roo.bootstrap.Component
2041 * Bootstrap Menu class - container for MenuItems
2042 * @cfg {String} type (dropdown|treeview|submenu) type of menu
2043 * @cfg {bool} hidden if the menu should be hidden when rendered.
2044 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
2045 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
2049 * @param {Object} config The config object
2053 Roo.bootstrap.Menu = function(config){
2054 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2055 if (this.registerMenu && this.type != 'treeview') {
2056 Roo.bootstrap.MenuMgr.register(this);
2063 * Fires before this menu is displayed
2064 * @param {Roo.menu.Menu} this
2069 * Fires before this menu is hidden
2070 * @param {Roo.menu.Menu} this
2075 * Fires after this menu is displayed
2076 * @param {Roo.menu.Menu} this
2081 * Fires after this menu is hidden
2082 * @param {Roo.menu.Menu} this
2087 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2088 * @param {Roo.menu.Menu} this
2089 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2090 * @param {Roo.EventObject} e
2095 * Fires when the mouse is hovering over this menu
2096 * @param {Roo.menu.Menu} this
2097 * @param {Roo.EventObject} e
2098 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2103 * Fires when the mouse exits this menu
2104 * @param {Roo.menu.Menu} this
2105 * @param {Roo.EventObject} e
2106 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2111 * Fires when a menu item contained in this menu is clicked
2112 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2113 * @param {Roo.EventObject} e
2117 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2120 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2124 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2127 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2129 registerMenu : true,
2131 menuItems :false, // stores the menu items..
2141 getChildContainer : function() {
2145 getAutoCreate : function(){
2147 //if (['right'].indexOf(this.align)!==-1) {
2148 // cfg.cn[1].cls += ' pull-right'
2154 cls : 'dropdown-menu' ,
2155 style : 'z-index:1000'
2159 if (this.type === 'submenu') {
2160 cfg.cls = 'submenu active';
2162 if (this.type === 'treeview') {
2163 cfg.cls = 'treeview-menu';
2168 initEvents : function() {
2170 // Roo.log("ADD event");
2171 // Roo.log(this.triggerEl.dom);
2173 this.triggerEl.on('click', this.onTriggerClick, this);
2175 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2178 if (this.triggerEl.hasClass('nav-item')) {
2179 // dropdown toggle on the 'a' in BS4?
2180 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2182 this.triggerEl.addClass('dropdown-toggle');
2185 this.el.on('touchstart' , this.onTouch, this);
2187 this.el.on('click' , this.onClick, this);
2189 this.el.on("mouseover", this.onMouseOver, this);
2190 this.el.on("mouseout", this.onMouseOut, this);
2194 findTargetItem : function(e)
2196 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2200 //Roo.log(t); Roo.log(t.id);
2202 //Roo.log(this.menuitems);
2203 return this.menuitems.get(t.id);
2205 //return this.items.get(t.menuItemId);
2211 onTouch : function(e)
2213 Roo.log("menu.onTouch");
2214 //e.stopEvent(); this make the user popdown broken
2218 onClick : function(e)
2220 Roo.log("menu.onClick");
2222 var t = this.findTargetItem(e);
2223 if(!t || t.isContainer){
2228 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2229 if(t == this.activeItem && t.shouldDeactivate(e)){
2230 this.activeItem.deactivate();
2231 delete this.activeItem;
2235 this.setActiveItem(t, true);
2243 Roo.log('pass click event');
2247 this.fireEvent("click", this, t, e);
2251 if(!t.href.length || t.href == '#'){
2252 (function() { _this.hide(); }).defer(100);
2257 onMouseOver : function(e){
2258 var t = this.findTargetItem(e);
2261 // if(t.canActivate && !t.disabled){
2262 // this.setActiveItem(t, true);
2266 this.fireEvent("mouseover", this, e, t);
2268 isVisible : function(){
2269 return !this.hidden;
2271 onMouseOut : function(e){
2272 var t = this.findTargetItem(e);
2275 // if(t == this.activeItem && t.shouldDeactivate(e)){
2276 // this.activeItem.deactivate();
2277 // delete this.activeItem;
2280 this.fireEvent("mouseout", this, e, t);
2285 * Displays this menu relative to another element
2286 * @param {String/HTMLElement/Roo.Element} element The element to align to
2287 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2288 * the element (defaults to this.defaultAlign)
2289 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2291 show : function(el, pos, parentMenu){
2292 this.parentMenu = parentMenu;
2296 this.fireEvent("beforeshow", this);
2297 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2300 * Displays this menu at a specific xy position
2301 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2302 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2304 showAt : function(xy, parentMenu, /* private: */_e){
2305 this.parentMenu = parentMenu;
2310 this.fireEvent("beforeshow", this);
2311 //xy = this.el.adjustForConstraints(xy);
2315 this.hideMenuItems();
2316 this.hidden = false;
2317 this.triggerEl.addClass('open');
2318 this.el.addClass('show');
2320 // reassign x when hitting right
2321 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2322 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2325 // reassign y when hitting bottom
2326 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2327 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2330 // but the list may align on trigger left or trigger top... should it be a properity?
2332 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2337 this.fireEvent("show", this);
2343 this.doFocus.defer(50, this);
2347 doFocus : function(){
2349 this.focusEl.focus();
2354 * Hides this menu and optionally all parent menus
2355 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2357 hide : function(deep)
2360 this.hideMenuItems();
2361 if(this.el && this.isVisible()){
2362 this.fireEvent("beforehide", this);
2363 if(this.activeItem){
2364 this.activeItem.deactivate();
2365 this.activeItem = null;
2367 this.triggerEl.removeClass('open');;
2368 this.el.removeClass('show');
2370 this.fireEvent("hide", this);
2372 if(deep === true && this.parentMenu){
2373 this.parentMenu.hide(true);
2377 onTriggerClick : function(e)
2379 Roo.log('trigger click');
2381 var target = e.getTarget();
2383 Roo.log(target.nodeName.toLowerCase());
2385 if(target.nodeName.toLowerCase() === 'i'){
2391 onTriggerPress : function(e)
2393 Roo.log('trigger press');
2394 //Roo.log(e.getTarget());
2395 // Roo.log(this.triggerEl.dom);
2397 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2398 var pel = Roo.get(e.getTarget());
2399 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2400 Roo.log('is treeview or dropdown?');
2404 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2408 if (this.isVisible()) {
2413 this.show(this.triggerEl, false, false);
2416 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2423 hideMenuItems : function()
2425 Roo.log("hide Menu Items");
2429 //$(backdrop).remove()
2430 this.el.select('.open',true).each(function(aa) {
2432 aa.removeClass('open');
2433 //var parent = getParent($(this))
2434 //var relatedTarget = { relatedTarget: this }
2436 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2437 //if (e.isDefaultPrevented()) return
2438 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2441 addxtypeChild : function (tree, cntr) {
2442 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2444 this.menuitems.add(comp);
2456 this.getEl().dom.innerHTML = '';
2457 this.menuitems.clear();
2471 * @class Roo.bootstrap.MenuItem
2472 * @extends Roo.bootstrap.Component
2473 * Bootstrap MenuItem class
2474 * @cfg {String} html the menu label
2475 * @cfg {String} href the link
2476 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2477 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2478 * @cfg {Boolean} active used on sidebars to highlight active itesm
2479 * @cfg {String} fa favicon to show on left of menu item.
2480 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2484 * Create a new MenuItem
2485 * @param {Object} config The config object
2489 Roo.bootstrap.MenuItem = function(config){
2490 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2495 * The raw click event for the entire grid.
2496 * @param {Roo.bootstrap.MenuItem} this
2497 * @param {Roo.EventObject} e
2503 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2507 preventDefault: false,
2508 isContainer : false,
2512 getAutoCreate : function(){
2514 if(this.isContainer){
2517 cls: 'dropdown-menu-item '
2527 cls : 'dropdown-item',
2532 if (this.fa !== false) {
2535 cls : 'fa fa-' + this.fa
2544 cls: 'dropdown-menu-item',
2547 if (this.parent().type == 'treeview') {
2548 cfg.cls = 'treeview-menu';
2551 cfg.cls += ' active';
2556 anc.href = this.href || cfg.cn[0].href ;
2557 ctag.html = this.html || cfg.cn[0].html ;
2561 initEvents: function()
2563 if (this.parent().type == 'treeview') {
2564 this.el.select('a').on('click', this.onClick, this);
2568 this.menu.parentType = this.xtype;
2569 this.menu.triggerEl = this.el;
2570 this.menu = this.addxtype(Roo.apply({}, this.menu));
2574 onClick : function(e)
2576 Roo.log('item on click ');
2578 if(this.preventDefault){
2581 //this.parent().hideMenuItems();
2583 this.fireEvent('click', this, e);
2602 * @class Roo.bootstrap.MenuSeparator
2603 * @extends Roo.bootstrap.Component
2604 * Bootstrap MenuSeparator class
2607 * Create a new MenuItem
2608 * @param {Object} config The config object
2612 Roo.bootstrap.MenuSeparator = function(config){
2613 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2616 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2618 getAutoCreate : function(){
2637 * @class Roo.bootstrap.Modal
2638 * @extends Roo.bootstrap.Component
2639 * Bootstrap Modal class
2640 * @cfg {String} title Title of dialog
2641 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2642 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2643 * @cfg {Boolean} specificTitle default false
2644 * @cfg {Array} buttons Array of buttons or standard button set..
2645 * @cfg {String} buttonPosition (left|right|center) default right
2646 * @cfg {Boolean} animate default true
2647 * @cfg {Boolean} allow_close default true
2648 * @cfg {Boolean} fitwindow default false
2649 * @cfg {String} size (sm|lg) default empty
2650 * @cfg {Number} max_width set the max width of modal
2654 * Create a new Modal Dialog
2655 * @param {Object} config The config object
2658 Roo.bootstrap.Modal = function(config){
2659 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2664 * The raw btnclick event for the button
2665 * @param {Roo.EventObject} e
2670 * Fire when dialog resize
2671 * @param {Roo.bootstrap.Modal} this
2672 * @param {Roo.EventObject} e
2676 this.buttons = this.buttons || [];
2679 this.tmpl = Roo.factory(this.tmpl);
2684 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2686 title : 'test dialog',
2696 specificTitle: false,
2698 buttonPosition: 'right',
2721 onRender : function(ct, position)
2723 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2726 var cfg = Roo.apply({}, this.getAutoCreate());
2729 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2731 //if (!cfg.name.length) {
2735 cfg.cls += ' ' + this.cls;
2738 cfg.style = this.style;
2740 this.el = Roo.get(document.body).createChild(cfg, position);
2742 //var type = this.el.dom.type;
2745 if(this.tabIndex !== undefined){
2746 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2749 this.dialogEl = this.el.select('.modal-dialog',true).first();
2750 this.bodyEl = this.el.select('.modal-body',true).first();
2751 this.closeEl = this.el.select('.modal-header .close', true).first();
2752 this.headerEl = this.el.select('.modal-header',true).first();
2753 this.titleEl = this.el.select('.modal-title',true).first();
2754 this.footerEl = this.el.select('.modal-footer',true).first();
2756 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2758 //this.el.addClass("x-dlg-modal");
2760 if (this.buttons.length) {
2761 Roo.each(this.buttons, function(bb) {
2762 var b = Roo.apply({}, bb);
2763 b.xns = b.xns || Roo.bootstrap;
2764 b.xtype = b.xtype || 'Button';
2765 if (typeof(b.listeners) == 'undefined') {
2766 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2769 var btn = Roo.factory(b);
2771 btn.render(this.el.select('.modal-footer div').first());
2775 // render the children.
2778 if(typeof(this.items) != 'undefined'){
2779 var items = this.items;
2782 for(var i =0;i < items.length;i++) {
2783 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2787 this.items = nitems;
2789 // where are these used - they used to be body/close/footer
2793 //this.el.addClass([this.fieldClass, this.cls]);
2797 getAutoCreate : function()
2801 html : this.html || ''
2806 cls : 'modal-title',
2810 if(this.specificTitle){
2816 if (this.allow_close && Roo.bootstrap.version == 3) {
2826 if (this.allow_close && Roo.bootstrap.version == 4) {
2836 if(this.size.length){
2837 size = 'modal-' + this.size;
2844 cls: "modal-dialog " + size,
2847 cls : "modal-content",
2850 cls : 'modal-header',
2855 cls : 'modal-footer',
2859 cls: 'btn-' + this.buttonPosition
2876 modal.cls += ' fade';
2882 getChildContainer : function() {
2887 getButtonContainer : function() {
2888 return this.el.select('.modal-footer div',true).first();
2891 initEvents : function()
2893 if (this.allow_close) {
2894 this.closeEl.on('click', this.hide, this);
2896 Roo.EventManager.onWindowResize(this.resize, this, true);
2903 this.maskEl.setSize(
2904 Roo.lib.Dom.getViewWidth(true),
2905 Roo.lib.Dom.getViewHeight(true)
2908 if (this.fitwindow) {
2910 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2911 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2916 if(this.max_width !== 0) {
2918 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2921 this.setSize(w, this.height);
2925 if(this.max_height) {
2926 this.setSize(w,Math.min(
2928 Roo.lib.Dom.getViewportHeight(true) - 60
2934 if(!this.fit_content) {
2935 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2939 this.setSize(w, Math.min(
2941 this.headerEl.getHeight() +
2942 this.footerEl.getHeight() +
2943 this.getChildHeight(this.bodyEl.dom.childNodes),
2944 Roo.lib.Dom.getViewportHeight(true) - 60)
2950 setSize : function(w,h)
2961 if (!this.rendered) {
2965 //this.el.setStyle('display', 'block');
2966 this.el.removeClass('hideing');
2967 this.el.dom.style.display='block';
2969 Roo.get(document.body).addClass('modal-open');
2971 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2974 this.el.addClass('show');
2975 this.el.addClass('in');
2978 this.el.addClass('show');
2979 this.el.addClass('in');
2982 // not sure how we can show data in here..
2984 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2987 Roo.get(document.body).addClass("x-body-masked");
2989 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2990 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2991 this.maskEl.dom.style.display = 'block';
2992 this.maskEl.addClass('show');
2997 this.fireEvent('show', this);
2999 // set zindex here - otherwise it appears to be ignored...
3000 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3003 this.items.forEach( function(e) {
3004 e.layout ? e.layout() : false;
3012 if(this.fireEvent("beforehide", this) !== false){
3014 this.maskEl.removeClass('show');
3016 this.maskEl.dom.style.display = '';
3017 Roo.get(document.body).removeClass("x-body-masked");
3018 this.el.removeClass('in');
3019 this.el.select('.modal-dialog', true).first().setStyle('transform','');
3021 if(this.animate){ // why
3022 this.el.addClass('hideing');
3023 this.el.removeClass('show');
3025 if (!this.el.hasClass('hideing')) {
3026 return; // it's been shown again...
3029 this.el.dom.style.display='';
3031 Roo.get(document.body).removeClass('modal-open');
3032 this.el.removeClass('hideing');
3036 this.el.removeClass('show');
3037 this.el.dom.style.display='';
3038 Roo.get(document.body).removeClass('modal-open');
3041 this.fireEvent('hide', this);
3044 isVisible : function()
3047 return this.el.hasClass('show') && !this.el.hasClass('hideing');
3051 addButton : function(str, cb)
3055 var b = Roo.apply({}, { html : str } );
3056 b.xns = b.xns || Roo.bootstrap;
3057 b.xtype = b.xtype || 'Button';
3058 if (typeof(b.listeners) == 'undefined') {
3059 b.listeners = { click : cb.createDelegate(this) };
3062 var btn = Roo.factory(b);
3064 btn.render(this.el.select('.modal-footer div').first());
3070 setDefaultButton : function(btn)
3072 //this.el.select('.modal-footer').()
3076 resizeTo: function(w,h)
3080 this.dialogEl.setWidth(w);
3081 if (this.diff === false) {
3082 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3085 this.bodyEl.setHeight(h - this.diff);
3087 this.fireEvent('resize', this);
3090 setContentSize : function(w, h)
3094 onButtonClick: function(btn,e)
3097 this.fireEvent('btnclick', btn.name, e);
3100 * Set the title of the Dialog
3101 * @param {String} str new Title
3103 setTitle: function(str) {
3104 this.titleEl.dom.innerHTML = str;
3107 * Set the body of the Dialog
3108 * @param {String} str new Title
3110 setBody: function(str) {
3111 this.bodyEl.dom.innerHTML = str;
3114 * Set the body of the Dialog using the template
3115 * @param {Obj} data - apply this data to the template and replace the body contents.
3117 applyBody: function(obj)
3120 Roo.log("Error - using apply Body without a template");
3123 this.tmpl.overwrite(this.bodyEl, obj);
3126 getChildHeight : function(child_nodes)
3130 child_nodes.length == 0
3135 var child_height = 0;
3137 for(var i = 0; i < child_nodes.length; i++) {
3140 * for modal with tabs...
3141 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3143 var layout_childs = child_nodes[i].childNodes;
3145 for(var j = 0; j < layout_childs.length; j++) {
3147 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3149 var layout_body_childs = layout_childs[j].childNodes;
3151 for(var k = 0; k < layout_body_childs.length; k++) {
3153 if(layout_body_childs[k].classList.contains('navbar')) {
3154 child_height += layout_body_childs[k].offsetHeight;
3158 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3160 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3162 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3164 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3165 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3180 child_height += child_nodes[i].offsetHeight;
3181 // Roo.log(child_nodes[i].offsetHeight);
3184 return child_height;
3190 Roo.apply(Roo.bootstrap.Modal, {
3192 * Button config that displays a single OK button
3201 * Button config that displays Yes and No buttons
3217 * Button config that displays OK and Cancel buttons
3232 * Button config that displays Yes, No and Cancel buttons
3256 * messagebox - can be used as a replace
3260 * @class Roo.MessageBox
3261 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3265 Roo.Msg.alert('Status', 'Changes saved successfully.');
3267 // Prompt for user data:
3268 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3270 // process text value...
3274 // Show a dialog using config options:
3276 title:'Save Changes?',
3277 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3278 buttons: Roo.Msg.YESNOCANCEL,
3285 Roo.bootstrap.MessageBox = function(){
3286 var dlg, opt, mask, waitTimer;
3287 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3288 var buttons, activeTextEl, bwidth;
3292 var handleButton = function(button){
3294 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3298 var handleHide = function(){
3300 dlg.el.removeClass(opt.cls);
3303 // Roo.TaskMgr.stop(waitTimer);
3304 // waitTimer = null;
3309 var updateButtons = function(b){
3312 buttons["ok"].hide();
3313 buttons["cancel"].hide();
3314 buttons["yes"].hide();
3315 buttons["no"].hide();
3316 //dlg.footer.dom.style.display = 'none';
3319 dlg.footerEl.dom.style.display = '';
3320 for(var k in buttons){
3321 if(typeof buttons[k] != "function"){
3324 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3325 width += buttons[k].el.getWidth()+15;
3335 var handleEsc = function(d, k, e){
3336 if(opt && opt.closable !== false){
3346 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3347 * @return {Roo.BasicDialog} The BasicDialog element
3349 getDialog : function(){
3351 dlg = new Roo.bootstrap.Modal( {
3354 //constraintoviewport:false,
3356 //collapsible : false,
3361 //buttonAlign:"center",
3362 closeClick : function(){
3363 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3366 handleButton("cancel");
3371 dlg.on("hide", handleHide);
3373 //dlg.addKeyListener(27, handleEsc);
3375 this.buttons = buttons;
3376 var bt = this.buttonText;
3377 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3378 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3379 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3380 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3382 bodyEl = dlg.bodyEl.createChild({
3384 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3385 '<textarea class="roo-mb-textarea"></textarea>' +
3386 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3388 msgEl = bodyEl.dom.firstChild;
3389 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3390 textboxEl.enableDisplayMode();
3391 textboxEl.addKeyListener([10,13], function(){
3392 if(dlg.isVisible() && opt && opt.buttons){
3395 }else if(opt.buttons.yes){
3396 handleButton("yes");
3400 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3401 textareaEl.enableDisplayMode();
3402 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3403 progressEl.enableDisplayMode();
3405 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3406 var pf = progressEl.dom.firstChild;
3408 pp = Roo.get(pf.firstChild);
3409 pp.setHeight(pf.offsetHeight);
3417 * Updates the message box body text
3418 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3419 * the XHTML-compliant non-breaking space character '&#160;')
3420 * @return {Roo.MessageBox} This message box
3422 updateText : function(text)
3424 if(!dlg.isVisible() && !opt.width){
3425 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3426 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3428 msgEl.innerHTML = text || ' ';
3430 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3431 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3433 Math.min(opt.width || cw , this.maxWidth),
3434 Math.max(opt.minWidth || this.minWidth, bwidth)
3437 activeTextEl.setWidth(w);
3439 if(dlg.isVisible()){
3440 dlg.fixedcenter = false;
3442 // to big, make it scroll. = But as usual stupid IE does not support
3445 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3446 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3447 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3449 bodyEl.dom.style.height = '';
3450 bodyEl.dom.style.overflowY = '';
3453 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3455 bodyEl.dom.style.overflowX = '';
3458 dlg.setContentSize(w, bodyEl.getHeight());
3459 if(dlg.isVisible()){
3460 dlg.fixedcenter = true;
3466 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3467 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3468 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3469 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3470 * @return {Roo.MessageBox} This message box
3472 updateProgress : function(value, text){
3474 this.updateText(text);
3477 if (pp) { // weird bug on my firefox - for some reason this is not defined
3478 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3479 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3485 * Returns true if the message box is currently displayed
3486 * @return {Boolean} True if the message box is visible, else false
3488 isVisible : function(){
3489 return dlg && dlg.isVisible();
3493 * Hides the message box if it is displayed
3496 if(this.isVisible()){
3502 * Displays a new message box, or reinitializes an existing message box, based on the config options
3503 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3504 * The following config object properties are supported:
3506 Property Type Description
3507 ---------- --------------- ------------------------------------------------------------------------------------
3508 animEl String/Element An id or Element from which the message box should animate as it opens and
3509 closes (defaults to undefined)
3510 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3511 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3512 closable Boolean False to hide the top-right close button (defaults to true). Note that
3513 progress and wait dialogs will ignore this property and always hide the
3514 close button as they can only be closed programmatically.
3515 cls String A custom CSS class to apply to the message box element
3516 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3517 displayed (defaults to 75)
3518 fn Function A callback function to execute after closing the dialog. The arguments to the
3519 function will be btn (the name of the button that was clicked, if applicable,
3520 e.g. "ok"), and text (the value of the active text field, if applicable).
3521 Progress and wait dialogs will ignore this option since they do not respond to
3522 user actions and can only be closed programmatically, so any required function
3523 should be called by the same code after it closes the dialog.
3524 icon String A CSS class that provides a background image to be used as an icon for
3525 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3526 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3527 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3528 modal Boolean False to allow user interaction with the page while the message box is
3529 displayed (defaults to true)
3530 msg String A string that will replace the existing message box body text (defaults
3531 to the XHTML-compliant non-breaking space character ' ')
3532 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3533 progress Boolean True to display a progress bar (defaults to false)
3534 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3535 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3536 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3537 title String The title text
3538 value String The string value to set into the active textbox element if displayed
3539 wait Boolean True to display a progress bar (defaults to false)
3540 width Number The width of the dialog in pixels
3547 msg: 'Please enter your address:',
3549 buttons: Roo.MessageBox.OKCANCEL,
3552 animEl: 'addAddressBtn'
3555 * @param {Object} config Configuration options
3556 * @return {Roo.MessageBox} This message box
3558 show : function(options)
3561 // this causes nightmares if you show one dialog after another
3562 // especially on callbacks..
3564 if(this.isVisible()){
3567 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3568 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3569 Roo.log("New Dialog Message:" + options.msg )
3570 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3571 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3574 var d = this.getDialog();
3576 d.setTitle(opt.title || " ");
3577 d.closeEl.setDisplayed(opt.closable !== false);
3578 activeTextEl = textboxEl;
3579 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3584 textareaEl.setHeight(typeof opt.multiline == "number" ?
3585 opt.multiline : this.defaultTextHeight);
3586 activeTextEl = textareaEl;
3595 progressEl.setDisplayed(opt.progress === true);
3596 this.updateProgress(0);
3597 activeTextEl.dom.value = opt.value || "";
3599 dlg.setDefaultButton(activeTextEl);
3601 var bs = opt.buttons;
3605 }else if(bs && bs.yes){
3606 db = buttons["yes"];
3608 dlg.setDefaultButton(db);
3610 bwidth = updateButtons(opt.buttons);
3611 this.updateText(opt.msg);
3613 d.el.addClass(opt.cls);
3615 d.proxyDrag = opt.proxyDrag === true;
3616 d.modal = opt.modal !== false;
3617 d.mask = opt.modal !== false ? mask : false;
3619 // force it to the end of the z-index stack so it gets a cursor in FF
3620 document.body.appendChild(dlg.el.dom);
3621 d.animateTarget = null;
3622 d.show(options.animEl);
3628 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3629 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3630 * and closing the message box when the process is complete.
3631 * @param {String} title The title bar text
3632 * @param {String} msg The message box body text
3633 * @return {Roo.MessageBox} This message box
3635 progress : function(title, msg){
3642 minWidth: this.minProgressWidth,
3649 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3650 * If a callback function is passed it will be called after the user clicks the button, and the
3651 * id of the button that was clicked will be passed as the only parameter to the callback
3652 * (could also be the top-right close button).
3653 * @param {String} title The title bar text
3654 * @param {String} msg The message box body text
3655 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3656 * @param {Object} scope (optional) The scope of the callback function
3657 * @return {Roo.MessageBox} This message box
3659 alert : function(title, msg, fn, scope)
3674 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3675 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3676 * You are responsible for closing the message box when the process is complete.
3677 * @param {String} msg The message box body text
3678 * @param {String} title (optional) The title bar text
3679 * @return {Roo.MessageBox} This message box
3681 wait : function(msg, title){
3692 waitTimer = Roo.TaskMgr.start({
3694 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3702 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3703 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3704 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3705 * @param {String} title The title bar text
3706 * @param {String} msg The message box body text
3707 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3708 * @param {Object} scope (optional) The scope of the callback function
3709 * @return {Roo.MessageBox} This message box
3711 confirm : function(title, msg, fn, scope){
3715 buttons: this.YESNO,
3724 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3725 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3726 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3727 * (could also be the top-right close button) and the text that was entered will be passed as the two
3728 * parameters to the callback.
3729 * @param {String} title The title bar text
3730 * @param {String} msg The message box body text
3731 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3732 * @param {Object} scope (optional) The scope of the callback function
3733 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3734 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3735 * @return {Roo.MessageBox} This message box
3737 prompt : function(title, msg, fn, scope, multiline){
3741 buttons: this.OKCANCEL,
3746 multiline: multiline,
3753 * Button config that displays a single OK button
3758 * Button config that displays Yes and No buttons
3761 YESNO : {yes:true, no:true},
3763 * Button config that displays OK and Cancel buttons
3766 OKCANCEL : {ok:true, cancel:true},
3768 * Button config that displays Yes, No and Cancel buttons
3771 YESNOCANCEL : {yes:true, no:true, cancel:true},
3774 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3777 defaultTextHeight : 75,
3779 * The maximum width in pixels of the message box (defaults to 600)
3784 * The minimum width in pixels of the message box (defaults to 100)
3789 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3790 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3793 minProgressWidth : 250,
3795 * An object containing the default button text strings that can be overriden for localized language support.
3796 * Supported properties are: ok, cancel, yes and no.
3797 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3810 * Shorthand for {@link Roo.MessageBox}
3812 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3813 Roo.Msg = Roo.Msg || Roo.MessageBox;
3822 * @class Roo.bootstrap.Navbar
3823 * @extends Roo.bootstrap.Component
3824 * Bootstrap Navbar class
3827 * Create a new Navbar
3828 * @param {Object} config The config object
3832 Roo.bootstrap.Navbar = function(config){
3833 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3837 * @event beforetoggle
3838 * Fire before toggle the menu
3839 * @param {Roo.EventObject} e
3841 "beforetoggle" : true
3845 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3854 getAutoCreate : function(){
3857 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3861 initEvents :function ()
3863 //Roo.log(this.el.select('.navbar-toggle',true));
3864 this.el.select('.navbar-toggle',true).on('click', function() {
3865 if(this.fireEvent('beforetoggle', this) !== false){
3866 var ce = this.el.select('.navbar-collapse',true).first();
3867 ce.toggleClass('in'); // old...
3868 if (ce.hasClass('collapse')) {
3870 ce.removeClass('collapse');
3871 ce.addClass('show');
3872 var h = ce.getHeight();
3874 ce.removeClass('show');
3875 // at this point we should be able to see it..
3876 ce.addClass('collapsing');
3878 ce.setHeight(0); // resize it ...
3879 ce.on('transitionend', function() {
3880 Roo.log('done transition');
3881 ce.removeClass('collapsing');
3882 ce.addClass('show');
3883 ce.removeClass('collapse');
3885 ce.dom.style.height = '';
3886 }, this, { single: true} );
3890 ce.setHeight(ce.getHeight());
3891 ce.removeClass('show');
3892 ce.addClass('collapsing');
3894 ce.on('transitionend', function() {
3895 ce.dom.style.height = '';
3896 ce.removeClass('collapsing');
3897 ce.addClass('collapse');
3898 }, this, { single: true} );
3910 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3912 var size = this.el.getSize();
3913 this.maskEl.setSize(size.width, size.height);
3914 this.maskEl.enableDisplayMode("block");
3923 getChildContainer : function()
3925 if (this.el.select('.collapse').getCount()) {
3926 return this.el.select('.collapse',true).first();
3959 * @class Roo.bootstrap.NavSimplebar
3960 * @extends Roo.bootstrap.Navbar
3961 * Bootstrap Sidebar class
3963 * @cfg {Boolean} inverse is inverted color
3965 * @cfg {String} type (nav | pills | tabs)
3966 * @cfg {Boolean} arrangement stacked | justified
3967 * @cfg {String} align (left | right) alignment
3969 * @cfg {Boolean} main (true|false) main nav bar? default false
3970 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3972 * @cfg {String} tag (header|footer|nav|div) default is nav
3974 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3978 * Create a new Sidebar
3979 * @param {Object} config The config object
3983 Roo.bootstrap.NavSimplebar = function(config){
3984 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3987 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
4003 getAutoCreate : function(){
4007 tag : this.tag || 'div',
4008 cls : 'navbar navbar-expand-lg'
4010 if (['light','white'].indexOf(this.weight) > -1) {
4011 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4013 cfg.cls += ' bg-' + this.weight;
4025 this.type = this.type || 'nav';
4026 if (['tabs','pills'].indexOf(this.type)!==-1) {
4027 cfg.cn[0].cls += ' nav-' + this.type
4031 if (this.type!=='nav') {
4032 Roo.log('nav type must be nav/tabs/pills')
4034 cfg.cn[0].cls += ' navbar-nav'
4040 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4041 cfg.cn[0].cls += ' nav-' + this.arrangement;
4045 if (this.align === 'right') {
4046 cfg.cn[0].cls += ' navbar-right';
4050 cfg.cls += ' navbar-inverse';
4074 * navbar-expand-md fixed-top
4078 * @class Roo.bootstrap.NavHeaderbar
4079 * @extends Roo.bootstrap.NavSimplebar
4080 * Bootstrap Sidebar class
4082 * @cfg {String} brand what is brand
4083 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4084 * @cfg {String} brand_href href of the brand
4085 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
4086 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4087 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4088 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4091 * Create a new Sidebar
4092 * @param {Object} config The config object
4096 Roo.bootstrap.NavHeaderbar = function(config){
4097 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4101 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
4108 desktopCenter : false,
4111 getAutoCreate : function(){
4114 tag: this.nav || 'nav',
4115 cls: 'navbar navbar-expand-md',
4121 if (this.desktopCenter) {
4122 cn.push({cls : 'container', cn : []});
4130 cls: 'navbar-toggle navbar-toggler',
4131 'data-toggle': 'collapse',
4136 html: 'Toggle navigation'
4140 cls: 'icon-bar navbar-toggler-icon'
4153 cn.push( Roo.bootstrap.version == 4 ? btn : {
4155 cls: 'navbar-header',
4164 cls: 'collapse navbar-collapse',
4168 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4170 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4171 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4173 // tag can override this..
4175 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4178 if (this.brand !== '') {
4179 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4180 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4182 href: this.brand_href ? this.brand_href : '#',
4183 cls: 'navbar-brand',
4191 cfg.cls += ' main-nav';
4199 getHeaderChildContainer : function()
4201 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4202 return this.el.select('.navbar-header',true).first();
4205 return this.getChildContainer();
4209 initEvents : function()
4211 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4213 if (this.autohide) {
4218 Roo.get(document).on('scroll',function(e) {
4219 var ns = Roo.get(document).getScroll().top;
4220 var os = prevScroll;
4224 ft.removeClass('slideDown');
4225 ft.addClass('slideUp');
4228 ft.removeClass('slideUp');
4229 ft.addClass('slideDown');
4250 * @class Roo.bootstrap.NavSidebar
4251 * @extends Roo.bootstrap.Navbar
4252 * Bootstrap Sidebar class
4255 * Create a new Sidebar
4256 * @param {Object} config The config object
4260 Roo.bootstrap.NavSidebar = function(config){
4261 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4264 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4266 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4268 getAutoCreate : function(){
4273 cls: 'sidebar sidebar-nav'
4295 * @class Roo.bootstrap.NavGroup
4296 * @extends Roo.bootstrap.Component
4297 * Bootstrap NavGroup class
4298 * @cfg {String} align (left|right)
4299 * @cfg {Boolean} inverse
4300 * @cfg {String} type (nav|pills|tab) default nav
4301 * @cfg {String} navId - reference Id for navbar.
4305 * Create a new nav group
4306 * @param {Object} config The config object
4309 Roo.bootstrap.NavGroup = function(config){
4310 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4313 Roo.bootstrap.NavGroup.register(this);
4317 * Fires when the active item changes
4318 * @param {Roo.bootstrap.NavGroup} this
4319 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4320 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4327 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4338 getAutoCreate : function()
4340 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4347 if (['tabs','pills'].indexOf(this.type)!==-1) {
4348 cfg.cls += ' nav-' + this.type
4350 if (this.type!=='nav') {
4351 Roo.log('nav type must be nav/tabs/pills')
4353 cfg.cls += ' navbar-nav'
4356 if (this.parent() && this.parent().sidebar) {
4359 cls: 'dashboard-menu sidebar-menu'
4365 if (this.form === true) {
4371 if (this.align === 'right') {
4372 cfg.cls += ' navbar-right ml-md-auto';
4374 cfg.cls += ' navbar-left';
4378 if (this.align === 'right') {
4379 cfg.cls += ' navbar-right ml-md-auto';
4381 cfg.cls += ' mr-auto';
4385 cfg.cls += ' navbar-inverse';
4393 * sets the active Navigation item
4394 * @param {Roo.bootstrap.NavItem} the new current navitem
4396 setActiveItem : function(item)
4399 Roo.each(this.navItems, function(v){
4404 v.setActive(false, true);
4411 item.setActive(true, true);
4412 this.fireEvent('changed', this, item, prev);
4417 * gets the active Navigation item
4418 * @return {Roo.bootstrap.NavItem} the current navitem
4420 getActive : function()
4424 Roo.each(this.navItems, function(v){
4435 indexOfNav : function()
4439 Roo.each(this.navItems, function(v,i){
4450 * adds a Navigation item
4451 * @param {Roo.bootstrap.NavItem} the navitem to add
4453 addItem : function(cfg)
4455 var cn = new Roo.bootstrap.NavItem(cfg);
4457 cn.parentId = this.id;
4458 cn.onRender(this.el, null);
4462 * register a Navigation item
4463 * @param {Roo.bootstrap.NavItem} the navitem to add
4465 register : function(item)
4467 this.navItems.push( item);
4468 item.navId = this.navId;
4473 * clear all the Navigation item
4476 clearAll : function()
4479 this.el.dom.innerHTML = '';
4482 getNavItem: function(tabId)
4485 Roo.each(this.navItems, function(e) {
4486 if (e.tabId == tabId) {
4496 setActiveNext : function()
4498 var i = this.indexOfNav(this.getActive());
4499 if (i > this.navItems.length) {
4502 this.setActiveItem(this.navItems[i+1]);
4504 setActivePrev : function()
4506 var i = this.indexOfNav(this.getActive());
4510 this.setActiveItem(this.navItems[i-1]);
4512 clearWasActive : function(except) {
4513 Roo.each(this.navItems, function(e) {
4514 if (e.tabId != except.tabId && e.was_active) {
4515 e.was_active = false;
4522 getWasActive : function ()
4525 Roo.each(this.navItems, function(e) {
4540 Roo.apply(Roo.bootstrap.NavGroup, {
4544 * register a Navigation Group
4545 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4547 register : function(navgrp)
4549 this.groups[navgrp.navId] = navgrp;
4553 * fetch a Navigation Group based on the navigation ID
4554 * @param {string} the navgroup to add
4555 * @returns {Roo.bootstrap.NavGroup} the navgroup
4557 get: function(navId) {
4558 if (typeof(this.groups[navId]) == 'undefined') {
4560 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4562 return this.groups[navId] ;
4577 * @class Roo.bootstrap.NavItem
4578 * @extends Roo.bootstrap.Component
4579 * Bootstrap Navbar.NavItem class
4580 * @cfg {String} href link to
4581 * @cfg {String} html content of button
4582 * @cfg {String} badge text inside badge
4583 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4584 * @cfg {String} glyphicon DEPRICATED - use fa
4585 * @cfg {String} icon DEPRICATED - use fa
4586 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4587 * @cfg {Boolean} active Is item active
4588 * @cfg {Boolean} disabled Is item disabled
4590 * @cfg {Boolean} preventDefault (true | false) default false
4591 * @cfg {String} tabId the tab that this item activates.
4592 * @cfg {String} tagtype (a|span) render as a href or span?
4593 * @cfg {Boolean} animateRef (true|false) link to element default false
4596 * Create a new Navbar Item
4597 * @param {Object} config The config object
4599 Roo.bootstrap.NavItem = function(config){
4600 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4605 * The raw click event for the entire grid.
4606 * @param {Roo.EventObject} e
4611 * Fires when the active item active state changes
4612 * @param {Roo.bootstrap.NavItem} this
4613 * @param {boolean} state the new state
4619 * Fires when scroll to element
4620 * @param {Roo.bootstrap.NavItem} this
4621 * @param {Object} options
4622 * @param {Roo.EventObject} e
4630 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4639 preventDefault : false,
4646 getAutoCreate : function(){
4655 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4657 if (this.disabled) {
4658 cfg.cls += ' disabled';
4661 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4665 href : this.href || "#",
4666 html: this.html || ''
4669 if (this.tagtype == 'a') {
4670 cfg.cn[0].cls = 'nav-link';
4673 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4676 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4678 if(this.glyphicon) {
4679 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4684 cfg.cn[0].html += " <span class='caret'></span>";
4688 if (this.badge !== '') {
4690 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4698 initEvents: function()
4700 if (typeof (this.menu) != 'undefined') {
4701 this.menu.parentType = this.xtype;
4702 this.menu.triggerEl = this.el;
4703 this.menu = this.addxtype(Roo.apply({}, this.menu));
4706 this.el.select('a',true).on('click', this.onClick, this);
4708 if(this.tagtype == 'span'){
4709 this.el.select('span',true).on('click', this.onClick, this);
4712 // at this point parent should be available..
4713 this.parent().register(this);
4716 onClick : function(e)
4718 if (e.getTarget('.dropdown-menu-item')) {
4719 // did you click on a menu itemm.... - then don't trigger onclick..
4724 this.preventDefault ||
4727 Roo.log("NavItem - prevent Default?");
4731 if (this.disabled) {
4735 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4736 if (tg && tg.transition) {
4737 Roo.log("waiting for the transitionend");
4743 //Roo.log("fire event clicked");
4744 if(this.fireEvent('click', this, e) === false){
4748 if(this.tagtype == 'span'){
4752 //Roo.log(this.href);
4753 var ael = this.el.select('a',true).first();
4756 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4757 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4758 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4759 return; // ignore... - it's a 'hash' to another page.
4761 Roo.log("NavItem - prevent Default?");
4763 this.scrollToElement(e);
4767 var p = this.parent();
4769 if (['tabs','pills'].indexOf(p.type)!==-1) {
4770 if (typeof(p.setActiveItem) !== 'undefined') {
4771 p.setActiveItem(this);
4775 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4776 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4777 // remove the collapsed menu expand...
4778 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4782 isActive: function () {
4785 setActive : function(state, fire, is_was_active)
4787 if (this.active && !state && this.navId) {
4788 this.was_active = true;
4789 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4791 nv.clearWasActive(this);
4795 this.active = state;
4798 this.el.removeClass('active');
4799 } else if (!this.el.hasClass('active')) {
4800 this.el.addClass('active');
4803 this.fireEvent('changed', this, state);
4806 // show a panel if it's registered and related..
4808 if (!this.navId || !this.tabId || !state || is_was_active) {
4812 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4816 var pan = tg.getPanelByName(this.tabId);
4820 // if we can not flip to new panel - go back to old nav highlight..
4821 if (false == tg.showPanel(pan)) {
4822 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4824 var onav = nv.getWasActive();
4826 onav.setActive(true, false, true);
4835 // this should not be here...
4836 setDisabled : function(state)
4838 this.disabled = state;
4840 this.el.removeClass('disabled');
4841 } else if (!this.el.hasClass('disabled')) {
4842 this.el.addClass('disabled');
4848 * Fetch the element to display the tooltip on.
4849 * @return {Roo.Element} defaults to this.el
4851 tooltipEl : function()
4853 return this.el.select('' + this.tagtype + '', true).first();
4856 scrollToElement : function(e)
4858 var c = document.body;
4861 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4863 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4864 c = document.documentElement;
4867 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4873 var o = target.calcOffsetsTo(c);
4880 this.fireEvent('scrollto', this, options, e);
4882 Roo.get(c).scrollTo('top', options.value, true);
4895 * <span> icon </span>
4896 * <span> text </span>
4897 * <span>badge </span>
4901 * @class Roo.bootstrap.NavSidebarItem
4902 * @extends Roo.bootstrap.NavItem
4903 * Bootstrap Navbar.NavSidebarItem class
4904 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4905 * {Boolean} open is the menu open
4906 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4907 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4908 * {String} buttonSize (sm|md|lg)the extra classes for the button
4909 * {Boolean} showArrow show arrow next to the text (default true)
4911 * Create a new Navbar Button
4912 * @param {Object} config The config object
4914 Roo.bootstrap.NavSidebarItem = function(config){
4915 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4920 * The raw click event for the entire grid.
4921 * @param {Roo.EventObject} e
4926 * Fires when the active item active state changes
4927 * @param {Roo.bootstrap.NavSidebarItem} this
4928 * @param {boolean} state the new state
4936 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4938 badgeWeight : 'default',
4944 buttonWeight : 'default',
4950 getAutoCreate : function(){
4955 href : this.href || '#',
4961 if(this.buttonView){
4964 href : this.href || '#',
4965 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4978 cfg.cls += ' active';
4981 if (this.disabled) {
4982 cfg.cls += ' disabled';
4985 cfg.cls += ' open x-open';
4988 if (this.glyphicon || this.icon) {
4989 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4990 a.cn.push({ tag : 'i', cls : c }) ;
4993 if(!this.buttonView){
4996 html : this.html || ''
5003 if (this.badge !== '') {
5004 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
5010 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5013 a.cls += ' dropdown-toggle treeview' ;
5019 initEvents : function()
5021 if (typeof (this.menu) != 'undefined') {
5022 this.menu.parentType = this.xtype;
5023 this.menu.triggerEl = this.el;
5024 this.menu = this.addxtype(Roo.apply({}, this.menu));
5027 this.el.on('click', this.onClick, this);
5029 if(this.badge !== ''){
5030 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5035 onClick : function(e)
5042 if(this.preventDefault){
5046 this.fireEvent('click', this);
5049 disable : function()
5051 this.setDisabled(true);
5056 this.setDisabled(false);
5059 setDisabled : function(state)
5061 if(this.disabled == state){
5065 this.disabled = state;
5068 this.el.addClass('disabled');
5072 this.el.removeClass('disabled');
5077 setActive : function(state)
5079 if(this.active == state){
5083 this.active = state;
5086 this.el.addClass('active');
5090 this.el.removeClass('active');
5095 isActive: function ()
5100 setBadge : function(str)
5106 this.badgeEl.dom.innerHTML = str;
5123 * @class Roo.bootstrap.Row
5124 * @extends Roo.bootstrap.Component
5125 * Bootstrap Row class (contains columns...)
5129 * @param {Object} config The config object
5132 Roo.bootstrap.Row = function(config){
5133 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5136 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5138 getAutoCreate : function(){
5157 * @class Roo.bootstrap.Element
5158 * @extends Roo.bootstrap.Component
5159 * Bootstrap Element class
5160 * @cfg {String} html contents of the element
5161 * @cfg {String} tag tag of the element
5162 * @cfg {String} cls class of the element
5163 * @cfg {Boolean} preventDefault (true|false) default false
5164 * @cfg {Boolean} clickable (true|false) default false
5167 * Create a new Element
5168 * @param {Object} config The config object
5171 Roo.bootstrap.Element = function(config){
5172 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5178 * When a element is chick
5179 * @param {Roo.bootstrap.Element} this
5180 * @param {Roo.EventObject} e
5186 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5191 preventDefault: false,
5194 getAutoCreate : function(){
5198 // cls: this.cls, double assign in parent class Component.js :: onRender
5205 initEvents: function()
5207 Roo.bootstrap.Element.superclass.initEvents.call(this);
5210 this.el.on('click', this.onClick, this);
5215 onClick : function(e)
5217 if(this.preventDefault){
5221 this.fireEvent('click', this, e);
5224 getValue : function()
5226 return this.el.dom.innerHTML;
5229 setValue : function(value)
5231 this.el.dom.innerHTML = value;
5246 * @class Roo.bootstrap.Pagination
5247 * @extends Roo.bootstrap.Component
5248 * Bootstrap Pagination class
5249 * @cfg {String} size xs | sm | md | lg
5250 * @cfg {Boolean} inverse false | true
5253 * Create a new Pagination
5254 * @param {Object} config The config object
5257 Roo.bootstrap.Pagination = function(config){
5258 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5261 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5267 getAutoCreate : function(){
5273 cfg.cls += ' inverse';
5279 cfg.cls += " " + this.cls;
5297 * @class Roo.bootstrap.PaginationItem
5298 * @extends Roo.bootstrap.Component
5299 * Bootstrap PaginationItem class
5300 * @cfg {String} html text
5301 * @cfg {String} href the link
5302 * @cfg {Boolean} preventDefault (true | false) default true
5303 * @cfg {Boolean} active (true | false) default false
5304 * @cfg {Boolean} disabled default false
5308 * Create a new PaginationItem
5309 * @param {Object} config The config object
5313 Roo.bootstrap.PaginationItem = function(config){
5314 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5319 * The raw click event for the entire grid.
5320 * @param {Roo.EventObject} e
5326 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5330 preventDefault: true,
5335 getAutoCreate : function(){
5341 href : this.href ? this.href : '#',
5342 html : this.html ? this.html : ''
5352 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5356 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5362 initEvents: function() {
5364 this.el.on('click', this.onClick, this);
5367 onClick : function(e)
5369 Roo.log('PaginationItem on click ');
5370 if(this.preventDefault){
5378 this.fireEvent('click', this, e);
5394 * @class Roo.bootstrap.Slider
5395 * @extends Roo.bootstrap.Component
5396 * Bootstrap Slider class
5399 * Create a new Slider
5400 * @param {Object} config The config object
5403 Roo.bootstrap.Slider = function(config){
5404 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5407 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5409 getAutoCreate : function(){
5413 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5417 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5429 * Ext JS Library 1.1.1
5430 * Copyright(c) 2006-2007, Ext JS, LLC.
5432 * Originally Released Under LGPL - original licence link has changed is not relivant.
5435 * <script type="text/javascript">
5440 * @class Roo.grid.ColumnModel
5441 * @extends Roo.util.Observable
5442 * This is the default implementation of a ColumnModel used by the Grid. It defines
5443 * the columns in the grid.
5446 var colModel = new Roo.grid.ColumnModel([
5447 {header: "Ticker", width: 60, sortable: true, locked: true},
5448 {header: "Company Name", width: 150, sortable: true},
5449 {header: "Market Cap.", width: 100, sortable: true},
5450 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5451 {header: "Employees", width: 100, sortable: true, resizable: false}
5456 * The config options listed for this class are options which may appear in each
5457 * individual column definition.
5458 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5460 * @param {Object} config An Array of column config objects. See this class's
5461 * config objects for details.
5463 Roo.grid.ColumnModel = function(config){
5465 * The config passed into the constructor
5467 this.config = config;
5470 // if no id, create one
5471 // if the column does not have a dataIndex mapping,
5472 // map it to the order it is in the config
5473 for(var i = 0, len = config.length; i < len; i++){
5475 if(typeof c.dataIndex == "undefined"){
5478 if(typeof c.renderer == "string"){
5479 c.renderer = Roo.util.Format[c.renderer];
5481 if(typeof c.id == "undefined"){
5484 if(c.editor && c.editor.xtype){
5485 c.editor = Roo.factory(c.editor, Roo.grid);
5487 if(c.editor && c.editor.isFormField){
5488 c.editor = new Roo.grid.GridEditor(c.editor);
5490 this.lookup[c.id] = c;
5494 * The width of columns which have no width specified (defaults to 100)
5497 this.defaultWidth = 100;
5500 * Default sortable of columns which have no sortable specified (defaults to false)
5503 this.defaultSortable = false;
5507 * @event widthchange
5508 * Fires when the width of a column changes.
5509 * @param {ColumnModel} this
5510 * @param {Number} columnIndex The column index
5511 * @param {Number} newWidth The new width
5513 "widthchange": true,
5515 * @event headerchange
5516 * Fires when the text of a header changes.
5517 * @param {ColumnModel} this
5518 * @param {Number} columnIndex The column index
5519 * @param {Number} newText The new header text
5521 "headerchange": true,
5523 * @event hiddenchange
5524 * Fires when a column is hidden or "unhidden".
5525 * @param {ColumnModel} this
5526 * @param {Number} columnIndex The column index
5527 * @param {Boolean} hidden true if hidden, false otherwise
5529 "hiddenchange": true,
5531 * @event columnmoved
5532 * Fires when a column is moved.
5533 * @param {ColumnModel} this
5534 * @param {Number} oldIndex
5535 * @param {Number} newIndex
5537 "columnmoved" : true,
5539 * @event columlockchange
5540 * Fires when a column's locked state is changed
5541 * @param {ColumnModel} this
5542 * @param {Number} colIndex
5543 * @param {Boolean} locked true if locked
5545 "columnlockchange" : true
5547 Roo.grid.ColumnModel.superclass.constructor.call(this);
5549 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5551 * @cfg {String} header The header text to display in the Grid view.
5554 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5555 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5556 * specified, the column's index is used as an index into the Record's data Array.
5559 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5560 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5563 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5564 * Defaults to the value of the {@link #defaultSortable} property.
5565 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5568 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5571 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5574 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5577 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5580 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5581 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5582 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5583 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5586 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5589 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5592 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5595 * @cfg {String} cursor (Optional)
5598 * @cfg {String} tooltip (Optional)
5601 * @cfg {Number} xs (Optional)
5604 * @cfg {Number} sm (Optional)
5607 * @cfg {Number} md (Optional)
5610 * @cfg {Number} lg (Optional)
5613 * Returns the id of the column at the specified index.
5614 * @param {Number} index The column index
5615 * @return {String} the id
5617 getColumnId : function(index){
5618 return this.config[index].id;
5622 * Returns the column for a specified id.
5623 * @param {String} id The column id
5624 * @return {Object} the column
5626 getColumnById : function(id){
5627 return this.lookup[id];
5632 * Returns the column for a specified dataIndex.
5633 * @param {String} dataIndex The column dataIndex
5634 * @return {Object|Boolean} the column or false if not found
5636 getColumnByDataIndex: function(dataIndex){
5637 var index = this.findColumnIndex(dataIndex);
5638 return index > -1 ? this.config[index] : false;
5642 * Returns the index for a specified column id.
5643 * @param {String} id The column id
5644 * @return {Number} the index, or -1 if not found
5646 getIndexById : function(id){
5647 for(var i = 0, len = this.config.length; i < len; i++){
5648 if(this.config[i].id == id){
5656 * Returns the index for a specified column dataIndex.
5657 * @param {String} dataIndex The column dataIndex
5658 * @return {Number} the index, or -1 if not found
5661 findColumnIndex : function(dataIndex){
5662 for(var i = 0, len = this.config.length; i < len; i++){
5663 if(this.config[i].dataIndex == dataIndex){
5671 moveColumn : function(oldIndex, newIndex){
5672 var c = this.config[oldIndex];
5673 this.config.splice(oldIndex, 1);
5674 this.config.splice(newIndex, 0, c);
5675 this.dataMap = null;
5676 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5679 isLocked : function(colIndex){
5680 return this.config[colIndex].locked === true;
5683 setLocked : function(colIndex, value, suppressEvent){
5684 if(this.isLocked(colIndex) == value){
5687 this.config[colIndex].locked = value;
5689 this.fireEvent("columnlockchange", this, colIndex, value);
5693 getTotalLockedWidth : function(){
5695 for(var i = 0; i < this.config.length; i++){
5696 if(this.isLocked(i) && !this.isHidden(i)){
5697 this.totalWidth += this.getColumnWidth(i);
5703 getLockedCount : function(){
5704 for(var i = 0, len = this.config.length; i < len; i++){
5705 if(!this.isLocked(i)){
5710 return this.config.length;
5714 * Returns the number of columns.
5717 getColumnCount : function(visibleOnly){
5718 if(visibleOnly === true){
5720 for(var i = 0, len = this.config.length; i < len; i++){
5721 if(!this.isHidden(i)){
5727 return this.config.length;
5731 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5732 * @param {Function} fn
5733 * @param {Object} scope (optional)
5734 * @return {Array} result
5736 getColumnsBy : function(fn, scope){
5738 for(var i = 0, len = this.config.length; i < len; i++){
5739 var c = this.config[i];
5740 if(fn.call(scope||this, c, i) === true){
5748 * Returns true if the specified column is sortable.
5749 * @param {Number} col The column index
5752 isSortable : function(col){
5753 if(typeof this.config[col].sortable == "undefined"){
5754 return this.defaultSortable;
5756 return this.config[col].sortable;
5760 * Returns the rendering (formatting) function defined for the column.
5761 * @param {Number} col The column index.
5762 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5764 getRenderer : function(col){
5765 if(!this.config[col].renderer){
5766 return Roo.grid.ColumnModel.defaultRenderer;
5768 return this.config[col].renderer;
5772 * Sets the rendering (formatting) function for a column.
5773 * @param {Number} col The column index
5774 * @param {Function} fn The function to use to process the cell's raw data
5775 * to return HTML markup for the grid view. The render function is called with
5776 * the following parameters:<ul>
5777 * <li>Data value.</li>
5778 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5779 * <li>css A CSS style string to apply to the table cell.</li>
5780 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5781 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5782 * <li>Row index</li>
5783 * <li>Column index</li>
5784 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5786 setRenderer : function(col, fn){
5787 this.config[col].renderer = fn;
5791 * Returns the width for the specified column.
5792 * @param {Number} col The column index
5795 getColumnWidth : function(col){
5796 return this.config[col].width * 1 || this.defaultWidth;
5800 * Sets the width for a column.
5801 * @param {Number} col The column index
5802 * @param {Number} width The new width
5804 setColumnWidth : function(col, width, suppressEvent){
5805 this.config[col].width = width;
5806 this.totalWidth = null;
5808 this.fireEvent("widthchange", this, col, width);
5813 * Returns the total width of all columns.
5814 * @param {Boolean} includeHidden True to include hidden column widths
5817 getTotalWidth : function(includeHidden){
5818 if(!this.totalWidth){
5819 this.totalWidth = 0;
5820 for(var i = 0, len = this.config.length; i < len; i++){
5821 if(includeHidden || !this.isHidden(i)){
5822 this.totalWidth += this.getColumnWidth(i);
5826 return this.totalWidth;
5830 * Returns the header for the specified column.
5831 * @param {Number} col The column index
5834 getColumnHeader : function(col){
5835 return this.config[col].header;
5839 * Sets the header for a column.
5840 * @param {Number} col The column index
5841 * @param {String} header The new header
5843 setColumnHeader : function(col, header){
5844 this.config[col].header = header;
5845 this.fireEvent("headerchange", this, col, header);
5849 * Returns the tooltip for the specified column.
5850 * @param {Number} col The column index
5853 getColumnTooltip : function(col){
5854 return this.config[col].tooltip;
5857 * Sets the tooltip for a column.
5858 * @param {Number} col The column index
5859 * @param {String} tooltip The new tooltip
5861 setColumnTooltip : function(col, tooltip){
5862 this.config[col].tooltip = tooltip;
5866 * Returns the dataIndex for the specified column.
5867 * @param {Number} col The column index
5870 getDataIndex : function(col){
5871 return this.config[col].dataIndex;
5875 * Sets the dataIndex for a column.
5876 * @param {Number} col The column index
5877 * @param {Number} dataIndex The new dataIndex
5879 setDataIndex : function(col, dataIndex){
5880 this.config[col].dataIndex = dataIndex;
5886 * Returns true if the cell is editable.
5887 * @param {Number} colIndex The column index
5888 * @param {Number} rowIndex The row index - this is nto actually used..?
5891 isCellEditable : function(colIndex, rowIndex){
5892 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5896 * Returns the editor defined for the cell/column.
5897 * return false or null to disable editing.
5898 * @param {Number} colIndex The column index
5899 * @param {Number} rowIndex The row index
5902 getCellEditor : function(colIndex, rowIndex){
5903 return this.config[colIndex].editor;
5907 * Sets if a column is editable.
5908 * @param {Number} col The column index
5909 * @param {Boolean} editable True if the column is editable
5911 setEditable : function(col, editable){
5912 this.config[col].editable = editable;
5917 * Returns true if the column is hidden.
5918 * @param {Number} colIndex The column index
5921 isHidden : function(colIndex){
5922 return this.config[colIndex].hidden;
5927 * Returns true if the column width cannot be changed
5929 isFixed : function(colIndex){
5930 return this.config[colIndex].fixed;
5934 * Returns true if the column can be resized
5937 isResizable : function(colIndex){
5938 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5941 * Sets if a column is hidden.
5942 * @param {Number} colIndex The column index
5943 * @param {Boolean} hidden True if the column is hidden
5945 setHidden : function(colIndex, hidden){
5946 this.config[colIndex].hidden = hidden;
5947 this.totalWidth = null;
5948 this.fireEvent("hiddenchange", this, colIndex, hidden);
5952 * Sets the editor for a column.
5953 * @param {Number} col The column index
5954 * @param {Object} editor The editor object
5956 setEditor : function(col, editor){
5957 this.config[col].editor = editor;
5961 Roo.grid.ColumnModel.defaultRenderer = function(value)
5963 if(typeof value == "object") {
5966 if(typeof value == "string" && value.length < 1){
5970 return String.format("{0}", value);
5973 // Alias for backwards compatibility
5974 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5977 * Ext JS Library 1.1.1
5978 * Copyright(c) 2006-2007, Ext JS, LLC.
5980 * Originally Released Under LGPL - original licence link has changed is not relivant.
5983 * <script type="text/javascript">
5987 * @class Roo.LoadMask
5988 * A simple utility class for generically masking elements while loading data. If the element being masked has
5989 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5990 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5991 * element's UpdateManager load indicator and will be destroyed after the initial load.
5993 * Create a new LoadMask
5994 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5995 * @param {Object} config The config object
5997 Roo.LoadMask = function(el, config){
5998 this.el = Roo.get(el);
5999 Roo.apply(this, config);
6001 this.store.on('beforeload', this.onBeforeLoad, this);
6002 this.store.on('load', this.onLoad, this);
6003 this.store.on('loadexception', this.onLoadException, this);
6004 this.removeMask = false;
6006 var um = this.el.getUpdateManager();
6007 um.showLoadIndicator = false; // disable the default indicator
6008 um.on('beforeupdate', this.onBeforeLoad, this);
6009 um.on('update', this.onLoad, this);
6010 um.on('failure', this.onLoad, this);
6011 this.removeMask = true;
6015 Roo.LoadMask.prototype = {
6017 * @cfg {Boolean} removeMask
6018 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6019 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
6023 * The text to display in a centered loading message box (defaults to 'Loading...')
6027 * @cfg {String} msgCls
6028 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6030 msgCls : 'x-mask-loading',
6033 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6039 * Disables the mask to prevent it from being displayed
6041 disable : function(){
6042 this.disabled = true;
6046 * Enables the mask so that it can be displayed
6048 enable : function(){
6049 this.disabled = false;
6052 onLoadException : function()
6056 if (typeof(arguments[3]) != 'undefined') {
6057 Roo.MessageBox.alert("Error loading",arguments[3]);
6061 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6062 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6069 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6074 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6078 onBeforeLoad : function(){
6080 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6085 destroy : function(){
6087 this.store.un('beforeload', this.onBeforeLoad, this);
6088 this.store.un('load', this.onLoad, this);
6089 this.store.un('loadexception', this.onLoadException, this);
6091 var um = this.el.getUpdateManager();
6092 um.un('beforeupdate', this.onBeforeLoad, this);
6093 um.un('update', this.onLoad, this);
6094 um.un('failure', this.onLoad, this);
6105 * @class Roo.bootstrap.Table
6106 * @extends Roo.bootstrap.Component
6107 * Bootstrap Table class
6108 * @cfg {String} cls table class
6109 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6110 * @cfg {String} bgcolor Specifies the background color for a table
6111 * @cfg {Number} border Specifies whether the table cells should have borders or not
6112 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6113 * @cfg {Number} cellspacing Specifies the space between cells
6114 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6115 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6116 * @cfg {String} sortable Specifies that the table should be sortable
6117 * @cfg {String} summary Specifies a summary of the content of a table
6118 * @cfg {Number} width Specifies the width of a table
6119 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6121 * @cfg {boolean} striped Should the rows be alternative striped
6122 * @cfg {boolean} bordered Add borders to the table
6123 * @cfg {boolean} hover Add hover highlighting
6124 * @cfg {boolean} condensed Format condensed
6125 * @cfg {boolean} responsive Format condensed
6126 * @cfg {Boolean} loadMask (true|false) default false
6127 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6128 * @cfg {Boolean} headerShow (true|false) generate thead, default true
6129 * @cfg {Boolean} rowSelection (true|false) default false
6130 * @cfg {Boolean} cellSelection (true|false) default false
6131 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6132 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
6133 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
6134 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6138 * Create a new Table
6139 * @param {Object} config The config object
6142 Roo.bootstrap.Table = function(config){
6143 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6148 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6149 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6150 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6151 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6153 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6155 this.sm.grid = this;
6156 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6157 this.sm = this.selModel;
6158 this.sm.xmodule = this.xmodule || false;
6161 if (this.cm && typeof(this.cm.config) == 'undefined') {
6162 this.colModel = new Roo.grid.ColumnModel(this.cm);
6163 this.cm = this.colModel;
6164 this.cm.xmodule = this.xmodule || false;
6167 this.store= Roo.factory(this.store, Roo.data);
6168 this.ds = this.store;
6169 this.ds.xmodule = this.xmodule || false;
6172 if (this.footer && this.store) {
6173 this.footer.dataSource = this.ds;
6174 this.footer = Roo.factory(this.footer);
6181 * Fires when a cell is clicked
6182 * @param {Roo.bootstrap.Table} this
6183 * @param {Roo.Element} el
6184 * @param {Number} rowIndex
6185 * @param {Number} columnIndex
6186 * @param {Roo.EventObject} e
6190 * @event celldblclick
6191 * Fires when a cell is double clicked
6192 * @param {Roo.bootstrap.Table} this
6193 * @param {Roo.Element} el
6194 * @param {Number} rowIndex
6195 * @param {Number} columnIndex
6196 * @param {Roo.EventObject} e
6198 "celldblclick" : true,
6201 * Fires when a row is clicked
6202 * @param {Roo.bootstrap.Table} this
6203 * @param {Roo.Element} el
6204 * @param {Number} rowIndex
6205 * @param {Roo.EventObject} e
6209 * @event rowdblclick
6210 * Fires when a row is double clicked
6211 * @param {Roo.bootstrap.Table} this
6212 * @param {Roo.Element} el
6213 * @param {Number} rowIndex
6214 * @param {Roo.EventObject} e
6216 "rowdblclick" : true,
6219 * Fires when a mouseover occur
6220 * @param {Roo.bootstrap.Table} this
6221 * @param {Roo.Element} el
6222 * @param {Number} rowIndex
6223 * @param {Number} columnIndex
6224 * @param {Roo.EventObject} e
6229 * Fires when a mouseout occur
6230 * @param {Roo.bootstrap.Table} this
6231 * @param {Roo.Element} el
6232 * @param {Number} rowIndex
6233 * @param {Number} columnIndex
6234 * @param {Roo.EventObject} e
6239 * Fires when a row is rendered, so you can change add a style to it.
6240 * @param {Roo.bootstrap.Table} this
6241 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6245 * @event rowsrendered
6246 * Fires when all the rows have been rendered
6247 * @param {Roo.bootstrap.Table} this
6249 'rowsrendered' : true,
6251 * @event contextmenu
6252 * The raw contextmenu event for the entire grid.
6253 * @param {Roo.EventObject} e
6255 "contextmenu" : true,
6257 * @event rowcontextmenu
6258 * Fires when a row is right clicked
6259 * @param {Roo.bootstrap.Table} this
6260 * @param {Number} rowIndex
6261 * @param {Roo.EventObject} e
6263 "rowcontextmenu" : true,
6265 * @event cellcontextmenu
6266 * Fires when a cell is right clicked
6267 * @param {Roo.bootstrap.Table} this
6268 * @param {Number} rowIndex
6269 * @param {Number} cellIndex
6270 * @param {Roo.EventObject} e
6272 "cellcontextmenu" : true,
6274 * @event headercontextmenu
6275 * Fires when a header is right clicked
6276 * @param {Roo.bootstrap.Table} this
6277 * @param {Number} columnIndex
6278 * @param {Roo.EventObject} e
6280 "headercontextmenu" : true
6284 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6310 rowSelection : false,
6311 cellSelection : false,
6314 // Roo.Element - the tbody
6316 // Roo.Element - thead element
6319 container: false, // used by gridpanel...
6325 auto_hide_footer : false,
6327 getAutoCreate : function()
6329 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6336 if (this.scrollBody) {
6337 cfg.cls += ' table-body-fixed';
6340 cfg.cls += ' table-striped';
6344 cfg.cls += ' table-hover';
6346 if (this.bordered) {
6347 cfg.cls += ' table-bordered';
6349 if (this.condensed) {
6350 cfg.cls += ' table-condensed';
6352 if (this.responsive) {
6353 cfg.cls += ' table-responsive';
6357 cfg.cls+= ' ' +this.cls;
6360 // this lot should be simplifed...
6373 ].forEach(function(k) {
6381 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6384 if(this.store || this.cm){
6385 if(this.headerShow){
6386 cfg.cn.push(this.renderHeader());
6389 cfg.cn.push(this.renderBody());
6391 if(this.footerShow){
6392 cfg.cn.push(this.renderFooter());
6394 // where does this come from?
6395 //cfg.cls+= ' TableGrid';
6398 return { cn : [ cfg ] };
6401 initEvents : function()
6403 if(!this.store || !this.cm){
6406 if (this.selModel) {
6407 this.selModel.initEvents();
6411 //Roo.log('initEvents with ds!!!!');
6413 this.mainBody = this.el.select('tbody', true).first();
6414 this.mainHead = this.el.select('thead', true).first();
6415 this.mainFoot = this.el.select('tfoot', true).first();
6421 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6422 e.on('click', _this.sort, _this);
6425 this.mainBody.on("click", this.onClick, this);
6426 this.mainBody.on("dblclick", this.onDblClick, this);
6428 // why is this done????? = it breaks dialogs??
6429 //this.parent().el.setStyle('position', 'relative');
6433 this.footer.parentId = this.id;
6434 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6437 this.el.select('tfoot tr td').first().addClass('hide');
6442 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6445 this.store.on('load', this.onLoad, this);
6446 this.store.on('beforeload', this.onBeforeLoad, this);
6447 this.store.on('update', this.onUpdate, this);
6448 this.store.on('add', this.onAdd, this);
6449 this.store.on("clear", this.clear, this);
6451 this.el.on("contextmenu", this.onContextMenu, this);
6453 this.mainBody.on('scroll', this.onBodyScroll, this);
6455 this.cm.on("headerchange", this.onHeaderChange, this);
6457 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6461 onContextMenu : function(e, t)
6463 this.processEvent("contextmenu", e);
6466 processEvent : function(name, e)
6468 if (name != 'touchstart' ) {
6469 this.fireEvent(name, e);
6472 var t = e.getTarget();
6474 var cell = Roo.get(t);
6480 if(cell.findParent('tfoot', false, true)){
6484 if(cell.findParent('thead', false, true)){
6486 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6487 cell = Roo.get(t).findParent('th', false, true);
6489 Roo.log("failed to find th in thead?");
6490 Roo.log(e.getTarget());
6495 var cellIndex = cell.dom.cellIndex;
6497 var ename = name == 'touchstart' ? 'click' : name;
6498 this.fireEvent("header" + ename, this, cellIndex, e);
6503 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6504 cell = Roo.get(t).findParent('td', false, true);
6506 Roo.log("failed to find th in tbody?");
6507 Roo.log(e.getTarget());
6512 var row = cell.findParent('tr', false, true);
6513 var cellIndex = cell.dom.cellIndex;
6514 var rowIndex = row.dom.rowIndex - 1;
6518 this.fireEvent("row" + name, this, rowIndex, e);
6522 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6528 onMouseover : function(e, el)
6530 var cell = Roo.get(el);
6536 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6537 cell = cell.findParent('td', false, true);
6540 var row = cell.findParent('tr', false, true);
6541 var cellIndex = cell.dom.cellIndex;
6542 var rowIndex = row.dom.rowIndex - 1; // start from 0
6544 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6548 onMouseout : function(e, el)
6550 var cell = Roo.get(el);
6556 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6557 cell = cell.findParent('td', false, true);
6560 var row = cell.findParent('tr', false, true);
6561 var cellIndex = cell.dom.cellIndex;
6562 var rowIndex = row.dom.rowIndex - 1; // start from 0
6564 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6568 onClick : function(e, el)
6570 var cell = Roo.get(el);
6572 if(!cell || (!this.cellSelection && !this.rowSelection)){
6576 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6577 cell = cell.findParent('td', false, true);
6580 if(!cell || typeof(cell) == 'undefined'){
6584 var row = cell.findParent('tr', false, true);
6586 if(!row || typeof(row) == 'undefined'){
6590 var cellIndex = cell.dom.cellIndex;
6591 var rowIndex = this.getRowIndex(row);
6593 // why??? - should these not be based on SelectionModel?
6594 if(this.cellSelection){
6595 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6598 if(this.rowSelection){
6599 this.fireEvent('rowclick', this, row, rowIndex, e);
6605 onDblClick : function(e,el)
6607 var cell = Roo.get(el);
6609 if(!cell || (!this.cellSelection && !this.rowSelection)){
6613 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6614 cell = cell.findParent('td', false, true);
6617 if(!cell || typeof(cell) == 'undefined'){
6621 var row = cell.findParent('tr', false, true);
6623 if(!row || typeof(row) == 'undefined'){
6627 var cellIndex = cell.dom.cellIndex;
6628 var rowIndex = this.getRowIndex(row);
6630 if(this.cellSelection){
6631 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6634 if(this.rowSelection){
6635 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6639 sort : function(e,el)
6641 var col = Roo.get(el);
6643 if(!col.hasClass('sortable')){
6647 var sort = col.attr('sort');
6650 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6654 this.store.sortInfo = {field : sort, direction : dir};
6657 Roo.log("calling footer first");
6658 this.footer.onClick('first');
6661 this.store.load({ params : { start : 0 } });
6665 renderHeader : function()
6673 this.totalWidth = 0;
6675 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6677 var config = cm.config[i];
6681 cls : 'x-hcol-' + i,
6683 html: cm.getColumnHeader(i)
6688 if(typeof(config.sortable) != 'undefined' && config.sortable){
6690 c.html = '<i class="glyphicon"></i>' + c.html;
6693 if(typeof(config.lgHeader) != 'undefined'){
6694 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6697 if(typeof(config.mdHeader) != 'undefined'){
6698 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6701 if(typeof(config.smHeader) != 'undefined'){
6702 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6705 if(typeof(config.xsHeader) != 'undefined'){
6706 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6713 if(typeof(config.tooltip) != 'undefined'){
6714 c.tooltip = config.tooltip;
6717 if(typeof(config.colspan) != 'undefined'){
6718 c.colspan = config.colspan;
6721 if(typeof(config.hidden) != 'undefined' && config.hidden){
6722 c.style += ' display:none;';
6725 if(typeof(config.dataIndex) != 'undefined'){
6726 c.sort = config.dataIndex;
6731 if(typeof(config.align) != 'undefined' && config.align.length){
6732 c.style += ' text-align:' + config.align + ';';
6735 if(typeof(config.width) != 'undefined'){
6736 c.style += ' width:' + config.width + 'px;';
6737 this.totalWidth += config.width;
6739 this.totalWidth += 100; // assume minimum of 100 per column?
6742 if(typeof(config.cls) != 'undefined'){
6743 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6746 ['xs','sm','md','lg'].map(function(size){
6748 if(typeof(config[size]) == 'undefined'){
6752 if (!config[size]) { // 0 = hidden
6753 c.cls += ' hidden-' + size;
6757 c.cls += ' col-' + size + '-' + config[size];
6767 renderBody : function()
6777 colspan : this.cm.getColumnCount()
6787 renderFooter : function()
6797 colspan : this.cm.getColumnCount()
6811 // Roo.log('ds onload');
6816 var ds = this.store;
6818 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6819 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6820 if (_this.store.sortInfo) {
6822 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6823 e.select('i', true).addClass(['glyphicon-arrow-up']);
6826 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6827 e.select('i', true).addClass(['glyphicon-arrow-down']);
6832 var tbody = this.mainBody;
6834 if(ds.getCount() > 0){
6835 ds.data.each(function(d,rowIndex){
6836 var row = this.renderRow(cm, ds, rowIndex);
6838 tbody.createChild(row);
6842 if(row.cellObjects.length){
6843 Roo.each(row.cellObjects, function(r){
6844 _this.renderCellObject(r);
6851 var tfoot = this.el.select('tfoot', true).first();
6853 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6855 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6857 var total = this.ds.getTotalCount();
6859 if(this.footer.pageSize < total){
6860 this.mainFoot.show();
6864 Roo.each(this.el.select('tbody td', true).elements, function(e){
6865 e.on('mouseover', _this.onMouseover, _this);
6868 Roo.each(this.el.select('tbody td', true).elements, function(e){
6869 e.on('mouseout', _this.onMouseout, _this);
6871 this.fireEvent('rowsrendered', this);
6877 onUpdate : function(ds,record)
6879 this.refreshRow(record);
6883 onRemove : function(ds, record, index, isUpdate){
6884 if(isUpdate !== true){
6885 this.fireEvent("beforerowremoved", this, index, record);
6887 var bt = this.mainBody.dom;
6889 var rows = this.el.select('tbody > tr', true).elements;
6891 if(typeof(rows[index]) != 'undefined'){
6892 bt.removeChild(rows[index].dom);
6895 // if(bt.rows[index]){
6896 // bt.removeChild(bt.rows[index]);
6899 if(isUpdate !== true){
6900 //this.stripeRows(index);
6901 //this.syncRowHeights(index, index);
6903 this.fireEvent("rowremoved", this, index, record);
6907 onAdd : function(ds, records, rowIndex)
6909 //Roo.log('on Add called');
6910 // - note this does not handle multiple adding very well..
6911 var bt = this.mainBody.dom;
6912 for (var i =0 ; i < records.length;i++) {
6913 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6914 //Roo.log(records[i]);
6915 //Roo.log(this.store.getAt(rowIndex+i));
6916 this.insertRow(this.store, rowIndex + i, false);
6923 refreshRow : function(record){
6924 var ds = this.store, index;
6925 if(typeof record == 'number'){
6927 record = ds.getAt(index);
6929 index = ds.indexOf(record);
6931 this.insertRow(ds, index, true);
6933 this.onRemove(ds, record, index+1, true);
6935 //this.syncRowHeights(index, index);
6937 this.fireEvent("rowupdated", this, index, record);
6940 insertRow : function(dm, rowIndex, isUpdate){
6943 this.fireEvent("beforerowsinserted", this, rowIndex);
6945 //var s = this.getScrollState();
6946 var row = this.renderRow(this.cm, this.store, rowIndex);
6947 // insert before rowIndex..
6948 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6952 if(row.cellObjects.length){
6953 Roo.each(row.cellObjects, function(r){
6954 _this.renderCellObject(r);
6959 this.fireEvent("rowsinserted", this, rowIndex);
6960 //this.syncRowHeights(firstRow, lastRow);
6961 //this.stripeRows(firstRow);
6968 getRowDom : function(rowIndex)
6970 var rows = this.el.select('tbody > tr', true).elements;
6972 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6975 // returns the object tree for a tr..
6978 renderRow : function(cm, ds, rowIndex)
6980 var d = ds.getAt(rowIndex);
6984 cls : 'x-row-' + rowIndex,
6988 var cellObjects = [];
6990 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6991 var config = cm.config[i];
6993 var renderer = cm.getRenderer(i);
6997 if(typeof(renderer) !== 'undefined'){
6998 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7000 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7001 // and are rendered into the cells after the row is rendered - using the id for the element.
7003 if(typeof(value) === 'object'){
7013 rowIndex : rowIndex,
7018 this.fireEvent('rowclass', this, rowcfg);
7022 cls : rowcfg.rowClass + ' x-col-' + i,
7024 html: (typeof(value) === 'object') ? '' : value
7031 if(typeof(config.colspan) != 'undefined'){
7032 td.colspan = config.colspan;
7035 if(typeof(config.hidden) != 'undefined' && config.hidden){
7036 td.style += ' display:none;';
7039 if(typeof(config.align) != 'undefined' && config.align.length){
7040 td.style += ' text-align:' + config.align + ';';
7042 if(typeof(config.valign) != 'undefined' && config.valign.length){
7043 td.style += ' vertical-align:' + config.valign + ';';
7046 if(typeof(config.width) != 'undefined'){
7047 td.style += ' width:' + config.width + 'px;';
7050 if(typeof(config.cursor) != 'undefined'){
7051 td.style += ' cursor:' + config.cursor + ';';
7054 if(typeof(config.cls) != 'undefined'){
7055 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7058 ['xs','sm','md','lg'].map(function(size){
7060 if(typeof(config[size]) == 'undefined'){
7064 if (!config[size]) { // 0 = hidden
7065 td.cls += ' hidden-' + size;
7069 td.cls += ' col-' + size + '-' + config[size];
7077 row.cellObjects = cellObjects;
7085 onBeforeLoad : function()
7094 this.el.select('tbody', true).first().dom.innerHTML = '';
7097 * Show or hide a row.
7098 * @param {Number} rowIndex to show or hide
7099 * @param {Boolean} state hide
7101 setRowVisibility : function(rowIndex, state)
7103 var bt = this.mainBody.dom;
7105 var rows = this.el.select('tbody > tr', true).elements;
7107 if(typeof(rows[rowIndex]) == 'undefined'){
7110 rows[rowIndex].dom.style.display = state ? '' : 'none';
7114 getSelectionModel : function(){
7116 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7118 return this.selModel;
7121 * Render the Roo.bootstrap object from renderder
7123 renderCellObject : function(r)
7127 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7129 var t = r.cfg.render(r.container);
7132 Roo.each(r.cfg.cn, function(c){
7134 container: t.getChildContainer(),
7137 _this.renderCellObject(child);
7142 getRowIndex : function(row)
7146 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7157 * Returns the grid's underlying element = used by panel.Grid
7158 * @return {Element} The element
7160 getGridEl : function(){
7164 * Forces a resize - used by panel.Grid
7165 * @return {Element} The element
7167 autoSize : function()
7169 //var ctr = Roo.get(this.container.dom.parentElement);
7170 var ctr = Roo.get(this.el.dom);
7172 var thd = this.getGridEl().select('thead',true).first();
7173 var tbd = this.getGridEl().select('tbody', true).first();
7174 var tfd = this.getGridEl().select('tfoot', true).first();
7176 var cw = ctr.getWidth();
7180 tbd.setSize(ctr.getWidth(),
7181 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7183 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7186 cw = Math.max(cw, this.totalWidth);
7187 this.getGridEl().select('tr',true).setWidth(cw);
7188 // resize 'expandable coloumn?
7190 return; // we doe not have a view in this design..
7193 onBodyScroll: function()
7195 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7197 this.mainHead.setStyle({
7198 'position' : 'relative',
7199 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7205 var scrollHeight = this.mainBody.dom.scrollHeight;
7207 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7209 var height = this.mainBody.getHeight();
7211 if(scrollHeight - height == scrollTop) {
7213 var total = this.ds.getTotalCount();
7215 if(this.footer.cursor + this.footer.pageSize < total){
7217 this.footer.ds.load({
7219 start : this.footer.cursor + this.footer.pageSize,
7220 limit : this.footer.pageSize
7230 onHeaderChange : function()
7232 var header = this.renderHeader();
7233 var table = this.el.select('table', true).first();
7235 this.mainHead.remove();
7236 this.mainHead = table.createChild(header, this.mainBody, false);
7239 onHiddenChange : function(colModel, colIndex, hidden)
7241 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7242 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7244 this.CSS.updateRule(thSelector, "display", "");
7245 this.CSS.updateRule(tdSelector, "display", "");
7248 this.CSS.updateRule(thSelector, "display", "none");
7249 this.CSS.updateRule(tdSelector, "display", "none");
7252 this.onHeaderChange();
7256 setColumnWidth: function(col_index, width)
7258 // width = "md-2 xs-2..."
7259 if(!this.colModel.config[col_index]) {
7263 var w = width.split(" ");
7265 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7267 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7270 for(var j = 0; j < w.length; j++) {
7276 var size_cls = w[j].split("-");
7278 if(!Number.isInteger(size_cls[1] * 1)) {
7282 if(!this.colModel.config[col_index][size_cls[0]]) {
7286 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7290 h_row[0].classList.replace(
7291 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7292 "col-"+size_cls[0]+"-"+size_cls[1]
7295 for(var i = 0; i < rows.length; i++) {
7297 var size_cls = w[j].split("-");
7299 if(!Number.isInteger(size_cls[1] * 1)) {
7303 if(!this.colModel.config[col_index][size_cls[0]]) {
7307 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7311 rows[i].classList.replace(
7312 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7313 "col-"+size_cls[0]+"-"+size_cls[1]
7317 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7332 * @class Roo.bootstrap.TableCell
7333 * @extends Roo.bootstrap.Component
7334 * Bootstrap TableCell class
7335 * @cfg {String} html cell contain text
7336 * @cfg {String} cls cell class
7337 * @cfg {String} tag cell tag (td|th) default td
7338 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7339 * @cfg {String} align Aligns the content in a cell
7340 * @cfg {String} axis Categorizes cells
7341 * @cfg {String} bgcolor Specifies the background color of a cell
7342 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7343 * @cfg {Number} colspan Specifies the number of columns a cell should span
7344 * @cfg {String} headers Specifies one or more header cells a cell is related to
7345 * @cfg {Number} height Sets the height of a cell
7346 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7347 * @cfg {Number} rowspan Sets the number of rows a cell should span
7348 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7349 * @cfg {String} valign Vertical aligns the content in a cell
7350 * @cfg {Number} width Specifies the width of a cell
7353 * Create a new TableCell
7354 * @param {Object} config The config object
7357 Roo.bootstrap.TableCell = function(config){
7358 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7361 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7381 getAutoCreate : function(){
7382 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7402 cfg.align=this.align
7408 cfg.bgcolor=this.bgcolor
7411 cfg.charoff=this.charoff
7414 cfg.colspan=this.colspan
7417 cfg.headers=this.headers
7420 cfg.height=this.height
7423 cfg.nowrap=this.nowrap
7426 cfg.rowspan=this.rowspan
7429 cfg.scope=this.scope
7432 cfg.valign=this.valign
7435 cfg.width=this.width
7454 * @class Roo.bootstrap.TableRow
7455 * @extends Roo.bootstrap.Component
7456 * Bootstrap TableRow class
7457 * @cfg {String} cls row class
7458 * @cfg {String} align Aligns the content in a table row
7459 * @cfg {String} bgcolor Specifies a background color for a table row
7460 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7461 * @cfg {String} valign Vertical aligns the content in a table row
7464 * Create a new TableRow
7465 * @param {Object} config The config object
7468 Roo.bootstrap.TableRow = function(config){
7469 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7472 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7480 getAutoCreate : function(){
7481 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7491 cfg.align = this.align;
7494 cfg.bgcolor = this.bgcolor;
7497 cfg.charoff = this.charoff;
7500 cfg.valign = this.valign;
7518 * @class Roo.bootstrap.TableBody
7519 * @extends Roo.bootstrap.Component
7520 * Bootstrap TableBody class
7521 * @cfg {String} cls element class
7522 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7523 * @cfg {String} align Aligns the content inside the element
7524 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7525 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7528 * Create a new TableBody
7529 * @param {Object} config The config object
7532 Roo.bootstrap.TableBody = function(config){
7533 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7536 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7544 getAutoCreate : function(){
7545 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7559 cfg.align = this.align;
7562 cfg.charoff = this.charoff;
7565 cfg.valign = this.valign;
7572 // initEvents : function()
7579 // this.store = Roo.factory(this.store, Roo.data);
7580 // this.store.on('load', this.onLoad, this);
7582 // this.store.load();
7586 // onLoad: function ()
7588 // this.fireEvent('load', this);
7598 * Ext JS Library 1.1.1
7599 * Copyright(c) 2006-2007, Ext JS, LLC.
7601 * Originally Released Under LGPL - original licence link has changed is not relivant.
7604 * <script type="text/javascript">
7607 // as we use this in bootstrap.
7608 Roo.namespace('Roo.form');
7610 * @class Roo.form.Action
7611 * Internal Class used to handle form actions
7613 * @param {Roo.form.BasicForm} el The form element or its id
7614 * @param {Object} config Configuration options
7619 // define the action interface
7620 Roo.form.Action = function(form, options){
7622 this.options = options || {};
7625 * Client Validation Failed
7628 Roo.form.Action.CLIENT_INVALID = 'client';
7630 * Server Validation Failed
7633 Roo.form.Action.SERVER_INVALID = 'server';
7635 * Connect to Server Failed
7638 Roo.form.Action.CONNECT_FAILURE = 'connect';
7640 * Reading Data from Server Failed
7643 Roo.form.Action.LOAD_FAILURE = 'load';
7645 Roo.form.Action.prototype = {
7647 failureType : undefined,
7648 response : undefined,
7652 run : function(options){
7657 success : function(response){
7662 handleResponse : function(response){
7666 // default connection failure
7667 failure : function(response){
7669 this.response = response;
7670 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7671 this.form.afterAction(this, false);
7674 processResponse : function(response){
7675 this.response = response;
7676 if(!response.responseText){
7679 this.result = this.handleResponse(response);
7683 // utility functions used internally
7684 getUrl : function(appendParams){
7685 var url = this.options.url || this.form.url || this.form.el.dom.action;
7687 var p = this.getParams();
7689 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7695 getMethod : function(){
7696 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7699 getParams : function(){
7700 var bp = this.form.baseParams;
7701 var p = this.options.params;
7703 if(typeof p == "object"){
7704 p = Roo.urlEncode(Roo.applyIf(p, bp));
7705 }else if(typeof p == 'string' && bp){
7706 p += '&' + Roo.urlEncode(bp);
7709 p = Roo.urlEncode(bp);
7714 createCallback : function(){
7716 success: this.success,
7717 failure: this.failure,
7719 timeout: (this.form.timeout*1000),
7720 upload: this.form.fileUpload ? this.success : undefined
7725 Roo.form.Action.Submit = function(form, options){
7726 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7729 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7732 haveProgress : false,
7733 uploadComplete : false,
7735 // uploadProgress indicator.
7736 uploadProgress : function()
7738 if (!this.form.progressUrl) {
7742 if (!this.haveProgress) {
7743 Roo.MessageBox.progress("Uploading", "Uploading");
7745 if (this.uploadComplete) {
7746 Roo.MessageBox.hide();
7750 this.haveProgress = true;
7752 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7754 var c = new Roo.data.Connection();
7756 url : this.form.progressUrl,
7761 success : function(req){
7762 //console.log(data);
7766 rdata = Roo.decode(req.responseText)
7768 Roo.log("Invalid data from server..");
7772 if (!rdata || !rdata.success) {
7774 Roo.MessageBox.alert(Roo.encode(rdata));
7777 var data = rdata.data;
7779 if (this.uploadComplete) {
7780 Roo.MessageBox.hide();
7785 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7786 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7789 this.uploadProgress.defer(2000,this);
7792 failure: function(data) {
7793 Roo.log('progress url failed ');
7804 // run get Values on the form, so it syncs any secondary forms.
7805 this.form.getValues();
7807 var o = this.options;
7808 var method = this.getMethod();
7809 var isPost = method == 'POST';
7810 if(o.clientValidation === false || this.form.isValid()){
7812 if (this.form.progressUrl) {
7813 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7814 (new Date() * 1) + '' + Math.random());
7819 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7820 form:this.form.el.dom,
7821 url:this.getUrl(!isPost),
7823 params:isPost ? this.getParams() : null,
7824 isUpload: this.form.fileUpload
7827 this.uploadProgress();
7829 }else if (o.clientValidation !== false){ // client validation failed
7830 this.failureType = Roo.form.Action.CLIENT_INVALID;
7831 this.form.afterAction(this, false);
7835 success : function(response)
7837 this.uploadComplete= true;
7838 if (this.haveProgress) {
7839 Roo.MessageBox.hide();
7843 var result = this.processResponse(response);
7844 if(result === true || result.success){
7845 this.form.afterAction(this, true);
7849 this.form.markInvalid(result.errors);
7850 this.failureType = Roo.form.Action.SERVER_INVALID;
7852 this.form.afterAction(this, false);
7854 failure : function(response)
7856 this.uploadComplete= true;
7857 if (this.haveProgress) {
7858 Roo.MessageBox.hide();
7861 this.response = response;
7862 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7863 this.form.afterAction(this, false);
7866 handleResponse : function(response){
7867 if(this.form.errorReader){
7868 var rs = this.form.errorReader.read(response);
7871 for(var i = 0, len = rs.records.length; i < len; i++) {
7872 var r = rs.records[i];
7876 if(errors.length < 1){
7880 success : rs.success,
7886 ret = Roo.decode(response.responseText);
7890 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7900 Roo.form.Action.Load = function(form, options){
7901 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7902 this.reader = this.form.reader;
7905 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7910 Roo.Ajax.request(Roo.apply(
7911 this.createCallback(), {
7912 method:this.getMethod(),
7913 url:this.getUrl(false),
7914 params:this.getParams()
7918 success : function(response){
7920 var result = this.processResponse(response);
7921 if(result === true || !result.success || !result.data){
7922 this.failureType = Roo.form.Action.LOAD_FAILURE;
7923 this.form.afterAction(this, false);
7926 this.form.clearInvalid();
7927 this.form.setValues(result.data);
7928 this.form.afterAction(this, true);
7931 handleResponse : function(response){
7932 if(this.form.reader){
7933 var rs = this.form.reader.read(response);
7934 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7936 success : rs.success,
7940 return Roo.decode(response.responseText);
7944 Roo.form.Action.ACTION_TYPES = {
7945 'load' : Roo.form.Action.Load,
7946 'submit' : Roo.form.Action.Submit
7955 * @class Roo.bootstrap.Form
7956 * @extends Roo.bootstrap.Component
7957 * Bootstrap Form class
7958 * @cfg {String} method GET | POST (default POST)
7959 * @cfg {String} labelAlign top | left (default top)
7960 * @cfg {String} align left | right - for navbars
7961 * @cfg {Boolean} loadMask load mask when submit (default true)
7966 * @param {Object} config The config object
7970 Roo.bootstrap.Form = function(config){
7972 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7974 Roo.bootstrap.Form.popover.apply();
7978 * @event clientvalidation
7979 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7980 * @param {Form} this
7981 * @param {Boolean} valid true if the form has passed client-side validation
7983 clientvalidation: true,
7985 * @event beforeaction
7986 * Fires before any action is performed. Return false to cancel the action.
7987 * @param {Form} this
7988 * @param {Action} action The action to be performed
7992 * @event actionfailed
7993 * Fires when an action fails.
7994 * @param {Form} this
7995 * @param {Action} action The action that failed
7997 actionfailed : true,
7999 * @event actioncomplete
8000 * Fires when an action is completed.
8001 * @param {Form} this
8002 * @param {Action} action The action that completed
8004 actioncomplete : true
8008 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
8011 * @cfg {String} method
8012 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8017 * The URL to use for form actions if one isn't supplied in the action options.
8020 * @cfg {Boolean} fileUpload
8021 * Set to true if this form is a file upload.
8025 * @cfg {Object} baseParams
8026 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8030 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8034 * @cfg {Sting} align (left|right) for navbar forms
8039 activeAction : null,
8042 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8043 * element by passing it or its id or mask the form itself by passing in true.
8046 waitMsgTarget : false,
8051 * @cfg {Boolean} errorMask (true|false) default false
8056 * @cfg {Number} maskOffset Default 100
8061 * @cfg {Boolean} maskBody
8065 getAutoCreate : function(){
8069 method : this.method || 'POST',
8070 id : this.id || Roo.id(),
8073 if (this.parent().xtype.match(/^Nav/)) {
8074 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8078 if (this.labelAlign == 'left' ) {
8079 cfg.cls += ' form-horizontal';
8085 initEvents : function()
8087 this.el.on('submit', this.onSubmit, this);
8088 // this was added as random key presses on the form where triggering form submit.
8089 this.el.on('keypress', function(e) {
8090 if (e.getCharCode() != 13) {
8093 // we might need to allow it for textareas.. and some other items.
8094 // check e.getTarget().
8096 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8100 Roo.log("keypress blocked");
8108 onSubmit : function(e){
8113 * Returns true if client-side validation on the form is successful.
8116 isValid : function(){
8117 var items = this.getItems();
8121 items.each(function(f){
8127 Roo.log('invalid field: ' + f.name);
8131 if(!target && f.el.isVisible(true)){
8137 if(this.errorMask && !valid){
8138 Roo.bootstrap.Form.popover.mask(this, target);
8145 * Returns true if any fields in this form have changed since their original load.
8148 isDirty : function(){
8150 var items = this.getItems();
8151 items.each(function(f){
8161 * Performs a predefined action (submit or load) or custom actions you define on this form.
8162 * @param {String} actionName The name of the action type
8163 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8164 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8165 * accept other config options):
8167 Property Type Description
8168 ---------------- --------------- ----------------------------------------------------------------------------------
8169 url String The url for the action (defaults to the form's url)
8170 method String The form method to use (defaults to the form's method, or POST if not defined)
8171 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8172 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8173 validate the form on the client (defaults to false)
8175 * @return {BasicForm} this
8177 doAction : function(action, options){
8178 if(typeof action == 'string'){
8179 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8181 if(this.fireEvent('beforeaction', this, action) !== false){
8182 this.beforeAction(action);
8183 action.run.defer(100, action);
8189 beforeAction : function(action){
8190 var o = action.options;
8195 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8197 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8200 // not really supported yet.. ??
8202 //if(this.waitMsgTarget === true){
8203 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8204 //}else if(this.waitMsgTarget){
8205 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8206 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8208 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8214 afterAction : function(action, success){
8215 this.activeAction = null;
8216 var o = action.options;
8221 Roo.get(document.body).unmask();
8227 //if(this.waitMsgTarget === true){
8228 // this.el.unmask();
8229 //}else if(this.waitMsgTarget){
8230 // this.waitMsgTarget.unmask();
8232 // Roo.MessageBox.updateProgress(1);
8233 // Roo.MessageBox.hide();
8240 Roo.callback(o.success, o.scope, [this, action]);
8241 this.fireEvent('actioncomplete', this, action);
8245 // failure condition..
8246 // we have a scenario where updates need confirming.
8247 // eg. if a locking scenario exists..
8248 // we look for { errors : { needs_confirm : true }} in the response.
8250 (typeof(action.result) != 'undefined') &&
8251 (typeof(action.result.errors) != 'undefined') &&
8252 (typeof(action.result.errors.needs_confirm) != 'undefined')
8255 Roo.log("not supported yet");
8258 Roo.MessageBox.confirm(
8259 "Change requires confirmation",
8260 action.result.errorMsg,
8265 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8275 Roo.callback(o.failure, o.scope, [this, action]);
8276 // show an error message if no failed handler is set..
8277 if (!this.hasListener('actionfailed')) {
8278 Roo.log("need to add dialog support");
8280 Roo.MessageBox.alert("Error",
8281 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8282 action.result.errorMsg :
8283 "Saving Failed, please check your entries or try again"
8288 this.fireEvent('actionfailed', this, action);
8293 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8294 * @param {String} id The value to search for
8297 findField : function(id){
8298 var items = this.getItems();
8299 var field = items.get(id);
8301 items.each(function(f){
8302 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8309 return field || null;
8312 * Mark fields in this form invalid in bulk.
8313 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8314 * @return {BasicForm} this
8316 markInvalid : function(errors){
8317 if(errors instanceof Array){
8318 for(var i = 0, len = errors.length; i < len; i++){
8319 var fieldError = errors[i];
8320 var f = this.findField(fieldError.id);
8322 f.markInvalid(fieldError.msg);
8328 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8329 field.markInvalid(errors[id]);
8333 //Roo.each(this.childForms || [], function (f) {
8334 // f.markInvalid(errors);
8341 * Set values for fields in this form in bulk.
8342 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8343 * @return {BasicForm} this
8345 setValues : function(values){
8346 if(values instanceof Array){ // array of objects
8347 for(var i = 0, len = values.length; i < len; i++){
8349 var f = this.findField(v.id);
8351 f.setValue(v.value);
8352 if(this.trackResetOnLoad){
8353 f.originalValue = f.getValue();
8357 }else{ // object hash
8360 if(typeof values[id] != 'function' && (field = this.findField(id))){
8362 if (field.setFromData &&
8364 field.displayField &&
8365 // combos' with local stores can
8366 // be queried via setValue()
8367 // to set their value..
8368 (field.store && !field.store.isLocal)
8372 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8373 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8374 field.setFromData(sd);
8376 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8378 field.setFromData(values);
8381 field.setValue(values[id]);
8385 if(this.trackResetOnLoad){
8386 field.originalValue = field.getValue();
8392 //Roo.each(this.childForms || [], function (f) {
8393 // f.setValues(values);
8400 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8401 * they are returned as an array.
8402 * @param {Boolean} asString
8405 getValues : function(asString){
8406 //if (this.childForms) {
8407 // copy values from the child forms
8408 // Roo.each(this.childForms, function (f) {
8409 // this.setValues(f.getValues());
8415 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8416 if(asString === true){
8419 return Roo.urlDecode(fs);
8423 * Returns the fields in this form as an object with key/value pairs.
8424 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8427 getFieldValues : function(with_hidden)
8429 var items = this.getItems();
8431 items.each(function(f){
8437 var v = f.getValue();
8439 if (f.inputType =='radio') {
8440 if (typeof(ret[f.getName()]) == 'undefined') {
8441 ret[f.getName()] = ''; // empty..
8444 if (!f.el.dom.checked) {
8452 if(f.xtype == 'MoneyField'){
8453 ret[f.currencyName] = f.getCurrency();
8456 // not sure if this supported any more..
8457 if ((typeof(v) == 'object') && f.getRawValue) {
8458 v = f.getRawValue() ; // dates..
8460 // combo boxes where name != hiddenName...
8461 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8462 ret[f.name] = f.getRawValue();
8464 ret[f.getName()] = v;
8471 * Clears all invalid messages in this form.
8472 * @return {BasicForm} this
8474 clearInvalid : function(){
8475 var items = this.getItems();
8477 items.each(function(f){
8486 * @return {BasicForm} this
8489 var items = this.getItems();
8490 items.each(function(f){
8494 Roo.each(this.childForms || [], function (f) {
8502 getItems : function()
8504 var r=new Roo.util.MixedCollection(false, function(o){
8505 return o.id || (o.id = Roo.id());
8507 var iter = function(el) {
8514 Roo.each(el.items,function(e) {
8523 hideFields : function(items)
8525 Roo.each(items, function(i){
8527 var f = this.findField(i);
8538 showFields : function(items)
8540 Roo.each(items, function(i){
8542 var f = this.findField(i);
8555 Roo.apply(Roo.bootstrap.Form, {
8582 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8583 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8584 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8585 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8588 this.maskEl.top.enableDisplayMode("block");
8589 this.maskEl.left.enableDisplayMode("block");
8590 this.maskEl.bottom.enableDisplayMode("block");
8591 this.maskEl.right.enableDisplayMode("block");
8593 this.toolTip = new Roo.bootstrap.Tooltip({
8594 cls : 'roo-form-error-popover',
8596 'left' : ['r-l', [-2,0], 'right'],
8597 'right' : ['l-r', [2,0], 'left'],
8598 'bottom' : ['tl-bl', [0,2], 'top'],
8599 'top' : [ 'bl-tl', [0,-2], 'bottom']
8603 this.toolTip.render(Roo.get(document.body));
8605 this.toolTip.el.enableDisplayMode("block");
8607 Roo.get(document.body).on('click', function(){
8611 Roo.get(document.body).on('touchstart', function(){
8615 this.isApplied = true
8618 mask : function(form, target)
8622 this.target = target;
8624 if(!this.form.errorMask || !target.el){
8628 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8630 Roo.log(scrollable);
8632 var ot = this.target.el.calcOffsetsTo(scrollable);
8634 var scrollTo = ot[1] - this.form.maskOffset;
8636 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8638 scrollable.scrollTo('top', scrollTo);
8640 var box = this.target.el.getBox();
8642 var zIndex = Roo.bootstrap.Modal.zIndex++;
8645 this.maskEl.top.setStyle('position', 'absolute');
8646 this.maskEl.top.setStyle('z-index', zIndex);
8647 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8648 this.maskEl.top.setLeft(0);
8649 this.maskEl.top.setTop(0);
8650 this.maskEl.top.show();
8652 this.maskEl.left.setStyle('position', 'absolute');
8653 this.maskEl.left.setStyle('z-index', zIndex);
8654 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8655 this.maskEl.left.setLeft(0);
8656 this.maskEl.left.setTop(box.y - this.padding);
8657 this.maskEl.left.show();
8659 this.maskEl.bottom.setStyle('position', 'absolute');
8660 this.maskEl.bottom.setStyle('z-index', zIndex);
8661 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8662 this.maskEl.bottom.setLeft(0);
8663 this.maskEl.bottom.setTop(box.bottom + this.padding);
8664 this.maskEl.bottom.show();
8666 this.maskEl.right.setStyle('position', 'absolute');
8667 this.maskEl.right.setStyle('z-index', zIndex);
8668 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8669 this.maskEl.right.setLeft(box.right + this.padding);
8670 this.maskEl.right.setTop(box.y - this.padding);
8671 this.maskEl.right.show();
8673 this.toolTip.bindEl = this.target.el;
8675 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8677 var tip = this.target.blankText;
8679 if(this.target.getValue() !== '' ) {
8681 if (this.target.invalidText.length) {
8682 tip = this.target.invalidText;
8683 } else if (this.target.regexText.length){
8684 tip = this.target.regexText;
8688 this.toolTip.show(tip);
8690 this.intervalID = window.setInterval(function() {
8691 Roo.bootstrap.Form.popover.unmask();
8694 window.onwheel = function(){ return false;};
8696 (function(){ this.isMasked = true; }).defer(500, this);
8702 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8706 this.maskEl.top.setStyle('position', 'absolute');
8707 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8708 this.maskEl.top.hide();
8710 this.maskEl.left.setStyle('position', 'absolute');
8711 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8712 this.maskEl.left.hide();
8714 this.maskEl.bottom.setStyle('position', 'absolute');
8715 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8716 this.maskEl.bottom.hide();
8718 this.maskEl.right.setStyle('position', 'absolute');
8719 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8720 this.maskEl.right.hide();
8722 this.toolTip.hide();
8724 this.toolTip.el.hide();
8726 window.onwheel = function(){ return true;};
8728 if(this.intervalID){
8729 window.clearInterval(this.intervalID);
8730 this.intervalID = false;
8733 this.isMasked = false;
8743 * Ext JS Library 1.1.1
8744 * Copyright(c) 2006-2007, Ext JS, LLC.
8746 * Originally Released Under LGPL - original licence link has changed is not relivant.
8749 * <script type="text/javascript">
8752 * @class Roo.form.VTypes
8753 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8756 Roo.form.VTypes = function(){
8757 // closure these in so they are only created once.
8758 var alpha = /^[a-zA-Z_]+$/;
8759 var alphanum = /^[a-zA-Z0-9_]+$/;
8760 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8761 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8763 // All these messages and functions are configurable
8766 * The function used to validate email addresses
8767 * @param {String} value The email address
8769 'email' : function(v){
8770 return email.test(v);
8773 * The error text to display when the email validation function returns false
8776 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8778 * The keystroke filter mask to be applied on email input
8781 'emailMask' : /[a-z0-9_\.\-@]/i,
8784 * The function used to validate URLs
8785 * @param {String} value The URL
8787 'url' : function(v){
8791 * The error text to display when the url validation function returns false
8794 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8797 * The function used to validate alpha values
8798 * @param {String} value The value
8800 'alpha' : function(v){
8801 return alpha.test(v);
8804 * The error text to display when the alpha validation function returns false
8807 'alphaText' : 'This field should only contain letters and _',
8809 * The keystroke filter mask to be applied on alpha input
8812 'alphaMask' : /[a-z_]/i,
8815 * The function used to validate alphanumeric values
8816 * @param {String} value The value
8818 'alphanum' : function(v){
8819 return alphanum.test(v);
8822 * The error text to display when the alphanumeric validation function returns false
8825 'alphanumText' : 'This field should only contain letters, numbers and _',
8827 * The keystroke filter mask to be applied on alphanumeric input
8830 'alphanumMask' : /[a-z0-9_]/i
8840 * @class Roo.bootstrap.Input
8841 * @extends Roo.bootstrap.Component
8842 * Bootstrap Input class
8843 * @cfg {Boolean} disabled is it disabled
8844 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8845 * @cfg {String} name name of the input
8846 * @cfg {string} fieldLabel - the label associated
8847 * @cfg {string} placeholder - placeholder to put in text.
8848 * @cfg {string} before - input group add on before
8849 * @cfg {string} after - input group add on after
8850 * @cfg {string} size - (lg|sm) or leave empty..
8851 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8852 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8853 * @cfg {Number} md colspan out of 12 for computer-sized screens
8854 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8855 * @cfg {string} value default value of the input
8856 * @cfg {Number} labelWidth set the width of label
8857 * @cfg {Number} labellg set the width of label (1-12)
8858 * @cfg {Number} labelmd set the width of label (1-12)
8859 * @cfg {Number} labelsm set the width of label (1-12)
8860 * @cfg {Number} labelxs set the width of label (1-12)
8861 * @cfg {String} labelAlign (top|left)
8862 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8863 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8864 * @cfg {String} indicatorpos (left|right) default left
8865 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8866 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8868 * @cfg {String} align (left|center|right) Default left
8869 * @cfg {Boolean} forceFeedback (true|false) Default false
8872 * Create a new Input
8873 * @param {Object} config The config object
8876 Roo.bootstrap.Input = function(config){
8878 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8883 * Fires when this field receives input focus.
8884 * @param {Roo.form.Field} this
8889 * Fires when this field loses input focus.
8890 * @param {Roo.form.Field} this
8895 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8896 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8897 * @param {Roo.form.Field} this
8898 * @param {Roo.EventObject} e The event object
8903 * Fires just before the field blurs if the field value has changed.
8904 * @param {Roo.form.Field} this
8905 * @param {Mixed} newValue The new value
8906 * @param {Mixed} oldValue The original value
8911 * Fires after the field has been marked as invalid.
8912 * @param {Roo.form.Field} this
8913 * @param {String} msg The validation message
8918 * Fires after the field has been validated with no errors.
8919 * @param {Roo.form.Field} this
8924 * Fires after the key up
8925 * @param {Roo.form.Field} this
8926 * @param {Roo.EventObject} e The event Object
8932 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8934 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8935 automatic validation (defaults to "keyup").
8937 validationEvent : "keyup",
8939 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8941 validateOnBlur : true,
8943 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8945 validationDelay : 250,
8947 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8949 focusClass : "x-form-focus", // not needed???
8953 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8955 invalidClass : "has-warning",
8958 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8960 validClass : "has-success",
8963 * @cfg {Boolean} hasFeedback (true|false) default true
8968 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8970 invalidFeedbackClass : "glyphicon-warning-sign",
8973 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8975 validFeedbackClass : "glyphicon-ok",
8978 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8980 selectOnFocus : false,
8983 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8987 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8992 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8994 disableKeyFilter : false,
8997 * @cfg {Boolean} disabled True to disable the field (defaults to false).
9001 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9005 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9007 blankText : "Please complete this mandatory field",
9010 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9014 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9016 maxLength : Number.MAX_VALUE,
9018 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9020 minLengthText : "The minimum length for this field is {0}",
9022 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9024 maxLengthText : "The maximum length for this field is {0}",
9028 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9029 * If available, this function will be called only after the basic validators all return true, and will be passed the
9030 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9034 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9035 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9036 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
9040 * @cfg {String} regexText -- Depricated - use Invalid Text
9045 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9051 autocomplete: false,
9070 formatedValue : false,
9071 forceFeedback : false,
9073 indicatorpos : 'left',
9083 parentLabelAlign : function()
9086 while (parent.parent()) {
9087 parent = parent.parent();
9088 if (typeof(parent.labelAlign) !='undefined') {
9089 return parent.labelAlign;
9096 getAutoCreate : function()
9098 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9104 if(this.inputType != 'hidden'){
9105 cfg.cls = 'form-group' //input-group
9111 type : this.inputType,
9113 cls : 'form-control',
9114 placeholder : this.placeholder || '',
9115 autocomplete : this.autocomplete || 'new-password'
9118 if(this.capture.length){
9119 input.capture = this.capture;
9122 if(this.accept.length){
9123 input.accept = this.accept + "/*";
9127 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9130 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9131 input.maxLength = this.maxLength;
9134 if (this.disabled) {
9135 input.disabled=true;
9138 if (this.readOnly) {
9139 input.readonly=true;
9143 input.name = this.name;
9147 input.cls += ' input-' + this.size;
9151 ['xs','sm','md','lg'].map(function(size){
9152 if (settings[size]) {
9153 cfg.cls += ' col-' + size + '-' + settings[size];
9157 var inputblock = input;
9161 cls: 'glyphicon form-control-feedback'
9164 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9167 cls : 'has-feedback',
9175 if (this.before || this.after) {
9178 cls : 'input-group',
9182 if (this.before && typeof(this.before) == 'string') {
9184 inputblock.cn.push({
9186 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9190 if (this.before && typeof(this.before) == 'object') {
9191 this.before = Roo.factory(this.before);
9193 inputblock.cn.push({
9195 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9196 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9200 inputblock.cn.push(input);
9202 if (this.after && typeof(this.after) == 'string') {
9203 inputblock.cn.push({
9205 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9209 if (this.after && typeof(this.after) == 'object') {
9210 this.after = Roo.factory(this.after);
9212 inputblock.cn.push({
9214 cls : 'roo-input-after input-group-append input-group-text input-group-' +
9215 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9219 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9220 inputblock.cls += ' has-feedback';
9221 inputblock.cn.push(feedback);
9226 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9227 tooltip : 'This field is required'
9229 if (Roo.bootstrap.version == 4) {
9232 style : 'display-none'
9235 if (align ==='left' && this.fieldLabel.length) {
9237 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
9244 cls : 'control-label col-form-label',
9245 html : this.fieldLabel
9256 var labelCfg = cfg.cn[1];
9257 var contentCfg = cfg.cn[2];
9259 if(this.indicatorpos == 'right'){
9264 cls : 'control-label col-form-label',
9268 html : this.fieldLabel
9282 labelCfg = cfg.cn[0];
9283 contentCfg = cfg.cn[1];
9287 if(this.labelWidth > 12){
9288 labelCfg.style = "width: " + this.labelWidth + 'px';
9291 if(this.labelWidth < 13 && this.labelmd == 0){
9292 this.labelmd = this.labelWidth;
9295 if(this.labellg > 0){
9296 labelCfg.cls += ' col-lg-' + this.labellg;
9297 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9300 if(this.labelmd > 0){
9301 labelCfg.cls += ' col-md-' + this.labelmd;
9302 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9305 if(this.labelsm > 0){
9306 labelCfg.cls += ' col-sm-' + this.labelsm;
9307 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9310 if(this.labelxs > 0){
9311 labelCfg.cls += ' col-xs-' + this.labelxs;
9312 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9316 } else if ( this.fieldLabel.length) {
9321 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9322 tooltip : 'This field is required'
9326 //cls : 'input-group-addon',
9327 html : this.fieldLabel
9335 if(this.indicatorpos == 'right'){
9340 //cls : 'input-group-addon',
9341 html : this.fieldLabel
9346 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9347 tooltip : 'This field is required'
9367 if (this.parentType === 'Navbar' && this.parent().bar) {
9368 cfg.cls += ' navbar-form';
9371 if (this.parentType === 'NavGroup') {
9372 cfg.cls += ' navbar-form';
9380 * return the real input element.
9382 inputEl: function ()
9384 return this.el.select('input.form-control',true).first();
9387 tooltipEl : function()
9389 return this.inputEl();
9392 indicatorEl : function()
9394 if (Roo.bootstrap.version == 4) {
9395 return false; // not enabled in v4 yet.
9398 var indicator = this.el.select('i.roo-required-indicator',true).first();
9408 setDisabled : function(v)
9410 var i = this.inputEl().dom;
9412 i.removeAttribute('disabled');
9416 i.setAttribute('disabled','true');
9418 initEvents : function()
9421 this.inputEl().on("keydown" , this.fireKey, this);
9422 this.inputEl().on("focus", this.onFocus, this);
9423 this.inputEl().on("blur", this.onBlur, this);
9425 this.inputEl().relayEvent('keyup', this);
9427 this.indicator = this.indicatorEl();
9430 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9433 // reference to original value for reset
9434 this.originalValue = this.getValue();
9435 //Roo.form.TextField.superclass.initEvents.call(this);
9436 if(this.validationEvent == 'keyup'){
9437 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9438 this.inputEl().on('keyup', this.filterValidation, this);
9440 else if(this.validationEvent !== false){
9441 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9444 if(this.selectOnFocus){
9445 this.on("focus", this.preFocus, this);
9448 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9449 this.inputEl().on("keypress", this.filterKeys, this);
9451 this.inputEl().relayEvent('keypress', this);
9454 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9455 this.el.on("click", this.autoSize, this);
9458 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9459 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9462 if (typeof(this.before) == 'object') {
9463 this.before.render(this.el.select('.roo-input-before',true).first());
9465 if (typeof(this.after) == 'object') {
9466 this.after.render(this.el.select('.roo-input-after',true).first());
9469 this.inputEl().on('change', this.onChange, this);
9472 filterValidation : function(e){
9473 if(!e.isNavKeyPress()){
9474 this.validationTask.delay(this.validationDelay);
9478 * Validates the field value
9479 * @return {Boolean} True if the value is valid, else false
9481 validate : function(){
9482 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9483 if(this.disabled || this.validateValue(this.getRawValue())){
9494 * Validates a value according to the field's validation rules and marks the field as invalid
9495 * if the validation fails
9496 * @param {Mixed} value The value to validate
9497 * @return {Boolean} True if the value is valid, else false
9499 validateValue : function(value)
9501 if(this.getVisibilityEl().hasClass('hidden')){
9505 if(value.length < 1) { // if it's blank
9506 if(this.allowBlank){
9512 if(value.length < this.minLength){
9515 if(value.length > this.maxLength){
9519 var vt = Roo.form.VTypes;
9520 if(!vt[this.vtype](value, this)){
9524 if(typeof this.validator == "function"){
9525 var msg = this.validator(value);
9529 if (typeof(msg) == 'string') {
9530 this.invalidText = msg;
9534 if(this.regex && !this.regex.test(value)){
9542 fireKey : function(e){
9543 //Roo.log('field ' + e.getKey());
9544 if(e.isNavKeyPress()){
9545 this.fireEvent("specialkey", this, e);
9548 focus : function (selectText){
9550 this.inputEl().focus();
9551 if(selectText === true){
9552 this.inputEl().dom.select();
9558 onFocus : function(){
9559 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9560 // this.el.addClass(this.focusClass);
9563 this.hasFocus = true;
9564 this.startValue = this.getValue();
9565 this.fireEvent("focus", this);
9569 beforeBlur : Roo.emptyFn,
9573 onBlur : function(){
9575 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9576 //this.el.removeClass(this.focusClass);
9578 this.hasFocus = false;
9579 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9582 var v = this.getValue();
9583 if(String(v) !== String(this.startValue)){
9584 this.fireEvent('change', this, v, this.startValue);
9586 this.fireEvent("blur", this);
9589 onChange : function(e)
9591 var v = this.getValue();
9592 if(String(v) !== String(this.startValue)){
9593 this.fireEvent('change', this, v, this.startValue);
9599 * Resets the current field value to the originally loaded value and clears any validation messages
9602 this.setValue(this.originalValue);
9606 * Returns the name of the field
9607 * @return {Mixed} name The name field
9609 getName: function(){
9613 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9614 * @return {Mixed} value The field value
9616 getValue : function(){
9618 var v = this.inputEl().getValue();
9623 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9624 * @return {Mixed} value The field value
9626 getRawValue : function(){
9627 var v = this.inputEl().getValue();
9633 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9634 * @param {Mixed} value The value to set
9636 setRawValue : function(v){
9637 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9640 selectText : function(start, end){
9641 var v = this.getRawValue();
9643 start = start === undefined ? 0 : start;
9644 end = end === undefined ? v.length : end;
9645 var d = this.inputEl().dom;
9646 if(d.setSelectionRange){
9647 d.setSelectionRange(start, end);
9648 }else if(d.createTextRange){
9649 var range = d.createTextRange();
9650 range.moveStart("character", start);
9651 range.moveEnd("character", v.length-end);
9658 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9659 * @param {Mixed} value The value to set
9661 setValue : function(v){
9664 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9670 processValue : function(value){
9671 if(this.stripCharsRe){
9672 var newValue = value.replace(this.stripCharsRe, '');
9673 if(newValue !== value){
9674 this.setRawValue(newValue);
9681 preFocus : function(){
9683 if(this.selectOnFocus){
9684 this.inputEl().dom.select();
9687 filterKeys : function(e){
9689 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9692 var c = e.getCharCode(), cc = String.fromCharCode(c);
9693 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9696 if(!this.maskRe.test(cc)){
9701 * Clear any invalid styles/messages for this field
9703 clearInvalid : function(){
9705 if(!this.el || this.preventMark){ // not rendered
9710 this.el.removeClass(this.invalidClass);
9712 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9714 var feedback = this.el.select('.form-control-feedback', true).first();
9717 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9723 this.indicator.removeClass('visible');
9724 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9727 this.fireEvent('valid', this);
9731 * Mark this field as valid
9733 markValid : function()
9735 if(!this.el || this.preventMark){ // not rendered...
9739 this.el.removeClass([this.invalidClass, this.validClass]);
9741 var feedback = this.el.select('.form-control-feedback', true).first();
9744 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9748 this.indicator.removeClass('visible');
9749 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9756 if(this.allowBlank && !this.getRawValue().length){
9760 this.el.addClass(this.validClass);
9762 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9764 var feedback = this.el.select('.form-control-feedback', true).first();
9767 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9768 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9773 this.fireEvent('valid', this);
9777 * Mark this field as invalid
9778 * @param {String} msg The validation message
9780 markInvalid : function(msg)
9782 if(!this.el || this.preventMark){ // not rendered
9786 this.el.removeClass([this.invalidClass, this.validClass]);
9788 var feedback = this.el.select('.form-control-feedback', true).first();
9791 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9798 if(this.allowBlank && !this.getRawValue().length){
9803 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9804 this.indicator.addClass('visible');
9807 this.el.addClass(this.invalidClass);
9809 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9811 var feedback = this.el.select('.form-control-feedback', true).first();
9814 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9816 if(this.getValue().length || this.forceFeedback){
9817 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9824 this.fireEvent('invalid', this, msg);
9827 SafariOnKeyDown : function(event)
9829 // this is a workaround for a password hang bug on chrome/ webkit.
9830 if (this.inputEl().dom.type != 'password') {
9834 var isSelectAll = false;
9836 if(this.inputEl().dom.selectionEnd > 0){
9837 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9839 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9840 event.preventDefault();
9845 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9847 event.preventDefault();
9848 // this is very hacky as keydown always get's upper case.
9850 var cc = String.fromCharCode(event.getCharCode());
9851 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9855 adjustWidth : function(tag, w){
9856 tag = tag.toLowerCase();
9857 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9858 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9862 if(tag == 'textarea'){
9865 }else if(Roo.isOpera){
9869 if(tag == 'textarea'){
9877 setFieldLabel : function(v)
9883 if(this.indicatorEl()){
9884 var ar = this.el.select('label > span',true);
9886 if (ar.elements.length) {
9887 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9888 this.fieldLabel = v;
9892 var br = this.el.select('label',true);
9894 if(br.elements.length) {
9895 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9896 this.fieldLabel = v;
9900 Roo.log('Cannot Found any of label > span || label in input');
9904 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9905 this.fieldLabel = v;
9920 * @class Roo.bootstrap.TextArea
9921 * @extends Roo.bootstrap.Input
9922 * Bootstrap TextArea class
9923 * @cfg {Number} cols Specifies the visible width of a text area
9924 * @cfg {Number} rows Specifies the visible number of lines in a text area
9925 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9926 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9927 * @cfg {string} html text
9930 * Create a new TextArea
9931 * @param {Object} config The config object
9934 Roo.bootstrap.TextArea = function(config){
9935 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9939 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9949 getAutoCreate : function(){
9951 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9957 if(this.inputType != 'hidden'){
9958 cfg.cls = 'form-group' //input-group
9966 value : this.value || '',
9967 html: this.html || '',
9968 cls : 'form-control',
9969 placeholder : this.placeholder || ''
9973 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9974 input.maxLength = this.maxLength;
9978 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9982 input.cols = this.cols;
9985 if (this.readOnly) {
9986 input.readonly = true;
9990 input.name = this.name;
9994 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9998 ['xs','sm','md','lg'].map(function(size){
9999 if (settings[size]) {
10000 cfg.cls += ' col-' + size + '-' + settings[size];
10004 var inputblock = input;
10006 if(this.hasFeedback && !this.allowBlank){
10010 cls: 'glyphicon form-control-feedback'
10014 cls : 'has-feedback',
10023 if (this.before || this.after) {
10026 cls : 'input-group',
10030 inputblock.cn.push({
10032 cls : 'input-group-addon',
10037 inputblock.cn.push(input);
10039 if(this.hasFeedback && !this.allowBlank){
10040 inputblock.cls += ' has-feedback';
10041 inputblock.cn.push(feedback);
10045 inputblock.cn.push({
10047 cls : 'input-group-addon',
10054 if (align ==='left' && this.fieldLabel.length) {
10059 cls : 'control-label',
10060 html : this.fieldLabel
10071 if(this.labelWidth > 12){
10072 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10075 if(this.labelWidth < 13 && this.labelmd == 0){
10076 this.labelmd = this.labelWidth;
10079 if(this.labellg > 0){
10080 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10081 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10084 if(this.labelmd > 0){
10085 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10086 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10089 if(this.labelsm > 0){
10090 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10091 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10094 if(this.labelxs > 0){
10095 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10096 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10099 } else if ( this.fieldLabel.length) {
10104 //cls : 'input-group-addon',
10105 html : this.fieldLabel
10123 if (this.disabled) {
10124 input.disabled=true;
10131 * return the real textarea element.
10133 inputEl: function ()
10135 return this.el.select('textarea.form-control',true).first();
10139 * Clear any invalid styles/messages for this field
10141 clearInvalid : function()
10144 if(!this.el || this.preventMark){ // not rendered
10148 var label = this.el.select('label', true).first();
10149 var icon = this.el.select('i.fa-star', true).first();
10155 this.el.removeClass(this.invalidClass);
10157 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10159 var feedback = this.el.select('.form-control-feedback', true).first();
10162 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10167 this.fireEvent('valid', this);
10171 * Mark this field as valid
10173 markValid : function()
10175 if(!this.el || this.preventMark){ // not rendered
10179 this.el.removeClass([this.invalidClass, this.validClass]);
10181 var feedback = this.el.select('.form-control-feedback', true).first();
10184 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10187 if(this.disabled || this.allowBlank){
10191 var label = this.el.select('label', true).first();
10192 var icon = this.el.select('i.fa-star', true).first();
10198 this.el.addClass(this.validClass);
10200 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10202 var feedback = this.el.select('.form-control-feedback', true).first();
10205 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10206 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10211 this.fireEvent('valid', this);
10215 * Mark this field as invalid
10216 * @param {String} msg The validation message
10218 markInvalid : function(msg)
10220 if(!this.el || this.preventMark){ // not rendered
10224 this.el.removeClass([this.invalidClass, this.validClass]);
10226 var feedback = this.el.select('.form-control-feedback', true).first();
10229 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10232 if(this.disabled || this.allowBlank){
10236 var label = this.el.select('label', true).first();
10237 var icon = this.el.select('i.fa-star', true).first();
10239 if(!this.getValue().length && label && !icon){
10240 this.el.createChild({
10242 cls : 'text-danger fa fa-lg fa-star',
10243 tooltip : 'This field is required',
10244 style : 'margin-right:5px;'
10248 this.el.addClass(this.invalidClass);
10250 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10252 var feedback = this.el.select('.form-control-feedback', true).first();
10255 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10257 if(this.getValue().length || this.forceFeedback){
10258 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10265 this.fireEvent('invalid', this, msg);
10273 * trigger field - base class for combo..
10278 * @class Roo.bootstrap.TriggerField
10279 * @extends Roo.bootstrap.Input
10280 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10281 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10282 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10283 * for which you can provide a custom implementation. For example:
10285 var trigger = new Roo.bootstrap.TriggerField();
10286 trigger.onTriggerClick = myTriggerFn;
10287 trigger.applyTo('my-field');
10290 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10291 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10292 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10293 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10294 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10297 * Create a new TriggerField.
10298 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10299 * to the base TextField)
10301 Roo.bootstrap.TriggerField = function(config){
10302 this.mimicing = false;
10303 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10306 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10308 * @cfg {String} triggerClass A CSS class to apply to the trigger
10311 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10316 * @cfg {Boolean} removable (true|false) special filter default false
10320 /** @cfg {Boolean} grow @hide */
10321 /** @cfg {Number} growMin @hide */
10322 /** @cfg {Number} growMax @hide */
10328 autoSize: Roo.emptyFn,
10332 deferHeight : true,
10335 actionMode : 'wrap',
10340 getAutoCreate : function(){
10342 var align = this.labelAlign || this.parentLabelAlign();
10347 cls: 'form-group' //input-group
10354 type : this.inputType,
10355 cls : 'form-control',
10356 autocomplete: 'new-password',
10357 placeholder : this.placeholder || ''
10361 input.name = this.name;
10364 input.cls += ' input-' + this.size;
10367 if (this.disabled) {
10368 input.disabled=true;
10371 var inputblock = input;
10373 if(this.hasFeedback && !this.allowBlank){
10377 cls: 'glyphicon form-control-feedback'
10380 if(this.removable && !this.editable && !this.tickable){
10382 cls : 'has-feedback',
10388 cls : 'roo-combo-removable-btn close'
10395 cls : 'has-feedback',
10404 if(this.removable && !this.editable && !this.tickable){
10406 cls : 'roo-removable',
10412 cls : 'roo-combo-removable-btn close'
10419 if (this.before || this.after) {
10422 cls : 'input-group',
10426 inputblock.cn.push({
10428 cls : 'input-group-addon input-group-prepend input-group-text',
10433 inputblock.cn.push(input);
10435 if(this.hasFeedback && !this.allowBlank){
10436 inputblock.cls += ' has-feedback';
10437 inputblock.cn.push(feedback);
10441 inputblock.cn.push({
10443 cls : 'input-group-addon input-group-append input-group-text',
10452 var ibwrap = inputblock;
10457 cls: 'roo-select2-choices',
10461 cls: 'roo-select2-search-field',
10473 cls: 'roo-select2-container input-group',
10478 cls: 'form-hidden-field'
10484 if(!this.multiple && this.showToggleBtn){
10490 if (this.caret != false) {
10493 cls: 'fa fa-' + this.caret
10500 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10505 cls: 'combobox-clear',
10519 combobox.cls += ' roo-select2-container-multi';
10523 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10524 tooltip : 'This field is required'
10526 if (Roo.bootstrap.version == 4) {
10529 style : 'display:none'
10534 if (align ==='left' && this.fieldLabel.length) {
10536 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10543 cls : 'control-label',
10544 html : this.fieldLabel
10556 var labelCfg = cfg.cn[1];
10557 var contentCfg = cfg.cn[2];
10559 if(this.indicatorpos == 'right'){
10564 cls : 'control-label',
10568 html : this.fieldLabel
10582 labelCfg = cfg.cn[0];
10583 contentCfg = cfg.cn[1];
10586 if(this.labelWidth > 12){
10587 labelCfg.style = "width: " + this.labelWidth + 'px';
10590 if(this.labelWidth < 13 && this.labelmd == 0){
10591 this.labelmd = this.labelWidth;
10594 if(this.labellg > 0){
10595 labelCfg.cls += ' col-lg-' + this.labellg;
10596 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10599 if(this.labelmd > 0){
10600 labelCfg.cls += ' col-md-' + this.labelmd;
10601 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10604 if(this.labelsm > 0){
10605 labelCfg.cls += ' col-sm-' + this.labelsm;
10606 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10609 if(this.labelxs > 0){
10610 labelCfg.cls += ' col-xs-' + this.labelxs;
10611 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10614 } else if ( this.fieldLabel.length) {
10615 // Roo.log(" label");
10620 //cls : 'input-group-addon',
10621 html : this.fieldLabel
10629 if(this.indicatorpos == 'right'){
10637 html : this.fieldLabel
10651 // Roo.log(" no label && no align");
10658 ['xs','sm','md','lg'].map(function(size){
10659 if (settings[size]) {
10660 cfg.cls += ' col-' + size + '-' + settings[size];
10671 onResize : function(w, h){
10672 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10673 // if(typeof w == 'number'){
10674 // var x = w - this.trigger.getWidth();
10675 // this.inputEl().setWidth(this.adjustWidth('input', x));
10676 // this.trigger.setStyle('left', x+'px');
10681 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10684 getResizeEl : function(){
10685 return this.inputEl();
10689 getPositionEl : function(){
10690 return this.inputEl();
10694 alignErrorIcon : function(){
10695 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10699 initEvents : function(){
10703 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10704 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10705 if(!this.multiple && this.showToggleBtn){
10706 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10707 if(this.hideTrigger){
10708 this.trigger.setDisplayed(false);
10710 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10714 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10717 if(this.removable && !this.editable && !this.tickable){
10718 var close = this.closeTriggerEl();
10721 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10722 close.on('click', this.removeBtnClick, this, close);
10726 //this.trigger.addClassOnOver('x-form-trigger-over');
10727 //this.trigger.addClassOnClick('x-form-trigger-click');
10730 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10734 closeTriggerEl : function()
10736 var close = this.el.select('.roo-combo-removable-btn', true).first();
10737 return close ? close : false;
10740 removeBtnClick : function(e, h, el)
10742 e.preventDefault();
10744 if(this.fireEvent("remove", this) !== false){
10746 this.fireEvent("afterremove", this)
10750 createList : function()
10752 this.list = Roo.get(document.body).createChild({
10753 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10754 cls: 'typeahead typeahead-long dropdown-menu',
10755 style: 'display:none'
10758 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10763 initTrigger : function(){
10768 onDestroy : function(){
10770 this.trigger.removeAllListeners();
10771 // this.trigger.remove();
10774 // this.wrap.remove();
10776 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10780 onFocus : function(){
10781 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10783 if(!this.mimicing){
10784 this.wrap.addClass('x-trigger-wrap-focus');
10785 this.mimicing = true;
10786 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10787 if(this.monitorTab){
10788 this.el.on("keydown", this.checkTab, this);
10795 checkTab : function(e){
10796 if(e.getKey() == e.TAB){
10797 this.triggerBlur();
10802 onBlur : function(){
10807 mimicBlur : function(e, t){
10809 if(!this.wrap.contains(t) && this.validateBlur()){
10810 this.triggerBlur();
10816 triggerBlur : function(){
10817 this.mimicing = false;
10818 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10819 if(this.monitorTab){
10820 this.el.un("keydown", this.checkTab, this);
10822 //this.wrap.removeClass('x-trigger-wrap-focus');
10823 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10827 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10828 validateBlur : function(e, t){
10833 onDisable : function(){
10834 this.inputEl().dom.disabled = true;
10835 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10837 // this.wrap.addClass('x-item-disabled');
10842 onEnable : function(){
10843 this.inputEl().dom.disabled = false;
10844 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10846 // this.el.removeClass('x-item-disabled');
10851 onShow : function(){
10852 var ae = this.getActionEl();
10855 ae.dom.style.display = '';
10856 ae.dom.style.visibility = 'visible';
10862 onHide : function(){
10863 var ae = this.getActionEl();
10864 ae.dom.style.display = 'none';
10868 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10869 * by an implementing function.
10871 * @param {EventObject} e
10873 onTriggerClick : Roo.emptyFn
10877 * Ext JS Library 1.1.1
10878 * Copyright(c) 2006-2007, Ext JS, LLC.
10880 * Originally Released Under LGPL - original licence link has changed is not relivant.
10883 * <script type="text/javascript">
10888 * @class Roo.data.SortTypes
10890 * Defines the default sorting (casting?) comparison functions used when sorting data.
10892 Roo.data.SortTypes = {
10894 * Default sort that does nothing
10895 * @param {Mixed} s The value being converted
10896 * @return {Mixed} The comparison value
10898 none : function(s){
10903 * The regular expression used to strip tags
10907 stripTagsRE : /<\/?[^>]+>/gi,
10910 * Strips all HTML tags to sort on text only
10911 * @param {Mixed} s The value being converted
10912 * @return {String} The comparison value
10914 asText : function(s){
10915 return String(s).replace(this.stripTagsRE, "");
10919 * Strips all HTML tags to sort on text only - Case insensitive
10920 * @param {Mixed} s The value being converted
10921 * @return {String} The comparison value
10923 asUCText : function(s){
10924 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10928 * Case insensitive string
10929 * @param {Mixed} s The value being converted
10930 * @return {String} The comparison value
10932 asUCString : function(s) {
10933 return String(s).toUpperCase();
10938 * @param {Mixed} s The value being converted
10939 * @return {Number} The comparison value
10941 asDate : function(s) {
10945 if(s instanceof Date){
10946 return s.getTime();
10948 return Date.parse(String(s));
10953 * @param {Mixed} s The value being converted
10954 * @return {Float} The comparison value
10956 asFloat : function(s) {
10957 var val = parseFloat(String(s).replace(/,/g, ""));
10966 * @param {Mixed} s The value being converted
10967 * @return {Number} The comparison value
10969 asInt : function(s) {
10970 var val = parseInt(String(s).replace(/,/g, ""));
10978 * Ext JS Library 1.1.1
10979 * Copyright(c) 2006-2007, Ext JS, LLC.
10981 * Originally Released Under LGPL - original licence link has changed is not relivant.
10984 * <script type="text/javascript">
10988 * @class Roo.data.Record
10989 * Instances of this class encapsulate both record <em>definition</em> information, and record
10990 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10991 * to access Records cached in an {@link Roo.data.Store} object.<br>
10993 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10994 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10997 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10999 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11000 * {@link #create}. The parameters are the same.
11001 * @param {Array} data An associative Array of data values keyed by the field name.
11002 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11003 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11004 * not specified an integer id is generated.
11006 Roo.data.Record = function(data, id){
11007 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11012 * Generate a constructor for a specific record layout.
11013 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11014 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11015 * Each field definition object may contain the following properties: <ul>
11016 * <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,
11017 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11018 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11019 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11020 * is being used, then this is a string containing the javascript expression to reference the data relative to
11021 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11022 * to the data item relative to the record element. If the mapping expression is the same as the field name,
11023 * this may be omitted.</p></li>
11024 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11025 * <ul><li>auto (Default, implies no conversion)</li>
11030 * <li>date</li></ul></p></li>
11031 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11032 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11033 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11034 * by the Reader into an object that will be stored in the Record. It is passed the
11035 * following parameters:<ul>
11036 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11038 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11040 * <br>usage:<br><pre><code>
11041 var TopicRecord = Roo.data.Record.create(
11042 {name: 'title', mapping: 'topic_title'},
11043 {name: 'author', mapping: 'username'},
11044 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11045 {name: 'lastPost', mapping: 'post_time', type: 'date'},
11046 {name: 'lastPoster', mapping: 'user2'},
11047 {name: 'excerpt', mapping: 'post_text'}
11050 var myNewRecord = new TopicRecord({
11051 title: 'Do my job please',
11054 lastPost: new Date(),
11055 lastPoster: 'Animal',
11056 excerpt: 'No way dude!'
11058 myStore.add(myNewRecord);
11063 Roo.data.Record.create = function(o){
11064 var f = function(){
11065 f.superclass.constructor.apply(this, arguments);
11067 Roo.extend(f, Roo.data.Record);
11068 var p = f.prototype;
11069 p.fields = new Roo.util.MixedCollection(false, function(field){
11072 for(var i = 0, len = o.length; i < len; i++){
11073 p.fields.add(new Roo.data.Field(o[i]));
11075 f.getField = function(name){
11076 return p.fields.get(name);
11081 Roo.data.Record.AUTO_ID = 1000;
11082 Roo.data.Record.EDIT = 'edit';
11083 Roo.data.Record.REJECT = 'reject';
11084 Roo.data.Record.COMMIT = 'commit';
11086 Roo.data.Record.prototype = {
11088 * Readonly flag - true if this record has been modified.
11097 join : function(store){
11098 this.store = store;
11102 * Set the named field to the specified value.
11103 * @param {String} name The name of the field to set.
11104 * @param {Object} value The value to set the field to.
11106 set : function(name, value){
11107 if(this.data[name] == value){
11111 if(!this.modified){
11112 this.modified = {};
11114 if(typeof this.modified[name] == 'undefined'){
11115 this.modified[name] = this.data[name];
11117 this.data[name] = value;
11118 if(!this.editing && this.store){
11119 this.store.afterEdit(this);
11124 * Get the value of the named field.
11125 * @param {String} name The name of the field to get the value of.
11126 * @return {Object} The value of the field.
11128 get : function(name){
11129 return this.data[name];
11133 beginEdit : function(){
11134 this.editing = true;
11135 this.modified = {};
11139 cancelEdit : function(){
11140 this.editing = false;
11141 delete this.modified;
11145 endEdit : function(){
11146 this.editing = false;
11147 if(this.dirty && this.store){
11148 this.store.afterEdit(this);
11153 * Usually called by the {@link Roo.data.Store} which owns the Record.
11154 * Rejects all changes made to the Record since either creation, or the last commit operation.
11155 * Modified fields are reverted to their original values.
11157 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11158 * of reject operations.
11160 reject : function(){
11161 var m = this.modified;
11163 if(typeof m[n] != "function"){
11164 this.data[n] = m[n];
11167 this.dirty = false;
11168 delete this.modified;
11169 this.editing = false;
11171 this.store.afterReject(this);
11176 * Usually called by the {@link Roo.data.Store} which owns the Record.
11177 * Commits all changes made to the Record since either creation, or the last commit operation.
11179 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11180 * of commit operations.
11182 commit : function(){
11183 this.dirty = false;
11184 delete this.modified;
11185 this.editing = false;
11187 this.store.afterCommit(this);
11192 hasError : function(){
11193 return this.error != null;
11197 clearError : function(){
11202 * Creates a copy of this record.
11203 * @param {String} id (optional) A new record id if you don't want to use this record's id
11206 copy : function(newId) {
11207 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11211 * Ext JS Library 1.1.1
11212 * Copyright(c) 2006-2007, Ext JS, LLC.
11214 * Originally Released Under LGPL - original licence link has changed is not relivant.
11217 * <script type="text/javascript">
11223 * @class Roo.data.Store
11224 * @extends Roo.util.Observable
11225 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11226 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11228 * 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
11229 * has no knowledge of the format of the data returned by the Proxy.<br>
11231 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11232 * instances from the data object. These records are cached and made available through accessor functions.
11234 * Creates a new Store.
11235 * @param {Object} config A config object containing the objects needed for the Store to access data,
11236 * and read the data into Records.
11238 Roo.data.Store = function(config){
11239 this.data = new Roo.util.MixedCollection(false);
11240 this.data.getKey = function(o){
11243 this.baseParams = {};
11245 this.paramNames = {
11250 "multisort" : "_multisort"
11253 if(config && config.data){
11254 this.inlineData = config.data;
11255 delete config.data;
11258 Roo.apply(this, config);
11260 if(this.reader){ // reader passed
11261 this.reader = Roo.factory(this.reader, Roo.data);
11262 this.reader.xmodule = this.xmodule || false;
11263 if(!this.recordType){
11264 this.recordType = this.reader.recordType;
11266 if(this.reader.onMetaChange){
11267 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11271 if(this.recordType){
11272 this.fields = this.recordType.prototype.fields;
11274 this.modified = [];
11278 * @event datachanged
11279 * Fires when the data cache has changed, and a widget which is using this Store
11280 * as a Record cache should refresh its view.
11281 * @param {Store} this
11283 datachanged : true,
11285 * @event metachange
11286 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11287 * @param {Store} this
11288 * @param {Object} meta The JSON metadata
11293 * Fires when Records have been added to the Store
11294 * @param {Store} this
11295 * @param {Roo.data.Record[]} records The array of Records added
11296 * @param {Number} index The index at which the record(s) were added
11301 * Fires when a Record has been removed from the Store
11302 * @param {Store} this
11303 * @param {Roo.data.Record} record The Record that was removed
11304 * @param {Number} index The index at which the record was removed
11309 * Fires when a Record has been updated
11310 * @param {Store} this
11311 * @param {Roo.data.Record} record The Record that was updated
11312 * @param {String} operation The update operation being performed. Value may be one of:
11314 Roo.data.Record.EDIT
11315 Roo.data.Record.REJECT
11316 Roo.data.Record.COMMIT
11322 * Fires when the data cache has been cleared.
11323 * @param {Store} this
11327 * @event beforeload
11328 * Fires before a request is made for a new data object. If the beforeload handler returns false
11329 * the load action will be canceled.
11330 * @param {Store} this
11331 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11335 * @event beforeloadadd
11336 * Fires after a new set of Records has been loaded.
11337 * @param {Store} this
11338 * @param {Roo.data.Record[]} records The Records that were loaded
11339 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11341 beforeloadadd : true,
11344 * Fires after a new set of Records has been loaded, before they are added to the store.
11345 * @param {Store} this
11346 * @param {Roo.data.Record[]} records The Records that were loaded
11347 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11348 * @params {Object} return from reader
11352 * @event loadexception
11353 * Fires if an exception occurs in the Proxy during loading.
11354 * Called with the signature of the Proxy's "loadexception" event.
11355 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11358 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11359 * @param {Object} load options
11360 * @param {Object} jsonData from your request (normally this contains the Exception)
11362 loadexception : true
11366 this.proxy = Roo.factory(this.proxy, Roo.data);
11367 this.proxy.xmodule = this.xmodule || false;
11368 this.relayEvents(this.proxy, ["loadexception"]);
11370 this.sortToggle = {};
11371 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11373 Roo.data.Store.superclass.constructor.call(this);
11375 if(this.inlineData){
11376 this.loadData(this.inlineData);
11377 delete this.inlineData;
11381 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11383 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11384 * without a remote query - used by combo/forms at present.
11388 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11391 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11394 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11395 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11398 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11399 * on any HTTP request
11402 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11405 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11409 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11410 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11412 remoteSort : false,
11415 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11416 * loaded or when a record is removed. (defaults to false).
11418 pruneModifiedRecords : false,
11421 lastOptions : null,
11424 * Add Records to the Store and fires the add event.
11425 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11427 add : function(records){
11428 records = [].concat(records);
11429 for(var i = 0, len = records.length; i < len; i++){
11430 records[i].join(this);
11432 var index = this.data.length;
11433 this.data.addAll(records);
11434 this.fireEvent("add", this, records, index);
11438 * Remove a Record from the Store and fires the remove event.
11439 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11441 remove : function(record){
11442 var index = this.data.indexOf(record);
11443 this.data.removeAt(index);
11445 if(this.pruneModifiedRecords){
11446 this.modified.remove(record);
11448 this.fireEvent("remove", this, record, index);
11452 * Remove all Records from the Store and fires the clear event.
11454 removeAll : function(){
11456 if(this.pruneModifiedRecords){
11457 this.modified = [];
11459 this.fireEvent("clear", this);
11463 * Inserts Records to the Store at the given index and fires the add event.
11464 * @param {Number} index The start index at which to insert the passed Records.
11465 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11467 insert : function(index, records){
11468 records = [].concat(records);
11469 for(var i = 0, len = records.length; i < len; i++){
11470 this.data.insert(index, records[i]);
11471 records[i].join(this);
11473 this.fireEvent("add", this, records, index);
11477 * Get the index within the cache of the passed Record.
11478 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11479 * @return {Number} The index of the passed Record. Returns -1 if not found.
11481 indexOf : function(record){
11482 return this.data.indexOf(record);
11486 * Get the index within the cache of the Record with the passed id.
11487 * @param {String} id The id of the Record to find.
11488 * @return {Number} The index of the Record. Returns -1 if not found.
11490 indexOfId : function(id){
11491 return this.data.indexOfKey(id);
11495 * Get the Record with the specified id.
11496 * @param {String} id The id of the Record to find.
11497 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11499 getById : function(id){
11500 return this.data.key(id);
11504 * Get the Record at the specified index.
11505 * @param {Number} index The index of the Record to find.
11506 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11508 getAt : function(index){
11509 return this.data.itemAt(index);
11513 * Returns a range of Records between specified indices.
11514 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11515 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11516 * @return {Roo.data.Record[]} An array of Records
11518 getRange : function(start, end){
11519 return this.data.getRange(start, end);
11523 storeOptions : function(o){
11524 o = Roo.apply({}, o);
11527 this.lastOptions = o;
11531 * Loads the Record cache from the configured Proxy using the configured Reader.
11533 * If using remote paging, then the first load call must specify the <em>start</em>
11534 * and <em>limit</em> properties in the options.params property to establish the initial
11535 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11537 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11538 * and this call will return before the new data has been loaded. Perform any post-processing
11539 * in a callback function, or in a "load" event handler.</strong>
11541 * @param {Object} options An object containing properties which control loading options:<ul>
11542 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11543 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11544 * passed the following arguments:<ul>
11545 * <li>r : Roo.data.Record[]</li>
11546 * <li>options: Options object from the load call</li>
11547 * <li>success: Boolean success indicator</li></ul></li>
11548 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11549 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11552 load : function(options){
11553 options = options || {};
11554 if(this.fireEvent("beforeload", this, options) !== false){
11555 this.storeOptions(options);
11556 var p = Roo.apply(options.params || {}, this.baseParams);
11557 // if meta was not loaded from remote source.. try requesting it.
11558 if (!this.reader.metaFromRemote) {
11559 p._requestMeta = 1;
11561 if(this.sortInfo && this.remoteSort){
11562 var pn = this.paramNames;
11563 p[pn["sort"]] = this.sortInfo.field;
11564 p[pn["dir"]] = this.sortInfo.direction;
11566 if (this.multiSort) {
11567 var pn = this.paramNames;
11568 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11571 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11576 * Reloads the Record cache from the configured Proxy using the configured Reader and
11577 * the options from the last load operation performed.
11578 * @param {Object} options (optional) An object containing properties which may override the options
11579 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11580 * the most recently used options are reused).
11582 reload : function(options){
11583 this.load(Roo.applyIf(options||{}, this.lastOptions));
11587 // Called as a callback by the Reader during a load operation.
11588 loadRecords : function(o, options, success){
11589 if(!o || success === false){
11590 if(success !== false){
11591 this.fireEvent("load", this, [], options, o);
11593 if(options.callback){
11594 options.callback.call(options.scope || this, [], options, false);
11598 // if data returned failure - throw an exception.
11599 if (o.success === false) {
11600 // show a message if no listener is registered.
11601 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11602 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11604 // loadmask wil be hooked into this..
11605 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11608 var r = o.records, t = o.totalRecords || r.length;
11610 this.fireEvent("beforeloadadd", this, r, options, o);
11612 if(!options || options.add !== true){
11613 if(this.pruneModifiedRecords){
11614 this.modified = [];
11616 for(var i = 0, len = r.length; i < len; i++){
11620 this.data = this.snapshot;
11621 delete this.snapshot;
11624 this.data.addAll(r);
11625 this.totalLength = t;
11627 this.fireEvent("datachanged", this);
11629 this.totalLength = Math.max(t, this.data.length+r.length);
11633 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11635 var e = new Roo.data.Record({});
11637 e.set(this.parent.displayField, this.parent.emptyTitle);
11638 e.set(this.parent.valueField, '');
11643 this.fireEvent("load", this, r, options, o);
11644 if(options.callback){
11645 options.callback.call(options.scope || this, r, options, true);
11651 * Loads data from a passed data block. A Reader which understands the format of the data
11652 * must have been configured in the constructor.
11653 * @param {Object} data The data block from which to read the Records. The format of the data expected
11654 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11655 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11657 loadData : function(o, append){
11658 var r = this.reader.readRecords(o);
11659 this.loadRecords(r, {add: append}, true);
11663 * Gets the number of cached records.
11665 * <em>If using paging, this may not be the total size of the dataset. If the data object
11666 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11667 * the data set size</em>
11669 getCount : function(){
11670 return this.data.length || 0;
11674 * Gets the total number of records in the dataset as returned by the server.
11676 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11677 * the dataset size</em>
11679 getTotalCount : function(){
11680 return this.totalLength || 0;
11684 * Returns the sort state of the Store as an object with two properties:
11686 field {String} The name of the field by which the Records are sorted
11687 direction {String} The sort order, "ASC" or "DESC"
11690 getSortState : function(){
11691 return this.sortInfo;
11695 applySort : function(){
11696 if(this.sortInfo && !this.remoteSort){
11697 var s = this.sortInfo, f = s.field;
11698 var st = this.fields.get(f).sortType;
11699 var fn = function(r1, r2){
11700 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11701 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11703 this.data.sort(s.direction, fn);
11704 if(this.snapshot && this.snapshot != this.data){
11705 this.snapshot.sort(s.direction, fn);
11711 * Sets the default sort column and order to be used by the next load operation.
11712 * @param {String} fieldName The name of the field to sort by.
11713 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11715 setDefaultSort : function(field, dir){
11716 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11720 * Sort the Records.
11721 * If remote sorting is used, the sort is performed on the server, and the cache is
11722 * reloaded. If local sorting is used, the cache is sorted internally.
11723 * @param {String} fieldName The name of the field to sort by.
11724 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11726 sort : function(fieldName, dir){
11727 var f = this.fields.get(fieldName);
11729 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11731 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11732 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11737 this.sortToggle[f.name] = dir;
11738 this.sortInfo = {field: f.name, direction: dir};
11739 if(!this.remoteSort){
11741 this.fireEvent("datachanged", this);
11743 this.load(this.lastOptions);
11748 * Calls the specified function for each of the Records in the cache.
11749 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11750 * Returning <em>false</em> aborts and exits the iteration.
11751 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11753 each : function(fn, scope){
11754 this.data.each(fn, scope);
11758 * Gets all records modified since the last commit. Modified records are persisted across load operations
11759 * (e.g., during paging).
11760 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11762 getModifiedRecords : function(){
11763 return this.modified;
11767 createFilterFn : function(property, value, anyMatch){
11768 if(!value.exec){ // not a regex
11769 value = String(value);
11770 if(value.length == 0){
11773 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11775 return function(r){
11776 return value.test(r.data[property]);
11781 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11782 * @param {String} property A field on your records
11783 * @param {Number} start The record index to start at (defaults to 0)
11784 * @param {Number} end The last record index to include (defaults to length - 1)
11785 * @return {Number} The sum
11787 sum : function(property, start, end){
11788 var rs = this.data.items, v = 0;
11789 start = start || 0;
11790 end = (end || end === 0) ? end : rs.length-1;
11792 for(var i = start; i <= end; i++){
11793 v += (rs[i].data[property] || 0);
11799 * Filter the records by a specified property.
11800 * @param {String} field A field on your records
11801 * @param {String/RegExp} value Either a string that the field
11802 * should start with or a RegExp to test against the field
11803 * @param {Boolean} anyMatch True to match any part not just the beginning
11805 filter : function(property, value, anyMatch){
11806 var fn = this.createFilterFn(property, value, anyMatch);
11807 return fn ? this.filterBy(fn) : this.clearFilter();
11811 * Filter by a function. The specified function will be called with each
11812 * record in this data source. If the function returns true the record is included,
11813 * otherwise it is filtered.
11814 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11815 * @param {Object} scope (optional) The scope of the function (defaults to this)
11817 filterBy : function(fn, scope){
11818 this.snapshot = this.snapshot || this.data;
11819 this.data = this.queryBy(fn, scope||this);
11820 this.fireEvent("datachanged", this);
11824 * Query the records by a specified property.
11825 * @param {String} field A field on your records
11826 * @param {String/RegExp} value Either a string that the field
11827 * should start with or a RegExp to test against the field
11828 * @param {Boolean} anyMatch True to match any part not just the beginning
11829 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11831 query : function(property, value, anyMatch){
11832 var fn = this.createFilterFn(property, value, anyMatch);
11833 return fn ? this.queryBy(fn) : this.data.clone();
11837 * Query by a function. The specified function will be called with each
11838 * record in this data source. If the function returns true the record is included
11840 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11841 * @param {Object} scope (optional) The scope of the function (defaults to this)
11842 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11844 queryBy : function(fn, scope){
11845 var data = this.snapshot || this.data;
11846 return data.filterBy(fn, scope||this);
11850 * Collects unique values for a particular dataIndex from this store.
11851 * @param {String} dataIndex The property to collect
11852 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11853 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11854 * @return {Array} An array of the unique values
11856 collect : function(dataIndex, allowNull, bypassFilter){
11857 var d = (bypassFilter === true && this.snapshot) ?
11858 this.snapshot.items : this.data.items;
11859 var v, sv, r = [], l = {};
11860 for(var i = 0, len = d.length; i < len; i++){
11861 v = d[i].data[dataIndex];
11863 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11872 * Revert to a view of the Record cache with no filtering applied.
11873 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11875 clearFilter : function(suppressEvent){
11876 if(this.snapshot && this.snapshot != this.data){
11877 this.data = this.snapshot;
11878 delete this.snapshot;
11879 if(suppressEvent !== true){
11880 this.fireEvent("datachanged", this);
11886 afterEdit : function(record){
11887 if(this.modified.indexOf(record) == -1){
11888 this.modified.push(record);
11890 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11894 afterReject : function(record){
11895 this.modified.remove(record);
11896 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11900 afterCommit : function(record){
11901 this.modified.remove(record);
11902 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11906 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11907 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11909 commitChanges : function(){
11910 var m = this.modified.slice(0);
11911 this.modified = [];
11912 for(var i = 0, len = m.length; i < len; i++){
11918 * Cancel outstanding changes on all changed records.
11920 rejectChanges : function(){
11921 var m = this.modified.slice(0);
11922 this.modified = [];
11923 for(var i = 0, len = m.length; i < len; i++){
11928 onMetaChange : function(meta, rtype, o){
11929 this.recordType = rtype;
11930 this.fields = rtype.prototype.fields;
11931 delete this.snapshot;
11932 this.sortInfo = meta.sortInfo || this.sortInfo;
11933 this.modified = [];
11934 this.fireEvent('metachange', this, this.reader.meta);
11937 moveIndex : function(data, type)
11939 var index = this.indexOf(data);
11941 var newIndex = index + type;
11945 this.insert(newIndex, data);
11950 * Ext JS Library 1.1.1
11951 * Copyright(c) 2006-2007, Ext JS, LLC.
11953 * Originally Released Under LGPL - original licence link has changed is not relivant.
11956 * <script type="text/javascript">
11960 * @class Roo.data.SimpleStore
11961 * @extends Roo.data.Store
11962 * Small helper class to make creating Stores from Array data easier.
11963 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11964 * @cfg {Array} fields An array of field definition objects, or field name strings.
11965 * @cfg {Array} data The multi-dimensional array of data
11967 * @param {Object} config
11969 Roo.data.SimpleStore = function(config){
11970 Roo.data.SimpleStore.superclass.constructor.call(this, {
11972 reader: new Roo.data.ArrayReader({
11975 Roo.data.Record.create(config.fields)
11977 proxy : new Roo.data.MemoryProxy(config.data)
11981 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11983 * Ext JS Library 1.1.1
11984 * Copyright(c) 2006-2007, Ext JS, LLC.
11986 * Originally Released Under LGPL - original licence link has changed is not relivant.
11989 * <script type="text/javascript">
11994 * @extends Roo.data.Store
11995 * @class Roo.data.JsonStore
11996 * Small helper class to make creating Stores for JSON data easier. <br/>
11998 var store = new Roo.data.JsonStore({
11999 url: 'get-images.php',
12001 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12004 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12005 * JsonReader and HttpProxy (unless inline data is provided).</b>
12006 * @cfg {Array} fields An array of field definition objects, or field name strings.
12008 * @param {Object} config
12010 Roo.data.JsonStore = function(c){
12011 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12012 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12013 reader: new Roo.data.JsonReader(c, c.fields)
12016 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12018 * Ext JS Library 1.1.1
12019 * Copyright(c) 2006-2007, Ext JS, LLC.
12021 * Originally Released Under LGPL - original licence link has changed is not relivant.
12024 * <script type="text/javascript">
12028 Roo.data.Field = function(config){
12029 if(typeof config == "string"){
12030 config = {name: config};
12032 Roo.apply(this, config);
12035 this.type = "auto";
12038 var st = Roo.data.SortTypes;
12039 // named sortTypes are supported, here we look them up
12040 if(typeof this.sortType == "string"){
12041 this.sortType = st[this.sortType];
12044 // set default sortType for strings and dates
12045 if(!this.sortType){
12048 this.sortType = st.asUCString;
12051 this.sortType = st.asDate;
12054 this.sortType = st.none;
12059 var stripRe = /[\$,%]/g;
12061 // prebuilt conversion function for this field, instead of
12062 // switching every time we're reading a value
12064 var cv, dateFormat = this.dateFormat;
12069 cv = function(v){ return v; };
12072 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12076 return v !== undefined && v !== null && v !== '' ?
12077 parseInt(String(v).replace(stripRe, ""), 10) : '';
12082 return v !== undefined && v !== null && v !== '' ?
12083 parseFloat(String(v).replace(stripRe, ""), 10) : '';
12088 cv = function(v){ return v === true || v === "true" || v == 1; };
12095 if(v instanceof Date){
12099 if(dateFormat == "timestamp"){
12100 return new Date(v*1000);
12102 return Date.parseDate(v, dateFormat);
12104 var parsed = Date.parse(v);
12105 return parsed ? new Date(parsed) : null;
12114 Roo.data.Field.prototype = {
12122 * Ext JS Library 1.1.1
12123 * Copyright(c) 2006-2007, Ext JS, LLC.
12125 * Originally Released Under LGPL - original licence link has changed is not relivant.
12128 * <script type="text/javascript">
12131 // Base class for reading structured data from a data source. This class is intended to be
12132 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12135 * @class Roo.data.DataReader
12136 * Base class for reading structured data from a data source. This class is intended to be
12137 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12140 Roo.data.DataReader = function(meta, recordType){
12144 this.recordType = recordType instanceof Array ?
12145 Roo.data.Record.create(recordType) : recordType;
12148 Roo.data.DataReader.prototype = {
12150 * Create an empty record
12151 * @param {Object} data (optional) - overlay some values
12152 * @return {Roo.data.Record} record created.
12154 newRow : function(d) {
12156 this.recordType.prototype.fields.each(function(c) {
12158 case 'int' : da[c.name] = 0; break;
12159 case 'date' : da[c.name] = new Date(); break;
12160 case 'float' : da[c.name] = 0.0; break;
12161 case 'boolean' : da[c.name] = false; break;
12162 default : da[c.name] = ""; break;
12166 return new this.recordType(Roo.apply(da, d));
12171 * Ext JS Library 1.1.1
12172 * Copyright(c) 2006-2007, Ext JS, LLC.
12174 * Originally Released Under LGPL - original licence link has changed is not relivant.
12177 * <script type="text/javascript">
12181 * @class Roo.data.DataProxy
12182 * @extends Roo.data.Observable
12183 * This class is an abstract base class for implementations which provide retrieval of
12184 * unformatted data objects.<br>
12186 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12187 * (of the appropriate type which knows how to parse the data object) to provide a block of
12188 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12190 * Custom implementations must implement the load method as described in
12191 * {@link Roo.data.HttpProxy#load}.
12193 Roo.data.DataProxy = function(){
12196 * @event beforeload
12197 * Fires before a network request is made to retrieve a data object.
12198 * @param {Object} This DataProxy object.
12199 * @param {Object} params The params parameter to the load function.
12204 * Fires before the load method's callback is called.
12205 * @param {Object} This DataProxy object.
12206 * @param {Object} o The data object.
12207 * @param {Object} arg The callback argument object passed to the load function.
12211 * @event loadexception
12212 * Fires if an Exception occurs during data retrieval.
12213 * @param {Object} This DataProxy object.
12214 * @param {Object} o The data object.
12215 * @param {Object} arg The callback argument object passed to the load function.
12216 * @param {Object} e The Exception.
12218 loadexception : true
12220 Roo.data.DataProxy.superclass.constructor.call(this);
12223 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12226 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12230 * Ext JS Library 1.1.1
12231 * Copyright(c) 2006-2007, Ext JS, LLC.
12233 * Originally Released Under LGPL - original licence link has changed is not relivant.
12236 * <script type="text/javascript">
12239 * @class Roo.data.MemoryProxy
12240 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12241 * to the Reader when its load method is called.
12243 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12245 Roo.data.MemoryProxy = function(data){
12249 Roo.data.MemoryProxy.superclass.constructor.call(this);
12253 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12256 * Load data from the requested source (in this case an in-memory
12257 * data object passed to the constructor), read the data object into
12258 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12259 * process that block using the passed callback.
12260 * @param {Object} params This parameter is not used by the MemoryProxy class.
12261 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12262 * object into a block of Roo.data.Records.
12263 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12264 * The function must be passed <ul>
12265 * <li>The Record block object</li>
12266 * <li>The "arg" argument from the load function</li>
12267 * <li>A boolean success indicator</li>
12269 * @param {Object} scope The scope in which to call the callback
12270 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12272 load : function(params, reader, callback, scope, arg){
12273 params = params || {};
12276 result = reader.readRecords(this.data);
12278 this.fireEvent("loadexception", this, arg, null, e);
12279 callback.call(scope, null, arg, false);
12282 callback.call(scope, result, arg, true);
12286 update : function(params, records){
12291 * Ext JS Library 1.1.1
12292 * Copyright(c) 2006-2007, Ext JS, LLC.
12294 * Originally Released Under LGPL - original licence link has changed is not relivant.
12297 * <script type="text/javascript">
12300 * @class Roo.data.HttpProxy
12301 * @extends Roo.data.DataProxy
12302 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12303 * configured to reference a certain URL.<br><br>
12305 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12306 * from which the running page was served.<br><br>
12308 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12310 * Be aware that to enable the browser to parse an XML document, the server must set
12311 * the Content-Type header in the HTTP response to "text/xml".
12313 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12314 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12315 * will be used to make the request.
12317 Roo.data.HttpProxy = function(conn){
12318 Roo.data.HttpProxy.superclass.constructor.call(this);
12319 // is conn a conn config or a real conn?
12321 this.useAjax = !conn || !conn.events;
12325 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12326 // thse are take from connection...
12329 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12332 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12333 * extra parameters to each request made by this object. (defaults to undefined)
12336 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12337 * to each request made by this object. (defaults to undefined)
12340 * @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)
12343 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12346 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12352 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12356 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12357 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12358 * a finer-grained basis than the DataProxy events.
12360 getConnection : function(){
12361 return this.useAjax ? Roo.Ajax : this.conn;
12365 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12366 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12367 * process that block using the passed callback.
12368 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12369 * for the request to the remote server.
12370 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12371 * object into a block of Roo.data.Records.
12372 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12373 * The function must be passed <ul>
12374 * <li>The Record block object</li>
12375 * <li>The "arg" argument from the load function</li>
12376 * <li>A boolean success indicator</li>
12378 * @param {Object} scope The scope in which to call the callback
12379 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12381 load : function(params, reader, callback, scope, arg){
12382 if(this.fireEvent("beforeload", this, params) !== false){
12384 params : params || {},
12386 callback : callback,
12391 callback : this.loadResponse,
12395 Roo.applyIf(o, this.conn);
12396 if(this.activeRequest){
12397 Roo.Ajax.abort(this.activeRequest);
12399 this.activeRequest = Roo.Ajax.request(o);
12401 this.conn.request(o);
12404 callback.call(scope||this, null, arg, false);
12409 loadResponse : function(o, success, response){
12410 delete this.activeRequest;
12412 this.fireEvent("loadexception", this, o, response);
12413 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12418 result = o.reader.read(response);
12420 this.fireEvent("loadexception", this, o, response, e);
12421 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12425 this.fireEvent("load", this, o, o.request.arg);
12426 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12430 update : function(dataSet){
12435 updateResponse : function(dataSet){
12440 * Ext JS Library 1.1.1
12441 * Copyright(c) 2006-2007, Ext JS, LLC.
12443 * Originally Released Under LGPL - original licence link has changed is not relivant.
12446 * <script type="text/javascript">
12450 * @class Roo.data.ScriptTagProxy
12451 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12452 * other than the originating domain of the running page.<br><br>
12454 * <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
12455 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12457 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12458 * source code that is used as the source inside a <script> tag.<br><br>
12460 * In order for the browser to process the returned data, the server must wrap the data object
12461 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12462 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12463 * depending on whether the callback name was passed:
12466 boolean scriptTag = false;
12467 String cb = request.getParameter("callback");
12470 response.setContentType("text/javascript");
12472 response.setContentType("application/x-json");
12474 Writer out = response.getWriter();
12476 out.write(cb + "(");
12478 out.print(dataBlock.toJsonString());
12485 * @param {Object} config A configuration object.
12487 Roo.data.ScriptTagProxy = function(config){
12488 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12489 Roo.apply(this, config);
12490 this.head = document.getElementsByTagName("head")[0];
12493 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12495 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12497 * @cfg {String} url The URL from which to request the data object.
12500 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12504 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12505 * the server the name of the callback function set up by the load call to process the returned data object.
12506 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12507 * javascript output which calls this named function passing the data object as its only parameter.
12509 callbackParam : "callback",
12511 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12512 * name to the request.
12517 * Load data from the configured URL, read the data object into
12518 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12519 * process that block using the passed callback.
12520 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12521 * for the request to the remote server.
12522 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12523 * object into a block of Roo.data.Records.
12524 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12525 * The function must be passed <ul>
12526 * <li>The Record block object</li>
12527 * <li>The "arg" argument from the load function</li>
12528 * <li>A boolean success indicator</li>
12530 * @param {Object} scope The scope in which to call the callback
12531 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12533 load : function(params, reader, callback, scope, arg){
12534 if(this.fireEvent("beforeload", this, params) !== false){
12536 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12538 var url = this.url;
12539 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12541 url += "&_dc=" + (new Date().getTime());
12543 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12546 cb : "stcCallback"+transId,
12547 scriptId : "stcScript"+transId,
12551 callback : callback,
12557 window[trans.cb] = function(o){
12558 conn.handleResponse(o, trans);
12561 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12563 if(this.autoAbort !== false){
12567 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12569 var script = document.createElement("script");
12570 script.setAttribute("src", url);
12571 script.setAttribute("type", "text/javascript");
12572 script.setAttribute("id", trans.scriptId);
12573 this.head.appendChild(script);
12575 this.trans = trans;
12577 callback.call(scope||this, null, arg, false);
12582 isLoading : function(){
12583 return this.trans ? true : false;
12587 * Abort the current server request.
12589 abort : function(){
12590 if(this.isLoading()){
12591 this.destroyTrans(this.trans);
12596 destroyTrans : function(trans, isLoaded){
12597 this.head.removeChild(document.getElementById(trans.scriptId));
12598 clearTimeout(trans.timeoutId);
12600 window[trans.cb] = undefined;
12602 delete window[trans.cb];
12605 // if hasn't been loaded, wait for load to remove it to prevent script error
12606 window[trans.cb] = function(){
12607 window[trans.cb] = undefined;
12609 delete window[trans.cb];
12616 handleResponse : function(o, trans){
12617 this.trans = false;
12618 this.destroyTrans(trans, true);
12621 result = trans.reader.readRecords(o);
12623 this.fireEvent("loadexception", this, o, trans.arg, e);
12624 trans.callback.call(trans.scope||window, null, trans.arg, false);
12627 this.fireEvent("load", this, o, trans.arg);
12628 trans.callback.call(trans.scope||window, result, trans.arg, true);
12632 handleFailure : function(trans){
12633 this.trans = false;
12634 this.destroyTrans(trans, false);
12635 this.fireEvent("loadexception", this, null, trans.arg);
12636 trans.callback.call(trans.scope||window, null, trans.arg, false);
12640 * Ext JS Library 1.1.1
12641 * Copyright(c) 2006-2007, Ext JS, LLC.
12643 * Originally Released Under LGPL - original licence link has changed is not relivant.
12646 * <script type="text/javascript">
12650 * @class Roo.data.JsonReader
12651 * @extends Roo.data.DataReader
12652 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12653 * based on mappings in a provided Roo.data.Record constructor.
12655 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12656 * in the reply previously.
12661 var RecordDef = Roo.data.Record.create([
12662 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12663 {name: 'occupation'} // This field will use "occupation" as the mapping.
12665 var myReader = new Roo.data.JsonReader({
12666 totalProperty: "results", // The property which contains the total dataset size (optional)
12667 root: "rows", // The property which contains an Array of row objects
12668 id: "id" // The property within each row object that provides an ID for the record (optional)
12672 * This would consume a JSON file like this:
12674 { 'results': 2, 'rows': [
12675 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12676 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12679 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12680 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12681 * paged from the remote server.
12682 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12683 * @cfg {String} root name of the property which contains the Array of row objects.
12684 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12685 * @cfg {Array} fields Array of field definition objects
12687 * Create a new JsonReader
12688 * @param {Object} meta Metadata configuration options
12689 * @param {Object} recordType Either an Array of field definition objects,
12690 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12692 Roo.data.JsonReader = function(meta, recordType){
12695 // set some defaults:
12696 Roo.applyIf(meta, {
12697 totalProperty: 'total',
12698 successProperty : 'success',
12703 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12705 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12708 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12709 * Used by Store query builder to append _requestMeta to params.
12712 metaFromRemote : false,
12714 * This method is only used by a DataProxy which has retrieved data from a remote server.
12715 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12716 * @return {Object} data A data block which is used by an Roo.data.Store object as
12717 * a cache of Roo.data.Records.
12719 read : function(response){
12720 var json = response.responseText;
12722 var o = /* eval:var:o */ eval("("+json+")");
12724 throw {message: "JsonReader.read: Json object not found"};
12730 this.metaFromRemote = true;
12731 this.meta = o.metaData;
12732 this.recordType = Roo.data.Record.create(o.metaData.fields);
12733 this.onMetaChange(this.meta, this.recordType, o);
12735 return this.readRecords(o);
12738 // private function a store will implement
12739 onMetaChange : function(meta, recordType, o){
12746 simpleAccess: function(obj, subsc) {
12753 getJsonAccessor: function(){
12755 return function(expr) {
12757 return(re.test(expr))
12758 ? new Function("obj", "return obj." + expr)
12763 return Roo.emptyFn;
12768 * Create a data block containing Roo.data.Records from an XML document.
12769 * @param {Object} o An object which contains an Array of row objects in the property specified
12770 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12771 * which contains the total size of the dataset.
12772 * @return {Object} data A data block which is used by an Roo.data.Store object as
12773 * a cache of Roo.data.Records.
12775 readRecords : function(o){
12777 * After any data loads, the raw JSON data is available for further custom processing.
12781 var s = this.meta, Record = this.recordType,
12782 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12784 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12786 if(s.totalProperty) {
12787 this.getTotal = this.getJsonAccessor(s.totalProperty);
12789 if(s.successProperty) {
12790 this.getSuccess = this.getJsonAccessor(s.successProperty);
12792 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12794 var g = this.getJsonAccessor(s.id);
12795 this.getId = function(rec) {
12797 return (r === undefined || r === "") ? null : r;
12800 this.getId = function(){return null;};
12803 for(var jj = 0; jj < fl; jj++){
12805 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12806 this.ef[jj] = this.getJsonAccessor(map);
12810 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12811 if(s.totalProperty){
12812 var vt = parseInt(this.getTotal(o), 10);
12817 if(s.successProperty){
12818 var vs = this.getSuccess(o);
12819 if(vs === false || vs === 'false'){
12824 for(var i = 0; i < c; i++){
12827 var id = this.getId(n);
12828 for(var j = 0; j < fl; j++){
12830 var v = this.ef[j](n);
12832 Roo.log('missing convert for ' + f.name);
12836 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12838 var record = new Record(values, id);
12840 records[i] = record;
12846 totalRecords : totalRecords
12851 * Ext JS Library 1.1.1
12852 * Copyright(c) 2006-2007, Ext JS, LLC.
12854 * Originally Released Under LGPL - original licence link has changed is not relivant.
12857 * <script type="text/javascript">
12861 * @class Roo.data.ArrayReader
12862 * @extends Roo.data.DataReader
12863 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12864 * Each element of that Array represents a row of data fields. The
12865 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12866 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12870 var RecordDef = Roo.data.Record.create([
12871 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12872 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12874 var myReader = new Roo.data.ArrayReader({
12875 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12879 * This would consume an Array like this:
12881 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12883 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12885 * Create a new JsonReader
12886 * @param {Object} meta Metadata configuration options.
12887 * @param {Object} recordType Either an Array of field definition objects
12888 * as specified to {@link Roo.data.Record#create},
12889 * or an {@link Roo.data.Record} object
12890 * created using {@link Roo.data.Record#create}.
12892 Roo.data.ArrayReader = function(meta, recordType){
12893 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12896 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12898 * Create a data block containing Roo.data.Records from an XML document.
12899 * @param {Object} o An Array of row objects which represents the dataset.
12900 * @return {Object} data A data block which is used by an Roo.data.Store object as
12901 * a cache of Roo.data.Records.
12903 readRecords : function(o){
12904 var sid = this.meta ? this.meta.id : null;
12905 var recordType = this.recordType, fields = recordType.prototype.fields;
12908 for(var i = 0; i < root.length; i++){
12911 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12912 for(var j = 0, jlen = fields.length; j < jlen; j++){
12913 var f = fields.items[j];
12914 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12915 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12917 values[f.name] = v;
12919 var record = new recordType(values, id);
12921 records[records.length] = record;
12925 totalRecords : records.length
12934 * @class Roo.bootstrap.ComboBox
12935 * @extends Roo.bootstrap.TriggerField
12936 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12937 * @cfg {Boolean} append (true|false) default false
12938 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12939 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12940 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12941 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12942 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12943 * @cfg {Boolean} animate default true
12944 * @cfg {Boolean} emptyResultText only for touch device
12945 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12946 * @cfg {String} emptyTitle default ''
12948 * Create a new ComboBox.
12949 * @param {Object} config Configuration options
12951 Roo.bootstrap.ComboBox = function(config){
12952 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12956 * Fires when the dropdown list is expanded
12957 * @param {Roo.bootstrap.ComboBox} combo This combo box
12962 * Fires when the dropdown list is collapsed
12963 * @param {Roo.bootstrap.ComboBox} combo This combo box
12967 * @event beforeselect
12968 * Fires before a list item is selected. Return false to cancel the selection.
12969 * @param {Roo.bootstrap.ComboBox} combo This combo box
12970 * @param {Roo.data.Record} record The data record returned from the underlying store
12971 * @param {Number} index The index of the selected item in the dropdown list
12973 'beforeselect' : true,
12976 * Fires when a list item is selected
12977 * @param {Roo.bootstrap.ComboBox} combo This combo box
12978 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12979 * @param {Number} index The index of the selected item in the dropdown list
12983 * @event beforequery
12984 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12985 * The event object passed has these properties:
12986 * @param {Roo.bootstrap.ComboBox} combo This combo box
12987 * @param {String} query The query
12988 * @param {Boolean} forceAll true to force "all" query
12989 * @param {Boolean} cancel true to cancel the query
12990 * @param {Object} e The query event object
12992 'beforequery': true,
12995 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12996 * @param {Roo.bootstrap.ComboBox} combo This combo box
13001 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13002 * @param {Roo.bootstrap.ComboBox} combo This combo box
13003 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13008 * Fires when the remove value from the combobox array
13009 * @param {Roo.bootstrap.ComboBox} combo This combo box
13013 * @event afterremove
13014 * Fires when the remove value from the combobox array
13015 * @param {Roo.bootstrap.ComboBox} combo This combo box
13017 'afterremove' : true,
13019 * @event specialfilter
13020 * Fires when specialfilter
13021 * @param {Roo.bootstrap.ComboBox} combo This combo box
13023 'specialfilter' : true,
13026 * Fires when tick the element
13027 * @param {Roo.bootstrap.ComboBox} combo This combo box
13031 * @event touchviewdisplay
13032 * Fires when touch view require special display (default is using displayField)
13033 * @param {Roo.bootstrap.ComboBox} combo This combo box
13034 * @param {Object} cfg set html .
13036 'touchviewdisplay' : true
13041 this.tickItems = [];
13043 this.selectedIndex = -1;
13044 if(this.mode == 'local'){
13045 if(config.queryDelay === undefined){
13046 this.queryDelay = 10;
13048 if(config.minChars === undefined){
13054 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13057 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13058 * rendering into an Roo.Editor, defaults to false)
13061 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13062 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13065 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13068 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13069 * the dropdown list (defaults to undefined, with no header element)
13073 * @cfg {String/Roo.Template} tpl The template to use to render the output
13077 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13079 listWidth: undefined,
13081 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13082 * mode = 'remote' or 'text' if mode = 'local')
13084 displayField: undefined,
13087 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13088 * mode = 'remote' or 'value' if mode = 'local').
13089 * Note: use of a valueField requires the user make a selection
13090 * in order for a value to be mapped.
13092 valueField: undefined,
13094 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13099 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13100 * field's data value (defaults to the underlying DOM element's name)
13102 hiddenName: undefined,
13104 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13108 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13110 selectedClass: 'active',
13113 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13117 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13118 * anchor positions (defaults to 'tl-bl')
13120 listAlign: 'tl-bl?',
13122 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13126 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13127 * query specified by the allQuery config option (defaults to 'query')
13129 triggerAction: 'query',
13131 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13132 * (defaults to 4, does not apply if editable = false)
13136 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13137 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13141 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13142 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13146 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13147 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13151 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13152 * when editable = true (defaults to false)
13154 selectOnFocus:false,
13156 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13158 queryParam: 'query',
13160 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13161 * when mode = 'remote' (defaults to 'Loading...')
13163 loadingText: 'Loading...',
13165 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13169 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13173 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13174 * traditional select (defaults to true)
13178 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13182 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13186 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13187 * listWidth has a higher value)
13191 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13192 * allow the user to set arbitrary text into the field (defaults to false)
13194 forceSelection:false,
13196 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13197 * if typeAhead = true (defaults to 250)
13199 typeAheadDelay : 250,
13201 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13202 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13204 valueNotFoundText : undefined,
13206 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13208 blockFocus : false,
13211 * @cfg {Boolean} disableClear Disable showing of clear button.
13213 disableClear : false,
13215 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13217 alwaysQuery : false,
13220 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13225 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13227 invalidClass : "has-warning",
13230 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13232 validClass : "has-success",
13235 * @cfg {Boolean} specialFilter (true|false) special filter default false
13237 specialFilter : false,
13240 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13242 mobileTouchView : true,
13245 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13247 useNativeIOS : false,
13250 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13252 mobile_restrict_height : false,
13254 ios_options : false,
13266 btnPosition : 'right',
13267 triggerList : true,
13268 showToggleBtn : true,
13270 emptyResultText: 'Empty',
13271 triggerText : 'Select',
13274 // element that contains real text value.. (when hidden is used..)
13276 getAutoCreate : function()
13281 * Render classic select for iso
13284 if(Roo.isIOS && this.useNativeIOS){
13285 cfg = this.getAutoCreateNativeIOS();
13293 if(Roo.isTouch && this.mobileTouchView){
13294 cfg = this.getAutoCreateTouchView();
13301 if(!this.tickable){
13302 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13307 * ComboBox with tickable selections
13310 var align = this.labelAlign || this.parentLabelAlign();
13313 cls : 'form-group roo-combobox-tickable' //input-group
13316 var btn_text_select = '';
13317 var btn_text_done = '';
13318 var btn_text_cancel = '';
13320 if (this.btn_text_show) {
13321 btn_text_select = 'Select';
13322 btn_text_done = 'Done';
13323 btn_text_cancel = 'Cancel';
13328 cls : 'tickable-buttons',
13333 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13334 //html : this.triggerText
13335 html: btn_text_select
13341 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13343 html: btn_text_done
13349 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13351 html: btn_text_cancel
13357 buttons.cn.unshift({
13359 cls: 'roo-select2-search-field-input'
13365 Roo.each(buttons.cn, function(c){
13367 c.cls += ' btn-' + _this.size;
13370 if (_this.disabled) {
13377 style : 'display: contents',
13382 cls: 'form-hidden-field'
13386 cls: 'roo-select2-choices',
13390 cls: 'roo-select2-search-field',
13401 cls: 'roo-select2-container input-group roo-select2-container-multi',
13407 // cls: 'typeahead typeahead-long dropdown-menu',
13408 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13413 if(this.hasFeedback && !this.allowBlank){
13417 cls: 'glyphicon form-control-feedback'
13420 combobox.cn.push(feedback);
13425 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13426 tooltip : 'This field is required'
13428 if (Roo.bootstrap.version == 4) {
13431 style : 'display:none'
13434 if (align ==='left' && this.fieldLabel.length) {
13436 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13443 cls : 'control-label col-form-label',
13444 html : this.fieldLabel
13456 var labelCfg = cfg.cn[1];
13457 var contentCfg = cfg.cn[2];
13460 if(this.indicatorpos == 'right'){
13466 cls : 'control-label col-form-label',
13470 html : this.fieldLabel
13486 labelCfg = cfg.cn[0];
13487 contentCfg = cfg.cn[1];
13491 if(this.labelWidth > 12){
13492 labelCfg.style = "width: " + this.labelWidth + 'px';
13495 if(this.labelWidth < 13 && this.labelmd == 0){
13496 this.labelmd = this.labelWidth;
13499 if(this.labellg > 0){
13500 labelCfg.cls += ' col-lg-' + this.labellg;
13501 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13504 if(this.labelmd > 0){
13505 labelCfg.cls += ' col-md-' + this.labelmd;
13506 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13509 if(this.labelsm > 0){
13510 labelCfg.cls += ' col-sm-' + this.labelsm;
13511 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13514 if(this.labelxs > 0){
13515 labelCfg.cls += ' col-xs-' + this.labelxs;
13516 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13520 } else if ( this.fieldLabel.length) {
13521 // Roo.log(" label");
13526 //cls : 'input-group-addon',
13527 html : this.fieldLabel
13532 if(this.indicatorpos == 'right'){
13536 //cls : 'input-group-addon',
13537 html : this.fieldLabel
13547 // Roo.log(" no label && no align");
13554 ['xs','sm','md','lg'].map(function(size){
13555 if (settings[size]) {
13556 cfg.cls += ' col-' + size + '-' + settings[size];
13564 _initEventsCalled : false,
13567 initEvents: function()
13569 if (this._initEventsCalled) { // as we call render... prevent looping...
13572 this._initEventsCalled = true;
13575 throw "can not find store for combo";
13578 this.indicator = this.indicatorEl();
13580 this.store = Roo.factory(this.store, Roo.data);
13581 this.store.parent = this;
13583 // if we are building from html. then this element is so complex, that we can not really
13584 // use the rendered HTML.
13585 // so we have to trash and replace the previous code.
13586 if (Roo.XComponent.build_from_html) {
13587 // remove this element....
13588 var e = this.el.dom, k=0;
13589 while (e ) { e = e.previousSibling; ++k;}
13594 this.rendered = false;
13596 this.render(this.parent().getChildContainer(true), k);
13599 if(Roo.isIOS && this.useNativeIOS){
13600 this.initIOSView();
13608 if(Roo.isTouch && this.mobileTouchView){
13609 this.initTouchView();
13614 this.initTickableEvents();
13618 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13620 if(this.hiddenName){
13622 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13624 this.hiddenField.dom.value =
13625 this.hiddenValue !== undefined ? this.hiddenValue :
13626 this.value !== undefined ? this.value : '';
13628 // prevent input submission
13629 this.el.dom.removeAttribute('name');
13630 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13635 // this.el.dom.setAttribute('autocomplete', 'off');
13638 var cls = 'x-combo-list';
13640 //this.list = new Roo.Layer({
13641 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13647 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13648 _this.list.setWidth(lw);
13651 this.list.on('mouseover', this.onViewOver, this);
13652 this.list.on('mousemove', this.onViewMove, this);
13653 this.list.on('scroll', this.onViewScroll, this);
13656 this.list.swallowEvent('mousewheel');
13657 this.assetHeight = 0;
13660 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13661 this.assetHeight += this.header.getHeight();
13664 this.innerList = this.list.createChild({cls:cls+'-inner'});
13665 this.innerList.on('mouseover', this.onViewOver, this);
13666 this.innerList.on('mousemove', this.onViewMove, this);
13667 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13669 if(this.allowBlank && !this.pageSize && !this.disableClear){
13670 this.footer = this.list.createChild({cls:cls+'-ft'});
13671 this.pageTb = new Roo.Toolbar(this.footer);
13675 this.footer = this.list.createChild({cls:cls+'-ft'});
13676 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13677 {pageSize: this.pageSize});
13681 if (this.pageTb && this.allowBlank && !this.disableClear) {
13683 this.pageTb.add(new Roo.Toolbar.Fill(), {
13684 cls: 'x-btn-icon x-btn-clear',
13686 handler: function()
13689 _this.clearValue();
13690 _this.onSelect(false, -1);
13695 this.assetHeight += this.footer.getHeight();
13700 this.tpl = Roo.bootstrap.version == 4 ?
13701 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
13702 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13705 this.view = new Roo.View(this.list, this.tpl, {
13706 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13708 //this.view.wrapEl.setDisplayed(false);
13709 this.view.on('click', this.onViewClick, this);
13712 this.store.on('beforeload', this.onBeforeLoad, this);
13713 this.store.on('load', this.onLoad, this);
13714 this.store.on('loadexception', this.onLoadException, this);
13716 if(this.resizable){
13717 this.resizer = new Roo.Resizable(this.list, {
13718 pinned:true, handles:'se'
13720 this.resizer.on('resize', function(r, w, h){
13721 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13722 this.listWidth = w;
13723 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13724 this.restrictHeight();
13726 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13729 if(!this.editable){
13730 this.editable = true;
13731 this.setEditable(false);
13736 if (typeof(this.events.add.listeners) != 'undefined') {
13738 this.addicon = this.wrap.createChild(
13739 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13741 this.addicon.on('click', function(e) {
13742 this.fireEvent('add', this);
13745 if (typeof(this.events.edit.listeners) != 'undefined') {
13747 this.editicon = this.wrap.createChild(
13748 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13749 if (this.addicon) {
13750 this.editicon.setStyle('margin-left', '40px');
13752 this.editicon.on('click', function(e) {
13754 // we fire even if inothing is selected..
13755 this.fireEvent('edit', this, this.lastData );
13761 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13762 "up" : function(e){
13763 this.inKeyMode = true;
13767 "down" : function(e){
13768 if(!this.isExpanded()){
13769 this.onTriggerClick();
13771 this.inKeyMode = true;
13776 "enter" : function(e){
13777 // this.onViewClick();
13781 if(this.fireEvent("specialkey", this, e)){
13782 this.onViewClick(false);
13788 "esc" : function(e){
13792 "tab" : function(e){
13795 if(this.fireEvent("specialkey", this, e)){
13796 this.onViewClick(false);
13804 doRelay : function(foo, bar, hname){
13805 if(hname == 'down' || this.scope.isExpanded()){
13806 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13815 this.queryDelay = Math.max(this.queryDelay || 10,
13816 this.mode == 'local' ? 10 : 250);
13819 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13821 if(this.typeAhead){
13822 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13824 if(this.editable !== false){
13825 this.inputEl().on("keyup", this.onKeyUp, this);
13827 if(this.forceSelection){
13828 this.inputEl().on('blur', this.doForce, this);
13832 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13833 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13837 initTickableEvents: function()
13841 if(this.hiddenName){
13843 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13845 this.hiddenField.dom.value =
13846 this.hiddenValue !== undefined ? this.hiddenValue :
13847 this.value !== undefined ? this.value : '';
13849 // prevent input submission
13850 this.el.dom.removeAttribute('name');
13851 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13856 // this.list = this.el.select('ul.dropdown-menu',true).first();
13858 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13859 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13860 if(this.triggerList){
13861 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13864 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13865 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13867 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13868 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13870 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13871 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13873 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13874 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13875 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13878 this.cancelBtn.hide();
13883 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13884 _this.list.setWidth(lw);
13887 this.list.on('mouseover', this.onViewOver, this);
13888 this.list.on('mousemove', this.onViewMove, this);
13890 this.list.on('scroll', this.onViewScroll, this);
13893 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13894 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13897 this.view = new Roo.View(this.list, this.tpl, {
13902 selectedClass: this.selectedClass
13905 //this.view.wrapEl.setDisplayed(false);
13906 this.view.on('click', this.onViewClick, this);
13910 this.store.on('beforeload', this.onBeforeLoad, this);
13911 this.store.on('load', this.onLoad, this);
13912 this.store.on('loadexception', this.onLoadException, this);
13915 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13916 "up" : function(e){
13917 this.inKeyMode = true;
13921 "down" : function(e){
13922 this.inKeyMode = true;
13926 "enter" : function(e){
13927 if(this.fireEvent("specialkey", this, e)){
13928 this.onViewClick(false);
13934 "esc" : function(e){
13935 this.onTickableFooterButtonClick(e, false, false);
13938 "tab" : function(e){
13939 this.fireEvent("specialkey", this, e);
13941 this.onTickableFooterButtonClick(e, false, false);
13948 doRelay : function(e, fn, key){
13949 if(this.scope.isExpanded()){
13950 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13959 this.queryDelay = Math.max(this.queryDelay || 10,
13960 this.mode == 'local' ? 10 : 250);
13963 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13965 if(this.typeAhead){
13966 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13969 if(this.editable !== false){
13970 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13973 this.indicator = this.indicatorEl();
13975 if(this.indicator){
13976 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13977 this.indicator.hide();
13982 onDestroy : function(){
13984 this.view.setStore(null);
13985 this.view.el.removeAllListeners();
13986 this.view.el.remove();
13987 this.view.purgeListeners();
13990 this.list.dom.innerHTML = '';
13994 this.store.un('beforeload', this.onBeforeLoad, this);
13995 this.store.un('load', this.onLoad, this);
13996 this.store.un('loadexception', this.onLoadException, this);
13998 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14002 fireKey : function(e){
14003 if(e.isNavKeyPress() && !this.list.isVisible()){
14004 this.fireEvent("specialkey", this, e);
14009 onResize: function(w, h){
14010 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14012 // if(typeof w != 'number'){
14013 // // we do not handle it!?!?
14016 // var tw = this.trigger.getWidth();
14017 // // tw += this.addicon ? this.addicon.getWidth() : 0;
14018 // // tw += this.editicon ? this.editicon.getWidth() : 0;
14020 // this.inputEl().setWidth( this.adjustWidth('input', x));
14022 // //this.trigger.setStyle('left', x+'px');
14024 // if(this.list && this.listWidth === undefined){
14025 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14026 // this.list.setWidth(lw);
14027 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14035 * Allow or prevent the user from directly editing the field text. If false is passed,
14036 * the user will only be able to select from the items defined in the dropdown list. This method
14037 * is the runtime equivalent of setting the 'editable' config option at config time.
14038 * @param {Boolean} value True to allow the user to directly edit the field text
14040 setEditable : function(value){
14041 if(value == this.editable){
14044 this.editable = value;
14046 this.inputEl().dom.setAttribute('readOnly', true);
14047 this.inputEl().on('mousedown', this.onTriggerClick, this);
14048 this.inputEl().addClass('x-combo-noedit');
14050 this.inputEl().dom.setAttribute('readOnly', false);
14051 this.inputEl().un('mousedown', this.onTriggerClick, this);
14052 this.inputEl().removeClass('x-combo-noedit');
14058 onBeforeLoad : function(combo,opts){
14059 if(!this.hasFocus){
14063 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14065 this.restrictHeight();
14066 this.selectedIndex = -1;
14070 onLoad : function(){
14072 this.hasQuery = false;
14074 if(!this.hasFocus){
14078 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14079 this.loading.hide();
14082 if(this.store.getCount() > 0){
14085 this.restrictHeight();
14086 if(this.lastQuery == this.allQuery){
14087 if(this.editable && !this.tickable){
14088 this.inputEl().dom.select();
14092 !this.selectByValue(this.value, true) &&
14095 !this.store.lastOptions ||
14096 typeof(this.store.lastOptions.add) == 'undefined' ||
14097 this.store.lastOptions.add != true
14100 this.select(0, true);
14103 if(this.autoFocus){
14106 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14107 this.taTask.delay(this.typeAheadDelay);
14111 this.onEmptyResults();
14117 onLoadException : function()
14119 this.hasQuery = false;
14121 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14122 this.loading.hide();
14125 if(this.tickable && this.editable){
14130 // only causes errors at present
14131 //Roo.log(this.store.reader.jsonData);
14132 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14134 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14140 onTypeAhead : function(){
14141 if(this.store.getCount() > 0){
14142 var r = this.store.getAt(0);
14143 var newValue = r.data[this.displayField];
14144 var len = newValue.length;
14145 var selStart = this.getRawValue().length;
14147 if(selStart != len){
14148 this.setRawValue(newValue);
14149 this.selectText(selStart, newValue.length);
14155 onSelect : function(record, index){
14157 if(this.fireEvent('beforeselect', this, record, index) !== false){
14159 this.setFromData(index > -1 ? record.data : false);
14162 this.fireEvent('select', this, record, index);
14167 * Returns the currently selected field value or empty string if no value is set.
14168 * @return {String} value The selected value
14170 getValue : function()
14172 if(Roo.isIOS && this.useNativeIOS){
14173 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14177 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14180 if(this.valueField){
14181 return typeof this.value != 'undefined' ? this.value : '';
14183 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14187 getRawValue : function()
14189 if(Roo.isIOS && this.useNativeIOS){
14190 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14193 var v = this.inputEl().getValue();
14199 * Clears any text/value currently set in the field
14201 clearValue : function(){
14203 if(this.hiddenField){
14204 this.hiddenField.dom.value = '';
14207 this.setRawValue('');
14208 this.lastSelectionText = '';
14209 this.lastData = false;
14211 var close = this.closeTriggerEl();
14222 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14223 * will be displayed in the field. If the value does not match the data value of an existing item,
14224 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14225 * Otherwise the field will be blank (although the value will still be set).
14226 * @param {String} value The value to match
14228 setValue : function(v)
14230 if(Roo.isIOS && this.useNativeIOS){
14231 this.setIOSValue(v);
14241 if(this.valueField){
14242 var r = this.findRecord(this.valueField, v);
14244 text = r.data[this.displayField];
14245 }else if(this.valueNotFoundText !== undefined){
14246 text = this.valueNotFoundText;
14249 this.lastSelectionText = text;
14250 if(this.hiddenField){
14251 this.hiddenField.dom.value = v;
14253 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14256 var close = this.closeTriggerEl();
14259 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14265 * @property {Object} the last set data for the element
14270 * Sets the value of the field based on a object which is related to the record format for the store.
14271 * @param {Object} value the value to set as. or false on reset?
14273 setFromData : function(o){
14280 var dv = ''; // display value
14281 var vv = ''; // value value..
14283 if (this.displayField) {
14284 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14286 // this is an error condition!!!
14287 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14290 if(this.valueField){
14291 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14294 var close = this.closeTriggerEl();
14297 if(dv.length || vv * 1 > 0){
14299 this.blockFocus=true;
14305 if(this.hiddenField){
14306 this.hiddenField.dom.value = vv;
14308 this.lastSelectionText = dv;
14309 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14313 // no hidden field.. - we store the value in 'value', but still display
14314 // display field!!!!
14315 this.lastSelectionText = dv;
14316 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14323 reset : function(){
14324 // overridden so that last data is reset..
14331 this.setValue(this.originalValue);
14332 //this.clearInvalid();
14333 this.lastData = false;
14335 this.view.clearSelections();
14341 findRecord : function(prop, value){
14343 if(this.store.getCount() > 0){
14344 this.store.each(function(r){
14345 if(r.data[prop] == value){
14355 getName: function()
14357 // returns hidden if it's set..
14358 if (!this.rendered) {return ''};
14359 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14363 onViewMove : function(e, t){
14364 this.inKeyMode = false;
14368 onViewOver : function(e, t){
14369 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14372 var item = this.view.findItemFromChild(t);
14375 var index = this.view.indexOf(item);
14376 this.select(index, false);
14381 onViewClick : function(view, doFocus, el, e)
14383 var index = this.view.getSelectedIndexes()[0];
14385 var r = this.store.getAt(index);
14389 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14396 Roo.each(this.tickItems, function(v,k){
14398 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14400 _this.tickItems.splice(k, 1);
14402 if(typeof(e) == 'undefined' && view == false){
14403 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14415 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14416 this.tickItems.push(r.data);
14419 if(typeof(e) == 'undefined' && view == false){
14420 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14427 this.onSelect(r, index);
14429 if(doFocus !== false && !this.blockFocus){
14430 this.inputEl().focus();
14435 restrictHeight : function(){
14436 //this.innerList.dom.style.height = '';
14437 //var inner = this.innerList.dom;
14438 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14439 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14440 //this.list.beginUpdate();
14441 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14442 this.list.alignTo(this.inputEl(), this.listAlign);
14443 this.list.alignTo(this.inputEl(), this.listAlign);
14444 //this.list.endUpdate();
14448 onEmptyResults : function(){
14450 if(this.tickable && this.editable){
14451 this.hasFocus = false;
14452 this.restrictHeight();
14460 * Returns true if the dropdown list is expanded, else false.
14462 isExpanded : function(){
14463 return this.list.isVisible();
14467 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14468 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14469 * @param {String} value The data value of the item to select
14470 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14471 * selected item if it is not currently in view (defaults to true)
14472 * @return {Boolean} True if the value matched an item in the list, else false
14474 selectByValue : function(v, scrollIntoView){
14475 if(v !== undefined && v !== null){
14476 var r = this.findRecord(this.valueField || this.displayField, v);
14478 this.select(this.store.indexOf(r), scrollIntoView);
14486 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14487 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14488 * @param {Number} index The zero-based index of the list item to select
14489 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14490 * selected item if it is not currently in view (defaults to true)
14492 select : function(index, scrollIntoView){
14493 this.selectedIndex = index;
14494 this.view.select(index);
14495 if(scrollIntoView !== false){
14496 var el = this.view.getNode(index);
14498 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14501 this.list.scrollChildIntoView(el, false);
14507 selectNext : function(){
14508 var ct = this.store.getCount();
14510 if(this.selectedIndex == -1){
14512 }else if(this.selectedIndex < ct-1){
14513 this.select(this.selectedIndex+1);
14519 selectPrev : function(){
14520 var ct = this.store.getCount();
14522 if(this.selectedIndex == -1){
14524 }else if(this.selectedIndex != 0){
14525 this.select(this.selectedIndex-1);
14531 onKeyUp : function(e){
14532 if(this.editable !== false && !e.isSpecialKey()){
14533 this.lastKey = e.getKey();
14534 this.dqTask.delay(this.queryDelay);
14539 validateBlur : function(){
14540 return !this.list || !this.list.isVisible();
14544 initQuery : function(){
14546 var v = this.getRawValue();
14548 if(this.tickable && this.editable){
14549 v = this.tickableInputEl().getValue();
14556 doForce : function(){
14557 if(this.inputEl().dom.value.length > 0){
14558 this.inputEl().dom.value =
14559 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14565 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14566 * query allowing the query action to be canceled if needed.
14567 * @param {String} query The SQL query to execute
14568 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14569 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14570 * saved in the current store (defaults to false)
14572 doQuery : function(q, forceAll){
14574 if(q === undefined || q === null){
14579 forceAll: forceAll,
14583 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14588 forceAll = qe.forceAll;
14589 if(forceAll === true || (q.length >= this.minChars)){
14591 this.hasQuery = true;
14593 if(this.lastQuery != q || this.alwaysQuery){
14594 this.lastQuery = q;
14595 if(this.mode == 'local'){
14596 this.selectedIndex = -1;
14598 this.store.clearFilter();
14601 if(this.specialFilter){
14602 this.fireEvent('specialfilter', this);
14607 this.store.filter(this.displayField, q);
14610 this.store.fireEvent("datachanged", this.store);
14617 this.store.baseParams[this.queryParam] = q;
14619 var options = {params : this.getParams(q)};
14622 options.add = true;
14623 options.params.start = this.page * this.pageSize;
14626 this.store.load(options);
14629 * this code will make the page width larger, at the beginning, the list not align correctly,
14630 * we should expand the list on onLoad
14631 * so command out it
14636 this.selectedIndex = -1;
14641 this.loadNext = false;
14645 getParams : function(q){
14647 //p[this.queryParam] = q;
14651 p.limit = this.pageSize;
14657 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14659 collapse : function(){
14660 if(!this.isExpanded()){
14666 this.hasFocus = false;
14670 this.cancelBtn.hide();
14671 this.trigger.show();
14674 this.tickableInputEl().dom.value = '';
14675 this.tickableInputEl().blur();
14680 Roo.get(document).un('mousedown', this.collapseIf, this);
14681 Roo.get(document).un('mousewheel', this.collapseIf, this);
14682 if (!this.editable) {
14683 Roo.get(document).un('keydown', this.listKeyPress, this);
14685 this.fireEvent('collapse', this);
14691 collapseIf : function(e){
14692 var in_combo = e.within(this.el);
14693 var in_list = e.within(this.list);
14694 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14696 if (in_combo || in_list || is_list) {
14697 //e.stopPropagation();
14702 this.onTickableFooterButtonClick(e, false, false);
14710 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14712 expand : function(){
14714 if(this.isExpanded() || !this.hasFocus){
14718 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14719 this.list.setWidth(lw);
14725 this.restrictHeight();
14729 this.tickItems = Roo.apply([], this.item);
14732 this.cancelBtn.show();
14733 this.trigger.hide();
14736 this.tickableInputEl().focus();
14741 Roo.get(document).on('mousedown', this.collapseIf, this);
14742 Roo.get(document).on('mousewheel', this.collapseIf, this);
14743 if (!this.editable) {
14744 Roo.get(document).on('keydown', this.listKeyPress, this);
14747 this.fireEvent('expand', this);
14751 // Implements the default empty TriggerField.onTriggerClick function
14752 onTriggerClick : function(e)
14754 Roo.log('trigger click');
14756 if(this.disabled || !this.triggerList){
14761 this.loadNext = false;
14763 if(this.isExpanded()){
14765 if (!this.blockFocus) {
14766 this.inputEl().focus();
14770 this.hasFocus = true;
14771 if(this.triggerAction == 'all') {
14772 this.doQuery(this.allQuery, true);
14774 this.doQuery(this.getRawValue());
14776 if (!this.blockFocus) {
14777 this.inputEl().focus();
14782 onTickableTriggerClick : function(e)
14789 this.loadNext = false;
14790 this.hasFocus = true;
14792 if(this.triggerAction == 'all') {
14793 this.doQuery(this.allQuery, true);
14795 this.doQuery(this.getRawValue());
14799 onSearchFieldClick : function(e)
14801 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14802 this.onTickableFooterButtonClick(e, false, false);
14806 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14811 this.loadNext = false;
14812 this.hasFocus = true;
14814 if(this.triggerAction == 'all') {
14815 this.doQuery(this.allQuery, true);
14817 this.doQuery(this.getRawValue());
14821 listKeyPress : function(e)
14823 //Roo.log('listkeypress');
14824 // scroll to first matching element based on key pres..
14825 if (e.isSpecialKey()) {
14828 var k = String.fromCharCode(e.getKey()).toUpperCase();
14831 var csel = this.view.getSelectedNodes();
14832 var cselitem = false;
14834 var ix = this.view.indexOf(csel[0]);
14835 cselitem = this.store.getAt(ix);
14836 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14842 this.store.each(function(v) {
14844 // start at existing selection.
14845 if (cselitem.id == v.id) {
14851 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14852 match = this.store.indexOf(v);
14858 if (match === false) {
14859 return true; // no more action?
14862 this.view.select(match);
14863 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14864 sn.scrollIntoView(sn.dom.parentNode, false);
14867 onViewScroll : function(e, t){
14869 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){
14873 this.hasQuery = true;
14875 this.loading = this.list.select('.loading', true).first();
14877 if(this.loading === null){
14878 this.list.createChild({
14880 cls: 'loading roo-select2-more-results roo-select2-active',
14881 html: 'Loading more results...'
14884 this.loading = this.list.select('.loading', true).first();
14886 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14888 this.loading.hide();
14891 this.loading.show();
14896 this.loadNext = true;
14898 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14903 addItem : function(o)
14905 var dv = ''; // display value
14907 if (this.displayField) {
14908 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14910 // this is an error condition!!!
14911 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14918 var choice = this.choices.createChild({
14920 cls: 'roo-select2-search-choice',
14929 cls: 'roo-select2-search-choice-close fa fa-times',
14934 }, this.searchField);
14936 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14938 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14946 this.inputEl().dom.value = '';
14951 onRemoveItem : function(e, _self, o)
14953 e.preventDefault();
14955 this.lastItem = Roo.apply([], this.item);
14957 var index = this.item.indexOf(o.data) * 1;
14960 Roo.log('not this item?!');
14964 this.item.splice(index, 1);
14969 this.fireEvent('remove', this, e);
14975 syncValue : function()
14977 if(!this.item.length){
14984 Roo.each(this.item, function(i){
14985 if(_this.valueField){
14986 value.push(i[_this.valueField]);
14993 this.value = value.join(',');
14995 if(this.hiddenField){
14996 this.hiddenField.dom.value = this.value;
14999 this.store.fireEvent("datachanged", this.store);
15004 clearItem : function()
15006 if(!this.multiple){
15012 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15020 if(this.tickable && !Roo.isTouch){
15021 this.view.refresh();
15025 inputEl: function ()
15027 if(Roo.isIOS && this.useNativeIOS){
15028 return this.el.select('select.roo-ios-select', true).first();
15031 if(Roo.isTouch && this.mobileTouchView){
15032 return this.el.select('input.form-control',true).first();
15036 return this.searchField;
15039 return this.el.select('input.form-control',true).first();
15042 onTickableFooterButtonClick : function(e, btn, el)
15044 e.preventDefault();
15046 this.lastItem = Roo.apply([], this.item);
15048 if(btn && btn.name == 'cancel'){
15049 this.tickItems = Roo.apply([], this.item);
15058 Roo.each(this.tickItems, function(o){
15066 validate : function()
15068 if(this.getVisibilityEl().hasClass('hidden')){
15072 var v = this.getRawValue();
15075 v = this.getValue();
15078 if(this.disabled || this.allowBlank || v.length){
15083 this.markInvalid();
15087 tickableInputEl : function()
15089 if(!this.tickable || !this.editable){
15090 return this.inputEl();
15093 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15097 getAutoCreateTouchView : function()
15102 cls: 'form-group' //input-group
15108 type : this.inputType,
15109 cls : 'form-control x-combo-noedit',
15110 autocomplete: 'new-password',
15111 placeholder : this.placeholder || '',
15116 input.name = this.name;
15120 input.cls += ' input-' + this.size;
15123 if (this.disabled) {
15124 input.disabled = true;
15135 inputblock.cls += ' input-group';
15137 inputblock.cn.unshift({
15139 cls : 'input-group-addon input-group-prepend input-group-text',
15144 if(this.removable && !this.multiple){
15145 inputblock.cls += ' roo-removable';
15147 inputblock.cn.push({
15150 cls : 'roo-combo-removable-btn close'
15154 if(this.hasFeedback && !this.allowBlank){
15156 inputblock.cls += ' has-feedback';
15158 inputblock.cn.push({
15160 cls: 'glyphicon form-control-feedback'
15167 inputblock.cls += (this.before) ? '' : ' input-group';
15169 inputblock.cn.push({
15171 cls : 'input-group-addon input-group-append input-group-text',
15177 var ibwrap = inputblock;
15182 cls: 'roo-select2-choices',
15186 cls: 'roo-select2-search-field',
15199 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15204 cls: 'form-hidden-field'
15210 if(!this.multiple && this.showToggleBtn){
15217 if (this.caret != false) {
15220 cls: 'fa fa-' + this.caret
15227 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15232 cls: 'combobox-clear',
15246 combobox.cls += ' roo-select2-container-multi';
15249 var align = this.labelAlign || this.parentLabelAlign();
15251 if (align ==='left' && this.fieldLabel.length) {
15256 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15257 tooltip : 'This field is required'
15261 cls : 'control-label col-form-label',
15262 html : this.fieldLabel
15273 var labelCfg = cfg.cn[1];
15274 var contentCfg = cfg.cn[2];
15277 if(this.indicatorpos == 'right'){
15282 cls : 'control-label col-form-label',
15286 html : this.fieldLabel
15290 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15291 tooltip : 'This field is required'
15304 labelCfg = cfg.cn[0];
15305 contentCfg = cfg.cn[1];
15310 if(this.labelWidth > 12){
15311 labelCfg.style = "width: " + this.labelWidth + 'px';
15314 if(this.labelWidth < 13 && this.labelmd == 0){
15315 this.labelmd = this.labelWidth;
15318 if(this.labellg > 0){
15319 labelCfg.cls += ' col-lg-' + this.labellg;
15320 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15323 if(this.labelmd > 0){
15324 labelCfg.cls += ' col-md-' + this.labelmd;
15325 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15328 if(this.labelsm > 0){
15329 labelCfg.cls += ' col-sm-' + this.labelsm;
15330 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15333 if(this.labelxs > 0){
15334 labelCfg.cls += ' col-xs-' + this.labelxs;
15335 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15339 } else if ( this.fieldLabel.length) {
15343 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15344 tooltip : 'This field is required'
15348 cls : 'control-label',
15349 html : this.fieldLabel
15360 if(this.indicatorpos == 'right'){
15364 cls : 'control-label',
15365 html : this.fieldLabel,
15369 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15370 tooltip : 'This field is required'
15387 var settings = this;
15389 ['xs','sm','md','lg'].map(function(size){
15390 if (settings[size]) {
15391 cfg.cls += ' col-' + size + '-' + settings[size];
15398 initTouchView : function()
15400 this.renderTouchView();
15402 this.touchViewEl.on('scroll', function(){
15403 this.el.dom.scrollTop = 0;
15406 this.originalValue = this.getValue();
15408 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15410 this.inputEl().on("click", this.showTouchView, this);
15411 if (this.triggerEl) {
15412 this.triggerEl.on("click", this.showTouchView, this);
15416 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15417 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15419 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15421 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15422 this.store.on('load', this.onTouchViewLoad, this);
15423 this.store.on('loadexception', this.onTouchViewLoadException, this);
15425 if(this.hiddenName){
15427 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15429 this.hiddenField.dom.value =
15430 this.hiddenValue !== undefined ? this.hiddenValue :
15431 this.value !== undefined ? this.value : '';
15433 this.el.dom.removeAttribute('name');
15434 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15438 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15439 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15442 if(this.removable && !this.multiple){
15443 var close = this.closeTriggerEl();
15445 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15446 close.on('click', this.removeBtnClick, this, close);
15450 * fix the bug in Safari iOS8
15452 this.inputEl().on("focus", function(e){
15453 document.activeElement.blur();
15456 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15463 renderTouchView : function()
15465 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15466 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15468 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15469 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15471 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15472 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15473 this.touchViewBodyEl.setStyle('overflow', 'auto');
15475 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15476 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15478 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15479 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15483 showTouchView : function()
15489 this.touchViewHeaderEl.hide();
15491 if(this.modalTitle.length){
15492 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15493 this.touchViewHeaderEl.show();
15496 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15497 this.touchViewEl.show();
15499 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15501 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15502 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15504 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15506 if(this.modalTitle.length){
15507 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15510 this.touchViewBodyEl.setHeight(bodyHeight);
15514 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15516 this.touchViewEl.addClass('in');
15519 if(this._touchViewMask){
15520 Roo.get(document.body).addClass("x-body-masked");
15521 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15522 this._touchViewMask.setStyle('z-index', 10000);
15523 this._touchViewMask.addClass('show');
15526 this.doTouchViewQuery();
15530 hideTouchView : function()
15532 this.touchViewEl.removeClass('in');
15536 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15538 this.touchViewEl.setStyle('display', 'none');
15541 if(this._touchViewMask){
15542 this._touchViewMask.removeClass('show');
15543 Roo.get(document.body).removeClass("x-body-masked");
15547 setTouchViewValue : function()
15554 Roo.each(this.tickItems, function(o){
15559 this.hideTouchView();
15562 doTouchViewQuery : function()
15571 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15575 if(!this.alwaysQuery || this.mode == 'local'){
15576 this.onTouchViewLoad();
15583 onTouchViewBeforeLoad : function(combo,opts)
15589 onTouchViewLoad : function()
15591 if(this.store.getCount() < 1){
15592 this.onTouchViewEmptyResults();
15596 this.clearTouchView();
15598 var rawValue = this.getRawValue();
15600 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15602 this.tickItems = [];
15604 this.store.data.each(function(d, rowIndex){
15605 var row = this.touchViewListGroup.createChild(template);
15607 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15608 row.addClass(d.data.cls);
15611 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15614 html : d.data[this.displayField]
15617 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15618 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15621 row.removeClass('selected');
15622 if(!this.multiple && this.valueField &&
15623 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15626 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15627 row.addClass('selected');
15630 if(this.multiple && this.valueField &&
15631 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15635 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15636 this.tickItems.push(d.data);
15639 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15643 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15645 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15647 if(this.modalTitle.length){
15648 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15651 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15653 if(this.mobile_restrict_height && listHeight < bodyHeight){
15654 this.touchViewBodyEl.setHeight(listHeight);
15659 if(firstChecked && listHeight > bodyHeight){
15660 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15665 onTouchViewLoadException : function()
15667 this.hideTouchView();
15670 onTouchViewEmptyResults : function()
15672 this.clearTouchView();
15674 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15676 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15680 clearTouchView : function()
15682 this.touchViewListGroup.dom.innerHTML = '';
15685 onTouchViewClick : function(e, el, o)
15687 e.preventDefault();
15690 var rowIndex = o.rowIndex;
15692 var r = this.store.getAt(rowIndex);
15694 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15696 if(!this.multiple){
15697 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15698 c.dom.removeAttribute('checked');
15701 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15703 this.setFromData(r.data);
15705 var close = this.closeTriggerEl();
15711 this.hideTouchView();
15713 this.fireEvent('select', this, r, rowIndex);
15718 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15719 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15720 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15724 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15725 this.addItem(r.data);
15726 this.tickItems.push(r.data);
15730 getAutoCreateNativeIOS : function()
15733 cls: 'form-group' //input-group,
15738 cls : 'roo-ios-select'
15742 combobox.name = this.name;
15745 if (this.disabled) {
15746 combobox.disabled = true;
15749 var settings = this;
15751 ['xs','sm','md','lg'].map(function(size){
15752 if (settings[size]) {
15753 cfg.cls += ' col-' + size + '-' + settings[size];
15763 initIOSView : function()
15765 this.store.on('load', this.onIOSViewLoad, this);
15770 onIOSViewLoad : function()
15772 if(this.store.getCount() < 1){
15776 this.clearIOSView();
15778 if(this.allowBlank) {
15780 var default_text = '-- SELECT --';
15782 if(this.placeholder.length){
15783 default_text = this.placeholder;
15786 if(this.emptyTitle.length){
15787 default_text += ' - ' + this.emptyTitle + ' -';
15790 var opt = this.inputEl().createChild({
15793 html : default_text
15797 o[this.valueField] = 0;
15798 o[this.displayField] = default_text;
15800 this.ios_options.push({
15807 this.store.data.each(function(d, rowIndex){
15811 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15812 html = d.data[this.displayField];
15817 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15818 value = d.data[this.valueField];
15827 if(this.value == d.data[this.valueField]){
15828 option['selected'] = true;
15831 var opt = this.inputEl().createChild(option);
15833 this.ios_options.push({
15840 this.inputEl().on('change', function(){
15841 this.fireEvent('select', this);
15846 clearIOSView: function()
15848 this.inputEl().dom.innerHTML = '';
15850 this.ios_options = [];
15853 setIOSValue: function(v)
15857 if(!this.ios_options){
15861 Roo.each(this.ios_options, function(opts){
15863 opts.el.dom.removeAttribute('selected');
15865 if(opts.data[this.valueField] != v){
15869 opts.el.dom.setAttribute('selected', true);
15875 * @cfg {Boolean} grow
15879 * @cfg {Number} growMin
15883 * @cfg {Number} growMax
15892 Roo.apply(Roo.bootstrap.ComboBox, {
15896 cls: 'modal-header',
15918 cls: 'list-group-item',
15922 cls: 'roo-combobox-list-group-item-value'
15926 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15940 listItemCheckbox : {
15942 cls: 'list-group-item',
15946 cls: 'roo-combobox-list-group-item-value'
15950 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15966 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15971 cls: 'modal-footer',
15979 cls: 'col-xs-6 text-left',
15982 cls: 'btn btn-danger roo-touch-view-cancel',
15988 cls: 'col-xs-6 text-right',
15991 cls: 'btn btn-success roo-touch-view-ok',
16002 Roo.apply(Roo.bootstrap.ComboBox, {
16004 touchViewTemplate : {
16006 cls: 'modal fade roo-combobox-touch-view',
16010 cls: 'modal-dialog',
16011 style : 'position:fixed', // we have to fix position....
16015 cls: 'modal-content',
16017 Roo.bootstrap.ComboBox.header,
16018 Roo.bootstrap.ComboBox.body,
16019 Roo.bootstrap.ComboBox.footer
16028 * Ext JS Library 1.1.1
16029 * Copyright(c) 2006-2007, Ext JS, LLC.
16031 * Originally Released Under LGPL - original licence link has changed is not relivant.
16034 * <script type="text/javascript">
16039 * @extends Roo.util.Observable
16040 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
16041 * This class also supports single and multi selection modes. <br>
16042 * Create a data model bound view:
16044 var store = new Roo.data.Store(...);
16046 var view = new Roo.View({
16048 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
16050 singleSelect: true,
16051 selectedClass: "ydataview-selected",
16055 // listen for node click?
16056 view.on("click", function(vw, index, node, e){
16057 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16061 dataModel.load("foobar.xml");
16063 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16065 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16066 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16068 * Note: old style constructor is still suported (container, template, config)
16071 * Create a new View
16072 * @param {Object} config The config object
16075 Roo.View = function(config, depreciated_tpl, depreciated_config){
16077 this.parent = false;
16079 if (typeof(depreciated_tpl) == 'undefined') {
16080 // new way.. - universal constructor.
16081 Roo.apply(this, config);
16082 this.el = Roo.get(this.el);
16085 this.el = Roo.get(config);
16086 this.tpl = depreciated_tpl;
16087 Roo.apply(this, depreciated_config);
16089 this.wrapEl = this.el.wrap().wrap();
16090 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16093 if(typeof(this.tpl) == "string"){
16094 this.tpl = new Roo.Template(this.tpl);
16096 // support xtype ctors..
16097 this.tpl = new Roo.factory(this.tpl, Roo);
16101 this.tpl.compile();
16106 * @event beforeclick
16107 * Fires before a click is processed. Returns false to cancel the default action.
16108 * @param {Roo.View} this
16109 * @param {Number} index The index of the target node
16110 * @param {HTMLElement} node The target node
16111 * @param {Roo.EventObject} e The raw event object
16113 "beforeclick" : true,
16116 * Fires when a template node is clicked.
16117 * @param {Roo.View} this
16118 * @param {Number} index The index of the target node
16119 * @param {HTMLElement} node The target node
16120 * @param {Roo.EventObject} e The raw event object
16125 * Fires when a template node is double clicked.
16126 * @param {Roo.View} this
16127 * @param {Number} index The index of the target node
16128 * @param {HTMLElement} node The target node
16129 * @param {Roo.EventObject} e The raw event object
16133 * @event contextmenu
16134 * Fires when a template node is right clicked.
16135 * @param {Roo.View} this
16136 * @param {Number} index The index of the target node
16137 * @param {HTMLElement} node The target node
16138 * @param {Roo.EventObject} e The raw event object
16140 "contextmenu" : true,
16142 * @event selectionchange
16143 * Fires when the selected nodes change.
16144 * @param {Roo.View} this
16145 * @param {Array} selections Array of the selected nodes
16147 "selectionchange" : true,
16150 * @event beforeselect
16151 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16152 * @param {Roo.View} this
16153 * @param {HTMLElement} node The node to be selected
16154 * @param {Array} selections Array of currently selected nodes
16156 "beforeselect" : true,
16158 * @event preparedata
16159 * Fires on every row to render, to allow you to change the data.
16160 * @param {Roo.View} this
16161 * @param {Object} data to be rendered (change this)
16163 "preparedata" : true
16171 "click": this.onClick,
16172 "dblclick": this.onDblClick,
16173 "contextmenu": this.onContextMenu,
16177 this.selections = [];
16179 this.cmp = new Roo.CompositeElementLite([]);
16181 this.store = Roo.factory(this.store, Roo.data);
16182 this.setStore(this.store, true);
16185 if ( this.footer && this.footer.xtype) {
16187 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16189 this.footer.dataSource = this.store;
16190 this.footer.container = fctr;
16191 this.footer = Roo.factory(this.footer, Roo);
16192 fctr.insertFirst(this.el);
16194 // this is a bit insane - as the paging toolbar seems to detach the el..
16195 // dom.parentNode.parentNode.parentNode
16196 // they get detached?
16200 Roo.View.superclass.constructor.call(this);
16205 Roo.extend(Roo.View, Roo.util.Observable, {
16208 * @cfg {Roo.data.Store} store Data store to load data from.
16213 * @cfg {String|Roo.Element} el The container element.
16218 * @cfg {String|Roo.Template} tpl The template used by this View
16222 * @cfg {String} dataName the named area of the template to use as the data area
16223 * Works with domtemplates roo-name="name"
16227 * @cfg {String} selectedClass The css class to add to selected nodes
16229 selectedClass : "x-view-selected",
16231 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16236 * @cfg {String} text to display on mask (default Loading)
16240 * @cfg {Boolean} multiSelect Allow multiple selection
16242 multiSelect : false,
16244 * @cfg {Boolean} singleSelect Allow single selection
16246 singleSelect: false,
16249 * @cfg {Boolean} toggleSelect - selecting
16251 toggleSelect : false,
16254 * @cfg {Boolean} tickable - selecting
16259 * Returns the element this view is bound to.
16260 * @return {Roo.Element}
16262 getEl : function(){
16263 return this.wrapEl;
16269 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16271 refresh : function(){
16272 //Roo.log('refresh');
16275 // if we are using something like 'domtemplate', then
16276 // the what gets used is:
16277 // t.applySubtemplate(NAME, data, wrapping data..)
16278 // the outer template then get' applied with
16279 // the store 'extra data'
16280 // and the body get's added to the
16281 // roo-name="data" node?
16282 // <span class='roo-tpl-{name}'></span> ?????
16286 this.clearSelections();
16287 this.el.update("");
16289 var records = this.store.getRange();
16290 if(records.length < 1) {
16292 // is this valid?? = should it render a template??
16294 this.el.update(this.emptyText);
16298 if (this.dataName) {
16299 this.el.update(t.apply(this.store.meta)); //????
16300 el = this.el.child('.roo-tpl-' + this.dataName);
16303 for(var i = 0, len = records.length; i < len; i++){
16304 var data = this.prepareData(records[i].data, i, records[i]);
16305 this.fireEvent("preparedata", this, data, i, records[i]);
16307 var d = Roo.apply({}, data);
16310 Roo.apply(d, {'roo-id' : Roo.id()});
16314 Roo.each(this.parent.item, function(item){
16315 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16318 Roo.apply(d, {'roo-data-checked' : 'checked'});
16322 html[html.length] = Roo.util.Format.trim(
16324 t.applySubtemplate(this.dataName, d, this.store.meta) :
16331 el.update(html.join(""));
16332 this.nodes = el.dom.childNodes;
16333 this.updateIndexes(0);
16338 * Function to override to reformat the data that is sent to
16339 * the template for each node.
16340 * DEPRICATED - use the preparedata event handler.
16341 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16342 * a JSON object for an UpdateManager bound view).
16344 prepareData : function(data, index, record)
16346 this.fireEvent("preparedata", this, data, index, record);
16350 onUpdate : function(ds, record){
16351 // Roo.log('on update');
16352 this.clearSelections();
16353 var index = this.store.indexOf(record);
16354 var n = this.nodes[index];
16355 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16356 n.parentNode.removeChild(n);
16357 this.updateIndexes(index, index);
16363 onAdd : function(ds, records, index)
16365 //Roo.log(['on Add', ds, records, index] );
16366 this.clearSelections();
16367 if(this.nodes.length == 0){
16371 var n = this.nodes[index];
16372 for(var i = 0, len = records.length; i < len; i++){
16373 var d = this.prepareData(records[i].data, i, records[i]);
16375 this.tpl.insertBefore(n, d);
16378 this.tpl.append(this.el, d);
16381 this.updateIndexes(index);
16384 onRemove : function(ds, record, index){
16385 // Roo.log('onRemove');
16386 this.clearSelections();
16387 var el = this.dataName ?
16388 this.el.child('.roo-tpl-' + this.dataName) :
16391 el.dom.removeChild(this.nodes[index]);
16392 this.updateIndexes(index);
16396 * Refresh an individual node.
16397 * @param {Number} index
16399 refreshNode : function(index){
16400 this.onUpdate(this.store, this.store.getAt(index));
16403 updateIndexes : function(startIndex, endIndex){
16404 var ns = this.nodes;
16405 startIndex = startIndex || 0;
16406 endIndex = endIndex || ns.length - 1;
16407 for(var i = startIndex; i <= endIndex; i++){
16408 ns[i].nodeIndex = i;
16413 * Changes the data store this view uses and refresh the view.
16414 * @param {Store} store
16416 setStore : function(store, initial){
16417 if(!initial && this.store){
16418 this.store.un("datachanged", this.refresh);
16419 this.store.un("add", this.onAdd);
16420 this.store.un("remove", this.onRemove);
16421 this.store.un("update", this.onUpdate);
16422 this.store.un("clear", this.refresh);
16423 this.store.un("beforeload", this.onBeforeLoad);
16424 this.store.un("load", this.onLoad);
16425 this.store.un("loadexception", this.onLoad);
16429 store.on("datachanged", this.refresh, this);
16430 store.on("add", this.onAdd, this);
16431 store.on("remove", this.onRemove, this);
16432 store.on("update", this.onUpdate, this);
16433 store.on("clear", this.refresh, this);
16434 store.on("beforeload", this.onBeforeLoad, this);
16435 store.on("load", this.onLoad, this);
16436 store.on("loadexception", this.onLoad, this);
16444 * onbeforeLoad - masks the loading area.
16447 onBeforeLoad : function(store,opts)
16449 //Roo.log('onBeforeLoad');
16451 this.el.update("");
16453 this.el.mask(this.mask ? this.mask : "Loading" );
16455 onLoad : function ()
16462 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16463 * @param {HTMLElement} node
16464 * @return {HTMLElement} The template node
16466 findItemFromChild : function(node){
16467 var el = this.dataName ?
16468 this.el.child('.roo-tpl-' + this.dataName,true) :
16471 if(!node || node.parentNode == el){
16474 var p = node.parentNode;
16475 while(p && p != el){
16476 if(p.parentNode == el){
16485 onClick : function(e){
16486 var item = this.findItemFromChild(e.getTarget());
16488 var index = this.indexOf(item);
16489 if(this.onItemClick(item, index, e) !== false){
16490 this.fireEvent("click", this, index, item, e);
16493 this.clearSelections();
16498 onContextMenu : function(e){
16499 var item = this.findItemFromChild(e.getTarget());
16501 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16506 onDblClick : function(e){
16507 var item = this.findItemFromChild(e.getTarget());
16509 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16513 onItemClick : function(item, index, e)
16515 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16518 if (this.toggleSelect) {
16519 var m = this.isSelected(item) ? 'unselect' : 'select';
16522 _t[m](item, true, false);
16525 if(this.multiSelect || this.singleSelect){
16526 if(this.multiSelect && e.shiftKey && this.lastSelection){
16527 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16529 this.select(item, this.multiSelect && e.ctrlKey);
16530 this.lastSelection = item;
16533 if(!this.tickable){
16534 e.preventDefault();
16542 * Get the number of selected nodes.
16545 getSelectionCount : function(){
16546 return this.selections.length;
16550 * Get the currently selected nodes.
16551 * @return {Array} An array of HTMLElements
16553 getSelectedNodes : function(){
16554 return this.selections;
16558 * Get the indexes of the selected nodes.
16561 getSelectedIndexes : function(){
16562 var indexes = [], s = this.selections;
16563 for(var i = 0, len = s.length; i < len; i++){
16564 indexes.push(s[i].nodeIndex);
16570 * Clear all selections
16571 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16573 clearSelections : function(suppressEvent){
16574 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16575 this.cmp.elements = this.selections;
16576 this.cmp.removeClass(this.selectedClass);
16577 this.selections = [];
16578 if(!suppressEvent){
16579 this.fireEvent("selectionchange", this, this.selections);
16585 * Returns true if the passed node is selected
16586 * @param {HTMLElement/Number} node The node or node index
16587 * @return {Boolean}
16589 isSelected : function(node){
16590 var s = this.selections;
16594 node = this.getNode(node);
16595 return s.indexOf(node) !== -1;
16600 * @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
16601 * @param {Boolean} keepExisting (optional) true to keep existing selections
16602 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16604 select : function(nodeInfo, keepExisting, suppressEvent){
16605 if(nodeInfo instanceof Array){
16607 this.clearSelections(true);
16609 for(var i = 0, len = nodeInfo.length; i < len; i++){
16610 this.select(nodeInfo[i], true, true);
16614 var node = this.getNode(nodeInfo);
16615 if(!node || this.isSelected(node)){
16616 return; // already selected.
16619 this.clearSelections(true);
16622 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16623 Roo.fly(node).addClass(this.selectedClass);
16624 this.selections.push(node);
16625 if(!suppressEvent){
16626 this.fireEvent("selectionchange", this, this.selections);
16634 * @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
16635 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16636 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16638 unselect : function(nodeInfo, keepExisting, suppressEvent)
16640 if(nodeInfo instanceof Array){
16641 Roo.each(this.selections, function(s) {
16642 this.unselect(s, nodeInfo);
16646 var node = this.getNode(nodeInfo);
16647 if(!node || !this.isSelected(node)){
16648 //Roo.log("not selected");
16649 return; // not selected.
16653 Roo.each(this.selections, function(s) {
16655 Roo.fly(node).removeClass(this.selectedClass);
16662 this.selections= ns;
16663 this.fireEvent("selectionchange", this, this.selections);
16667 * Gets a template node.
16668 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16669 * @return {HTMLElement} The node or null if it wasn't found
16671 getNode : function(nodeInfo){
16672 if(typeof nodeInfo == "string"){
16673 return document.getElementById(nodeInfo);
16674 }else if(typeof nodeInfo == "number"){
16675 return this.nodes[nodeInfo];
16681 * Gets a range template nodes.
16682 * @param {Number} startIndex
16683 * @param {Number} endIndex
16684 * @return {Array} An array of nodes
16686 getNodes : function(start, end){
16687 var ns = this.nodes;
16688 start = start || 0;
16689 end = typeof end == "undefined" ? ns.length - 1 : end;
16692 for(var i = start; i <= end; i++){
16696 for(var i = start; i >= end; i--){
16704 * Finds the index of the passed node
16705 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16706 * @return {Number} The index of the node or -1
16708 indexOf : function(node){
16709 node = this.getNode(node);
16710 if(typeof node.nodeIndex == "number"){
16711 return node.nodeIndex;
16713 var ns = this.nodes;
16714 for(var i = 0, len = ns.length; i < len; i++){
16725 * based on jquery fullcalendar
16729 Roo.bootstrap = Roo.bootstrap || {};
16731 * @class Roo.bootstrap.Calendar
16732 * @extends Roo.bootstrap.Component
16733 * Bootstrap Calendar class
16734 * @cfg {Boolean} loadMask (true|false) default false
16735 * @cfg {Object} header generate the user specific header of the calendar, default false
16738 * Create a new Container
16739 * @param {Object} config The config object
16744 Roo.bootstrap.Calendar = function(config){
16745 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16749 * Fires when a date is selected
16750 * @param {DatePicker} this
16751 * @param {Date} date The selected date
16755 * @event monthchange
16756 * Fires when the displayed month changes
16757 * @param {DatePicker} this
16758 * @param {Date} date The selected month
16760 'monthchange': true,
16762 * @event evententer
16763 * Fires when mouse over an event
16764 * @param {Calendar} this
16765 * @param {event} Event
16767 'evententer': true,
16769 * @event eventleave
16770 * Fires when the mouse leaves an
16771 * @param {Calendar} this
16774 'eventleave': true,
16776 * @event eventclick
16777 * Fires when the mouse click an
16778 * @param {Calendar} this
16787 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16790 * @cfg {Number} startDay
16791 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16799 getAutoCreate : function(){
16802 var fc_button = function(name, corner, style, content ) {
16803 return Roo.apply({},{
16805 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16807 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16810 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16821 style : 'width:100%',
16828 cls : 'fc-header-left',
16830 fc_button('prev', 'left', 'arrow', '‹' ),
16831 fc_button('next', 'right', 'arrow', '›' ),
16832 { tag: 'span', cls: 'fc-header-space' },
16833 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16841 cls : 'fc-header-center',
16845 cls: 'fc-header-title',
16848 html : 'month / year'
16856 cls : 'fc-header-right',
16858 /* fc_button('month', 'left', '', 'month' ),
16859 fc_button('week', '', '', 'week' ),
16860 fc_button('day', 'right', '', 'day' )
16872 header = this.header;
16875 var cal_heads = function() {
16877 // fixme - handle this.
16879 for (var i =0; i < Date.dayNames.length; i++) {
16880 var d = Date.dayNames[i];
16883 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16884 html : d.substring(0,3)
16888 ret[0].cls += ' fc-first';
16889 ret[6].cls += ' fc-last';
16892 var cal_cell = function(n) {
16895 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16900 cls: 'fc-day-number',
16904 cls: 'fc-day-content',
16908 style: 'position: relative;' // height: 17px;
16920 var cal_rows = function() {
16923 for (var r = 0; r < 6; r++) {
16930 for (var i =0; i < Date.dayNames.length; i++) {
16931 var d = Date.dayNames[i];
16932 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16935 row.cn[0].cls+=' fc-first';
16936 row.cn[0].cn[0].style = 'min-height:90px';
16937 row.cn[6].cls+=' fc-last';
16941 ret[0].cls += ' fc-first';
16942 ret[4].cls += ' fc-prev-last';
16943 ret[5].cls += ' fc-last';
16950 cls: 'fc-border-separate',
16951 style : 'width:100%',
16959 cls : 'fc-first fc-last',
16977 cls : 'fc-content',
16978 style : "position: relative;",
16981 cls : 'fc-view fc-view-month fc-grid',
16982 style : 'position: relative',
16983 unselectable : 'on',
16986 cls : 'fc-event-container',
16987 style : 'position:absolute;z-index:8;top:0;left:0;'
17005 initEvents : function()
17008 throw "can not find store for calendar";
17014 style: "text-align:center",
17018 style: "background-color:white;width:50%;margin:250 auto",
17022 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
17033 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17035 var size = this.el.select('.fc-content', true).first().getSize();
17036 this.maskEl.setSize(size.width, size.height);
17037 this.maskEl.enableDisplayMode("block");
17038 if(!this.loadMask){
17039 this.maskEl.hide();
17042 this.store = Roo.factory(this.store, Roo.data);
17043 this.store.on('load', this.onLoad, this);
17044 this.store.on('beforeload', this.onBeforeLoad, this);
17048 this.cells = this.el.select('.fc-day',true);
17049 //Roo.log(this.cells);
17050 this.textNodes = this.el.query('.fc-day-number');
17051 this.cells.addClassOnOver('fc-state-hover');
17053 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17054 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17055 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17056 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17058 this.on('monthchange', this.onMonthChange, this);
17060 this.update(new Date().clearTime());
17063 resize : function() {
17064 var sz = this.el.getSize();
17066 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17067 this.el.select('.fc-day-content div',true).setHeight(34);
17072 showPrevMonth : function(e){
17073 this.update(this.activeDate.add("mo", -1));
17075 showToday : function(e){
17076 this.update(new Date().clearTime());
17079 showNextMonth : function(e){
17080 this.update(this.activeDate.add("mo", 1));
17084 showPrevYear : function(){
17085 this.update(this.activeDate.add("y", -1));
17089 showNextYear : function(){
17090 this.update(this.activeDate.add("y", 1));
17095 update : function(date)
17097 var vd = this.activeDate;
17098 this.activeDate = date;
17099 // if(vd && this.el){
17100 // var t = date.getTime();
17101 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17102 // Roo.log('using add remove');
17104 // this.fireEvent('monthchange', this, date);
17106 // this.cells.removeClass("fc-state-highlight");
17107 // this.cells.each(function(c){
17108 // if(c.dateValue == t){
17109 // c.addClass("fc-state-highlight");
17110 // setTimeout(function(){
17111 // try{c.dom.firstChild.focus();}catch(e){}
17121 var days = date.getDaysInMonth();
17123 var firstOfMonth = date.getFirstDateOfMonth();
17124 var startingPos = firstOfMonth.getDay()-this.startDay;
17126 if(startingPos < this.startDay){
17130 var pm = date.add(Date.MONTH, -1);
17131 var prevStart = pm.getDaysInMonth()-startingPos;
17133 this.cells = this.el.select('.fc-day',true);
17134 this.textNodes = this.el.query('.fc-day-number');
17135 this.cells.addClassOnOver('fc-state-hover');
17137 var cells = this.cells.elements;
17138 var textEls = this.textNodes;
17140 Roo.each(cells, function(cell){
17141 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17144 days += startingPos;
17146 // convert everything to numbers so it's fast
17147 var day = 86400000;
17148 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17151 //Roo.log(prevStart);
17153 var today = new Date().clearTime().getTime();
17154 var sel = date.clearTime().getTime();
17155 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17156 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17157 var ddMatch = this.disabledDatesRE;
17158 var ddText = this.disabledDatesText;
17159 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17160 var ddaysText = this.disabledDaysText;
17161 var format = this.format;
17163 var setCellClass = function(cal, cell){
17167 //Roo.log('set Cell Class');
17169 var t = d.getTime();
17173 cell.dateValue = t;
17175 cell.className += " fc-today";
17176 cell.className += " fc-state-highlight";
17177 cell.title = cal.todayText;
17180 // disable highlight in other month..
17181 //cell.className += " fc-state-highlight";
17186 cell.className = " fc-state-disabled";
17187 cell.title = cal.minText;
17191 cell.className = " fc-state-disabled";
17192 cell.title = cal.maxText;
17196 if(ddays.indexOf(d.getDay()) != -1){
17197 cell.title = ddaysText;
17198 cell.className = " fc-state-disabled";
17201 if(ddMatch && format){
17202 var fvalue = d.dateFormat(format);
17203 if(ddMatch.test(fvalue)){
17204 cell.title = ddText.replace("%0", fvalue);
17205 cell.className = " fc-state-disabled";
17209 if (!cell.initialClassName) {
17210 cell.initialClassName = cell.dom.className;
17213 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17218 for(; i < startingPos; i++) {
17219 textEls[i].innerHTML = (++prevStart);
17220 d.setDate(d.getDate()+1);
17222 cells[i].className = "fc-past fc-other-month";
17223 setCellClass(this, cells[i]);
17228 for(; i < days; i++){
17229 intDay = i - startingPos + 1;
17230 textEls[i].innerHTML = (intDay);
17231 d.setDate(d.getDate()+1);
17233 cells[i].className = ''; // "x-date-active";
17234 setCellClass(this, cells[i]);
17238 for(; i < 42; i++) {
17239 textEls[i].innerHTML = (++extraDays);
17240 d.setDate(d.getDate()+1);
17242 cells[i].className = "fc-future fc-other-month";
17243 setCellClass(this, cells[i]);
17246 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17248 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17250 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17251 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17253 if(totalRows != 6){
17254 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17255 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17258 this.fireEvent('monthchange', this, date);
17262 if(!this.internalRender){
17263 var main = this.el.dom.firstChild;
17264 var w = main.offsetWidth;
17265 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17266 Roo.fly(main).setWidth(w);
17267 this.internalRender = true;
17268 // opera does not respect the auto grow header center column
17269 // then, after it gets a width opera refuses to recalculate
17270 // without a second pass
17271 if(Roo.isOpera && !this.secondPass){
17272 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17273 this.secondPass = true;
17274 this.update.defer(10, this, [date]);
17281 findCell : function(dt) {
17282 dt = dt.clearTime().getTime();
17284 this.cells.each(function(c){
17285 //Roo.log("check " +c.dateValue + '?=' + dt);
17286 if(c.dateValue == dt){
17296 findCells : function(ev) {
17297 var s = ev.start.clone().clearTime().getTime();
17299 var e= ev.end.clone().clearTime().getTime();
17302 this.cells.each(function(c){
17303 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17305 if(c.dateValue > e){
17308 if(c.dateValue < s){
17317 // findBestRow: function(cells)
17321 // for (var i =0 ; i < cells.length;i++) {
17322 // ret = Math.max(cells[i].rows || 0,ret);
17329 addItem : function(ev)
17331 // look for vertical location slot in
17332 var cells = this.findCells(ev);
17334 // ev.row = this.findBestRow(cells);
17336 // work out the location.
17340 for(var i =0; i < cells.length; i++) {
17342 cells[i].row = cells[0].row;
17345 cells[i].row = cells[i].row + 1;
17355 if (crow.start.getY() == cells[i].getY()) {
17357 crow.end = cells[i];
17374 cells[0].events.push(ev);
17376 this.calevents.push(ev);
17379 clearEvents: function() {
17381 if(!this.calevents){
17385 Roo.each(this.cells.elements, function(c){
17391 Roo.each(this.calevents, function(e) {
17392 Roo.each(e.els, function(el) {
17393 el.un('mouseenter' ,this.onEventEnter, this);
17394 el.un('mouseleave' ,this.onEventLeave, this);
17399 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17405 renderEvents: function()
17409 this.cells.each(function(c) {
17418 if(c.row != c.events.length){
17419 r = 4 - (4 - (c.row - c.events.length));
17422 c.events = ev.slice(0, r);
17423 c.more = ev.slice(r);
17425 if(c.more.length && c.more.length == 1){
17426 c.events.push(c.more.pop());
17429 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17433 this.cells.each(function(c) {
17435 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17438 for (var e = 0; e < c.events.length; e++){
17439 var ev = c.events[e];
17440 var rows = ev.rows;
17442 for(var i = 0; i < rows.length; i++) {
17444 // how many rows should it span..
17447 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17448 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17450 unselectable : "on",
17453 cls: 'fc-event-inner',
17457 // cls: 'fc-event-time',
17458 // html : cells.length > 1 ? '' : ev.time
17462 cls: 'fc-event-title',
17463 html : String.format('{0}', ev.title)
17470 cls: 'ui-resizable-handle ui-resizable-e',
17471 html : '  '
17478 cfg.cls += ' fc-event-start';
17480 if ((i+1) == rows.length) {
17481 cfg.cls += ' fc-event-end';
17484 var ctr = _this.el.select('.fc-event-container',true).first();
17485 var cg = ctr.createChild(cfg);
17487 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17488 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17490 var r = (c.more.length) ? 1 : 0;
17491 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17492 cg.setWidth(ebox.right - sbox.x -2);
17494 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17495 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17496 cg.on('click', _this.onEventClick, _this, ev);
17507 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17508 style : 'position: absolute',
17509 unselectable : "on",
17512 cls: 'fc-event-inner',
17516 cls: 'fc-event-title',
17524 cls: 'ui-resizable-handle ui-resizable-e',
17525 html : '  '
17531 var ctr = _this.el.select('.fc-event-container',true).first();
17532 var cg = ctr.createChild(cfg);
17534 var sbox = c.select('.fc-day-content',true).first().getBox();
17535 var ebox = c.select('.fc-day-content',true).first().getBox();
17537 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17538 cg.setWidth(ebox.right - sbox.x -2);
17540 cg.on('click', _this.onMoreEventClick, _this, c.more);
17550 onEventEnter: function (e, el,event,d) {
17551 this.fireEvent('evententer', this, el, event);
17554 onEventLeave: function (e, el,event,d) {
17555 this.fireEvent('eventleave', this, el, event);
17558 onEventClick: function (e, el,event,d) {
17559 this.fireEvent('eventclick', this, el, event);
17562 onMonthChange: function () {
17566 onMoreEventClick: function(e, el, more)
17570 this.calpopover.placement = 'right';
17571 this.calpopover.setTitle('More');
17573 this.calpopover.setContent('');
17575 var ctr = this.calpopover.el.select('.popover-content', true).first();
17577 Roo.each(more, function(m){
17579 cls : 'fc-event-hori fc-event-draggable',
17582 var cg = ctr.createChild(cfg);
17584 cg.on('click', _this.onEventClick, _this, m);
17587 this.calpopover.show(el);
17592 onLoad: function ()
17594 this.calevents = [];
17597 if(this.store.getCount() > 0){
17598 this.store.data.each(function(d){
17601 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17602 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17603 time : d.data.start_time,
17604 title : d.data.title,
17605 description : d.data.description,
17606 venue : d.data.venue
17611 this.renderEvents();
17613 if(this.calevents.length && this.loadMask){
17614 this.maskEl.hide();
17618 onBeforeLoad: function()
17620 this.clearEvents();
17622 this.maskEl.show();
17636 * @class Roo.bootstrap.Popover
17637 * @extends Roo.bootstrap.Component
17638 * Bootstrap Popover class
17639 * @cfg {String} html contents of the popover (or false to use children..)
17640 * @cfg {String} title of popover (or false to hide)
17641 * @cfg {String} placement how it is placed
17642 * @cfg {String} trigger click || hover (or false to trigger manually)
17643 * @cfg {String} over what (parent or false to trigger manually.)
17644 * @cfg {Number} delay - delay before showing
17647 * Create a new Popover
17648 * @param {Object} config The config object
17651 Roo.bootstrap.Popover = function(config){
17652 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17658 * After the popover show
17660 * @param {Roo.bootstrap.Popover} this
17665 * After the popover hide
17667 * @param {Roo.bootstrap.Popover} this
17673 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17675 title: 'Fill in a title',
17678 placement : 'right',
17679 trigger : 'hover', // hover
17685 can_build_overlaid : false,
17687 getChildContainer : function()
17689 return this.el.select('.popover-content',true).first();
17692 getAutoCreate : function(){
17695 cls : 'popover roo-dynamic',
17696 style: 'display:block',
17702 cls : 'popover-inner',
17706 cls: 'popover-title popover-header',
17710 cls : 'popover-content popover-body',
17721 setTitle: function(str)
17724 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17726 setContent: function(str)
17729 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17731 // as it get's added to the bottom of the page.
17732 onRender : function(ct, position)
17734 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17736 var cfg = Roo.apply({}, this.getAutoCreate());
17740 cfg.cls += ' ' + this.cls;
17743 cfg.style = this.style;
17745 //Roo.log("adding to ");
17746 this.el = Roo.get(document.body).createChild(cfg, position);
17747 // Roo.log(this.el);
17752 initEvents : function()
17754 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17755 this.el.enableDisplayMode('block');
17757 if (this.over === false) {
17760 if (this.triggers === false) {
17763 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17764 var triggers = this.trigger ? this.trigger.split(' ') : [];
17765 Roo.each(triggers, function(trigger) {
17767 if (trigger == 'click') {
17768 on_el.on('click', this.toggle, this);
17769 } else if (trigger != 'manual') {
17770 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17771 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17773 on_el.on(eventIn ,this.enter, this);
17774 on_el.on(eventOut, this.leave, this);
17785 toggle : function () {
17786 this.hoverState == 'in' ? this.leave() : this.enter();
17789 enter : function () {
17791 clearTimeout(this.timeout);
17793 this.hoverState = 'in';
17795 if (!this.delay || !this.delay.show) {
17800 this.timeout = setTimeout(function () {
17801 if (_t.hoverState == 'in') {
17804 }, this.delay.show)
17807 leave : function() {
17808 clearTimeout(this.timeout);
17810 this.hoverState = 'out';
17812 if (!this.delay || !this.delay.hide) {
17817 this.timeout = setTimeout(function () {
17818 if (_t.hoverState == 'out') {
17821 }, this.delay.hide)
17824 show : function (on_el)
17827 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17831 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17832 if (this.html !== false) {
17833 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17835 this.el.removeClass([
17836 'fade','top','bottom', 'left', 'right','in',
17837 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17839 if (!this.title.length) {
17840 this.el.select('.popover-title',true).hide();
17843 var placement = typeof this.placement == 'function' ?
17844 this.placement.call(this, this.el, on_el) :
17847 var autoToken = /\s?auto?\s?/i;
17848 var autoPlace = autoToken.test(placement);
17850 placement = placement.replace(autoToken, '') || 'top';
17854 //this.el.setXY([0,0]);
17856 this.el.dom.style.display='block';
17857 this.el.addClass(placement);
17859 //this.el.appendTo(on_el);
17861 var p = this.getPosition();
17862 var box = this.el.getBox();
17867 var align = Roo.bootstrap.Popover.alignment[placement];
17870 this.el.alignTo(on_el, align[0],align[1]);
17871 //var arrow = this.el.select('.arrow',true).first();
17872 //arrow.set(align[2],
17874 this.el.addClass('in');
17877 if (this.el.hasClass('fade')) {
17881 this.hoverState = 'in';
17883 this.fireEvent('show', this);
17888 this.el.setXY([0,0]);
17889 this.el.removeClass('in');
17891 this.hoverState = null;
17893 this.fireEvent('hide', this);
17898 Roo.bootstrap.Popover.alignment = {
17899 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17900 'right' : ['l-r', [10,0], 'left bs-popover-left'],
17901 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17902 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17913 * @class Roo.bootstrap.Progress
17914 * @extends Roo.bootstrap.Component
17915 * Bootstrap Progress class
17916 * @cfg {Boolean} striped striped of the progress bar
17917 * @cfg {Boolean} active animated of the progress bar
17921 * Create a new Progress
17922 * @param {Object} config The config object
17925 Roo.bootstrap.Progress = function(config){
17926 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17929 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17934 getAutoCreate : function(){
17942 cfg.cls += ' progress-striped';
17946 cfg.cls += ' active';
17965 * @class Roo.bootstrap.ProgressBar
17966 * @extends Roo.bootstrap.Component
17967 * Bootstrap ProgressBar class
17968 * @cfg {Number} aria_valuenow aria-value now
17969 * @cfg {Number} aria_valuemin aria-value min
17970 * @cfg {Number} aria_valuemax aria-value max
17971 * @cfg {String} label label for the progress bar
17972 * @cfg {String} panel (success | info | warning | danger )
17973 * @cfg {String} role role of the progress bar
17974 * @cfg {String} sr_only text
17978 * Create a new ProgressBar
17979 * @param {Object} config The config object
17982 Roo.bootstrap.ProgressBar = function(config){
17983 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17986 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17990 aria_valuemax : 100,
17996 getAutoCreate : function()
18001 cls: 'progress-bar',
18002 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18014 cfg.role = this.role;
18017 if(this.aria_valuenow){
18018 cfg['aria-valuenow'] = this.aria_valuenow;
18021 if(this.aria_valuemin){
18022 cfg['aria-valuemin'] = this.aria_valuemin;
18025 if(this.aria_valuemax){
18026 cfg['aria-valuemax'] = this.aria_valuemax;
18029 if(this.label && !this.sr_only){
18030 cfg.html = this.label;
18034 cfg.cls += ' progress-bar-' + this.panel;
18040 update : function(aria_valuenow)
18042 this.aria_valuenow = aria_valuenow;
18044 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18059 * @class Roo.bootstrap.TabGroup
18060 * @extends Roo.bootstrap.Column
18061 * Bootstrap Column class
18062 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18063 * @cfg {Boolean} carousel true to make the group behave like a carousel
18064 * @cfg {Boolean} bullets show bullets for the panels
18065 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18066 * @cfg {Number} timer auto slide timer .. default 0 millisecond
18067 * @cfg {Boolean} showarrow (true|false) show arrow default true
18070 * Create a new TabGroup
18071 * @param {Object} config The config object
18074 Roo.bootstrap.TabGroup = function(config){
18075 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18077 this.navId = Roo.id();
18080 Roo.bootstrap.TabGroup.register(this);
18084 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18087 transition : false,
18092 slideOnTouch : false,
18095 getAutoCreate : function()
18097 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18099 cfg.cls += ' tab-content';
18101 if (this.carousel) {
18102 cfg.cls += ' carousel slide';
18105 cls : 'carousel-inner',
18109 if(this.bullets && !Roo.isTouch){
18112 cls : 'carousel-bullets',
18116 if(this.bullets_cls){
18117 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18124 cfg.cn[0].cn.push(bullets);
18127 if(this.showarrow){
18128 cfg.cn[0].cn.push({
18130 class : 'carousel-arrow',
18134 class : 'carousel-prev',
18138 class : 'fa fa-chevron-left'
18144 class : 'carousel-next',
18148 class : 'fa fa-chevron-right'
18161 initEvents: function()
18163 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18164 // this.el.on("touchstart", this.onTouchStart, this);
18167 if(this.autoslide){
18170 this.slideFn = window.setInterval(function() {
18171 _this.showPanelNext();
18175 if(this.showarrow){
18176 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18177 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18183 // onTouchStart : function(e, el, o)
18185 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18189 // this.showPanelNext();
18193 getChildContainer : function()
18195 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18199 * register a Navigation item
18200 * @param {Roo.bootstrap.NavItem} the navitem to add
18202 register : function(item)
18204 this.tabs.push( item);
18205 item.navId = this.navId; // not really needed..
18210 getActivePanel : function()
18213 Roo.each(this.tabs, function(t) {
18223 getPanelByName : function(n)
18226 Roo.each(this.tabs, function(t) {
18227 if (t.tabId == n) {
18235 indexOfPanel : function(p)
18238 Roo.each(this.tabs, function(t,i) {
18239 if (t.tabId == p.tabId) {
18248 * show a specific panel
18249 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18250 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18252 showPanel : function (pan)
18254 if(this.transition || typeof(pan) == 'undefined'){
18255 Roo.log("waiting for the transitionend");
18259 if (typeof(pan) == 'number') {
18260 pan = this.tabs[pan];
18263 if (typeof(pan) == 'string') {
18264 pan = this.getPanelByName(pan);
18267 var cur = this.getActivePanel();
18270 Roo.log('pan or acitve pan is undefined');
18274 if (pan.tabId == this.getActivePanel().tabId) {
18278 if (false === cur.fireEvent('beforedeactivate')) {
18282 if(this.bullets > 0 && !Roo.isTouch){
18283 this.setActiveBullet(this.indexOfPanel(pan));
18286 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18288 this.transition = true;
18289 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18290 var lr = dir == 'next' ? 'left' : 'right';
18291 pan.el.addClass(dir); // or prev
18292 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18293 cur.el.addClass(lr); // or right
18294 pan.el.addClass(lr);
18297 cur.el.on('transitionend', function() {
18298 Roo.log("trans end?");
18300 pan.el.removeClass([lr,dir]);
18301 pan.setActive(true);
18303 cur.el.removeClass([lr]);
18304 cur.setActive(false);
18306 _this.transition = false;
18308 }, this, { single: true } );
18313 cur.setActive(false);
18314 pan.setActive(true);
18319 showPanelNext : function()
18321 var i = this.indexOfPanel(this.getActivePanel());
18323 if (i >= this.tabs.length - 1 && !this.autoslide) {
18327 if (i >= this.tabs.length - 1 && this.autoslide) {
18331 this.showPanel(this.tabs[i+1]);
18334 showPanelPrev : function()
18336 var i = this.indexOfPanel(this.getActivePanel());
18338 if (i < 1 && !this.autoslide) {
18342 if (i < 1 && this.autoslide) {
18343 i = this.tabs.length;
18346 this.showPanel(this.tabs[i-1]);
18350 addBullet: function()
18352 if(!this.bullets || Roo.isTouch){
18355 var ctr = this.el.select('.carousel-bullets',true).first();
18356 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18357 var bullet = ctr.createChild({
18358 cls : 'bullet bullet-' + i
18359 },ctr.dom.lastChild);
18364 bullet.on('click', (function(e, el, o, ii, t){
18366 e.preventDefault();
18368 this.showPanel(ii);
18370 if(this.autoslide && this.slideFn){
18371 clearInterval(this.slideFn);
18372 this.slideFn = window.setInterval(function() {
18373 _this.showPanelNext();
18377 }).createDelegate(this, [i, bullet], true));
18382 setActiveBullet : function(i)
18388 Roo.each(this.el.select('.bullet', true).elements, function(el){
18389 el.removeClass('selected');
18392 var bullet = this.el.select('.bullet-' + i, true).first();
18398 bullet.addClass('selected');
18409 Roo.apply(Roo.bootstrap.TabGroup, {
18413 * register a Navigation Group
18414 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18416 register : function(navgrp)
18418 this.groups[navgrp.navId] = navgrp;
18422 * fetch a Navigation Group based on the navigation ID
18423 * if one does not exist , it will get created.
18424 * @param {string} the navgroup to add
18425 * @returns {Roo.bootstrap.NavGroup} the navgroup
18427 get: function(navId) {
18428 if (typeof(this.groups[navId]) == 'undefined') {
18429 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18431 return this.groups[navId] ;
18446 * @class Roo.bootstrap.TabPanel
18447 * @extends Roo.bootstrap.Component
18448 * Bootstrap TabPanel class
18449 * @cfg {Boolean} active panel active
18450 * @cfg {String} html panel content
18451 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18452 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18453 * @cfg {String} href click to link..
18457 * Create a new TabPanel
18458 * @param {Object} config The config object
18461 Roo.bootstrap.TabPanel = function(config){
18462 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18466 * Fires when the active status changes
18467 * @param {Roo.bootstrap.TabPanel} this
18468 * @param {Boolean} state the new state
18473 * @event beforedeactivate
18474 * Fires before a tab is de-activated - can be used to do validation on a form.
18475 * @param {Roo.bootstrap.TabPanel} this
18476 * @return {Boolean} false if there is an error
18479 'beforedeactivate': true
18482 this.tabId = this.tabId || Roo.id();
18486 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18494 getAutoCreate : function(){
18497 // item is needed for carousel - not sure if it has any effect otherwise
18498 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18499 html: this.html || ''
18503 cfg.cls += ' active';
18507 cfg.tabId = this.tabId;
18514 initEvents: function()
18516 var p = this.parent();
18518 this.navId = this.navId || p.navId;
18520 if (typeof(this.navId) != 'undefined') {
18521 // not really needed.. but just in case.. parent should be a NavGroup.
18522 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18526 var i = tg.tabs.length - 1;
18528 if(this.active && tg.bullets > 0 && i < tg.bullets){
18529 tg.setActiveBullet(i);
18533 this.el.on('click', this.onClick, this);
18536 this.el.on("touchstart", this.onTouchStart, this);
18537 this.el.on("touchmove", this.onTouchMove, this);
18538 this.el.on("touchend", this.onTouchEnd, this);
18543 onRender : function(ct, position)
18545 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18548 setActive : function(state)
18550 Roo.log("panel - set active " + this.tabId + "=" + state);
18552 this.active = state;
18554 this.el.removeClass('active');
18556 } else if (!this.el.hasClass('active')) {
18557 this.el.addClass('active');
18560 this.fireEvent('changed', this, state);
18563 onClick : function(e)
18565 e.preventDefault();
18567 if(!this.href.length){
18571 window.location.href = this.href;
18580 onTouchStart : function(e)
18582 this.swiping = false;
18584 this.startX = e.browserEvent.touches[0].clientX;
18585 this.startY = e.browserEvent.touches[0].clientY;
18588 onTouchMove : function(e)
18590 this.swiping = true;
18592 this.endX = e.browserEvent.touches[0].clientX;
18593 this.endY = e.browserEvent.touches[0].clientY;
18596 onTouchEnd : function(e)
18603 var tabGroup = this.parent();
18605 if(this.endX > this.startX){ // swiping right
18606 tabGroup.showPanelPrev();
18610 if(this.startX > this.endX){ // swiping left
18611 tabGroup.showPanelNext();
18630 * @class Roo.bootstrap.DateField
18631 * @extends Roo.bootstrap.Input
18632 * Bootstrap DateField class
18633 * @cfg {Number} weekStart default 0
18634 * @cfg {String} viewMode default empty, (months|years)
18635 * @cfg {String} minViewMode default empty, (months|years)
18636 * @cfg {Number} startDate default -Infinity
18637 * @cfg {Number} endDate default Infinity
18638 * @cfg {Boolean} todayHighlight default false
18639 * @cfg {Boolean} todayBtn default false
18640 * @cfg {Boolean} calendarWeeks default false
18641 * @cfg {Object} daysOfWeekDisabled default empty
18642 * @cfg {Boolean} singleMode default false (true | false)
18644 * @cfg {Boolean} keyboardNavigation default true
18645 * @cfg {String} language default en
18648 * Create a new DateField
18649 * @param {Object} config The config object
18652 Roo.bootstrap.DateField = function(config){
18653 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18657 * Fires when this field show.
18658 * @param {Roo.bootstrap.DateField} this
18659 * @param {Mixed} date The date value
18664 * Fires when this field hide.
18665 * @param {Roo.bootstrap.DateField} this
18666 * @param {Mixed} date The date value
18671 * Fires when select a date.
18672 * @param {Roo.bootstrap.DateField} this
18673 * @param {Mixed} date The date value
18677 * @event beforeselect
18678 * Fires when before select a date.
18679 * @param {Roo.bootstrap.DateField} this
18680 * @param {Mixed} date The date value
18682 beforeselect : true
18686 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18689 * @cfg {String} format
18690 * The default date format string which can be overriden for localization support. The format must be
18691 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18695 * @cfg {String} altFormats
18696 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18697 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18699 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18707 todayHighlight : false,
18713 keyboardNavigation: true,
18715 calendarWeeks: false,
18717 startDate: -Infinity,
18721 daysOfWeekDisabled: [],
18725 singleMode : false,
18727 UTCDate: function()
18729 return new Date(Date.UTC.apply(Date, arguments));
18732 UTCToday: function()
18734 var today = new Date();
18735 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18738 getDate: function() {
18739 var d = this.getUTCDate();
18740 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18743 getUTCDate: function() {
18747 setDate: function(d) {
18748 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18751 setUTCDate: function(d) {
18753 this.setValue(this.formatDate(this.date));
18756 onRender: function(ct, position)
18759 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18761 this.language = this.language || 'en';
18762 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18763 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18765 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18766 this.format = this.format || 'm/d/y';
18767 this.isInline = false;
18768 this.isInput = true;
18769 this.component = this.el.select('.add-on', true).first() || false;
18770 this.component = (this.component && this.component.length === 0) ? false : this.component;
18771 this.hasInput = this.component && this.inputEl().length;
18773 if (typeof(this.minViewMode === 'string')) {
18774 switch (this.minViewMode) {
18776 this.minViewMode = 1;
18779 this.minViewMode = 2;
18782 this.minViewMode = 0;
18787 if (typeof(this.viewMode === 'string')) {
18788 switch (this.viewMode) {
18801 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18803 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18805 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18807 this.picker().on('mousedown', this.onMousedown, this);
18808 this.picker().on('click', this.onClick, this);
18810 this.picker().addClass('datepicker-dropdown');
18812 this.startViewMode = this.viewMode;
18814 if(this.singleMode){
18815 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18816 v.setVisibilityMode(Roo.Element.DISPLAY);
18820 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18821 v.setStyle('width', '189px');
18825 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18826 if(!this.calendarWeeks){
18831 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18832 v.attr('colspan', function(i, val){
18833 return parseInt(val) + 1;
18838 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18840 this.setStartDate(this.startDate);
18841 this.setEndDate(this.endDate);
18843 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18850 if(this.isInline) {
18855 picker : function()
18857 return this.pickerEl;
18858 // return this.el.select('.datepicker', true).first();
18861 fillDow: function()
18863 var dowCnt = this.weekStart;
18872 if(this.calendarWeeks){
18880 while (dowCnt < this.weekStart + 7) {
18884 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18888 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18891 fillMonths: function()
18894 var months = this.picker().select('>.datepicker-months td', true).first();
18896 months.dom.innerHTML = '';
18902 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18905 months.createChild(month);
18912 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;
18914 if (this.date < this.startDate) {
18915 this.viewDate = new Date(this.startDate);
18916 } else if (this.date > this.endDate) {
18917 this.viewDate = new Date(this.endDate);
18919 this.viewDate = new Date(this.date);
18927 var d = new Date(this.viewDate),
18928 year = d.getUTCFullYear(),
18929 month = d.getUTCMonth(),
18930 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18931 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18932 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18933 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18934 currentDate = this.date && this.date.valueOf(),
18935 today = this.UTCToday();
18937 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18939 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18941 // this.picker.select('>tfoot th.today').
18942 // .text(dates[this.language].today)
18943 // .toggle(this.todayBtn !== false);
18945 this.updateNavArrows();
18948 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18950 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18952 prevMonth.setUTCDate(day);
18954 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18956 var nextMonth = new Date(prevMonth);
18958 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18960 nextMonth = nextMonth.valueOf();
18962 var fillMonths = false;
18964 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18966 while(prevMonth.valueOf() <= nextMonth) {
18969 if (prevMonth.getUTCDay() === this.weekStart) {
18971 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18979 if(this.calendarWeeks){
18980 // ISO 8601: First week contains first thursday.
18981 // ISO also states week starts on Monday, but we can be more abstract here.
18983 // Start of current week: based on weekstart/current date
18984 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18985 // Thursday of this week
18986 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18987 // First Thursday of year, year from thursday
18988 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18989 // Calendar week: ms between thursdays, div ms per day, div 7 days
18990 calWeek = (th - yth) / 864e5 / 7 + 1;
18992 fillMonths.cn.push({
19000 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19002 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19005 if (this.todayHighlight &&
19006 prevMonth.getUTCFullYear() == today.getFullYear() &&
19007 prevMonth.getUTCMonth() == today.getMonth() &&
19008 prevMonth.getUTCDate() == today.getDate()) {
19009 clsName += ' today';
19012 if (currentDate && prevMonth.valueOf() === currentDate) {
19013 clsName += ' active';
19016 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19017 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19018 clsName += ' disabled';
19021 fillMonths.cn.push({
19023 cls: 'day ' + clsName,
19024 html: prevMonth.getDate()
19027 prevMonth.setDate(prevMonth.getDate()+1);
19030 var currentYear = this.date && this.date.getUTCFullYear();
19031 var currentMonth = this.date && this.date.getUTCMonth();
19033 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19035 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19036 v.removeClass('active');
19038 if(currentYear === year && k === currentMonth){
19039 v.addClass('active');
19042 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19043 v.addClass('disabled');
19049 year = parseInt(year/10, 10) * 10;
19051 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19053 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19056 for (var i = -1; i < 11; i++) {
19057 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19059 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19067 showMode: function(dir)
19070 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19073 Roo.each(this.picker().select('>div',true).elements, function(v){
19074 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19077 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19082 if(this.isInline) {
19086 this.picker().removeClass(['bottom', 'top']);
19088 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19090 * place to the top of element!
19094 this.picker().addClass('top');
19095 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19100 this.picker().addClass('bottom');
19102 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19105 parseDate : function(value)
19107 if(!value || value instanceof Date){
19110 var v = Date.parseDate(value, this.format);
19111 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19112 v = Date.parseDate(value, 'Y-m-d');
19114 if(!v && this.altFormats){
19115 if(!this.altFormatsArray){
19116 this.altFormatsArray = this.altFormats.split("|");
19118 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19119 v = Date.parseDate(value, this.altFormatsArray[i]);
19125 formatDate : function(date, fmt)
19127 return (!date || !(date instanceof Date)) ?
19128 date : date.dateFormat(fmt || this.format);
19131 onFocus : function()
19133 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19137 onBlur : function()
19139 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19141 var d = this.inputEl().getValue();
19148 showPopup : function()
19150 this.picker().show();
19154 this.fireEvent('showpopup', this, this.date);
19157 hidePopup : function()
19159 if(this.isInline) {
19162 this.picker().hide();
19163 this.viewMode = this.startViewMode;
19166 this.fireEvent('hidepopup', this, this.date);
19170 onMousedown: function(e)
19172 e.stopPropagation();
19173 e.preventDefault();
19178 Roo.bootstrap.DateField.superclass.keyup.call(this);
19182 setValue: function(v)
19184 if(this.fireEvent('beforeselect', this, v) !== false){
19185 var d = new Date(this.parseDate(v) ).clearTime();
19187 if(isNaN(d.getTime())){
19188 this.date = this.viewDate = '';
19189 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19193 v = this.formatDate(d);
19195 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19197 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19201 this.fireEvent('select', this, this.date);
19205 getValue: function()
19207 return this.formatDate(this.date);
19210 fireKey: function(e)
19212 if (!this.picker().isVisible()){
19213 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19219 var dateChanged = false,
19221 newDate, newViewDate;
19226 e.preventDefault();
19230 if (!this.keyboardNavigation) {
19233 dir = e.keyCode == 37 ? -1 : 1;
19236 newDate = this.moveYear(this.date, dir);
19237 newViewDate = this.moveYear(this.viewDate, dir);
19238 } else if (e.shiftKey){
19239 newDate = this.moveMonth(this.date, dir);
19240 newViewDate = this.moveMonth(this.viewDate, dir);
19242 newDate = new Date(this.date);
19243 newDate.setUTCDate(this.date.getUTCDate() + dir);
19244 newViewDate = new Date(this.viewDate);
19245 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19247 if (this.dateWithinRange(newDate)){
19248 this.date = newDate;
19249 this.viewDate = newViewDate;
19250 this.setValue(this.formatDate(this.date));
19252 e.preventDefault();
19253 dateChanged = true;
19258 if (!this.keyboardNavigation) {
19261 dir = e.keyCode == 38 ? -1 : 1;
19263 newDate = this.moveYear(this.date, dir);
19264 newViewDate = this.moveYear(this.viewDate, dir);
19265 } else if (e.shiftKey){
19266 newDate = this.moveMonth(this.date, dir);
19267 newViewDate = this.moveMonth(this.viewDate, dir);
19269 newDate = new Date(this.date);
19270 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19271 newViewDate = new Date(this.viewDate);
19272 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19274 if (this.dateWithinRange(newDate)){
19275 this.date = newDate;
19276 this.viewDate = newViewDate;
19277 this.setValue(this.formatDate(this.date));
19279 e.preventDefault();
19280 dateChanged = true;
19284 this.setValue(this.formatDate(this.date));
19286 e.preventDefault();
19289 this.setValue(this.formatDate(this.date));
19303 onClick: function(e)
19305 e.stopPropagation();
19306 e.preventDefault();
19308 var target = e.getTarget();
19310 if(target.nodeName.toLowerCase() === 'i'){
19311 target = Roo.get(target).dom.parentNode;
19314 var nodeName = target.nodeName;
19315 var className = target.className;
19316 var html = target.innerHTML;
19317 //Roo.log(nodeName);
19319 switch(nodeName.toLowerCase()) {
19321 switch(className) {
19327 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19328 switch(this.viewMode){
19330 this.viewDate = this.moveMonth(this.viewDate, dir);
19334 this.viewDate = this.moveYear(this.viewDate, dir);
19340 var date = new Date();
19341 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19343 this.setValue(this.formatDate(this.date));
19350 if (className.indexOf('disabled') < 0) {
19351 this.viewDate.setUTCDate(1);
19352 if (className.indexOf('month') > -1) {
19353 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19355 var year = parseInt(html, 10) || 0;
19356 this.viewDate.setUTCFullYear(year);
19360 if(this.singleMode){
19361 this.setValue(this.formatDate(this.viewDate));
19372 //Roo.log(className);
19373 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19374 var day = parseInt(html, 10) || 1;
19375 var year = this.viewDate.getUTCFullYear(),
19376 month = this.viewDate.getUTCMonth();
19378 if (className.indexOf('old') > -1) {
19385 } else if (className.indexOf('new') > -1) {
19393 //Roo.log([year,month,day]);
19394 this.date = this.UTCDate(year, month, day,0,0,0,0);
19395 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19397 //Roo.log(this.formatDate(this.date));
19398 this.setValue(this.formatDate(this.date));
19405 setStartDate: function(startDate)
19407 this.startDate = startDate || -Infinity;
19408 if (this.startDate !== -Infinity) {
19409 this.startDate = this.parseDate(this.startDate);
19412 this.updateNavArrows();
19415 setEndDate: function(endDate)
19417 this.endDate = endDate || Infinity;
19418 if (this.endDate !== Infinity) {
19419 this.endDate = this.parseDate(this.endDate);
19422 this.updateNavArrows();
19425 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19427 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19428 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19429 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19431 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19432 return parseInt(d, 10);
19435 this.updateNavArrows();
19438 updateNavArrows: function()
19440 if(this.singleMode){
19444 var d = new Date(this.viewDate),
19445 year = d.getUTCFullYear(),
19446 month = d.getUTCMonth();
19448 Roo.each(this.picker().select('.prev', true).elements, function(v){
19450 switch (this.viewMode) {
19453 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19459 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19466 Roo.each(this.picker().select('.next', true).elements, function(v){
19468 switch (this.viewMode) {
19471 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19477 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19485 moveMonth: function(date, dir)
19490 var new_date = new Date(date.valueOf()),
19491 day = new_date.getUTCDate(),
19492 month = new_date.getUTCMonth(),
19493 mag = Math.abs(dir),
19495 dir = dir > 0 ? 1 : -1;
19498 // If going back one month, make sure month is not current month
19499 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19501 return new_date.getUTCMonth() == month;
19503 // If going forward one month, make sure month is as expected
19504 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19506 return new_date.getUTCMonth() != new_month;
19508 new_month = month + dir;
19509 new_date.setUTCMonth(new_month);
19510 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19511 if (new_month < 0 || new_month > 11) {
19512 new_month = (new_month + 12) % 12;
19515 // For magnitudes >1, move one month at a time...
19516 for (var i=0; i<mag; i++) {
19517 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19518 new_date = this.moveMonth(new_date, dir);
19520 // ...then reset the day, keeping it in the new month
19521 new_month = new_date.getUTCMonth();
19522 new_date.setUTCDate(day);
19524 return new_month != new_date.getUTCMonth();
19527 // Common date-resetting loop -- if date is beyond end of month, make it
19530 new_date.setUTCDate(--day);
19531 new_date.setUTCMonth(new_month);
19536 moveYear: function(date, dir)
19538 return this.moveMonth(date, dir*12);
19541 dateWithinRange: function(date)
19543 return date >= this.startDate && date <= this.endDate;
19549 this.picker().remove();
19552 validateValue : function(value)
19554 if(this.getVisibilityEl().hasClass('hidden')){
19558 if(value.length < 1) {
19559 if(this.allowBlank){
19565 if(value.length < this.minLength){
19568 if(value.length > this.maxLength){
19572 var vt = Roo.form.VTypes;
19573 if(!vt[this.vtype](value, this)){
19577 if(typeof this.validator == "function"){
19578 var msg = this.validator(value);
19584 if(this.regex && !this.regex.test(value)){
19588 if(typeof(this.parseDate(value)) == 'undefined'){
19592 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19596 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19606 this.date = this.viewDate = '';
19608 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19613 Roo.apply(Roo.bootstrap.DateField, {
19624 html: '<i class="fa fa-arrow-left"/>'
19634 html: '<i class="fa fa-arrow-right"/>'
19676 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19677 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19678 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19679 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19680 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19693 navFnc: 'FullYear',
19698 navFnc: 'FullYear',
19703 Roo.apply(Roo.bootstrap.DateField, {
19707 cls: 'datepicker dropdown-menu roo-dynamic',
19711 cls: 'datepicker-days',
19715 cls: 'table-condensed',
19717 Roo.bootstrap.DateField.head,
19721 Roo.bootstrap.DateField.footer
19728 cls: 'datepicker-months',
19732 cls: 'table-condensed',
19734 Roo.bootstrap.DateField.head,
19735 Roo.bootstrap.DateField.content,
19736 Roo.bootstrap.DateField.footer
19743 cls: 'datepicker-years',
19747 cls: 'table-condensed',
19749 Roo.bootstrap.DateField.head,
19750 Roo.bootstrap.DateField.content,
19751 Roo.bootstrap.DateField.footer
19770 * @class Roo.bootstrap.TimeField
19771 * @extends Roo.bootstrap.Input
19772 * Bootstrap DateField class
19776 * Create a new TimeField
19777 * @param {Object} config The config object
19780 Roo.bootstrap.TimeField = function(config){
19781 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19785 * Fires when this field show.
19786 * @param {Roo.bootstrap.DateField} thisthis
19787 * @param {Mixed} date The date value
19792 * Fires when this field hide.
19793 * @param {Roo.bootstrap.DateField} this
19794 * @param {Mixed} date The date value
19799 * Fires when select a date.
19800 * @param {Roo.bootstrap.DateField} this
19801 * @param {Mixed} date The date value
19807 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19810 * @cfg {String} format
19811 * The default time format string which can be overriden for localization support. The format must be
19812 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19816 onRender: function(ct, position)
19819 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19821 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19823 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19825 this.pop = this.picker().select('>.datepicker-time',true).first();
19826 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19828 this.picker().on('mousedown', this.onMousedown, this);
19829 this.picker().on('click', this.onClick, this);
19831 this.picker().addClass('datepicker-dropdown');
19836 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19837 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19838 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19839 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19840 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19841 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19845 fireKey: function(e){
19846 if (!this.picker().isVisible()){
19847 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19853 e.preventDefault();
19861 this.onTogglePeriod();
19864 this.onIncrementMinutes();
19867 this.onDecrementMinutes();
19876 onClick: function(e) {
19877 e.stopPropagation();
19878 e.preventDefault();
19881 picker : function()
19883 return this.el.select('.datepicker', true).first();
19886 fillTime: function()
19888 var time = this.pop.select('tbody', true).first();
19890 time.dom.innerHTML = '';
19905 cls: 'hours-up glyphicon glyphicon-chevron-up'
19925 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19946 cls: 'timepicker-hour',
19961 cls: 'timepicker-minute',
19976 cls: 'btn btn-primary period',
19998 cls: 'hours-down glyphicon glyphicon-chevron-down'
20018 cls: 'minutes-down glyphicon glyphicon-chevron-down'
20036 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20043 var hours = this.time.getHours();
20044 var minutes = this.time.getMinutes();
20057 hours = hours - 12;
20061 hours = '0' + hours;
20065 minutes = '0' + minutes;
20068 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20069 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20070 this.pop.select('button', true).first().dom.innerHTML = period;
20076 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20078 var cls = ['bottom'];
20080 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20087 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20092 this.picker().addClass(cls.join('-'));
20096 Roo.each(cls, function(c){
20098 _this.picker().setTop(_this.inputEl().getHeight());
20102 _this.picker().setTop(0 - _this.picker().getHeight());
20107 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20111 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20118 onFocus : function()
20120 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20124 onBlur : function()
20126 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20132 this.picker().show();
20137 this.fireEvent('show', this, this.date);
20142 this.picker().hide();
20145 this.fireEvent('hide', this, this.date);
20148 setTime : function()
20151 this.setValue(this.time.format(this.format));
20153 this.fireEvent('select', this, this.date);
20158 onMousedown: function(e){
20159 e.stopPropagation();
20160 e.preventDefault();
20163 onIncrementHours: function()
20165 Roo.log('onIncrementHours');
20166 this.time = this.time.add(Date.HOUR, 1);
20171 onDecrementHours: function()
20173 Roo.log('onDecrementHours');
20174 this.time = this.time.add(Date.HOUR, -1);
20178 onIncrementMinutes: function()
20180 Roo.log('onIncrementMinutes');
20181 this.time = this.time.add(Date.MINUTE, 1);
20185 onDecrementMinutes: function()
20187 Roo.log('onDecrementMinutes');
20188 this.time = this.time.add(Date.MINUTE, -1);
20192 onTogglePeriod: function()
20194 Roo.log('onTogglePeriod');
20195 this.time = this.time.add(Date.HOUR, 12);
20202 Roo.apply(Roo.bootstrap.TimeField, {
20232 cls: 'btn btn-info ok',
20244 Roo.apply(Roo.bootstrap.TimeField, {
20248 cls: 'datepicker dropdown-menu',
20252 cls: 'datepicker-time',
20256 cls: 'table-condensed',
20258 Roo.bootstrap.TimeField.content,
20259 Roo.bootstrap.TimeField.footer
20278 * @class Roo.bootstrap.MonthField
20279 * @extends Roo.bootstrap.Input
20280 * Bootstrap MonthField class
20282 * @cfg {String} language default en
20285 * Create a new MonthField
20286 * @param {Object} config The config object
20289 Roo.bootstrap.MonthField = function(config){
20290 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20295 * Fires when this field show.
20296 * @param {Roo.bootstrap.MonthField} this
20297 * @param {Mixed} date The date value
20302 * Fires when this field hide.
20303 * @param {Roo.bootstrap.MonthField} this
20304 * @param {Mixed} date The date value
20309 * Fires when select a date.
20310 * @param {Roo.bootstrap.MonthField} this
20311 * @param {String} oldvalue The old value
20312 * @param {String} newvalue The new value
20318 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20320 onRender: function(ct, position)
20323 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20325 this.language = this.language || 'en';
20326 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20327 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20329 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20330 this.isInline = false;
20331 this.isInput = true;
20332 this.component = this.el.select('.add-on', true).first() || false;
20333 this.component = (this.component && this.component.length === 0) ? false : this.component;
20334 this.hasInput = this.component && this.inputEL().length;
20336 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20338 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20340 this.picker().on('mousedown', this.onMousedown, this);
20341 this.picker().on('click', this.onClick, this);
20343 this.picker().addClass('datepicker-dropdown');
20345 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20346 v.setStyle('width', '189px');
20353 if(this.isInline) {
20359 setValue: function(v, suppressEvent)
20361 var o = this.getValue();
20363 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20367 if(suppressEvent !== true){
20368 this.fireEvent('select', this, o, v);
20373 getValue: function()
20378 onClick: function(e)
20380 e.stopPropagation();
20381 e.preventDefault();
20383 var target = e.getTarget();
20385 if(target.nodeName.toLowerCase() === 'i'){
20386 target = Roo.get(target).dom.parentNode;
20389 var nodeName = target.nodeName;
20390 var className = target.className;
20391 var html = target.innerHTML;
20393 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20397 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20399 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20405 picker : function()
20407 return this.pickerEl;
20410 fillMonths: function()
20413 var months = this.picker().select('>.datepicker-months td', true).first();
20415 months.dom.innerHTML = '';
20421 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20424 months.createChild(month);
20433 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20434 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20437 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20438 e.removeClass('active');
20440 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20441 e.addClass('active');
20448 if(this.isInline) {
20452 this.picker().removeClass(['bottom', 'top']);
20454 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20456 * place to the top of element!
20460 this.picker().addClass('top');
20461 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20466 this.picker().addClass('bottom');
20468 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20471 onFocus : function()
20473 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20477 onBlur : function()
20479 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20481 var d = this.inputEl().getValue();
20490 this.picker().show();
20491 this.picker().select('>.datepicker-months', true).first().show();
20495 this.fireEvent('show', this, this.date);
20500 if(this.isInline) {
20503 this.picker().hide();
20504 this.fireEvent('hide', this, this.date);
20508 onMousedown: function(e)
20510 e.stopPropagation();
20511 e.preventDefault();
20516 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20520 fireKey: function(e)
20522 if (!this.picker().isVisible()){
20523 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20534 e.preventDefault();
20538 dir = e.keyCode == 37 ? -1 : 1;
20540 this.vIndex = this.vIndex + dir;
20542 if(this.vIndex < 0){
20546 if(this.vIndex > 11){
20550 if(isNaN(this.vIndex)){
20554 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20560 dir = e.keyCode == 38 ? -1 : 1;
20562 this.vIndex = this.vIndex + dir * 4;
20564 if(this.vIndex < 0){
20568 if(this.vIndex > 11){
20572 if(isNaN(this.vIndex)){
20576 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20581 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20582 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20586 e.preventDefault();
20589 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20590 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20606 this.picker().remove();
20611 Roo.apply(Roo.bootstrap.MonthField, {
20630 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20631 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20636 Roo.apply(Roo.bootstrap.MonthField, {
20640 cls: 'datepicker dropdown-menu roo-dynamic',
20644 cls: 'datepicker-months',
20648 cls: 'table-condensed',
20650 Roo.bootstrap.DateField.content
20670 * @class Roo.bootstrap.CheckBox
20671 * @extends Roo.bootstrap.Input
20672 * Bootstrap CheckBox class
20674 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20675 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20676 * @cfg {String} boxLabel The text that appears beside the checkbox
20677 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20678 * @cfg {Boolean} checked initnal the element
20679 * @cfg {Boolean} inline inline the element (default false)
20680 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20681 * @cfg {String} tooltip label tooltip
20684 * Create a new CheckBox
20685 * @param {Object} config The config object
20688 Roo.bootstrap.CheckBox = function(config){
20689 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20694 * Fires when the element is checked or unchecked.
20695 * @param {Roo.bootstrap.CheckBox} this This input
20696 * @param {Boolean} checked The new checked value
20701 * Fires when the element is click.
20702 * @param {Roo.bootstrap.CheckBox} this This input
20709 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20711 inputType: 'checkbox',
20720 getAutoCreate : function()
20722 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20728 cfg.cls = 'form-group ' + this.inputType; //input-group
20731 cfg.cls += ' ' + this.inputType + '-inline';
20737 type : this.inputType,
20738 value : this.inputValue,
20739 cls : 'roo-' + this.inputType, //'form-box',
20740 placeholder : this.placeholder || ''
20744 if(this.inputType != 'radio'){
20748 cls : 'roo-hidden-value',
20749 value : this.checked ? this.inputValue : this.valueOff
20754 if (this.weight) { // Validity check?
20755 cfg.cls += " " + this.inputType + "-" + this.weight;
20758 if (this.disabled) {
20759 input.disabled=true;
20763 input.checked = this.checked;
20768 input.name = this.name;
20770 if(this.inputType != 'radio'){
20771 hidden.name = this.name;
20772 input.name = '_hidden_' + this.name;
20777 input.cls += ' input-' + this.size;
20782 ['xs','sm','md','lg'].map(function(size){
20783 if (settings[size]) {
20784 cfg.cls += ' col-' + size + '-' + settings[size];
20788 var inputblock = input;
20790 if (this.before || this.after) {
20793 cls : 'input-group',
20798 inputblock.cn.push({
20800 cls : 'input-group-addon',
20805 inputblock.cn.push(input);
20807 if(this.inputType != 'radio'){
20808 inputblock.cn.push(hidden);
20812 inputblock.cn.push({
20814 cls : 'input-group-addon',
20821 if (align ==='left' && this.fieldLabel.length) {
20822 // Roo.log("left and has label");
20827 cls : 'control-label',
20828 html : this.fieldLabel
20838 if(this.labelWidth > 12){
20839 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20842 if(this.labelWidth < 13 && this.labelmd == 0){
20843 this.labelmd = this.labelWidth;
20846 if(this.labellg > 0){
20847 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20848 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20851 if(this.labelmd > 0){
20852 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20853 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20856 if(this.labelsm > 0){
20857 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20858 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20861 if(this.labelxs > 0){
20862 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20863 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20866 } else if ( this.fieldLabel.length) {
20867 // Roo.log(" label");
20871 tag: this.boxLabel ? 'span' : 'label',
20873 cls: 'control-label box-input-label',
20874 //cls : 'input-group-addon',
20875 html : this.fieldLabel
20884 // Roo.log(" no label && no align");
20885 cfg.cn = [ inputblock ] ;
20891 var boxLabelCfg = {
20893 //'for': id, // box label is handled by onclick - so no for...
20895 html: this.boxLabel
20899 boxLabelCfg.tooltip = this.tooltip;
20902 cfg.cn.push(boxLabelCfg);
20905 if(this.inputType != 'radio'){
20906 cfg.cn.push(hidden);
20914 * return the real input element.
20916 inputEl: function ()
20918 return this.el.select('input.roo-' + this.inputType,true).first();
20920 hiddenEl: function ()
20922 return this.el.select('input.roo-hidden-value',true).first();
20925 labelEl: function()
20927 return this.el.select('label.control-label',true).first();
20929 /* depricated... */
20933 return this.labelEl();
20936 boxLabelEl: function()
20938 return this.el.select('label.box-label',true).first();
20941 initEvents : function()
20943 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20945 this.inputEl().on('click', this.onClick, this);
20947 if (this.boxLabel) {
20948 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20951 this.startValue = this.getValue();
20954 Roo.bootstrap.CheckBox.register(this);
20958 onClick : function(e)
20960 if(this.fireEvent('click', this, e) !== false){
20961 this.setChecked(!this.checked);
20966 setChecked : function(state,suppressEvent)
20968 this.startValue = this.getValue();
20970 if(this.inputType == 'radio'){
20972 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20973 e.dom.checked = false;
20976 this.inputEl().dom.checked = true;
20978 this.inputEl().dom.value = this.inputValue;
20980 if(suppressEvent !== true){
20981 this.fireEvent('check', this, true);
20989 this.checked = state;
20991 this.inputEl().dom.checked = state;
20994 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20996 if(suppressEvent !== true){
20997 this.fireEvent('check', this, state);
21003 getValue : function()
21005 if(this.inputType == 'radio'){
21006 return this.getGroupValue();
21009 return this.hiddenEl().dom.value;
21013 getGroupValue : function()
21015 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21019 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21022 setValue : function(v,suppressEvent)
21024 if(this.inputType == 'radio'){
21025 this.setGroupValue(v, suppressEvent);
21029 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21034 setGroupValue : function(v, suppressEvent)
21036 this.startValue = this.getValue();
21038 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21039 e.dom.checked = false;
21041 if(e.dom.value == v){
21042 e.dom.checked = true;
21046 if(suppressEvent !== true){
21047 this.fireEvent('check', this, true);
21055 validate : function()
21057 if(this.getVisibilityEl().hasClass('hidden')){
21063 (this.inputType == 'radio' && this.validateRadio()) ||
21064 (this.inputType == 'checkbox' && this.validateCheckbox())
21070 this.markInvalid();
21074 validateRadio : function()
21076 if(this.getVisibilityEl().hasClass('hidden')){
21080 if(this.allowBlank){
21086 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21087 if(!e.dom.checked){
21099 validateCheckbox : function()
21102 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21103 //return (this.getValue() == this.inputValue) ? true : false;
21106 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21114 for(var i in group){
21115 if(group[i].el.isVisible(true)){
21123 for(var i in group){
21128 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21135 * Mark this field as valid
21137 markValid : function()
21141 this.fireEvent('valid', this);
21143 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21146 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21153 if(this.inputType == 'radio'){
21154 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21155 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21156 e.findParent('.form-group', false, true).addClass(_this.validClass);
21163 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21164 this.el.findParent('.form-group', false, true).addClass(this.validClass);
21168 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21174 for(var i in group){
21175 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21176 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21181 * Mark this field as invalid
21182 * @param {String} msg The validation message
21184 markInvalid : function(msg)
21186 if(this.allowBlank){
21192 this.fireEvent('invalid', this, msg);
21194 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21197 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21201 label.markInvalid();
21204 if(this.inputType == 'radio'){
21205 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21206 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21207 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21214 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21215 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21219 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21225 for(var i in group){
21226 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21227 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21232 clearInvalid : function()
21234 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21236 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21238 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21240 if (label && label.iconEl) {
21241 label.iconEl.removeClass(label.validClass);
21242 label.iconEl.removeClass(label.invalidClass);
21246 disable : function()
21248 if(this.inputType != 'radio'){
21249 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21256 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21257 _this.getActionEl().addClass(this.disabledClass);
21258 e.dom.disabled = true;
21262 this.disabled = true;
21263 this.fireEvent("disable", this);
21267 enable : function()
21269 if(this.inputType != 'radio'){
21270 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21277 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21278 _this.getActionEl().removeClass(this.disabledClass);
21279 e.dom.disabled = false;
21283 this.disabled = false;
21284 this.fireEvent("enable", this);
21288 setBoxLabel : function(v)
21293 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21299 Roo.apply(Roo.bootstrap.CheckBox, {
21304 * register a CheckBox Group
21305 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21307 register : function(checkbox)
21309 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21310 this.groups[checkbox.groupId] = {};
21313 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21317 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21321 * fetch a CheckBox Group based on the group ID
21322 * @param {string} the group ID
21323 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21325 get: function(groupId) {
21326 if (typeof(this.groups[groupId]) == 'undefined') {
21330 return this.groups[groupId] ;
21343 * @class Roo.bootstrap.Radio
21344 * @extends Roo.bootstrap.Component
21345 * Bootstrap Radio class
21346 * @cfg {String} boxLabel - the label associated
21347 * @cfg {String} value - the value of radio
21350 * Create a new Radio
21351 * @param {Object} config The config object
21353 Roo.bootstrap.Radio = function(config){
21354 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21358 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21364 getAutoCreate : function()
21368 cls : 'form-group radio',
21373 html : this.boxLabel
21381 initEvents : function()
21383 this.parent().register(this);
21385 this.el.on('click', this.onClick, this);
21389 onClick : function(e)
21391 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21392 this.setChecked(true);
21396 setChecked : function(state, suppressEvent)
21398 this.parent().setValue(this.value, suppressEvent);
21402 setBoxLabel : function(v)
21407 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21422 * @class Roo.bootstrap.SecurePass
21423 * @extends Roo.bootstrap.Input
21424 * Bootstrap SecurePass class
21428 * Create a new SecurePass
21429 * @param {Object} config The config object
21432 Roo.bootstrap.SecurePass = function (config) {
21433 // these go here, so the translation tool can replace them..
21435 PwdEmpty: "Please type a password, and then retype it to confirm.",
21436 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21437 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21438 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21439 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21440 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21441 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21442 TooWeak: "Your password is Too Weak."
21444 this.meterLabel = "Password strength:";
21445 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21446 this.meterClass = [
21447 "roo-password-meter-tooweak",
21448 "roo-password-meter-weak",
21449 "roo-password-meter-medium",
21450 "roo-password-meter-strong",
21451 "roo-password-meter-grey"
21456 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21459 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21461 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21463 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21464 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21465 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21466 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21467 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21468 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21469 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21479 * @cfg {String/Object} Label for the strength meter (defaults to
21480 * 'Password strength:')
21485 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21486 * ['Weak', 'Medium', 'Strong'])
21489 pwdStrengths: false,
21502 initEvents: function ()
21504 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21506 if (this.el.is('input[type=password]') && Roo.isSafari) {
21507 this.el.on('keydown', this.SafariOnKeyDown, this);
21510 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21513 onRender: function (ct, position)
21515 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21516 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21517 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21519 this.trigger.createChild({
21524 cls: 'roo-password-meter-grey col-xs-12',
21527 //width: this.meterWidth + 'px'
21531 cls: 'roo-password-meter-text'
21537 if (this.hideTrigger) {
21538 this.trigger.setDisplayed(false);
21540 this.setSize(this.width || '', this.height || '');
21543 onDestroy: function ()
21545 if (this.trigger) {
21546 this.trigger.removeAllListeners();
21547 this.trigger.remove();
21550 this.wrap.remove();
21552 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21555 checkStrength: function ()
21557 var pwd = this.inputEl().getValue();
21558 if (pwd == this._lastPwd) {
21563 if (this.ClientSideStrongPassword(pwd)) {
21565 } else if (this.ClientSideMediumPassword(pwd)) {
21567 } else if (this.ClientSideWeakPassword(pwd)) {
21573 Roo.log('strength1: ' + strength);
21575 //var pm = this.trigger.child('div/div/div').dom;
21576 var pm = this.trigger.child('div/div');
21577 pm.removeClass(this.meterClass);
21578 pm.addClass(this.meterClass[strength]);
21581 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21583 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21585 this._lastPwd = pwd;
21589 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21591 this._lastPwd = '';
21593 var pm = this.trigger.child('div/div');
21594 pm.removeClass(this.meterClass);
21595 pm.addClass('roo-password-meter-grey');
21598 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21601 this.inputEl().dom.type='password';
21604 validateValue: function (value)
21607 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21610 if (value.length == 0) {
21611 if (this.allowBlank) {
21612 this.clearInvalid();
21616 this.markInvalid(this.errors.PwdEmpty);
21617 this.errorMsg = this.errors.PwdEmpty;
21625 if ('[\x21-\x7e]*'.match(value)) {
21626 this.markInvalid(this.errors.PwdBadChar);
21627 this.errorMsg = this.errors.PwdBadChar;
21630 if (value.length < 6) {
21631 this.markInvalid(this.errors.PwdShort);
21632 this.errorMsg = this.errors.PwdShort;
21635 if (value.length > 16) {
21636 this.markInvalid(this.errors.PwdLong);
21637 this.errorMsg = this.errors.PwdLong;
21641 if (this.ClientSideStrongPassword(value)) {
21643 } else if (this.ClientSideMediumPassword(value)) {
21645 } else if (this.ClientSideWeakPassword(value)) {
21652 if (strength < 2) {
21653 //this.markInvalid(this.errors.TooWeak);
21654 this.errorMsg = this.errors.TooWeak;
21659 console.log('strength2: ' + strength);
21661 //var pm = this.trigger.child('div/div/div').dom;
21663 var pm = this.trigger.child('div/div');
21664 pm.removeClass(this.meterClass);
21665 pm.addClass(this.meterClass[strength]);
21667 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21669 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21671 this.errorMsg = '';
21675 CharacterSetChecks: function (type)
21678 this.fResult = false;
21681 isctype: function (character, type)
21684 case this.kCapitalLetter:
21685 if (character >= 'A' && character <= 'Z') {
21690 case this.kSmallLetter:
21691 if (character >= 'a' && character <= 'z') {
21697 if (character >= '0' && character <= '9') {
21702 case this.kPunctuation:
21703 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21714 IsLongEnough: function (pwd, size)
21716 return !(pwd == null || isNaN(size) || pwd.length < size);
21719 SpansEnoughCharacterSets: function (word, nb)
21721 if (!this.IsLongEnough(word, nb))
21726 var characterSetChecks = new Array(
21727 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21728 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21731 for (var index = 0; index < word.length; ++index) {
21732 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21733 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21734 characterSetChecks[nCharSet].fResult = true;
21741 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21742 if (characterSetChecks[nCharSet].fResult) {
21747 if (nCharSets < nb) {
21753 ClientSideStrongPassword: function (pwd)
21755 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21758 ClientSideMediumPassword: function (pwd)
21760 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21763 ClientSideWeakPassword: function (pwd)
21765 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21768 })//<script type="text/javascript">
21771 * Based Ext JS Library 1.1.1
21772 * Copyright(c) 2006-2007, Ext JS, LLC.
21778 * @class Roo.HtmlEditorCore
21779 * @extends Roo.Component
21780 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21782 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21785 Roo.HtmlEditorCore = function(config){
21788 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21793 * @event initialize
21794 * Fires when the editor is fully initialized (including the iframe)
21795 * @param {Roo.HtmlEditorCore} this
21800 * Fires when the editor is first receives the focus. Any insertion must wait
21801 * until after this event.
21802 * @param {Roo.HtmlEditorCore} this
21806 * @event beforesync
21807 * Fires before the textarea is updated with content from the editor iframe. Return false
21808 * to cancel the sync.
21809 * @param {Roo.HtmlEditorCore} this
21810 * @param {String} html
21814 * @event beforepush
21815 * Fires before the iframe editor is updated with content from the textarea. Return false
21816 * to cancel the push.
21817 * @param {Roo.HtmlEditorCore} this
21818 * @param {String} html
21823 * Fires when the textarea is updated with content from the editor iframe.
21824 * @param {Roo.HtmlEditorCore} this
21825 * @param {String} html
21830 * Fires when the iframe editor is updated with content from the textarea.
21831 * @param {Roo.HtmlEditorCore} this
21832 * @param {String} html
21837 * @event editorevent
21838 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21839 * @param {Roo.HtmlEditorCore} this
21845 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21847 // defaults : white / black...
21848 this.applyBlacklists();
21855 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21859 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21865 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21870 * @cfg {Number} height (in pixels)
21874 * @cfg {Number} width (in pixels)
21879 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21882 stylesheets: false,
21887 // private properties
21888 validationEvent : false,
21890 initialized : false,
21892 sourceEditMode : false,
21893 onFocus : Roo.emptyFn,
21895 hideMode:'offsets',
21899 // blacklist + whitelisted elements..
21906 * Protected method that will not generally be called directly. It
21907 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21908 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21910 getDocMarkup : function(){
21914 // inherit styels from page...??
21915 if (this.stylesheets === false) {
21917 Roo.get(document.head).select('style').each(function(node) {
21918 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21921 Roo.get(document.head).select('link').each(function(node) {
21922 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21925 } else if (!this.stylesheets.length) {
21927 st = '<style type="text/css">' +
21928 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21931 st = '<style type="text/css">' +
21936 st += '<style type="text/css">' +
21937 'IMG { cursor: pointer } ' +
21940 var cls = 'roo-htmleditor-body';
21942 if(this.bodyCls.length){
21943 cls += ' ' + this.bodyCls;
21946 return '<html><head>' + st +
21947 //<style type="text/css">' +
21948 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21950 ' </head><body class="' + cls + '"></body></html>';
21954 onRender : function(ct, position)
21957 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21958 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21961 this.el.dom.style.border = '0 none';
21962 this.el.dom.setAttribute('tabIndex', -1);
21963 this.el.addClass('x-hidden hide');
21967 if(Roo.isIE){ // fix IE 1px bogus margin
21968 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21972 this.frameId = Roo.id();
21976 var iframe = this.owner.wrap.createChild({
21978 cls: 'form-control', // bootstrap..
21980 name: this.frameId,
21981 frameBorder : 'no',
21982 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21987 this.iframe = iframe.dom;
21989 this.assignDocWin();
21991 this.doc.designMode = 'on';
21994 this.doc.write(this.getDocMarkup());
21998 var task = { // must defer to wait for browser to be ready
22000 //console.log("run task?" + this.doc.readyState);
22001 this.assignDocWin();
22002 if(this.doc.body || this.doc.readyState == 'complete'){
22004 this.doc.designMode="on";
22008 Roo.TaskMgr.stop(task);
22009 this.initEditor.defer(10, this);
22016 Roo.TaskMgr.start(task);
22021 onResize : function(w, h)
22023 Roo.log('resize: ' +w + ',' + h );
22024 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22028 if(typeof w == 'number'){
22030 this.iframe.style.width = w + 'px';
22032 if(typeof h == 'number'){
22034 this.iframe.style.height = h + 'px';
22036 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22043 * Toggles the editor between standard and source edit mode.
22044 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22046 toggleSourceEdit : function(sourceEditMode){
22048 this.sourceEditMode = sourceEditMode === true;
22050 if(this.sourceEditMode){
22052 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
22055 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22056 //this.iframe.className = '';
22059 //this.setSize(this.owner.wrap.getSize());
22060 //this.fireEvent('editmodechange', this, this.sourceEditMode);
22067 * Protected method that will not generally be called directly. If you need/want
22068 * custom HTML cleanup, this is the method you should override.
22069 * @param {String} html The HTML to be cleaned
22070 * return {String} The cleaned HTML
22072 cleanHtml : function(html){
22073 html = String(html);
22074 if(html.length > 5){
22075 if(Roo.isSafari){ // strip safari nonsense
22076 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22079 if(html == ' '){
22086 * HTML Editor -> Textarea
22087 * Protected method that will not generally be called directly. Syncs the contents
22088 * of the editor iframe with the textarea.
22090 syncValue : function(){
22091 if(this.initialized){
22092 var bd = (this.doc.body || this.doc.documentElement);
22093 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22094 var html = bd.innerHTML;
22096 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22097 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22099 html = '<div style="'+m[0]+'">' + html + '</div>';
22102 html = this.cleanHtml(html);
22103 // fix up the special chars.. normaly like back quotes in word...
22104 // however we do not want to do this with chinese..
22105 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22106 var cc = b.charCodeAt();
22108 (cc >= 0x4E00 && cc < 0xA000 ) ||
22109 (cc >= 0x3400 && cc < 0x4E00 ) ||
22110 (cc >= 0xf900 && cc < 0xfb00 )
22116 if(this.owner.fireEvent('beforesync', this, html) !== false){
22117 this.el.dom.value = html;
22118 this.owner.fireEvent('sync', this, html);
22124 * Protected method that will not generally be called directly. Pushes the value of the textarea
22125 * into the iframe editor.
22127 pushValue : function(){
22128 if(this.initialized){
22129 var v = this.el.dom.value.trim();
22131 // if(v.length < 1){
22135 if(this.owner.fireEvent('beforepush', this, v) !== false){
22136 var d = (this.doc.body || this.doc.documentElement);
22138 this.cleanUpPaste();
22139 this.el.dom.value = d.innerHTML;
22140 this.owner.fireEvent('push', this, v);
22146 deferFocus : function(){
22147 this.focus.defer(10, this);
22151 focus : function(){
22152 if(this.win && !this.sourceEditMode){
22159 assignDocWin: function()
22161 var iframe = this.iframe;
22164 this.doc = iframe.contentWindow.document;
22165 this.win = iframe.contentWindow;
22167 // if (!Roo.get(this.frameId)) {
22170 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22171 // this.win = Roo.get(this.frameId).dom.contentWindow;
22173 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22177 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22178 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22183 initEditor : function(){
22184 //console.log("INIT EDITOR");
22185 this.assignDocWin();
22189 this.doc.designMode="on";
22191 this.doc.write(this.getDocMarkup());
22194 var dbody = (this.doc.body || this.doc.documentElement);
22195 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22196 // this copies styles from the containing element into thsi one..
22197 // not sure why we need all of this..
22198 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22200 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22201 //ss['background-attachment'] = 'fixed'; // w3c
22202 dbody.bgProperties = 'fixed'; // ie
22203 //Roo.DomHelper.applyStyles(dbody, ss);
22204 Roo.EventManager.on(this.doc, {
22205 //'mousedown': this.onEditorEvent,
22206 'mouseup': this.onEditorEvent,
22207 'dblclick': this.onEditorEvent,
22208 'click': this.onEditorEvent,
22209 'keyup': this.onEditorEvent,
22214 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22216 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22217 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22219 this.initialized = true;
22221 this.owner.fireEvent('initialize', this);
22226 onDestroy : function(){
22232 //for (var i =0; i < this.toolbars.length;i++) {
22233 // // fixme - ask toolbars for heights?
22234 // this.toolbars[i].onDestroy();
22237 //this.wrap.dom.innerHTML = '';
22238 //this.wrap.remove();
22243 onFirstFocus : function(){
22245 this.assignDocWin();
22248 this.activated = true;
22251 if(Roo.isGecko){ // prevent silly gecko errors
22253 var s = this.win.getSelection();
22254 if(!s.focusNode || s.focusNode.nodeType != 3){
22255 var r = s.getRangeAt(0);
22256 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22261 this.execCmd('useCSS', true);
22262 this.execCmd('styleWithCSS', false);
22265 this.owner.fireEvent('activate', this);
22269 adjustFont: function(btn){
22270 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22271 //if(Roo.isSafari){ // safari
22274 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22275 if(Roo.isSafari){ // safari
22276 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22277 v = (v < 10) ? 10 : v;
22278 v = (v > 48) ? 48 : v;
22279 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22284 v = Math.max(1, v+adjust);
22286 this.execCmd('FontSize', v );
22289 onEditorEvent : function(e)
22291 this.owner.fireEvent('editorevent', this, e);
22292 // this.updateToolbar();
22293 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22296 insertTag : function(tg)
22298 // could be a bit smarter... -> wrap the current selected tRoo..
22299 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22301 range = this.createRange(this.getSelection());
22302 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22303 wrappingNode.appendChild(range.extractContents());
22304 range.insertNode(wrappingNode);
22311 this.execCmd("formatblock", tg);
22315 insertText : function(txt)
22319 var range = this.createRange();
22320 range.deleteContents();
22321 //alert(Sender.getAttribute('label'));
22323 range.insertNode(this.doc.createTextNode(txt));
22329 * Executes a Midas editor command on the editor document and performs necessary focus and
22330 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22331 * @param {String} cmd The Midas command
22332 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22334 relayCmd : function(cmd, value){
22336 this.execCmd(cmd, value);
22337 this.owner.fireEvent('editorevent', this);
22338 //this.updateToolbar();
22339 this.owner.deferFocus();
22343 * Executes a Midas editor command directly on the editor document.
22344 * For visual commands, you should use {@link #relayCmd} instead.
22345 * <b>This should only be called after the editor is initialized.</b>
22346 * @param {String} cmd The Midas command
22347 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22349 execCmd : function(cmd, value){
22350 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22357 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22359 * @param {String} text | dom node..
22361 insertAtCursor : function(text)
22364 if(!this.activated){
22370 var r = this.doc.selection.createRange();
22381 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22385 // from jquery ui (MIT licenced)
22387 var win = this.win;
22389 if (win.getSelection && win.getSelection().getRangeAt) {
22390 range = win.getSelection().getRangeAt(0);
22391 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22392 range.insertNode(node);
22393 } else if (win.document.selection && win.document.selection.createRange) {
22394 // no firefox support
22395 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22396 win.document.selection.createRange().pasteHTML(txt);
22398 // no firefox support
22399 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22400 this.execCmd('InsertHTML', txt);
22409 mozKeyPress : function(e){
22411 var c = e.getCharCode(), cmd;
22414 c = String.fromCharCode(c).toLowerCase();
22428 this.cleanUpPaste.defer(100, this);
22436 e.preventDefault();
22444 fixKeys : function(){ // load time branching for fastest keydown performance
22446 return function(e){
22447 var k = e.getKey(), r;
22450 r = this.doc.selection.createRange();
22453 r.pasteHTML('    ');
22460 r = this.doc.selection.createRange();
22462 var target = r.parentElement();
22463 if(!target || target.tagName.toLowerCase() != 'li'){
22465 r.pasteHTML('<br />');
22471 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22472 this.cleanUpPaste.defer(100, this);
22478 }else if(Roo.isOpera){
22479 return function(e){
22480 var k = e.getKey();
22484 this.execCmd('InsertHTML','    ');
22487 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22488 this.cleanUpPaste.defer(100, this);
22493 }else if(Roo.isSafari){
22494 return function(e){
22495 var k = e.getKey();
22499 this.execCmd('InsertText','\t');
22503 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22504 this.cleanUpPaste.defer(100, this);
22512 getAllAncestors: function()
22514 var p = this.getSelectedNode();
22517 a.push(p); // push blank onto stack..
22518 p = this.getParentElement();
22522 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22526 a.push(this.doc.body);
22530 lastSelNode : false,
22533 getSelection : function()
22535 this.assignDocWin();
22536 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22539 getSelectedNode: function()
22541 // this may only work on Gecko!!!
22543 // should we cache this!!!!
22548 var range = this.createRange(this.getSelection()).cloneRange();
22551 var parent = range.parentElement();
22553 var testRange = range.duplicate();
22554 testRange.moveToElementText(parent);
22555 if (testRange.inRange(range)) {
22558 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22561 parent = parent.parentElement;
22566 // is ancestor a text element.
22567 var ac = range.commonAncestorContainer;
22568 if (ac.nodeType == 3) {
22569 ac = ac.parentNode;
22572 var ar = ac.childNodes;
22575 var other_nodes = [];
22576 var has_other_nodes = false;
22577 for (var i=0;i<ar.length;i++) {
22578 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22581 // fullly contained node.
22583 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22588 // probably selected..
22589 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22590 other_nodes.push(ar[i]);
22594 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22599 has_other_nodes = true;
22601 if (!nodes.length && other_nodes.length) {
22602 nodes= other_nodes;
22604 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22610 createRange: function(sel)
22612 // this has strange effects when using with
22613 // top toolbar - not sure if it's a great idea.
22614 //this.editor.contentWindow.focus();
22615 if (typeof sel != "undefined") {
22617 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22619 return this.doc.createRange();
22622 return this.doc.createRange();
22625 getParentElement: function()
22628 this.assignDocWin();
22629 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22631 var range = this.createRange(sel);
22634 var p = range.commonAncestorContainer;
22635 while (p.nodeType == 3) { // text node
22646 * Range intersection.. the hard stuff...
22650 * [ -- selected range --- ]
22654 * if end is before start or hits it. fail.
22655 * if start is after end or hits it fail.
22657 * if either hits (but other is outside. - then it's not
22663 // @see http://www.thismuchiknow.co.uk/?p=64.
22664 rangeIntersectsNode : function(range, node)
22666 var nodeRange = node.ownerDocument.createRange();
22668 nodeRange.selectNode(node);
22670 nodeRange.selectNodeContents(node);
22673 var rangeStartRange = range.cloneRange();
22674 rangeStartRange.collapse(true);
22676 var rangeEndRange = range.cloneRange();
22677 rangeEndRange.collapse(false);
22679 var nodeStartRange = nodeRange.cloneRange();
22680 nodeStartRange.collapse(true);
22682 var nodeEndRange = nodeRange.cloneRange();
22683 nodeEndRange.collapse(false);
22685 return rangeStartRange.compareBoundaryPoints(
22686 Range.START_TO_START, nodeEndRange) == -1 &&
22687 rangeEndRange.compareBoundaryPoints(
22688 Range.START_TO_START, nodeStartRange) == 1;
22692 rangeCompareNode : function(range, node)
22694 var nodeRange = node.ownerDocument.createRange();
22696 nodeRange.selectNode(node);
22698 nodeRange.selectNodeContents(node);
22702 range.collapse(true);
22704 nodeRange.collapse(true);
22706 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22707 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22709 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22711 var nodeIsBefore = ss == 1;
22712 var nodeIsAfter = ee == -1;
22714 if (nodeIsBefore && nodeIsAfter) {
22717 if (!nodeIsBefore && nodeIsAfter) {
22718 return 1; //right trailed.
22721 if (nodeIsBefore && !nodeIsAfter) {
22722 return 2; // left trailed.
22728 // private? - in a new class?
22729 cleanUpPaste : function()
22731 // cleans up the whole document..
22732 Roo.log('cleanuppaste');
22734 this.cleanUpChildren(this.doc.body);
22735 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22736 if (clean != this.doc.body.innerHTML) {
22737 this.doc.body.innerHTML = clean;
22742 cleanWordChars : function(input) {// change the chars to hex code
22743 var he = Roo.HtmlEditorCore;
22745 var output = input;
22746 Roo.each(he.swapCodes, function(sw) {
22747 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22749 output = output.replace(swapper, sw[1]);
22756 cleanUpChildren : function (n)
22758 if (!n.childNodes.length) {
22761 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22762 this.cleanUpChild(n.childNodes[i]);
22769 cleanUpChild : function (node)
22772 //console.log(node);
22773 if (node.nodeName == "#text") {
22774 // clean up silly Windows -- stuff?
22777 if (node.nodeName == "#comment") {
22778 node.parentNode.removeChild(node);
22779 // clean up silly Windows -- stuff?
22782 var lcname = node.tagName.toLowerCase();
22783 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22784 // whitelist of tags..
22786 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22788 node.parentNode.removeChild(node);
22793 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22795 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22796 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22798 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22799 // remove_keep_children = true;
22802 if (remove_keep_children) {
22803 this.cleanUpChildren(node);
22804 // inserts everything just before this node...
22805 while (node.childNodes.length) {
22806 var cn = node.childNodes[0];
22807 node.removeChild(cn);
22808 node.parentNode.insertBefore(cn, node);
22810 node.parentNode.removeChild(node);
22814 if (!node.attributes || !node.attributes.length) {
22815 this.cleanUpChildren(node);
22819 function cleanAttr(n,v)
22822 if (v.match(/^\./) || v.match(/^\//)) {
22825 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22828 if (v.match(/^#/)) {
22831 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22832 node.removeAttribute(n);
22836 var cwhite = this.cwhite;
22837 var cblack = this.cblack;
22839 function cleanStyle(n,v)
22841 if (v.match(/expression/)) { //XSS?? should we even bother..
22842 node.removeAttribute(n);
22846 var parts = v.split(/;/);
22849 Roo.each(parts, function(p) {
22850 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22854 var l = p.split(':').shift().replace(/\s+/g,'');
22855 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22857 if ( cwhite.length && cblack.indexOf(l) > -1) {
22858 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22859 //node.removeAttribute(n);
22863 // only allow 'c whitelisted system attributes'
22864 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22865 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22866 //node.removeAttribute(n);
22876 if (clean.length) {
22877 node.setAttribute(n, clean.join(';'));
22879 node.removeAttribute(n);
22885 for (var i = node.attributes.length-1; i > -1 ; i--) {
22886 var a = node.attributes[i];
22889 if (a.name.toLowerCase().substr(0,2)=='on') {
22890 node.removeAttribute(a.name);
22893 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22894 node.removeAttribute(a.name);
22897 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22898 cleanAttr(a.name,a.value); // fixme..
22901 if (a.name == 'style') {
22902 cleanStyle(a.name,a.value);
22905 /// clean up MS crap..
22906 // tecnically this should be a list of valid class'es..
22909 if (a.name == 'class') {
22910 if (a.value.match(/^Mso/)) {
22911 node.className = '';
22914 if (a.value.match(/^body$/)) {
22915 node.className = '';
22926 this.cleanUpChildren(node);
22932 * Clean up MS wordisms...
22934 cleanWord : function(node)
22939 this.cleanWord(this.doc.body);
22942 if (node.nodeName == "#text") {
22943 // clean up silly Windows -- stuff?
22946 if (node.nodeName == "#comment") {
22947 node.parentNode.removeChild(node);
22948 // clean up silly Windows -- stuff?
22952 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22953 node.parentNode.removeChild(node);
22957 // remove - but keep children..
22958 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22959 while (node.childNodes.length) {
22960 var cn = node.childNodes[0];
22961 node.removeChild(cn);
22962 node.parentNode.insertBefore(cn, node);
22964 node.parentNode.removeChild(node);
22965 this.iterateChildren(node, this.cleanWord);
22969 if (node.className.length) {
22971 var cn = node.className.split(/\W+/);
22973 Roo.each(cn, function(cls) {
22974 if (cls.match(/Mso[a-zA-Z]+/)) {
22979 node.className = cna.length ? cna.join(' ') : '';
22981 node.removeAttribute("class");
22985 if (node.hasAttribute("lang")) {
22986 node.removeAttribute("lang");
22989 if (node.hasAttribute("style")) {
22991 var styles = node.getAttribute("style").split(";");
22993 Roo.each(styles, function(s) {
22994 if (!s.match(/:/)) {
22997 var kv = s.split(":");
22998 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23001 // what ever is left... we allow.
23004 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23005 if (!nstyle.length) {
23006 node.removeAttribute('style');
23009 this.iterateChildren(node, this.cleanWord);
23015 * iterateChildren of a Node, calling fn each time, using this as the scole..
23016 * @param {DomNode} node node to iterate children of.
23017 * @param {Function} fn method of this class to call on each item.
23019 iterateChildren : function(node, fn)
23021 if (!node.childNodes.length) {
23024 for (var i = node.childNodes.length-1; i > -1 ; i--) {
23025 fn.call(this, node.childNodes[i])
23031 * cleanTableWidths.
23033 * Quite often pasting from word etc.. results in tables with column and widths.
23034 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23037 cleanTableWidths : function(node)
23042 this.cleanTableWidths(this.doc.body);
23047 if (node.nodeName == "#text" || node.nodeName == "#comment") {
23050 Roo.log(node.tagName);
23051 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23052 this.iterateChildren(node, this.cleanTableWidths);
23055 if (node.hasAttribute('width')) {
23056 node.removeAttribute('width');
23060 if (node.hasAttribute("style")) {
23063 var styles = node.getAttribute("style").split(";");
23065 Roo.each(styles, function(s) {
23066 if (!s.match(/:/)) {
23069 var kv = s.split(":");
23070 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23073 // what ever is left... we allow.
23076 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23077 if (!nstyle.length) {
23078 node.removeAttribute('style');
23082 this.iterateChildren(node, this.cleanTableWidths);
23090 domToHTML : function(currentElement, depth, nopadtext) {
23092 depth = depth || 0;
23093 nopadtext = nopadtext || false;
23095 if (!currentElement) {
23096 return this.domToHTML(this.doc.body);
23099 //Roo.log(currentElement);
23101 var allText = false;
23102 var nodeName = currentElement.nodeName;
23103 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23105 if (nodeName == '#text') {
23107 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23112 if (nodeName != 'BODY') {
23115 // Prints the node tagName, such as <A>, <IMG>, etc
23118 for(i = 0; i < currentElement.attributes.length;i++) {
23120 var aname = currentElement.attributes.item(i).name;
23121 if (!currentElement.attributes.item(i).value.length) {
23124 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23127 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23136 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23139 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23144 // Traverse the tree
23146 var currentElementChild = currentElement.childNodes.item(i);
23147 var allText = true;
23148 var innerHTML = '';
23150 while (currentElementChild) {
23151 // Formatting code (indent the tree so it looks nice on the screen)
23152 var nopad = nopadtext;
23153 if (lastnode == 'SPAN') {
23157 if (currentElementChild.nodeName == '#text') {
23158 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23159 toadd = nopadtext ? toadd : toadd.trim();
23160 if (!nopad && toadd.length > 80) {
23161 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23163 innerHTML += toadd;
23166 currentElementChild = currentElement.childNodes.item(i);
23172 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23174 // Recursively traverse the tree structure of the child node
23175 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23176 lastnode = currentElementChild.nodeName;
23178 currentElementChild=currentElement.childNodes.item(i);
23184 // The remaining code is mostly for formatting the tree
23185 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23190 ret+= "</"+tagName+">";
23196 applyBlacklists : function()
23198 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23199 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23203 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23204 if (b.indexOf(tag) > -1) {
23207 this.white.push(tag);
23211 Roo.each(w, function(tag) {
23212 if (b.indexOf(tag) > -1) {
23215 if (this.white.indexOf(tag) > -1) {
23218 this.white.push(tag);
23223 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23224 if (w.indexOf(tag) > -1) {
23227 this.black.push(tag);
23231 Roo.each(b, function(tag) {
23232 if (w.indexOf(tag) > -1) {
23235 if (this.black.indexOf(tag) > -1) {
23238 this.black.push(tag);
23243 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23244 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23248 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23249 if (b.indexOf(tag) > -1) {
23252 this.cwhite.push(tag);
23256 Roo.each(w, function(tag) {
23257 if (b.indexOf(tag) > -1) {
23260 if (this.cwhite.indexOf(tag) > -1) {
23263 this.cwhite.push(tag);
23268 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23269 if (w.indexOf(tag) > -1) {
23272 this.cblack.push(tag);
23276 Roo.each(b, function(tag) {
23277 if (w.indexOf(tag) > -1) {
23280 if (this.cblack.indexOf(tag) > -1) {
23283 this.cblack.push(tag);
23288 setStylesheets : function(stylesheets)
23290 if(typeof(stylesheets) == 'string'){
23291 Roo.get(this.iframe.contentDocument.head).createChild({
23293 rel : 'stylesheet',
23302 Roo.each(stylesheets, function(s) {
23307 Roo.get(_this.iframe.contentDocument.head).createChild({
23309 rel : 'stylesheet',
23318 removeStylesheets : function()
23322 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23327 setStyle : function(style)
23329 Roo.get(this.iframe.contentDocument.head).createChild({
23338 // hide stuff that is not compatible
23352 * @event specialkey
23356 * @cfg {String} fieldClass @hide
23359 * @cfg {String} focusClass @hide
23362 * @cfg {String} autoCreate @hide
23365 * @cfg {String} inputType @hide
23368 * @cfg {String} invalidClass @hide
23371 * @cfg {String} invalidText @hide
23374 * @cfg {String} msgFx @hide
23377 * @cfg {String} validateOnBlur @hide
23381 Roo.HtmlEditorCore.white = [
23382 'area', 'br', 'img', 'input', 'hr', 'wbr',
23384 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23385 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23386 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23387 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23388 'table', 'ul', 'xmp',
23390 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23393 'dir', 'menu', 'ol', 'ul', 'dl',
23399 Roo.HtmlEditorCore.black = [
23400 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23402 'base', 'basefont', 'bgsound', 'blink', 'body',
23403 'frame', 'frameset', 'head', 'html', 'ilayer',
23404 'iframe', 'layer', 'link', 'meta', 'object',
23405 'script', 'style' ,'title', 'xml' // clean later..
23407 Roo.HtmlEditorCore.clean = [
23408 'script', 'style', 'title', 'xml'
23410 Roo.HtmlEditorCore.remove = [
23415 Roo.HtmlEditorCore.ablack = [
23419 Roo.HtmlEditorCore.aclean = [
23420 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23424 Roo.HtmlEditorCore.pwhite= [
23425 'http', 'https', 'mailto'
23428 // white listed style attributes.
23429 Roo.HtmlEditorCore.cwhite= [
23430 // 'text-align', /// default is to allow most things..
23436 // black listed style attributes.
23437 Roo.HtmlEditorCore.cblack= [
23438 // 'font-size' -- this can be set by the project
23442 Roo.HtmlEditorCore.swapCodes =[
23461 * @class Roo.bootstrap.HtmlEditor
23462 * @extends Roo.bootstrap.TextArea
23463 * Bootstrap HtmlEditor class
23466 * Create a new HtmlEditor
23467 * @param {Object} config The config object
23470 Roo.bootstrap.HtmlEditor = function(config){
23471 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23472 if (!this.toolbars) {
23473 this.toolbars = [];
23476 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23479 * @event initialize
23480 * Fires when the editor is fully initialized (including the iframe)
23481 * @param {HtmlEditor} this
23486 * Fires when the editor is first receives the focus. Any insertion must wait
23487 * until after this event.
23488 * @param {HtmlEditor} this
23492 * @event beforesync
23493 * Fires before the textarea is updated with content from the editor iframe. Return false
23494 * to cancel the sync.
23495 * @param {HtmlEditor} this
23496 * @param {String} html
23500 * @event beforepush
23501 * Fires before the iframe editor is updated with content from the textarea. Return false
23502 * to cancel the push.
23503 * @param {HtmlEditor} this
23504 * @param {String} html
23509 * Fires when the textarea is updated with content from the editor iframe.
23510 * @param {HtmlEditor} this
23511 * @param {String} html
23516 * Fires when the iframe editor is updated with content from the textarea.
23517 * @param {HtmlEditor} this
23518 * @param {String} html
23522 * @event editmodechange
23523 * Fires when the editor switches edit modes
23524 * @param {HtmlEditor} this
23525 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23527 editmodechange: true,
23529 * @event editorevent
23530 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23531 * @param {HtmlEditor} this
23535 * @event firstfocus
23536 * Fires when on first focus - needed by toolbars..
23537 * @param {HtmlEditor} this
23542 * Auto save the htmlEditor value as a file into Events
23543 * @param {HtmlEditor} this
23547 * @event savedpreview
23548 * preview the saved version of htmlEditor
23549 * @param {HtmlEditor} this
23556 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23560 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23565 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23570 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23575 * @cfg {Number} height (in pixels)
23579 * @cfg {Number} width (in pixels)
23584 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23587 stylesheets: false,
23592 // private properties
23593 validationEvent : false,
23595 initialized : false,
23598 onFocus : Roo.emptyFn,
23600 hideMode:'offsets',
23602 tbContainer : false,
23606 toolbarContainer :function() {
23607 return this.wrap.select('.x-html-editor-tb',true).first();
23611 * Protected method that will not generally be called directly. It
23612 * is called when the editor creates its toolbar. Override this method if you need to
23613 * add custom toolbar buttons.
23614 * @param {HtmlEditor} editor
23616 createToolbar : function(){
23617 Roo.log('renewing');
23618 Roo.log("create toolbars");
23620 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23621 this.toolbars[0].render(this.toolbarContainer());
23625 // if (!editor.toolbars || !editor.toolbars.length) {
23626 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23629 // for (var i =0 ; i < editor.toolbars.length;i++) {
23630 // editor.toolbars[i] = Roo.factory(
23631 // typeof(editor.toolbars[i]) == 'string' ?
23632 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23633 // Roo.bootstrap.HtmlEditor);
23634 // editor.toolbars[i].init(editor);
23640 onRender : function(ct, position)
23642 // Roo.log("Call onRender: " + this.xtype);
23644 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23646 this.wrap = this.inputEl().wrap({
23647 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23650 this.editorcore.onRender(ct, position);
23652 if (this.resizable) {
23653 this.resizeEl = new Roo.Resizable(this.wrap, {
23657 minHeight : this.height,
23658 height: this.height,
23659 handles : this.resizable,
23662 resize : function(r, w, h) {
23663 _t.onResize(w,h); // -something
23669 this.createToolbar(this);
23672 if(!this.width && this.resizable){
23673 this.setSize(this.wrap.getSize());
23675 if (this.resizeEl) {
23676 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23677 // should trigger onReize..
23683 onResize : function(w, h)
23685 Roo.log('resize: ' +w + ',' + h );
23686 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23690 if(this.inputEl() ){
23691 if(typeof w == 'number'){
23692 var aw = w - this.wrap.getFrameWidth('lr');
23693 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23696 if(typeof h == 'number'){
23697 var tbh = -11; // fixme it needs to tool bar size!
23698 for (var i =0; i < this.toolbars.length;i++) {
23699 // fixme - ask toolbars for heights?
23700 tbh += this.toolbars[i].el.getHeight();
23701 //if (this.toolbars[i].footer) {
23702 // tbh += this.toolbars[i].footer.el.getHeight();
23710 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23711 ah -= 5; // knock a few pixes off for look..
23712 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23716 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23717 this.editorcore.onResize(ew,eh);
23722 * Toggles the editor between standard and source edit mode.
23723 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23725 toggleSourceEdit : function(sourceEditMode)
23727 this.editorcore.toggleSourceEdit(sourceEditMode);
23729 if(this.editorcore.sourceEditMode){
23730 Roo.log('editor - showing textarea');
23733 // Roo.log(this.syncValue());
23735 this.inputEl().removeClass(['hide', 'x-hidden']);
23736 this.inputEl().dom.removeAttribute('tabIndex');
23737 this.inputEl().focus();
23739 Roo.log('editor - hiding textarea');
23741 // Roo.log(this.pushValue());
23744 this.inputEl().addClass(['hide', 'x-hidden']);
23745 this.inputEl().dom.setAttribute('tabIndex', -1);
23746 //this.deferFocus();
23749 if(this.resizable){
23750 this.setSize(this.wrap.getSize());
23753 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23756 // private (for BoxComponent)
23757 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23759 // private (for BoxComponent)
23760 getResizeEl : function(){
23764 // private (for BoxComponent)
23765 getPositionEl : function(){
23770 initEvents : function(){
23771 this.originalValue = this.getValue();
23775 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23778 // markInvalid : Roo.emptyFn,
23780 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23783 // clearInvalid : Roo.emptyFn,
23785 setValue : function(v){
23786 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23787 this.editorcore.pushValue();
23792 deferFocus : function(){
23793 this.focus.defer(10, this);
23797 focus : function(){
23798 this.editorcore.focus();
23804 onDestroy : function(){
23810 for (var i =0; i < this.toolbars.length;i++) {
23811 // fixme - ask toolbars for heights?
23812 this.toolbars[i].onDestroy();
23815 this.wrap.dom.innerHTML = '';
23816 this.wrap.remove();
23821 onFirstFocus : function(){
23822 //Roo.log("onFirstFocus");
23823 this.editorcore.onFirstFocus();
23824 for (var i =0; i < this.toolbars.length;i++) {
23825 this.toolbars[i].onFirstFocus();
23831 syncValue : function()
23833 this.editorcore.syncValue();
23836 pushValue : function()
23838 this.editorcore.pushValue();
23842 // hide stuff that is not compatible
23856 * @event specialkey
23860 * @cfg {String} fieldClass @hide
23863 * @cfg {String} focusClass @hide
23866 * @cfg {String} autoCreate @hide
23869 * @cfg {String} inputType @hide
23872 * @cfg {String} invalidClass @hide
23875 * @cfg {String} invalidText @hide
23878 * @cfg {String} msgFx @hide
23881 * @cfg {String} validateOnBlur @hide
23890 Roo.namespace('Roo.bootstrap.htmleditor');
23892 * @class Roo.bootstrap.HtmlEditorToolbar1
23897 new Roo.bootstrap.HtmlEditor({
23900 new Roo.bootstrap.HtmlEditorToolbar1({
23901 disable : { fonts: 1 , format: 1, ..., ... , ...],
23907 * @cfg {Object} disable List of elements to disable..
23908 * @cfg {Array} btns List of additional buttons.
23912 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23915 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23918 Roo.apply(this, config);
23920 // default disabled, based on 'good practice'..
23921 this.disable = this.disable || {};
23922 Roo.applyIf(this.disable, {
23925 specialElements : true
23927 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23929 this.editor = config.editor;
23930 this.editorcore = config.editor.editorcore;
23932 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23934 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23935 // dont call parent... till later.
23937 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23942 editorcore : false,
23947 "h1","h2","h3","h4","h5","h6",
23949 "abbr", "acronym", "address", "cite", "samp", "var",
23953 onRender : function(ct, position)
23955 // Roo.log("Call onRender: " + this.xtype);
23957 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23959 this.el.dom.style.marginBottom = '0';
23961 var editorcore = this.editorcore;
23962 var editor= this.editor;
23965 var btn = function(id,cmd , toggle, handler, html){
23967 var event = toggle ? 'toggle' : 'click';
23972 xns: Roo.bootstrap,
23976 enableToggle:toggle !== false,
23978 pressed : toggle ? false : null,
23981 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23982 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23988 // var cb_box = function...
23993 xns: Roo.bootstrap,
23998 xns: Roo.bootstrap,
24002 Roo.each(this.formats, function(f) {
24003 style.menu.items.push({
24005 xns: Roo.bootstrap,
24006 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24011 editorcore.insertTag(this.tagname);
24018 children.push(style);
24020 btn('bold',false,true);
24021 btn('italic',false,true);
24022 btn('align-left', 'justifyleft',true);
24023 btn('align-center', 'justifycenter',true);
24024 btn('align-right' , 'justifyright',true);
24025 btn('link', false, false, function(btn) {
24026 //Roo.log("create link?");
24027 var url = prompt(this.createLinkText, this.defaultLinkValue);
24028 if(url && url != 'http:/'+'/'){
24029 this.editorcore.relayCmd('createlink', url);
24032 btn('list','insertunorderedlist',true);
24033 btn('pencil', false,true, function(btn){
24035 this.toggleSourceEdit(btn.pressed);
24038 if (this.editor.btns.length > 0) {
24039 for (var i = 0; i<this.editor.btns.length; i++) {
24040 children.push(this.editor.btns[i]);
24048 xns: Roo.bootstrap,
24053 xns: Roo.bootstrap,
24058 cog.menu.items.push({
24060 xns: Roo.bootstrap,
24061 html : Clean styles,
24066 editorcore.insertTag(this.tagname);
24075 this.xtype = 'NavSimplebar';
24077 for(var i=0;i< children.length;i++) {
24079 this.buttons.add(this.addxtypeChild(children[i]));
24083 editor.on('editorevent', this.updateToolbar, this);
24085 onBtnClick : function(id)
24087 this.editorcore.relayCmd(id);
24088 this.editorcore.focus();
24092 * Protected method that will not generally be called directly. It triggers
24093 * a toolbar update by reading the markup state of the current selection in the editor.
24095 updateToolbar: function(){
24097 if(!this.editorcore.activated){
24098 this.editor.onFirstFocus(); // is this neeed?
24102 var btns = this.buttons;
24103 var doc = this.editorcore.doc;
24104 btns.get('bold').setActive(doc.queryCommandState('bold'));
24105 btns.get('italic').setActive(doc.queryCommandState('italic'));
24106 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24108 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24109 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24110 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24112 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24113 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24116 var ans = this.editorcore.getAllAncestors();
24117 if (this.formatCombo) {
24120 var store = this.formatCombo.store;
24121 this.formatCombo.setValue("");
24122 for (var i =0; i < ans.length;i++) {
24123 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24125 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24133 // hides menus... - so this cant be on a menu...
24134 Roo.bootstrap.MenuMgr.hideAll();
24136 Roo.bootstrap.MenuMgr.hideAll();
24137 //this.editorsyncValue();
24139 onFirstFocus: function() {
24140 this.buttons.each(function(item){
24144 toggleSourceEdit : function(sourceEditMode){
24147 if(sourceEditMode){
24148 Roo.log("disabling buttons");
24149 this.buttons.each( function(item){
24150 if(item.cmd != 'pencil'){
24156 Roo.log("enabling buttons");
24157 if(this.editorcore.initialized){
24158 this.buttons.each( function(item){
24164 Roo.log("calling toggole on editor");
24165 // tell the editor that it's been pressed..
24166 this.editor.toggleSourceEdit(sourceEditMode);
24176 * @class Roo.bootstrap.Table.AbstractSelectionModel
24177 * @extends Roo.util.Observable
24178 * Abstract base class for grid SelectionModels. It provides the interface that should be
24179 * implemented by descendant classes. This class should not be directly instantiated.
24182 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24183 this.locked = false;
24184 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24188 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24189 /** @ignore Called by the grid automatically. Do not call directly. */
24190 init : function(grid){
24196 * Locks the selections.
24199 this.locked = true;
24203 * Unlocks the selections.
24205 unlock : function(){
24206 this.locked = false;
24210 * Returns true if the selections are locked.
24211 * @return {Boolean}
24213 isLocked : function(){
24214 return this.locked;
24218 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24219 * @class Roo.bootstrap.Table.RowSelectionModel
24220 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24221 * It supports multiple selections and keyboard selection/navigation.
24223 * @param {Object} config
24226 Roo.bootstrap.Table.RowSelectionModel = function(config){
24227 Roo.apply(this, config);
24228 this.selections = new Roo.util.MixedCollection(false, function(o){
24233 this.lastActive = false;
24237 * @event selectionchange
24238 * Fires when the selection changes
24239 * @param {SelectionModel} this
24241 "selectionchange" : true,
24243 * @event afterselectionchange
24244 * Fires after the selection changes (eg. by key press or clicking)
24245 * @param {SelectionModel} this
24247 "afterselectionchange" : true,
24249 * @event beforerowselect
24250 * Fires when a row is selected being selected, return false to cancel.
24251 * @param {SelectionModel} this
24252 * @param {Number} rowIndex The selected index
24253 * @param {Boolean} keepExisting False if other selections will be cleared
24255 "beforerowselect" : true,
24258 * Fires when a row is selected.
24259 * @param {SelectionModel} this
24260 * @param {Number} rowIndex The selected index
24261 * @param {Roo.data.Record} r The record
24263 "rowselect" : true,
24265 * @event rowdeselect
24266 * Fires when a row is deselected.
24267 * @param {SelectionModel} this
24268 * @param {Number} rowIndex The selected index
24270 "rowdeselect" : true
24272 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24273 this.locked = false;
24276 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24278 * @cfg {Boolean} singleSelect
24279 * True to allow selection of only one row at a time (defaults to false)
24281 singleSelect : false,
24284 initEvents : function()
24287 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24288 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24289 //}else{ // allow click to work like normal
24290 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24292 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24293 this.grid.on("rowclick", this.handleMouseDown, this);
24295 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24296 "up" : function(e){
24298 this.selectPrevious(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);
24311 "down" : function(e){
24313 this.selectNext(e.shiftKey);
24314 }else if(this.last !== false && this.lastActive !== false){
24315 var last = this.last;
24316 this.selectRange(this.last, this.lastActive+1);
24317 this.grid.getView().focusRow(this.lastActive);
24318 if(last !== false){
24322 this.selectFirstRow();
24324 this.fireEvent("afterselectionchange", this);
24328 this.grid.store.on('load', function(){
24329 this.selections.clear();
24332 var view = this.grid.view;
24333 view.on("refresh", this.onRefresh, this);
24334 view.on("rowupdated", this.onRowUpdated, this);
24335 view.on("rowremoved", this.onRemove, this);
24340 onRefresh : function()
24342 var ds = this.grid.store, i, v = this.grid.view;
24343 var s = this.selections;
24344 s.each(function(r){
24345 if((i = ds.indexOfId(r.id)) != -1){
24354 onRemove : function(v, index, r){
24355 this.selections.remove(r);
24359 onRowUpdated : function(v, index, r){
24360 if(this.isSelected(r)){
24361 v.onRowSelect(index);
24367 * @param {Array} records The records to select
24368 * @param {Boolean} keepExisting (optional) True to keep existing selections
24370 selectRecords : function(records, keepExisting)
24373 this.clearSelections();
24375 var ds = this.grid.store;
24376 for(var i = 0, len = records.length; i < len; i++){
24377 this.selectRow(ds.indexOf(records[i]), true);
24382 * Gets the number of selected rows.
24385 getCount : function(){
24386 return this.selections.length;
24390 * Selects the first row in the grid.
24392 selectFirstRow : function(){
24397 * Select the last row.
24398 * @param {Boolean} keepExisting (optional) True to keep existing selections
24400 selectLastRow : function(keepExisting){
24401 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24402 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24406 * Selects the row immediately following the last selected row.
24407 * @param {Boolean} keepExisting (optional) True to keep existing selections
24409 selectNext : function(keepExisting)
24411 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24412 this.selectRow(this.last+1, keepExisting);
24413 this.grid.getView().focusRow(this.last);
24418 * Selects the row that precedes the last selected row.
24419 * @param {Boolean} keepExisting (optional) True to keep existing selections
24421 selectPrevious : function(keepExisting){
24423 this.selectRow(this.last-1, keepExisting);
24424 this.grid.getView().focusRow(this.last);
24429 * Returns the selected records
24430 * @return {Array} Array of selected records
24432 getSelections : function(){
24433 return [].concat(this.selections.items);
24437 * Returns the first selected record.
24440 getSelected : function(){
24441 return this.selections.itemAt(0);
24446 * Clears all selections.
24448 clearSelections : function(fast)
24454 var ds = this.grid.store;
24455 var s = this.selections;
24456 s.each(function(r){
24457 this.deselectRow(ds.indexOfId(r.id));
24461 this.selections.clear();
24468 * Selects all rows.
24470 selectAll : function(){
24474 this.selections.clear();
24475 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24476 this.selectRow(i, true);
24481 * Returns True if there is a selection.
24482 * @return {Boolean}
24484 hasSelection : function(){
24485 return this.selections.length > 0;
24489 * Returns True if the specified row is selected.
24490 * @param {Number/Record} record The record or index of the record to check
24491 * @return {Boolean}
24493 isSelected : function(index){
24494 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24495 return (r && this.selections.key(r.id) ? true : false);
24499 * Returns True if the specified record id is selected.
24500 * @param {String} id The id of record to check
24501 * @return {Boolean}
24503 isIdSelected : function(id){
24504 return (this.selections.key(id) ? true : false);
24509 handleMouseDBClick : function(e, t){
24513 handleMouseDown : function(e, t)
24515 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24516 if(this.isLocked() || rowIndex < 0 ){
24519 if(e.shiftKey && this.last !== false){
24520 var last = this.last;
24521 this.selectRange(last, rowIndex, e.ctrlKey);
24522 this.last = last; // reset the last
24526 var isSelected = this.isSelected(rowIndex);
24527 //Roo.log("select row:" + rowIndex);
24529 this.deselectRow(rowIndex);
24531 this.selectRow(rowIndex, true);
24535 if(e.button !== 0 && isSelected){
24536 alert('rowIndex 2: ' + rowIndex);
24537 view.focusRow(rowIndex);
24538 }else if(e.ctrlKey && isSelected){
24539 this.deselectRow(rowIndex);
24540 }else if(!isSelected){
24541 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24542 view.focusRow(rowIndex);
24546 this.fireEvent("afterselectionchange", this);
24549 handleDragableRowClick : function(grid, rowIndex, e)
24551 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24552 this.selectRow(rowIndex, false);
24553 grid.view.focusRow(rowIndex);
24554 this.fireEvent("afterselectionchange", this);
24559 * Selects multiple rows.
24560 * @param {Array} rows Array of the indexes of the row to select
24561 * @param {Boolean} keepExisting (optional) True to keep existing selections
24563 selectRows : function(rows, keepExisting){
24565 this.clearSelections();
24567 for(var i = 0, len = rows.length; i < len; i++){
24568 this.selectRow(rows[i], true);
24573 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24574 * @param {Number} startRow The index of the first row in the range
24575 * @param {Number} endRow The index of the last row in the range
24576 * @param {Boolean} keepExisting (optional) True to retain existing selections
24578 selectRange : function(startRow, endRow, keepExisting){
24583 this.clearSelections();
24585 if(startRow <= endRow){
24586 for(var i = startRow; i <= endRow; i++){
24587 this.selectRow(i, true);
24590 for(var i = startRow; i >= endRow; i--){
24591 this.selectRow(i, true);
24597 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24598 * @param {Number} startRow The index of the first row in the range
24599 * @param {Number} endRow The index of the last row in the range
24601 deselectRange : function(startRow, endRow, preventViewNotify){
24605 for(var i = startRow; i <= endRow; i++){
24606 this.deselectRow(i, preventViewNotify);
24612 * @param {Number} row The index of the row to select
24613 * @param {Boolean} keepExisting (optional) True to keep existing selections
24615 selectRow : function(index, keepExisting, preventViewNotify)
24617 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24620 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24621 if(!keepExisting || this.singleSelect){
24622 this.clearSelections();
24625 var r = this.grid.store.getAt(index);
24626 //console.log('selectRow - record id :' + r.id);
24628 this.selections.add(r);
24629 this.last = this.lastActive = index;
24630 if(!preventViewNotify){
24631 var proxy = new Roo.Element(
24632 this.grid.getRowDom(index)
24634 proxy.addClass('bg-info info');
24636 this.fireEvent("rowselect", this, index, r);
24637 this.fireEvent("selectionchange", this);
24643 * @param {Number} row The index of the row to deselect
24645 deselectRow : function(index, preventViewNotify)
24650 if(this.last == index){
24653 if(this.lastActive == index){
24654 this.lastActive = false;
24657 var r = this.grid.store.getAt(index);
24662 this.selections.remove(r);
24663 //.console.log('deselectRow - record id :' + r.id);
24664 if(!preventViewNotify){
24666 var proxy = new Roo.Element(
24667 this.grid.getRowDom(index)
24669 proxy.removeClass('bg-info info');
24671 this.fireEvent("rowdeselect", this, index);
24672 this.fireEvent("selectionchange", this);
24676 restoreLast : function(){
24678 this.last = this._last;
24683 acceptsNav : function(row, col, cm){
24684 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24688 onEditorKey : function(field, e){
24689 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24694 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24696 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24698 }else if(k == e.ENTER && !e.ctrlKey){
24702 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24704 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24706 }else if(k == e.ESC){
24710 g.startEditing(newCell[0], newCell[1]);
24716 * Ext JS Library 1.1.1
24717 * Copyright(c) 2006-2007, Ext JS, LLC.
24719 * Originally Released Under LGPL - original licence link has changed is not relivant.
24722 * <script type="text/javascript">
24726 * @class Roo.bootstrap.PagingToolbar
24727 * @extends Roo.bootstrap.NavSimplebar
24728 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24730 * Create a new PagingToolbar
24731 * @param {Object} config The config object
24732 * @param {Roo.data.Store} store
24734 Roo.bootstrap.PagingToolbar = function(config)
24736 // old args format still supported... - xtype is prefered..
24737 // created from xtype...
24739 this.ds = config.dataSource;
24741 if (config.store && !this.ds) {
24742 this.store= Roo.factory(config.store, Roo.data);
24743 this.ds = this.store;
24744 this.ds.xmodule = this.xmodule || false;
24747 this.toolbarItems = [];
24748 if (config.items) {
24749 this.toolbarItems = config.items;
24752 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24757 this.bind(this.ds);
24760 if (Roo.bootstrap.version == 4) {
24761 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24763 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24768 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24770 * @cfg {Roo.data.Store} dataSource
24771 * The underlying data store providing the paged data
24774 * @cfg {String/HTMLElement/Element} container
24775 * container The id or element that will contain the toolbar
24778 * @cfg {Boolean} displayInfo
24779 * True to display the displayMsg (defaults to false)
24782 * @cfg {Number} pageSize
24783 * The number of records to display per page (defaults to 20)
24787 * @cfg {String} displayMsg
24788 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24790 displayMsg : 'Displaying {0} - {1} of {2}',
24792 * @cfg {String} emptyMsg
24793 * The message to display when no records are found (defaults to "No data to display")
24795 emptyMsg : 'No data to display',
24797 * Customizable piece of the default paging text (defaults to "Page")
24800 beforePageText : "Page",
24802 * Customizable piece of the default paging text (defaults to "of %0")
24805 afterPageText : "of {0}",
24807 * Customizable piece of the default paging text (defaults to "First Page")
24810 firstText : "First Page",
24812 * Customizable piece of the default paging text (defaults to "Previous Page")
24815 prevText : "Previous Page",
24817 * Customizable piece of the default paging text (defaults to "Next Page")
24820 nextText : "Next Page",
24822 * Customizable piece of the default paging text (defaults to "Last Page")
24825 lastText : "Last Page",
24827 * Customizable piece of the default paging text (defaults to "Refresh")
24830 refreshText : "Refresh",
24834 onRender : function(ct, position)
24836 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24837 this.navgroup.parentId = this.id;
24838 this.navgroup.onRender(this.el, null);
24839 // add the buttons to the navgroup
24841 if(this.displayInfo){
24842 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24843 this.displayEl = this.el.select('.x-paging-info', true).first();
24844 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24845 // this.displayEl = navel.el.select('span',true).first();
24851 Roo.each(_this.buttons, function(e){ // this might need to use render????
24852 Roo.factory(e).render(_this.el);
24856 Roo.each(_this.toolbarItems, function(e) {
24857 _this.navgroup.addItem(e);
24861 this.first = this.navgroup.addItem({
24862 tooltip: this.firstText,
24863 cls: "prev btn-outline-secondary",
24864 html : ' <i class="fa fa-step-backward"></i>',
24866 preventDefault: true,
24867 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24870 this.prev = this.navgroup.addItem({
24871 tooltip: this.prevText,
24872 cls: "prev btn-outline-secondary",
24873 html : ' <i class="fa fa-backward"></i>',
24875 preventDefault: true,
24876 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24878 //this.addSeparator();
24881 var field = this.navgroup.addItem( {
24883 cls : 'x-paging-position btn-outline-secondary',
24885 html : this.beforePageText +
24886 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24887 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24890 this.field = field.el.select('input', true).first();
24891 this.field.on("keydown", this.onPagingKeydown, this);
24892 this.field.on("focus", function(){this.dom.select();});
24895 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24896 //this.field.setHeight(18);
24897 //this.addSeparator();
24898 this.next = this.navgroup.addItem({
24899 tooltip: this.nextText,
24900 cls: "next btn-outline-secondary",
24901 html : ' <i class="fa fa-forward"></i>',
24903 preventDefault: true,
24904 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24906 this.last = this.navgroup.addItem({
24907 tooltip: this.lastText,
24908 html : ' <i class="fa fa-step-forward"></i>',
24909 cls: "next btn-outline-secondary",
24911 preventDefault: true,
24912 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24914 //this.addSeparator();
24915 this.loading = this.navgroup.addItem({
24916 tooltip: this.refreshText,
24917 cls: "btn-outline-secondary",
24918 html : ' <i class="fa fa-refresh"></i>',
24919 preventDefault: true,
24920 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24926 updateInfo : function(){
24927 if(this.displayEl){
24928 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24929 var msg = count == 0 ?
24933 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24935 this.displayEl.update(msg);
24940 onLoad : function(ds, r, o)
24942 this.cursor = o.params.start ? o.params.start : 0;
24944 var d = this.getPageData(),
24949 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24950 this.field.dom.value = ap;
24951 this.first.setDisabled(ap == 1);
24952 this.prev.setDisabled(ap == 1);
24953 this.next.setDisabled(ap == ps);
24954 this.last.setDisabled(ap == ps);
24955 this.loading.enable();
24960 getPageData : function(){
24961 var total = this.ds.getTotalCount();
24964 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24965 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24970 onLoadError : function(){
24971 this.loading.enable();
24975 onPagingKeydown : function(e){
24976 var k = e.getKey();
24977 var d = this.getPageData();
24979 var v = this.field.dom.value, pageNum;
24980 if(!v || isNaN(pageNum = parseInt(v, 10))){
24981 this.field.dom.value = d.activePage;
24984 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24985 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24988 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))
24990 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24991 this.field.dom.value = pageNum;
24992 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24995 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24997 var v = this.field.dom.value, pageNum;
24998 var increment = (e.shiftKey) ? 10 : 1;
24999 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25002 if(!v || isNaN(pageNum = parseInt(v, 10))) {
25003 this.field.dom.value = d.activePage;
25006 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25008 this.field.dom.value = parseInt(v, 10) + increment;
25009 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25010 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25017 beforeLoad : function(){
25019 this.loading.disable();
25024 onClick : function(which){
25033 ds.load({params:{start: 0, limit: this.pageSize}});
25036 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25039 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25042 var total = ds.getTotalCount();
25043 var extra = total % this.pageSize;
25044 var lastStart = extra ? (total - extra) : total-this.pageSize;
25045 ds.load({params:{start: lastStart, limit: this.pageSize}});
25048 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25054 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25055 * @param {Roo.data.Store} store The data store to unbind
25057 unbind : function(ds){
25058 ds.un("beforeload", this.beforeLoad, this);
25059 ds.un("load", this.onLoad, this);
25060 ds.un("loadexception", this.onLoadError, this);
25061 ds.un("remove", this.updateInfo, this);
25062 ds.un("add", this.updateInfo, this);
25063 this.ds = undefined;
25067 * Binds the paging toolbar to the specified {@link Roo.data.Store}
25068 * @param {Roo.data.Store} store The data store to bind
25070 bind : function(ds){
25071 ds.on("beforeload", this.beforeLoad, this);
25072 ds.on("load", this.onLoad, this);
25073 ds.on("loadexception", this.onLoadError, this);
25074 ds.on("remove", this.updateInfo, this);
25075 ds.on("add", this.updateInfo, this);
25086 * @class Roo.bootstrap.MessageBar
25087 * @extends Roo.bootstrap.Component
25088 * Bootstrap MessageBar class
25089 * @cfg {String} html contents of the MessageBar
25090 * @cfg {String} weight (info | success | warning | danger) default info
25091 * @cfg {String} beforeClass insert the bar before the given class
25092 * @cfg {Boolean} closable (true | false) default false
25093 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25096 * Create a new Element
25097 * @param {Object} config The config object
25100 Roo.bootstrap.MessageBar = function(config){
25101 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25104 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25110 beforeClass: 'bootstrap-sticky-wrap',
25112 getAutoCreate : function(){
25116 cls: 'alert alert-dismissable alert-' + this.weight,
25121 html: this.html || ''
25127 cfg.cls += ' alert-messages-fixed';
25141 onRender : function(ct, position)
25143 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25146 var cfg = Roo.apply({}, this.getAutoCreate());
25150 cfg.cls += ' ' + this.cls;
25153 cfg.style = this.style;
25155 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25157 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25160 this.el.select('>button.close').on('click', this.hide, this);
25166 if (!this.rendered) {
25172 this.fireEvent('show', this);
25178 if (!this.rendered) {
25184 this.fireEvent('hide', this);
25187 update : function()
25189 // var e = this.el.dom.firstChild;
25191 // if(this.closable){
25192 // e = e.nextSibling;
25195 // e.data = this.html || '';
25197 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25213 * @class Roo.bootstrap.Graph
25214 * @extends Roo.bootstrap.Component
25215 * Bootstrap Graph class
25219 @cfg {String} graphtype bar | vbar | pie
25220 @cfg {number} g_x coodinator | centre x (pie)
25221 @cfg {number} g_y coodinator | centre y (pie)
25222 @cfg {number} g_r radius (pie)
25223 @cfg {number} g_height height of the chart (respected by all elements in the set)
25224 @cfg {number} g_width width of the chart (respected by all elements in the set)
25225 @cfg {Object} title The title of the chart
25228 -opts (object) options for the chart
25230 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25231 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25233 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.
25234 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25236 o stretch (boolean)
25238 -opts (object) options for the pie
25241 o startAngle (number)
25242 o endAngle (number)
25246 * Create a new Input
25247 * @param {Object} config The config object
25250 Roo.bootstrap.Graph = function(config){
25251 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25257 * The img click event for the img.
25258 * @param {Roo.EventObject} e
25264 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25275 //g_colors: this.colors,
25282 getAutoCreate : function(){
25293 onRender : function(ct,position){
25296 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25298 if (typeof(Raphael) == 'undefined') {
25299 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25303 this.raphael = Raphael(this.el.dom);
25305 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25306 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25307 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25308 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25310 r.text(160, 10, "Single Series Chart").attr(txtattr);
25311 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25312 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25313 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25315 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25316 r.barchart(330, 10, 300, 220, data1);
25317 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25318 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25321 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25322 // r.barchart(30, 30, 560, 250, xdata, {
25323 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25324 // axis : "0 0 1 1",
25325 // axisxlabels : xdata
25326 // //yvalues : cols,
25329 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25331 // this.load(null,xdata,{
25332 // axis : "0 0 1 1",
25333 // axisxlabels : xdata
25338 load : function(graphtype,xdata,opts)
25340 this.raphael.clear();
25342 graphtype = this.graphtype;
25347 var r = this.raphael,
25348 fin = function () {
25349 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25351 fout = function () {
25352 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25354 pfin = function() {
25355 this.sector.stop();
25356 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25359 this.label[0].stop();
25360 this.label[0].attr({ r: 7.5 });
25361 this.label[1].attr({ "font-weight": 800 });
25364 pfout = function() {
25365 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25368 this.label[0].animate({ r: 5 }, 500, "bounce");
25369 this.label[1].attr({ "font-weight": 400 });
25375 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25378 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25381 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25382 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25384 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25391 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25396 setTitle: function(o)
25401 initEvents: function() {
25404 this.el.on('click', this.onClick, this);
25408 onClick : function(e)
25410 Roo.log('img onclick');
25411 this.fireEvent('click', this, e);
25423 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25426 * @class Roo.bootstrap.dash.NumberBox
25427 * @extends Roo.bootstrap.Component
25428 * Bootstrap NumberBox class
25429 * @cfg {String} headline Box headline
25430 * @cfg {String} content Box content
25431 * @cfg {String} icon Box icon
25432 * @cfg {String} footer Footer text
25433 * @cfg {String} fhref Footer href
25436 * Create a new NumberBox
25437 * @param {Object} config The config object
25441 Roo.bootstrap.dash.NumberBox = function(config){
25442 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25446 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25455 getAutoCreate : function(){
25459 cls : 'small-box ',
25467 cls : 'roo-headline',
25468 html : this.headline
25472 cls : 'roo-content',
25473 html : this.content
25487 cls : 'ion ' + this.icon
25496 cls : 'small-box-footer',
25497 href : this.fhref || '#',
25501 cfg.cn.push(footer);
25508 onRender : function(ct,position){
25509 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25516 setHeadline: function (value)
25518 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25521 setFooter: function (value, href)
25523 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25526 this.el.select('a.small-box-footer',true).first().attr('href', href);
25531 setContent: function (value)
25533 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25536 initEvents: function()
25550 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25553 * @class Roo.bootstrap.dash.TabBox
25554 * @extends Roo.bootstrap.Component
25555 * Bootstrap TabBox class
25556 * @cfg {String} title Title of the TabBox
25557 * @cfg {String} icon Icon of the TabBox
25558 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25559 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25562 * Create a new TabBox
25563 * @param {Object} config The config object
25567 Roo.bootstrap.dash.TabBox = function(config){
25568 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25573 * When a pane is added
25574 * @param {Roo.bootstrap.dash.TabPane} pane
25578 * @event activatepane
25579 * When a pane is activated
25580 * @param {Roo.bootstrap.dash.TabPane} pane
25582 "activatepane" : true
25590 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25595 tabScrollable : false,
25597 getChildContainer : function()
25599 return this.el.select('.tab-content', true).first();
25602 getAutoCreate : function(){
25606 cls: 'pull-left header',
25614 cls: 'fa ' + this.icon
25620 cls: 'nav nav-tabs pull-right',
25626 if(this.tabScrollable){
25633 cls: 'nav nav-tabs pull-right',
25644 cls: 'nav-tabs-custom',
25649 cls: 'tab-content no-padding',
25657 initEvents : function()
25659 //Roo.log('add add pane handler');
25660 this.on('addpane', this.onAddPane, this);
25663 * Updates the box title
25664 * @param {String} html to set the title to.
25666 setTitle : function(value)
25668 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25670 onAddPane : function(pane)
25672 this.panes.push(pane);
25673 //Roo.log('addpane');
25675 // tabs are rendere left to right..
25676 if(!this.showtabs){
25680 var ctr = this.el.select('.nav-tabs', true).first();
25683 var existing = ctr.select('.nav-tab',true);
25684 var qty = existing.getCount();;
25687 var tab = ctr.createChild({
25689 cls : 'nav-tab' + (qty ? '' : ' active'),
25697 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25700 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25702 pane.el.addClass('active');
25707 onTabClick : function(ev,un,ob,pane)
25709 //Roo.log('tab - prev default');
25710 ev.preventDefault();
25713 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25714 pane.tab.addClass('active');
25715 //Roo.log(pane.title);
25716 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25717 // technically we should have a deactivate event.. but maybe add later.
25718 // and it should not de-activate the selected tab...
25719 this.fireEvent('activatepane', pane);
25720 pane.el.addClass('active');
25721 pane.fireEvent('activate');
25726 getActivePane : function()
25729 Roo.each(this.panes, function(p) {
25730 if(p.el.hasClass('active')){
25751 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25753 * @class Roo.bootstrap.TabPane
25754 * @extends Roo.bootstrap.Component
25755 * Bootstrap TabPane class
25756 * @cfg {Boolean} active (false | true) Default false
25757 * @cfg {String} title title of panel
25761 * Create a new TabPane
25762 * @param {Object} config The config object
25765 Roo.bootstrap.dash.TabPane = function(config){
25766 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25772 * When a pane is activated
25773 * @param {Roo.bootstrap.dash.TabPane} pane
25780 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25785 // the tabBox that this is attached to.
25788 getAutoCreate : function()
25796 cfg.cls += ' active';
25801 initEvents : function()
25803 //Roo.log('trigger add pane handler');
25804 this.parent().fireEvent('addpane', this)
25808 * Updates the tab title
25809 * @param {String} html to set the title to.
25811 setTitle: function(str)
25817 this.tab.select('a', true).first().dom.innerHTML = str;
25834 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25837 * @class Roo.bootstrap.menu.Menu
25838 * @extends Roo.bootstrap.Component
25839 * Bootstrap Menu class - container for Menu
25840 * @cfg {String} html Text of the menu
25841 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25842 * @cfg {String} icon Font awesome icon
25843 * @cfg {String} pos Menu align to (top | bottom) default bottom
25847 * Create a new Menu
25848 * @param {Object} config The config object
25852 Roo.bootstrap.menu.Menu = function(config){
25853 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25857 * @event beforeshow
25858 * Fires before this menu is displayed
25859 * @param {Roo.bootstrap.menu.Menu} this
25863 * @event beforehide
25864 * Fires before this menu is hidden
25865 * @param {Roo.bootstrap.menu.Menu} this
25870 * Fires after this menu is displayed
25871 * @param {Roo.bootstrap.menu.Menu} this
25876 * Fires after this menu is hidden
25877 * @param {Roo.bootstrap.menu.Menu} this
25882 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25883 * @param {Roo.bootstrap.menu.Menu} this
25884 * @param {Roo.EventObject} e
25891 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25895 weight : 'default',
25900 getChildContainer : function() {
25901 if(this.isSubMenu){
25905 return this.el.select('ul.dropdown-menu', true).first();
25908 getAutoCreate : function()
25913 cls : 'roo-menu-text',
25921 cls : 'fa ' + this.icon
25932 cls : 'dropdown-button btn btn-' + this.weight,
25937 cls : 'dropdown-toggle btn btn-' + this.weight,
25947 cls : 'dropdown-menu'
25953 if(this.pos == 'top'){
25954 cfg.cls += ' dropup';
25957 if(this.isSubMenu){
25960 cls : 'dropdown-menu'
25967 onRender : function(ct, position)
25969 this.isSubMenu = ct.hasClass('dropdown-submenu');
25971 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25974 initEvents : function()
25976 if(this.isSubMenu){
25980 this.hidden = true;
25982 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25983 this.triggerEl.on('click', this.onTriggerPress, this);
25985 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25986 this.buttonEl.on('click', this.onClick, this);
25992 if(this.isSubMenu){
25996 return this.el.select('ul.dropdown-menu', true).first();
25999 onClick : function(e)
26001 this.fireEvent("click", this, e);
26004 onTriggerPress : function(e)
26006 if (this.isVisible()) {
26013 isVisible : function(){
26014 return !this.hidden;
26019 this.fireEvent("beforeshow", this);
26021 this.hidden = false;
26022 this.el.addClass('open');
26024 Roo.get(document).on("mouseup", this.onMouseUp, this);
26026 this.fireEvent("show", this);
26033 this.fireEvent("beforehide", this);
26035 this.hidden = true;
26036 this.el.removeClass('open');
26038 Roo.get(document).un("mouseup", this.onMouseUp);
26040 this.fireEvent("hide", this);
26043 onMouseUp : function()
26057 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26060 * @class Roo.bootstrap.menu.Item
26061 * @extends Roo.bootstrap.Component
26062 * Bootstrap MenuItem class
26063 * @cfg {Boolean} submenu (true | false) default false
26064 * @cfg {String} html text of the item
26065 * @cfg {String} href the link
26066 * @cfg {Boolean} disable (true | false) default false
26067 * @cfg {Boolean} preventDefault (true | false) default true
26068 * @cfg {String} icon Font awesome icon
26069 * @cfg {String} pos Submenu align to (left | right) default right
26073 * Create a new Item
26074 * @param {Object} config The config object
26078 Roo.bootstrap.menu.Item = function(config){
26079 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26083 * Fires when the mouse is hovering over this menu
26084 * @param {Roo.bootstrap.menu.Item} this
26085 * @param {Roo.EventObject} e
26090 * Fires when the mouse exits this menu
26091 * @param {Roo.bootstrap.menu.Item} this
26092 * @param {Roo.EventObject} e
26098 * The raw click event for the entire grid.
26099 * @param {Roo.EventObject} e
26105 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26110 preventDefault: true,
26115 getAutoCreate : function()
26120 cls : 'roo-menu-item-text',
26128 cls : 'fa ' + this.icon
26137 href : this.href || '#',
26144 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26148 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26150 if(this.pos == 'left'){
26151 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26158 initEvents : function()
26160 this.el.on('mouseover', this.onMouseOver, this);
26161 this.el.on('mouseout', this.onMouseOut, this);
26163 this.el.select('a', true).first().on('click', this.onClick, this);
26167 onClick : function(e)
26169 if(this.preventDefault){
26170 e.preventDefault();
26173 this.fireEvent("click", this, e);
26176 onMouseOver : function(e)
26178 if(this.submenu && this.pos == 'left'){
26179 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26182 this.fireEvent("mouseover", this, e);
26185 onMouseOut : function(e)
26187 this.fireEvent("mouseout", this, e);
26199 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26202 * @class Roo.bootstrap.menu.Separator
26203 * @extends Roo.bootstrap.Component
26204 * Bootstrap Separator class
26207 * Create a new Separator
26208 * @param {Object} config The config object
26212 Roo.bootstrap.menu.Separator = function(config){
26213 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26216 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26218 getAutoCreate : function(){
26239 * @class Roo.bootstrap.Tooltip
26240 * Bootstrap Tooltip class
26241 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26242 * to determine which dom element triggers the tooltip.
26244 * It needs to add support for additional attributes like tooltip-position
26247 * Create a new Toolti
26248 * @param {Object} config The config object
26251 Roo.bootstrap.Tooltip = function(config){
26252 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26254 this.alignment = Roo.bootstrap.Tooltip.alignment;
26256 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26257 this.alignment = config.alignment;
26262 Roo.apply(Roo.bootstrap.Tooltip, {
26264 * @function init initialize tooltip monitoring.
26268 currentTip : false,
26269 currentRegion : false,
26275 Roo.get(document).on('mouseover', this.enter ,this);
26276 Roo.get(document).on('mouseout', this.leave, this);
26279 this.currentTip = new Roo.bootstrap.Tooltip();
26282 enter : function(ev)
26284 var dom = ev.getTarget();
26286 //Roo.log(['enter',dom]);
26287 var el = Roo.fly(dom);
26288 if (this.currentEl) {
26290 //Roo.log(this.currentEl);
26291 //Roo.log(this.currentEl.contains(dom));
26292 if (this.currentEl == el) {
26295 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26301 if (this.currentTip.el) {
26302 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26306 if(!el || el.dom == document){
26312 // you can not look for children, as if el is the body.. then everythign is the child..
26313 if (!el.attr('tooltip')) { //
26314 if (!el.select("[tooltip]").elements.length) {
26317 // is the mouse over this child...?
26318 bindEl = el.select("[tooltip]").first();
26319 var xy = ev.getXY();
26320 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26321 //Roo.log("not in region.");
26324 //Roo.log("child element over..");
26327 this.currentEl = bindEl;
26328 this.currentTip.bind(bindEl);
26329 this.currentRegion = Roo.lib.Region.getRegion(dom);
26330 this.currentTip.enter();
26333 leave : function(ev)
26335 var dom = ev.getTarget();
26336 //Roo.log(['leave',dom]);
26337 if (!this.currentEl) {
26342 if (dom != this.currentEl.dom) {
26345 var xy = ev.getXY();
26346 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26349 // only activate leave if mouse cursor is outside... bounding box..
26354 if (this.currentTip) {
26355 this.currentTip.leave();
26357 //Roo.log('clear currentEl');
26358 this.currentEl = false;
26363 'left' : ['r-l', [-2,0], 'right'],
26364 'right' : ['l-r', [2,0], 'left'],
26365 'bottom' : ['t-b', [0,2], 'top'],
26366 'top' : [ 'b-t', [0,-2], 'bottom']
26372 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26377 delay : null, // can be { show : 300 , hide: 500}
26381 hoverState : null, //???
26383 placement : 'bottom',
26387 getAutoCreate : function(){
26394 cls : 'tooltip-arrow'
26397 cls : 'tooltip-inner'
26404 bind : function(el)
26410 enter : function () {
26412 if (this.timeout != null) {
26413 clearTimeout(this.timeout);
26416 this.hoverState = 'in';
26417 //Roo.log("enter - show");
26418 if (!this.delay || !this.delay.show) {
26423 this.timeout = setTimeout(function () {
26424 if (_t.hoverState == 'in') {
26427 }, this.delay.show);
26431 clearTimeout(this.timeout);
26433 this.hoverState = 'out';
26434 if (!this.delay || !this.delay.hide) {
26440 this.timeout = setTimeout(function () {
26441 //Roo.log("leave - timeout");
26443 if (_t.hoverState == 'out') {
26445 Roo.bootstrap.Tooltip.currentEl = false;
26450 show : function (msg)
26453 this.render(document.body);
26456 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26458 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26460 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26462 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26464 var placement = typeof this.placement == 'function' ?
26465 this.placement.call(this, this.el, on_el) :
26468 var autoToken = /\s?auto?\s?/i;
26469 var autoPlace = autoToken.test(placement);
26471 placement = placement.replace(autoToken, '') || 'top';
26475 //this.el.setXY([0,0]);
26477 //this.el.dom.style.display='block';
26479 //this.el.appendTo(on_el);
26481 var p = this.getPosition();
26482 var box = this.el.getBox();
26488 var align = this.alignment[placement];
26490 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26492 if(placement == 'top' || placement == 'bottom'){
26494 placement = 'right';
26497 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26498 placement = 'left';
26501 var scroll = Roo.select('body', true).first().getScroll();
26503 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26507 align = this.alignment[placement];
26510 this.el.alignTo(this.bindEl, align[0],align[1]);
26511 //var arrow = this.el.select('.arrow',true).first();
26512 //arrow.set(align[2],
26514 this.el.addClass(placement);
26516 this.el.addClass('in fade');
26518 this.hoverState = null;
26520 if (this.el.hasClass('fade')) {
26531 //this.el.setXY([0,0]);
26532 this.el.removeClass('in');
26548 * @class Roo.bootstrap.LocationPicker
26549 * @extends Roo.bootstrap.Component
26550 * Bootstrap LocationPicker class
26551 * @cfg {Number} latitude Position when init default 0
26552 * @cfg {Number} longitude Position when init default 0
26553 * @cfg {Number} zoom default 15
26554 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26555 * @cfg {Boolean} mapTypeControl default false
26556 * @cfg {Boolean} disableDoubleClickZoom default false
26557 * @cfg {Boolean} scrollwheel default true
26558 * @cfg {Boolean} streetViewControl default false
26559 * @cfg {Number} radius default 0
26560 * @cfg {String} locationName
26561 * @cfg {Boolean} draggable default true
26562 * @cfg {Boolean} enableAutocomplete default false
26563 * @cfg {Boolean} enableReverseGeocode default true
26564 * @cfg {String} markerTitle
26567 * Create a new LocationPicker
26568 * @param {Object} config The config object
26572 Roo.bootstrap.LocationPicker = function(config){
26574 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26579 * Fires when the picker initialized.
26580 * @param {Roo.bootstrap.LocationPicker} this
26581 * @param {Google Location} location
26585 * @event positionchanged
26586 * Fires when the picker position changed.
26587 * @param {Roo.bootstrap.LocationPicker} this
26588 * @param {Google Location} location
26590 positionchanged : true,
26593 * Fires when the map resize.
26594 * @param {Roo.bootstrap.LocationPicker} this
26599 * Fires when the map show.
26600 * @param {Roo.bootstrap.LocationPicker} this
26605 * Fires when the map hide.
26606 * @param {Roo.bootstrap.LocationPicker} this
26611 * Fires when click the map.
26612 * @param {Roo.bootstrap.LocationPicker} this
26613 * @param {Map event} e
26617 * @event mapRightClick
26618 * Fires when right click the map.
26619 * @param {Roo.bootstrap.LocationPicker} this
26620 * @param {Map event} e
26622 mapRightClick : true,
26624 * @event markerClick
26625 * Fires when click the marker.
26626 * @param {Roo.bootstrap.LocationPicker} this
26627 * @param {Map event} e
26629 markerClick : true,
26631 * @event markerRightClick
26632 * Fires when right click the marker.
26633 * @param {Roo.bootstrap.LocationPicker} this
26634 * @param {Map event} e
26636 markerRightClick : true,
26638 * @event OverlayViewDraw
26639 * Fires when OverlayView Draw
26640 * @param {Roo.bootstrap.LocationPicker} this
26642 OverlayViewDraw : true,
26644 * @event OverlayViewOnAdd
26645 * Fires when OverlayView Draw
26646 * @param {Roo.bootstrap.LocationPicker} this
26648 OverlayViewOnAdd : true,
26650 * @event OverlayViewOnRemove
26651 * Fires when OverlayView Draw
26652 * @param {Roo.bootstrap.LocationPicker} this
26654 OverlayViewOnRemove : true,
26656 * @event OverlayViewShow
26657 * Fires when OverlayView Draw
26658 * @param {Roo.bootstrap.LocationPicker} this
26659 * @param {Pixel} cpx
26661 OverlayViewShow : true,
26663 * @event OverlayViewHide
26664 * Fires when OverlayView Draw
26665 * @param {Roo.bootstrap.LocationPicker} this
26667 OverlayViewHide : true,
26669 * @event loadexception
26670 * Fires when load google lib failed.
26671 * @param {Roo.bootstrap.LocationPicker} this
26673 loadexception : true
26678 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26680 gMapContext: false,
26686 mapTypeControl: false,
26687 disableDoubleClickZoom: false,
26689 streetViewControl: false,
26693 enableAutocomplete: false,
26694 enableReverseGeocode: true,
26697 getAutoCreate: function()
26702 cls: 'roo-location-picker'
26708 initEvents: function(ct, position)
26710 if(!this.el.getWidth() || this.isApplied()){
26714 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26719 initial: function()
26721 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26722 this.fireEvent('loadexception', this);
26726 if(!this.mapTypeId){
26727 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26730 this.gMapContext = this.GMapContext();
26732 this.initOverlayView();
26734 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26738 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26739 _this.setPosition(_this.gMapContext.marker.position);
26742 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26743 _this.fireEvent('mapClick', this, event);
26747 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26748 _this.fireEvent('mapRightClick', this, event);
26752 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26753 _this.fireEvent('markerClick', this, event);
26757 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26758 _this.fireEvent('markerRightClick', this, event);
26762 this.setPosition(this.gMapContext.location);
26764 this.fireEvent('initial', this, this.gMapContext.location);
26767 initOverlayView: function()
26771 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26775 _this.fireEvent('OverlayViewDraw', _this);
26780 _this.fireEvent('OverlayViewOnAdd', _this);
26783 onRemove: function()
26785 _this.fireEvent('OverlayViewOnRemove', _this);
26788 show: function(cpx)
26790 _this.fireEvent('OverlayViewShow', _this, cpx);
26795 _this.fireEvent('OverlayViewHide', _this);
26801 fromLatLngToContainerPixel: function(event)
26803 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26806 isApplied: function()
26808 return this.getGmapContext() == false ? false : true;
26811 getGmapContext: function()
26813 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26816 GMapContext: function()
26818 var position = new google.maps.LatLng(this.latitude, this.longitude);
26820 var _map = new google.maps.Map(this.el.dom, {
26823 mapTypeId: this.mapTypeId,
26824 mapTypeControl: this.mapTypeControl,
26825 disableDoubleClickZoom: this.disableDoubleClickZoom,
26826 scrollwheel: this.scrollwheel,
26827 streetViewControl: this.streetViewControl,
26828 locationName: this.locationName,
26829 draggable: this.draggable,
26830 enableAutocomplete: this.enableAutocomplete,
26831 enableReverseGeocode: this.enableReverseGeocode
26834 var _marker = new google.maps.Marker({
26835 position: position,
26837 title: this.markerTitle,
26838 draggable: this.draggable
26845 location: position,
26846 radius: this.radius,
26847 locationName: this.locationName,
26848 addressComponents: {
26849 formatted_address: null,
26850 addressLine1: null,
26851 addressLine2: null,
26853 streetNumber: null,
26857 stateOrProvince: null
26860 domContainer: this.el.dom,
26861 geodecoder: new google.maps.Geocoder()
26865 drawCircle: function(center, radius, options)
26867 if (this.gMapContext.circle != null) {
26868 this.gMapContext.circle.setMap(null);
26872 options = Roo.apply({}, options, {
26873 strokeColor: "#0000FF",
26874 strokeOpacity: .35,
26876 fillColor: "#0000FF",
26880 options.map = this.gMapContext.map;
26881 options.radius = radius;
26882 options.center = center;
26883 this.gMapContext.circle = new google.maps.Circle(options);
26884 return this.gMapContext.circle;
26890 setPosition: function(location)
26892 this.gMapContext.location = location;
26893 this.gMapContext.marker.setPosition(location);
26894 this.gMapContext.map.panTo(location);
26895 this.drawCircle(location, this.gMapContext.radius, {});
26899 if (this.gMapContext.settings.enableReverseGeocode) {
26900 this.gMapContext.geodecoder.geocode({
26901 latLng: this.gMapContext.location
26902 }, function(results, status) {
26904 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26905 _this.gMapContext.locationName = results[0].formatted_address;
26906 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26908 _this.fireEvent('positionchanged', this, location);
26915 this.fireEvent('positionchanged', this, location);
26920 google.maps.event.trigger(this.gMapContext.map, "resize");
26922 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26924 this.fireEvent('resize', this);
26927 setPositionByLatLng: function(latitude, longitude)
26929 this.setPosition(new google.maps.LatLng(latitude, longitude));
26932 getCurrentPosition: function()
26935 latitude: this.gMapContext.location.lat(),
26936 longitude: this.gMapContext.location.lng()
26940 getAddressName: function()
26942 return this.gMapContext.locationName;
26945 getAddressComponents: function()
26947 return this.gMapContext.addressComponents;
26950 address_component_from_google_geocode: function(address_components)
26954 for (var i = 0; i < address_components.length; i++) {
26955 var component = address_components[i];
26956 if (component.types.indexOf("postal_code") >= 0) {
26957 result.postalCode = component.short_name;
26958 } else if (component.types.indexOf("street_number") >= 0) {
26959 result.streetNumber = component.short_name;
26960 } else if (component.types.indexOf("route") >= 0) {
26961 result.streetName = component.short_name;
26962 } else if (component.types.indexOf("neighborhood") >= 0) {
26963 result.city = component.short_name;
26964 } else if (component.types.indexOf("locality") >= 0) {
26965 result.city = component.short_name;
26966 } else if (component.types.indexOf("sublocality") >= 0) {
26967 result.district = component.short_name;
26968 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26969 result.stateOrProvince = component.short_name;
26970 } else if (component.types.indexOf("country") >= 0) {
26971 result.country = component.short_name;
26975 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26976 result.addressLine2 = "";
26980 setZoomLevel: function(zoom)
26982 this.gMapContext.map.setZoom(zoom);
26995 this.fireEvent('show', this);
27006 this.fireEvent('hide', this);
27011 Roo.apply(Roo.bootstrap.LocationPicker, {
27013 OverlayView : function(map, options)
27015 options = options || {};
27029 * @class Roo.bootstrap.Alert
27030 * @extends Roo.bootstrap.Component
27031 * Bootstrap Alert class
27032 * @cfg {String} title The title of alert
27033 * @cfg {String} html The content of alert
27034 * @cfg {String} weight ( success | info | warning | danger )
27035 * @cfg {String} faicon font-awesomeicon
27038 * Create a new alert
27039 * @param {Object} config The config object
27043 Roo.bootstrap.Alert = function(config){
27044 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27048 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
27055 getAutoCreate : function()
27064 cls : 'roo-alert-icon'
27069 cls : 'roo-alert-title',
27074 cls : 'roo-alert-text',
27081 cfg.cn[0].cls += ' fa ' + this.faicon;
27085 cfg.cls += ' alert-' + this.weight;
27091 initEvents: function()
27093 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27096 setTitle : function(str)
27098 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27101 setText : function(str)
27103 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27106 setWeight : function(weight)
27109 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27112 this.weight = weight;
27114 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27117 setIcon : function(icon)
27120 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27123 this.faicon = icon;
27125 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27146 * @class Roo.bootstrap.UploadCropbox
27147 * @extends Roo.bootstrap.Component
27148 * Bootstrap UploadCropbox class
27149 * @cfg {String} emptyText show when image has been loaded
27150 * @cfg {String} rotateNotify show when image too small to rotate
27151 * @cfg {Number} errorTimeout default 3000
27152 * @cfg {Number} minWidth default 300
27153 * @cfg {Number} minHeight default 300
27154 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27155 * @cfg {Boolean} isDocument (true|false) default false
27156 * @cfg {String} url action url
27157 * @cfg {String} paramName default 'imageUpload'
27158 * @cfg {String} method default POST
27159 * @cfg {Boolean} loadMask (true|false) default true
27160 * @cfg {Boolean} loadingText default 'Loading...'
27163 * Create a new UploadCropbox
27164 * @param {Object} config The config object
27167 Roo.bootstrap.UploadCropbox = function(config){
27168 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27172 * @event beforeselectfile
27173 * Fire before select file
27174 * @param {Roo.bootstrap.UploadCropbox} this
27176 "beforeselectfile" : true,
27179 * Fire after initEvent
27180 * @param {Roo.bootstrap.UploadCropbox} this
27185 * Fire after initEvent
27186 * @param {Roo.bootstrap.UploadCropbox} this
27187 * @param {String} data
27192 * Fire when preparing the file data
27193 * @param {Roo.bootstrap.UploadCropbox} this
27194 * @param {Object} file
27199 * Fire when get exception
27200 * @param {Roo.bootstrap.UploadCropbox} this
27201 * @param {XMLHttpRequest} xhr
27203 "exception" : true,
27205 * @event beforeloadcanvas
27206 * Fire before load the canvas
27207 * @param {Roo.bootstrap.UploadCropbox} this
27208 * @param {String} src
27210 "beforeloadcanvas" : true,
27213 * Fire when trash image
27214 * @param {Roo.bootstrap.UploadCropbox} this
27219 * Fire when download the image
27220 * @param {Roo.bootstrap.UploadCropbox} this
27224 * @event footerbuttonclick
27225 * Fire when footerbuttonclick
27226 * @param {Roo.bootstrap.UploadCropbox} this
27227 * @param {String} type
27229 "footerbuttonclick" : true,
27233 * @param {Roo.bootstrap.UploadCropbox} this
27238 * Fire when rotate the image
27239 * @param {Roo.bootstrap.UploadCropbox} this
27240 * @param {String} pos
27245 * Fire when inspect the file
27246 * @param {Roo.bootstrap.UploadCropbox} this
27247 * @param {Object} file
27252 * Fire when xhr upload the file
27253 * @param {Roo.bootstrap.UploadCropbox} this
27254 * @param {Object} data
27259 * Fire when arrange the file data
27260 * @param {Roo.bootstrap.UploadCropbox} this
27261 * @param {Object} formData
27266 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27269 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27271 emptyText : 'Click to upload image',
27272 rotateNotify : 'Image is too small to rotate',
27273 errorTimeout : 3000,
27287 cropType : 'image/jpeg',
27289 canvasLoaded : false,
27290 isDocument : false,
27292 paramName : 'imageUpload',
27294 loadingText : 'Loading...',
27297 getAutoCreate : function()
27301 cls : 'roo-upload-cropbox',
27305 cls : 'roo-upload-cropbox-selector',
27310 cls : 'roo-upload-cropbox-body',
27311 style : 'cursor:pointer',
27315 cls : 'roo-upload-cropbox-preview'
27319 cls : 'roo-upload-cropbox-thumb'
27323 cls : 'roo-upload-cropbox-empty-notify',
27324 html : this.emptyText
27328 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27329 html : this.rotateNotify
27335 cls : 'roo-upload-cropbox-footer',
27338 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27348 onRender : function(ct, position)
27350 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27352 if (this.buttons.length) {
27354 Roo.each(this.buttons, function(bb) {
27356 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27358 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27364 this.maskEl = this.el;
27368 initEvents : function()
27370 this.urlAPI = (window.createObjectURL && window) ||
27371 (window.URL && URL.revokeObjectURL && URL) ||
27372 (window.webkitURL && webkitURL);
27374 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27375 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27377 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27378 this.selectorEl.hide();
27380 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27381 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27383 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27384 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27385 this.thumbEl.hide();
27387 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27388 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27390 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27391 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27392 this.errorEl.hide();
27394 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27395 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27396 this.footerEl.hide();
27398 this.setThumbBoxSize();
27404 this.fireEvent('initial', this);
27411 window.addEventListener("resize", function() { _this.resize(); } );
27413 this.bodyEl.on('click', this.beforeSelectFile, this);
27416 this.bodyEl.on('touchstart', this.onTouchStart, this);
27417 this.bodyEl.on('touchmove', this.onTouchMove, this);
27418 this.bodyEl.on('touchend', this.onTouchEnd, this);
27422 this.bodyEl.on('mousedown', this.onMouseDown, this);
27423 this.bodyEl.on('mousemove', this.onMouseMove, this);
27424 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27425 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27426 Roo.get(document).on('mouseup', this.onMouseUp, this);
27429 this.selectorEl.on('change', this.onFileSelected, this);
27435 this.baseScale = 1;
27437 this.baseRotate = 1;
27438 this.dragable = false;
27439 this.pinching = false;
27442 this.cropData = false;
27443 this.notifyEl.dom.innerHTML = this.emptyText;
27445 this.selectorEl.dom.value = '';
27449 resize : function()
27451 if(this.fireEvent('resize', this) != false){
27452 this.setThumbBoxPosition();
27453 this.setCanvasPosition();
27457 onFooterButtonClick : function(e, el, o, type)
27460 case 'rotate-left' :
27461 this.onRotateLeft(e);
27463 case 'rotate-right' :
27464 this.onRotateRight(e);
27467 this.beforeSelectFile(e);
27482 this.fireEvent('footerbuttonclick', this, type);
27485 beforeSelectFile : function(e)
27487 e.preventDefault();
27489 if(this.fireEvent('beforeselectfile', this) != false){
27490 this.selectorEl.dom.click();
27494 onFileSelected : function(e)
27496 e.preventDefault();
27498 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27502 var file = this.selectorEl.dom.files[0];
27504 if(this.fireEvent('inspect', this, file) != false){
27505 this.prepare(file);
27510 trash : function(e)
27512 this.fireEvent('trash', this);
27515 download : function(e)
27517 this.fireEvent('download', this);
27520 loadCanvas : function(src)
27522 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27526 this.imageEl = document.createElement('img');
27530 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27532 this.imageEl.src = src;
27536 onLoadCanvas : function()
27538 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27539 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27541 this.bodyEl.un('click', this.beforeSelectFile, this);
27543 this.notifyEl.hide();
27544 this.thumbEl.show();
27545 this.footerEl.show();
27547 this.baseRotateLevel();
27549 if(this.isDocument){
27550 this.setThumbBoxSize();
27553 this.setThumbBoxPosition();
27555 this.baseScaleLevel();
27561 this.canvasLoaded = true;
27564 this.maskEl.unmask();
27569 setCanvasPosition : function()
27571 if(!this.canvasEl){
27575 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27576 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27578 this.previewEl.setLeft(pw);
27579 this.previewEl.setTop(ph);
27583 onMouseDown : function(e)
27587 this.dragable = true;
27588 this.pinching = false;
27590 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27591 this.dragable = false;
27595 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27596 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27600 onMouseMove : function(e)
27604 if(!this.canvasLoaded){
27608 if (!this.dragable){
27612 var minX = Math.ceil(this.thumbEl.getLeft(true));
27613 var minY = Math.ceil(this.thumbEl.getTop(true));
27615 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27616 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27618 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27619 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27621 x = x - this.mouseX;
27622 y = y - this.mouseY;
27624 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27625 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27627 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27628 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27630 this.previewEl.setLeft(bgX);
27631 this.previewEl.setTop(bgY);
27633 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27634 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27637 onMouseUp : function(e)
27641 this.dragable = false;
27644 onMouseWheel : function(e)
27648 this.startScale = this.scale;
27650 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27652 if(!this.zoomable()){
27653 this.scale = this.startScale;
27662 zoomable : function()
27664 var minScale = this.thumbEl.getWidth() / this.minWidth;
27666 if(this.minWidth < this.minHeight){
27667 minScale = this.thumbEl.getHeight() / this.minHeight;
27670 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27671 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27675 (this.rotate == 0 || this.rotate == 180) &&
27677 width > this.imageEl.OriginWidth ||
27678 height > this.imageEl.OriginHeight ||
27679 (width < this.minWidth && height < this.minHeight)
27687 (this.rotate == 90 || this.rotate == 270) &&
27689 width > this.imageEl.OriginWidth ||
27690 height > this.imageEl.OriginHeight ||
27691 (width < this.minHeight && height < this.minWidth)
27698 !this.isDocument &&
27699 (this.rotate == 0 || this.rotate == 180) &&
27701 width < this.minWidth ||
27702 width > this.imageEl.OriginWidth ||
27703 height < this.minHeight ||
27704 height > this.imageEl.OriginHeight
27711 !this.isDocument &&
27712 (this.rotate == 90 || this.rotate == 270) &&
27714 width < this.minHeight ||
27715 width > this.imageEl.OriginWidth ||
27716 height < this.minWidth ||
27717 height > this.imageEl.OriginHeight
27727 onRotateLeft : function(e)
27729 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27731 var minScale = this.thumbEl.getWidth() / this.minWidth;
27733 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27734 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27736 this.startScale = this.scale;
27738 while (this.getScaleLevel() < minScale){
27740 this.scale = this.scale + 1;
27742 if(!this.zoomable()){
27747 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27748 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27753 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27760 this.scale = this.startScale;
27762 this.onRotateFail();
27767 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27769 if(this.isDocument){
27770 this.setThumbBoxSize();
27771 this.setThumbBoxPosition();
27772 this.setCanvasPosition();
27777 this.fireEvent('rotate', this, 'left');
27781 onRotateRight : function(e)
27783 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27785 var minScale = this.thumbEl.getWidth() / this.minWidth;
27787 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27788 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27790 this.startScale = this.scale;
27792 while (this.getScaleLevel() < minScale){
27794 this.scale = this.scale + 1;
27796 if(!this.zoomable()){
27801 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27802 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27807 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27814 this.scale = this.startScale;
27816 this.onRotateFail();
27821 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27823 if(this.isDocument){
27824 this.setThumbBoxSize();
27825 this.setThumbBoxPosition();
27826 this.setCanvasPosition();
27831 this.fireEvent('rotate', this, 'right');
27834 onRotateFail : function()
27836 this.errorEl.show(true);
27840 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27845 this.previewEl.dom.innerHTML = '';
27847 var canvasEl = document.createElement("canvas");
27849 var contextEl = canvasEl.getContext("2d");
27851 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27852 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27853 var center = this.imageEl.OriginWidth / 2;
27855 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27856 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27857 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27858 center = this.imageEl.OriginHeight / 2;
27861 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27863 contextEl.translate(center, center);
27864 contextEl.rotate(this.rotate * Math.PI / 180);
27866 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27868 this.canvasEl = document.createElement("canvas");
27870 this.contextEl = this.canvasEl.getContext("2d");
27872 switch (this.rotate) {
27875 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27876 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27878 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27883 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27884 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27886 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27887 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);
27891 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27896 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27897 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27899 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27900 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27904 this.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);
27909 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27910 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27912 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27913 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27917 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);
27924 this.previewEl.appendChild(this.canvasEl);
27926 this.setCanvasPosition();
27931 if(!this.canvasLoaded){
27935 var imageCanvas = document.createElement("canvas");
27937 var imageContext = imageCanvas.getContext("2d");
27939 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27940 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27942 var center = imageCanvas.width / 2;
27944 imageContext.translate(center, center);
27946 imageContext.rotate(this.rotate * Math.PI / 180);
27948 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27950 var canvas = document.createElement("canvas");
27952 var context = canvas.getContext("2d");
27954 canvas.width = this.minWidth;
27955 canvas.height = this.minHeight;
27957 switch (this.rotate) {
27960 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27961 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27963 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27964 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27966 var targetWidth = this.minWidth - 2 * x;
27967 var targetHeight = this.minHeight - 2 * y;
27971 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27972 scale = targetWidth / width;
27975 if(x > 0 && y == 0){
27976 scale = targetHeight / height;
27979 if(x > 0 && y > 0){
27980 scale = targetWidth / width;
27982 if(width < height){
27983 scale = targetHeight / height;
27987 context.scale(scale, scale);
27989 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27990 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27992 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27993 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27995 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28000 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28001 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28003 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28004 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28006 var targetWidth = this.minWidth - 2 * x;
28007 var targetHeight = this.minHeight - 2 * y;
28011 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28012 scale = targetWidth / width;
28015 if(x > 0 && y == 0){
28016 scale = targetHeight / height;
28019 if(x > 0 && y > 0){
28020 scale = targetWidth / width;
28022 if(width < height){
28023 scale = targetHeight / height;
28027 context.scale(scale, scale);
28029 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28030 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28032 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28033 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28035 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28037 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28042 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28043 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28045 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28046 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28048 var targetWidth = this.minWidth - 2 * x;
28049 var targetHeight = this.minHeight - 2 * y;
28053 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28054 scale = targetWidth / width;
28057 if(x > 0 && y == 0){
28058 scale = targetHeight / height;
28061 if(x > 0 && y > 0){
28062 scale = targetWidth / width;
28064 if(width < height){
28065 scale = targetHeight / height;
28069 context.scale(scale, scale);
28071 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28072 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28074 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28075 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28077 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28078 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28080 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28085 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28086 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28088 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28089 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28091 var targetWidth = this.minWidth - 2 * x;
28092 var targetHeight = this.minHeight - 2 * y;
28096 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28097 scale = targetWidth / width;
28100 if(x > 0 && y == 0){
28101 scale = targetHeight / height;
28104 if(x > 0 && y > 0){
28105 scale = targetWidth / width;
28107 if(width < height){
28108 scale = targetHeight / height;
28112 context.scale(scale, scale);
28114 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28115 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28117 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28118 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28120 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28122 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28129 this.cropData = canvas.toDataURL(this.cropType);
28131 if(this.fireEvent('crop', this, this.cropData) !== false){
28132 this.process(this.file, this.cropData);
28139 setThumbBoxSize : function()
28143 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28144 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28145 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28147 this.minWidth = width;
28148 this.minHeight = height;
28150 if(this.rotate == 90 || this.rotate == 270){
28151 this.minWidth = height;
28152 this.minHeight = width;
28157 width = Math.ceil(this.minWidth * height / this.minHeight);
28159 if(this.minWidth > this.minHeight){
28161 height = Math.ceil(this.minHeight * width / this.minWidth);
28164 this.thumbEl.setStyle({
28165 width : width + 'px',
28166 height : height + 'px'
28173 setThumbBoxPosition : function()
28175 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28176 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28178 this.thumbEl.setLeft(x);
28179 this.thumbEl.setTop(y);
28183 baseRotateLevel : function()
28185 this.baseRotate = 1;
28188 typeof(this.exif) != 'undefined' &&
28189 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28190 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28192 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28195 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28199 baseScaleLevel : function()
28203 if(this.isDocument){
28205 if(this.baseRotate == 6 || this.baseRotate == 8){
28207 height = this.thumbEl.getHeight();
28208 this.baseScale = height / this.imageEl.OriginWidth;
28210 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28211 width = this.thumbEl.getWidth();
28212 this.baseScale = width / this.imageEl.OriginHeight;
28218 height = this.thumbEl.getHeight();
28219 this.baseScale = height / this.imageEl.OriginHeight;
28221 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28222 width = this.thumbEl.getWidth();
28223 this.baseScale = width / this.imageEl.OriginWidth;
28229 if(this.baseRotate == 6 || this.baseRotate == 8){
28231 width = this.thumbEl.getHeight();
28232 this.baseScale = width / this.imageEl.OriginHeight;
28234 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28235 height = this.thumbEl.getWidth();
28236 this.baseScale = height / this.imageEl.OriginHeight;
28239 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28240 height = this.thumbEl.getWidth();
28241 this.baseScale = height / this.imageEl.OriginHeight;
28243 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28244 width = this.thumbEl.getHeight();
28245 this.baseScale = width / this.imageEl.OriginWidth;
28252 width = this.thumbEl.getWidth();
28253 this.baseScale = width / this.imageEl.OriginWidth;
28255 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28256 height = this.thumbEl.getHeight();
28257 this.baseScale = height / this.imageEl.OriginHeight;
28260 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28262 height = this.thumbEl.getHeight();
28263 this.baseScale = height / this.imageEl.OriginHeight;
28265 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28266 width = this.thumbEl.getWidth();
28267 this.baseScale = width / this.imageEl.OriginWidth;
28275 getScaleLevel : function()
28277 return this.baseScale * Math.pow(1.1, this.scale);
28280 onTouchStart : function(e)
28282 if(!this.canvasLoaded){
28283 this.beforeSelectFile(e);
28287 var touches = e.browserEvent.touches;
28293 if(touches.length == 1){
28294 this.onMouseDown(e);
28298 if(touches.length != 2){
28304 for(var i = 0, finger; finger = touches[i]; i++){
28305 coords.push(finger.pageX, finger.pageY);
28308 var x = Math.pow(coords[0] - coords[2], 2);
28309 var y = Math.pow(coords[1] - coords[3], 2);
28311 this.startDistance = Math.sqrt(x + y);
28313 this.startScale = this.scale;
28315 this.pinching = true;
28316 this.dragable = false;
28320 onTouchMove : function(e)
28322 if(!this.pinching && !this.dragable){
28326 var touches = e.browserEvent.touches;
28333 this.onMouseMove(e);
28339 for(var i = 0, finger; finger = touches[i]; i++){
28340 coords.push(finger.pageX, finger.pageY);
28343 var x = Math.pow(coords[0] - coords[2], 2);
28344 var y = Math.pow(coords[1] - coords[3], 2);
28346 this.endDistance = Math.sqrt(x + y);
28348 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28350 if(!this.zoomable()){
28351 this.scale = this.startScale;
28359 onTouchEnd : function(e)
28361 this.pinching = false;
28362 this.dragable = false;
28366 process : function(file, crop)
28369 this.maskEl.mask(this.loadingText);
28372 this.xhr = new XMLHttpRequest();
28374 file.xhr = this.xhr;
28376 this.xhr.open(this.method, this.url, true);
28379 "Accept": "application/json",
28380 "Cache-Control": "no-cache",
28381 "X-Requested-With": "XMLHttpRequest"
28384 for (var headerName in headers) {
28385 var headerValue = headers[headerName];
28387 this.xhr.setRequestHeader(headerName, headerValue);
28393 this.xhr.onload = function()
28395 _this.xhrOnLoad(_this.xhr);
28398 this.xhr.onerror = function()
28400 _this.xhrOnError(_this.xhr);
28403 var formData = new FormData();
28405 formData.append('returnHTML', 'NO');
28408 formData.append('crop', crop);
28411 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28412 formData.append(this.paramName, file, file.name);
28415 if(typeof(file.filename) != 'undefined'){
28416 formData.append('filename', file.filename);
28419 if(typeof(file.mimetype) != 'undefined'){
28420 formData.append('mimetype', file.mimetype);
28423 if(this.fireEvent('arrange', this, formData) != false){
28424 this.xhr.send(formData);
28428 xhrOnLoad : function(xhr)
28431 this.maskEl.unmask();
28434 if (xhr.readyState !== 4) {
28435 this.fireEvent('exception', this, xhr);
28439 var response = Roo.decode(xhr.responseText);
28441 if(!response.success){
28442 this.fireEvent('exception', this, xhr);
28446 var response = Roo.decode(xhr.responseText);
28448 this.fireEvent('upload', this, response);
28452 xhrOnError : function()
28455 this.maskEl.unmask();
28458 Roo.log('xhr on error');
28460 var response = Roo.decode(xhr.responseText);
28466 prepare : function(file)
28469 this.maskEl.mask(this.loadingText);
28475 if(typeof(file) === 'string'){
28476 this.loadCanvas(file);
28480 if(!file || !this.urlAPI){
28485 this.cropType = file.type;
28489 if(this.fireEvent('prepare', this, this.file) != false){
28491 var reader = new FileReader();
28493 reader.onload = function (e) {
28494 if (e.target.error) {
28495 Roo.log(e.target.error);
28499 var buffer = e.target.result,
28500 dataView = new DataView(buffer),
28502 maxOffset = dataView.byteLength - 4,
28506 if (dataView.getUint16(0) === 0xffd8) {
28507 while (offset < maxOffset) {
28508 markerBytes = dataView.getUint16(offset);
28510 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28511 markerLength = dataView.getUint16(offset + 2) + 2;
28512 if (offset + markerLength > dataView.byteLength) {
28513 Roo.log('Invalid meta data: Invalid segment size.');
28517 if(markerBytes == 0xffe1){
28518 _this.parseExifData(
28525 offset += markerLength;
28535 var url = _this.urlAPI.createObjectURL(_this.file);
28537 _this.loadCanvas(url);
28542 reader.readAsArrayBuffer(this.file);
28548 parseExifData : function(dataView, offset, length)
28550 var tiffOffset = offset + 10,
28554 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28555 // No Exif data, might be XMP data instead
28559 // Check for the ASCII code for "Exif" (0x45786966):
28560 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28561 // No Exif data, might be XMP data instead
28564 if (tiffOffset + 8 > dataView.byteLength) {
28565 Roo.log('Invalid Exif data: Invalid segment size.');
28568 // Check for the two null bytes:
28569 if (dataView.getUint16(offset + 8) !== 0x0000) {
28570 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28573 // Check the byte alignment:
28574 switch (dataView.getUint16(tiffOffset)) {
28576 littleEndian = true;
28579 littleEndian = false;
28582 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28585 // Check for the TIFF tag marker (0x002A):
28586 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28587 Roo.log('Invalid Exif data: Missing TIFF marker.');
28590 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28591 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28593 this.parseExifTags(
28596 tiffOffset + dirOffset,
28601 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28606 if (dirOffset + 6 > dataView.byteLength) {
28607 Roo.log('Invalid Exif data: Invalid directory offset.');
28610 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28611 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28612 if (dirEndOffset + 4 > dataView.byteLength) {
28613 Roo.log('Invalid Exif data: Invalid directory size.');
28616 for (i = 0; i < tagsNumber; i += 1) {
28620 dirOffset + 2 + 12 * i, // tag offset
28624 // Return the offset to the next directory:
28625 return dataView.getUint32(dirEndOffset, littleEndian);
28628 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28630 var tag = dataView.getUint16(offset, littleEndian);
28632 this.exif[tag] = this.getExifValue(
28636 dataView.getUint16(offset + 2, littleEndian), // tag type
28637 dataView.getUint32(offset + 4, littleEndian), // tag length
28642 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28644 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28653 Roo.log('Invalid Exif data: Invalid tag type.');
28657 tagSize = tagType.size * length;
28658 // Determine if the value is contained in the dataOffset bytes,
28659 // or if the value at the dataOffset is a pointer to the actual data:
28660 dataOffset = tagSize > 4 ?
28661 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28662 if (dataOffset + tagSize > dataView.byteLength) {
28663 Roo.log('Invalid Exif data: Invalid data offset.');
28666 if (length === 1) {
28667 return tagType.getValue(dataView, dataOffset, littleEndian);
28670 for (i = 0; i < length; i += 1) {
28671 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28674 if (tagType.ascii) {
28676 // Concatenate the chars:
28677 for (i = 0; i < values.length; i += 1) {
28679 // Ignore the terminating NULL byte(s):
28680 if (c === '\u0000') {
28692 Roo.apply(Roo.bootstrap.UploadCropbox, {
28694 'Orientation': 0x0112
28698 1: 0, //'top-left',
28700 3: 180, //'bottom-right',
28701 // 4: 'bottom-left',
28703 6: 90, //'right-top',
28704 // 7: 'right-bottom',
28705 8: 270 //'left-bottom'
28709 // byte, 8-bit unsigned int:
28711 getValue: function (dataView, dataOffset) {
28712 return dataView.getUint8(dataOffset);
28716 // ascii, 8-bit byte:
28718 getValue: function (dataView, dataOffset) {
28719 return String.fromCharCode(dataView.getUint8(dataOffset));
28724 // short, 16 bit int:
28726 getValue: function (dataView, dataOffset, littleEndian) {
28727 return dataView.getUint16(dataOffset, littleEndian);
28731 // long, 32 bit int:
28733 getValue: function (dataView, dataOffset, littleEndian) {
28734 return dataView.getUint32(dataOffset, littleEndian);
28738 // rational = two long values, first is numerator, second is denominator:
28740 getValue: function (dataView, dataOffset, littleEndian) {
28741 return dataView.getUint32(dataOffset, littleEndian) /
28742 dataView.getUint32(dataOffset + 4, littleEndian);
28746 // slong, 32 bit signed int:
28748 getValue: function (dataView, dataOffset, littleEndian) {
28749 return dataView.getInt32(dataOffset, littleEndian);
28753 // srational, two slongs, first is numerator, second is denominator:
28755 getValue: function (dataView, dataOffset, littleEndian) {
28756 return dataView.getInt32(dataOffset, littleEndian) /
28757 dataView.getInt32(dataOffset + 4, littleEndian);
28767 cls : 'btn-group roo-upload-cropbox-rotate-left',
28768 action : 'rotate-left',
28772 cls : 'btn btn-default',
28773 html : '<i class="fa fa-undo"></i>'
28779 cls : 'btn-group roo-upload-cropbox-picture',
28780 action : 'picture',
28784 cls : 'btn btn-default',
28785 html : '<i class="fa fa-picture-o"></i>'
28791 cls : 'btn-group roo-upload-cropbox-rotate-right',
28792 action : 'rotate-right',
28796 cls : 'btn btn-default',
28797 html : '<i class="fa fa-repeat"></i>'
28805 cls : 'btn-group roo-upload-cropbox-rotate-left',
28806 action : 'rotate-left',
28810 cls : 'btn btn-default',
28811 html : '<i class="fa fa-undo"></i>'
28817 cls : 'btn-group roo-upload-cropbox-download',
28818 action : 'download',
28822 cls : 'btn btn-default',
28823 html : '<i class="fa fa-download"></i>'
28829 cls : 'btn-group roo-upload-cropbox-crop',
28834 cls : 'btn btn-default',
28835 html : '<i class="fa fa-crop"></i>'
28841 cls : 'btn-group roo-upload-cropbox-trash',
28846 cls : 'btn btn-default',
28847 html : '<i class="fa fa-trash"></i>'
28853 cls : 'btn-group roo-upload-cropbox-rotate-right',
28854 action : 'rotate-right',
28858 cls : 'btn btn-default',
28859 html : '<i class="fa fa-repeat"></i>'
28867 cls : 'btn-group roo-upload-cropbox-rotate-left',
28868 action : 'rotate-left',
28872 cls : 'btn btn-default',
28873 html : '<i class="fa fa-undo"></i>'
28879 cls : 'btn-group roo-upload-cropbox-rotate-right',
28880 action : 'rotate-right',
28884 cls : 'btn btn-default',
28885 html : '<i class="fa fa-repeat"></i>'
28898 * @class Roo.bootstrap.DocumentManager
28899 * @extends Roo.bootstrap.Component
28900 * Bootstrap DocumentManager class
28901 * @cfg {String} paramName default 'imageUpload'
28902 * @cfg {String} toolTipName default 'filename'
28903 * @cfg {String} method default POST
28904 * @cfg {String} url action url
28905 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28906 * @cfg {Boolean} multiple multiple upload default true
28907 * @cfg {Number} thumbSize default 300
28908 * @cfg {String} fieldLabel
28909 * @cfg {Number} labelWidth default 4
28910 * @cfg {String} labelAlign (left|top) default left
28911 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28912 * @cfg {Number} labellg set the width of label (1-12)
28913 * @cfg {Number} labelmd set the width of label (1-12)
28914 * @cfg {Number} labelsm set the width of label (1-12)
28915 * @cfg {Number} labelxs set the width of label (1-12)
28918 * Create a new DocumentManager
28919 * @param {Object} config The config object
28922 Roo.bootstrap.DocumentManager = function(config){
28923 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28926 this.delegates = [];
28931 * Fire when initial the DocumentManager
28932 * @param {Roo.bootstrap.DocumentManager} this
28937 * inspect selected file
28938 * @param {Roo.bootstrap.DocumentManager} this
28939 * @param {File} file
28944 * Fire when xhr load exception
28945 * @param {Roo.bootstrap.DocumentManager} this
28946 * @param {XMLHttpRequest} xhr
28948 "exception" : true,
28950 * @event afterupload
28951 * Fire when xhr load exception
28952 * @param {Roo.bootstrap.DocumentManager} this
28953 * @param {XMLHttpRequest} xhr
28955 "afterupload" : true,
28958 * prepare the form data
28959 * @param {Roo.bootstrap.DocumentManager} this
28960 * @param {Object} formData
28965 * Fire when remove the file
28966 * @param {Roo.bootstrap.DocumentManager} this
28967 * @param {Object} file
28972 * Fire after refresh the file
28973 * @param {Roo.bootstrap.DocumentManager} this
28978 * Fire after click the image
28979 * @param {Roo.bootstrap.DocumentManager} this
28980 * @param {Object} file
28985 * Fire when upload a image and editable set to true
28986 * @param {Roo.bootstrap.DocumentManager} this
28987 * @param {Object} file
28991 * @event beforeselectfile
28992 * Fire before select file
28993 * @param {Roo.bootstrap.DocumentManager} this
28995 "beforeselectfile" : true,
28998 * Fire before process file
28999 * @param {Roo.bootstrap.DocumentManager} this
29000 * @param {Object} file
29004 * @event previewrendered
29005 * Fire when preview rendered
29006 * @param {Roo.bootstrap.DocumentManager} this
29007 * @param {Object} file
29009 "previewrendered" : true,
29012 "previewResize" : true
29017 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
29026 paramName : 'imageUpload',
29027 toolTipName : 'filename',
29030 labelAlign : 'left',
29040 getAutoCreate : function()
29042 var managerWidget = {
29044 cls : 'roo-document-manager',
29048 cls : 'roo-document-manager-selector',
29053 cls : 'roo-document-manager-uploader',
29057 cls : 'roo-document-manager-upload-btn',
29058 html : '<i class="fa fa-plus"></i>'
29069 cls : 'column col-md-12',
29074 if(this.fieldLabel.length){
29079 cls : 'column col-md-12',
29080 html : this.fieldLabel
29084 cls : 'column col-md-12',
29089 if(this.labelAlign == 'left'){
29094 html : this.fieldLabel
29103 if(this.labelWidth > 12){
29104 content[0].style = "width: " + this.labelWidth + 'px';
29107 if(this.labelWidth < 13 && this.labelmd == 0){
29108 this.labelmd = this.labelWidth;
29111 if(this.labellg > 0){
29112 content[0].cls += ' col-lg-' + this.labellg;
29113 content[1].cls += ' col-lg-' + (12 - this.labellg);
29116 if(this.labelmd > 0){
29117 content[0].cls += ' col-md-' + this.labelmd;
29118 content[1].cls += ' col-md-' + (12 - this.labelmd);
29121 if(this.labelsm > 0){
29122 content[0].cls += ' col-sm-' + this.labelsm;
29123 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29126 if(this.labelxs > 0){
29127 content[0].cls += ' col-xs-' + this.labelxs;
29128 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29136 cls : 'row clearfix',
29144 initEvents : function()
29146 this.managerEl = this.el.select('.roo-document-manager', true).first();
29147 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29149 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29150 this.selectorEl.hide();
29153 this.selectorEl.attr('multiple', 'multiple');
29156 this.selectorEl.on('change', this.onFileSelected, this);
29158 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29159 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29161 this.uploader.on('click', this.onUploaderClick, this);
29163 this.renderProgressDialog();
29167 window.addEventListener("resize", function() { _this.refresh(); } );
29169 this.fireEvent('initial', this);
29172 renderProgressDialog : function()
29176 this.progressDialog = new Roo.bootstrap.Modal({
29177 cls : 'roo-document-manager-progress-dialog',
29178 allow_close : false,
29188 btnclick : function() {
29189 _this.uploadCancel();
29195 this.progressDialog.render(Roo.get(document.body));
29197 this.progress = new Roo.bootstrap.Progress({
29198 cls : 'roo-document-manager-progress',
29203 this.progress.render(this.progressDialog.getChildContainer());
29205 this.progressBar = new Roo.bootstrap.ProgressBar({
29206 cls : 'roo-document-manager-progress-bar',
29209 aria_valuemax : 12,
29213 this.progressBar.render(this.progress.getChildContainer());
29216 onUploaderClick : function(e)
29218 e.preventDefault();
29220 if(this.fireEvent('beforeselectfile', this) != false){
29221 this.selectorEl.dom.click();
29226 onFileSelected : function(e)
29228 e.preventDefault();
29230 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29234 Roo.each(this.selectorEl.dom.files, function(file){
29235 if(this.fireEvent('inspect', this, file) != false){
29236 this.files.push(file);
29246 this.selectorEl.dom.value = '';
29248 if(!this.files || !this.files.length){
29252 if(this.boxes > 0 && this.files.length > this.boxes){
29253 this.files = this.files.slice(0, this.boxes);
29256 this.uploader.show();
29258 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29259 this.uploader.hide();
29268 Roo.each(this.files, function(file){
29270 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29271 var f = this.renderPreview(file);
29276 if(file.type.indexOf('image') != -1){
29277 this.delegates.push(
29279 _this.process(file);
29280 }).createDelegate(this)
29288 _this.process(file);
29289 }).createDelegate(this)
29294 this.files = files;
29296 this.delegates = this.delegates.concat(docs);
29298 if(!this.delegates.length){
29303 this.progressBar.aria_valuemax = this.delegates.length;
29310 arrange : function()
29312 if(!this.delegates.length){
29313 this.progressDialog.hide();
29318 var delegate = this.delegates.shift();
29320 this.progressDialog.show();
29322 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29324 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29329 refresh : function()
29331 this.uploader.show();
29333 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29334 this.uploader.hide();
29337 Roo.isTouch ? this.closable(false) : this.closable(true);
29339 this.fireEvent('refresh', this);
29342 onRemove : function(e, el, o)
29344 e.preventDefault();
29346 this.fireEvent('remove', this, o);
29350 remove : function(o)
29354 Roo.each(this.files, function(file){
29355 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29364 this.files = files;
29371 Roo.each(this.files, function(file){
29376 file.target.remove();
29385 onClick : function(e, el, o)
29387 e.preventDefault();
29389 this.fireEvent('click', this, o);
29393 closable : function(closable)
29395 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29397 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29409 xhrOnLoad : function(xhr)
29411 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29415 if (xhr.readyState !== 4) {
29417 this.fireEvent('exception', this, xhr);
29421 var response = Roo.decode(xhr.responseText);
29423 if(!response.success){
29425 this.fireEvent('exception', this, xhr);
29429 var file = this.renderPreview(response.data);
29431 this.files.push(file);
29435 this.fireEvent('afterupload', this, xhr);
29439 xhrOnError : function(xhr)
29441 Roo.log('xhr on error');
29443 var response = Roo.decode(xhr.responseText);
29450 process : function(file)
29452 if(this.fireEvent('process', this, file) !== false){
29453 if(this.editable && file.type.indexOf('image') != -1){
29454 this.fireEvent('edit', this, file);
29458 this.uploadStart(file, false);
29465 uploadStart : function(file, crop)
29467 this.xhr = new XMLHttpRequest();
29469 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29474 file.xhr = this.xhr;
29476 this.managerEl.createChild({
29478 cls : 'roo-document-manager-loading',
29482 tooltip : file.name,
29483 cls : 'roo-document-manager-thumb',
29484 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29490 this.xhr.open(this.method, this.url, true);
29493 "Accept": "application/json",
29494 "Cache-Control": "no-cache",
29495 "X-Requested-With": "XMLHttpRequest"
29498 for (var headerName in headers) {
29499 var headerValue = headers[headerName];
29501 this.xhr.setRequestHeader(headerName, headerValue);
29507 this.xhr.onload = function()
29509 _this.xhrOnLoad(_this.xhr);
29512 this.xhr.onerror = function()
29514 _this.xhrOnError(_this.xhr);
29517 var formData = new FormData();
29519 formData.append('returnHTML', 'NO');
29522 formData.append('crop', crop);
29525 formData.append(this.paramName, file, file.name);
29532 if(this.fireEvent('prepare', this, formData, options) != false){
29534 if(options.manually){
29538 this.xhr.send(formData);
29542 this.uploadCancel();
29545 uploadCancel : function()
29551 this.delegates = [];
29553 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29560 renderPreview : function(file)
29562 if(typeof(file.target) != 'undefined' && file.target){
29566 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29568 var previewEl = this.managerEl.createChild({
29570 cls : 'roo-document-manager-preview',
29574 tooltip : file[this.toolTipName],
29575 cls : 'roo-document-manager-thumb',
29576 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29581 html : '<i class="fa fa-times-circle"></i>'
29586 var close = previewEl.select('button.close', true).first();
29588 close.on('click', this.onRemove, this, file);
29590 file.target = previewEl;
29592 var image = previewEl.select('img', true).first();
29596 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29598 image.on('click', this.onClick, this, file);
29600 this.fireEvent('previewrendered', this, file);
29606 onPreviewLoad : function(file, image)
29608 if(typeof(file.target) == 'undefined' || !file.target){
29612 var width = image.dom.naturalWidth || image.dom.width;
29613 var height = image.dom.naturalHeight || image.dom.height;
29615 if(!this.previewResize) {
29619 if(width > height){
29620 file.target.addClass('wide');
29624 file.target.addClass('tall');
29629 uploadFromSource : function(file, crop)
29631 this.xhr = new XMLHttpRequest();
29633 this.managerEl.createChild({
29635 cls : 'roo-document-manager-loading',
29639 tooltip : file.name,
29640 cls : 'roo-document-manager-thumb',
29641 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29647 this.xhr.open(this.method, this.url, true);
29650 "Accept": "application/json",
29651 "Cache-Control": "no-cache",
29652 "X-Requested-With": "XMLHttpRequest"
29655 for (var headerName in headers) {
29656 var headerValue = headers[headerName];
29658 this.xhr.setRequestHeader(headerName, headerValue);
29664 this.xhr.onload = function()
29666 _this.xhrOnLoad(_this.xhr);
29669 this.xhr.onerror = function()
29671 _this.xhrOnError(_this.xhr);
29674 var formData = new FormData();
29676 formData.append('returnHTML', 'NO');
29678 formData.append('crop', crop);
29680 if(typeof(file.filename) != 'undefined'){
29681 formData.append('filename', file.filename);
29684 if(typeof(file.mimetype) != 'undefined'){
29685 formData.append('mimetype', file.mimetype);
29690 if(this.fireEvent('prepare', this, formData) != false){
29691 this.xhr.send(formData);
29701 * @class Roo.bootstrap.DocumentViewer
29702 * @extends Roo.bootstrap.Component
29703 * Bootstrap DocumentViewer class
29704 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29705 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29708 * Create a new DocumentViewer
29709 * @param {Object} config The config object
29712 Roo.bootstrap.DocumentViewer = function(config){
29713 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29718 * Fire after initEvent
29719 * @param {Roo.bootstrap.DocumentViewer} this
29725 * @param {Roo.bootstrap.DocumentViewer} this
29730 * Fire after download button
29731 * @param {Roo.bootstrap.DocumentViewer} this
29736 * Fire after trash button
29737 * @param {Roo.bootstrap.DocumentViewer} this
29744 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29746 showDownload : true,
29750 getAutoCreate : function()
29754 cls : 'roo-document-viewer',
29758 cls : 'roo-document-viewer-body',
29762 cls : 'roo-document-viewer-thumb',
29766 cls : 'roo-document-viewer-image'
29774 cls : 'roo-document-viewer-footer',
29777 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29781 cls : 'btn-group roo-document-viewer-download',
29785 cls : 'btn btn-default',
29786 html : '<i class="fa fa-download"></i>'
29792 cls : 'btn-group roo-document-viewer-trash',
29796 cls : 'btn btn-default',
29797 html : '<i class="fa fa-trash"></i>'
29810 initEvents : function()
29812 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29813 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29815 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29816 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29818 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29819 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29821 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29822 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29824 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29825 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29827 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29828 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29830 this.bodyEl.on('click', this.onClick, this);
29831 this.downloadBtn.on('click', this.onDownload, this);
29832 this.trashBtn.on('click', this.onTrash, this);
29834 this.downloadBtn.hide();
29835 this.trashBtn.hide();
29837 if(this.showDownload){
29838 this.downloadBtn.show();
29841 if(this.showTrash){
29842 this.trashBtn.show();
29845 if(!this.showDownload && !this.showTrash) {
29846 this.footerEl.hide();
29851 initial : function()
29853 this.fireEvent('initial', this);
29857 onClick : function(e)
29859 e.preventDefault();
29861 this.fireEvent('click', this);
29864 onDownload : function(e)
29866 e.preventDefault();
29868 this.fireEvent('download', this);
29871 onTrash : function(e)
29873 e.preventDefault();
29875 this.fireEvent('trash', this);
29887 * @class Roo.bootstrap.NavProgressBar
29888 * @extends Roo.bootstrap.Component
29889 * Bootstrap NavProgressBar class
29892 * Create a new nav progress bar
29893 * @param {Object} config The config object
29896 Roo.bootstrap.NavProgressBar = function(config){
29897 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29899 this.bullets = this.bullets || [];
29901 // Roo.bootstrap.NavProgressBar.register(this);
29905 * Fires when the active item changes
29906 * @param {Roo.bootstrap.NavProgressBar} this
29907 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29908 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29915 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29920 getAutoCreate : function()
29922 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29926 cls : 'roo-navigation-bar-group',
29930 cls : 'roo-navigation-top-bar'
29934 cls : 'roo-navigation-bullets-bar',
29938 cls : 'roo-navigation-bar'
29945 cls : 'roo-navigation-bottom-bar'
29955 initEvents: function()
29960 onRender : function(ct, position)
29962 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29964 if(this.bullets.length){
29965 Roo.each(this.bullets, function(b){
29974 addItem : function(cfg)
29976 var item = new Roo.bootstrap.NavProgressItem(cfg);
29978 item.parentId = this.id;
29979 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29982 var top = new Roo.bootstrap.Element({
29984 cls : 'roo-navigation-bar-text'
29987 var bottom = new Roo.bootstrap.Element({
29989 cls : 'roo-navigation-bar-text'
29992 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29993 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29995 var topText = new Roo.bootstrap.Element({
29997 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30000 var bottomText = new Roo.bootstrap.Element({
30002 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30005 topText.onRender(top.el, null);
30006 bottomText.onRender(bottom.el, null);
30009 item.bottomEl = bottom;
30012 this.barItems.push(item);
30017 getActive : function()
30019 var active = false;
30021 Roo.each(this.barItems, function(v){
30023 if (!v.isActive()) {
30035 setActiveItem : function(item)
30039 Roo.each(this.barItems, function(v){
30040 if (v.rid == item.rid) {
30044 if (v.isActive()) {
30045 v.setActive(false);
30050 item.setActive(true);
30052 this.fireEvent('changed', this, item, prev);
30055 getBarItem: function(rid)
30059 Roo.each(this.barItems, function(e) {
30060 if (e.rid != rid) {
30071 indexOfItem : function(item)
30075 Roo.each(this.barItems, function(v, i){
30077 if (v.rid != item.rid) {
30088 setActiveNext : function()
30090 var i = this.indexOfItem(this.getActive());
30092 if (i > this.barItems.length) {
30096 this.setActiveItem(this.barItems[i+1]);
30099 setActivePrev : function()
30101 var i = this.indexOfItem(this.getActive());
30107 this.setActiveItem(this.barItems[i-1]);
30110 format : function()
30112 if(!this.barItems.length){
30116 var width = 100 / this.barItems.length;
30118 Roo.each(this.barItems, function(i){
30119 i.el.setStyle('width', width + '%');
30120 i.topEl.el.setStyle('width', width + '%');
30121 i.bottomEl.el.setStyle('width', width + '%');
30130 * Nav Progress Item
30135 * @class Roo.bootstrap.NavProgressItem
30136 * @extends Roo.bootstrap.Component
30137 * Bootstrap NavProgressItem class
30138 * @cfg {String} rid the reference id
30139 * @cfg {Boolean} active (true|false) Is item active default false
30140 * @cfg {Boolean} disabled (true|false) Is item active default false
30141 * @cfg {String} html
30142 * @cfg {String} position (top|bottom) text position default bottom
30143 * @cfg {String} icon show icon instead of number
30146 * Create a new NavProgressItem
30147 * @param {Object} config The config object
30149 Roo.bootstrap.NavProgressItem = function(config){
30150 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30155 * The raw click event for the entire grid.
30156 * @param {Roo.bootstrap.NavProgressItem} this
30157 * @param {Roo.EventObject} e
30164 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30170 position : 'bottom',
30173 getAutoCreate : function()
30175 var iconCls = 'roo-navigation-bar-item-icon';
30177 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30181 cls: 'roo-navigation-bar-item',
30191 cfg.cls += ' active';
30194 cfg.cls += ' disabled';
30200 disable : function()
30202 this.setDisabled(true);
30205 enable : function()
30207 this.setDisabled(false);
30210 initEvents: function()
30212 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30214 this.iconEl.on('click', this.onClick, this);
30217 onClick : function(e)
30219 e.preventDefault();
30225 if(this.fireEvent('click', this, e) === false){
30229 this.parent().setActiveItem(this);
30232 isActive: function ()
30234 return this.active;
30237 setActive : function(state)
30239 if(this.active == state){
30243 this.active = state;
30246 this.el.addClass('active');
30250 this.el.removeClass('active');
30255 setDisabled : function(state)
30257 if(this.disabled == state){
30261 this.disabled = state;
30264 this.el.addClass('disabled');
30268 this.el.removeClass('disabled');
30271 tooltipEl : function()
30273 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30286 * @class Roo.bootstrap.FieldLabel
30287 * @extends Roo.bootstrap.Component
30288 * Bootstrap FieldLabel class
30289 * @cfg {String} html contents of the element
30290 * @cfg {String} tag tag of the element default label
30291 * @cfg {String} cls class of the element
30292 * @cfg {String} target label target
30293 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30294 * @cfg {String} invalidClass default "text-warning"
30295 * @cfg {String} validClass default "text-success"
30296 * @cfg {String} iconTooltip default "This field is required"
30297 * @cfg {String} indicatorpos (left|right) default left
30300 * Create a new FieldLabel
30301 * @param {Object} config The config object
30304 Roo.bootstrap.FieldLabel = function(config){
30305 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30310 * Fires after the field has been marked as invalid.
30311 * @param {Roo.form.FieldLabel} this
30312 * @param {String} msg The validation message
30317 * Fires after the field has been validated with no errors.
30318 * @param {Roo.form.FieldLabel} this
30324 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30331 invalidClass : 'has-warning',
30332 validClass : 'has-success',
30333 iconTooltip : 'This field is required',
30334 indicatorpos : 'left',
30336 getAutoCreate : function(){
30339 if (!this.allowBlank) {
30345 cls : 'roo-bootstrap-field-label ' + this.cls,
30350 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30351 tooltip : this.iconTooltip
30360 if(this.indicatorpos == 'right'){
30363 cls : 'roo-bootstrap-field-label ' + this.cls,
30372 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30373 tooltip : this.iconTooltip
30382 initEvents: function()
30384 Roo.bootstrap.Element.superclass.initEvents.call(this);
30386 this.indicator = this.indicatorEl();
30388 if(this.indicator){
30389 this.indicator.removeClass('visible');
30390 this.indicator.addClass('invisible');
30393 Roo.bootstrap.FieldLabel.register(this);
30396 indicatorEl : function()
30398 var indicator = this.el.select('i.roo-required-indicator',true).first();
30409 * Mark this field as valid
30411 markValid : function()
30413 if(this.indicator){
30414 this.indicator.removeClass('visible');
30415 this.indicator.addClass('invisible');
30418 this.el.removeClass(this.invalidClass);
30420 this.el.addClass(this.validClass);
30422 this.fireEvent('valid', this);
30426 * Mark this field as invalid
30427 * @param {String} msg The validation message
30429 markInvalid : function(msg)
30431 if(this.indicator){
30432 this.indicator.removeClass('invisible');
30433 this.indicator.addClass('visible');
30436 this.el.removeClass(this.validClass);
30438 this.el.addClass(this.invalidClass);
30440 this.fireEvent('invalid', this, msg);
30446 Roo.apply(Roo.bootstrap.FieldLabel, {
30451 * register a FieldLabel Group
30452 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30454 register : function(label)
30456 if(this.groups.hasOwnProperty(label.target)){
30460 this.groups[label.target] = label;
30464 * fetch a FieldLabel Group based on the target
30465 * @param {string} target
30466 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30468 get: function(target) {
30469 if (typeof(this.groups[target]) == 'undefined') {
30473 return this.groups[target] ;
30482 * page DateSplitField.
30488 * @class Roo.bootstrap.DateSplitField
30489 * @extends Roo.bootstrap.Component
30490 * Bootstrap DateSplitField class
30491 * @cfg {string} fieldLabel - the label associated
30492 * @cfg {Number} labelWidth set the width of label (0-12)
30493 * @cfg {String} labelAlign (top|left)
30494 * @cfg {Boolean} dayAllowBlank (true|false) default false
30495 * @cfg {Boolean} monthAllowBlank (true|false) default false
30496 * @cfg {Boolean} yearAllowBlank (true|false) default false
30497 * @cfg {string} dayPlaceholder
30498 * @cfg {string} monthPlaceholder
30499 * @cfg {string} yearPlaceholder
30500 * @cfg {string} dayFormat default 'd'
30501 * @cfg {string} monthFormat default 'm'
30502 * @cfg {string} yearFormat default 'Y'
30503 * @cfg {Number} labellg set the width of label (1-12)
30504 * @cfg {Number} labelmd set the width of label (1-12)
30505 * @cfg {Number} labelsm set the width of label (1-12)
30506 * @cfg {Number} labelxs set the width of label (1-12)
30510 * Create a new DateSplitField
30511 * @param {Object} config The config object
30514 Roo.bootstrap.DateSplitField = function(config){
30515 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30521 * getting the data of years
30522 * @param {Roo.bootstrap.DateSplitField} this
30523 * @param {Object} years
30528 * getting the data of days
30529 * @param {Roo.bootstrap.DateSplitField} this
30530 * @param {Object} days
30535 * Fires after the field has been marked as invalid.
30536 * @param {Roo.form.Field} this
30537 * @param {String} msg The validation message
30542 * Fires after the field has been validated with no errors.
30543 * @param {Roo.form.Field} this
30549 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30552 labelAlign : 'top',
30554 dayAllowBlank : false,
30555 monthAllowBlank : false,
30556 yearAllowBlank : false,
30557 dayPlaceholder : '',
30558 monthPlaceholder : '',
30559 yearPlaceholder : '',
30563 isFormField : true,
30569 getAutoCreate : function()
30573 cls : 'row roo-date-split-field-group',
30578 cls : 'form-hidden-field roo-date-split-field-group-value',
30584 var labelCls = 'col-md-12';
30585 var contentCls = 'col-md-4';
30587 if(this.fieldLabel){
30591 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30595 html : this.fieldLabel
30600 if(this.labelAlign == 'left'){
30602 if(this.labelWidth > 12){
30603 label.style = "width: " + this.labelWidth + 'px';
30606 if(this.labelWidth < 13 && this.labelmd == 0){
30607 this.labelmd = this.labelWidth;
30610 if(this.labellg > 0){
30611 labelCls = ' col-lg-' + this.labellg;
30612 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30615 if(this.labelmd > 0){
30616 labelCls = ' col-md-' + this.labelmd;
30617 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30620 if(this.labelsm > 0){
30621 labelCls = ' col-sm-' + this.labelsm;
30622 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30625 if(this.labelxs > 0){
30626 labelCls = ' col-xs-' + this.labelxs;
30627 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30631 label.cls += ' ' + labelCls;
30633 cfg.cn.push(label);
30636 Roo.each(['day', 'month', 'year'], function(t){
30639 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30646 inputEl: function ()
30648 return this.el.select('.roo-date-split-field-group-value', true).first();
30651 onRender : function(ct, position)
30655 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30657 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30659 this.dayField = new Roo.bootstrap.ComboBox({
30660 allowBlank : this.dayAllowBlank,
30661 alwaysQuery : true,
30662 displayField : 'value',
30665 forceSelection : true,
30667 placeholder : this.dayPlaceholder,
30668 selectOnFocus : true,
30669 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30670 triggerAction : 'all',
30672 valueField : 'value',
30673 store : new Roo.data.SimpleStore({
30674 data : (function() {
30676 _this.fireEvent('days', _this, days);
30679 fields : [ 'value' ]
30682 select : function (_self, record, index)
30684 _this.setValue(_this.getValue());
30689 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30691 this.monthField = new Roo.bootstrap.MonthField({
30692 after : '<i class=\"fa fa-calendar\"></i>',
30693 allowBlank : this.monthAllowBlank,
30694 placeholder : this.monthPlaceholder,
30697 render : function (_self)
30699 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30700 e.preventDefault();
30704 select : function (_self, oldvalue, newvalue)
30706 _this.setValue(_this.getValue());
30711 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30713 this.yearField = new Roo.bootstrap.ComboBox({
30714 allowBlank : this.yearAllowBlank,
30715 alwaysQuery : true,
30716 displayField : 'value',
30719 forceSelection : true,
30721 placeholder : this.yearPlaceholder,
30722 selectOnFocus : true,
30723 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30724 triggerAction : 'all',
30726 valueField : 'value',
30727 store : new Roo.data.SimpleStore({
30728 data : (function() {
30730 _this.fireEvent('years', _this, years);
30733 fields : [ 'value' ]
30736 select : function (_self, record, index)
30738 _this.setValue(_this.getValue());
30743 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30746 setValue : function(v, format)
30748 this.inputEl.dom.value = v;
30750 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30752 var d = Date.parseDate(v, f);
30759 this.setDay(d.format(this.dayFormat));
30760 this.setMonth(d.format(this.monthFormat));
30761 this.setYear(d.format(this.yearFormat));
30768 setDay : function(v)
30770 this.dayField.setValue(v);
30771 this.inputEl.dom.value = this.getValue();
30776 setMonth : function(v)
30778 this.monthField.setValue(v, true);
30779 this.inputEl.dom.value = this.getValue();
30784 setYear : function(v)
30786 this.yearField.setValue(v);
30787 this.inputEl.dom.value = this.getValue();
30792 getDay : function()
30794 return this.dayField.getValue();
30797 getMonth : function()
30799 return this.monthField.getValue();
30802 getYear : function()
30804 return this.yearField.getValue();
30807 getValue : function()
30809 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30811 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30821 this.inputEl.dom.value = '';
30826 validate : function()
30828 var d = this.dayField.validate();
30829 var m = this.monthField.validate();
30830 var y = this.yearField.validate();
30835 (!this.dayAllowBlank && !d) ||
30836 (!this.monthAllowBlank && !m) ||
30837 (!this.yearAllowBlank && !y)
30842 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30851 this.markInvalid();
30856 markValid : function()
30859 var label = this.el.select('label', true).first();
30860 var icon = this.el.select('i.fa-star', true).first();
30866 this.fireEvent('valid', this);
30870 * Mark this field as invalid
30871 * @param {String} msg The validation message
30873 markInvalid : function(msg)
30876 var label = this.el.select('label', true).first();
30877 var icon = this.el.select('i.fa-star', true).first();
30879 if(label && !icon){
30880 this.el.select('.roo-date-split-field-label', true).createChild({
30882 cls : 'text-danger fa fa-lg fa-star',
30883 tooltip : 'This field is required',
30884 style : 'margin-right:5px;'
30888 this.fireEvent('invalid', this, msg);
30891 clearInvalid : function()
30893 var label = this.el.select('label', true).first();
30894 var icon = this.el.select('i.fa-star', true).first();
30900 this.fireEvent('valid', this);
30903 getName: function()
30913 * http://masonry.desandro.com
30915 * The idea is to render all the bricks based on vertical width...
30917 * The original code extends 'outlayer' - we might need to use that....
30923 * @class Roo.bootstrap.LayoutMasonry
30924 * @extends Roo.bootstrap.Component
30925 * Bootstrap Layout Masonry class
30928 * Create a new Element
30929 * @param {Object} config The config object
30932 Roo.bootstrap.LayoutMasonry = function(config){
30934 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30938 Roo.bootstrap.LayoutMasonry.register(this);
30944 * Fire after layout the items
30945 * @param {Roo.bootstrap.LayoutMasonry} this
30946 * @param {Roo.EventObject} e
30953 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30956 * @cfg {Boolean} isLayoutInstant = no animation?
30958 isLayoutInstant : false, // needed?
30961 * @cfg {Number} boxWidth width of the columns
30966 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30971 * @cfg {Number} padWidth padding below box..
30976 * @cfg {Number} gutter gutter width..
30981 * @cfg {Number} maxCols maximum number of columns
30987 * @cfg {Boolean} isAutoInitial defalut true
30989 isAutoInitial : true,
30994 * @cfg {Boolean} isHorizontal defalut false
30996 isHorizontal : false,
30998 currentSize : null,
31004 bricks: null, //CompositeElement
31008 _isLayoutInited : false,
31010 // isAlternative : false, // only use for vertical layout...
31013 * @cfg {Number} alternativePadWidth padding below box..
31015 alternativePadWidth : 50,
31017 selectedBrick : [],
31019 getAutoCreate : function(){
31021 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31025 cls: 'blog-masonary-wrapper ' + this.cls,
31027 cls : 'mas-boxes masonary'
31034 getChildContainer: function( )
31036 if (this.boxesEl) {
31037 return this.boxesEl;
31040 this.boxesEl = this.el.select('.mas-boxes').first();
31042 return this.boxesEl;
31046 initEvents : function()
31050 if(this.isAutoInitial){
31051 Roo.log('hook children rendered');
31052 this.on('childrenrendered', function() {
31053 Roo.log('children rendered');
31059 initial : function()
31061 this.selectedBrick = [];
31063 this.currentSize = this.el.getBox(true);
31065 Roo.EventManager.onWindowResize(this.resize, this);
31067 if(!this.isAutoInitial){
31075 //this.layout.defer(500,this);
31079 resize : function()
31081 var cs = this.el.getBox(true);
31084 this.currentSize.width == cs.width &&
31085 this.currentSize.x == cs.x &&
31086 this.currentSize.height == cs.height &&
31087 this.currentSize.y == cs.y
31089 Roo.log("no change in with or X or Y");
31093 this.currentSize = cs;
31099 layout : function()
31101 this._resetLayout();
31103 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31105 this.layoutItems( isInstant );
31107 this._isLayoutInited = true;
31109 this.fireEvent('layout', this);
31113 _resetLayout : function()
31115 if(this.isHorizontal){
31116 this.horizontalMeasureColumns();
31120 this.verticalMeasureColumns();
31124 verticalMeasureColumns : function()
31126 this.getContainerWidth();
31128 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31129 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31133 var boxWidth = this.boxWidth + this.padWidth;
31135 if(this.containerWidth < this.boxWidth){
31136 boxWidth = this.containerWidth
31139 var containerWidth = this.containerWidth;
31141 var cols = Math.floor(containerWidth / boxWidth);
31143 this.cols = Math.max( cols, 1 );
31145 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31147 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31149 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31151 this.colWidth = boxWidth + avail - this.padWidth;
31153 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31154 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31157 horizontalMeasureColumns : function()
31159 this.getContainerWidth();
31161 var boxWidth = this.boxWidth;
31163 if(this.containerWidth < boxWidth){
31164 boxWidth = this.containerWidth;
31167 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31169 this.el.setHeight(boxWidth);
31173 getContainerWidth : function()
31175 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31178 layoutItems : function( isInstant )
31180 Roo.log(this.bricks);
31182 var items = Roo.apply([], this.bricks);
31184 if(this.isHorizontal){
31185 this._horizontalLayoutItems( items , isInstant );
31189 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31190 // this._verticalAlternativeLayoutItems( items , isInstant );
31194 this._verticalLayoutItems( items , isInstant );
31198 _verticalLayoutItems : function ( items , isInstant)
31200 if ( !items || !items.length ) {
31205 ['xs', 'xs', 'xs', 'tall'],
31206 ['xs', 'xs', 'tall'],
31207 ['xs', 'xs', 'sm'],
31208 ['xs', 'xs', 'xs'],
31214 ['sm', 'xs', 'xs'],
31218 ['tall', 'xs', 'xs', 'xs'],
31219 ['tall', 'xs', 'xs'],
31231 Roo.each(items, function(item, k){
31233 switch (item.size) {
31234 // these layouts take up a full box,
31245 boxes.push([item]);
31268 var filterPattern = function(box, length)
31276 var pattern = box.slice(0, length);
31280 Roo.each(pattern, function(i){
31281 format.push(i.size);
31284 Roo.each(standard, function(s){
31286 if(String(s) != String(format)){
31295 if(!match && length == 1){
31300 filterPattern(box, length - 1);
31304 queue.push(pattern);
31306 box = box.slice(length, box.length);
31308 filterPattern(box, 4);
31314 Roo.each(boxes, function(box, k){
31320 if(box.length == 1){
31325 filterPattern(box, 4);
31329 this._processVerticalLayoutQueue( queue, isInstant );
31333 // _verticalAlternativeLayoutItems : function( items , isInstant )
31335 // if ( !items || !items.length ) {
31339 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31343 _horizontalLayoutItems : function ( items , isInstant)
31345 if ( !items || !items.length || items.length < 3) {
31351 var eItems = items.slice(0, 3);
31353 items = items.slice(3, items.length);
31356 ['xs', 'xs', 'xs', 'wide'],
31357 ['xs', 'xs', 'wide'],
31358 ['xs', 'xs', 'sm'],
31359 ['xs', 'xs', 'xs'],
31365 ['sm', 'xs', 'xs'],
31369 ['wide', 'xs', 'xs', 'xs'],
31370 ['wide', 'xs', 'xs'],
31383 Roo.each(items, function(item, k){
31385 switch (item.size) {
31396 boxes.push([item]);
31420 var filterPattern = function(box, length)
31428 var pattern = box.slice(0, length);
31432 Roo.each(pattern, function(i){
31433 format.push(i.size);
31436 Roo.each(standard, function(s){
31438 if(String(s) != String(format)){
31447 if(!match && length == 1){
31452 filterPattern(box, length - 1);
31456 queue.push(pattern);
31458 box = box.slice(length, box.length);
31460 filterPattern(box, 4);
31466 Roo.each(boxes, function(box, k){
31472 if(box.length == 1){
31477 filterPattern(box, 4);
31484 var pos = this.el.getBox(true);
31488 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31490 var hit_end = false;
31492 Roo.each(queue, function(box){
31496 Roo.each(box, function(b){
31498 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31508 Roo.each(box, function(b){
31510 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31513 mx = Math.max(mx, b.x);
31517 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31521 Roo.each(box, function(b){
31523 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31537 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31540 /** Sets position of item in DOM
31541 * @param {Element} item
31542 * @param {Number} x - horizontal position
31543 * @param {Number} y - vertical position
31544 * @param {Boolean} isInstant - disables transitions
31546 _processVerticalLayoutQueue : function( queue, isInstant )
31548 var pos = this.el.getBox(true);
31553 for (var i = 0; i < this.cols; i++){
31557 Roo.each(queue, function(box, k){
31559 var col = k % this.cols;
31561 Roo.each(box, function(b,kk){
31563 b.el.position('absolute');
31565 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31566 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31568 if(b.size == 'md-left' || b.size == 'md-right'){
31569 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31570 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31573 b.el.setWidth(width);
31574 b.el.setHeight(height);
31576 b.el.select('iframe',true).setSize(width,height);
31580 for (var i = 0; i < this.cols; i++){
31582 if(maxY[i] < maxY[col]){
31587 col = Math.min(col, i);
31591 x = pos.x + col * (this.colWidth + this.padWidth);
31595 var positions = [];
31597 switch (box.length){
31599 positions = this.getVerticalOneBoxColPositions(x, y, box);
31602 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31605 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31608 positions = this.getVerticalFourBoxColPositions(x, y, box);
31614 Roo.each(box, function(b,kk){
31616 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31618 var sz = b.el.getSize();
31620 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31628 for (var i = 0; i < this.cols; i++){
31629 mY = Math.max(mY, maxY[i]);
31632 this.el.setHeight(mY - pos.y);
31636 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31638 // var pos = this.el.getBox(true);
31641 // var maxX = pos.right;
31643 // var maxHeight = 0;
31645 // Roo.each(items, function(item, k){
31649 // item.el.position('absolute');
31651 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31653 // item.el.setWidth(width);
31655 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31657 // item.el.setHeight(height);
31660 // item.el.setXY([x, y], isInstant ? false : true);
31662 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31665 // y = y + height + this.alternativePadWidth;
31667 // maxHeight = maxHeight + height + this.alternativePadWidth;
31671 // this.el.setHeight(maxHeight);
31675 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31677 var pos = this.el.getBox(true);
31682 var maxX = pos.right;
31684 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31686 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31688 Roo.each(queue, function(box, k){
31690 Roo.each(box, function(b, kk){
31692 b.el.position('absolute');
31694 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31695 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31697 if(b.size == 'md-left' || b.size == 'md-right'){
31698 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31699 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31702 b.el.setWidth(width);
31703 b.el.setHeight(height);
31711 var positions = [];
31713 switch (box.length){
31715 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31718 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31721 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31724 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31730 Roo.each(box, function(b,kk){
31732 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31734 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31742 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31744 Roo.each(eItems, function(b,k){
31746 b.size = (k == 0) ? 'sm' : 'xs';
31747 b.x = (k == 0) ? 2 : 1;
31748 b.y = (k == 0) ? 2 : 1;
31750 b.el.position('absolute');
31752 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31754 b.el.setWidth(width);
31756 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31758 b.el.setHeight(height);
31762 var positions = [];
31765 x : maxX - this.unitWidth * 2 - this.gutter,
31770 x : maxX - this.unitWidth,
31771 y : minY + (this.unitWidth + this.gutter) * 2
31775 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31779 Roo.each(eItems, function(b,k){
31781 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31787 getVerticalOneBoxColPositions : function(x, y, box)
31791 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31793 if(box[0].size == 'md-left'){
31797 if(box[0].size == 'md-right'){
31802 x : x + (this.unitWidth + this.gutter) * rand,
31809 getVerticalTwoBoxColPositions : function(x, y, box)
31813 if(box[0].size == 'xs'){
31817 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31821 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31835 x : x + (this.unitWidth + this.gutter) * 2,
31836 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31843 getVerticalThreeBoxColPositions : function(x, y, box)
31847 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31855 x : x + (this.unitWidth + this.gutter) * 1,
31860 x : x + (this.unitWidth + this.gutter) * 2,
31868 if(box[0].size == 'xs' && box[1].size == 'xs'){
31877 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31881 x : x + (this.unitWidth + this.gutter) * 1,
31895 x : x + (this.unitWidth + this.gutter) * 2,
31900 x : x + (this.unitWidth + this.gutter) * 2,
31901 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31908 getVerticalFourBoxColPositions : function(x, y, box)
31912 if(box[0].size == 'xs'){
31921 y : y + (this.unitHeight + this.gutter) * 1
31926 y : y + (this.unitHeight + this.gutter) * 2
31930 x : x + (this.unitWidth + this.gutter) * 1,
31944 x : x + (this.unitWidth + this.gutter) * 2,
31949 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31950 y : y + (this.unitHeight + this.gutter) * 1
31954 x : x + (this.unitWidth + this.gutter) * 2,
31955 y : y + (this.unitWidth + this.gutter) * 2
31962 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31966 if(box[0].size == 'md-left'){
31968 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31975 if(box[0].size == 'md-right'){
31977 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31978 y : minY + (this.unitWidth + this.gutter) * 1
31984 var rand = Math.floor(Math.random() * (4 - box[0].y));
31987 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31988 y : minY + (this.unitWidth + this.gutter) * rand
31995 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31999 if(box[0].size == 'xs'){
32002 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32007 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32008 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32016 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32021 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32022 y : minY + (this.unitWidth + this.gutter) * 2
32029 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32033 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32036 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32041 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32042 y : minY + (this.unitWidth + this.gutter) * 1
32046 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32047 y : minY + (this.unitWidth + this.gutter) * 2
32054 if(box[0].size == 'xs' && box[1].size == 'xs'){
32057 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32062 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32067 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32068 y : minY + (this.unitWidth + this.gutter) * 1
32076 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32081 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32082 y : minY + (this.unitWidth + this.gutter) * 2
32086 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32087 y : minY + (this.unitWidth + this.gutter) * 2
32094 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32098 if(box[0].size == 'xs'){
32101 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32106 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32111 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),
32116 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32117 y : minY + (this.unitWidth + this.gutter) * 1
32125 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32130 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32131 y : minY + (this.unitWidth + this.gutter) * 2
32135 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32136 y : minY + (this.unitWidth + this.gutter) * 2
32140 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),
32141 y : minY + (this.unitWidth + this.gutter) * 2
32149 * remove a Masonry Brick
32150 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32152 removeBrick : function(brick_id)
32158 for (var i = 0; i<this.bricks.length; i++) {
32159 if (this.bricks[i].id == brick_id) {
32160 this.bricks.splice(i,1);
32161 this.el.dom.removeChild(Roo.get(brick_id).dom);
32168 * adds a Masonry Brick
32169 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32171 addBrick : function(cfg)
32173 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32174 //this.register(cn);
32175 cn.parentId = this.id;
32176 cn.render(this.el);
32181 * register a Masonry Brick
32182 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32185 register : function(brick)
32187 this.bricks.push(brick);
32188 brick.masonryId = this.id;
32192 * clear all the Masonry Brick
32194 clearAll : function()
32197 //this.getChildContainer().dom.innerHTML = "";
32198 this.el.dom.innerHTML = '';
32201 getSelected : function()
32203 if (!this.selectedBrick) {
32207 return this.selectedBrick;
32211 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32215 * register a Masonry Layout
32216 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32219 register : function(layout)
32221 this.groups[layout.id] = layout;
32224 * fetch a Masonry Layout based on the masonry layout ID
32225 * @param {string} the masonry layout to add
32226 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32229 get: function(layout_id) {
32230 if (typeof(this.groups[layout_id]) == 'undefined') {
32233 return this.groups[layout_id] ;
32245 * http://masonry.desandro.com
32247 * The idea is to render all the bricks based on vertical width...
32249 * The original code extends 'outlayer' - we might need to use that....
32255 * @class Roo.bootstrap.LayoutMasonryAuto
32256 * @extends Roo.bootstrap.Component
32257 * Bootstrap Layout Masonry class
32260 * Create a new Element
32261 * @param {Object} config The config object
32264 Roo.bootstrap.LayoutMasonryAuto = function(config){
32265 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32268 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32271 * @cfg {Boolean} isFitWidth - resize the width..
32273 isFitWidth : false, // options..
32275 * @cfg {Boolean} isOriginLeft = left align?
32277 isOriginLeft : true,
32279 * @cfg {Boolean} isOriginTop = top align?
32281 isOriginTop : false,
32283 * @cfg {Boolean} isLayoutInstant = no animation?
32285 isLayoutInstant : false, // needed?
32287 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32289 isResizingContainer : true,
32291 * @cfg {Number} columnWidth width of the columns
32297 * @cfg {Number} maxCols maximum number of columns
32302 * @cfg {Number} padHeight padding below box..
32308 * @cfg {Boolean} isAutoInitial defalut true
32311 isAutoInitial : true,
32317 initialColumnWidth : 0,
32318 currentSize : null,
32320 colYs : null, // array.
32327 bricks: null, //CompositeElement
32328 cols : 0, // array?
32329 // element : null, // wrapped now this.el
32330 _isLayoutInited : null,
32333 getAutoCreate : function(){
32337 cls: 'blog-masonary-wrapper ' + this.cls,
32339 cls : 'mas-boxes masonary'
32346 getChildContainer: function( )
32348 if (this.boxesEl) {
32349 return this.boxesEl;
32352 this.boxesEl = this.el.select('.mas-boxes').first();
32354 return this.boxesEl;
32358 initEvents : function()
32362 if(this.isAutoInitial){
32363 Roo.log('hook children rendered');
32364 this.on('childrenrendered', function() {
32365 Roo.log('children rendered');
32372 initial : function()
32374 this.reloadItems();
32376 this.currentSize = this.el.getBox(true);
32378 /// was window resize... - let's see if this works..
32379 Roo.EventManager.onWindowResize(this.resize, this);
32381 if(!this.isAutoInitial){
32386 this.layout.defer(500,this);
32389 reloadItems: function()
32391 this.bricks = this.el.select('.masonry-brick', true);
32393 this.bricks.each(function(b) {
32394 //Roo.log(b.getSize());
32395 if (!b.attr('originalwidth')) {
32396 b.attr('originalwidth', b.getSize().width);
32401 Roo.log(this.bricks.elements.length);
32404 resize : function()
32407 var cs = this.el.getBox(true);
32409 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32410 Roo.log("no change in with or X");
32413 this.currentSize = cs;
32417 layout : function()
32420 this._resetLayout();
32421 //this._manageStamps();
32423 // don't animate first layout
32424 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32425 this.layoutItems( isInstant );
32427 // flag for initalized
32428 this._isLayoutInited = true;
32431 layoutItems : function( isInstant )
32433 //var items = this._getItemsForLayout( this.items );
32434 // original code supports filtering layout items.. we just ignore it..
32436 this._layoutItems( this.bricks , isInstant );
32438 this._postLayout();
32440 _layoutItems : function ( items , isInstant)
32442 //this.fireEvent( 'layout', this, items );
32445 if ( !items || !items.elements.length ) {
32446 // no items, emit event with empty array
32451 items.each(function(item) {
32452 Roo.log("layout item");
32454 // get x/y object from method
32455 var position = this._getItemLayoutPosition( item );
32457 position.item = item;
32458 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32459 queue.push( position );
32462 this._processLayoutQueue( queue );
32464 /** Sets position of item in DOM
32465 * @param {Element} item
32466 * @param {Number} x - horizontal position
32467 * @param {Number} y - vertical position
32468 * @param {Boolean} isInstant - disables transitions
32470 _processLayoutQueue : function( queue )
32472 for ( var i=0, len = queue.length; i < len; i++ ) {
32473 var obj = queue[i];
32474 obj.item.position('absolute');
32475 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32481 * Any logic you want to do after each layout,
32482 * i.e. size the container
32484 _postLayout : function()
32486 this.resizeContainer();
32489 resizeContainer : function()
32491 if ( !this.isResizingContainer ) {
32494 var size = this._getContainerSize();
32496 this.el.setSize(size.width,size.height);
32497 this.boxesEl.setSize(size.width,size.height);
32503 _resetLayout : function()
32505 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32506 this.colWidth = this.el.getWidth();
32507 //this.gutter = this.el.getWidth();
32509 this.measureColumns();
32515 this.colYs.push( 0 );
32521 measureColumns : function()
32523 this.getContainerWidth();
32524 // if columnWidth is 0, default to outerWidth of first item
32525 if ( !this.columnWidth ) {
32526 var firstItem = this.bricks.first();
32527 Roo.log(firstItem);
32528 this.columnWidth = this.containerWidth;
32529 if (firstItem && firstItem.attr('originalwidth') ) {
32530 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32532 // columnWidth fall back to item of first element
32533 Roo.log("set column width?");
32534 this.initialColumnWidth = this.columnWidth ;
32536 // if first elem has no width, default to size of container
32541 if (this.initialColumnWidth) {
32542 this.columnWidth = this.initialColumnWidth;
32547 // column width is fixed at the top - however if container width get's smaller we should
32550 // this bit calcs how man columns..
32552 var columnWidth = this.columnWidth += this.gutter;
32554 // calculate columns
32555 var containerWidth = this.containerWidth + this.gutter;
32557 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32558 // fix rounding errors, typically with gutters
32559 var excess = columnWidth - containerWidth % columnWidth;
32562 // if overshoot is less than a pixel, round up, otherwise floor it
32563 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32564 cols = Math[ mathMethod ]( cols );
32565 this.cols = Math.max( cols, 1 );
32566 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32568 // padding positioning..
32569 var totalColWidth = this.cols * this.columnWidth;
32570 var padavail = this.containerWidth - totalColWidth;
32571 // so for 2 columns - we need 3 'pads'
32573 var padNeeded = (1+this.cols) * this.padWidth;
32575 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32577 this.columnWidth += padExtra
32578 //this.padWidth = Math.floor(padavail / ( this.cols));
32580 // adjust colum width so that padding is fixed??
32582 // we have 3 columns ... total = width * 3
32583 // we have X left over... that should be used by
32585 //if (this.expandC) {
32593 getContainerWidth : function()
32595 /* // container is parent if fit width
32596 var container = this.isFitWidth ? this.element.parentNode : this.element;
32597 // check that this.size and size are there
32598 // IE8 triggers resize on body size change, so they might not be
32600 var size = getSize( container ); //FIXME
32601 this.containerWidth = size && size.innerWidth; //FIXME
32604 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32608 _getItemLayoutPosition : function( item ) // what is item?
32610 // we resize the item to our columnWidth..
32612 item.setWidth(this.columnWidth);
32613 item.autoBoxAdjust = false;
32615 var sz = item.getSize();
32617 // how many columns does this brick span
32618 var remainder = this.containerWidth % this.columnWidth;
32620 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32621 // round if off by 1 pixel, otherwise use ceil
32622 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32623 colSpan = Math.min( colSpan, this.cols );
32625 // normally this should be '1' as we dont' currently allow multi width columns..
32627 var colGroup = this._getColGroup( colSpan );
32628 // get the minimum Y value from the columns
32629 var minimumY = Math.min.apply( Math, colGroup );
32630 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32632 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32634 // position the brick
32636 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32637 y: this.currentSize.y + minimumY + this.padHeight
32641 // apply setHeight to necessary columns
32642 var setHeight = minimumY + sz.height + this.padHeight;
32643 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32645 var setSpan = this.cols + 1 - colGroup.length;
32646 for ( var i = 0; i < setSpan; i++ ) {
32647 this.colYs[ shortColIndex + i ] = setHeight ;
32654 * @param {Number} colSpan - number of columns the element spans
32655 * @returns {Array} colGroup
32657 _getColGroup : function( colSpan )
32659 if ( colSpan < 2 ) {
32660 // if brick spans only one column, use all the column Ys
32665 // how many different places could this brick fit horizontally
32666 var groupCount = this.cols + 1 - colSpan;
32667 // for each group potential horizontal position
32668 for ( var i = 0; i < groupCount; i++ ) {
32669 // make an array of colY values for that one group
32670 var groupColYs = this.colYs.slice( i, i + colSpan );
32671 // and get the max value of the array
32672 colGroup[i] = Math.max.apply( Math, groupColYs );
32677 _manageStamp : function( stamp )
32679 var stampSize = stamp.getSize();
32680 var offset = stamp.getBox();
32681 // get the columns that this stamp affects
32682 var firstX = this.isOriginLeft ? offset.x : offset.right;
32683 var lastX = firstX + stampSize.width;
32684 var firstCol = Math.floor( firstX / this.columnWidth );
32685 firstCol = Math.max( 0, firstCol );
32687 var lastCol = Math.floor( lastX / this.columnWidth );
32688 // lastCol should not go over if multiple of columnWidth #425
32689 lastCol -= lastX % this.columnWidth ? 0 : 1;
32690 lastCol = Math.min( this.cols - 1, lastCol );
32692 // set colYs to bottom of the stamp
32693 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32696 for ( var i = firstCol; i <= lastCol; i++ ) {
32697 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32702 _getContainerSize : function()
32704 this.maxY = Math.max.apply( Math, this.colYs );
32709 if ( this.isFitWidth ) {
32710 size.width = this._getContainerFitWidth();
32716 _getContainerFitWidth : function()
32718 var unusedCols = 0;
32719 // count unused columns
32722 if ( this.colYs[i] !== 0 ) {
32727 // fit container to columns that have been used
32728 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32731 needsResizeLayout : function()
32733 var previousWidth = this.containerWidth;
32734 this.getContainerWidth();
32735 return previousWidth !== this.containerWidth;
32750 * @class Roo.bootstrap.MasonryBrick
32751 * @extends Roo.bootstrap.Component
32752 * Bootstrap MasonryBrick class
32755 * Create a new MasonryBrick
32756 * @param {Object} config The config object
32759 Roo.bootstrap.MasonryBrick = function(config){
32761 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32763 Roo.bootstrap.MasonryBrick.register(this);
32769 * When a MasonryBrick is clcik
32770 * @param {Roo.bootstrap.MasonryBrick} this
32771 * @param {Roo.EventObject} e
32777 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32780 * @cfg {String} title
32784 * @cfg {String} html
32788 * @cfg {String} bgimage
32792 * @cfg {String} videourl
32796 * @cfg {String} cls
32800 * @cfg {String} href
32804 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32809 * @cfg {String} placetitle (center|bottom)
32814 * @cfg {Boolean} isFitContainer defalut true
32816 isFitContainer : true,
32819 * @cfg {Boolean} preventDefault defalut false
32821 preventDefault : false,
32824 * @cfg {Boolean} inverse defalut false
32826 maskInverse : false,
32828 getAutoCreate : function()
32830 if(!this.isFitContainer){
32831 return this.getSplitAutoCreate();
32834 var cls = 'masonry-brick masonry-brick-full';
32836 if(this.href.length){
32837 cls += ' masonry-brick-link';
32840 if(this.bgimage.length){
32841 cls += ' masonry-brick-image';
32844 if(this.maskInverse){
32845 cls += ' mask-inverse';
32848 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32849 cls += ' enable-mask';
32853 cls += ' masonry-' + this.size + '-brick';
32856 if(this.placetitle.length){
32858 switch (this.placetitle) {
32860 cls += ' masonry-center-title';
32863 cls += ' masonry-bottom-title';
32870 if(!this.html.length && !this.bgimage.length){
32871 cls += ' masonry-center-title';
32874 if(!this.html.length && this.bgimage.length){
32875 cls += ' masonry-bottom-title';
32880 cls += ' ' + this.cls;
32884 tag: (this.href.length) ? 'a' : 'div',
32889 cls: 'masonry-brick-mask'
32893 cls: 'masonry-brick-paragraph',
32899 if(this.href.length){
32900 cfg.href = this.href;
32903 var cn = cfg.cn[1].cn;
32905 if(this.title.length){
32908 cls: 'masonry-brick-title',
32913 if(this.html.length){
32916 cls: 'masonry-brick-text',
32921 if (!this.title.length && !this.html.length) {
32922 cfg.cn[1].cls += ' hide';
32925 if(this.bgimage.length){
32928 cls: 'masonry-brick-image-view',
32933 if(this.videourl.length){
32934 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32935 // youtube support only?
32938 cls: 'masonry-brick-image-view',
32941 allowfullscreen : true
32949 getSplitAutoCreate : function()
32951 var cls = 'masonry-brick masonry-brick-split';
32953 if(this.href.length){
32954 cls += ' masonry-brick-link';
32957 if(this.bgimage.length){
32958 cls += ' masonry-brick-image';
32962 cls += ' masonry-' + this.size + '-brick';
32965 switch (this.placetitle) {
32967 cls += ' masonry-center-title';
32970 cls += ' masonry-bottom-title';
32973 if(!this.bgimage.length){
32974 cls += ' masonry-center-title';
32977 if(this.bgimage.length){
32978 cls += ' masonry-bottom-title';
32984 cls += ' ' + this.cls;
32988 tag: (this.href.length) ? 'a' : 'div',
32993 cls: 'masonry-brick-split-head',
32997 cls: 'masonry-brick-paragraph',
33004 cls: 'masonry-brick-split-body',
33010 if(this.href.length){
33011 cfg.href = this.href;
33014 if(this.title.length){
33015 cfg.cn[0].cn[0].cn.push({
33017 cls: 'masonry-brick-title',
33022 if(this.html.length){
33023 cfg.cn[1].cn.push({
33025 cls: 'masonry-brick-text',
33030 if(this.bgimage.length){
33031 cfg.cn[0].cn.push({
33033 cls: 'masonry-brick-image-view',
33038 if(this.videourl.length){
33039 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33040 // youtube support only?
33041 cfg.cn[0].cn.cn.push({
33043 cls: 'masonry-brick-image-view',
33046 allowfullscreen : true
33053 initEvents: function()
33055 switch (this.size) {
33088 this.el.on('touchstart', this.onTouchStart, this);
33089 this.el.on('touchmove', this.onTouchMove, this);
33090 this.el.on('touchend', this.onTouchEnd, this);
33091 this.el.on('contextmenu', this.onContextMenu, this);
33093 this.el.on('mouseenter' ,this.enter, this);
33094 this.el.on('mouseleave', this.leave, this);
33095 this.el.on('click', this.onClick, this);
33098 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33099 this.parent().bricks.push(this);
33104 onClick: function(e, el)
33106 var time = this.endTimer - this.startTimer;
33107 // Roo.log(e.preventDefault());
33110 e.preventDefault();
33115 if(!this.preventDefault){
33119 e.preventDefault();
33121 if (this.activeClass != '') {
33122 this.selectBrick();
33125 this.fireEvent('click', this, e);
33128 enter: function(e, el)
33130 e.preventDefault();
33132 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33136 if(this.bgimage.length && this.html.length){
33137 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33141 leave: function(e, el)
33143 e.preventDefault();
33145 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33149 if(this.bgimage.length && this.html.length){
33150 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33154 onTouchStart: function(e, el)
33156 // e.preventDefault();
33158 this.touchmoved = false;
33160 if(!this.isFitContainer){
33164 if(!this.bgimage.length || !this.html.length){
33168 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33170 this.timer = new Date().getTime();
33174 onTouchMove: function(e, el)
33176 this.touchmoved = true;
33179 onContextMenu : function(e,el)
33181 e.preventDefault();
33182 e.stopPropagation();
33186 onTouchEnd: function(e, el)
33188 // e.preventDefault();
33190 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33197 if(!this.bgimage.length || !this.html.length){
33199 if(this.href.length){
33200 window.location.href = this.href;
33206 if(!this.isFitContainer){
33210 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33212 window.location.href = this.href;
33215 //selection on single brick only
33216 selectBrick : function() {
33218 if (!this.parentId) {
33222 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33223 var index = m.selectedBrick.indexOf(this.id);
33226 m.selectedBrick.splice(index,1);
33227 this.el.removeClass(this.activeClass);
33231 for(var i = 0; i < m.selectedBrick.length; i++) {
33232 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33233 b.el.removeClass(b.activeClass);
33236 m.selectedBrick = [];
33238 m.selectedBrick.push(this.id);
33239 this.el.addClass(this.activeClass);
33243 isSelected : function(){
33244 return this.el.hasClass(this.activeClass);
33249 Roo.apply(Roo.bootstrap.MasonryBrick, {
33252 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33254 * register a Masonry Brick
33255 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33258 register : function(brick)
33260 //this.groups[brick.id] = brick;
33261 this.groups.add(brick.id, brick);
33264 * fetch a masonry brick based on the masonry brick ID
33265 * @param {string} the masonry brick to add
33266 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33269 get: function(brick_id)
33271 // if (typeof(this.groups[brick_id]) == 'undefined') {
33274 // return this.groups[brick_id] ;
33276 if(this.groups.key(brick_id)) {
33277 return this.groups.key(brick_id);
33295 * @class Roo.bootstrap.Brick
33296 * @extends Roo.bootstrap.Component
33297 * Bootstrap Brick class
33300 * Create a new Brick
33301 * @param {Object} config The config object
33304 Roo.bootstrap.Brick = function(config){
33305 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33311 * When a Brick is click
33312 * @param {Roo.bootstrap.Brick} this
33313 * @param {Roo.EventObject} e
33319 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33322 * @cfg {String} title
33326 * @cfg {String} html
33330 * @cfg {String} bgimage
33334 * @cfg {String} cls
33338 * @cfg {String} href
33342 * @cfg {String} video
33346 * @cfg {Boolean} square
33350 getAutoCreate : function()
33352 var cls = 'roo-brick';
33354 if(this.href.length){
33355 cls += ' roo-brick-link';
33358 if(this.bgimage.length){
33359 cls += ' roo-brick-image';
33362 if(!this.html.length && !this.bgimage.length){
33363 cls += ' roo-brick-center-title';
33366 if(!this.html.length && this.bgimage.length){
33367 cls += ' roo-brick-bottom-title';
33371 cls += ' ' + this.cls;
33375 tag: (this.href.length) ? 'a' : 'div',
33380 cls: 'roo-brick-paragraph',
33386 if(this.href.length){
33387 cfg.href = this.href;
33390 var cn = cfg.cn[0].cn;
33392 if(this.title.length){
33395 cls: 'roo-brick-title',
33400 if(this.html.length){
33403 cls: 'roo-brick-text',
33410 if(this.bgimage.length){
33413 cls: 'roo-brick-image-view',
33421 initEvents: function()
33423 if(this.title.length || this.html.length){
33424 this.el.on('mouseenter' ,this.enter, this);
33425 this.el.on('mouseleave', this.leave, this);
33428 Roo.EventManager.onWindowResize(this.resize, this);
33430 if(this.bgimage.length){
33431 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33432 this.imageEl.on('load', this.onImageLoad, this);
33439 onImageLoad : function()
33444 resize : function()
33446 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33448 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33450 if(this.bgimage.length){
33451 var image = this.el.select('.roo-brick-image-view', true).first();
33453 image.setWidth(paragraph.getWidth());
33456 image.setHeight(paragraph.getWidth());
33459 this.el.setHeight(image.getHeight());
33460 paragraph.setHeight(image.getHeight());
33466 enter: function(e, el)
33468 e.preventDefault();
33470 if(this.bgimage.length){
33471 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33472 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33476 leave: function(e, el)
33478 e.preventDefault();
33480 if(this.bgimage.length){
33481 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33482 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33497 * @class Roo.bootstrap.NumberField
33498 * @extends Roo.bootstrap.Input
33499 * Bootstrap NumberField class
33505 * Create a new NumberField
33506 * @param {Object} config The config object
33509 Roo.bootstrap.NumberField = function(config){
33510 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33513 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33516 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33518 allowDecimals : true,
33520 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33522 decimalSeparator : ".",
33524 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33526 decimalPrecision : 2,
33528 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33530 allowNegative : true,
33533 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33537 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33539 minValue : Number.NEGATIVE_INFINITY,
33541 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33543 maxValue : Number.MAX_VALUE,
33545 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33547 minText : "The minimum value for this field is {0}",
33549 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33551 maxText : "The maximum value for this field is {0}",
33553 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33554 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33556 nanText : "{0} is not a valid number",
33558 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33560 thousandsDelimiter : false,
33562 * @cfg {String} valueAlign alignment of value
33564 valueAlign : "left",
33566 getAutoCreate : function()
33568 var hiddenInput = {
33572 cls: 'hidden-number-input'
33576 hiddenInput.name = this.name;
33581 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33583 this.name = hiddenInput.name;
33585 if(cfg.cn.length > 0) {
33586 cfg.cn.push(hiddenInput);
33593 initEvents : function()
33595 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33597 var allowed = "0123456789";
33599 if(this.allowDecimals){
33600 allowed += this.decimalSeparator;
33603 if(this.allowNegative){
33607 if(this.thousandsDelimiter) {
33611 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33613 var keyPress = function(e){
33615 var k = e.getKey();
33617 var c = e.getCharCode();
33620 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33621 allowed.indexOf(String.fromCharCode(c)) === -1
33627 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33631 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33636 this.el.on("keypress", keyPress, this);
33639 validateValue : function(value)
33642 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33646 var num = this.parseValue(value);
33649 this.markInvalid(String.format(this.nanText, value));
33653 if(num < this.minValue){
33654 this.markInvalid(String.format(this.minText, this.minValue));
33658 if(num > this.maxValue){
33659 this.markInvalid(String.format(this.maxText, this.maxValue));
33666 getValue : function()
33668 var v = this.hiddenEl().getValue();
33670 return this.fixPrecision(this.parseValue(v));
33673 parseValue : function(value)
33675 if(this.thousandsDelimiter) {
33677 r = new RegExp(",", "g");
33678 value = value.replace(r, "");
33681 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33682 return isNaN(value) ? '' : value;
33685 fixPrecision : function(value)
33687 if(this.thousandsDelimiter) {
33689 r = new RegExp(",", "g");
33690 value = value.replace(r, "");
33693 var nan = isNaN(value);
33695 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33696 return nan ? '' : value;
33698 return parseFloat(value).toFixed(this.decimalPrecision);
33701 setValue : function(v)
33703 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33709 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33711 this.inputEl().dom.value = (v == '') ? '' :
33712 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33714 if(!this.allowZero && v === '0') {
33715 this.hiddenEl().dom.value = '';
33716 this.inputEl().dom.value = '';
33723 decimalPrecisionFcn : function(v)
33725 return Math.floor(v);
33728 beforeBlur : function()
33730 var v = this.parseValue(this.getRawValue());
33732 if(v || v === 0 || v === ''){
33737 hiddenEl : function()
33739 return this.el.select('input.hidden-number-input',true).first();
33751 * @class Roo.bootstrap.DocumentSlider
33752 * @extends Roo.bootstrap.Component
33753 * Bootstrap DocumentSlider class
33756 * Create a new DocumentViewer
33757 * @param {Object} config The config object
33760 Roo.bootstrap.DocumentSlider = function(config){
33761 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33768 * Fire after initEvent
33769 * @param {Roo.bootstrap.DocumentSlider} this
33774 * Fire after update
33775 * @param {Roo.bootstrap.DocumentSlider} this
33781 * @param {Roo.bootstrap.DocumentSlider} this
33787 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33793 getAutoCreate : function()
33797 cls : 'roo-document-slider',
33801 cls : 'roo-document-slider-header',
33805 cls : 'roo-document-slider-header-title'
33811 cls : 'roo-document-slider-body',
33815 cls : 'roo-document-slider-prev',
33819 cls : 'fa fa-chevron-left'
33825 cls : 'roo-document-slider-thumb',
33829 cls : 'roo-document-slider-image'
33835 cls : 'roo-document-slider-next',
33839 cls : 'fa fa-chevron-right'
33851 initEvents : function()
33853 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33854 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33856 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33857 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33859 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33860 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33862 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33863 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33865 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33866 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33868 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33869 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33871 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33872 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33874 this.thumbEl.on('click', this.onClick, this);
33876 this.prevIndicator.on('click', this.prev, this);
33878 this.nextIndicator.on('click', this.next, this);
33882 initial : function()
33884 if(this.files.length){
33885 this.indicator = 1;
33889 this.fireEvent('initial', this);
33892 update : function()
33894 this.imageEl.attr('src', this.files[this.indicator - 1]);
33896 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33898 this.prevIndicator.show();
33900 if(this.indicator == 1){
33901 this.prevIndicator.hide();
33904 this.nextIndicator.show();
33906 if(this.indicator == this.files.length){
33907 this.nextIndicator.hide();
33910 this.thumbEl.scrollTo('top');
33912 this.fireEvent('update', this);
33915 onClick : function(e)
33917 e.preventDefault();
33919 this.fireEvent('click', this);
33924 e.preventDefault();
33926 this.indicator = Math.max(1, this.indicator - 1);
33933 e.preventDefault();
33935 this.indicator = Math.min(this.files.length, this.indicator + 1);
33949 * @class Roo.bootstrap.RadioSet
33950 * @extends Roo.bootstrap.Input
33951 * Bootstrap RadioSet class
33952 * @cfg {String} indicatorpos (left|right) default left
33953 * @cfg {Boolean} inline (true|false) inline the element (default true)
33954 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33956 * Create a new RadioSet
33957 * @param {Object} config The config object
33960 Roo.bootstrap.RadioSet = function(config){
33962 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33966 Roo.bootstrap.RadioSet.register(this);
33971 * Fires when the element is checked or unchecked.
33972 * @param {Roo.bootstrap.RadioSet} this This radio
33973 * @param {Roo.bootstrap.Radio} item The checked item
33978 * Fires when the element is click.
33979 * @param {Roo.bootstrap.RadioSet} this This radio set
33980 * @param {Roo.bootstrap.Radio} item The checked item
33981 * @param {Roo.EventObject} e The event object
33988 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33996 indicatorpos : 'left',
33998 getAutoCreate : function()
34002 cls : 'roo-radio-set-label',
34006 html : this.fieldLabel
34011 if(this.indicatorpos == 'left'){
34014 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34015 tooltip : 'This field is required'
34020 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34021 tooltip : 'This field is required'
34027 cls : 'roo-radio-set-items'
34030 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34032 if (align === 'left' && this.fieldLabel.length) {
34035 cls : "roo-radio-set-right",
34041 if(this.labelWidth > 12){
34042 label.style = "width: " + this.labelWidth + 'px';
34045 if(this.labelWidth < 13 && this.labelmd == 0){
34046 this.labelmd = this.labelWidth;
34049 if(this.labellg > 0){
34050 label.cls += ' col-lg-' + this.labellg;
34051 items.cls += ' col-lg-' + (12 - this.labellg);
34054 if(this.labelmd > 0){
34055 label.cls += ' col-md-' + this.labelmd;
34056 items.cls += ' col-md-' + (12 - this.labelmd);
34059 if(this.labelsm > 0){
34060 label.cls += ' col-sm-' + this.labelsm;
34061 items.cls += ' col-sm-' + (12 - this.labelsm);
34064 if(this.labelxs > 0){
34065 label.cls += ' col-xs-' + this.labelxs;
34066 items.cls += ' col-xs-' + (12 - this.labelxs);
34072 cls : 'roo-radio-set',
34076 cls : 'roo-radio-set-input',
34079 value : this.value ? this.value : ''
34086 if(this.weight.length){
34087 cfg.cls += ' roo-radio-' + this.weight;
34091 cfg.cls += ' roo-radio-set-inline';
34095 ['xs','sm','md','lg'].map(function(size){
34096 if (settings[size]) {
34097 cfg.cls += ' col-' + size + '-' + settings[size];
34105 initEvents : function()
34107 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34108 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34110 if(!this.fieldLabel.length){
34111 this.labelEl.hide();
34114 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34115 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34117 this.indicator = this.indicatorEl();
34119 if(this.indicator){
34120 this.indicator.addClass('invisible');
34123 this.originalValue = this.getValue();
34127 inputEl: function ()
34129 return this.el.select('.roo-radio-set-input', true).first();
34132 getChildContainer : function()
34134 return this.itemsEl;
34137 register : function(item)
34139 this.radioes.push(item);
34143 validate : function()
34145 if(this.getVisibilityEl().hasClass('hidden')){
34151 Roo.each(this.radioes, function(i){
34160 if(this.allowBlank) {
34164 if(this.disabled || valid){
34169 this.markInvalid();
34174 markValid : function()
34176 if(this.labelEl.isVisible(true)){
34177 this.indicatorEl().removeClass('visible');
34178 this.indicatorEl().addClass('invisible');
34181 this.el.removeClass([this.invalidClass, this.validClass]);
34182 this.el.addClass(this.validClass);
34184 this.fireEvent('valid', this);
34187 markInvalid : function(msg)
34189 if(this.allowBlank || this.disabled){
34193 if(this.labelEl.isVisible(true)){
34194 this.indicatorEl().removeClass('invisible');
34195 this.indicatorEl().addClass('visible');
34198 this.el.removeClass([this.invalidClass, this.validClass]);
34199 this.el.addClass(this.invalidClass);
34201 this.fireEvent('invalid', this, msg);
34205 setValue : function(v, suppressEvent)
34207 if(this.value === v){
34214 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34217 Roo.each(this.radioes, function(i){
34219 i.el.removeClass('checked');
34222 Roo.each(this.radioes, function(i){
34224 if(i.value === v || i.value.toString() === v.toString()){
34226 i.el.addClass('checked');
34228 if(suppressEvent !== true){
34229 this.fireEvent('check', this, i);
34240 clearInvalid : function(){
34242 if(!this.el || this.preventMark){
34246 this.el.removeClass([this.invalidClass]);
34248 this.fireEvent('valid', this);
34253 Roo.apply(Roo.bootstrap.RadioSet, {
34257 register : function(set)
34259 this.groups[set.name] = set;
34262 get: function(name)
34264 if (typeof(this.groups[name]) == 'undefined') {
34268 return this.groups[name] ;
34274 * Ext JS Library 1.1.1
34275 * Copyright(c) 2006-2007, Ext JS, LLC.
34277 * Originally Released Under LGPL - original licence link has changed is not relivant.
34280 * <script type="text/javascript">
34285 * @class Roo.bootstrap.SplitBar
34286 * @extends Roo.util.Observable
34287 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34291 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34292 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34293 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34294 split.minSize = 100;
34295 split.maxSize = 600;
34296 split.animate = true;
34297 split.on('moved', splitterMoved);
34300 * Create a new SplitBar
34301 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34302 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34303 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34304 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34305 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34306 position of the SplitBar).
34308 Roo.bootstrap.SplitBar = function(cfg){
34313 // dragElement : elm
34314 // resizingElement: el,
34316 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34317 // placement : Roo.bootstrap.SplitBar.LEFT ,
34318 // existingProxy ???
34321 this.el = Roo.get(cfg.dragElement, true);
34322 this.el.dom.unselectable = "on";
34324 this.resizingEl = Roo.get(cfg.resizingElement, true);
34328 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34329 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34332 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34335 * The minimum size of the resizing element. (Defaults to 0)
34341 * The maximum size of the resizing element. (Defaults to 2000)
34344 this.maxSize = 2000;
34347 * Whether to animate the transition to the new size
34350 this.animate = false;
34353 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34356 this.useShim = false;
34361 if(!cfg.existingProxy){
34363 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34365 this.proxy = Roo.get(cfg.existingProxy).dom;
34368 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34371 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34374 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34377 this.dragSpecs = {};
34380 * @private The adapter to use to positon and resize elements
34382 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34383 this.adapter.init(this);
34385 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34387 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34388 this.el.addClass("roo-splitbar-h");
34391 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34392 this.el.addClass("roo-splitbar-v");
34398 * Fires when the splitter is moved (alias for {@link #event-moved})
34399 * @param {Roo.bootstrap.SplitBar} this
34400 * @param {Number} newSize the new width or height
34405 * Fires when the splitter is moved
34406 * @param {Roo.bootstrap.SplitBar} this
34407 * @param {Number} newSize the new width or height
34411 * @event beforeresize
34412 * Fires before the splitter is dragged
34413 * @param {Roo.bootstrap.SplitBar} this
34415 "beforeresize" : true,
34417 "beforeapply" : true
34420 Roo.util.Observable.call(this);
34423 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34424 onStartProxyDrag : function(x, y){
34425 this.fireEvent("beforeresize", this);
34427 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34429 o.enableDisplayMode("block");
34430 // all splitbars share the same overlay
34431 Roo.bootstrap.SplitBar.prototype.overlay = o;
34433 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34434 this.overlay.show();
34435 Roo.get(this.proxy).setDisplayed("block");
34436 var size = this.adapter.getElementSize(this);
34437 this.activeMinSize = this.getMinimumSize();;
34438 this.activeMaxSize = this.getMaximumSize();;
34439 var c1 = size - this.activeMinSize;
34440 var c2 = Math.max(this.activeMaxSize - size, 0);
34441 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34442 this.dd.resetConstraints();
34443 this.dd.setXConstraint(
34444 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34445 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34447 this.dd.setYConstraint(0, 0);
34449 this.dd.resetConstraints();
34450 this.dd.setXConstraint(0, 0);
34451 this.dd.setYConstraint(
34452 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34453 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34456 this.dragSpecs.startSize = size;
34457 this.dragSpecs.startPoint = [x, y];
34458 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34462 * @private Called after the drag operation by the DDProxy
34464 onEndProxyDrag : function(e){
34465 Roo.get(this.proxy).setDisplayed(false);
34466 var endPoint = Roo.lib.Event.getXY(e);
34468 this.overlay.hide();
34471 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34472 newSize = this.dragSpecs.startSize +
34473 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34474 endPoint[0] - this.dragSpecs.startPoint[0] :
34475 this.dragSpecs.startPoint[0] - endPoint[0]
34478 newSize = this.dragSpecs.startSize +
34479 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34480 endPoint[1] - this.dragSpecs.startPoint[1] :
34481 this.dragSpecs.startPoint[1] - endPoint[1]
34484 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34485 if(newSize != this.dragSpecs.startSize){
34486 if(this.fireEvent('beforeapply', this, newSize) !== false){
34487 this.adapter.setElementSize(this, newSize);
34488 this.fireEvent("moved", this, newSize);
34489 this.fireEvent("resize", this, newSize);
34495 * Get the adapter this SplitBar uses
34496 * @return The adapter object
34498 getAdapter : function(){
34499 return this.adapter;
34503 * Set the adapter this SplitBar uses
34504 * @param {Object} adapter A SplitBar adapter object
34506 setAdapter : function(adapter){
34507 this.adapter = adapter;
34508 this.adapter.init(this);
34512 * Gets the minimum size for the resizing element
34513 * @return {Number} The minimum size
34515 getMinimumSize : function(){
34516 return this.minSize;
34520 * Sets the minimum size for the resizing element
34521 * @param {Number} minSize The minimum size
34523 setMinimumSize : function(minSize){
34524 this.minSize = minSize;
34528 * Gets the maximum size for the resizing element
34529 * @return {Number} The maximum size
34531 getMaximumSize : function(){
34532 return this.maxSize;
34536 * Sets the maximum size for the resizing element
34537 * @param {Number} maxSize The maximum size
34539 setMaximumSize : function(maxSize){
34540 this.maxSize = maxSize;
34544 * Sets the initialize size for the resizing element
34545 * @param {Number} size The initial size
34547 setCurrentSize : function(size){
34548 var oldAnimate = this.animate;
34549 this.animate = false;
34550 this.adapter.setElementSize(this, size);
34551 this.animate = oldAnimate;
34555 * Destroy this splitbar.
34556 * @param {Boolean} removeEl True to remove the element
34558 destroy : function(removeEl){
34560 this.shim.remove();
34563 this.proxy.parentNode.removeChild(this.proxy);
34571 * @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.
34573 Roo.bootstrap.SplitBar.createProxy = function(dir){
34574 var proxy = new Roo.Element(document.createElement("div"));
34575 proxy.unselectable();
34576 var cls = 'roo-splitbar-proxy';
34577 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34578 document.body.appendChild(proxy.dom);
34583 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34584 * Default Adapter. It assumes the splitter and resizing element are not positioned
34585 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34587 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34590 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34591 // do nothing for now
34592 init : function(s){
34596 * Called before drag operations to get the current size of the resizing element.
34597 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34599 getElementSize : function(s){
34600 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34601 return s.resizingEl.getWidth();
34603 return s.resizingEl.getHeight();
34608 * Called after drag operations to set the size of the resizing element.
34609 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34610 * @param {Number} newSize The new size to set
34611 * @param {Function} onComplete A function to be invoked when resizing is complete
34613 setElementSize : function(s, newSize, onComplete){
34614 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34616 s.resizingEl.setWidth(newSize);
34618 onComplete(s, newSize);
34621 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34626 s.resizingEl.setHeight(newSize);
34628 onComplete(s, newSize);
34631 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34638 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34639 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34640 * Adapter that moves the splitter element to align with the resized sizing element.
34641 * Used with an absolute positioned SplitBar.
34642 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34643 * document.body, make sure you assign an id to the body element.
34645 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34646 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34647 this.container = Roo.get(container);
34650 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34651 init : function(s){
34652 this.basic.init(s);
34655 getElementSize : function(s){
34656 return this.basic.getElementSize(s);
34659 setElementSize : function(s, newSize, onComplete){
34660 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34663 moveSplitter : function(s){
34664 var yes = Roo.bootstrap.SplitBar;
34665 switch(s.placement){
34667 s.el.setX(s.resizingEl.getRight());
34670 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34673 s.el.setY(s.resizingEl.getBottom());
34676 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34683 * Orientation constant - Create a vertical SplitBar
34687 Roo.bootstrap.SplitBar.VERTICAL = 1;
34690 * Orientation constant - Create a horizontal SplitBar
34694 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34697 * Placement constant - The resizing element is to the left of the splitter element
34701 Roo.bootstrap.SplitBar.LEFT = 1;
34704 * Placement constant - The resizing element is to the right of the splitter element
34708 Roo.bootstrap.SplitBar.RIGHT = 2;
34711 * Placement constant - The resizing element is positioned above the splitter element
34715 Roo.bootstrap.SplitBar.TOP = 3;
34718 * Placement constant - The resizing element is positioned under splitter element
34722 Roo.bootstrap.SplitBar.BOTTOM = 4;
34723 Roo.namespace("Roo.bootstrap.layout");/*
34725 * Ext JS Library 1.1.1
34726 * Copyright(c) 2006-2007, Ext JS, LLC.
34728 * Originally Released Under LGPL - original licence link has changed is not relivant.
34731 * <script type="text/javascript">
34735 * @class Roo.bootstrap.layout.Manager
34736 * @extends Roo.bootstrap.Component
34737 * Base class for layout managers.
34739 Roo.bootstrap.layout.Manager = function(config)
34741 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34747 /** false to disable window resize monitoring @type Boolean */
34748 this.monitorWindowResize = true;
34753 * Fires when a layout is performed.
34754 * @param {Roo.LayoutManager} this
34758 * @event regionresized
34759 * Fires when the user resizes a region.
34760 * @param {Roo.LayoutRegion} region The resized region
34761 * @param {Number} newSize The new size (width for east/west, height for north/south)
34763 "regionresized" : true,
34765 * @event regioncollapsed
34766 * Fires when a region is collapsed.
34767 * @param {Roo.LayoutRegion} region The collapsed region
34769 "regioncollapsed" : true,
34771 * @event regionexpanded
34772 * Fires when a region is expanded.
34773 * @param {Roo.LayoutRegion} region The expanded region
34775 "regionexpanded" : true
34777 this.updating = false;
34780 this.el = Roo.get(config.el);
34786 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34791 monitorWindowResize : true,
34797 onRender : function(ct, position)
34800 this.el = Roo.get(ct);
34803 //this.fireEvent('render',this);
34807 initEvents: function()
34811 // ie scrollbar fix
34812 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34813 document.body.scroll = "no";
34814 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34815 this.el.position('relative');
34817 this.id = this.el.id;
34818 this.el.addClass("roo-layout-container");
34819 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34820 if(this.el.dom != document.body ) {
34821 this.el.on('resize', this.layout,this);
34822 this.el.on('show', this.layout,this);
34828 * Returns true if this layout is currently being updated
34829 * @return {Boolean}
34831 isUpdating : function(){
34832 return this.updating;
34836 * Suspend the LayoutManager from doing auto-layouts while
34837 * making multiple add or remove calls
34839 beginUpdate : function(){
34840 this.updating = true;
34844 * Restore auto-layouts and optionally disable the manager from performing a layout
34845 * @param {Boolean} noLayout true to disable a layout update
34847 endUpdate : function(noLayout){
34848 this.updating = false;
34854 layout: function(){
34858 onRegionResized : function(region, newSize){
34859 this.fireEvent("regionresized", region, newSize);
34863 onRegionCollapsed : function(region){
34864 this.fireEvent("regioncollapsed", region);
34867 onRegionExpanded : function(region){
34868 this.fireEvent("regionexpanded", region);
34872 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34873 * performs box-model adjustments.
34874 * @return {Object} The size as an object {width: (the width), height: (the height)}
34876 getViewSize : function()
34879 if(this.el.dom != document.body){
34880 size = this.el.getSize();
34882 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34884 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34885 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34890 * Returns the Element this layout is bound to.
34891 * @return {Roo.Element}
34893 getEl : function(){
34898 * Returns the specified region.
34899 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34900 * @return {Roo.LayoutRegion}
34902 getRegion : function(target){
34903 return this.regions[target.toLowerCase()];
34906 onWindowResize : function(){
34907 if(this.monitorWindowResize){
34914 * Ext JS Library 1.1.1
34915 * Copyright(c) 2006-2007, Ext JS, LLC.
34917 * Originally Released Under LGPL - original licence link has changed is not relivant.
34920 * <script type="text/javascript">
34923 * @class Roo.bootstrap.layout.Border
34924 * @extends Roo.bootstrap.layout.Manager
34925 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34926 * please see: examples/bootstrap/nested.html<br><br>
34928 <b>The container the layout is rendered into can be either the body element or any other element.
34929 If it is not the body element, the container needs to either be an absolute positioned element,
34930 or you will need to add "position:relative" to the css of the container. You will also need to specify
34931 the container size if it is not the body element.</b>
34934 * Create a new Border
34935 * @param {Object} config Configuration options
34937 Roo.bootstrap.layout.Border = function(config){
34938 config = config || {};
34939 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34943 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34944 if(config[region]){
34945 config[region].region = region;
34946 this.addRegion(config[region]);
34952 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34954 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34956 * Creates and adds a new region if it doesn't already exist.
34957 * @param {String} target The target region key (north, south, east, west or center).
34958 * @param {Object} config The regions config object
34959 * @return {BorderLayoutRegion} The new region
34961 addRegion : function(config)
34963 if(!this.regions[config.region]){
34964 var r = this.factory(config);
34965 this.bindRegion(r);
34967 return this.regions[config.region];
34971 bindRegion : function(r){
34972 this.regions[r.config.region] = r;
34974 r.on("visibilitychange", this.layout, this);
34975 r.on("paneladded", this.layout, this);
34976 r.on("panelremoved", this.layout, this);
34977 r.on("invalidated", this.layout, this);
34978 r.on("resized", this.onRegionResized, this);
34979 r.on("collapsed", this.onRegionCollapsed, this);
34980 r.on("expanded", this.onRegionExpanded, this);
34984 * Performs a layout update.
34986 layout : function()
34988 if(this.updating) {
34992 // render all the rebions if they have not been done alreayd?
34993 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34994 if(this.regions[region] && !this.regions[region].bodyEl){
34995 this.regions[region].onRender(this.el)
34999 var size = this.getViewSize();
35000 var w = size.width;
35001 var h = size.height;
35006 //var x = 0, y = 0;
35008 var rs = this.regions;
35009 var north = rs["north"];
35010 var south = rs["south"];
35011 var west = rs["west"];
35012 var east = rs["east"];
35013 var center = rs["center"];
35014 //if(this.hideOnLayout){ // not supported anymore
35015 //c.el.setStyle("display", "none");
35017 if(north && north.isVisible()){
35018 var b = north.getBox();
35019 var m = north.getMargins();
35020 b.width = w - (m.left+m.right);
35023 centerY = b.height + b.y + m.bottom;
35024 centerH -= centerY;
35025 north.updateBox(this.safeBox(b));
35027 if(south && south.isVisible()){
35028 var b = south.getBox();
35029 var m = south.getMargins();
35030 b.width = w - (m.left+m.right);
35032 var totalHeight = (b.height + m.top + m.bottom);
35033 b.y = h - totalHeight + m.top;
35034 centerH -= totalHeight;
35035 south.updateBox(this.safeBox(b));
35037 if(west && west.isVisible()){
35038 var b = west.getBox();
35039 var m = west.getMargins();
35040 b.height = centerH - (m.top+m.bottom);
35042 b.y = centerY + m.top;
35043 var totalWidth = (b.width + m.left + m.right);
35044 centerX += totalWidth;
35045 centerW -= totalWidth;
35046 west.updateBox(this.safeBox(b));
35048 if(east && east.isVisible()){
35049 var b = east.getBox();
35050 var m = east.getMargins();
35051 b.height = centerH - (m.top+m.bottom);
35052 var totalWidth = (b.width + m.left + m.right);
35053 b.x = w - totalWidth + m.left;
35054 b.y = centerY + m.top;
35055 centerW -= totalWidth;
35056 east.updateBox(this.safeBox(b));
35059 var m = center.getMargins();
35061 x: centerX + m.left,
35062 y: centerY + m.top,
35063 width: centerW - (m.left+m.right),
35064 height: centerH - (m.top+m.bottom)
35066 //if(this.hideOnLayout){
35067 //center.el.setStyle("display", "block");
35069 center.updateBox(this.safeBox(centerBox));
35072 this.fireEvent("layout", this);
35076 safeBox : function(box){
35077 box.width = Math.max(0, box.width);
35078 box.height = Math.max(0, box.height);
35083 * Adds a ContentPanel (or subclass) to this layout.
35084 * @param {String} target The target region key (north, south, east, west or center).
35085 * @param {Roo.ContentPanel} panel The panel to add
35086 * @return {Roo.ContentPanel} The added panel
35088 add : function(target, panel){
35090 target = target.toLowerCase();
35091 return this.regions[target].add(panel);
35095 * Remove a ContentPanel (or subclass) to this layout.
35096 * @param {String} target The target region key (north, south, east, west or center).
35097 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35098 * @return {Roo.ContentPanel} The removed panel
35100 remove : function(target, panel){
35101 target = target.toLowerCase();
35102 return this.regions[target].remove(panel);
35106 * Searches all regions for a panel with the specified id
35107 * @param {String} panelId
35108 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35110 findPanel : function(panelId){
35111 var rs = this.regions;
35112 for(var target in rs){
35113 if(typeof rs[target] != "function"){
35114 var p = rs[target].getPanel(panelId);
35124 * Searches all regions for a panel with the specified id and activates (shows) it.
35125 * @param {String/ContentPanel} panelId The panels id or the panel itself
35126 * @return {Roo.ContentPanel} The shown panel or null
35128 showPanel : function(panelId) {
35129 var rs = this.regions;
35130 for(var target in rs){
35131 var r = rs[target];
35132 if(typeof r != "function"){
35133 if(r.hasPanel(panelId)){
35134 return r.showPanel(panelId);
35142 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35143 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35146 restoreState : function(provider){
35148 provider = Roo.state.Manager;
35150 var sm = new Roo.LayoutStateManager();
35151 sm.init(this, provider);
35157 * Adds a xtype elements to the layout.
35161 xtype : 'ContentPanel',
35168 xtype : 'NestedLayoutPanel',
35174 items : [ ... list of content panels or nested layout panels.. ]
35178 * @param {Object} cfg Xtype definition of item to add.
35180 addxtype : function(cfg)
35182 // basically accepts a pannel...
35183 // can accept a layout region..!?!?
35184 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35187 // theory? children can only be panels??
35189 //if (!cfg.xtype.match(/Panel$/)) {
35194 if (typeof(cfg.region) == 'undefined') {
35195 Roo.log("Failed to add Panel, region was not set");
35199 var region = cfg.region;
35205 xitems = cfg.items;
35212 case 'Content': // ContentPanel (el, cfg)
35213 case 'Scroll': // ContentPanel (el, cfg)
35215 cfg.autoCreate = true;
35216 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35218 // var el = this.el.createChild();
35219 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35222 this.add(region, ret);
35226 case 'TreePanel': // our new panel!
35227 cfg.el = this.el.createChild();
35228 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35229 this.add(region, ret);
35234 // create a new Layout (which is a Border Layout...
35236 var clayout = cfg.layout;
35237 clayout.el = this.el.createChild();
35238 clayout.items = clayout.items || [];
35242 // replace this exitems with the clayout ones..
35243 xitems = clayout.items;
35245 // force background off if it's in center...
35246 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35247 cfg.background = false;
35249 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35252 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35253 //console.log('adding nested layout panel ' + cfg.toSource());
35254 this.add(region, ret);
35255 nb = {}; /// find first...
35260 // needs grid and region
35262 //var el = this.getRegion(region).el.createChild();
35264 *var el = this.el.createChild();
35265 // create the grid first...
35266 cfg.grid.container = el;
35267 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35270 if (region == 'center' && this.active ) {
35271 cfg.background = false;
35274 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35276 this.add(region, ret);
35278 if (cfg.background) {
35279 // render grid on panel activation (if panel background)
35280 ret.on('activate', function(gp) {
35281 if (!gp.grid.rendered) {
35282 // gp.grid.render(el);
35286 // cfg.grid.render(el);
35292 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35293 // it was the old xcomponent building that caused this before.
35294 // espeically if border is the top element in the tree.
35304 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35306 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35307 this.add(region, ret);
35311 throw "Can not add '" + cfg.xtype + "' to Border";
35317 this.beginUpdate();
35321 Roo.each(xitems, function(i) {
35322 region = nb && i.region ? i.region : false;
35324 var add = ret.addxtype(i);
35327 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35328 if (!i.background) {
35329 abn[region] = nb[region] ;
35336 // make the last non-background panel active..
35337 //if (nb) { Roo.log(abn); }
35340 for(var r in abn) {
35341 region = this.getRegion(r);
35343 // tried using nb[r], but it does not work..
35345 region.showPanel(abn[r]);
35356 factory : function(cfg)
35359 var validRegions = Roo.bootstrap.layout.Border.regions;
35361 var target = cfg.region;
35364 var r = Roo.bootstrap.layout;
35368 return new r.North(cfg);
35370 return new r.South(cfg);
35372 return new r.East(cfg);
35374 return new r.West(cfg);
35376 return new r.Center(cfg);
35378 throw 'Layout region "'+target+'" not supported.';
35385 * Ext JS Library 1.1.1
35386 * Copyright(c) 2006-2007, Ext JS, LLC.
35388 * Originally Released Under LGPL - original licence link has changed is not relivant.
35391 * <script type="text/javascript">
35395 * @class Roo.bootstrap.layout.Basic
35396 * @extends Roo.util.Observable
35397 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35398 * and does not have a titlebar, tabs or any other features. All it does is size and position
35399 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35400 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35401 * @cfg {string} region the region that it inhabits..
35402 * @cfg {bool} skipConfig skip config?
35406 Roo.bootstrap.layout.Basic = function(config){
35408 this.mgr = config.mgr;
35410 this.position = config.region;
35412 var skipConfig = config.skipConfig;
35416 * @scope Roo.BasicLayoutRegion
35420 * @event beforeremove
35421 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35422 * @param {Roo.LayoutRegion} this
35423 * @param {Roo.ContentPanel} panel The panel
35424 * @param {Object} e The cancel event object
35426 "beforeremove" : true,
35428 * @event invalidated
35429 * Fires when the layout for this region is changed.
35430 * @param {Roo.LayoutRegion} this
35432 "invalidated" : true,
35434 * @event visibilitychange
35435 * Fires when this region is shown or hidden
35436 * @param {Roo.LayoutRegion} this
35437 * @param {Boolean} visibility true or false
35439 "visibilitychange" : true,
35441 * @event paneladded
35442 * Fires when a panel is added.
35443 * @param {Roo.LayoutRegion} this
35444 * @param {Roo.ContentPanel} panel The panel
35446 "paneladded" : true,
35448 * @event panelremoved
35449 * Fires when a panel is removed.
35450 * @param {Roo.LayoutRegion} this
35451 * @param {Roo.ContentPanel} panel The panel
35453 "panelremoved" : true,
35455 * @event beforecollapse
35456 * Fires when this region before collapse.
35457 * @param {Roo.LayoutRegion} this
35459 "beforecollapse" : true,
35462 * Fires when this region is collapsed.
35463 * @param {Roo.LayoutRegion} this
35465 "collapsed" : true,
35468 * Fires when this region is expanded.
35469 * @param {Roo.LayoutRegion} this
35474 * Fires when this region is slid into view.
35475 * @param {Roo.LayoutRegion} this
35477 "slideshow" : true,
35480 * Fires when this region slides out of view.
35481 * @param {Roo.LayoutRegion} this
35483 "slidehide" : true,
35485 * @event panelactivated
35486 * Fires when a panel is activated.
35487 * @param {Roo.LayoutRegion} this
35488 * @param {Roo.ContentPanel} panel The activated panel
35490 "panelactivated" : true,
35493 * Fires when the user resizes this region.
35494 * @param {Roo.LayoutRegion} this
35495 * @param {Number} newSize The new size (width for east/west, height for north/south)
35499 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35500 this.panels = new Roo.util.MixedCollection();
35501 this.panels.getKey = this.getPanelId.createDelegate(this);
35503 this.activePanel = null;
35504 // ensure listeners are added...
35506 if (config.listeners || config.events) {
35507 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35508 listeners : config.listeners || {},
35509 events : config.events || {}
35513 if(skipConfig !== true){
35514 this.applyConfig(config);
35518 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35520 getPanelId : function(p){
35524 applyConfig : function(config){
35525 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35526 this.config = config;
35531 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35532 * the width, for horizontal (north, south) the height.
35533 * @param {Number} newSize The new width or height
35535 resizeTo : function(newSize){
35536 var el = this.el ? this.el :
35537 (this.activePanel ? this.activePanel.getEl() : null);
35539 switch(this.position){
35542 el.setWidth(newSize);
35543 this.fireEvent("resized", this, newSize);
35547 el.setHeight(newSize);
35548 this.fireEvent("resized", this, newSize);
35554 getBox : function(){
35555 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35558 getMargins : function(){
35559 return this.margins;
35562 updateBox : function(box){
35564 var el = this.activePanel.getEl();
35565 el.dom.style.left = box.x + "px";
35566 el.dom.style.top = box.y + "px";
35567 this.activePanel.setSize(box.width, box.height);
35571 * Returns the container element for this region.
35572 * @return {Roo.Element}
35574 getEl : function(){
35575 return this.activePanel;
35579 * Returns true if this region is currently visible.
35580 * @return {Boolean}
35582 isVisible : function(){
35583 return this.activePanel ? true : false;
35586 setActivePanel : function(panel){
35587 panel = this.getPanel(panel);
35588 if(this.activePanel && this.activePanel != panel){
35589 this.activePanel.setActiveState(false);
35590 this.activePanel.getEl().setLeftTop(-10000,-10000);
35592 this.activePanel = panel;
35593 panel.setActiveState(true);
35595 panel.setSize(this.box.width, this.box.height);
35597 this.fireEvent("panelactivated", this, panel);
35598 this.fireEvent("invalidated");
35602 * Show the specified panel.
35603 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35604 * @return {Roo.ContentPanel} The shown panel or null
35606 showPanel : function(panel){
35607 panel = this.getPanel(panel);
35609 this.setActivePanel(panel);
35615 * Get the active panel for this region.
35616 * @return {Roo.ContentPanel} The active panel or null
35618 getActivePanel : function(){
35619 return this.activePanel;
35623 * Add the passed ContentPanel(s)
35624 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35625 * @return {Roo.ContentPanel} The panel added (if only one was added)
35627 add : function(panel){
35628 if(arguments.length > 1){
35629 for(var i = 0, len = arguments.length; i < len; i++) {
35630 this.add(arguments[i]);
35634 if(this.hasPanel(panel)){
35635 this.showPanel(panel);
35638 var el = panel.getEl();
35639 if(el.dom.parentNode != this.mgr.el.dom){
35640 this.mgr.el.dom.appendChild(el.dom);
35642 if(panel.setRegion){
35643 panel.setRegion(this);
35645 this.panels.add(panel);
35646 el.setStyle("position", "absolute");
35647 if(!panel.background){
35648 this.setActivePanel(panel);
35649 if(this.config.initialSize && this.panels.getCount()==1){
35650 this.resizeTo(this.config.initialSize);
35653 this.fireEvent("paneladded", this, panel);
35658 * Returns true if the panel is in this region.
35659 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35660 * @return {Boolean}
35662 hasPanel : function(panel){
35663 if(typeof panel == "object"){ // must be panel obj
35664 panel = panel.getId();
35666 return this.getPanel(panel) ? true : false;
35670 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35671 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35672 * @param {Boolean} preservePanel Overrides the config preservePanel option
35673 * @return {Roo.ContentPanel} The panel that was removed
35675 remove : function(panel, preservePanel){
35676 panel = this.getPanel(panel);
35681 this.fireEvent("beforeremove", this, panel, e);
35682 if(e.cancel === true){
35685 var panelId = panel.getId();
35686 this.panels.removeKey(panelId);
35691 * Returns the panel specified or null if it's not in this region.
35692 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35693 * @return {Roo.ContentPanel}
35695 getPanel : function(id){
35696 if(typeof id == "object"){ // must be panel obj
35699 return this.panels.get(id);
35703 * Returns this regions position (north/south/east/west/center).
35706 getPosition: function(){
35707 return this.position;
35711 * Ext JS Library 1.1.1
35712 * Copyright(c) 2006-2007, Ext JS, LLC.
35714 * Originally Released Under LGPL - original licence link has changed is not relivant.
35717 * <script type="text/javascript">
35721 * @class Roo.bootstrap.layout.Region
35722 * @extends Roo.bootstrap.layout.Basic
35723 * This class represents a region in a layout manager.
35725 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35726 * @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})
35727 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35728 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35729 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35730 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35731 * @cfg {String} title The title for the region (overrides panel titles)
35732 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35733 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35734 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35735 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35736 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35737 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35738 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35739 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35740 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35741 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35743 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35744 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35745 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35746 * @cfg {Number} width For East/West panels
35747 * @cfg {Number} height For North/South panels
35748 * @cfg {Boolean} split To show the splitter
35749 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35751 * @cfg {string} cls Extra CSS classes to add to region
35753 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35754 * @cfg {string} region the region that it inhabits..
35757 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35758 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35760 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35761 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35762 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35764 Roo.bootstrap.layout.Region = function(config)
35766 this.applyConfig(config);
35768 var mgr = config.mgr;
35769 var pos = config.region;
35770 config.skipConfig = true;
35771 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35774 this.onRender(mgr.el);
35777 this.visible = true;
35778 this.collapsed = false;
35779 this.unrendered_panels = [];
35782 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35784 position: '', // set by wrapper (eg. north/south etc..)
35785 unrendered_panels : null, // unrendered panels.
35786 createBody : function(){
35787 /** This region's body element
35788 * @type Roo.Element */
35789 this.bodyEl = this.el.createChild({
35791 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35795 onRender: function(ctr, pos)
35797 var dh = Roo.DomHelper;
35798 /** This region's container element
35799 * @type Roo.Element */
35800 this.el = dh.append(ctr.dom, {
35802 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35804 /** This region's title element
35805 * @type Roo.Element */
35807 this.titleEl = dh.append(this.el.dom,
35810 unselectable: "on",
35811 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35813 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35814 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35817 this.titleEl.enableDisplayMode();
35818 /** This region's title text element
35819 * @type HTMLElement */
35820 this.titleTextEl = this.titleEl.dom.firstChild;
35821 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35823 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35824 this.closeBtn.enableDisplayMode();
35825 this.closeBtn.on("click", this.closeClicked, this);
35826 this.closeBtn.hide();
35828 this.createBody(this.config);
35829 if(this.config.hideWhenEmpty){
35831 this.on("paneladded", this.validateVisibility, this);
35832 this.on("panelremoved", this.validateVisibility, this);
35834 if(this.autoScroll){
35835 this.bodyEl.setStyle("overflow", "auto");
35837 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35839 //if(c.titlebar !== false){
35840 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35841 this.titleEl.hide();
35843 this.titleEl.show();
35844 if(this.config.title){
35845 this.titleTextEl.innerHTML = this.config.title;
35849 if(this.config.collapsed){
35850 this.collapse(true);
35852 if(this.config.hidden){
35856 if (this.unrendered_panels && this.unrendered_panels.length) {
35857 for (var i =0;i< this.unrendered_panels.length; i++) {
35858 this.add(this.unrendered_panels[i]);
35860 this.unrendered_panels = null;
35866 applyConfig : function(c)
35869 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35870 var dh = Roo.DomHelper;
35871 if(c.titlebar !== false){
35872 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35873 this.collapseBtn.on("click", this.collapse, this);
35874 this.collapseBtn.enableDisplayMode();
35876 if(c.showPin === true || this.showPin){
35877 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35878 this.stickBtn.enableDisplayMode();
35879 this.stickBtn.on("click", this.expand, this);
35880 this.stickBtn.hide();
35885 /** This region's collapsed element
35886 * @type Roo.Element */
35889 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35890 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35893 if(c.floatable !== false){
35894 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35895 this.collapsedEl.on("click", this.collapseClick, this);
35898 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35899 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35900 id: "message", unselectable: "on", style:{"float":"left"}});
35901 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35903 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35904 this.expandBtn.on("click", this.expand, this);
35908 if(this.collapseBtn){
35909 this.collapseBtn.setVisible(c.collapsible == true);
35912 this.cmargins = c.cmargins || this.cmargins ||
35913 (this.position == "west" || this.position == "east" ?
35914 {top: 0, left: 2, right:2, bottom: 0} :
35915 {top: 2, left: 0, right:0, bottom: 2});
35917 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35920 this.bottomTabs = c.tabPosition != "top";
35922 this.autoScroll = c.autoScroll || false;
35927 this.duration = c.duration || .30;
35928 this.slideDuration = c.slideDuration || .45;
35933 * Returns true if this region is currently visible.
35934 * @return {Boolean}
35936 isVisible : function(){
35937 return this.visible;
35941 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35942 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35944 //setCollapsedTitle : function(title){
35945 // title = title || " ";
35946 // if(this.collapsedTitleTextEl){
35947 // this.collapsedTitleTextEl.innerHTML = title;
35951 getBox : function(){
35953 // if(!this.collapsed){
35954 b = this.el.getBox(false, true);
35956 // b = this.collapsedEl.getBox(false, true);
35961 getMargins : function(){
35962 return this.margins;
35963 //return this.collapsed ? this.cmargins : this.margins;
35966 highlight : function(){
35967 this.el.addClass("x-layout-panel-dragover");
35970 unhighlight : function(){
35971 this.el.removeClass("x-layout-panel-dragover");
35974 updateBox : function(box)
35976 if (!this.bodyEl) {
35977 return; // not rendered yet..
35981 if(!this.collapsed){
35982 this.el.dom.style.left = box.x + "px";
35983 this.el.dom.style.top = box.y + "px";
35984 this.updateBody(box.width, box.height);
35986 this.collapsedEl.dom.style.left = box.x + "px";
35987 this.collapsedEl.dom.style.top = box.y + "px";
35988 this.collapsedEl.setSize(box.width, box.height);
35991 this.tabs.autoSizeTabs();
35995 updateBody : function(w, h)
35998 this.el.setWidth(w);
35999 w -= this.el.getBorderWidth("rl");
36000 if(this.config.adjustments){
36001 w += this.config.adjustments[0];
36004 if(h !== null && h > 0){
36005 this.el.setHeight(h);
36006 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36007 h -= this.el.getBorderWidth("tb");
36008 if(this.config.adjustments){
36009 h += this.config.adjustments[1];
36011 this.bodyEl.setHeight(h);
36013 h = this.tabs.syncHeight(h);
36016 if(this.panelSize){
36017 w = w !== null ? w : this.panelSize.width;
36018 h = h !== null ? h : this.panelSize.height;
36020 if(this.activePanel){
36021 var el = this.activePanel.getEl();
36022 w = w !== null ? w : el.getWidth();
36023 h = h !== null ? h : el.getHeight();
36024 this.panelSize = {width: w, height: h};
36025 this.activePanel.setSize(w, h);
36027 if(Roo.isIE && this.tabs){
36028 this.tabs.el.repaint();
36033 * Returns the container element for this region.
36034 * @return {Roo.Element}
36036 getEl : function(){
36041 * Hides this region.
36044 //if(!this.collapsed){
36045 this.el.dom.style.left = "-2000px";
36048 // this.collapsedEl.dom.style.left = "-2000px";
36049 // this.collapsedEl.hide();
36051 this.visible = false;
36052 this.fireEvent("visibilitychange", this, false);
36056 * Shows this region if it was previously hidden.
36059 //if(!this.collapsed){
36062 // this.collapsedEl.show();
36064 this.visible = true;
36065 this.fireEvent("visibilitychange", this, true);
36068 closeClicked : function(){
36069 if(this.activePanel){
36070 this.remove(this.activePanel);
36074 collapseClick : function(e){
36076 e.stopPropagation();
36079 e.stopPropagation();
36085 * Collapses this region.
36086 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36089 collapse : function(skipAnim, skipCheck = false){
36090 if(this.collapsed) {
36094 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36096 this.collapsed = true;
36098 this.split.el.hide();
36100 if(this.config.animate && skipAnim !== true){
36101 this.fireEvent("invalidated", this);
36102 this.animateCollapse();
36104 this.el.setLocation(-20000,-20000);
36106 this.collapsedEl.show();
36107 this.fireEvent("collapsed", this);
36108 this.fireEvent("invalidated", this);
36114 animateCollapse : function(){
36119 * Expands this region if it was previously collapsed.
36120 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36121 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36124 expand : function(e, skipAnim){
36126 e.stopPropagation();
36128 if(!this.collapsed || this.el.hasActiveFx()) {
36132 this.afterSlideIn();
36135 this.collapsed = false;
36136 if(this.config.animate && skipAnim !== true){
36137 this.animateExpand();
36141 this.split.el.show();
36143 this.collapsedEl.setLocation(-2000,-2000);
36144 this.collapsedEl.hide();
36145 this.fireEvent("invalidated", this);
36146 this.fireEvent("expanded", this);
36150 animateExpand : function(){
36154 initTabs : function()
36156 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36158 var ts = new Roo.bootstrap.panel.Tabs({
36159 el: this.bodyEl.dom,
36160 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36161 disableTooltips: this.config.disableTabTips,
36162 toolbar : this.config.toolbar
36165 if(this.config.hideTabs){
36166 ts.stripWrap.setDisplayed(false);
36169 ts.resizeTabs = this.config.resizeTabs === true;
36170 ts.minTabWidth = this.config.minTabWidth || 40;
36171 ts.maxTabWidth = this.config.maxTabWidth || 250;
36172 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36173 ts.monitorResize = false;
36174 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36175 ts.bodyEl.addClass('roo-layout-tabs-body');
36176 this.panels.each(this.initPanelAsTab, this);
36179 initPanelAsTab : function(panel){
36180 var ti = this.tabs.addTab(
36184 this.config.closeOnTab && panel.isClosable(),
36187 if(panel.tabTip !== undefined){
36188 ti.setTooltip(panel.tabTip);
36190 ti.on("activate", function(){
36191 this.setActivePanel(panel);
36194 if(this.config.closeOnTab){
36195 ti.on("beforeclose", function(t, e){
36197 this.remove(panel);
36201 panel.tabItem = ti;
36206 updatePanelTitle : function(panel, title)
36208 if(this.activePanel == panel){
36209 this.updateTitle(title);
36212 var ti = this.tabs.getTab(panel.getEl().id);
36214 if(panel.tabTip !== undefined){
36215 ti.setTooltip(panel.tabTip);
36220 updateTitle : function(title){
36221 if(this.titleTextEl && !this.config.title){
36222 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36226 setActivePanel : function(panel)
36228 panel = this.getPanel(panel);
36229 if(this.activePanel && this.activePanel != panel){
36230 if(this.activePanel.setActiveState(false) === false){
36234 this.activePanel = panel;
36235 panel.setActiveState(true);
36236 if(this.panelSize){
36237 panel.setSize(this.panelSize.width, this.panelSize.height);
36240 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36242 this.updateTitle(panel.getTitle());
36244 this.fireEvent("invalidated", this);
36246 this.fireEvent("panelactivated", this, panel);
36250 * Shows the specified panel.
36251 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36252 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36254 showPanel : function(panel)
36256 panel = this.getPanel(panel);
36259 var tab = this.tabs.getTab(panel.getEl().id);
36260 if(tab.isHidden()){
36261 this.tabs.unhideTab(tab.id);
36265 this.setActivePanel(panel);
36272 * Get the active panel for this region.
36273 * @return {Roo.ContentPanel} The active panel or null
36275 getActivePanel : function(){
36276 return this.activePanel;
36279 validateVisibility : function(){
36280 if(this.panels.getCount() < 1){
36281 this.updateTitle(" ");
36282 this.closeBtn.hide();
36285 if(!this.isVisible()){
36292 * Adds the passed ContentPanel(s) to this region.
36293 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36294 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36296 add : function(panel)
36298 if(arguments.length > 1){
36299 for(var i = 0, len = arguments.length; i < len; i++) {
36300 this.add(arguments[i]);
36305 // if we have not been rendered yet, then we can not really do much of this..
36306 if (!this.bodyEl) {
36307 this.unrendered_panels.push(panel);
36314 if(this.hasPanel(panel)){
36315 this.showPanel(panel);
36318 panel.setRegion(this);
36319 this.panels.add(panel);
36320 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36321 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36322 // and hide them... ???
36323 this.bodyEl.dom.appendChild(panel.getEl().dom);
36324 if(panel.background !== true){
36325 this.setActivePanel(panel);
36327 this.fireEvent("paneladded", this, panel);
36334 this.initPanelAsTab(panel);
36338 if(panel.background !== true){
36339 this.tabs.activate(panel.getEl().id);
36341 this.fireEvent("paneladded", this, panel);
36346 * Hides the tab for the specified panel.
36347 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36349 hidePanel : function(panel){
36350 if(this.tabs && (panel = this.getPanel(panel))){
36351 this.tabs.hideTab(panel.getEl().id);
36356 * Unhides the tab for a previously hidden panel.
36357 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36359 unhidePanel : function(panel){
36360 if(this.tabs && (panel = this.getPanel(panel))){
36361 this.tabs.unhideTab(panel.getEl().id);
36365 clearPanels : function(){
36366 while(this.panels.getCount() > 0){
36367 this.remove(this.panels.first());
36372 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36373 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36374 * @param {Boolean} preservePanel Overrides the config preservePanel option
36375 * @return {Roo.ContentPanel} The panel that was removed
36377 remove : function(panel, preservePanel)
36379 panel = this.getPanel(panel);
36384 this.fireEvent("beforeremove", this, panel, e);
36385 if(e.cancel === true){
36388 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36389 var panelId = panel.getId();
36390 this.panels.removeKey(panelId);
36392 document.body.appendChild(panel.getEl().dom);
36395 this.tabs.removeTab(panel.getEl().id);
36396 }else if (!preservePanel){
36397 this.bodyEl.dom.removeChild(panel.getEl().dom);
36399 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36400 var p = this.panels.first();
36401 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36402 tempEl.appendChild(p.getEl().dom);
36403 this.bodyEl.update("");
36404 this.bodyEl.dom.appendChild(p.getEl().dom);
36406 this.updateTitle(p.getTitle());
36408 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36409 this.setActivePanel(p);
36411 panel.setRegion(null);
36412 if(this.activePanel == panel){
36413 this.activePanel = null;
36415 if(this.config.autoDestroy !== false && preservePanel !== true){
36416 try{panel.destroy();}catch(e){}
36418 this.fireEvent("panelremoved", this, panel);
36423 * Returns the TabPanel component used by this region
36424 * @return {Roo.TabPanel}
36426 getTabs : function(){
36430 createTool : function(parentEl, className){
36431 var btn = Roo.DomHelper.append(parentEl, {
36433 cls: "x-layout-tools-button",
36436 cls: "roo-layout-tools-button-inner " + className,
36440 btn.addClassOnOver("roo-layout-tools-button-over");
36445 * Ext JS Library 1.1.1
36446 * Copyright(c) 2006-2007, Ext JS, LLC.
36448 * Originally Released Under LGPL - original licence link has changed is not relivant.
36451 * <script type="text/javascript">
36457 * @class Roo.SplitLayoutRegion
36458 * @extends Roo.LayoutRegion
36459 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36461 Roo.bootstrap.layout.Split = function(config){
36462 this.cursor = config.cursor;
36463 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36466 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36468 splitTip : "Drag to resize.",
36469 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36470 useSplitTips : false,
36472 applyConfig : function(config){
36473 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36476 onRender : function(ctr,pos) {
36478 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36479 if(!this.config.split){
36484 var splitEl = Roo.DomHelper.append(ctr.dom, {
36486 id: this.el.id + "-split",
36487 cls: "roo-layout-split roo-layout-split-"+this.position,
36490 /** The SplitBar for this region
36491 * @type Roo.SplitBar */
36492 // does not exist yet...
36493 Roo.log([this.position, this.orientation]);
36495 this.split = new Roo.bootstrap.SplitBar({
36496 dragElement : splitEl,
36497 resizingElement: this.el,
36498 orientation : this.orientation
36501 this.split.on("moved", this.onSplitMove, this);
36502 this.split.useShim = this.config.useShim === true;
36503 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36504 if(this.useSplitTips){
36505 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36507 //if(config.collapsible){
36508 // this.split.el.on("dblclick", this.collapse, this);
36511 if(typeof this.config.minSize != "undefined"){
36512 this.split.minSize = this.config.minSize;
36514 if(typeof this.config.maxSize != "undefined"){
36515 this.split.maxSize = this.config.maxSize;
36517 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36518 this.hideSplitter();
36523 getHMaxSize : function(){
36524 var cmax = this.config.maxSize || 10000;
36525 var center = this.mgr.getRegion("center");
36526 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36529 getVMaxSize : function(){
36530 var cmax = this.config.maxSize || 10000;
36531 var center = this.mgr.getRegion("center");
36532 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36535 onSplitMove : function(split, newSize){
36536 this.fireEvent("resized", this, newSize);
36540 * Returns the {@link Roo.SplitBar} for this region.
36541 * @return {Roo.SplitBar}
36543 getSplitBar : function(){
36548 this.hideSplitter();
36549 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36552 hideSplitter : function(){
36554 this.split.el.setLocation(-2000,-2000);
36555 this.split.el.hide();
36561 this.split.el.show();
36563 Roo.bootstrap.layout.Split.superclass.show.call(this);
36566 beforeSlide: function(){
36567 if(Roo.isGecko){// firefox overflow auto bug workaround
36568 this.bodyEl.clip();
36570 this.tabs.bodyEl.clip();
36572 if(this.activePanel){
36573 this.activePanel.getEl().clip();
36575 if(this.activePanel.beforeSlide){
36576 this.activePanel.beforeSlide();
36582 afterSlide : function(){
36583 if(Roo.isGecko){// firefox overflow auto bug workaround
36584 this.bodyEl.unclip();
36586 this.tabs.bodyEl.unclip();
36588 if(this.activePanel){
36589 this.activePanel.getEl().unclip();
36590 if(this.activePanel.afterSlide){
36591 this.activePanel.afterSlide();
36597 initAutoHide : function(){
36598 if(this.autoHide !== false){
36599 if(!this.autoHideHd){
36600 var st = new Roo.util.DelayedTask(this.slideIn, this);
36601 this.autoHideHd = {
36602 "mouseout": function(e){
36603 if(!e.within(this.el, true)){
36607 "mouseover" : function(e){
36613 this.el.on(this.autoHideHd);
36617 clearAutoHide : function(){
36618 if(this.autoHide !== false){
36619 this.el.un("mouseout", this.autoHideHd.mouseout);
36620 this.el.un("mouseover", this.autoHideHd.mouseover);
36624 clearMonitor : function(){
36625 Roo.get(document).un("click", this.slideInIf, this);
36628 // these names are backwards but not changed for compat
36629 slideOut : function(){
36630 if(this.isSlid || this.el.hasActiveFx()){
36633 this.isSlid = true;
36634 if(this.collapseBtn){
36635 this.collapseBtn.hide();
36637 this.closeBtnState = this.closeBtn.getStyle('display');
36638 this.closeBtn.hide();
36640 this.stickBtn.show();
36643 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36644 this.beforeSlide();
36645 this.el.setStyle("z-index", 10001);
36646 this.el.slideIn(this.getSlideAnchor(), {
36647 callback: function(){
36649 this.initAutoHide();
36650 Roo.get(document).on("click", this.slideInIf, this);
36651 this.fireEvent("slideshow", this);
36658 afterSlideIn : function(){
36659 this.clearAutoHide();
36660 this.isSlid = false;
36661 this.clearMonitor();
36662 this.el.setStyle("z-index", "");
36663 if(this.collapseBtn){
36664 this.collapseBtn.show();
36666 this.closeBtn.setStyle('display', this.closeBtnState);
36668 this.stickBtn.hide();
36670 this.fireEvent("slidehide", this);
36673 slideIn : function(cb){
36674 if(!this.isSlid || this.el.hasActiveFx()){
36678 this.isSlid = false;
36679 this.beforeSlide();
36680 this.el.slideOut(this.getSlideAnchor(), {
36681 callback: function(){
36682 this.el.setLeftTop(-10000, -10000);
36684 this.afterSlideIn();
36692 slideInIf : function(e){
36693 if(!e.within(this.el)){
36698 animateCollapse : function(){
36699 this.beforeSlide();
36700 this.el.setStyle("z-index", 20000);
36701 var anchor = this.getSlideAnchor();
36702 this.el.slideOut(anchor, {
36703 callback : function(){
36704 this.el.setStyle("z-index", "");
36705 this.collapsedEl.slideIn(anchor, {duration:.3});
36707 this.el.setLocation(-10000,-10000);
36709 this.fireEvent("collapsed", this);
36716 animateExpand : function(){
36717 this.beforeSlide();
36718 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36719 this.el.setStyle("z-index", 20000);
36720 this.collapsedEl.hide({
36723 this.el.slideIn(this.getSlideAnchor(), {
36724 callback : function(){
36725 this.el.setStyle("z-index", "");
36728 this.split.el.show();
36730 this.fireEvent("invalidated", this);
36731 this.fireEvent("expanded", this);
36759 getAnchor : function(){
36760 return this.anchors[this.position];
36763 getCollapseAnchor : function(){
36764 return this.canchors[this.position];
36767 getSlideAnchor : function(){
36768 return this.sanchors[this.position];
36771 getAlignAdj : function(){
36772 var cm = this.cmargins;
36773 switch(this.position){
36789 getExpandAdj : function(){
36790 var c = this.collapsedEl, cm = this.cmargins;
36791 switch(this.position){
36793 return [-(cm.right+c.getWidth()+cm.left), 0];
36796 return [cm.right+c.getWidth()+cm.left, 0];
36799 return [0, -(cm.top+cm.bottom+c.getHeight())];
36802 return [0, cm.top+cm.bottom+c.getHeight()];
36808 * Ext JS Library 1.1.1
36809 * Copyright(c) 2006-2007, Ext JS, LLC.
36811 * Originally Released Under LGPL - original licence link has changed is not relivant.
36814 * <script type="text/javascript">
36817 * These classes are private internal classes
36819 Roo.bootstrap.layout.Center = function(config){
36820 config.region = "center";
36821 Roo.bootstrap.layout.Region.call(this, config);
36822 this.visible = true;
36823 this.minWidth = config.minWidth || 20;
36824 this.minHeight = config.minHeight || 20;
36827 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36829 // center panel can't be hidden
36833 // center panel can't be hidden
36836 getMinWidth: function(){
36837 return this.minWidth;
36840 getMinHeight: function(){
36841 return this.minHeight;
36854 Roo.bootstrap.layout.North = function(config)
36856 config.region = 'north';
36857 config.cursor = 'n-resize';
36859 Roo.bootstrap.layout.Split.call(this, config);
36863 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36864 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36865 this.split.el.addClass("roo-layout-split-v");
36867 var size = config.initialSize || config.height;
36868 if(typeof size != "undefined"){
36869 this.el.setHeight(size);
36872 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36874 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36878 getBox : function(){
36879 if(this.collapsed){
36880 return this.collapsedEl.getBox();
36882 var box = this.el.getBox();
36884 box.height += this.split.el.getHeight();
36889 updateBox : function(box){
36890 if(this.split && !this.collapsed){
36891 box.height -= this.split.el.getHeight();
36892 this.split.el.setLeft(box.x);
36893 this.split.el.setTop(box.y+box.height);
36894 this.split.el.setWidth(box.width);
36896 if(this.collapsed){
36897 this.updateBody(box.width, null);
36899 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36907 Roo.bootstrap.layout.South = function(config){
36908 config.region = 'south';
36909 config.cursor = 's-resize';
36910 Roo.bootstrap.layout.Split.call(this, config);
36912 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36913 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36914 this.split.el.addClass("roo-layout-split-v");
36916 var size = config.initialSize || config.height;
36917 if(typeof size != "undefined"){
36918 this.el.setHeight(size);
36922 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36923 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36924 getBox : function(){
36925 if(this.collapsed){
36926 return this.collapsedEl.getBox();
36928 var box = this.el.getBox();
36930 var sh = this.split.el.getHeight();
36937 updateBox : function(box){
36938 if(this.split && !this.collapsed){
36939 var sh = this.split.el.getHeight();
36942 this.split.el.setLeft(box.x);
36943 this.split.el.setTop(box.y-sh);
36944 this.split.el.setWidth(box.width);
36946 if(this.collapsed){
36947 this.updateBody(box.width, null);
36949 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36953 Roo.bootstrap.layout.East = function(config){
36954 config.region = "east";
36955 config.cursor = "e-resize";
36956 Roo.bootstrap.layout.Split.call(this, config);
36958 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36959 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36960 this.split.el.addClass("roo-layout-split-h");
36962 var size = config.initialSize || config.width;
36963 if(typeof size != "undefined"){
36964 this.el.setWidth(size);
36967 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36968 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36969 getBox : function(){
36970 if(this.collapsed){
36971 return this.collapsedEl.getBox();
36973 var box = this.el.getBox();
36975 var sw = this.split.el.getWidth();
36982 updateBox : function(box){
36983 if(this.split && !this.collapsed){
36984 var sw = this.split.el.getWidth();
36986 this.split.el.setLeft(box.x);
36987 this.split.el.setTop(box.y);
36988 this.split.el.setHeight(box.height);
36991 if(this.collapsed){
36992 this.updateBody(null, box.height);
36994 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36998 Roo.bootstrap.layout.West = function(config){
36999 config.region = "west";
37000 config.cursor = "w-resize";
37002 Roo.bootstrap.layout.Split.call(this, config);
37004 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37005 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37006 this.split.el.addClass("roo-layout-split-h");
37010 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37011 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37013 onRender: function(ctr, pos)
37015 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37016 var size = this.config.initialSize || this.config.width;
37017 if(typeof size != "undefined"){
37018 this.el.setWidth(size);
37022 getBox : function(){
37023 if(this.collapsed){
37024 return this.collapsedEl.getBox();
37026 var box = this.el.getBox();
37028 box.width += this.split.el.getWidth();
37033 updateBox : function(box){
37034 if(this.split && !this.collapsed){
37035 var sw = this.split.el.getWidth();
37037 this.split.el.setLeft(box.x+box.width);
37038 this.split.el.setTop(box.y);
37039 this.split.el.setHeight(box.height);
37041 if(this.collapsed){
37042 this.updateBody(null, box.height);
37044 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37047 Roo.namespace("Roo.bootstrap.panel");/*
37049 * Ext JS Library 1.1.1
37050 * Copyright(c) 2006-2007, Ext JS, LLC.
37052 * Originally Released Under LGPL - original licence link has changed is not relivant.
37055 * <script type="text/javascript">
37058 * @class Roo.ContentPanel
37059 * @extends Roo.util.Observable
37060 * A basic ContentPanel element.
37061 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
37062 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
37063 * @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
37064 * @cfg {Boolean} closable True if the panel can be closed/removed
37065 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
37066 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37067 * @cfg {Toolbar} toolbar A toolbar for this panel
37068 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37069 * @cfg {String} title The title for this panel
37070 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37071 * @cfg {String} url Calls {@link #setUrl} with this value
37072 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37073 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37074 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37075 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37076 * @cfg {Boolean} badges render the badges
37079 * Create a new ContentPanel.
37080 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37081 * @param {String/Object} config A string to set only the title or a config object
37082 * @param {String} content (optional) Set the HTML content for this panel
37083 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37085 Roo.bootstrap.panel.Content = function( config){
37087 this.tpl = config.tpl || false;
37089 var el = config.el;
37090 var content = config.content;
37092 if(config.autoCreate){ // xtype is available if this is called from factory
37095 this.el = Roo.get(el);
37096 if(!this.el && config && config.autoCreate){
37097 if(typeof config.autoCreate == "object"){
37098 if(!config.autoCreate.id){
37099 config.autoCreate.id = config.id||el;
37101 this.el = Roo.DomHelper.append(document.body,
37102 config.autoCreate, true);
37104 var elcfg = { tag: "div",
37105 cls: "roo-layout-inactive-content",
37109 elcfg.html = config.html;
37113 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37116 this.closable = false;
37117 this.loaded = false;
37118 this.active = false;
37121 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37123 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37125 this.wrapEl = this.el; //this.el.wrap();
37127 if (config.toolbar.items) {
37128 ti = config.toolbar.items ;
37129 delete config.toolbar.items ;
37133 this.toolbar.render(this.wrapEl, 'before');
37134 for(var i =0;i < ti.length;i++) {
37135 // Roo.log(['add child', items[i]]);
37136 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37138 this.toolbar.items = nitems;
37139 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37140 delete config.toolbar;
37144 // xtype created footer. - not sure if will work as we normally have to render first..
37145 if (this.footer && !this.footer.el && this.footer.xtype) {
37146 if (!this.wrapEl) {
37147 this.wrapEl = this.el.wrap();
37150 this.footer.container = this.wrapEl.createChild();
37152 this.footer = Roo.factory(this.footer, Roo);
37157 if(typeof config == "string"){
37158 this.title = config;
37160 Roo.apply(this, config);
37164 this.resizeEl = Roo.get(this.resizeEl, true);
37166 this.resizeEl = this.el;
37168 // handle view.xtype
37176 * Fires when this panel is activated.
37177 * @param {Roo.ContentPanel} this
37181 * @event deactivate
37182 * Fires when this panel is activated.
37183 * @param {Roo.ContentPanel} this
37185 "deactivate" : true,
37189 * Fires when this panel is resized if fitToFrame is true.
37190 * @param {Roo.ContentPanel} this
37191 * @param {Number} width The width after any component adjustments
37192 * @param {Number} height The height after any component adjustments
37198 * Fires when this tab is created
37199 * @param {Roo.ContentPanel} this
37210 if(this.autoScroll){
37211 this.resizeEl.setStyle("overflow", "auto");
37213 // fix randome scrolling
37214 //this.el.on('scroll', function() {
37215 // Roo.log('fix random scolling');
37216 // this.scrollTo('top',0);
37219 content = content || this.content;
37221 this.setContent(content);
37223 if(config && config.url){
37224 this.setUrl(this.url, this.params, this.loadOnce);
37229 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37231 if (this.view && typeof(this.view.xtype) != 'undefined') {
37232 this.view.el = this.el.appendChild(document.createElement("div"));
37233 this.view = Roo.factory(this.view);
37234 this.view.render && this.view.render(false, '');
37238 this.fireEvent('render', this);
37241 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37245 setRegion : function(region){
37246 this.region = region;
37247 this.setActiveClass(region && !this.background);
37251 setActiveClass: function(state)
37254 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37255 this.el.setStyle('position','relative');
37257 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37258 this.el.setStyle('position', 'absolute');
37263 * Returns the toolbar for this Panel if one was configured.
37264 * @return {Roo.Toolbar}
37266 getToolbar : function(){
37267 return this.toolbar;
37270 setActiveState : function(active)
37272 this.active = active;
37273 this.setActiveClass(active);
37275 if(this.fireEvent("deactivate", this) === false){
37280 this.fireEvent("activate", this);
37284 * Updates this panel's element
37285 * @param {String} content The new content
37286 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37288 setContent : function(content, loadScripts){
37289 this.el.update(content, loadScripts);
37292 ignoreResize : function(w, h){
37293 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37296 this.lastSize = {width: w, height: h};
37301 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37302 * @return {Roo.UpdateManager} The UpdateManager
37304 getUpdateManager : function(){
37305 return this.el.getUpdateManager();
37308 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37309 * @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:
37312 url: "your-url.php",
37313 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37314 callback: yourFunction,
37315 scope: yourObject, //(optional scope)
37318 text: "Loading...",
37323 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37324 * 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.
37325 * @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}
37326 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37327 * @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.
37328 * @return {Roo.ContentPanel} this
37331 var um = this.el.getUpdateManager();
37332 um.update.apply(um, arguments);
37338 * 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.
37339 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37340 * @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)
37341 * @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)
37342 * @return {Roo.UpdateManager} The UpdateManager
37344 setUrl : function(url, params, loadOnce){
37345 if(this.refreshDelegate){
37346 this.removeListener("activate", this.refreshDelegate);
37348 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37349 this.on("activate", this.refreshDelegate);
37350 return this.el.getUpdateManager();
37353 _handleRefresh : function(url, params, loadOnce){
37354 if(!loadOnce || !this.loaded){
37355 var updater = this.el.getUpdateManager();
37356 updater.update(url, params, this._setLoaded.createDelegate(this));
37360 _setLoaded : function(){
37361 this.loaded = true;
37365 * Returns this panel's id
37368 getId : function(){
37373 * Returns this panel's element - used by regiosn to add.
37374 * @return {Roo.Element}
37376 getEl : function(){
37377 return this.wrapEl || this.el;
37382 adjustForComponents : function(width, height)
37384 //Roo.log('adjustForComponents ');
37385 if(this.resizeEl != this.el){
37386 width -= this.el.getFrameWidth('lr');
37387 height -= this.el.getFrameWidth('tb');
37390 var te = this.toolbar.getEl();
37391 te.setWidth(width);
37392 height -= te.getHeight();
37395 var te = this.footer.getEl();
37396 te.setWidth(width);
37397 height -= te.getHeight();
37401 if(this.adjustments){
37402 width += this.adjustments[0];
37403 height += this.adjustments[1];
37405 return {"width": width, "height": height};
37408 setSize : function(width, height){
37409 if(this.fitToFrame && !this.ignoreResize(width, height)){
37410 if(this.fitContainer && this.resizeEl != this.el){
37411 this.el.setSize(width, height);
37413 var size = this.adjustForComponents(width, height);
37414 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37415 this.fireEvent('resize', this, size.width, size.height);
37420 * Returns this panel's title
37423 getTitle : function(){
37425 if (typeof(this.title) != 'object') {
37430 for (var k in this.title) {
37431 if (!this.title.hasOwnProperty(k)) {
37435 if (k.indexOf('-') >= 0) {
37436 var s = k.split('-');
37437 for (var i = 0; i<s.length; i++) {
37438 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37441 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37448 * Set this panel's title
37449 * @param {String} title
37451 setTitle : function(title){
37452 this.title = title;
37454 this.region.updatePanelTitle(this, title);
37459 * Returns true is this panel was configured to be closable
37460 * @return {Boolean}
37462 isClosable : function(){
37463 return this.closable;
37466 beforeSlide : function(){
37468 this.resizeEl.clip();
37471 afterSlide : function(){
37473 this.resizeEl.unclip();
37477 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37478 * Will fail silently if the {@link #setUrl} method has not been called.
37479 * This does not activate the panel, just updates its content.
37481 refresh : function(){
37482 if(this.refreshDelegate){
37483 this.loaded = false;
37484 this.refreshDelegate();
37489 * Destroys this panel
37491 destroy : function(){
37492 this.el.removeAllListeners();
37493 var tempEl = document.createElement("span");
37494 tempEl.appendChild(this.el.dom);
37495 tempEl.innerHTML = "";
37501 * form - if the content panel contains a form - this is a reference to it.
37502 * @type {Roo.form.Form}
37506 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37507 * This contains a reference to it.
37513 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37523 * @param {Object} cfg Xtype definition of item to add.
37527 getChildContainer: function () {
37528 return this.getEl();
37533 var ret = new Roo.factory(cfg);
37538 if (cfg.xtype.match(/^Form$/)) {
37541 //if (this.footer) {
37542 // el = this.footer.container.insertSibling(false, 'before');
37544 el = this.el.createChild();
37547 this.form = new Roo.form.Form(cfg);
37550 if ( this.form.allItems.length) {
37551 this.form.render(el.dom);
37555 // should only have one of theses..
37556 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37557 // views.. should not be just added - used named prop 'view''
37559 cfg.el = this.el.appendChild(document.createElement("div"));
37562 var ret = new Roo.factory(cfg);
37564 ret.render && ret.render(false, ''); // render blank..
37574 * @class Roo.bootstrap.panel.Grid
37575 * @extends Roo.bootstrap.panel.Content
37577 * Create a new GridPanel.
37578 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37579 * @param {Object} config A the config object
37585 Roo.bootstrap.panel.Grid = function(config)
37589 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37590 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37592 config.el = this.wrapper;
37593 //this.el = this.wrapper;
37595 if (config.container) {
37596 // ctor'ed from a Border/panel.grid
37599 this.wrapper.setStyle("overflow", "hidden");
37600 this.wrapper.addClass('roo-grid-container');
37605 if(config.toolbar){
37606 var tool_el = this.wrapper.createChild();
37607 this.toolbar = Roo.factory(config.toolbar);
37609 if (config.toolbar.items) {
37610 ti = config.toolbar.items ;
37611 delete config.toolbar.items ;
37615 this.toolbar.render(tool_el);
37616 for(var i =0;i < ti.length;i++) {
37617 // Roo.log(['add child', items[i]]);
37618 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37620 this.toolbar.items = nitems;
37622 delete config.toolbar;
37625 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37626 config.grid.scrollBody = true;;
37627 config.grid.monitorWindowResize = false; // turn off autosizing
37628 config.grid.autoHeight = false;
37629 config.grid.autoWidth = false;
37631 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37633 if (config.background) {
37634 // render grid on panel activation (if panel background)
37635 this.on('activate', function(gp) {
37636 if (!gp.grid.rendered) {
37637 gp.grid.render(this.wrapper);
37638 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37643 this.grid.render(this.wrapper);
37644 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37647 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37648 // ??? needed ??? config.el = this.wrapper;
37653 // xtype created footer. - not sure if will work as we normally have to render first..
37654 if (this.footer && !this.footer.el && this.footer.xtype) {
37656 var ctr = this.grid.getView().getFooterPanel(true);
37657 this.footer.dataSource = this.grid.dataSource;
37658 this.footer = Roo.factory(this.footer, Roo);
37659 this.footer.render(ctr);
37669 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37670 getId : function(){
37671 return this.grid.id;
37675 * Returns the grid for this panel
37676 * @return {Roo.bootstrap.Table}
37678 getGrid : function(){
37682 setSize : function(width, height){
37683 if(!this.ignoreResize(width, height)){
37684 var grid = this.grid;
37685 var size = this.adjustForComponents(width, height);
37686 var gridel = grid.getGridEl();
37687 gridel.setSize(size.width, size.height);
37689 var thd = grid.getGridEl().select('thead',true).first();
37690 var tbd = grid.getGridEl().select('tbody', true).first();
37692 tbd.setSize(width, height - thd.getHeight());
37701 beforeSlide : function(){
37702 this.grid.getView().scroller.clip();
37705 afterSlide : function(){
37706 this.grid.getView().scroller.unclip();
37709 destroy : function(){
37710 this.grid.destroy();
37712 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37717 * @class Roo.bootstrap.panel.Nest
37718 * @extends Roo.bootstrap.panel.Content
37720 * Create a new Panel, that can contain a layout.Border.
37723 * @param {Roo.BorderLayout} layout The layout for this panel
37724 * @param {String/Object} config A string to set only the title or a config object
37726 Roo.bootstrap.panel.Nest = function(config)
37728 // construct with only one argument..
37729 /* FIXME - implement nicer consturctors
37730 if (layout.layout) {
37732 layout = config.layout;
37733 delete config.layout;
37735 if (layout.xtype && !layout.getEl) {
37736 // then layout needs constructing..
37737 layout = Roo.factory(layout, Roo);
37741 config.el = config.layout.getEl();
37743 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37745 config.layout.monitorWindowResize = false; // turn off autosizing
37746 this.layout = config.layout;
37747 this.layout.getEl().addClass("roo-layout-nested-layout");
37754 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37756 setSize : function(width, height){
37757 if(!this.ignoreResize(width, height)){
37758 var size = this.adjustForComponents(width, height);
37759 var el = this.layout.getEl();
37760 if (size.height < 1) {
37761 el.setWidth(size.width);
37763 el.setSize(size.width, size.height);
37765 var touch = el.dom.offsetWidth;
37766 this.layout.layout();
37767 // ie requires a double layout on the first pass
37768 if(Roo.isIE && !this.initialized){
37769 this.initialized = true;
37770 this.layout.layout();
37775 // activate all subpanels if not currently active..
37777 setActiveState : function(active){
37778 this.active = active;
37779 this.setActiveClass(active);
37782 this.fireEvent("deactivate", this);
37786 this.fireEvent("activate", this);
37787 // not sure if this should happen before or after..
37788 if (!this.layout) {
37789 return; // should not happen..
37792 for (var r in this.layout.regions) {
37793 reg = this.layout.getRegion(r);
37794 if (reg.getActivePanel()) {
37795 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37796 reg.setActivePanel(reg.getActivePanel());
37799 if (!reg.panels.length) {
37802 reg.showPanel(reg.getPanel(0));
37811 * Returns the nested BorderLayout for this panel
37812 * @return {Roo.BorderLayout}
37814 getLayout : function(){
37815 return this.layout;
37819 * Adds a xtype elements to the layout of the nested panel
37823 xtype : 'ContentPanel',
37830 xtype : 'NestedLayoutPanel',
37836 items : [ ... list of content panels or nested layout panels.. ]
37840 * @param {Object} cfg Xtype definition of item to add.
37842 addxtype : function(cfg) {
37843 return this.layout.addxtype(cfg);
37848 * Ext JS Library 1.1.1
37849 * Copyright(c) 2006-2007, Ext JS, LLC.
37851 * Originally Released Under LGPL - original licence link has changed is not relivant.
37854 * <script type="text/javascript">
37857 * @class Roo.TabPanel
37858 * @extends Roo.util.Observable
37859 * A lightweight tab container.
37863 // basic tabs 1, built from existing content
37864 var tabs = new Roo.TabPanel("tabs1");
37865 tabs.addTab("script", "View Script");
37866 tabs.addTab("markup", "View Markup");
37867 tabs.activate("script");
37869 // more advanced tabs, built from javascript
37870 var jtabs = new Roo.TabPanel("jtabs");
37871 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37873 // set up the UpdateManager
37874 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37875 var updater = tab2.getUpdateManager();
37876 updater.setDefaultUrl("ajax1.htm");
37877 tab2.on('activate', updater.refresh, updater, true);
37879 // Use setUrl for Ajax loading
37880 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37881 tab3.setUrl("ajax2.htm", null, true);
37884 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37887 jtabs.activate("jtabs-1");
37890 * Create a new TabPanel.
37891 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37892 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37894 Roo.bootstrap.panel.Tabs = function(config){
37896 * The container element for this TabPanel.
37897 * @type Roo.Element
37899 this.el = Roo.get(config.el);
37902 if(typeof config == "boolean"){
37903 this.tabPosition = config ? "bottom" : "top";
37905 Roo.apply(this, config);
37909 if(this.tabPosition == "bottom"){
37910 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37911 this.el.addClass("roo-tabs-bottom");
37913 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37914 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37915 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37917 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37919 if(this.tabPosition != "bottom"){
37920 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37921 * @type Roo.Element
37923 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37924 this.el.addClass("roo-tabs-top");
37928 this.bodyEl.setStyle("position", "relative");
37930 this.active = null;
37931 this.activateDelegate = this.activate.createDelegate(this);
37936 * Fires when the active tab changes
37937 * @param {Roo.TabPanel} this
37938 * @param {Roo.TabPanelItem} activePanel The new active tab
37942 * @event beforetabchange
37943 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37944 * @param {Roo.TabPanel} this
37945 * @param {Object} e Set cancel to true on this object to cancel the tab change
37946 * @param {Roo.TabPanelItem} tab The tab being changed to
37948 "beforetabchange" : true
37951 Roo.EventManager.onWindowResize(this.onResize, this);
37952 this.cpad = this.el.getPadding("lr");
37953 this.hiddenCount = 0;
37956 // toolbar on the tabbar support...
37957 if (this.toolbar) {
37958 alert("no toolbar support yet");
37959 this.toolbar = false;
37961 var tcfg = this.toolbar;
37962 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37963 this.toolbar = new Roo.Toolbar(tcfg);
37964 if (Roo.isSafari) {
37965 var tbl = tcfg.container.child('table', true);
37966 tbl.setAttribute('width', '100%');
37974 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37977 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37979 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37981 tabPosition : "top",
37983 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37985 currentTabWidth : 0,
37987 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37991 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37995 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37997 preferredTabWidth : 175,
37999 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38001 resizeTabs : false,
38003 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38005 monitorResize : true,
38007 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
38012 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38013 * @param {String} id The id of the div to use <b>or create</b>
38014 * @param {String} text The text for the tab
38015 * @param {String} content (optional) Content to put in the TabPanelItem body
38016 * @param {Boolean} closable (optional) True to create a close icon on the tab
38017 * @return {Roo.TabPanelItem} The created TabPanelItem
38019 addTab : function(id, text, content, closable, tpl)
38021 var item = new Roo.bootstrap.panel.TabItem({
38025 closable : closable,
38028 this.addTabItem(item);
38030 item.setContent(content);
38036 * Returns the {@link Roo.TabPanelItem} with the specified id/index
38037 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38038 * @return {Roo.TabPanelItem}
38040 getTab : function(id){
38041 return this.items[id];
38045 * Hides the {@link Roo.TabPanelItem} with the specified id/index
38046 * @param {String/Number} id The id or index of the TabPanelItem to hide.
38048 hideTab : function(id){
38049 var t = this.items[id];
38052 this.hiddenCount++;
38053 this.autoSizeTabs();
38058 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38059 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38061 unhideTab : function(id){
38062 var t = this.items[id];
38064 t.setHidden(false);
38065 this.hiddenCount--;
38066 this.autoSizeTabs();
38071 * Adds an existing {@link Roo.TabPanelItem}.
38072 * @param {Roo.TabPanelItem} item The TabPanelItem to add
38074 addTabItem : function(item){
38075 this.items[item.id] = item;
38076 this.items.push(item);
38077 // if(this.resizeTabs){
38078 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38079 // this.autoSizeTabs();
38081 // item.autoSize();
38086 * Removes a {@link Roo.TabPanelItem}.
38087 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38089 removeTab : function(id){
38090 var items = this.items;
38091 var tab = items[id];
38092 if(!tab) { return; }
38093 var index = items.indexOf(tab);
38094 if(this.active == tab && items.length > 1){
38095 var newTab = this.getNextAvailable(index);
38100 this.stripEl.dom.removeChild(tab.pnode.dom);
38101 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38102 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38104 items.splice(index, 1);
38105 delete this.items[tab.id];
38106 tab.fireEvent("close", tab);
38107 tab.purgeListeners();
38108 this.autoSizeTabs();
38111 getNextAvailable : function(start){
38112 var items = this.items;
38114 // look for a next tab that will slide over to
38115 // replace the one being removed
38116 while(index < items.length){
38117 var item = items[++index];
38118 if(item && !item.isHidden()){
38122 // if one isn't found select the previous tab (on the left)
38125 var item = items[--index];
38126 if(item && !item.isHidden()){
38134 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38135 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38137 disableTab : function(id){
38138 var tab = this.items[id];
38139 if(tab && this.active != tab){
38145 * Enables a {@link Roo.TabPanelItem} that is disabled.
38146 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38148 enableTab : function(id){
38149 var tab = this.items[id];
38154 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38155 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38156 * @return {Roo.TabPanelItem} The TabPanelItem.
38158 activate : function(id){
38159 var tab = this.items[id];
38163 if(tab == this.active || tab.disabled){
38167 this.fireEvent("beforetabchange", this, e, tab);
38168 if(e.cancel !== true && !tab.disabled){
38170 this.active.hide();
38172 this.active = this.items[id];
38173 this.active.show();
38174 this.fireEvent("tabchange", this, this.active);
38180 * Gets the active {@link Roo.TabPanelItem}.
38181 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38183 getActiveTab : function(){
38184 return this.active;
38188 * Updates the tab body element to fit the height of the container element
38189 * for overflow scrolling
38190 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38192 syncHeight : function(targetHeight){
38193 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38194 var bm = this.bodyEl.getMargins();
38195 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38196 this.bodyEl.setHeight(newHeight);
38200 onResize : function(){
38201 if(this.monitorResize){
38202 this.autoSizeTabs();
38207 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38209 beginUpdate : function(){
38210 this.updating = true;
38214 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38216 endUpdate : function(){
38217 this.updating = false;
38218 this.autoSizeTabs();
38222 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38224 autoSizeTabs : function(){
38225 var count = this.items.length;
38226 var vcount = count - this.hiddenCount;
38227 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38230 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38231 var availWidth = Math.floor(w / vcount);
38232 var b = this.stripBody;
38233 if(b.getWidth() > w){
38234 var tabs = this.items;
38235 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38236 if(availWidth < this.minTabWidth){
38237 /*if(!this.sleft){ // incomplete scrolling code
38238 this.createScrollButtons();
38241 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38244 if(this.currentTabWidth < this.preferredTabWidth){
38245 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38251 * Returns the number of tabs in this TabPanel.
38254 getCount : function(){
38255 return this.items.length;
38259 * Resizes all the tabs to the passed width
38260 * @param {Number} The new width
38262 setTabWidth : function(width){
38263 this.currentTabWidth = width;
38264 for(var i = 0, len = this.items.length; i < len; i++) {
38265 if(!this.items[i].isHidden()) {
38266 this.items[i].setWidth(width);
38272 * Destroys this TabPanel
38273 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38275 destroy : function(removeEl){
38276 Roo.EventManager.removeResizeListener(this.onResize, this);
38277 for(var i = 0, len = this.items.length; i < len; i++){
38278 this.items[i].purgeListeners();
38280 if(removeEl === true){
38281 this.el.update("");
38286 createStrip : function(container)
38288 var strip = document.createElement("nav");
38289 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38290 container.appendChild(strip);
38294 createStripList : function(strip)
38296 // div wrapper for retard IE
38297 // returns the "tr" element.
38298 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38299 //'<div class="x-tabs-strip-wrap">'+
38300 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38301 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38302 return strip.firstChild; //.firstChild.firstChild.firstChild;
38304 createBody : function(container)
38306 var body = document.createElement("div");
38307 Roo.id(body, "tab-body");
38308 //Roo.fly(body).addClass("x-tabs-body");
38309 Roo.fly(body).addClass("tab-content");
38310 container.appendChild(body);
38313 createItemBody :function(bodyEl, id){
38314 var body = Roo.getDom(id);
38316 body = document.createElement("div");
38319 //Roo.fly(body).addClass("x-tabs-item-body");
38320 Roo.fly(body).addClass("tab-pane");
38321 bodyEl.insertBefore(body, bodyEl.firstChild);
38325 createStripElements : function(stripEl, text, closable, tpl)
38327 var td = document.createElement("li"); // was td..
38330 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38333 stripEl.appendChild(td);
38335 td.className = "x-tabs-closable";
38336 if(!this.closeTpl){
38337 this.closeTpl = new Roo.Template(
38338 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38339 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38340 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38343 var el = this.closeTpl.overwrite(td, {"text": text});
38344 var close = el.getElementsByTagName("div")[0];
38345 var inner = el.getElementsByTagName("em")[0];
38346 return {"el": el, "close": close, "inner": inner};
38349 // not sure what this is..
38350 // if(!this.tabTpl){
38351 //this.tabTpl = new Roo.Template(
38352 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38353 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38355 // this.tabTpl = new Roo.Template(
38356 // '<a href="#">' +
38357 // '<span unselectable="on"' +
38358 // (this.disableTooltips ? '' : ' title="{text}"') +
38359 // ' >{text}</span></a>'
38365 var template = tpl || this.tabTpl || false;
38369 template = new Roo.Template(
38371 '<span unselectable="on"' +
38372 (this.disableTooltips ? '' : ' title="{text}"') +
38373 ' >{text}</span></a>'
38377 switch (typeof(template)) {
38381 template = new Roo.Template(template);
38387 var el = template.overwrite(td, {"text": text});
38389 var inner = el.getElementsByTagName("span")[0];
38391 return {"el": el, "inner": inner};
38399 * @class Roo.TabPanelItem
38400 * @extends Roo.util.Observable
38401 * Represents an individual item (tab plus body) in a TabPanel.
38402 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38403 * @param {String} id The id of this TabPanelItem
38404 * @param {String} text The text for the tab of this TabPanelItem
38405 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38407 Roo.bootstrap.panel.TabItem = function(config){
38409 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38410 * @type Roo.TabPanel
38412 this.tabPanel = config.panel;
38414 * The id for this TabPanelItem
38417 this.id = config.id;
38419 this.disabled = false;
38421 this.text = config.text;
38423 this.loaded = false;
38424 this.closable = config.closable;
38427 * The body element for this TabPanelItem.
38428 * @type Roo.Element
38430 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38431 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38432 this.bodyEl.setStyle("display", "block");
38433 this.bodyEl.setStyle("zoom", "1");
38434 //this.hideAction();
38436 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38438 this.el = Roo.get(els.el);
38439 this.inner = Roo.get(els.inner, true);
38440 this.textEl = Roo.get(this.el.dom.firstChild, true);
38441 this.pnode = Roo.get(els.el.parentNode, true);
38442 // this.el.on("mousedown", this.onTabMouseDown, this);
38443 this.el.on("click", this.onTabClick, this);
38445 if(config.closable){
38446 var c = Roo.get(els.close, true);
38447 c.dom.title = this.closeText;
38448 c.addClassOnOver("close-over");
38449 c.on("click", this.closeClick, this);
38455 * Fires when this tab becomes the active tab.
38456 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38457 * @param {Roo.TabPanelItem} this
38461 * @event beforeclose
38462 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38463 * @param {Roo.TabPanelItem} this
38464 * @param {Object} e Set cancel to true on this object to cancel the close.
38466 "beforeclose": true,
38469 * Fires when this tab is closed.
38470 * @param {Roo.TabPanelItem} this
38474 * @event deactivate
38475 * Fires when this tab is no longer the active tab.
38476 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38477 * @param {Roo.TabPanelItem} this
38479 "deactivate" : true
38481 this.hidden = false;
38483 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38486 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38488 purgeListeners : function(){
38489 Roo.util.Observable.prototype.purgeListeners.call(this);
38490 this.el.removeAllListeners();
38493 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38496 this.pnode.addClass("active");
38499 this.tabPanel.stripWrap.repaint();
38501 this.fireEvent("activate", this.tabPanel, this);
38505 * Returns true if this tab is the active tab.
38506 * @return {Boolean}
38508 isActive : function(){
38509 return this.tabPanel.getActiveTab() == this;
38513 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38516 this.pnode.removeClass("active");
38518 this.fireEvent("deactivate", this.tabPanel, this);
38521 hideAction : function(){
38522 this.bodyEl.hide();
38523 this.bodyEl.setStyle("position", "absolute");
38524 this.bodyEl.setLeft("-20000px");
38525 this.bodyEl.setTop("-20000px");
38528 showAction : function(){
38529 this.bodyEl.setStyle("position", "relative");
38530 this.bodyEl.setTop("");
38531 this.bodyEl.setLeft("");
38532 this.bodyEl.show();
38536 * Set the tooltip for the tab.
38537 * @param {String} tooltip The tab's tooltip
38539 setTooltip : function(text){
38540 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38541 this.textEl.dom.qtip = text;
38542 this.textEl.dom.removeAttribute('title');
38544 this.textEl.dom.title = text;
38548 onTabClick : function(e){
38549 e.preventDefault();
38550 this.tabPanel.activate(this.id);
38553 onTabMouseDown : function(e){
38554 e.preventDefault();
38555 this.tabPanel.activate(this.id);
38558 getWidth : function(){
38559 return this.inner.getWidth();
38562 setWidth : function(width){
38563 var iwidth = width - this.pnode.getPadding("lr");
38564 this.inner.setWidth(iwidth);
38565 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38566 this.pnode.setWidth(width);
38570 * Show or hide the tab
38571 * @param {Boolean} hidden True to hide or false to show.
38573 setHidden : function(hidden){
38574 this.hidden = hidden;
38575 this.pnode.setStyle("display", hidden ? "none" : "");
38579 * Returns true if this tab is "hidden"
38580 * @return {Boolean}
38582 isHidden : function(){
38583 return this.hidden;
38587 * Returns the text for this tab
38590 getText : function(){
38594 autoSize : function(){
38595 //this.el.beginMeasure();
38596 this.textEl.setWidth(1);
38598 * #2804 [new] Tabs in Roojs
38599 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38601 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38602 //this.el.endMeasure();
38606 * Sets the text for the tab (Note: this also sets the tooltip text)
38607 * @param {String} text The tab's text and tooltip
38609 setText : function(text){
38611 this.textEl.update(text);
38612 this.setTooltip(text);
38613 //if(!this.tabPanel.resizeTabs){
38614 // this.autoSize();
38618 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38620 activate : function(){
38621 this.tabPanel.activate(this.id);
38625 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38627 disable : function(){
38628 if(this.tabPanel.active != this){
38629 this.disabled = true;
38630 this.pnode.addClass("disabled");
38635 * Enables this TabPanelItem if it was previously disabled.
38637 enable : function(){
38638 this.disabled = false;
38639 this.pnode.removeClass("disabled");
38643 * Sets the content for this TabPanelItem.
38644 * @param {String} content The content
38645 * @param {Boolean} loadScripts true to look for and load scripts
38647 setContent : function(content, loadScripts){
38648 this.bodyEl.update(content, loadScripts);
38652 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38653 * @return {Roo.UpdateManager} The UpdateManager
38655 getUpdateManager : function(){
38656 return this.bodyEl.getUpdateManager();
38660 * Set a URL to be used to load the content for this TabPanelItem.
38661 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38662 * @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)
38663 * @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)
38664 * @return {Roo.UpdateManager} The UpdateManager
38666 setUrl : function(url, params, loadOnce){
38667 if(this.refreshDelegate){
38668 this.un('activate', this.refreshDelegate);
38670 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38671 this.on("activate", this.refreshDelegate);
38672 return this.bodyEl.getUpdateManager();
38676 _handleRefresh : function(url, params, loadOnce){
38677 if(!loadOnce || !this.loaded){
38678 var updater = this.bodyEl.getUpdateManager();
38679 updater.update(url, params, this._setLoaded.createDelegate(this));
38684 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38685 * Will fail silently if the setUrl method has not been called.
38686 * This does not activate the panel, just updates its content.
38688 refresh : function(){
38689 if(this.refreshDelegate){
38690 this.loaded = false;
38691 this.refreshDelegate();
38696 _setLoaded : function(){
38697 this.loaded = true;
38701 closeClick : function(e){
38704 this.fireEvent("beforeclose", this, o);
38705 if(o.cancel !== true){
38706 this.tabPanel.removeTab(this.id);
38710 * The text displayed in the tooltip for the close icon.
38713 closeText : "Close this tab"
38716 * This script refer to:
38717 * Title: International Telephone Input
38718 * Author: Jack O'Connor
38719 * Code version: v12.1.12
38720 * Availability: https://github.com/jackocnr/intl-tel-input.git
38723 Roo.bootstrap.PhoneInputData = function() {
38726 "Afghanistan (افغانستان)",
38731 "Albania (Shqipëri)",
38736 "Algeria (الجزائر)",
38761 "Antigua and Barbuda",
38771 "Armenia (Հայաստան)",
38787 "Austria (Österreich)",
38792 "Azerbaijan (Azərbaycan)",
38802 "Bahrain (البحرين)",
38807 "Bangladesh (বাংলাদেশ)",
38817 "Belarus (Беларусь)",
38822 "Belgium (België)",
38852 "Bosnia and Herzegovina (Босна и Херцеговина)",
38867 "British Indian Ocean Territory",
38872 "British Virgin Islands",
38882 "Bulgaria (България)",
38892 "Burundi (Uburundi)",
38897 "Cambodia (កម្ពុជា)",
38902 "Cameroon (Cameroun)",
38911 ["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"]
38914 "Cape Verde (Kabu Verdi)",
38919 "Caribbean Netherlands",
38930 "Central African Republic (République centrafricaine)",
38950 "Christmas Island",
38956 "Cocos (Keeling) Islands",
38967 "Comoros (جزر القمر)",
38972 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38977 "Congo (Republic) (Congo-Brazzaville)",
38997 "Croatia (Hrvatska)",
39018 "Czech Republic (Česká republika)",
39023 "Denmark (Danmark)",
39038 "Dominican Republic (República Dominicana)",
39042 ["809", "829", "849"]
39060 "Equatorial Guinea (Guinea Ecuatorial)",
39080 "Falkland Islands (Islas Malvinas)",
39085 "Faroe Islands (Føroyar)",
39106 "French Guiana (Guyane française)",
39111 "French Polynesia (Polynésie française)",
39126 "Georgia (საქართველო)",
39131 "Germany (Deutschland)",
39151 "Greenland (Kalaallit Nunaat)",
39188 "Guinea-Bissau (Guiné Bissau)",
39213 "Hungary (Magyarország)",
39218 "Iceland (Ísland)",
39238 "Iraq (العراق)",
39254 "Israel (ישראל)",
39281 "Jordan (الأردن)",
39286 "Kazakhstan (Казахстан)",
39307 "Kuwait (الكويت)",
39312 "Kyrgyzstan (Кыргызстан)",
39322 "Latvia (Latvija)",
39327 "Lebanon (لبنان)",
39342 "Libya (ليبيا)",
39352 "Lithuania (Lietuva)",
39367 "Macedonia (FYROM) (Македонија)",
39372 "Madagascar (Madagasikara)",
39402 "Marshall Islands",
39412 "Mauritania (موريتانيا)",
39417 "Mauritius (Moris)",
39438 "Moldova (Republica Moldova)",
39448 "Mongolia (Монгол)",
39453 "Montenegro (Crna Gora)",
39463 "Morocco (المغرب)",
39469 "Mozambique (Moçambique)",
39474 "Myanmar (Burma) (မြန်မာ)",
39479 "Namibia (Namibië)",
39494 "Netherlands (Nederland)",
39499 "New Caledonia (Nouvelle-Calédonie)",
39534 "North Korea (조선 민주주의 인민 공화국)",
39539 "Northern Mariana Islands",
39555 "Pakistan (پاکستان)",
39565 "Palestine (فلسطين)",
39575 "Papua New Guinea",
39617 "Réunion (La Réunion)",
39623 "Romania (România)",
39639 "Saint Barthélemy",
39650 "Saint Kitts and Nevis",
39660 "Saint Martin (Saint-Martin (partie française))",
39666 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39671 "Saint Vincent and the Grenadines",
39686 "São Tomé and Príncipe (São Tomé e Príncipe)",
39691 "Saudi Arabia (المملكة العربية السعودية)",
39696 "Senegal (Sénégal)",
39726 "Slovakia (Slovensko)",
39731 "Slovenia (Slovenija)",
39741 "Somalia (Soomaaliya)",
39751 "South Korea (대한민국)",
39756 "South Sudan (جنوب السودان)",
39766 "Sri Lanka (ශ්රී ලංකාව)",
39771 "Sudan (السودان)",
39781 "Svalbard and Jan Mayen",
39792 "Sweden (Sverige)",
39797 "Switzerland (Schweiz)",
39802 "Syria (سوريا)",
39847 "Trinidad and Tobago",
39852 "Tunisia (تونس)",
39857 "Turkey (Türkiye)",
39867 "Turks and Caicos Islands",
39877 "U.S. Virgin Islands",
39887 "Ukraine (Україна)",
39892 "United Arab Emirates (الإمارات العربية المتحدة)",
39914 "Uzbekistan (Oʻzbekiston)",
39924 "Vatican City (Città del Vaticano)",
39935 "Vietnam (Việt Nam)",
39940 "Wallis and Futuna (Wallis-et-Futuna)",
39945 "Western Sahara (الصحراء الغربية)",
39951 "Yemen (اليمن)",
39975 * This script refer to:
39976 * Title: International Telephone Input
39977 * Author: Jack O'Connor
39978 * Code version: v12.1.12
39979 * Availability: https://github.com/jackocnr/intl-tel-input.git
39983 * @class Roo.bootstrap.PhoneInput
39984 * @extends Roo.bootstrap.TriggerField
39985 * An input with International dial-code selection
39987 * @cfg {String} defaultDialCode default '+852'
39988 * @cfg {Array} preferedCountries default []
39991 * Create a new PhoneInput.
39992 * @param {Object} config Configuration options
39995 Roo.bootstrap.PhoneInput = function(config) {
39996 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39999 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40001 listWidth: undefined,
40003 selectedClass: 'active',
40005 invalidClass : "has-warning",
40007 validClass: 'has-success',
40009 allowed: '0123456789',
40014 * @cfg {String} defaultDialCode The default dial code when initializing the input
40016 defaultDialCode: '+852',
40019 * @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
40021 preferedCountries: false,
40023 getAutoCreate : function()
40025 var data = Roo.bootstrap.PhoneInputData();
40026 var align = this.labelAlign || this.parentLabelAlign();
40029 this.allCountries = [];
40030 this.dialCodeMapping = [];
40032 for (var i = 0; i < data.length; i++) {
40034 this.allCountries[i] = {
40038 priority: c[3] || 0,
40039 areaCodes: c[4] || null
40041 this.dialCodeMapping[c[2]] = {
40044 priority: c[3] || 0,
40045 areaCodes: c[4] || null
40057 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40058 maxlength: this.max_length,
40059 cls : 'form-control tel-input',
40060 autocomplete: 'new-password'
40063 var hiddenInput = {
40066 cls: 'hidden-tel-input'
40070 hiddenInput.name = this.name;
40073 if (this.disabled) {
40074 input.disabled = true;
40077 var flag_container = {
40094 cls: this.hasFeedback ? 'has-feedback' : '',
40100 cls: 'dial-code-holder',
40107 cls: 'roo-select2-container input-group',
40114 if (this.fieldLabel.length) {
40117 tooltip: 'This field is required'
40123 cls: 'control-label',
40129 html: this.fieldLabel
40132 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40138 if(this.indicatorpos == 'right') {
40139 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40146 if(align == 'left') {
40154 if(this.labelWidth > 12){
40155 label.style = "width: " + this.labelWidth + 'px';
40157 if(this.labelWidth < 13 && this.labelmd == 0){
40158 this.labelmd = this.labelWidth;
40160 if(this.labellg > 0){
40161 label.cls += ' col-lg-' + this.labellg;
40162 input.cls += ' col-lg-' + (12 - this.labellg);
40164 if(this.labelmd > 0){
40165 label.cls += ' col-md-' + this.labelmd;
40166 container.cls += ' col-md-' + (12 - this.labelmd);
40168 if(this.labelsm > 0){
40169 label.cls += ' col-sm-' + this.labelsm;
40170 container.cls += ' col-sm-' + (12 - this.labelsm);
40172 if(this.labelxs > 0){
40173 label.cls += ' col-xs-' + this.labelxs;
40174 container.cls += ' col-xs-' + (12 - this.labelxs);
40184 var settings = this;
40186 ['xs','sm','md','lg'].map(function(size){
40187 if (settings[size]) {
40188 cfg.cls += ' col-' + size + '-' + settings[size];
40192 this.store = new Roo.data.Store({
40193 proxy : new Roo.data.MemoryProxy({}),
40194 reader : new Roo.data.JsonReader({
40205 'name' : 'dialCode',
40209 'name' : 'priority',
40213 'name' : 'areaCodes',
40220 if(!this.preferedCountries) {
40221 this.preferedCountries = [
40228 var p = this.preferedCountries.reverse();
40231 for (var i = 0; i < p.length; i++) {
40232 for (var j = 0; j < this.allCountries.length; j++) {
40233 if(this.allCountries[j].iso2 == p[i]) {
40234 var t = this.allCountries[j];
40235 this.allCountries.splice(j,1);
40236 this.allCountries.unshift(t);
40242 this.store.proxy.data = {
40244 data: this.allCountries
40250 initEvents : function()
40253 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40255 this.indicator = this.indicatorEl();
40256 this.flag = this.flagEl();
40257 this.dialCodeHolder = this.dialCodeHolderEl();
40259 this.trigger = this.el.select('div.flag-box',true).first();
40260 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40265 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40266 _this.list.setWidth(lw);
40269 this.list.on('mouseover', this.onViewOver, this);
40270 this.list.on('mousemove', this.onViewMove, this);
40271 this.inputEl().on("keyup", this.onKeyUp, this);
40272 this.inputEl().on("keypress", this.onKeyPress, this);
40274 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40276 this.view = new Roo.View(this.list, this.tpl, {
40277 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40280 this.view.on('click', this.onViewClick, this);
40281 this.setValue(this.defaultDialCode);
40284 onTriggerClick : function(e)
40286 Roo.log('trigger click');
40291 if(this.isExpanded()){
40293 this.hasFocus = false;
40295 this.store.load({});
40296 this.hasFocus = true;
40301 isExpanded : function()
40303 return this.list.isVisible();
40306 collapse : function()
40308 if(!this.isExpanded()){
40312 Roo.get(document).un('mousedown', this.collapseIf, this);
40313 Roo.get(document).un('mousewheel', this.collapseIf, this);
40314 this.fireEvent('collapse', this);
40318 expand : function()
40322 if(this.isExpanded() || !this.hasFocus){
40326 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40327 this.list.setWidth(lw);
40330 this.restrictHeight();
40332 Roo.get(document).on('mousedown', this.collapseIf, this);
40333 Roo.get(document).on('mousewheel', this.collapseIf, this);
40335 this.fireEvent('expand', this);
40338 restrictHeight : function()
40340 this.list.alignTo(this.inputEl(), this.listAlign);
40341 this.list.alignTo(this.inputEl(), this.listAlign);
40344 onViewOver : function(e, t)
40346 if(this.inKeyMode){
40349 var item = this.view.findItemFromChild(t);
40352 var index = this.view.indexOf(item);
40353 this.select(index, false);
40358 onViewClick : function(view, doFocus, el, e)
40360 var index = this.view.getSelectedIndexes()[0];
40362 var r = this.store.getAt(index);
40365 this.onSelect(r, index);
40367 if(doFocus !== false && !this.blockFocus){
40368 this.inputEl().focus();
40372 onViewMove : function(e, t)
40374 this.inKeyMode = false;
40377 select : function(index, scrollIntoView)
40379 this.selectedIndex = index;
40380 this.view.select(index);
40381 if(scrollIntoView !== false){
40382 var el = this.view.getNode(index);
40384 this.list.scrollChildIntoView(el, false);
40389 createList : function()
40391 this.list = Roo.get(document.body).createChild({
40393 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40394 style: 'display:none'
40397 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40400 collapseIf : function(e)
40402 var in_combo = e.within(this.el);
40403 var in_list = e.within(this.list);
40404 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40406 if (in_combo || in_list || is_list) {
40412 onSelect : function(record, index)
40414 if(this.fireEvent('beforeselect', this, record, index) !== false){
40416 this.setFlagClass(record.data.iso2);
40417 this.setDialCode(record.data.dialCode);
40418 this.hasFocus = false;
40420 this.fireEvent('select', this, record, index);
40424 flagEl : function()
40426 var flag = this.el.select('div.flag',true).first();
40433 dialCodeHolderEl : function()
40435 var d = this.el.select('input.dial-code-holder',true).first();
40442 setDialCode : function(v)
40444 this.dialCodeHolder.dom.value = '+'+v;
40447 setFlagClass : function(n)
40449 this.flag.dom.className = 'flag '+n;
40452 getValue : function()
40454 var v = this.inputEl().getValue();
40455 if(this.dialCodeHolder) {
40456 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40461 setValue : function(v)
40463 var d = this.getDialCode(v);
40465 //invalid dial code
40466 if(v.length == 0 || !d || d.length == 0) {
40468 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40469 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40475 this.setFlagClass(this.dialCodeMapping[d].iso2);
40476 this.setDialCode(d);
40477 this.inputEl().dom.value = v.replace('+'+d,'');
40478 this.hiddenEl().dom.value = this.getValue();
40483 getDialCode : function(v)
40487 if (v.length == 0) {
40488 return this.dialCodeHolder.dom.value;
40492 if (v.charAt(0) != "+") {
40495 var numericChars = "";
40496 for (var i = 1; i < v.length; i++) {
40497 var c = v.charAt(i);
40500 if (this.dialCodeMapping[numericChars]) {
40501 dialCode = v.substr(1, i);
40503 if (numericChars.length == 4) {
40513 this.setValue(this.defaultDialCode);
40517 hiddenEl : function()
40519 return this.el.select('input.hidden-tel-input',true).first();
40522 // after setting val
40523 onKeyUp : function(e){
40524 this.setValue(this.getValue());
40527 onKeyPress : function(e){
40528 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40535 * @class Roo.bootstrap.MoneyField
40536 * @extends Roo.bootstrap.ComboBox
40537 * Bootstrap MoneyField class
40540 * Create a new MoneyField.
40541 * @param {Object} config Configuration options
40544 Roo.bootstrap.MoneyField = function(config) {
40546 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40550 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40553 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40555 allowDecimals : true,
40557 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40559 decimalSeparator : ".",
40561 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40563 decimalPrecision : 0,
40565 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40567 allowNegative : true,
40569 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40573 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40575 minValue : Number.NEGATIVE_INFINITY,
40577 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40579 maxValue : Number.MAX_VALUE,
40581 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40583 minText : "The minimum value for this field is {0}",
40585 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40587 maxText : "The maximum value for this field is {0}",
40589 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40590 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40592 nanText : "{0} is not a valid number",
40594 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40598 * @cfg {String} defaults currency of the MoneyField
40599 * value should be in lkey
40601 defaultCurrency : false,
40603 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40605 thousandsDelimiter : false,
40607 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40618 getAutoCreate : function()
40620 var align = this.labelAlign || this.parentLabelAlign();
40632 cls : 'form-control roo-money-amount-input',
40633 autocomplete: 'new-password'
40636 var hiddenInput = {
40640 cls: 'hidden-number-input'
40643 if(this.max_length) {
40644 input.maxlength = this.max_length;
40648 hiddenInput.name = this.name;
40651 if (this.disabled) {
40652 input.disabled = true;
40655 var clg = 12 - this.inputlg;
40656 var cmd = 12 - this.inputmd;
40657 var csm = 12 - this.inputsm;
40658 var cxs = 12 - this.inputxs;
40662 cls : 'row roo-money-field',
40666 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40670 cls: 'roo-select2-container input-group',
40674 cls : 'form-control roo-money-currency-input',
40675 autocomplete: 'new-password',
40677 name : this.currencyName
40681 cls : 'input-group-addon',
40695 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40699 cls: this.hasFeedback ? 'has-feedback' : '',
40710 if (this.fieldLabel.length) {
40713 tooltip: 'This field is required'
40719 cls: 'control-label',
40725 html: this.fieldLabel
40728 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40734 if(this.indicatorpos == 'right') {
40735 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40742 if(align == 'left') {
40750 if(this.labelWidth > 12){
40751 label.style = "width: " + this.labelWidth + 'px';
40753 if(this.labelWidth < 13 && this.labelmd == 0){
40754 this.labelmd = this.labelWidth;
40756 if(this.labellg > 0){
40757 label.cls += ' col-lg-' + this.labellg;
40758 input.cls += ' col-lg-' + (12 - this.labellg);
40760 if(this.labelmd > 0){
40761 label.cls += ' col-md-' + this.labelmd;
40762 container.cls += ' col-md-' + (12 - this.labelmd);
40764 if(this.labelsm > 0){
40765 label.cls += ' col-sm-' + this.labelsm;
40766 container.cls += ' col-sm-' + (12 - this.labelsm);
40768 if(this.labelxs > 0){
40769 label.cls += ' col-xs-' + this.labelxs;
40770 container.cls += ' col-xs-' + (12 - this.labelxs);
40781 var settings = this;
40783 ['xs','sm','md','lg'].map(function(size){
40784 if (settings[size]) {
40785 cfg.cls += ' col-' + size + '-' + settings[size];
40792 initEvents : function()
40794 this.indicator = this.indicatorEl();
40796 this.initCurrencyEvent();
40798 this.initNumberEvent();
40801 initCurrencyEvent : function()
40804 throw "can not find store for combo";
40807 this.store = Roo.factory(this.store, Roo.data);
40808 this.store.parent = this;
40812 this.triggerEl = this.el.select('.input-group-addon', true).first();
40814 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40819 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40820 _this.list.setWidth(lw);
40823 this.list.on('mouseover', this.onViewOver, this);
40824 this.list.on('mousemove', this.onViewMove, this);
40825 this.list.on('scroll', this.onViewScroll, this);
40828 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40831 this.view = new Roo.View(this.list, this.tpl, {
40832 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40835 this.view.on('click', this.onViewClick, this);
40837 this.store.on('beforeload', this.onBeforeLoad, this);
40838 this.store.on('load', this.onLoad, this);
40839 this.store.on('loadexception', this.onLoadException, this);
40841 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40842 "up" : function(e){
40843 this.inKeyMode = true;
40847 "down" : function(e){
40848 if(!this.isExpanded()){
40849 this.onTriggerClick();
40851 this.inKeyMode = true;
40856 "enter" : function(e){
40859 if(this.fireEvent("specialkey", this, e)){
40860 this.onViewClick(false);
40866 "esc" : function(e){
40870 "tab" : function(e){
40873 if(this.fireEvent("specialkey", this, e)){
40874 this.onViewClick(false);
40882 doRelay : function(foo, bar, hname){
40883 if(hname == 'down' || this.scope.isExpanded()){
40884 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40892 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40896 initNumberEvent : function(e)
40898 this.inputEl().on("keydown" , this.fireKey, this);
40899 this.inputEl().on("focus", this.onFocus, this);
40900 this.inputEl().on("blur", this.onBlur, this);
40902 this.inputEl().relayEvent('keyup', this);
40904 if(this.indicator){
40905 this.indicator.addClass('invisible');
40908 this.originalValue = this.getValue();
40910 if(this.validationEvent == 'keyup'){
40911 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40912 this.inputEl().on('keyup', this.filterValidation, this);
40914 else if(this.validationEvent !== false){
40915 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40918 if(this.selectOnFocus){
40919 this.on("focus", this.preFocus, this);
40922 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40923 this.inputEl().on("keypress", this.filterKeys, this);
40925 this.inputEl().relayEvent('keypress', this);
40928 var allowed = "0123456789";
40930 if(this.allowDecimals){
40931 allowed += this.decimalSeparator;
40934 if(this.allowNegative){
40938 if(this.thousandsDelimiter) {
40942 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40944 var keyPress = function(e){
40946 var k = e.getKey();
40948 var c = e.getCharCode();
40951 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40952 allowed.indexOf(String.fromCharCode(c)) === -1
40958 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40962 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40967 this.inputEl().on("keypress", keyPress, this);
40971 onTriggerClick : function(e)
40978 this.loadNext = false;
40980 if(this.isExpanded()){
40985 this.hasFocus = true;
40987 if(this.triggerAction == 'all') {
40988 this.doQuery(this.allQuery, true);
40992 this.doQuery(this.getRawValue());
40995 getCurrency : function()
40997 var v = this.currencyEl().getValue();
41002 restrictHeight : function()
41004 this.list.alignTo(this.currencyEl(), this.listAlign);
41005 this.list.alignTo(this.currencyEl(), this.listAlign);
41008 onViewClick : function(view, doFocus, el, e)
41010 var index = this.view.getSelectedIndexes()[0];
41012 var r = this.store.getAt(index);
41015 this.onSelect(r, index);
41019 onSelect : function(record, index){
41021 if(this.fireEvent('beforeselect', this, record, index) !== false){
41023 this.setFromCurrencyData(index > -1 ? record.data : false);
41027 this.fireEvent('select', this, record, index);
41031 setFromCurrencyData : function(o)
41035 this.lastCurrency = o;
41037 if (this.currencyField) {
41038 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41040 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
41043 this.lastSelectionText = currency;
41045 //setting default currency
41046 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41047 this.setCurrency(this.defaultCurrency);
41051 this.setCurrency(currency);
41054 setFromData : function(o)
41058 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41060 this.setFromCurrencyData(c);
41065 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41067 Roo.log('no value set for '+ (this.name ? this.name : this.id));
41070 this.setValue(value);
41074 setCurrency : function(v)
41076 this.currencyValue = v;
41079 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41084 setValue : function(v)
41086 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41092 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41094 this.inputEl().dom.value = (v == '') ? '' :
41095 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41097 if(!this.allowZero && v === '0') {
41098 this.hiddenEl().dom.value = '';
41099 this.inputEl().dom.value = '';
41106 getRawValue : function()
41108 var v = this.inputEl().getValue();
41113 getValue : function()
41115 return this.fixPrecision(this.parseValue(this.getRawValue()));
41118 parseValue : function(value)
41120 if(this.thousandsDelimiter) {
41122 r = new RegExp(",", "g");
41123 value = value.replace(r, "");
41126 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41127 return isNaN(value) ? '' : value;
41131 fixPrecision : function(value)
41133 if(this.thousandsDelimiter) {
41135 r = new RegExp(",", "g");
41136 value = value.replace(r, "");
41139 var nan = isNaN(value);
41141 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41142 return nan ? '' : value;
41144 return parseFloat(value).toFixed(this.decimalPrecision);
41147 decimalPrecisionFcn : function(v)
41149 return Math.floor(v);
41152 validateValue : function(value)
41154 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41158 var num = this.parseValue(value);
41161 this.markInvalid(String.format(this.nanText, value));
41165 if(num < this.minValue){
41166 this.markInvalid(String.format(this.minText, this.minValue));
41170 if(num > this.maxValue){
41171 this.markInvalid(String.format(this.maxText, this.maxValue));
41178 validate : function()
41180 if(this.disabled || this.allowBlank){
41185 var currency = this.getCurrency();
41187 if(this.validateValue(this.getRawValue()) && currency.length){
41192 this.markInvalid();
41196 getName: function()
41201 beforeBlur : function()
41207 var v = this.parseValue(this.getRawValue());
41214 onBlur : function()
41218 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41219 //this.el.removeClass(this.focusClass);
41222 this.hasFocus = false;
41224 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41228 var v = this.getValue();
41230 if(String(v) !== String(this.startValue)){
41231 this.fireEvent('change', this, v, this.startValue);
41234 this.fireEvent("blur", this);
41237 inputEl : function()
41239 return this.el.select('.roo-money-amount-input', true).first();
41242 currencyEl : function()
41244 return this.el.select('.roo-money-currency-input', true).first();
41247 hiddenEl : function()
41249 return this.el.select('input.hidden-number-input',true).first();