2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = (
9 Roo.each(document.styleSheets, function(s) {
10 if ( s.href && 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','d-none']);
418 this.fireEvent('show', this);
423 * Hide a component - adds 'hidden' class
427 if(!this.getVisibilityEl()){
431 this.getVisibilityEl().addClass(['hidden','d-none']);
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 + ' hidden' + size + '-down';;
1078 cfg.cls += ' col-' + size + '-' + settings[size] + (
1079 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1085 cfg.cls += ' hidden';
1088 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1089 cfg.cls +=' alert alert-' + this.alert;
1093 if (this.html.length) {
1094 cfg.html = this.html;
1098 if (this.fasize > 1) {
1099 fasize = ' fa-' + this.fasize + 'x';
1101 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1106 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1125 * @class Roo.bootstrap.Container
1126 * @extends Roo.bootstrap.Component
1127 * Bootstrap Container class
1128 * @cfg {Boolean} jumbotron is it a jumbotron element
1129 * @cfg {String} html content of element
1130 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1131 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1132 * @cfg {String} header content of header (for panel)
1133 * @cfg {String} footer content of footer (for panel)
1134 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1135 * @cfg {String} tag (header|aside|section) type of HTML tag.
1136 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1137 * @cfg {String} fa font awesome icon
1138 * @cfg {String} icon (info-sign|check|...) glyphicon name
1139 * @cfg {Boolean} hidden (true|false) hide the element
1140 * @cfg {Boolean} expandable (true|false) default false
1141 * @cfg {Boolean} expanded (true|false) default true
1142 * @cfg {String} rheader contet on the right of header
1143 * @cfg {Boolean} clickable (true|false) default false
1147 * Create a new Container
1148 * @param {Object} config The config object
1151 Roo.bootstrap.Container = function(config){
1152 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1158 * After the panel has been expand
1160 * @param {Roo.bootstrap.Container} this
1165 * After the panel has been collapsed
1167 * @param {Roo.bootstrap.Container} this
1172 * When a element is chick
1173 * @param {Roo.bootstrap.Container} this
1174 * @param {Roo.EventObject} e
1180 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1198 getChildContainer : function() {
1204 if (this.panel.length) {
1205 return this.el.select('.panel-body',true).first();
1212 getAutoCreate : function(){
1215 tag : this.tag || 'div',
1219 if (this.jumbotron) {
1220 cfg.cls = 'jumbotron';
1225 // - this is applied by the parent..
1227 // cfg.cls = this.cls + '';
1230 if (this.sticky.length) {
1232 var bd = Roo.get(document.body);
1233 if (!bd.hasClass('bootstrap-sticky')) {
1234 bd.addClass('bootstrap-sticky');
1235 Roo.select('html',true).setStyle('height', '100%');
1238 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1242 if (this.well.length) {
1243 switch (this.well) {
1246 cfg.cls +=' well well-' +this.well;
1255 cfg.cls += ' hidden';
1259 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1260 cfg.cls +=' alert alert-' + this.alert;
1265 if (this.panel.length) {
1266 cfg.cls += ' panel panel-' + this.panel;
1268 if (this.header.length) {
1272 if(this.expandable){
1274 cfg.cls = cfg.cls + ' expandable';
1278 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1286 cls : 'panel-title',
1287 html : (this.expandable ? ' ' : '') + this.header
1291 cls: 'panel-header-right',
1297 cls : 'panel-heading',
1298 style : this.expandable ? 'cursor: pointer' : '',
1306 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1311 if (this.footer.length) {
1313 cls : 'panel-footer',
1322 body.html = this.html || cfg.html;
1323 // prefix with the icons..
1325 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1328 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1333 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1334 cfg.cls = 'container';
1340 initEvents: function()
1342 if(this.expandable){
1343 var headerEl = this.headerEl();
1346 headerEl.on('click', this.onToggleClick, this);
1351 this.el.on('click', this.onClick, this);
1356 onToggleClick : function()
1358 var headerEl = this.headerEl();
1374 if(this.fireEvent('expand', this)) {
1376 this.expanded = true;
1378 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1380 this.el.select('.panel-body',true).first().removeClass('hide');
1382 var toggleEl = this.toggleEl();
1388 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1393 collapse : function()
1395 if(this.fireEvent('collapse', this)) {
1397 this.expanded = false;
1399 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1400 this.el.select('.panel-body',true).first().addClass('hide');
1402 var toggleEl = this.toggleEl();
1408 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1412 toggleEl : function()
1414 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1418 return this.el.select('.panel-heading .fa',true).first();
1421 headerEl : function()
1423 if(!this.el || !this.panel.length || !this.header.length){
1427 return this.el.select('.panel-heading',true).first()
1432 if(!this.el || !this.panel.length){
1436 return this.el.select('.panel-body',true).first()
1439 titleEl : function()
1441 if(!this.el || !this.panel.length || !this.header.length){
1445 return this.el.select('.panel-title',true).first();
1448 setTitle : function(v)
1450 var titleEl = this.titleEl();
1456 titleEl.dom.innerHTML = v;
1459 getTitle : function()
1462 var titleEl = this.titleEl();
1468 return titleEl.dom.innerHTML;
1471 setRightTitle : function(v)
1473 var t = this.el.select('.panel-header-right',true).first();
1479 t.dom.innerHTML = v;
1482 onClick : function(e)
1486 this.fireEvent('click', this, e);
1493 * This is BS4's Card element.. - similar to our containers probably..
1497 * @class Roo.bootstrap.Card
1498 * @extends Roo.bootstrap.Component
1499 * Bootstrap Card class
1502 * possible... may not be implemented..
1503 * @cfg {String} header_image src url of image.
1504 * @cfg {String} header
1505 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1507 * @cfg {String} title
1508 * @cfg {String} subtitle
1509 * @cfg {String} html -- html contents - or just use children..
1510 * @cfg {String} footer
1512 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1514 * @cfg {String} margin (0|1|2|3|4|5|auto)
1515 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1516 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1517 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1518 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1519 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1520 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1522 * @cfg {String} padding (0|1|2|3|4|5)
1523 * @cfg {String} padding_top (0|1|2|3|4|5)
1524 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1525 * @cfg {String} padding_left (0|1|2|3|4|5)
1526 * @cfg {String} padding_right (0|1|2|3|4|5)
1527 * @cfg {String} padding_x (0|1|2|3|4|5)
1528 * @cfg {String} padding_y (0|1|2|3|4|5)
1530 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1531 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1532 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1533 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1534 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1537 * Create a new Container
1538 * @param {Object} config The config object
1541 Roo.bootstrap.Card = function(config){
1542 Roo.bootstrap.Card.superclass.constructor.call(this, config);
1550 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
1555 margin: '', /// may be better in component?
1585 childContainer : false,
1587 layoutCls : function()
1592 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
1593 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
1595 if (t['margin' + (v.length ? '_' : '') + v].length) {
1596 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
1598 if (t['padding' + (v.length ? '_' : '') + v].length) {
1599 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
1603 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
1604 if (t['display' + (v.length ? '_' : '') + v].length) {
1605 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v].length
1609 // more generic support?
1617 // Roo.log("Call onRender: " + this.xtype);
1618 /* We are looking at something like this.
1620 <img src="..." class="card-img-top" alt="...">
1621 <div class="card-body">
1622 <h5 class="card-title">Card title</h5>
1623 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
1625 >> this bit is really the body...
1626 <div> << we will ad dthis in hopefully it will not break shit.
1628 ** card text does not actually have any styling...
1630 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
1633 <a href="#" class="card-link">Card link</a>
1636 <div class="card-footer">
1637 <small class="text-muted">Last updated 3 mins ago</small>
1641 getAutoCreate : function(){
1649 if (this.weight.length && this.weight != 'light') {
1650 cfg.cls += ' text-white'
1652 if (this.weight.length) {
1653 cfg.cls += ' bg-' + this.weight;
1656 cfg.cls += this.layoutCls();
1658 if (this.header.length) {
1660 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
1661 cls : 'card-header',
1662 html : this.header // escape?
1665 if (this.header_image.length) {
1668 cls : 'card-img-top',
1669 src: this.header_image // escape?
1680 if (this.title.length) {
1684 src: this.title // escape?
1688 if (this.subtitle.length) {
1692 src: this.subtitle // escape?
1698 cls : 'roo-card-body-ctr'
1701 // fixme ? handle objects?
1702 if (this.footer.length) {
1705 cls : 'card-footer',
1706 html: this.footer // escape?
1715 getChildContainer : function()
1721 return this.el.select('.roo-card-body-ctr',true).first();
1735 * @class Roo.bootstrap.Img
1736 * @extends Roo.bootstrap.Component
1737 * Bootstrap Img class
1738 * @cfg {Boolean} imgResponsive false | true
1739 * @cfg {String} border rounded | circle | thumbnail
1740 * @cfg {String} src image source
1741 * @cfg {String} alt image alternative text
1742 * @cfg {String} href a tag href
1743 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1744 * @cfg {String} xsUrl xs image source
1745 * @cfg {String} smUrl sm image source
1746 * @cfg {String} mdUrl md image source
1747 * @cfg {String} lgUrl lg image source
1750 * Create a new Input
1751 * @param {Object} config The config object
1754 Roo.bootstrap.Img = function(config){
1755 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1761 * The img click event for the img.
1762 * @param {Roo.EventObject} e
1768 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1770 imgResponsive: true,
1780 getAutoCreate : function()
1782 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1783 return this.createSingleImg();
1788 cls: 'roo-image-responsive-group',
1793 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1795 if(!_this[size + 'Url']){
1801 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1802 html: _this.html || cfg.html,
1803 src: _this[size + 'Url']
1806 img.cls += ' roo-image-responsive-' + size;
1808 var s = ['xs', 'sm', 'md', 'lg'];
1810 s.splice(s.indexOf(size), 1);
1812 Roo.each(s, function(ss){
1813 img.cls += ' hidden-' + ss;
1816 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1817 cfg.cls += ' img-' + _this.border;
1821 cfg.alt = _this.alt;
1834 a.target = _this.target;
1838 cfg.cn.push((_this.href) ? a : img);
1845 createSingleImg : function()
1849 cls: (this.imgResponsive) ? 'img-responsive' : '',
1851 src : 'about:blank' // just incase src get's set to undefined?!?
1854 cfg.html = this.html || cfg.html;
1856 cfg.src = this.src || cfg.src;
1858 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1859 cfg.cls += ' img-' + this.border;
1876 a.target = this.target;
1881 return (this.href) ? a : cfg;
1884 initEvents: function()
1887 this.el.on('click', this.onClick, this);
1892 onClick : function(e)
1894 Roo.log('img onclick');
1895 this.fireEvent('click', this, e);
1898 * Sets the url of the image - used to update it
1899 * @param {String} url the url of the image
1902 setSrc : function(url)
1906 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1907 this.el.dom.src = url;
1911 this.el.select('img', true).first().dom.src = url;
1927 * @class Roo.bootstrap.Link
1928 * @extends Roo.bootstrap.Component
1929 * Bootstrap Link Class
1930 * @cfg {String} alt image alternative text
1931 * @cfg {String} href a tag href
1932 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1933 * @cfg {String} html the content of the link.
1934 * @cfg {String} anchor name for the anchor link
1935 * @cfg {String} fa - favicon
1937 * @cfg {Boolean} preventDefault (true | false) default false
1941 * Create a new Input
1942 * @param {Object} config The config object
1945 Roo.bootstrap.Link = function(config){
1946 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1952 * The img click event for the img.
1953 * @param {Roo.EventObject} e
1959 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1963 preventDefault: false,
1969 getAutoCreate : function()
1971 var html = this.html || '';
1973 if (this.fa !== false) {
1974 html = '<i class="fa fa-' + this.fa + '"></i>';
1979 // anchor's do not require html/href...
1980 if (this.anchor === false) {
1982 cfg.href = this.href || '#';
1984 cfg.name = this.anchor;
1985 if (this.html !== false || this.fa !== false) {
1988 if (this.href !== false) {
1989 cfg.href = this.href;
1993 if(this.alt !== false){
1998 if(this.target !== false) {
1999 cfg.target = this.target;
2005 initEvents: function() {
2007 if(!this.href || this.preventDefault){
2008 this.el.on('click', this.onClick, this);
2012 onClick : function(e)
2014 if(this.preventDefault){
2017 //Roo.log('img onclick');
2018 this.fireEvent('click', this, e);
2031 * @class Roo.bootstrap.Header
2032 * @extends Roo.bootstrap.Component
2033 * Bootstrap Header class
2034 * @cfg {String} html content of header
2035 * @cfg {Number} level (1|2|3|4|5|6) default 1
2038 * Create a new Header
2039 * @param {Object} config The config object
2043 Roo.bootstrap.Header = function(config){
2044 Roo.bootstrap.Header.superclass.constructor.call(this, config);
2047 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
2055 getAutoCreate : function(){
2060 tag: 'h' + (1 *this.level),
2061 html: this.html || ''
2073 * Ext JS Library 1.1.1
2074 * Copyright(c) 2006-2007, Ext JS, LLC.
2076 * Originally Released Under LGPL - original licence link has changed is not relivant.
2079 * <script type="text/javascript">
2083 * @class Roo.bootstrap.MenuMgr
2084 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
2087 Roo.bootstrap.MenuMgr = function(){
2088 var menus, active, groups = {}, attached = false, lastShow = new Date();
2090 // private - called when first menu is created
2093 active = new Roo.util.MixedCollection();
2094 Roo.get(document).addKeyListener(27, function(){
2095 if(active.length > 0){
2103 if(active && active.length > 0){
2104 var c = active.clone();
2114 if(active.length < 1){
2115 Roo.get(document).un("mouseup", onMouseDown);
2123 var last = active.last();
2124 lastShow = new Date();
2127 Roo.get(document).on("mouseup", onMouseDown);
2132 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
2133 m.parentMenu.activeChild = m;
2134 }else if(last && last.isVisible()){
2135 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
2140 function onBeforeHide(m){
2142 m.activeChild.hide();
2144 if(m.autoHideTimer){
2145 clearTimeout(m.autoHideTimer);
2146 delete m.autoHideTimer;
2151 function onBeforeShow(m){
2152 var pm = m.parentMenu;
2153 if(!pm && !m.allowOtherMenus){
2155 }else if(pm && pm.activeChild && active != m){
2156 pm.activeChild.hide();
2160 // private this should really trigger on mouseup..
2161 function onMouseDown(e){
2162 Roo.log("on Mouse Up");
2164 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
2165 Roo.log("MenuManager hideAll");
2174 function onBeforeCheck(mi, state){
2176 var g = groups[mi.group];
2177 for(var i = 0, l = g.length; i < l; i++){
2179 g[i].setChecked(false);
2188 * Hides all menus that are currently visible
2190 hideAll : function(){
2195 register : function(menu){
2199 menus[menu.id] = menu;
2200 menu.on("beforehide", onBeforeHide);
2201 menu.on("hide", onHide);
2202 menu.on("beforeshow", onBeforeShow);
2203 menu.on("show", onShow);
2205 if(g && menu.events["checkchange"]){
2209 groups[g].push(menu);
2210 menu.on("checkchange", onCheck);
2215 * Returns a {@link Roo.menu.Menu} object
2216 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
2217 * be used to generate and return a new Menu instance.
2219 get : function(menu){
2220 if(typeof menu == "string"){ // menu id
2222 }else if(menu.events){ // menu instance
2225 /*else if(typeof menu.length == 'number'){ // array of menu items?
2226 return new Roo.bootstrap.Menu({items:menu});
2227 }else{ // otherwise, must be a config
2228 return new Roo.bootstrap.Menu(menu);
2235 unregister : function(menu){
2236 delete menus[menu.id];
2237 menu.un("beforehide", onBeforeHide);
2238 menu.un("hide", onHide);
2239 menu.un("beforeshow", onBeforeShow);
2240 menu.un("show", onShow);
2242 if(g && menu.events["checkchange"]){
2243 groups[g].remove(menu);
2244 menu.un("checkchange", onCheck);
2249 registerCheckable : function(menuItem){
2250 var g = menuItem.group;
2255 groups[g].push(menuItem);
2256 menuItem.on("beforecheckchange", onBeforeCheck);
2261 unregisterCheckable : function(menuItem){
2262 var g = menuItem.group;
2264 groups[g].remove(menuItem);
2265 menuItem.un("beforecheckchange", onBeforeCheck);
2277 * @class Roo.bootstrap.Menu
2278 * @extends Roo.bootstrap.Component
2279 * Bootstrap Menu class - container for MenuItems
2280 * @cfg {String} type (dropdown|treeview|submenu) type of menu
2281 * @cfg {bool} hidden if the menu should be hidden when rendered.
2282 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
2283 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
2287 * @param {Object} config The config object
2291 Roo.bootstrap.Menu = function(config){
2292 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2293 if (this.registerMenu && this.type != 'treeview') {
2294 Roo.bootstrap.MenuMgr.register(this);
2301 * Fires before this menu is displayed (return false to block)
2302 * @param {Roo.menu.Menu} this
2307 * Fires before this menu is hidden (return false to block)
2308 * @param {Roo.menu.Menu} this
2313 * Fires after this menu is displayed
2314 * @param {Roo.menu.Menu} this
2319 * Fires after this menu is hidden
2320 * @param {Roo.menu.Menu} this
2325 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2326 * @param {Roo.menu.Menu} this
2327 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2328 * @param {Roo.EventObject} e
2333 * Fires when the mouse is hovering over this menu
2334 * @param {Roo.menu.Menu} this
2335 * @param {Roo.EventObject} e
2336 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2341 * Fires when the mouse exits this menu
2342 * @param {Roo.menu.Menu} this
2343 * @param {Roo.EventObject} e
2344 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2349 * Fires when a menu item contained in this menu is clicked
2350 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2351 * @param {Roo.EventObject} e
2355 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2358 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2362 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2365 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2367 registerMenu : true,
2369 menuItems :false, // stores the menu items..
2379 getChildContainer : function() {
2383 getAutoCreate : function(){
2385 //if (['right'].indexOf(this.align)!==-1) {
2386 // cfg.cn[1].cls += ' pull-right'
2392 cls : 'dropdown-menu' ,
2393 style : 'z-index:1000'
2397 if (this.type === 'submenu') {
2398 cfg.cls = 'submenu active';
2400 if (this.type === 'treeview') {
2401 cfg.cls = 'treeview-menu';
2406 initEvents : function() {
2408 // Roo.log("ADD event");
2409 // Roo.log(this.triggerEl.dom);
2411 this.triggerEl.on('click', this.onTriggerClick, this);
2413 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2416 if (this.triggerEl.hasClass('nav-item')) {
2417 // dropdown toggle on the 'a' in BS4?
2418 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2420 this.triggerEl.addClass('dropdown-toggle');
2423 this.el.on('touchstart' , this.onTouch, this);
2425 this.el.on('click' , this.onClick, this);
2427 this.el.on("mouseover", this.onMouseOver, this);
2428 this.el.on("mouseout", this.onMouseOut, this);
2432 findTargetItem : function(e)
2434 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2438 //Roo.log(t); Roo.log(t.id);
2440 //Roo.log(this.menuitems);
2441 return this.menuitems.get(t.id);
2443 //return this.items.get(t.menuItemId);
2449 onTouch : function(e)
2451 Roo.log("menu.onTouch");
2452 //e.stopEvent(); this make the user popdown broken
2456 onClick : function(e)
2458 Roo.log("menu.onClick");
2460 var t = this.findTargetItem(e);
2461 if(!t || t.isContainer){
2466 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2467 if(t == this.activeItem && t.shouldDeactivate(e)){
2468 this.activeItem.deactivate();
2469 delete this.activeItem;
2473 this.setActiveItem(t, true);
2481 Roo.log('pass click event');
2485 this.fireEvent("click", this, t, e);
2489 if(!t.href.length || t.href == '#'){
2490 (function() { _this.hide(); }).defer(100);
2495 onMouseOver : function(e){
2496 var t = this.findTargetItem(e);
2499 // if(t.canActivate && !t.disabled){
2500 // this.setActiveItem(t, true);
2504 this.fireEvent("mouseover", this, e, t);
2506 isVisible : function(){
2507 return !this.hidden;
2509 onMouseOut : function(e){
2510 var t = this.findTargetItem(e);
2513 // if(t == this.activeItem && t.shouldDeactivate(e)){
2514 // this.activeItem.deactivate();
2515 // delete this.activeItem;
2518 this.fireEvent("mouseout", this, e, t);
2523 * Displays this menu relative to another element
2524 * @param {String/HTMLElement/Roo.Element} element The element to align to
2525 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2526 * the element (defaults to this.defaultAlign)
2527 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2529 show : function(el, pos, parentMenu)
2531 if (false === this.fireEvent("beforeshow", this)) {
2532 Roo.log("show canceled");
2535 this.parentMenu = parentMenu;
2540 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2543 * Displays this menu at a specific xy position
2544 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2545 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2547 showAt : function(xy, parentMenu, /* private: */_e){
2548 this.parentMenu = parentMenu;
2553 this.fireEvent("beforeshow", this);
2554 //xy = this.el.adjustForConstraints(xy);
2558 this.hideMenuItems();
2559 this.hidden = false;
2560 this.triggerEl.addClass('open');
2561 this.el.addClass('show');
2563 // reassign x when hitting right
2564 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2565 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2568 // reassign y when hitting bottom
2569 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2570 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2573 // but the list may align on trigger left or trigger top... should it be a properity?
2575 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2580 this.fireEvent("show", this);
2586 this.doFocus.defer(50, this);
2590 doFocus : function(){
2592 this.focusEl.focus();
2597 * Hides this menu and optionally all parent menus
2598 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2600 hide : function(deep)
2602 if (false === this.fireEvent("beforehide", this)) {
2603 Roo.log("hide canceled");
2606 this.hideMenuItems();
2607 if(this.el && this.isVisible()){
2609 if(this.activeItem){
2610 this.activeItem.deactivate();
2611 this.activeItem = null;
2613 this.triggerEl.removeClass('open');;
2614 this.el.removeClass('show');
2616 this.fireEvent("hide", this);
2618 if(deep === true && this.parentMenu){
2619 this.parentMenu.hide(true);
2623 onTriggerClick : function(e)
2625 Roo.log('trigger click');
2627 var target = e.getTarget();
2629 Roo.log(target.nodeName.toLowerCase());
2631 if(target.nodeName.toLowerCase() === 'i'){
2637 onTriggerPress : function(e)
2639 Roo.log('trigger press');
2640 //Roo.log(e.getTarget());
2641 // Roo.log(this.triggerEl.dom);
2643 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2644 var pel = Roo.get(e.getTarget());
2645 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2646 Roo.log('is treeview or dropdown?');
2650 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2654 if (this.isVisible()) {
2659 this.show(this.triggerEl, '?', false);
2662 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2669 hideMenuItems : function()
2671 Roo.log("hide Menu Items");
2676 this.el.select('.open',true).each(function(aa) {
2678 aa.removeClass('open');
2682 addxtypeChild : function (tree, cntr) {
2683 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2685 this.menuitems.add(comp);
2697 this.getEl().dom.innerHTML = '';
2698 this.menuitems.clear();
2712 * @class Roo.bootstrap.MenuItem
2713 * @extends Roo.bootstrap.Component
2714 * Bootstrap MenuItem class
2715 * @cfg {String} html the menu label
2716 * @cfg {String} href the link
2717 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2718 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2719 * @cfg {Boolean} active used on sidebars to highlight active itesm
2720 * @cfg {String} fa favicon to show on left of menu item.
2721 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2725 * Create a new MenuItem
2726 * @param {Object} config The config object
2730 Roo.bootstrap.MenuItem = function(config){
2731 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2736 * The raw click event for the entire grid.
2737 * @param {Roo.bootstrap.MenuItem} this
2738 * @param {Roo.EventObject} e
2744 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2748 preventDefault: false,
2749 isContainer : false,
2753 getAutoCreate : function(){
2755 if(this.isContainer){
2758 cls: 'dropdown-menu-item '
2768 cls : 'dropdown-item',
2773 if (this.fa !== false) {
2776 cls : 'fa fa-' + this.fa
2785 cls: 'dropdown-menu-item',
2788 if (this.parent().type == 'treeview') {
2789 cfg.cls = 'treeview-menu';
2792 cfg.cls += ' active';
2797 anc.href = this.href || cfg.cn[0].href ;
2798 ctag.html = this.html || cfg.cn[0].html ;
2802 initEvents: function()
2804 if (this.parent().type == 'treeview') {
2805 this.el.select('a').on('click', this.onClick, this);
2809 this.menu.parentType = this.xtype;
2810 this.menu.triggerEl = this.el;
2811 this.menu = this.addxtype(Roo.apply({}, this.menu));
2815 onClick : function(e)
2817 Roo.log('item on click ');
2819 if(this.preventDefault){
2822 //this.parent().hideMenuItems();
2824 this.fireEvent('click', this, e);
2843 * @class Roo.bootstrap.MenuSeparator
2844 * @extends Roo.bootstrap.Component
2845 * Bootstrap MenuSeparator class
2848 * Create a new MenuItem
2849 * @param {Object} config The config object
2853 Roo.bootstrap.MenuSeparator = function(config){
2854 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2857 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2859 getAutoCreate : function(){
2878 * @class Roo.bootstrap.Modal
2879 * @extends Roo.bootstrap.Component
2880 * Bootstrap Modal class
2881 * @cfg {String} title Title of dialog
2882 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2883 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2884 * @cfg {Boolean} specificTitle default false
2885 * @cfg {Array} buttons Array of buttons or standard button set..
2886 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2887 * @cfg {Boolean} animate default true
2888 * @cfg {Boolean} allow_close default true
2889 * @cfg {Boolean} fitwindow default false
2890 * @cfg {Number} width fixed width - usefull for chrome extension only really.
2891 * @cfg {Number} height fixed height - usefull for chrome extension only really.
2892 * @cfg {String} size (sm|lg) default empty
2893 * @cfg {Number} max_width set the max width of modal
2897 * Create a new Modal Dialog
2898 * @param {Object} config The config object
2901 Roo.bootstrap.Modal = function(config){
2902 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2907 * The raw btnclick event for the button
2908 * @param {Roo.EventObject} e
2913 * Fire when dialog resize
2914 * @param {Roo.bootstrap.Modal} this
2915 * @param {Roo.EventObject} e
2919 this.buttons = this.buttons || [];
2922 this.tmpl = Roo.factory(this.tmpl);
2927 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2929 title : 'test dialog',
2939 specificTitle: false,
2941 buttonPosition: 'right',
2964 onRender : function(ct, position)
2966 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2969 var cfg = Roo.apply({}, this.getAutoCreate());
2972 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2974 //if (!cfg.name.length) {
2978 cfg.cls += ' ' + this.cls;
2981 cfg.style = this.style;
2983 this.el = Roo.get(document.body).createChild(cfg, position);
2985 //var type = this.el.dom.type;
2988 if(this.tabIndex !== undefined){
2989 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2992 this.dialogEl = this.el.select('.modal-dialog',true).first();
2993 this.bodyEl = this.el.select('.modal-body',true).first();
2994 this.closeEl = this.el.select('.modal-header .close', true).first();
2995 this.headerEl = this.el.select('.modal-header',true).first();
2996 this.titleEl = this.el.select('.modal-title',true).first();
2997 this.footerEl = this.el.select('.modal-footer',true).first();
2999 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
3001 //this.el.addClass("x-dlg-modal");
3003 if (this.buttons.length) {
3004 Roo.each(this.buttons, function(bb) {
3005 var b = Roo.apply({}, bb);
3006 b.xns = b.xns || Roo.bootstrap;
3007 b.xtype = b.xtype || 'Button';
3008 if (typeof(b.listeners) == 'undefined') {
3009 b.listeners = { click : this.onButtonClick.createDelegate(this) };
3012 var btn = Roo.factory(b);
3014 btn.render(this.getButtonContainer());
3018 // render the children.
3021 if(typeof(this.items) != 'undefined'){
3022 var items = this.items;
3025 for(var i =0;i < items.length;i++) {
3026 nitems.push(this.addxtype(Roo.apply({}, items[i])));
3030 this.items = nitems;
3032 // where are these used - they used to be body/close/footer
3036 //this.el.addClass([this.fieldClass, this.cls]);
3040 getAutoCreate : function()
3042 // we will default to modal-body-overflow - might need to remove or make optional later.
3044 cls : 'modal-body enable-modal-body-overflow ',
3045 html : this.html || ''
3050 cls : 'modal-title',
3054 if(this.specificTitle){
3060 if (this.allow_close && Roo.bootstrap.version == 3) {
3070 if (this.allow_close && Roo.bootstrap.version == 4) {
3080 if(this.size.length){
3081 size = 'modal-' + this.size;
3084 var footer = Roo.bootstrap.version == 3 ?
3086 cls : 'modal-footer',
3090 cls: 'btn-' + this.buttonPosition
3095 { // BS4 uses mr-auto on left buttons....
3096 cls : 'modal-footer'
3107 cls: "modal-dialog " + size,
3110 cls : "modal-content",
3113 cls : 'modal-header',
3128 modal.cls += ' fade';
3134 getChildContainer : function() {
3139 getButtonContainer : function() {
3141 return Roo.bootstrap.version == 4 ?
3142 this.el.select('.modal-footer',true).first()
3143 : this.el.select('.modal-footer div',true).first();
3146 initEvents : function()
3148 if (this.allow_close) {
3149 this.closeEl.on('click', this.hide, this);
3151 Roo.EventManager.onWindowResize(this.resize, this, true);
3159 this.maskEl.setSize(
3160 Roo.lib.Dom.getViewWidth(true),
3161 Roo.lib.Dom.getViewHeight(true)
3164 if (this.fitwindow) {
3168 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
3169 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
3174 if(this.max_width !== 0) {
3176 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
3179 this.setSize(w, this.height);
3183 if(this.max_height) {
3184 this.setSize(w,Math.min(
3186 Roo.lib.Dom.getViewportHeight(true) - 60
3192 if(!this.fit_content) {
3193 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
3197 this.setSize(w, Math.min(
3199 this.headerEl.getHeight() +
3200 this.footerEl.getHeight() +
3201 this.getChildHeight(this.bodyEl.dom.childNodes),
3202 Roo.lib.Dom.getViewportHeight(true) - 60)
3208 setSize : function(w,h)
3219 if (!this.rendered) {
3223 //this.el.setStyle('display', 'block');
3224 this.el.removeClass('hideing');
3225 this.el.dom.style.display='block';
3227 Roo.get(document.body).addClass('modal-open');
3229 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
3232 this.el.addClass('show');
3233 this.el.addClass('in');
3236 this.el.addClass('show');
3237 this.el.addClass('in');
3240 // not sure how we can show data in here..
3242 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3245 Roo.get(document.body).addClass("x-body-masked");
3247 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3248 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3249 this.maskEl.dom.style.display = 'block';
3250 this.maskEl.addClass('show');
3255 this.fireEvent('show', this);
3257 // set zindex here - otherwise it appears to be ignored...
3258 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3261 this.items.forEach( function(e) {
3262 e.layout ? e.layout() : false;
3270 if(this.fireEvent("beforehide", this) !== false){
3272 this.maskEl.removeClass('show');
3274 this.maskEl.dom.style.display = '';
3275 Roo.get(document.body).removeClass("x-body-masked");
3276 this.el.removeClass('in');
3277 this.el.select('.modal-dialog', true).first().setStyle('transform','');
3279 if(this.animate){ // why
3280 this.el.addClass('hideing');
3281 this.el.removeClass('show');
3283 if (!this.el.hasClass('hideing')) {
3284 return; // it's been shown again...
3287 this.el.dom.style.display='';
3289 Roo.get(document.body).removeClass('modal-open');
3290 this.el.removeClass('hideing');
3294 this.el.removeClass('show');
3295 this.el.dom.style.display='';
3296 Roo.get(document.body).removeClass('modal-open');
3299 this.fireEvent('hide', this);
3302 isVisible : function()
3305 return this.el.hasClass('show') && !this.el.hasClass('hideing');
3309 addButton : function(str, cb)
3313 var b = Roo.apply({}, { html : str } );
3314 b.xns = b.xns || Roo.bootstrap;
3315 b.xtype = b.xtype || 'Button';
3316 if (typeof(b.listeners) == 'undefined') {
3317 b.listeners = { click : cb.createDelegate(this) };
3320 var btn = Roo.factory(b);
3322 btn.render(this.getButtonContainer());
3328 setDefaultButton : function(btn)
3330 //this.el.select('.modal-footer').()
3333 resizeTo: function(w,h)
3335 this.dialogEl.setWidth(w);
3337 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
3339 this.bodyEl.setHeight(h - diff);
3341 this.fireEvent('resize', this);
3344 setContentSize : function(w, h)
3348 onButtonClick: function(btn,e)
3351 this.fireEvent('btnclick', btn.name, e);
3354 * Set the title of the Dialog
3355 * @param {String} str new Title
3357 setTitle: function(str) {
3358 this.titleEl.dom.innerHTML = str;
3361 * Set the body of the Dialog
3362 * @param {String} str new Title
3364 setBody: function(str) {
3365 this.bodyEl.dom.innerHTML = str;
3368 * Set the body of the Dialog using the template
3369 * @param {Obj} data - apply this data to the template and replace the body contents.
3371 applyBody: function(obj)
3374 Roo.log("Error - using apply Body without a template");
3377 this.tmpl.overwrite(this.bodyEl, obj);
3380 getChildHeight : function(child_nodes)
3384 child_nodes.length == 0
3389 var child_height = 0;
3391 for(var i = 0; i < child_nodes.length; i++) {
3394 * for modal with tabs...
3395 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3397 var layout_childs = child_nodes[i].childNodes;
3399 for(var j = 0; j < layout_childs.length; j++) {
3401 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3403 var layout_body_childs = layout_childs[j].childNodes;
3405 for(var k = 0; k < layout_body_childs.length; k++) {
3407 if(layout_body_childs[k].classList.contains('navbar')) {
3408 child_height += layout_body_childs[k].offsetHeight;
3412 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3414 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3416 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3418 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3419 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3434 child_height += child_nodes[i].offsetHeight;
3435 // Roo.log(child_nodes[i].offsetHeight);
3438 return child_height;
3444 Roo.apply(Roo.bootstrap.Modal, {
3446 * Button config that displays a single OK button
3455 * Button config that displays Yes and No buttons
3471 * Button config that displays OK and Cancel buttons
3486 * Button config that displays Yes, No and Cancel buttons
3510 * messagebox - can be used as a replace
3514 * @class Roo.MessageBox
3515 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3519 Roo.Msg.alert('Status', 'Changes saved successfully.');
3521 // Prompt for user data:
3522 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3524 // process text value...
3528 // Show a dialog using config options:
3530 title:'Save Changes?',
3531 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3532 buttons: Roo.Msg.YESNOCANCEL,
3539 Roo.bootstrap.MessageBox = function(){
3540 var dlg, opt, mask, waitTimer;
3541 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3542 var buttons, activeTextEl, bwidth;
3546 var handleButton = function(button){
3548 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3552 var handleHide = function(){
3554 dlg.el.removeClass(opt.cls);
3557 // Roo.TaskMgr.stop(waitTimer);
3558 // waitTimer = null;
3563 var updateButtons = function(b){
3566 buttons["ok"].hide();
3567 buttons["cancel"].hide();
3568 buttons["yes"].hide();
3569 buttons["no"].hide();
3570 dlg.footerEl.hide();
3574 dlg.footerEl.show();
3575 for(var k in buttons){
3576 if(typeof buttons[k] != "function"){
3579 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3580 width += buttons[k].el.getWidth()+15;
3590 var handleEsc = function(d, k, e){
3591 if(opt && opt.closable !== false){
3601 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3602 * @return {Roo.BasicDialog} The BasicDialog element
3604 getDialog : function(){
3606 dlg = new Roo.bootstrap.Modal( {
3609 //constraintoviewport:false,
3611 //collapsible : false,
3616 //buttonAlign:"center",
3617 closeClick : function(){
3618 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3621 handleButton("cancel");
3626 dlg.on("hide", handleHide);
3628 //dlg.addKeyListener(27, handleEsc);
3630 this.buttons = buttons;
3631 var bt = this.buttonText;
3632 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3633 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3634 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3635 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3637 bodyEl = dlg.bodyEl.createChild({
3639 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3640 '<textarea class="roo-mb-textarea"></textarea>' +
3641 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3643 msgEl = bodyEl.dom.firstChild;
3644 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3645 textboxEl.enableDisplayMode();
3646 textboxEl.addKeyListener([10,13], function(){
3647 if(dlg.isVisible() && opt && opt.buttons){
3650 }else if(opt.buttons.yes){
3651 handleButton("yes");
3655 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3656 textareaEl.enableDisplayMode();
3657 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3658 progressEl.enableDisplayMode();
3660 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3661 var pf = progressEl.dom.firstChild;
3663 pp = Roo.get(pf.firstChild);
3664 pp.setHeight(pf.offsetHeight);
3672 * Updates the message box body text
3673 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3674 * the XHTML-compliant non-breaking space character '&#160;')
3675 * @return {Roo.MessageBox} This message box
3677 updateText : function(text)
3679 if(!dlg.isVisible() && !opt.width){
3680 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3681 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3683 msgEl.innerHTML = text || ' ';
3685 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3686 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3688 Math.min(opt.width || cw , this.maxWidth),
3689 Math.max(opt.minWidth || this.minWidth, bwidth)
3692 activeTextEl.setWidth(w);
3694 if(dlg.isVisible()){
3695 dlg.fixedcenter = false;
3697 // to big, make it scroll. = But as usual stupid IE does not support
3700 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3701 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3702 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3704 bodyEl.dom.style.height = '';
3705 bodyEl.dom.style.overflowY = '';
3708 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3710 bodyEl.dom.style.overflowX = '';
3713 dlg.setContentSize(w, bodyEl.getHeight());
3714 if(dlg.isVisible()){
3715 dlg.fixedcenter = true;
3721 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3722 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3723 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3724 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3725 * @return {Roo.MessageBox} This message box
3727 updateProgress : function(value, text){
3729 this.updateText(text);
3732 if (pp) { // weird bug on my firefox - for some reason this is not defined
3733 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3734 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3740 * Returns true if the message box is currently displayed
3741 * @return {Boolean} True if the message box is visible, else false
3743 isVisible : function(){
3744 return dlg && dlg.isVisible();
3748 * Hides the message box if it is displayed
3751 if(this.isVisible()){
3757 * Displays a new message box, or reinitializes an existing message box, based on the config options
3758 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3759 * The following config object properties are supported:
3761 Property Type Description
3762 ---------- --------------- ------------------------------------------------------------------------------------
3763 animEl String/Element An id or Element from which the message box should animate as it opens and
3764 closes (defaults to undefined)
3765 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3766 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3767 closable Boolean False to hide the top-right close button (defaults to true). Note that
3768 progress and wait dialogs will ignore this property and always hide the
3769 close button as they can only be closed programmatically.
3770 cls String A custom CSS class to apply to the message box element
3771 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3772 displayed (defaults to 75)
3773 fn Function A callback function to execute after closing the dialog. The arguments to the
3774 function will be btn (the name of the button that was clicked, if applicable,
3775 e.g. "ok"), and text (the value of the active text field, if applicable).
3776 Progress and wait dialogs will ignore this option since they do not respond to
3777 user actions and can only be closed programmatically, so any required function
3778 should be called by the same code after it closes the dialog.
3779 icon String A CSS class that provides a background image to be used as an icon for
3780 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3781 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3782 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3783 modal Boolean False to allow user interaction with the page while the message box is
3784 displayed (defaults to true)
3785 msg String A string that will replace the existing message box body text (defaults
3786 to the XHTML-compliant non-breaking space character ' ')
3787 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3788 progress Boolean True to display a progress bar (defaults to false)
3789 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3790 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3791 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3792 title String The title text
3793 value String The string value to set into the active textbox element if displayed
3794 wait Boolean True to display a progress bar (defaults to false)
3795 width Number The width of the dialog in pixels
3802 msg: 'Please enter your address:',
3804 buttons: Roo.MessageBox.OKCANCEL,
3807 animEl: 'addAddressBtn'
3810 * @param {Object} config Configuration options
3811 * @return {Roo.MessageBox} This message box
3813 show : function(options)
3816 // this causes nightmares if you show one dialog after another
3817 // especially on callbacks..
3819 if(this.isVisible()){
3822 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3823 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3824 Roo.log("New Dialog Message:" + options.msg )
3825 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3826 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3829 var d = this.getDialog();
3831 d.setTitle(opt.title || " ");
3832 d.closeEl.setDisplayed(opt.closable !== false);
3833 activeTextEl = textboxEl;
3834 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3839 textareaEl.setHeight(typeof opt.multiline == "number" ?
3840 opt.multiline : this.defaultTextHeight);
3841 activeTextEl = textareaEl;
3850 progressEl.setDisplayed(opt.progress === true);
3852 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3854 this.updateProgress(0);
3855 activeTextEl.dom.value = opt.value || "";
3857 dlg.setDefaultButton(activeTextEl);
3859 var bs = opt.buttons;
3863 }else if(bs && bs.yes){
3864 db = buttons["yes"];
3866 dlg.setDefaultButton(db);
3868 bwidth = updateButtons(opt.buttons);
3869 this.updateText(opt.msg);
3871 d.el.addClass(opt.cls);
3873 d.proxyDrag = opt.proxyDrag === true;
3874 d.modal = opt.modal !== false;
3875 d.mask = opt.modal !== false ? mask : false;
3877 // force it to the end of the z-index stack so it gets a cursor in FF
3878 document.body.appendChild(dlg.el.dom);
3879 d.animateTarget = null;
3880 d.show(options.animEl);
3886 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3887 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3888 * and closing the message box when the process is complete.
3889 * @param {String} title The title bar text
3890 * @param {String} msg The message box body text
3891 * @return {Roo.MessageBox} This message box
3893 progress : function(title, msg){
3900 minWidth: this.minProgressWidth,
3907 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3908 * If a callback function is passed it will be called after the user clicks the button, and the
3909 * id of the button that was clicked will be passed as the only parameter to the callback
3910 * (could also be the top-right close button).
3911 * @param {String} title The title bar text
3912 * @param {String} msg The message box body text
3913 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3914 * @param {Object} scope (optional) The scope of the callback function
3915 * @return {Roo.MessageBox} This message box
3917 alert : function(title, msg, fn, scope)
3932 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3933 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3934 * You are responsible for closing the message box when the process is complete.
3935 * @param {String} msg The message box body text
3936 * @param {String} title (optional) The title bar text
3937 * @return {Roo.MessageBox} This message box
3939 wait : function(msg, title){
3950 waitTimer = Roo.TaskMgr.start({
3952 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3960 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3961 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3962 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3963 * @param {String} title The title bar text
3964 * @param {String} msg The message box body text
3965 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3966 * @param {Object} scope (optional) The scope of the callback function
3967 * @return {Roo.MessageBox} This message box
3969 confirm : function(title, msg, fn, scope){
3973 buttons: this.YESNO,
3982 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3983 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3984 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3985 * (could also be the top-right close button) and the text that was entered will be passed as the two
3986 * parameters to the callback.
3987 * @param {String} title The title bar text
3988 * @param {String} msg The message box body text
3989 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3990 * @param {Object} scope (optional) The scope of the callback function
3991 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3992 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3993 * @return {Roo.MessageBox} This message box
3995 prompt : function(title, msg, fn, scope, multiline){
3999 buttons: this.OKCANCEL,
4004 multiline: multiline,
4011 * Button config that displays a single OK button
4016 * Button config that displays Yes and No buttons
4019 YESNO : {yes:true, no:true},
4021 * Button config that displays OK and Cancel buttons
4024 OKCANCEL : {ok:true, cancel:true},
4026 * Button config that displays Yes, No and Cancel buttons
4029 YESNOCANCEL : {yes:true, no:true, cancel:true},
4032 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
4035 defaultTextHeight : 75,
4037 * The maximum width in pixels of the message box (defaults to 600)
4042 * The minimum width in pixels of the message box (defaults to 100)
4047 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
4048 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
4051 minProgressWidth : 250,
4053 * An object containing the default button text strings that can be overriden for localized language support.
4054 * Supported properties are: ok, cancel, yes and no.
4055 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
4068 * Shorthand for {@link Roo.MessageBox}
4070 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
4071 Roo.Msg = Roo.Msg || Roo.MessageBox;
4080 * @class Roo.bootstrap.Navbar
4081 * @extends Roo.bootstrap.Component
4082 * Bootstrap Navbar class
4085 * Create a new Navbar
4086 * @param {Object} config The config object
4090 Roo.bootstrap.Navbar = function(config){
4091 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
4095 * @event beforetoggle
4096 * Fire before toggle the menu
4097 * @param {Roo.EventObject} e
4099 "beforetoggle" : true
4103 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
4112 getAutoCreate : function(){
4115 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
4119 initEvents :function ()
4121 //Roo.log(this.el.select('.navbar-toggle',true));
4122 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
4129 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
4131 var size = this.el.getSize();
4132 this.maskEl.setSize(size.width, size.height);
4133 this.maskEl.enableDisplayMode("block");
4142 getChildContainer : function()
4144 if (this.el && this.el.select('.collapse').getCount()) {
4145 return this.el.select('.collapse',true).first();
4160 onToggle : function()
4163 if(this.fireEvent('beforetoggle', this) === false){
4166 var ce = this.el.select('.navbar-collapse',true).first();
4168 if (!ce.hasClass('show')) {
4178 * Expand the navbar pulldown
4180 expand : function ()
4183 var ce = this.el.select('.navbar-collapse',true).first();
4184 if (ce.hasClass('collapsing')) {
4187 ce.dom.style.height = '';
4189 ce.addClass('in'); // old...
4190 ce.removeClass('collapse');
4191 ce.addClass('show');
4192 var h = ce.getHeight();
4194 ce.removeClass('show');
4195 // at this point we should be able to see it..
4196 ce.addClass('collapsing');
4198 ce.setHeight(0); // resize it ...
4199 ce.on('transitionend', function() {
4200 //Roo.log('done transition');
4201 ce.removeClass('collapsing');
4202 ce.addClass('show');
4203 ce.removeClass('collapse');
4205 ce.dom.style.height = '';
4206 }, this, { single: true} );
4208 ce.dom.scrollTop = 0;
4211 * Collapse the navbar pulldown
4213 collapse : function()
4215 var ce = this.el.select('.navbar-collapse',true).first();
4217 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
4218 // it's collapsed or collapsing..
4221 ce.removeClass('in'); // old...
4222 ce.setHeight(ce.getHeight());
4223 ce.removeClass('show');
4224 ce.addClass('collapsing');
4226 ce.on('transitionend', function() {
4227 ce.dom.style.height = '';
4228 ce.removeClass('collapsing');
4229 ce.addClass('collapse');
4230 }, this, { single: true} );
4250 * @class Roo.bootstrap.NavSimplebar
4251 * @extends Roo.bootstrap.Navbar
4252 * Bootstrap Sidebar class
4254 * @cfg {Boolean} inverse is inverted color
4256 * @cfg {String} type (nav | pills | tabs)
4257 * @cfg {Boolean} arrangement stacked | justified
4258 * @cfg {String} align (left | right) alignment
4260 * @cfg {Boolean} main (true|false) main nav bar? default false
4261 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4263 * @cfg {String} tag (header|footer|nav|div) default is nav
4265 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4269 * Create a new Sidebar
4270 * @param {Object} config The config object
4274 Roo.bootstrap.NavSimplebar = function(config){
4275 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4278 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
4294 getAutoCreate : function(){
4298 tag : this.tag || 'div',
4299 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
4301 if (['light','white'].indexOf(this.weight) > -1) {
4302 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4304 cfg.cls += ' bg-' + this.weight;
4307 cfg.cls += ' navbar-inverse';
4311 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4313 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
4322 cls: 'nav nav-' + this.xtype,
4328 this.type = this.type || 'nav';
4329 if (['tabs','pills'].indexOf(this.type) != -1) {
4330 cfg.cn[0].cls += ' nav-' + this.type
4334 if (this.type!=='nav') {
4335 Roo.log('nav type must be nav/tabs/pills')
4337 cfg.cn[0].cls += ' navbar-nav'
4343 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4344 cfg.cn[0].cls += ' nav-' + this.arrangement;
4348 if (this.align === 'right') {
4349 cfg.cn[0].cls += ' navbar-right';
4374 * navbar-expand-md fixed-top
4378 * @class Roo.bootstrap.NavHeaderbar
4379 * @extends Roo.bootstrap.NavSimplebar
4380 * Bootstrap Sidebar class
4382 * @cfg {String} brand what is brand
4383 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4384 * @cfg {String} brand_href href of the brand
4385 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
4386 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4387 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4388 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4391 * Create a new Sidebar
4392 * @param {Object} config The config object
4396 Roo.bootstrap.NavHeaderbar = function(config){
4397 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4401 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
4408 desktopCenter : false,
4411 getAutoCreate : function(){
4414 tag: this.nav || 'nav',
4415 cls: 'navbar navbar-expand-md',
4421 if (this.desktopCenter) {
4422 cn.push({cls : 'container', cn : []});
4430 cls: 'navbar-toggle navbar-toggler',
4431 'data-toggle': 'collapse',
4436 html: 'Toggle navigation'
4440 cls: 'icon-bar navbar-toggler-icon'
4453 cn.push( Roo.bootstrap.version == 4 ? btn : {
4455 cls: 'navbar-header',
4464 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
4468 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4470 if (['light','white'].indexOf(this.weight) > -1) {
4471 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4473 cfg.cls += ' bg-' + this.weight;
4476 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4477 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4479 // tag can override this..
4481 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4484 if (this.brand !== '') {
4485 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4486 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4488 href: this.brand_href ? this.brand_href : '#',
4489 cls: 'navbar-brand',
4497 cfg.cls += ' main-nav';
4505 getHeaderChildContainer : function()
4507 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4508 return this.el.select('.navbar-header',true).first();
4511 return this.getChildContainer();
4515 initEvents : function()
4517 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4519 if (this.autohide) {
4524 Roo.get(document).on('scroll',function(e) {
4525 var ns = Roo.get(document).getScroll().top;
4526 var os = prevScroll;
4530 ft.removeClass('slideDown');
4531 ft.addClass('slideUp');
4534 ft.removeClass('slideUp');
4535 ft.addClass('slideDown');
4556 * @class Roo.bootstrap.NavSidebar
4557 * @extends Roo.bootstrap.Navbar
4558 * Bootstrap Sidebar class
4561 * Create a new Sidebar
4562 * @param {Object} config The config object
4566 Roo.bootstrap.NavSidebar = function(config){
4567 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4570 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4572 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4574 getAutoCreate : function(){
4579 cls: 'sidebar sidebar-nav'
4601 * @class Roo.bootstrap.NavGroup
4602 * @extends Roo.bootstrap.Component
4603 * Bootstrap NavGroup class
4604 * @cfg {String} align (left|right)
4605 * @cfg {Boolean} inverse
4606 * @cfg {String} type (nav|pills|tab) default nav
4607 * @cfg {String} navId - reference Id for navbar.
4611 * Create a new nav group
4612 * @param {Object} config The config object
4615 Roo.bootstrap.NavGroup = function(config){
4616 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4619 Roo.bootstrap.NavGroup.register(this);
4623 * Fires when the active item changes
4624 * @param {Roo.bootstrap.NavGroup} this
4625 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4626 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4633 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4644 getAutoCreate : function()
4646 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4652 if (Roo.bootstrap.version == 4) {
4653 if (['tabs','pills'].indexOf(this.type) != -1) {
4654 cfg.cls += ' nav-' + this.type;
4656 // trying to remove so header bar can right align top?
4657 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
4658 // do not use on header bar...
4659 cfg.cls += ' navbar-nav';
4664 if (['tabs','pills'].indexOf(this.type) != -1) {
4665 cfg.cls += ' nav-' + this.type
4667 if (this.type !== 'nav') {
4668 Roo.log('nav type must be nav/tabs/pills')
4670 cfg.cls += ' navbar-nav'
4674 if (this.parent() && this.parent().sidebar) {
4677 cls: 'dashboard-menu sidebar-menu'
4683 if (this.form === true) {
4686 cls: 'navbar-form form-inline'
4688 //nav navbar-right ml-md-auto
4689 if (this.align === 'right') {
4690 cfg.cls += ' navbar-right ml-md-auto';
4692 cfg.cls += ' navbar-left';
4696 if (this.align === 'right') {
4697 cfg.cls += ' navbar-right ml-md-auto';
4699 cfg.cls += ' mr-auto';
4703 cfg.cls += ' navbar-inverse';
4711 * sets the active Navigation item
4712 * @param {Roo.bootstrap.NavItem} the new current navitem
4714 setActiveItem : function(item)
4717 Roo.each(this.navItems, function(v){
4722 v.setActive(false, true);
4729 item.setActive(true, true);
4730 this.fireEvent('changed', this, item, prev);
4735 * gets the active Navigation item
4736 * @return {Roo.bootstrap.NavItem} the current navitem
4738 getActive : function()
4742 Roo.each(this.navItems, function(v){
4753 indexOfNav : function()
4757 Roo.each(this.navItems, function(v,i){
4768 * adds a Navigation item
4769 * @param {Roo.bootstrap.NavItem} the navitem to add
4771 addItem : function(cfg)
4773 if (this.form && Roo.bootstrap.version == 4) {
4776 var cn = new Roo.bootstrap.NavItem(cfg);
4778 cn.parentId = this.id;
4779 cn.onRender(this.el, null);
4783 * register a Navigation item
4784 * @param {Roo.bootstrap.NavItem} the navitem to add
4786 register : function(item)
4788 this.navItems.push( item);
4789 item.navId = this.navId;
4794 * clear all the Navigation item
4797 clearAll : function()
4800 this.el.dom.innerHTML = '';
4803 getNavItem: function(tabId)
4806 Roo.each(this.navItems, function(e) {
4807 if (e.tabId == tabId) {
4817 setActiveNext : function()
4819 var i = this.indexOfNav(this.getActive());
4820 if (i > this.navItems.length) {
4823 this.setActiveItem(this.navItems[i+1]);
4825 setActivePrev : function()
4827 var i = this.indexOfNav(this.getActive());
4831 this.setActiveItem(this.navItems[i-1]);
4833 clearWasActive : function(except) {
4834 Roo.each(this.navItems, function(e) {
4835 if (e.tabId != except.tabId && e.was_active) {
4836 e.was_active = false;
4843 getWasActive : function ()
4846 Roo.each(this.navItems, function(e) {
4861 Roo.apply(Roo.bootstrap.NavGroup, {
4865 * register a Navigation Group
4866 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4868 register : function(navgrp)
4870 this.groups[navgrp.navId] = navgrp;
4874 * fetch a Navigation Group based on the navigation ID
4875 * @param {string} the navgroup to add
4876 * @returns {Roo.bootstrap.NavGroup} the navgroup
4878 get: function(navId) {
4879 if (typeof(this.groups[navId]) == 'undefined') {
4881 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4883 return this.groups[navId] ;
4898 * @class Roo.bootstrap.NavItem
4899 * @extends Roo.bootstrap.Component
4900 * Bootstrap Navbar.NavItem class
4901 * @cfg {String} href link to
4902 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4904 * @cfg {String} html content of button
4905 * @cfg {String} badge text inside badge
4906 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4907 * @cfg {String} glyphicon DEPRICATED - use fa
4908 * @cfg {String} icon DEPRICATED - use fa
4909 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4910 * @cfg {Boolean} active Is item active
4911 * @cfg {Boolean} disabled Is item disabled
4913 * @cfg {Boolean} preventDefault (true | false) default false
4914 * @cfg {String} tabId the tab that this item activates.
4915 * @cfg {String} tagtype (a|span) render as a href or span?
4916 * @cfg {Boolean} animateRef (true|false) link to element default false
4919 * Create a new Navbar Item
4920 * @param {Object} config The config object
4922 Roo.bootstrap.NavItem = function(config){
4923 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4928 * The raw click event for the entire grid.
4929 * @param {Roo.EventObject} e
4934 * Fires when the active item active state changes
4935 * @param {Roo.bootstrap.NavItem} this
4936 * @param {boolean} state the new state
4942 * Fires when scroll to element
4943 * @param {Roo.bootstrap.NavItem} this
4944 * @param {Object} options
4945 * @param {Roo.EventObject} e
4953 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4962 preventDefault : false,
4970 button_outline : false,
4974 getAutoCreate : function(){
4982 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4984 if (this.disabled) {
4985 cfg.cls += ' disabled';
4989 if (this.button_weight.length) {
4990 cfg.tag = this.href ? 'a' : 'button';
4991 cfg.html = this.html || '';
4992 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
4994 cfg.href = this.href;
4997 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
5000 // menu .. should add dropdown-menu class - so no need for carat..
5002 if (this.badge !== '') {
5004 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5009 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
5013 href : this.href || "#",
5014 html: this.html || ''
5017 if (this.tagtype == 'a') {
5018 cfg.cn[0].cls = 'nav-link';
5021 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
5024 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
5026 if(this.glyphicon) {
5027 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
5032 cfg.cn[0].html += " <span class='caret'></span>";
5036 if (this.badge !== '') {
5038 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5046 onRender : function(ct, position)
5048 // Roo.log("Call onRender: " + this.xtype);
5049 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
5053 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
5054 this.navLink = this.el.select('.nav-link',true).first();
5059 initEvents: function()
5061 if (typeof (this.menu) != 'undefined') {
5062 this.menu.parentType = this.xtype;
5063 this.menu.triggerEl = this.el;
5064 this.menu = this.addxtype(Roo.apply({}, this.menu));
5067 this.el.select('a',true).on('click', this.onClick, this);
5069 if(this.tagtype == 'span'){
5070 this.el.select('span',true).on('click', this.onClick, this);
5073 // at this point parent should be available..
5074 this.parent().register(this);
5077 onClick : function(e)
5079 if (e.getTarget('.dropdown-menu-item')) {
5080 // did you click on a menu itemm.... - then don't trigger onclick..
5085 this.preventDefault ||
5088 Roo.log("NavItem - prevent Default?");
5092 if (this.disabled) {
5096 var tg = Roo.bootstrap.TabGroup.get(this.navId);
5097 if (tg && tg.transition) {
5098 Roo.log("waiting for the transitionend");
5104 //Roo.log("fire event clicked");
5105 if(this.fireEvent('click', this, e) === false){
5109 if(this.tagtype == 'span'){
5113 //Roo.log(this.href);
5114 var ael = this.el.select('a',true).first();
5117 if(ael && this.animateRef && this.href.indexOf('#') > -1){
5118 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
5119 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
5120 return; // ignore... - it's a 'hash' to another page.
5122 Roo.log("NavItem - prevent Default?");
5124 this.scrollToElement(e);
5128 var p = this.parent();
5130 if (['tabs','pills'].indexOf(p.type)!==-1) {
5131 if (typeof(p.setActiveItem) !== 'undefined') {
5132 p.setActiveItem(this);
5136 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
5137 if (p.parentType == 'NavHeaderbar' && !this.menu) {
5138 // remove the collapsed menu expand...
5139 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
5143 isActive: function () {
5146 setActive : function(state, fire, is_was_active)
5148 if (this.active && !state && this.navId) {
5149 this.was_active = true;
5150 var nv = Roo.bootstrap.NavGroup.get(this.navId);
5152 nv.clearWasActive(this);
5156 this.active = state;
5159 this.el.removeClass('active');
5160 this.navLink ? this.navLink.removeClass('active') : false;
5161 } else if (!this.el.hasClass('active')) {
5163 this.el.addClass('active');
5164 if (Roo.bootstrap.version == 4 && this.navLink ) {
5165 this.navLink.addClass('active');
5170 this.fireEvent('changed', this, state);
5173 // show a panel if it's registered and related..
5175 if (!this.navId || !this.tabId || !state || is_was_active) {
5179 var tg = Roo.bootstrap.TabGroup.get(this.navId);
5183 var pan = tg.getPanelByName(this.tabId);
5187 // if we can not flip to new panel - go back to old nav highlight..
5188 if (false == tg.showPanel(pan)) {
5189 var nv = Roo.bootstrap.NavGroup.get(this.navId);
5191 var onav = nv.getWasActive();
5193 onav.setActive(true, false, true);
5202 // this should not be here...
5203 setDisabled : function(state)
5205 this.disabled = state;
5207 this.el.removeClass('disabled');
5208 } else if (!this.el.hasClass('disabled')) {
5209 this.el.addClass('disabled');
5215 * Fetch the element to display the tooltip on.
5216 * @return {Roo.Element} defaults to this.el
5218 tooltipEl : function()
5220 return this.el.select('' + this.tagtype + '', true).first();
5223 scrollToElement : function(e)
5225 var c = document.body;
5228 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
5230 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
5231 c = document.documentElement;
5234 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
5240 var o = target.calcOffsetsTo(c);
5247 this.fireEvent('scrollto', this, options, e);
5249 Roo.get(c).scrollTo('top', options.value, true);
5262 * <span> icon </span>
5263 * <span> text </span>
5264 * <span>badge </span>
5268 * @class Roo.bootstrap.NavSidebarItem
5269 * @extends Roo.bootstrap.NavItem
5270 * Bootstrap Navbar.NavSidebarItem class
5271 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5272 * {Boolean} open is the menu open
5273 * {Boolean} buttonView use button as the tigger el rather that a (default false)
5274 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5275 * {String} buttonSize (sm|md|lg)the extra classes for the button
5276 * {Boolean} showArrow show arrow next to the text (default true)
5278 * Create a new Navbar Button
5279 * @param {Object} config The config object
5281 Roo.bootstrap.NavSidebarItem = function(config){
5282 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5287 * The raw click event for the entire grid.
5288 * @param {Roo.EventObject} e
5293 * Fires when the active item active state changes
5294 * @param {Roo.bootstrap.NavSidebarItem} this
5295 * @param {boolean} state the new state
5303 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
5305 badgeWeight : 'default',
5311 buttonWeight : 'default',
5317 getAutoCreate : function(){
5322 href : this.href || '#',
5328 if(this.buttonView){
5331 href : this.href || '#',
5332 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5345 cfg.cls += ' active';
5348 if (this.disabled) {
5349 cfg.cls += ' disabled';
5352 cfg.cls += ' open x-open';
5355 if (this.glyphicon || this.icon) {
5356 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
5357 a.cn.push({ tag : 'i', cls : c }) ;
5360 if(!this.buttonView){
5363 html : this.html || ''
5370 if (this.badge !== '') {
5371 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
5377 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5380 a.cls += ' dropdown-toggle treeview' ;
5386 initEvents : function()
5388 if (typeof (this.menu) != 'undefined') {
5389 this.menu.parentType = this.xtype;
5390 this.menu.triggerEl = this.el;
5391 this.menu = this.addxtype(Roo.apply({}, this.menu));
5394 this.el.on('click', this.onClick, this);
5396 if(this.badge !== ''){
5397 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5402 onClick : function(e)
5409 if(this.preventDefault){
5413 this.fireEvent('click', this, e);
5416 disable : function()
5418 this.setDisabled(true);
5423 this.setDisabled(false);
5426 setDisabled : function(state)
5428 if(this.disabled == state){
5432 this.disabled = state;
5435 this.el.addClass('disabled');
5439 this.el.removeClass('disabled');
5444 setActive : function(state)
5446 if(this.active == state){
5450 this.active = state;
5453 this.el.addClass('active');
5457 this.el.removeClass('active');
5462 isActive: function ()
5467 setBadge : function(str)
5473 this.badgeEl.dom.innerHTML = str;
5490 * @class Roo.bootstrap.Row
5491 * @extends Roo.bootstrap.Component
5492 * Bootstrap Row class (contains columns...)
5496 * @param {Object} config The config object
5499 Roo.bootstrap.Row = function(config){
5500 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5503 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5505 getAutoCreate : function(){
5524 * @class Roo.bootstrap.Element
5525 * @extends Roo.bootstrap.Component
5526 * Bootstrap Element class
5527 * @cfg {String} html contents of the element
5528 * @cfg {String} tag tag of the element
5529 * @cfg {String} cls class of the element
5530 * @cfg {Boolean} preventDefault (true|false) default false
5531 * @cfg {Boolean} clickable (true|false) default false
5534 * Create a new Element
5535 * @param {Object} config The config object
5538 Roo.bootstrap.Element = function(config){
5539 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5545 * When a element is chick
5546 * @param {Roo.bootstrap.Element} this
5547 * @param {Roo.EventObject} e
5553 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5558 preventDefault: false,
5561 getAutoCreate : function(){
5565 // cls: this.cls, double assign in parent class Component.js :: onRender
5572 initEvents: function()
5574 Roo.bootstrap.Element.superclass.initEvents.call(this);
5577 this.el.on('click', this.onClick, this);
5582 onClick : function(e)
5584 if(this.preventDefault){
5588 this.fireEvent('click', this, e);
5591 getValue : function()
5593 return this.el.dom.innerHTML;
5596 setValue : function(value)
5598 this.el.dom.innerHTML = value;
5613 * @class Roo.bootstrap.Pagination
5614 * @extends Roo.bootstrap.Component
5615 * Bootstrap Pagination class
5616 * @cfg {String} size xs | sm | md | lg
5617 * @cfg {Boolean} inverse false | true
5620 * Create a new Pagination
5621 * @param {Object} config The config object
5624 Roo.bootstrap.Pagination = function(config){
5625 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5628 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5634 getAutoCreate : function(){
5640 cfg.cls += ' inverse';
5646 cfg.cls += " " + this.cls;
5664 * @class Roo.bootstrap.PaginationItem
5665 * @extends Roo.bootstrap.Component
5666 * Bootstrap PaginationItem class
5667 * @cfg {String} html text
5668 * @cfg {String} href the link
5669 * @cfg {Boolean} preventDefault (true | false) default true
5670 * @cfg {Boolean} active (true | false) default false
5671 * @cfg {Boolean} disabled default false
5675 * Create a new PaginationItem
5676 * @param {Object} config The config object
5680 Roo.bootstrap.PaginationItem = function(config){
5681 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5686 * The raw click event for the entire grid.
5687 * @param {Roo.EventObject} e
5693 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5697 preventDefault: true,
5702 getAutoCreate : function(){
5708 href : this.href ? this.href : '#',
5709 html : this.html ? this.html : ''
5719 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5723 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5729 initEvents: function() {
5731 this.el.on('click', this.onClick, this);
5734 onClick : function(e)
5736 Roo.log('PaginationItem on click ');
5737 if(this.preventDefault){
5745 this.fireEvent('click', this, e);
5761 * @class Roo.bootstrap.Slider
5762 * @extends Roo.bootstrap.Component
5763 * Bootstrap Slider class
5766 * Create a new Slider
5767 * @param {Object} config The config object
5770 Roo.bootstrap.Slider = function(config){
5771 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5774 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5776 getAutoCreate : function(){
5780 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5784 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5796 * Ext JS Library 1.1.1
5797 * Copyright(c) 2006-2007, Ext JS, LLC.
5799 * Originally Released Under LGPL - original licence link has changed is not relivant.
5802 * <script type="text/javascript">
5807 * @class Roo.grid.ColumnModel
5808 * @extends Roo.util.Observable
5809 * This is the default implementation of a ColumnModel used by the Grid. It defines
5810 * the columns in the grid.
5813 var colModel = new Roo.grid.ColumnModel([
5814 {header: "Ticker", width: 60, sortable: true, locked: true},
5815 {header: "Company Name", width: 150, sortable: true},
5816 {header: "Market Cap.", width: 100, sortable: true},
5817 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5818 {header: "Employees", width: 100, sortable: true, resizable: false}
5823 * The config options listed for this class are options which may appear in each
5824 * individual column definition.
5825 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5827 * @param {Object} config An Array of column config objects. See this class's
5828 * config objects for details.
5830 Roo.grid.ColumnModel = function(config){
5832 * The config passed into the constructor
5834 this.config = config;
5837 // if no id, create one
5838 // if the column does not have a dataIndex mapping,
5839 // map it to the order it is in the config
5840 for(var i = 0, len = config.length; i < len; i++){
5842 if(typeof c.dataIndex == "undefined"){
5845 if(typeof c.renderer == "string"){
5846 c.renderer = Roo.util.Format[c.renderer];
5848 if(typeof c.id == "undefined"){
5851 if(c.editor && c.editor.xtype){
5852 c.editor = Roo.factory(c.editor, Roo.grid);
5854 if(c.editor && c.editor.isFormField){
5855 c.editor = new Roo.grid.GridEditor(c.editor);
5857 this.lookup[c.id] = c;
5861 * The width of columns which have no width specified (defaults to 100)
5864 this.defaultWidth = 100;
5867 * Default sortable of columns which have no sortable specified (defaults to false)
5870 this.defaultSortable = false;
5874 * @event widthchange
5875 * Fires when the width of a column changes.
5876 * @param {ColumnModel} this
5877 * @param {Number} columnIndex The column index
5878 * @param {Number} newWidth The new width
5880 "widthchange": true,
5882 * @event headerchange
5883 * Fires when the text of a header changes.
5884 * @param {ColumnModel} this
5885 * @param {Number} columnIndex The column index
5886 * @param {Number} newText The new header text
5888 "headerchange": true,
5890 * @event hiddenchange
5891 * Fires when a column is hidden or "unhidden".
5892 * @param {ColumnModel} this
5893 * @param {Number} columnIndex The column index
5894 * @param {Boolean} hidden true if hidden, false otherwise
5896 "hiddenchange": true,
5898 * @event columnmoved
5899 * Fires when a column is moved.
5900 * @param {ColumnModel} this
5901 * @param {Number} oldIndex
5902 * @param {Number} newIndex
5904 "columnmoved" : true,
5906 * @event columlockchange
5907 * Fires when a column's locked state is changed
5908 * @param {ColumnModel} this
5909 * @param {Number} colIndex
5910 * @param {Boolean} locked true if locked
5912 "columnlockchange" : true
5914 Roo.grid.ColumnModel.superclass.constructor.call(this);
5916 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5918 * @cfg {String} header The header text to display in the Grid view.
5921 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5922 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5923 * specified, the column's index is used as an index into the Record's data Array.
5926 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5927 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5930 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5931 * Defaults to the value of the {@link #defaultSortable} property.
5932 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5935 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5938 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5941 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5944 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5947 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5948 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5949 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5950 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5953 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5956 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5959 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5962 * @cfg {String} cursor (Optional)
5965 * @cfg {String} tooltip (Optional)
5968 * @cfg {Number} xs (Optional)
5971 * @cfg {Number} sm (Optional)
5974 * @cfg {Number} md (Optional)
5977 * @cfg {Number} lg (Optional)
5980 * Returns the id of the column at the specified index.
5981 * @param {Number} index The column index
5982 * @return {String} the id
5984 getColumnId : function(index){
5985 return this.config[index].id;
5989 * Returns the column for a specified id.
5990 * @param {String} id The column id
5991 * @return {Object} the column
5993 getColumnById : function(id){
5994 return this.lookup[id];
5999 * Returns the column for a specified dataIndex.
6000 * @param {String} dataIndex The column dataIndex
6001 * @return {Object|Boolean} the column or false if not found
6003 getColumnByDataIndex: function(dataIndex){
6004 var index = this.findColumnIndex(dataIndex);
6005 return index > -1 ? this.config[index] : false;
6009 * Returns the index for a specified column id.
6010 * @param {String} id The column id
6011 * @return {Number} the index, or -1 if not found
6013 getIndexById : function(id){
6014 for(var i = 0, len = this.config.length; i < len; i++){
6015 if(this.config[i].id == id){
6023 * Returns the index for a specified column dataIndex.
6024 * @param {String} dataIndex The column dataIndex
6025 * @return {Number} the index, or -1 if not found
6028 findColumnIndex : function(dataIndex){
6029 for(var i = 0, len = this.config.length; i < len; i++){
6030 if(this.config[i].dataIndex == dataIndex){
6038 moveColumn : function(oldIndex, newIndex){
6039 var c = this.config[oldIndex];
6040 this.config.splice(oldIndex, 1);
6041 this.config.splice(newIndex, 0, c);
6042 this.dataMap = null;
6043 this.fireEvent("columnmoved", this, oldIndex, newIndex);
6046 isLocked : function(colIndex){
6047 return this.config[colIndex].locked === true;
6050 setLocked : function(colIndex, value, suppressEvent){
6051 if(this.isLocked(colIndex) == value){
6054 this.config[colIndex].locked = value;
6056 this.fireEvent("columnlockchange", this, colIndex, value);
6060 getTotalLockedWidth : function(){
6062 for(var i = 0; i < this.config.length; i++){
6063 if(this.isLocked(i) && !this.isHidden(i)){
6064 this.totalWidth += this.getColumnWidth(i);
6070 getLockedCount : function(){
6071 for(var i = 0, len = this.config.length; i < len; i++){
6072 if(!this.isLocked(i)){
6077 return this.config.length;
6081 * Returns the number of columns.
6084 getColumnCount : function(visibleOnly){
6085 if(visibleOnly === true){
6087 for(var i = 0, len = this.config.length; i < len; i++){
6088 if(!this.isHidden(i)){
6094 return this.config.length;
6098 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
6099 * @param {Function} fn
6100 * @param {Object} scope (optional)
6101 * @return {Array} result
6103 getColumnsBy : function(fn, scope){
6105 for(var i = 0, len = this.config.length; i < len; i++){
6106 var c = this.config[i];
6107 if(fn.call(scope||this, c, i) === true){
6115 * Returns true if the specified column is sortable.
6116 * @param {Number} col The column index
6119 isSortable : function(col){
6120 if(typeof this.config[col].sortable == "undefined"){
6121 return this.defaultSortable;
6123 return this.config[col].sortable;
6127 * Returns the rendering (formatting) function defined for the column.
6128 * @param {Number} col The column index.
6129 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
6131 getRenderer : function(col){
6132 if(!this.config[col].renderer){
6133 return Roo.grid.ColumnModel.defaultRenderer;
6135 return this.config[col].renderer;
6139 * Sets the rendering (formatting) function for a column.
6140 * @param {Number} col The column index
6141 * @param {Function} fn The function to use to process the cell's raw data
6142 * to return HTML markup for the grid view. The render function is called with
6143 * the following parameters:<ul>
6144 * <li>Data value.</li>
6145 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
6146 * <li>css A CSS style string to apply to the table cell.</li>
6147 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
6148 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
6149 * <li>Row index</li>
6150 * <li>Column index</li>
6151 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
6153 setRenderer : function(col, fn){
6154 this.config[col].renderer = fn;
6158 * Returns the width for the specified column.
6159 * @param {Number} col The column index
6162 getColumnWidth : function(col){
6163 return this.config[col].width * 1 || this.defaultWidth;
6167 * Sets the width for a column.
6168 * @param {Number} col The column index
6169 * @param {Number} width The new width
6171 setColumnWidth : function(col, width, suppressEvent){
6172 this.config[col].width = width;
6173 this.totalWidth = null;
6175 this.fireEvent("widthchange", this, col, width);
6180 * Returns the total width of all columns.
6181 * @param {Boolean} includeHidden True to include hidden column widths
6184 getTotalWidth : function(includeHidden){
6185 if(!this.totalWidth){
6186 this.totalWidth = 0;
6187 for(var i = 0, len = this.config.length; i < len; i++){
6188 if(includeHidden || !this.isHidden(i)){
6189 this.totalWidth += this.getColumnWidth(i);
6193 return this.totalWidth;
6197 * Returns the header for the specified column.
6198 * @param {Number} col The column index
6201 getColumnHeader : function(col){
6202 return this.config[col].header;
6206 * Sets the header for a column.
6207 * @param {Number} col The column index
6208 * @param {String} header The new header
6210 setColumnHeader : function(col, header){
6211 this.config[col].header = header;
6212 this.fireEvent("headerchange", this, col, header);
6216 * Returns the tooltip for the specified column.
6217 * @param {Number} col The column index
6220 getColumnTooltip : function(col){
6221 return this.config[col].tooltip;
6224 * Sets the tooltip for a column.
6225 * @param {Number} col The column index
6226 * @param {String} tooltip The new tooltip
6228 setColumnTooltip : function(col, tooltip){
6229 this.config[col].tooltip = tooltip;
6233 * Returns the dataIndex for the specified column.
6234 * @param {Number} col The column index
6237 getDataIndex : function(col){
6238 return this.config[col].dataIndex;
6242 * Sets the dataIndex for a column.
6243 * @param {Number} col The column index
6244 * @param {Number} dataIndex The new dataIndex
6246 setDataIndex : function(col, dataIndex){
6247 this.config[col].dataIndex = dataIndex;
6253 * Returns true if the cell is editable.
6254 * @param {Number} colIndex The column index
6255 * @param {Number} rowIndex The row index - this is nto actually used..?
6258 isCellEditable : function(colIndex, rowIndex){
6259 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6263 * Returns the editor defined for the cell/column.
6264 * return false or null to disable editing.
6265 * @param {Number} colIndex The column index
6266 * @param {Number} rowIndex The row index
6269 getCellEditor : function(colIndex, rowIndex){
6270 return this.config[colIndex].editor;
6274 * Sets if a column is editable.
6275 * @param {Number} col The column index
6276 * @param {Boolean} editable True if the column is editable
6278 setEditable : function(col, editable){
6279 this.config[col].editable = editable;
6284 * Returns true if the column is hidden.
6285 * @param {Number} colIndex The column index
6288 isHidden : function(colIndex){
6289 return this.config[colIndex].hidden;
6294 * Returns true if the column width cannot be changed
6296 isFixed : function(colIndex){
6297 return this.config[colIndex].fixed;
6301 * Returns true if the column can be resized
6304 isResizable : function(colIndex){
6305 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6308 * Sets if a column is hidden.
6309 * @param {Number} colIndex The column index
6310 * @param {Boolean} hidden True if the column is hidden
6312 setHidden : function(colIndex, hidden){
6313 this.config[colIndex].hidden = hidden;
6314 this.totalWidth = null;
6315 this.fireEvent("hiddenchange", this, colIndex, hidden);
6319 * Sets the editor for a column.
6320 * @param {Number} col The column index
6321 * @param {Object} editor The editor object
6323 setEditor : function(col, editor){
6324 this.config[col].editor = editor;
6328 Roo.grid.ColumnModel.defaultRenderer = function(value)
6330 if(typeof value == "object") {
6333 if(typeof value == "string" && value.length < 1){
6337 return String.format("{0}", value);
6340 // Alias for backwards compatibility
6341 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6344 * Ext JS Library 1.1.1
6345 * Copyright(c) 2006-2007, Ext JS, LLC.
6347 * Originally Released Under LGPL - original licence link has changed is not relivant.
6350 * <script type="text/javascript">
6354 * @class Roo.LoadMask
6355 * A simple utility class for generically masking elements while loading data. If the element being masked has
6356 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6357 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
6358 * element's UpdateManager load indicator and will be destroyed after the initial load.
6360 * Create a new LoadMask
6361 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6362 * @param {Object} config The config object
6364 Roo.LoadMask = function(el, config){
6365 this.el = Roo.get(el);
6366 Roo.apply(this, config);
6368 this.store.on('beforeload', this.onBeforeLoad, this);
6369 this.store.on('load', this.onLoad, this);
6370 this.store.on('loadexception', this.onLoadException, this);
6371 this.removeMask = false;
6373 var um = this.el.getUpdateManager();
6374 um.showLoadIndicator = false; // disable the default indicator
6375 um.on('beforeupdate', this.onBeforeLoad, this);
6376 um.on('update', this.onLoad, this);
6377 um.on('failure', this.onLoad, this);
6378 this.removeMask = true;
6382 Roo.LoadMask.prototype = {
6384 * @cfg {Boolean} removeMask
6385 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6386 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
6390 * The text to display in a centered loading message box (defaults to 'Loading...')
6394 * @cfg {String} msgCls
6395 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6397 msgCls : 'x-mask-loading',
6400 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6406 * Disables the mask to prevent it from being displayed
6408 disable : function(){
6409 this.disabled = true;
6413 * Enables the mask so that it can be displayed
6415 enable : function(){
6416 this.disabled = false;
6419 onLoadException : function()
6423 if (typeof(arguments[3]) != 'undefined') {
6424 Roo.MessageBox.alert("Error loading",arguments[3]);
6428 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6429 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6436 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6441 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6445 onBeforeLoad : function(){
6447 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6452 destroy : function(){
6454 this.store.un('beforeload', this.onBeforeLoad, this);
6455 this.store.un('load', this.onLoad, this);
6456 this.store.un('loadexception', this.onLoadException, this);
6458 var um = this.el.getUpdateManager();
6459 um.un('beforeupdate', this.onBeforeLoad, this);
6460 um.un('update', this.onLoad, this);
6461 um.un('failure', this.onLoad, this);
6472 * @class Roo.bootstrap.Table
6473 * @extends Roo.bootstrap.Component
6474 * Bootstrap Table class
6475 * @cfg {String} cls table class
6476 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6477 * @cfg {String} bgcolor Specifies the background color for a table
6478 * @cfg {Number} border Specifies whether the table cells should have borders or not
6479 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6480 * @cfg {Number} cellspacing Specifies the space between cells
6481 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6482 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6483 * @cfg {String} sortable Specifies that the table should be sortable
6484 * @cfg {String} summary Specifies a summary of the content of a table
6485 * @cfg {Number} width Specifies the width of a table
6486 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6488 * @cfg {boolean} striped Should the rows be alternative striped
6489 * @cfg {boolean} bordered Add borders to the table
6490 * @cfg {boolean} hover Add hover highlighting
6491 * @cfg {boolean} condensed Format condensed
6492 * @cfg {boolean} responsive Format condensed
6493 * @cfg {Boolean} loadMask (true|false) default false
6494 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6495 * @cfg {Boolean} headerShow (true|false) generate thead, default true
6496 * @cfg {Boolean} rowSelection (true|false) default false
6497 * @cfg {Boolean} cellSelection (true|false) default false
6498 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6499 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
6500 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
6501 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6505 * Create a new Table
6506 * @param {Object} config The config object
6509 Roo.bootstrap.Table = function(config){
6510 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6515 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6516 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6517 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6518 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6520 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6522 this.sm.grid = this;
6523 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6524 this.sm = this.selModel;
6525 this.sm.xmodule = this.xmodule || false;
6528 if (this.cm && typeof(this.cm.config) == 'undefined') {
6529 this.colModel = new Roo.grid.ColumnModel(this.cm);
6530 this.cm = this.colModel;
6531 this.cm.xmodule = this.xmodule || false;
6534 this.store= Roo.factory(this.store, Roo.data);
6535 this.ds = this.store;
6536 this.ds.xmodule = this.xmodule || false;
6539 if (this.footer && this.store) {
6540 this.footer.dataSource = this.ds;
6541 this.footer = Roo.factory(this.footer);
6548 * Fires when a cell is clicked
6549 * @param {Roo.bootstrap.Table} this
6550 * @param {Roo.Element} el
6551 * @param {Number} rowIndex
6552 * @param {Number} columnIndex
6553 * @param {Roo.EventObject} e
6557 * @event celldblclick
6558 * Fires when a cell is double clicked
6559 * @param {Roo.bootstrap.Table} this
6560 * @param {Roo.Element} el
6561 * @param {Number} rowIndex
6562 * @param {Number} columnIndex
6563 * @param {Roo.EventObject} e
6565 "celldblclick" : true,
6568 * Fires when a row is clicked
6569 * @param {Roo.bootstrap.Table} this
6570 * @param {Roo.Element} el
6571 * @param {Number} rowIndex
6572 * @param {Roo.EventObject} e
6576 * @event rowdblclick
6577 * Fires when a row is double clicked
6578 * @param {Roo.bootstrap.Table} this
6579 * @param {Roo.Element} el
6580 * @param {Number} rowIndex
6581 * @param {Roo.EventObject} e
6583 "rowdblclick" : true,
6586 * Fires when a mouseover occur
6587 * @param {Roo.bootstrap.Table} this
6588 * @param {Roo.Element} el
6589 * @param {Number} rowIndex
6590 * @param {Number} columnIndex
6591 * @param {Roo.EventObject} e
6596 * Fires when a mouseout occur
6597 * @param {Roo.bootstrap.Table} this
6598 * @param {Roo.Element} el
6599 * @param {Number} rowIndex
6600 * @param {Number} columnIndex
6601 * @param {Roo.EventObject} e
6606 * Fires when a row is rendered, so you can change add a style to it.
6607 * @param {Roo.bootstrap.Table} this
6608 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6612 * @event rowsrendered
6613 * Fires when all the rows have been rendered
6614 * @param {Roo.bootstrap.Table} this
6616 'rowsrendered' : true,
6618 * @event contextmenu
6619 * The raw contextmenu event for the entire grid.
6620 * @param {Roo.EventObject} e
6622 "contextmenu" : true,
6624 * @event rowcontextmenu
6625 * Fires when a row is right clicked
6626 * @param {Roo.bootstrap.Table} this
6627 * @param {Number} rowIndex
6628 * @param {Roo.EventObject} e
6630 "rowcontextmenu" : true,
6632 * @event cellcontextmenu
6633 * Fires when a cell is right clicked
6634 * @param {Roo.bootstrap.Table} this
6635 * @param {Number} rowIndex
6636 * @param {Number} cellIndex
6637 * @param {Roo.EventObject} e
6639 "cellcontextmenu" : true,
6641 * @event headercontextmenu
6642 * Fires when a header is right clicked
6643 * @param {Roo.bootstrap.Table} this
6644 * @param {Number} columnIndex
6645 * @param {Roo.EventObject} e
6647 "headercontextmenu" : true
6651 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6677 rowSelection : false,
6678 cellSelection : false,
6681 // Roo.Element - the tbody
6683 // Roo.Element - thead element
6686 container: false, // used by gridpanel...
6692 auto_hide_footer : false,
6694 getAutoCreate : function()
6696 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6703 if (this.scrollBody) {
6704 cfg.cls += ' table-body-fixed';
6707 cfg.cls += ' table-striped';
6711 cfg.cls += ' table-hover';
6713 if (this.bordered) {
6714 cfg.cls += ' table-bordered';
6716 if (this.condensed) {
6717 cfg.cls += ' table-condensed';
6719 if (this.responsive) {
6720 cfg.cls += ' table-responsive';
6724 cfg.cls+= ' ' +this.cls;
6727 // this lot should be simplifed...
6740 ].forEach(function(k) {
6748 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6751 if(this.store || this.cm){
6752 if(this.headerShow){
6753 cfg.cn.push(this.renderHeader());
6756 cfg.cn.push(this.renderBody());
6758 if(this.footerShow){
6759 cfg.cn.push(this.renderFooter());
6761 // where does this come from?
6762 //cfg.cls+= ' TableGrid';
6765 return { cn : [ cfg ] };
6768 initEvents : function()
6770 if(!this.store || !this.cm){
6773 if (this.selModel) {
6774 this.selModel.initEvents();
6778 //Roo.log('initEvents with ds!!!!');
6780 this.mainBody = this.el.select('tbody', true).first();
6781 this.mainHead = this.el.select('thead', true).first();
6782 this.mainFoot = this.el.select('tfoot', true).first();
6788 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6789 e.on('click', _this.sort, _this);
6792 this.mainBody.on("click", this.onClick, this);
6793 this.mainBody.on("dblclick", this.onDblClick, this);
6795 // why is this done????? = it breaks dialogs??
6796 //this.parent().el.setStyle('position', 'relative');
6800 this.footer.parentId = this.id;
6801 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6804 this.el.select('tfoot tr td').first().addClass('hide');
6809 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6812 this.store.on('load', this.onLoad, this);
6813 this.store.on('beforeload', this.onBeforeLoad, this);
6814 this.store.on('update', this.onUpdate, this);
6815 this.store.on('add', this.onAdd, this);
6816 this.store.on("clear", this.clear, this);
6818 this.el.on("contextmenu", this.onContextMenu, this);
6820 this.mainBody.on('scroll', this.onBodyScroll, this);
6822 this.cm.on("headerchange", this.onHeaderChange, this);
6824 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6828 onContextMenu : function(e, t)
6830 this.processEvent("contextmenu", e);
6833 processEvent : function(name, e)
6835 if (name != 'touchstart' ) {
6836 this.fireEvent(name, e);
6839 var t = e.getTarget();
6841 var cell = Roo.get(t);
6847 if(cell.findParent('tfoot', false, true)){
6851 if(cell.findParent('thead', false, true)){
6853 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6854 cell = Roo.get(t).findParent('th', false, true);
6856 Roo.log("failed to find th in thead?");
6857 Roo.log(e.getTarget());
6862 var cellIndex = cell.dom.cellIndex;
6864 var ename = name == 'touchstart' ? 'click' : name;
6865 this.fireEvent("header" + ename, this, cellIndex, e);
6870 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6871 cell = Roo.get(t).findParent('td', false, true);
6873 Roo.log("failed to find th in tbody?");
6874 Roo.log(e.getTarget());
6879 var row = cell.findParent('tr', false, true);
6880 var cellIndex = cell.dom.cellIndex;
6881 var rowIndex = row.dom.rowIndex - 1;
6885 this.fireEvent("row" + name, this, rowIndex, e);
6889 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6895 onMouseover : function(e, el)
6897 var cell = Roo.get(el);
6903 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6904 cell = cell.findParent('td', false, true);
6907 var row = cell.findParent('tr', false, true);
6908 var cellIndex = cell.dom.cellIndex;
6909 var rowIndex = row.dom.rowIndex - 1; // start from 0
6911 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6915 onMouseout : function(e, el)
6917 var cell = Roo.get(el);
6923 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6924 cell = cell.findParent('td', false, true);
6927 var row = cell.findParent('tr', false, true);
6928 var cellIndex = cell.dom.cellIndex;
6929 var rowIndex = row.dom.rowIndex - 1; // start from 0
6931 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6935 onClick : function(e, el)
6937 var cell = Roo.get(el);
6939 if(!cell || (!this.cellSelection && !this.rowSelection)){
6943 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6944 cell = cell.findParent('td', false, true);
6947 if(!cell || typeof(cell) == 'undefined'){
6951 var row = cell.findParent('tr', false, true);
6953 if(!row || typeof(row) == 'undefined'){
6957 var cellIndex = cell.dom.cellIndex;
6958 var rowIndex = this.getRowIndex(row);
6960 // why??? - should these not be based on SelectionModel?
6961 if(this.cellSelection){
6962 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6965 if(this.rowSelection){
6966 this.fireEvent('rowclick', this, row, rowIndex, e);
6972 onDblClick : function(e,el)
6974 var cell = Roo.get(el);
6976 if(!cell || (!this.cellSelection && !this.rowSelection)){
6980 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6981 cell = cell.findParent('td', false, true);
6984 if(!cell || typeof(cell) == 'undefined'){
6988 var row = cell.findParent('tr', false, true);
6990 if(!row || typeof(row) == 'undefined'){
6994 var cellIndex = cell.dom.cellIndex;
6995 var rowIndex = this.getRowIndex(row);
6997 if(this.cellSelection){
6998 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7001 if(this.rowSelection){
7002 this.fireEvent('rowdblclick', this, row, rowIndex, e);
7006 sort : function(e,el)
7008 var col = Roo.get(el);
7010 if(!col.hasClass('sortable')){
7014 var sort = col.attr('sort');
7017 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7021 this.store.sortInfo = {field : sort, direction : dir};
7024 Roo.log("calling footer first");
7025 this.footer.onClick('first');
7028 this.store.load({ params : { start : 0 } });
7032 renderHeader : function()
7040 this.totalWidth = 0;
7042 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7044 var config = cm.config[i];
7048 cls : 'x-hcol-' + i,
7050 html: cm.getColumnHeader(i)
7055 if(typeof(config.sortable) != 'undefined' && config.sortable){
7057 c.html = '<i class="glyphicon"></i>' + c.html;
7060 // could use BS4 hidden-..-down
7062 if(typeof(config.lgHeader) != 'undefined'){
7063 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
7066 if(typeof(config.mdHeader) != 'undefined'){
7067 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
7070 if(typeof(config.smHeader) != 'undefined'){
7071 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
7074 if(typeof(config.xsHeader) != 'undefined'){
7075 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
7082 if(typeof(config.tooltip) != 'undefined'){
7083 c.tooltip = config.tooltip;
7086 if(typeof(config.colspan) != 'undefined'){
7087 c.colspan = config.colspan;
7090 if(typeof(config.hidden) != 'undefined' && config.hidden){
7091 c.style += ' display:none;';
7094 if(typeof(config.dataIndex) != 'undefined'){
7095 c.sort = config.dataIndex;
7100 if(typeof(config.align) != 'undefined' && config.align.length){
7101 c.style += ' text-align:' + config.align + ';';
7104 if(typeof(config.width) != 'undefined'){
7105 c.style += ' width:' + config.width + 'px;';
7106 this.totalWidth += config.width;
7108 this.totalWidth += 100; // assume minimum of 100 per column?
7111 if(typeof(config.cls) != 'undefined'){
7112 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
7115 ['xs','sm','md','lg'].map(function(size){
7117 if(typeof(config[size]) == 'undefined'){
7121 if (!config[size]) { // 0 = hidden
7122 // BS 4 '0' is treated as hide that column and below.
7123 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
7127 c.cls += ' col-' + size + '-' + config[size] + (
7128 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7140 renderBody : function()
7150 colspan : this.cm.getColumnCount()
7160 renderFooter : function()
7170 colspan : this.cm.getColumnCount()
7184 // Roo.log('ds onload');
7189 var ds = this.store;
7191 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7192 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
7193 if (_this.store.sortInfo) {
7195 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
7196 e.select('i', true).addClass(['glyphicon-arrow-up']);
7199 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
7200 e.select('i', true).addClass(['glyphicon-arrow-down']);
7205 var tbody = this.mainBody;
7207 if(ds.getCount() > 0){
7208 ds.data.each(function(d,rowIndex){
7209 var row = this.renderRow(cm, ds, rowIndex);
7211 tbody.createChild(row);
7215 if(row.cellObjects.length){
7216 Roo.each(row.cellObjects, function(r){
7217 _this.renderCellObject(r);
7224 var tfoot = this.el.select('tfoot', true).first();
7226 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
7228 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
7230 var total = this.ds.getTotalCount();
7232 if(this.footer.pageSize < total){
7233 this.mainFoot.show();
7237 Roo.each(this.el.select('tbody td', true).elements, function(e){
7238 e.on('mouseover', _this.onMouseover, _this);
7241 Roo.each(this.el.select('tbody td', true).elements, function(e){
7242 e.on('mouseout', _this.onMouseout, _this);
7244 this.fireEvent('rowsrendered', this);
7250 onUpdate : function(ds,record)
7252 this.refreshRow(record);
7256 onRemove : function(ds, record, index, isUpdate){
7257 if(isUpdate !== true){
7258 this.fireEvent("beforerowremoved", this, index, record);
7260 var bt = this.mainBody.dom;
7262 var rows = this.el.select('tbody > tr', true).elements;
7264 if(typeof(rows[index]) != 'undefined'){
7265 bt.removeChild(rows[index].dom);
7268 // if(bt.rows[index]){
7269 // bt.removeChild(bt.rows[index]);
7272 if(isUpdate !== true){
7273 //this.stripeRows(index);
7274 //this.syncRowHeights(index, index);
7276 this.fireEvent("rowremoved", this, index, record);
7280 onAdd : function(ds, records, rowIndex)
7282 //Roo.log('on Add called');
7283 // - note this does not handle multiple adding very well..
7284 var bt = this.mainBody.dom;
7285 for (var i =0 ; i < records.length;i++) {
7286 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7287 //Roo.log(records[i]);
7288 //Roo.log(this.store.getAt(rowIndex+i));
7289 this.insertRow(this.store, rowIndex + i, false);
7296 refreshRow : function(record){
7297 var ds = this.store, index;
7298 if(typeof record == 'number'){
7300 record = ds.getAt(index);
7302 index = ds.indexOf(record);
7304 this.insertRow(ds, index, true);
7306 this.onRemove(ds, record, index+1, true);
7308 //this.syncRowHeights(index, index);
7310 this.fireEvent("rowupdated", this, index, record);
7313 insertRow : function(dm, rowIndex, isUpdate){
7316 this.fireEvent("beforerowsinserted", this, rowIndex);
7318 //var s = this.getScrollState();
7319 var row = this.renderRow(this.cm, this.store, rowIndex);
7320 // insert before rowIndex..
7321 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7325 if(row.cellObjects.length){
7326 Roo.each(row.cellObjects, function(r){
7327 _this.renderCellObject(r);
7332 this.fireEvent("rowsinserted", this, rowIndex);
7333 //this.syncRowHeights(firstRow, lastRow);
7334 //this.stripeRows(firstRow);
7341 getRowDom : function(rowIndex)
7343 var rows = this.el.select('tbody > tr', true).elements;
7345 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7348 // returns the object tree for a tr..
7351 renderRow : function(cm, ds, rowIndex)
7353 var d = ds.getAt(rowIndex);
7357 cls : 'x-row-' + rowIndex,
7361 var cellObjects = [];
7363 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7364 var config = cm.config[i];
7366 var renderer = cm.getRenderer(i);
7370 if(typeof(renderer) !== 'undefined'){
7371 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7373 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7374 // and are rendered into the cells after the row is rendered - using the id for the element.
7376 if(typeof(value) === 'object'){
7386 rowIndex : rowIndex,
7391 this.fireEvent('rowclass', this, rowcfg);
7395 cls : rowcfg.rowClass + ' x-col-' + i,
7397 html: (typeof(value) === 'object') ? '' : value
7404 if(typeof(config.colspan) != 'undefined'){
7405 td.colspan = config.colspan;
7408 if(typeof(config.hidden) != 'undefined' && config.hidden){
7409 td.style += ' display:none;';
7412 if(typeof(config.align) != 'undefined' && config.align.length){
7413 td.style += ' text-align:' + config.align + ';';
7415 if(typeof(config.valign) != 'undefined' && config.valign.length){
7416 td.style += ' vertical-align:' + config.valign + ';';
7419 if(typeof(config.width) != 'undefined'){
7420 td.style += ' width:' + config.width + 'px;';
7423 if(typeof(config.cursor) != 'undefined'){
7424 td.style += ' cursor:' + config.cursor + ';';
7427 if(typeof(config.cls) != 'undefined'){
7428 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7431 ['xs','sm','md','lg'].map(function(size){
7433 if(typeof(config[size]) == 'undefined'){
7439 if (!config[size]) { // 0 = hidden
7440 // BS 4 '0' is treated as hide that column and below.
7441 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
7445 td.cls += ' col-' + size + '-' + config[size] + (
7446 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7456 row.cellObjects = cellObjects;
7464 onBeforeLoad : function()
7473 this.el.select('tbody', true).first().dom.innerHTML = '';
7476 * Show or hide a row.
7477 * @param {Number} rowIndex to show or hide
7478 * @param {Boolean} state hide
7480 setRowVisibility : function(rowIndex, state)
7482 var bt = this.mainBody.dom;
7484 var rows = this.el.select('tbody > tr', true).elements;
7486 if(typeof(rows[rowIndex]) == 'undefined'){
7489 rows[rowIndex].dom.style.display = state ? '' : 'none';
7493 getSelectionModel : function(){
7495 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7497 return this.selModel;
7500 * Render the Roo.bootstrap object from renderder
7502 renderCellObject : function(r)
7506 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7508 var t = r.cfg.render(r.container);
7511 Roo.each(r.cfg.cn, function(c){
7513 container: t.getChildContainer(),
7516 _this.renderCellObject(child);
7521 getRowIndex : function(row)
7525 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7536 * Returns the grid's underlying element = used by panel.Grid
7537 * @return {Element} The element
7539 getGridEl : function(){
7543 * Forces a resize - used by panel.Grid
7544 * @return {Element} The element
7546 autoSize : function()
7548 //var ctr = Roo.get(this.container.dom.parentElement);
7549 var ctr = Roo.get(this.el.dom);
7551 var thd = this.getGridEl().select('thead',true).first();
7552 var tbd = this.getGridEl().select('tbody', true).first();
7553 var tfd = this.getGridEl().select('tfoot', true).first();
7555 var cw = ctr.getWidth();
7559 tbd.setWidth(ctr.getWidth());
7560 // if the body has a max height - and then scrolls - we should perhaps set up the height here
7561 // this needs fixing for various usage - currently only hydra job advers I think..
7563 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7565 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7568 cw = Math.max(cw, this.totalWidth);
7569 this.getGridEl().select('tr',true).setWidth(cw);
7570 // resize 'expandable coloumn?
7572 return; // we doe not have a view in this design..
7575 onBodyScroll: function()
7577 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7579 this.mainHead.setStyle({
7580 'position' : 'relative',
7581 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7587 var scrollHeight = this.mainBody.dom.scrollHeight;
7589 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7591 var height = this.mainBody.getHeight();
7593 if(scrollHeight - height == scrollTop) {
7595 var total = this.ds.getTotalCount();
7597 if(this.footer.cursor + this.footer.pageSize < total){
7599 this.footer.ds.load({
7601 start : this.footer.cursor + this.footer.pageSize,
7602 limit : this.footer.pageSize
7612 onHeaderChange : function()
7614 var header = this.renderHeader();
7615 var table = this.el.select('table', true).first();
7617 this.mainHead.remove();
7618 this.mainHead = table.createChild(header, this.mainBody, false);
7621 onHiddenChange : function(colModel, colIndex, hidden)
7623 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7624 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7626 this.CSS.updateRule(thSelector, "display", "");
7627 this.CSS.updateRule(tdSelector, "display", "");
7630 this.CSS.updateRule(thSelector, "display", "none");
7631 this.CSS.updateRule(tdSelector, "display", "none");
7634 this.onHeaderChange();
7638 setColumnWidth: function(col_index, width)
7640 // width = "md-2 xs-2..."
7641 if(!this.colModel.config[col_index]) {
7645 var w = width.split(" ");
7647 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7649 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7652 for(var j = 0; j < w.length; j++) {
7658 var size_cls = w[j].split("-");
7660 if(!Number.isInteger(size_cls[1] * 1)) {
7664 if(!this.colModel.config[col_index][size_cls[0]]) {
7668 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7672 h_row[0].classList.replace(
7673 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7674 "col-"+size_cls[0]+"-"+size_cls[1]
7677 for(var i = 0; i < rows.length; i++) {
7679 var size_cls = w[j].split("-");
7681 if(!Number.isInteger(size_cls[1] * 1)) {
7685 if(!this.colModel.config[col_index][size_cls[0]]) {
7689 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7693 rows[i].classList.replace(
7694 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7695 "col-"+size_cls[0]+"-"+size_cls[1]
7699 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7714 * @class Roo.bootstrap.TableCell
7715 * @extends Roo.bootstrap.Component
7716 * Bootstrap TableCell class
7717 * @cfg {String} html cell contain text
7718 * @cfg {String} cls cell class
7719 * @cfg {String} tag cell tag (td|th) default td
7720 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7721 * @cfg {String} align Aligns the content in a cell
7722 * @cfg {String} axis Categorizes cells
7723 * @cfg {String} bgcolor Specifies the background color of a cell
7724 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7725 * @cfg {Number} colspan Specifies the number of columns a cell should span
7726 * @cfg {String} headers Specifies one or more header cells a cell is related to
7727 * @cfg {Number} height Sets the height of a cell
7728 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7729 * @cfg {Number} rowspan Sets the number of rows a cell should span
7730 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7731 * @cfg {String} valign Vertical aligns the content in a cell
7732 * @cfg {Number} width Specifies the width of a cell
7735 * Create a new TableCell
7736 * @param {Object} config The config object
7739 Roo.bootstrap.TableCell = function(config){
7740 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7743 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7763 getAutoCreate : function(){
7764 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7784 cfg.align=this.align
7790 cfg.bgcolor=this.bgcolor
7793 cfg.charoff=this.charoff
7796 cfg.colspan=this.colspan
7799 cfg.headers=this.headers
7802 cfg.height=this.height
7805 cfg.nowrap=this.nowrap
7808 cfg.rowspan=this.rowspan
7811 cfg.scope=this.scope
7814 cfg.valign=this.valign
7817 cfg.width=this.width
7836 * @class Roo.bootstrap.TableRow
7837 * @extends Roo.bootstrap.Component
7838 * Bootstrap TableRow class
7839 * @cfg {String} cls row class
7840 * @cfg {String} align Aligns the content in a table row
7841 * @cfg {String} bgcolor Specifies a background color for a table row
7842 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7843 * @cfg {String} valign Vertical aligns the content in a table row
7846 * Create a new TableRow
7847 * @param {Object} config The config object
7850 Roo.bootstrap.TableRow = function(config){
7851 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7854 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7862 getAutoCreate : function(){
7863 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7873 cfg.align = this.align;
7876 cfg.bgcolor = this.bgcolor;
7879 cfg.charoff = this.charoff;
7882 cfg.valign = this.valign;
7900 * @class Roo.bootstrap.TableBody
7901 * @extends Roo.bootstrap.Component
7902 * Bootstrap TableBody class
7903 * @cfg {String} cls element class
7904 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7905 * @cfg {String} align Aligns the content inside the element
7906 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7907 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7910 * Create a new TableBody
7911 * @param {Object} config The config object
7914 Roo.bootstrap.TableBody = function(config){
7915 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7918 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7926 getAutoCreate : function(){
7927 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7941 cfg.align = this.align;
7944 cfg.charoff = this.charoff;
7947 cfg.valign = this.valign;
7954 // initEvents : function()
7961 // this.store = Roo.factory(this.store, Roo.data);
7962 // this.store.on('load', this.onLoad, this);
7964 // this.store.load();
7968 // onLoad: function ()
7970 // this.fireEvent('load', this);
7980 * Ext JS Library 1.1.1
7981 * Copyright(c) 2006-2007, Ext JS, LLC.
7983 * Originally Released Under LGPL - original licence link has changed is not relivant.
7986 * <script type="text/javascript">
7989 // as we use this in bootstrap.
7990 Roo.namespace('Roo.form');
7992 * @class Roo.form.Action
7993 * Internal Class used to handle form actions
7995 * @param {Roo.form.BasicForm} el The form element or its id
7996 * @param {Object} config Configuration options
8001 // define the action interface
8002 Roo.form.Action = function(form, options){
8004 this.options = options || {};
8007 * Client Validation Failed
8010 Roo.form.Action.CLIENT_INVALID = 'client';
8012 * Server Validation Failed
8015 Roo.form.Action.SERVER_INVALID = 'server';
8017 * Connect to Server Failed
8020 Roo.form.Action.CONNECT_FAILURE = 'connect';
8022 * Reading Data from Server Failed
8025 Roo.form.Action.LOAD_FAILURE = 'load';
8027 Roo.form.Action.prototype = {
8029 failureType : undefined,
8030 response : undefined,
8034 run : function(options){
8039 success : function(response){
8044 handleResponse : function(response){
8048 // default connection failure
8049 failure : function(response){
8051 this.response = response;
8052 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8053 this.form.afterAction(this, false);
8056 processResponse : function(response){
8057 this.response = response;
8058 if(!response.responseText){
8061 this.result = this.handleResponse(response);
8065 // utility functions used internally
8066 getUrl : function(appendParams){
8067 var url = this.options.url || this.form.url || this.form.el.dom.action;
8069 var p = this.getParams();
8071 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
8077 getMethod : function(){
8078 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
8081 getParams : function(){
8082 var bp = this.form.baseParams;
8083 var p = this.options.params;
8085 if(typeof p == "object"){
8086 p = Roo.urlEncode(Roo.applyIf(p, bp));
8087 }else if(typeof p == 'string' && bp){
8088 p += '&' + Roo.urlEncode(bp);
8091 p = Roo.urlEncode(bp);
8096 createCallback : function(){
8098 success: this.success,
8099 failure: this.failure,
8101 timeout: (this.form.timeout*1000),
8102 upload: this.form.fileUpload ? this.success : undefined
8107 Roo.form.Action.Submit = function(form, options){
8108 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
8111 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
8114 haveProgress : false,
8115 uploadComplete : false,
8117 // uploadProgress indicator.
8118 uploadProgress : function()
8120 if (!this.form.progressUrl) {
8124 if (!this.haveProgress) {
8125 Roo.MessageBox.progress("Uploading", "Uploading");
8127 if (this.uploadComplete) {
8128 Roo.MessageBox.hide();
8132 this.haveProgress = true;
8134 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
8136 var c = new Roo.data.Connection();
8138 url : this.form.progressUrl,
8143 success : function(req){
8144 //console.log(data);
8148 rdata = Roo.decode(req.responseText)
8150 Roo.log("Invalid data from server..");
8154 if (!rdata || !rdata.success) {
8156 Roo.MessageBox.alert(Roo.encode(rdata));
8159 var data = rdata.data;
8161 if (this.uploadComplete) {
8162 Roo.MessageBox.hide();
8167 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
8168 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
8171 this.uploadProgress.defer(2000,this);
8174 failure: function(data) {
8175 Roo.log('progress url failed ');
8186 // run get Values on the form, so it syncs any secondary forms.
8187 this.form.getValues();
8189 var o = this.options;
8190 var method = this.getMethod();
8191 var isPost = method == 'POST';
8192 if(o.clientValidation === false || this.form.isValid()){
8194 if (this.form.progressUrl) {
8195 this.form.findField('UPLOAD_IDENTIFIER').setValue(
8196 (new Date() * 1) + '' + Math.random());
8201 Roo.Ajax.request(Roo.apply(this.createCallback(), {
8202 form:this.form.el.dom,
8203 url:this.getUrl(!isPost),
8205 params:isPost ? this.getParams() : null,
8206 isUpload: this.form.fileUpload,
8207 formData : this.form.formData
8210 this.uploadProgress();
8212 }else if (o.clientValidation !== false){ // client validation failed
8213 this.failureType = Roo.form.Action.CLIENT_INVALID;
8214 this.form.afterAction(this, false);
8218 success : function(response)
8220 this.uploadComplete= true;
8221 if (this.haveProgress) {
8222 Roo.MessageBox.hide();
8226 var result = this.processResponse(response);
8227 if(result === true || result.success){
8228 this.form.afterAction(this, true);
8232 this.form.markInvalid(result.errors);
8233 this.failureType = Roo.form.Action.SERVER_INVALID;
8235 this.form.afterAction(this, false);
8237 failure : function(response)
8239 this.uploadComplete= true;
8240 if (this.haveProgress) {
8241 Roo.MessageBox.hide();
8244 this.response = response;
8245 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8246 this.form.afterAction(this, false);
8249 handleResponse : function(response){
8250 if(this.form.errorReader){
8251 var rs = this.form.errorReader.read(response);
8254 for(var i = 0, len = rs.records.length; i < len; i++) {
8255 var r = rs.records[i];
8259 if(errors.length < 1){
8263 success : rs.success,
8269 ret = Roo.decode(response.responseText);
8273 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8283 Roo.form.Action.Load = function(form, options){
8284 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8285 this.reader = this.form.reader;
8288 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8293 Roo.Ajax.request(Roo.apply(
8294 this.createCallback(), {
8295 method:this.getMethod(),
8296 url:this.getUrl(false),
8297 params:this.getParams()
8301 success : function(response){
8303 var result = this.processResponse(response);
8304 if(result === true || !result.success || !result.data){
8305 this.failureType = Roo.form.Action.LOAD_FAILURE;
8306 this.form.afterAction(this, false);
8309 this.form.clearInvalid();
8310 this.form.setValues(result.data);
8311 this.form.afterAction(this, true);
8314 handleResponse : function(response){
8315 if(this.form.reader){
8316 var rs = this.form.reader.read(response);
8317 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8319 success : rs.success,
8323 return Roo.decode(response.responseText);
8327 Roo.form.Action.ACTION_TYPES = {
8328 'load' : Roo.form.Action.Load,
8329 'submit' : Roo.form.Action.Submit
8338 * @class Roo.bootstrap.Form
8339 * @extends Roo.bootstrap.Component
8340 * Bootstrap Form class
8341 * @cfg {String} method GET | POST (default POST)
8342 * @cfg {String} labelAlign top | left (default top)
8343 * @cfg {String} align left | right - for navbars
8344 * @cfg {Boolean} loadMask load mask when submit (default true)
8349 * @param {Object} config The config object
8353 Roo.bootstrap.Form = function(config){
8355 Roo.bootstrap.Form.superclass.constructor.call(this, config);
8357 Roo.bootstrap.Form.popover.apply();
8361 * @event clientvalidation
8362 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8363 * @param {Form} this
8364 * @param {Boolean} valid true if the form has passed client-side validation
8366 clientvalidation: true,
8368 * @event beforeaction
8369 * Fires before any action is performed. Return false to cancel the action.
8370 * @param {Form} this
8371 * @param {Action} action The action to be performed
8375 * @event actionfailed
8376 * Fires when an action fails.
8377 * @param {Form} this
8378 * @param {Action} action The action that failed
8380 actionfailed : true,
8382 * @event actioncomplete
8383 * Fires when an action is completed.
8384 * @param {Form} this
8385 * @param {Action} action The action that completed
8387 actioncomplete : true
8391 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
8394 * @cfg {String} method
8395 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8400 * The URL to use for form actions if one isn't supplied in the action options.
8403 * @cfg {Boolean} fileUpload
8404 * Set to true if this form is a file upload.
8408 * @cfg {Object} baseParams
8409 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8413 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8417 * @cfg {Sting} align (left|right) for navbar forms
8422 activeAction : null,
8425 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8426 * element by passing it or its id or mask the form itself by passing in true.
8429 waitMsgTarget : false,
8434 * @cfg {Boolean} errorMask (true|false) default false
8439 * @cfg {Number} maskOffset Default 100
8444 * @cfg {Boolean} maskBody
8448 getAutoCreate : function(){
8452 method : this.method || 'POST',
8453 id : this.id || Roo.id(),
8456 if (this.parent().xtype.match(/^Nav/)) {
8457 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8461 if (this.labelAlign == 'left' ) {
8462 cfg.cls += ' form-horizontal';
8468 initEvents : function()
8470 this.el.on('submit', this.onSubmit, this);
8471 // this was added as random key presses on the form where triggering form submit.
8472 this.el.on('keypress', function(e) {
8473 if (e.getCharCode() != 13) {
8476 // we might need to allow it for textareas.. and some other items.
8477 // check e.getTarget().
8479 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8483 Roo.log("keypress blocked");
8491 onSubmit : function(e){
8496 * Returns true if client-side validation on the form is successful.
8499 isValid : function(){
8500 var items = this.getItems();
8504 items.each(function(f){
8510 Roo.log('invalid field: ' + f.name);
8514 if(!target && f.el.isVisible(true)){
8520 if(this.errorMask && !valid){
8521 Roo.bootstrap.Form.popover.mask(this, target);
8528 * Returns true if any fields in this form have changed since their original load.
8531 isDirty : function(){
8533 var items = this.getItems();
8534 items.each(function(f){
8544 * Performs a predefined action (submit or load) or custom actions you define on this form.
8545 * @param {String} actionName The name of the action type
8546 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8547 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8548 * accept other config options):
8550 Property Type Description
8551 ---------------- --------------- ----------------------------------------------------------------------------------
8552 url String The url for the action (defaults to the form's url)
8553 method String The form method to use (defaults to the form's method, or POST if not defined)
8554 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8555 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8556 validate the form on the client (defaults to false)
8558 * @return {BasicForm} this
8560 doAction : function(action, options){
8561 if(typeof action == 'string'){
8562 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8564 if(this.fireEvent('beforeaction', this, action) !== false){
8565 this.beforeAction(action);
8566 action.run.defer(100, action);
8572 beforeAction : function(action){
8573 var o = action.options;
8578 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8580 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8583 // not really supported yet.. ??
8585 //if(this.waitMsgTarget === true){
8586 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8587 //}else if(this.waitMsgTarget){
8588 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8589 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8591 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8597 afterAction : function(action, success){
8598 this.activeAction = null;
8599 var o = action.options;
8604 Roo.get(document.body).unmask();
8610 //if(this.waitMsgTarget === true){
8611 // this.el.unmask();
8612 //}else if(this.waitMsgTarget){
8613 // this.waitMsgTarget.unmask();
8615 // Roo.MessageBox.updateProgress(1);
8616 // Roo.MessageBox.hide();
8623 Roo.callback(o.success, o.scope, [this, action]);
8624 this.fireEvent('actioncomplete', this, action);
8628 // failure condition..
8629 // we have a scenario where updates need confirming.
8630 // eg. if a locking scenario exists..
8631 // we look for { errors : { needs_confirm : true }} in the response.
8633 (typeof(action.result) != 'undefined') &&
8634 (typeof(action.result.errors) != 'undefined') &&
8635 (typeof(action.result.errors.needs_confirm) != 'undefined')
8638 Roo.log("not supported yet");
8641 Roo.MessageBox.confirm(
8642 "Change requires confirmation",
8643 action.result.errorMsg,
8648 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8658 Roo.callback(o.failure, o.scope, [this, action]);
8659 // show an error message if no failed handler is set..
8660 if (!this.hasListener('actionfailed')) {
8661 Roo.log("need to add dialog support");
8663 Roo.MessageBox.alert("Error",
8664 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8665 action.result.errorMsg :
8666 "Saving Failed, please check your entries or try again"
8671 this.fireEvent('actionfailed', this, action);
8676 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8677 * @param {String} id The value to search for
8680 findField : function(id){
8681 var items = this.getItems();
8682 var field = items.get(id);
8684 items.each(function(f){
8685 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8692 return field || null;
8695 * Mark fields in this form invalid in bulk.
8696 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8697 * @return {BasicForm} this
8699 markInvalid : function(errors){
8700 if(errors instanceof Array){
8701 for(var i = 0, len = errors.length; i < len; i++){
8702 var fieldError = errors[i];
8703 var f = this.findField(fieldError.id);
8705 f.markInvalid(fieldError.msg);
8711 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8712 field.markInvalid(errors[id]);
8716 //Roo.each(this.childForms || [], function (f) {
8717 // f.markInvalid(errors);
8724 * Set values for fields in this form in bulk.
8725 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8726 * @return {BasicForm} this
8728 setValues : function(values){
8729 if(values instanceof Array){ // array of objects
8730 for(var i = 0, len = values.length; i < len; i++){
8732 var f = this.findField(v.id);
8734 f.setValue(v.value);
8735 if(this.trackResetOnLoad){
8736 f.originalValue = f.getValue();
8740 }else{ // object hash
8743 if(typeof values[id] != 'function' && (field = this.findField(id))){
8745 if (field.setFromData &&
8747 field.displayField &&
8748 // combos' with local stores can
8749 // be queried via setValue()
8750 // to set their value..
8751 (field.store && !field.store.isLocal)
8755 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8756 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8757 field.setFromData(sd);
8759 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8761 field.setFromData(values);
8764 field.setValue(values[id]);
8768 if(this.trackResetOnLoad){
8769 field.originalValue = field.getValue();
8775 //Roo.each(this.childForms || [], function (f) {
8776 // f.setValues(values);
8783 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8784 * they are returned as an array.
8785 * @param {Boolean} asString
8788 getValues : function(asString){
8789 //if (this.childForms) {
8790 // copy values from the child forms
8791 // Roo.each(this.childForms, function (f) {
8792 // this.setValues(f.getValues());
8798 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8799 if(asString === true){
8802 return Roo.urlDecode(fs);
8806 * Returns the fields in this form as an object with key/value pairs.
8807 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8810 getFieldValues : function(with_hidden)
8812 var items = this.getItems();
8814 items.each(function(f){
8820 var v = f.getValue();
8822 if (f.inputType =='radio') {
8823 if (typeof(ret[f.getName()]) == 'undefined') {
8824 ret[f.getName()] = ''; // empty..
8827 if (!f.el.dom.checked) {
8835 if(f.xtype == 'MoneyField'){
8836 ret[f.currencyName] = f.getCurrency();
8839 // not sure if this supported any more..
8840 if ((typeof(v) == 'object') && f.getRawValue) {
8841 v = f.getRawValue() ; // dates..
8843 // combo boxes where name != hiddenName...
8844 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8845 ret[f.name] = f.getRawValue();
8847 ret[f.getName()] = v;
8854 * Clears all invalid messages in this form.
8855 * @return {BasicForm} this
8857 clearInvalid : function(){
8858 var items = this.getItems();
8860 items.each(function(f){
8869 * @return {BasicForm} this
8872 var items = this.getItems();
8873 items.each(function(f){
8877 Roo.each(this.childForms || [], function (f) {
8885 getItems : function()
8887 var r=new Roo.util.MixedCollection(false, function(o){
8888 return o.id || (o.id = Roo.id());
8890 var iter = function(el) {
8897 Roo.each(el.items,function(e) {
8906 hideFields : function(items)
8908 Roo.each(items, function(i){
8910 var f = this.findField(i);
8921 showFields : function(items)
8923 Roo.each(items, function(i){
8925 var f = this.findField(i);
8938 Roo.apply(Roo.bootstrap.Form, {
8965 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8966 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8967 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8968 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8971 this.maskEl.top.enableDisplayMode("block");
8972 this.maskEl.left.enableDisplayMode("block");
8973 this.maskEl.bottom.enableDisplayMode("block");
8974 this.maskEl.right.enableDisplayMode("block");
8976 this.toolTip = new Roo.bootstrap.Tooltip({
8977 cls : 'roo-form-error-popover',
8979 'left' : ['r-l', [-2,0], 'right'],
8980 'right' : ['l-r', [2,0], 'left'],
8981 'bottom' : ['tl-bl', [0,2], 'top'],
8982 'top' : [ 'bl-tl', [0,-2], 'bottom']
8986 this.toolTip.render(Roo.get(document.body));
8988 this.toolTip.el.enableDisplayMode("block");
8990 Roo.get(document.body).on('click', function(){
8994 Roo.get(document.body).on('touchstart', function(){
8998 this.isApplied = true
9001 mask : function(form, target)
9005 this.target = target;
9007 if(!this.form.errorMask || !target.el){
9011 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9013 Roo.log(scrollable);
9015 var ot = this.target.el.calcOffsetsTo(scrollable);
9017 var scrollTo = ot[1] - this.form.maskOffset;
9019 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9021 scrollable.scrollTo('top', scrollTo);
9023 var box = this.target.el.getBox();
9025 var zIndex = Roo.bootstrap.Modal.zIndex++;
9028 this.maskEl.top.setStyle('position', 'absolute');
9029 this.maskEl.top.setStyle('z-index', zIndex);
9030 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9031 this.maskEl.top.setLeft(0);
9032 this.maskEl.top.setTop(0);
9033 this.maskEl.top.show();
9035 this.maskEl.left.setStyle('position', 'absolute');
9036 this.maskEl.left.setStyle('z-index', zIndex);
9037 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9038 this.maskEl.left.setLeft(0);
9039 this.maskEl.left.setTop(box.y - this.padding);
9040 this.maskEl.left.show();
9042 this.maskEl.bottom.setStyle('position', 'absolute');
9043 this.maskEl.bottom.setStyle('z-index', zIndex);
9044 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9045 this.maskEl.bottom.setLeft(0);
9046 this.maskEl.bottom.setTop(box.bottom + this.padding);
9047 this.maskEl.bottom.show();
9049 this.maskEl.right.setStyle('position', 'absolute');
9050 this.maskEl.right.setStyle('z-index', zIndex);
9051 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9052 this.maskEl.right.setLeft(box.right + this.padding);
9053 this.maskEl.right.setTop(box.y - this.padding);
9054 this.maskEl.right.show();
9056 this.toolTip.bindEl = this.target.el;
9058 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
9060 var tip = this.target.blankText;
9062 if(this.target.getValue() !== '' ) {
9064 if (this.target.invalidText.length) {
9065 tip = this.target.invalidText;
9066 } else if (this.target.regexText.length){
9067 tip = this.target.regexText;
9071 this.toolTip.show(tip);
9073 this.intervalID = window.setInterval(function() {
9074 Roo.bootstrap.Form.popover.unmask();
9077 window.onwheel = function(){ return false;};
9079 (function(){ this.isMasked = true; }).defer(500, this);
9085 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
9089 this.maskEl.top.setStyle('position', 'absolute');
9090 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
9091 this.maskEl.top.hide();
9093 this.maskEl.left.setStyle('position', 'absolute');
9094 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
9095 this.maskEl.left.hide();
9097 this.maskEl.bottom.setStyle('position', 'absolute');
9098 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
9099 this.maskEl.bottom.hide();
9101 this.maskEl.right.setStyle('position', 'absolute');
9102 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
9103 this.maskEl.right.hide();
9105 this.toolTip.hide();
9107 this.toolTip.el.hide();
9109 window.onwheel = function(){ return true;};
9111 if(this.intervalID){
9112 window.clearInterval(this.intervalID);
9113 this.intervalID = false;
9116 this.isMasked = false;
9126 * Ext JS Library 1.1.1
9127 * Copyright(c) 2006-2007, Ext JS, LLC.
9129 * Originally Released Under LGPL - original licence link has changed is not relivant.
9132 * <script type="text/javascript">
9135 * @class Roo.form.VTypes
9136 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
9139 Roo.form.VTypes = function(){
9140 // closure these in so they are only created once.
9141 var alpha = /^[a-zA-Z_]+$/;
9142 var alphanum = /^[a-zA-Z0-9_]+$/;
9143 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
9144 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
9146 // All these messages and functions are configurable
9149 * The function used to validate email addresses
9150 * @param {String} value The email address
9152 'email' : function(v){
9153 return email.test(v);
9156 * The error text to display when the email validation function returns false
9159 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
9161 * The keystroke filter mask to be applied on email input
9164 'emailMask' : /[a-z0-9_\.\-@]/i,
9167 * The function used to validate URLs
9168 * @param {String} value The URL
9170 'url' : function(v){
9174 * The error text to display when the url validation function returns false
9177 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
9180 * The function used to validate alpha values
9181 * @param {String} value The value
9183 'alpha' : function(v){
9184 return alpha.test(v);
9187 * The error text to display when the alpha validation function returns false
9190 'alphaText' : 'This field should only contain letters and _',
9192 * The keystroke filter mask to be applied on alpha input
9195 'alphaMask' : /[a-z_]/i,
9198 * The function used to validate alphanumeric values
9199 * @param {String} value The value
9201 'alphanum' : function(v){
9202 return alphanum.test(v);
9205 * The error text to display when the alphanumeric validation function returns false
9208 'alphanumText' : 'This field should only contain letters, numbers and _',
9210 * The keystroke filter mask to be applied on alphanumeric input
9213 'alphanumMask' : /[a-z0-9_]/i
9223 * @class Roo.bootstrap.Input
9224 * @extends Roo.bootstrap.Component
9225 * Bootstrap Input class
9226 * @cfg {Boolean} disabled is it disabled
9227 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
9228 * @cfg {String} name name of the input
9229 * @cfg {string} fieldLabel - the label associated
9230 * @cfg {string} placeholder - placeholder to put in text.
9231 * @cfg {string} before - input group add on before
9232 * @cfg {string} after - input group add on after
9233 * @cfg {string} size - (lg|sm) or leave empty..
9234 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
9235 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
9236 * @cfg {Number} md colspan out of 12 for computer-sized screens
9237 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
9238 * @cfg {string} value default value of the input
9239 * @cfg {Number} labelWidth set the width of label
9240 * @cfg {Number} labellg set the width of label (1-12)
9241 * @cfg {Number} labelmd set the width of label (1-12)
9242 * @cfg {Number} labelsm set the width of label (1-12)
9243 * @cfg {Number} labelxs set the width of label (1-12)
9244 * @cfg {String} labelAlign (top|left)
9245 * @cfg {Boolean} readOnly Specifies that the field should be read-only
9246 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9247 * @cfg {String} indicatorpos (left|right) default left
9248 * @cfg {String} capture (user|camera) use for file input only. (default empty)
9249 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9251 * @cfg {String} align (left|center|right) Default left
9252 * @cfg {Boolean} forceFeedback (true|false) Default false
9255 * Create a new Input
9256 * @param {Object} config The config object
9259 Roo.bootstrap.Input = function(config){
9261 Roo.bootstrap.Input.superclass.constructor.call(this, config);
9266 * Fires when this field receives input focus.
9267 * @param {Roo.form.Field} this
9272 * Fires when this field loses input focus.
9273 * @param {Roo.form.Field} this
9278 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
9279 * {@link Roo.EventObject#getKey} to determine which key was pressed.
9280 * @param {Roo.form.Field} this
9281 * @param {Roo.EventObject} e The event object
9286 * Fires just before the field blurs if the field value has changed.
9287 * @param {Roo.form.Field} this
9288 * @param {Mixed} newValue The new value
9289 * @param {Mixed} oldValue The original value
9294 * Fires after the field has been marked as invalid.
9295 * @param {Roo.form.Field} this
9296 * @param {String} msg The validation message
9301 * Fires after the field has been validated with no errors.
9302 * @param {Roo.form.Field} this
9307 * Fires after the key up
9308 * @param {Roo.form.Field} this
9309 * @param {Roo.EventObject} e The event Object
9315 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
9317 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9318 automatic validation (defaults to "keyup").
9320 validationEvent : "keyup",
9322 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9324 validateOnBlur : true,
9326 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9328 validationDelay : 250,
9330 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9332 focusClass : "x-form-focus", // not needed???
9336 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9338 invalidClass : "has-warning",
9341 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9343 validClass : "has-success",
9346 * @cfg {Boolean} hasFeedback (true|false) default true
9351 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9353 invalidFeedbackClass : "glyphicon-warning-sign",
9356 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9358 validFeedbackClass : "glyphicon-ok",
9361 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9363 selectOnFocus : false,
9366 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9370 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9375 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9377 disableKeyFilter : false,
9380 * @cfg {Boolean} disabled True to disable the field (defaults to false).
9384 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9388 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9390 blankText : "Please complete this mandatory field",
9393 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9397 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9399 maxLength : Number.MAX_VALUE,
9401 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9403 minLengthText : "The minimum length for this field is {0}",
9405 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9407 maxLengthText : "The maximum length for this field is {0}",
9411 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9412 * If available, this function will be called only after the basic validators all return true, and will be passed the
9413 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9417 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9418 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9419 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
9423 * @cfg {String} regexText -- Depricated - use Invalid Text
9428 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9434 autocomplete: false,
9453 formatedValue : false,
9454 forceFeedback : false,
9456 indicatorpos : 'left',
9466 parentLabelAlign : function()
9469 while (parent.parent()) {
9470 parent = parent.parent();
9471 if (typeof(parent.labelAlign) !='undefined') {
9472 return parent.labelAlign;
9479 getAutoCreate : function()
9481 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9487 if(this.inputType != 'hidden'){
9488 cfg.cls = 'form-group' //input-group
9494 type : this.inputType,
9496 cls : 'form-control',
9497 placeholder : this.placeholder || '',
9498 autocomplete : this.autocomplete || 'new-password'
9501 if(this.capture.length){
9502 input.capture = this.capture;
9505 if(this.accept.length){
9506 input.accept = this.accept + "/*";
9510 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9513 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9514 input.maxLength = this.maxLength;
9517 if (this.disabled) {
9518 input.disabled=true;
9521 if (this.readOnly) {
9522 input.readonly=true;
9526 input.name = this.name;
9530 input.cls += ' input-' + this.size;
9534 ['xs','sm','md','lg'].map(function(size){
9535 if (settings[size]) {
9536 cfg.cls += ' col-' + size + '-' + settings[size];
9540 var inputblock = input;
9544 cls: 'glyphicon form-control-feedback'
9547 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9550 cls : 'has-feedback',
9558 if (this.before || this.after) {
9561 cls : 'input-group',
9565 if (this.before && typeof(this.before) == 'string') {
9567 inputblock.cn.push({
9569 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9573 if (this.before && typeof(this.before) == 'object') {
9574 this.before = Roo.factory(this.before);
9576 inputblock.cn.push({
9578 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9579 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9583 inputblock.cn.push(input);
9585 if (this.after && typeof(this.after) == 'string') {
9586 inputblock.cn.push({
9588 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9592 if (this.after && typeof(this.after) == 'object') {
9593 this.after = Roo.factory(this.after);
9595 inputblock.cn.push({
9597 cls : 'roo-input-after input-group-append input-group-text input-group-' +
9598 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9602 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9603 inputblock.cls += ' has-feedback';
9604 inputblock.cn.push(feedback);
9609 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9610 tooltip : 'This field is required'
9612 if (Roo.bootstrap.version == 4) {
9615 style : 'display-none'
9618 if (align ==='left' && this.fieldLabel.length) {
9620 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
9627 cls : 'control-label col-form-label',
9628 html : this.fieldLabel
9639 var labelCfg = cfg.cn[1];
9640 var contentCfg = cfg.cn[2];
9642 if(this.indicatorpos == 'right'){
9647 cls : 'control-label col-form-label',
9651 html : this.fieldLabel
9665 labelCfg = cfg.cn[0];
9666 contentCfg = cfg.cn[1];
9670 if(this.labelWidth > 12){
9671 labelCfg.style = "width: " + this.labelWidth + 'px';
9674 if(this.labelWidth < 13 && this.labelmd == 0){
9675 this.labelmd = this.labelWidth;
9678 if(this.labellg > 0){
9679 labelCfg.cls += ' col-lg-' + this.labellg;
9680 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9683 if(this.labelmd > 0){
9684 labelCfg.cls += ' col-md-' + this.labelmd;
9685 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9688 if(this.labelsm > 0){
9689 labelCfg.cls += ' col-sm-' + this.labelsm;
9690 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9693 if(this.labelxs > 0){
9694 labelCfg.cls += ' col-xs-' + this.labelxs;
9695 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9699 } else if ( this.fieldLabel.length) {
9704 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9705 tooltip : 'This field is required'
9709 //cls : 'input-group-addon',
9710 html : this.fieldLabel
9718 if(this.indicatorpos == 'right'){
9723 //cls : 'input-group-addon',
9724 html : this.fieldLabel
9729 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9730 tooltip : 'This field is required'
9750 if (this.parentType === 'Navbar' && this.parent().bar) {
9751 cfg.cls += ' navbar-form';
9754 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9755 // on BS4 we do this only if not form
9756 cfg.cls += ' navbar-form';
9764 * return the real input element.
9766 inputEl: function ()
9768 return this.el.select('input.form-control',true).first();
9771 tooltipEl : function()
9773 return this.inputEl();
9776 indicatorEl : function()
9778 if (Roo.bootstrap.version == 4) {
9779 return false; // not enabled in v4 yet.
9782 var indicator = this.el.select('i.roo-required-indicator',true).first();
9792 setDisabled : function(v)
9794 var i = this.inputEl().dom;
9796 i.removeAttribute('disabled');
9800 i.setAttribute('disabled','true');
9802 initEvents : function()
9805 this.inputEl().on("keydown" , this.fireKey, this);
9806 this.inputEl().on("focus", this.onFocus, this);
9807 this.inputEl().on("blur", this.onBlur, this);
9809 this.inputEl().relayEvent('keyup', this);
9811 this.indicator = this.indicatorEl();
9814 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9817 // reference to original value for reset
9818 this.originalValue = this.getValue();
9819 //Roo.form.TextField.superclass.initEvents.call(this);
9820 if(this.validationEvent == 'keyup'){
9821 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9822 this.inputEl().on('keyup', this.filterValidation, this);
9824 else if(this.validationEvent !== false){
9825 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9828 if(this.selectOnFocus){
9829 this.on("focus", this.preFocus, this);
9832 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9833 this.inputEl().on("keypress", this.filterKeys, this);
9835 this.inputEl().relayEvent('keypress', this);
9838 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9839 this.el.on("click", this.autoSize, this);
9842 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9843 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9846 if (typeof(this.before) == 'object') {
9847 this.before.render(this.el.select('.roo-input-before',true).first());
9849 if (typeof(this.after) == 'object') {
9850 this.after.render(this.el.select('.roo-input-after',true).first());
9853 this.inputEl().on('change', this.onChange, this);
9856 filterValidation : function(e){
9857 if(!e.isNavKeyPress()){
9858 this.validationTask.delay(this.validationDelay);
9862 * Validates the field value
9863 * @return {Boolean} True if the value is valid, else false
9865 validate : function(){
9866 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9867 if(this.disabled || this.validateValue(this.getRawValue())){
9878 * Validates a value according to the field's validation rules and marks the field as invalid
9879 * if the validation fails
9880 * @param {Mixed} value The value to validate
9881 * @return {Boolean} True if the value is valid, else false
9883 validateValue : function(value)
9885 if(this.getVisibilityEl().hasClass('hidden')){
9889 if(value.length < 1) { // if it's blank
9890 if(this.allowBlank){
9896 if(value.length < this.minLength){
9899 if(value.length > this.maxLength){
9903 var vt = Roo.form.VTypes;
9904 if(!vt[this.vtype](value, this)){
9908 if(typeof this.validator == "function"){
9909 var msg = this.validator(value);
9913 if (typeof(msg) == 'string') {
9914 this.invalidText = msg;
9918 if(this.regex && !this.regex.test(value)){
9926 fireKey : function(e){
9927 //Roo.log('field ' + e.getKey());
9928 if(e.isNavKeyPress()){
9929 this.fireEvent("specialkey", this, e);
9932 focus : function (selectText){
9934 this.inputEl().focus();
9935 if(selectText === true){
9936 this.inputEl().dom.select();
9942 onFocus : function(){
9943 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9944 // this.el.addClass(this.focusClass);
9947 this.hasFocus = true;
9948 this.startValue = this.getValue();
9949 this.fireEvent("focus", this);
9953 beforeBlur : Roo.emptyFn,
9957 onBlur : function(){
9959 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9960 //this.el.removeClass(this.focusClass);
9962 this.hasFocus = false;
9963 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9966 var v = this.getValue();
9967 if(String(v) !== String(this.startValue)){
9968 this.fireEvent('change', this, v, this.startValue);
9970 this.fireEvent("blur", this);
9973 onChange : function(e)
9975 var v = this.getValue();
9976 if(String(v) !== String(this.startValue)){
9977 this.fireEvent('change', this, v, this.startValue);
9983 * Resets the current field value to the originally loaded value and clears any validation messages
9986 this.setValue(this.originalValue);
9990 * Returns the name of the field
9991 * @return {Mixed} name The name field
9993 getName: function(){
9997 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9998 * @return {Mixed} value The field value
10000 getValue : function(){
10002 var v = this.inputEl().getValue();
10007 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
10008 * @return {Mixed} value The field value
10010 getRawValue : function(){
10011 var v = this.inputEl().getValue();
10017 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
10018 * @param {Mixed} value The value to set
10020 setRawValue : function(v){
10021 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10024 selectText : function(start, end){
10025 var v = this.getRawValue();
10027 start = start === undefined ? 0 : start;
10028 end = end === undefined ? v.length : end;
10029 var d = this.inputEl().dom;
10030 if(d.setSelectionRange){
10031 d.setSelectionRange(start, end);
10032 }else if(d.createTextRange){
10033 var range = d.createTextRange();
10034 range.moveStart("character", start);
10035 range.moveEnd("character", v.length-end);
10042 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
10043 * @param {Mixed} value The value to set
10045 setValue : function(v){
10048 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10054 processValue : function(value){
10055 if(this.stripCharsRe){
10056 var newValue = value.replace(this.stripCharsRe, '');
10057 if(newValue !== value){
10058 this.setRawValue(newValue);
10065 preFocus : function(){
10067 if(this.selectOnFocus){
10068 this.inputEl().dom.select();
10071 filterKeys : function(e){
10072 var k = e.getKey();
10073 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
10076 var c = e.getCharCode(), cc = String.fromCharCode(c);
10077 if(Roo.isIE && (e.isSpecialKey() || !cc)){
10080 if(!this.maskRe.test(cc)){
10085 * Clear any invalid styles/messages for this field
10087 clearInvalid : function(){
10089 if(!this.el || this.preventMark){ // not rendered
10094 this.el.removeClass([this.invalidClass, 'is-invalid']);
10096 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10098 var feedback = this.el.select('.form-control-feedback', true).first();
10101 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10106 if(this.indicator){
10107 this.indicator.removeClass('visible');
10108 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10111 this.fireEvent('valid', this);
10115 * Mark this field as valid
10117 markValid : function()
10119 if(!this.el || this.preventMark){ // not rendered...
10123 this.el.removeClass([this.invalidClass, this.validClass]);
10124 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10126 var feedback = this.el.select('.form-control-feedback', true).first();
10129 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10132 if(this.indicator){
10133 this.indicator.removeClass('visible');
10134 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10141 if(this.allowBlank && !this.getRawValue().length){
10144 if (Roo.bootstrap.version == 3) {
10145 this.el.addClass(this.validClass);
10147 this.inputEl().addClass('is-valid');
10150 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10152 var feedback = this.el.select('.form-control-feedback', true).first();
10155 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10156 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10161 this.fireEvent('valid', this);
10165 * Mark this field as invalid
10166 * @param {String} msg The validation message
10168 markInvalid : function(msg)
10170 if(!this.el || this.preventMark){ // not rendered
10174 this.el.removeClass([this.invalidClass, this.validClass]);
10175 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10177 var feedback = this.el.select('.form-control-feedback', true).first();
10180 this.el.select('.form-control-feedback', true).first().removeClass(
10181 [this.invalidFeedbackClass, this.validFeedbackClass]);
10188 if(this.allowBlank && !this.getRawValue().length){
10192 if(this.indicator){
10193 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10194 this.indicator.addClass('visible');
10196 if (Roo.bootstrap.version == 3) {
10197 this.el.addClass(this.invalidClass);
10199 this.inputEl().addClass('is-invalid');
10204 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10206 var feedback = this.el.select('.form-control-feedback', true).first();
10209 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10211 if(this.getValue().length || this.forceFeedback){
10212 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10219 this.fireEvent('invalid', this, msg);
10222 SafariOnKeyDown : function(event)
10224 // this is a workaround for a password hang bug on chrome/ webkit.
10225 if (this.inputEl().dom.type != 'password') {
10229 var isSelectAll = false;
10231 if(this.inputEl().dom.selectionEnd > 0){
10232 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
10234 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
10235 event.preventDefault();
10240 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10242 event.preventDefault();
10243 // this is very hacky as keydown always get's upper case.
10245 var cc = String.fromCharCode(event.getCharCode());
10246 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
10250 adjustWidth : function(tag, w){
10251 tag = tag.toLowerCase();
10252 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10253 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10254 if(tag == 'input'){
10257 if(tag == 'textarea'){
10260 }else if(Roo.isOpera){
10261 if(tag == 'input'){
10264 if(tag == 'textarea'){
10272 setFieldLabel : function(v)
10274 if(!this.rendered){
10278 if(this.indicatorEl()){
10279 var ar = this.el.select('label > span',true);
10281 if (ar.elements.length) {
10282 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10283 this.fieldLabel = v;
10287 var br = this.el.select('label',true);
10289 if(br.elements.length) {
10290 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10291 this.fieldLabel = v;
10295 Roo.log('Cannot Found any of label > span || label in input');
10299 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10300 this.fieldLabel = v;
10315 * @class Roo.bootstrap.TextArea
10316 * @extends Roo.bootstrap.Input
10317 * Bootstrap TextArea class
10318 * @cfg {Number} cols Specifies the visible width of a text area
10319 * @cfg {Number} rows Specifies the visible number of lines in a text area
10320 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10321 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10322 * @cfg {string} html text
10325 * Create a new TextArea
10326 * @param {Object} config The config object
10329 Roo.bootstrap.TextArea = function(config){
10330 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10334 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
10344 getAutoCreate : function(){
10346 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10352 if(this.inputType != 'hidden'){
10353 cfg.cls = 'form-group' //input-group
10361 value : this.value || '',
10362 html: this.html || '',
10363 cls : 'form-control',
10364 placeholder : this.placeholder || ''
10368 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10369 input.maxLength = this.maxLength;
10373 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10377 input.cols = this.cols;
10380 if (this.readOnly) {
10381 input.readonly = true;
10385 input.name = this.name;
10389 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10393 ['xs','sm','md','lg'].map(function(size){
10394 if (settings[size]) {
10395 cfg.cls += ' col-' + size + '-' + settings[size];
10399 var inputblock = input;
10401 if(this.hasFeedback && !this.allowBlank){
10405 cls: 'glyphicon form-control-feedback'
10409 cls : 'has-feedback',
10418 if (this.before || this.after) {
10421 cls : 'input-group',
10425 inputblock.cn.push({
10427 cls : 'input-group-addon',
10432 inputblock.cn.push(input);
10434 if(this.hasFeedback && !this.allowBlank){
10435 inputblock.cls += ' has-feedback';
10436 inputblock.cn.push(feedback);
10440 inputblock.cn.push({
10442 cls : 'input-group-addon',
10449 if (align ==='left' && this.fieldLabel.length) {
10454 cls : 'control-label',
10455 html : this.fieldLabel
10466 if(this.labelWidth > 12){
10467 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10470 if(this.labelWidth < 13 && this.labelmd == 0){
10471 this.labelmd = this.labelWidth;
10474 if(this.labellg > 0){
10475 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10476 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10479 if(this.labelmd > 0){
10480 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10481 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10484 if(this.labelsm > 0){
10485 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10486 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10489 if(this.labelxs > 0){
10490 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10491 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10494 } else if ( this.fieldLabel.length) {
10499 //cls : 'input-group-addon',
10500 html : this.fieldLabel
10518 if (this.disabled) {
10519 input.disabled=true;
10526 * return the real textarea element.
10528 inputEl: function ()
10530 return this.el.select('textarea.form-control',true).first();
10534 * Clear any invalid styles/messages for this field
10536 clearInvalid : function()
10539 if(!this.el || this.preventMark){ // not rendered
10543 var label = this.el.select('label', true).first();
10544 var icon = this.el.select('i.fa-star', true).first();
10549 this.el.removeClass( this.validClass);
10550 this.inputEl().removeClass('is-invalid');
10552 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10554 var feedback = this.el.select('.form-control-feedback', true).first();
10557 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10562 this.fireEvent('valid', this);
10566 * Mark this field as valid
10568 markValid : function()
10570 if(!this.el || this.preventMark){ // not rendered
10574 this.el.removeClass([this.invalidClass, this.validClass]);
10575 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10577 var feedback = this.el.select('.form-control-feedback', true).first();
10580 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10583 if(this.disabled || this.allowBlank){
10587 var label = this.el.select('label', true).first();
10588 var icon = this.el.select('i.fa-star', true).first();
10593 if (Roo.bootstrap.version == 3) {
10594 this.el.addClass(this.validClass);
10596 this.inputEl().addClass('is-valid');
10600 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10602 var feedback = this.el.select('.form-control-feedback', true).first();
10605 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10606 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10611 this.fireEvent('valid', this);
10615 * Mark this field as invalid
10616 * @param {String} msg The validation message
10618 markInvalid : function(msg)
10620 if(!this.el || this.preventMark){ // not rendered
10624 this.el.removeClass([this.invalidClass, this.validClass]);
10625 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10627 var feedback = this.el.select('.form-control-feedback', true).first();
10630 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10633 if(this.disabled || this.allowBlank){
10637 var label = this.el.select('label', true).first();
10638 var icon = this.el.select('i.fa-star', true).first();
10640 if(!this.getValue().length && label && !icon){
10641 this.el.createChild({
10643 cls : 'text-danger fa fa-lg fa-star',
10644 tooltip : 'This field is required',
10645 style : 'margin-right:5px;'
10649 if (Roo.bootstrap.version == 3) {
10650 this.el.addClass(this.invalidClass);
10652 this.inputEl().addClass('is-invalid');
10655 // fixme ... this may be depricated need to test..
10656 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10658 var feedback = this.el.select('.form-control-feedback', true).first();
10661 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10663 if(this.getValue().length || this.forceFeedback){
10664 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10671 this.fireEvent('invalid', this, msg);
10679 * trigger field - base class for combo..
10684 * @class Roo.bootstrap.TriggerField
10685 * @extends Roo.bootstrap.Input
10686 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10687 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10688 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10689 * for which you can provide a custom implementation. For example:
10691 var trigger = new Roo.bootstrap.TriggerField();
10692 trigger.onTriggerClick = myTriggerFn;
10693 trigger.applyTo('my-field');
10696 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10697 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10698 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10699 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10700 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
10703 * Create a new TriggerField.
10704 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10705 * to the base TextField)
10707 Roo.bootstrap.TriggerField = function(config){
10708 this.mimicing = false;
10709 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10712 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10714 * @cfg {String} triggerClass A CSS class to apply to the trigger
10717 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10722 * @cfg {Boolean} removable (true|false) special filter default false
10726 /** @cfg {Boolean} grow @hide */
10727 /** @cfg {Number} growMin @hide */
10728 /** @cfg {Number} growMax @hide */
10734 autoSize: Roo.emptyFn,
10738 deferHeight : true,
10741 actionMode : 'wrap',
10746 getAutoCreate : function(){
10748 var align = this.labelAlign || this.parentLabelAlign();
10753 cls: 'form-group' //input-group
10760 type : this.inputType,
10761 cls : 'form-control',
10762 autocomplete: 'new-password',
10763 placeholder : this.placeholder || ''
10767 input.name = this.name;
10770 input.cls += ' input-' + this.size;
10773 if (this.disabled) {
10774 input.disabled=true;
10777 var inputblock = input;
10779 if(this.hasFeedback && !this.allowBlank){
10783 cls: 'glyphicon form-control-feedback'
10786 if(this.removable && !this.editable && !this.tickable){
10788 cls : 'has-feedback',
10794 cls : 'roo-combo-removable-btn close'
10801 cls : 'has-feedback',
10810 if(this.removable && !this.editable && !this.tickable){
10812 cls : 'roo-removable',
10818 cls : 'roo-combo-removable-btn close'
10825 if (this.before || this.after) {
10828 cls : 'input-group',
10832 inputblock.cn.push({
10834 cls : 'input-group-addon input-group-prepend input-group-text',
10839 inputblock.cn.push(input);
10841 if(this.hasFeedback && !this.allowBlank){
10842 inputblock.cls += ' has-feedback';
10843 inputblock.cn.push(feedback);
10847 inputblock.cn.push({
10849 cls : 'input-group-addon input-group-append input-group-text',
10858 var ibwrap = inputblock;
10863 cls: 'roo-select2-choices',
10867 cls: 'roo-select2-search-field',
10879 cls: 'roo-select2-container input-group',
10884 cls: 'form-hidden-field'
10890 if(!this.multiple && this.showToggleBtn){
10896 if (this.caret != false) {
10899 cls: 'fa fa-' + this.caret
10906 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10908 Roo.bootstrap.version == 3 ? caret : '',
10911 cls: 'combobox-clear',
10925 combobox.cls += ' roo-select2-container-multi';
10929 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10930 tooltip : 'This field is required'
10932 if (Roo.bootstrap.version == 4) {
10935 style : 'display:none'
10940 if (align ==='left' && this.fieldLabel.length) {
10942 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10949 cls : 'control-label',
10950 html : this.fieldLabel
10962 var labelCfg = cfg.cn[1];
10963 var contentCfg = cfg.cn[2];
10965 if(this.indicatorpos == 'right'){
10970 cls : 'control-label',
10974 html : this.fieldLabel
10988 labelCfg = cfg.cn[0];
10989 contentCfg = cfg.cn[1];
10992 if(this.labelWidth > 12){
10993 labelCfg.style = "width: " + this.labelWidth + 'px';
10996 if(this.labelWidth < 13 && this.labelmd == 0){
10997 this.labelmd = this.labelWidth;
11000 if(this.labellg > 0){
11001 labelCfg.cls += ' col-lg-' + this.labellg;
11002 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11005 if(this.labelmd > 0){
11006 labelCfg.cls += ' col-md-' + this.labelmd;
11007 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11010 if(this.labelsm > 0){
11011 labelCfg.cls += ' col-sm-' + this.labelsm;
11012 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11015 if(this.labelxs > 0){
11016 labelCfg.cls += ' col-xs-' + this.labelxs;
11017 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11020 } else if ( this.fieldLabel.length) {
11021 // Roo.log(" label");
11026 //cls : 'input-group-addon',
11027 html : this.fieldLabel
11035 if(this.indicatorpos == 'right'){
11043 html : this.fieldLabel
11057 // Roo.log(" no label && no align");
11064 ['xs','sm','md','lg'].map(function(size){
11065 if (settings[size]) {
11066 cfg.cls += ' col-' + size + '-' + settings[size];
11077 onResize : function(w, h){
11078 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
11079 // if(typeof w == 'number'){
11080 // var x = w - this.trigger.getWidth();
11081 // this.inputEl().setWidth(this.adjustWidth('input', x));
11082 // this.trigger.setStyle('left', x+'px');
11087 adjustSize : Roo.BoxComponent.prototype.adjustSize,
11090 getResizeEl : function(){
11091 return this.inputEl();
11095 getPositionEl : function(){
11096 return this.inputEl();
11100 alignErrorIcon : function(){
11101 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
11105 initEvents : function(){
11109 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
11110 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
11111 if(!this.multiple && this.showToggleBtn){
11112 this.trigger = this.el.select('span.dropdown-toggle',true).first();
11113 if(this.hideTrigger){
11114 this.trigger.setDisplayed(false);
11116 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
11120 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
11123 if(this.removable && !this.editable && !this.tickable){
11124 var close = this.closeTriggerEl();
11127 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
11128 close.on('click', this.removeBtnClick, this, close);
11132 //this.trigger.addClassOnOver('x-form-trigger-over');
11133 //this.trigger.addClassOnClick('x-form-trigger-click');
11136 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
11140 closeTriggerEl : function()
11142 var close = this.el.select('.roo-combo-removable-btn', true).first();
11143 return close ? close : false;
11146 removeBtnClick : function(e, h, el)
11148 e.preventDefault();
11150 if(this.fireEvent("remove", this) !== false){
11152 this.fireEvent("afterremove", this)
11156 createList : function()
11158 this.list = Roo.get(document.body).createChild({
11159 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
11160 cls: 'typeahead typeahead-long dropdown-menu',
11161 style: 'display:none'
11164 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
11169 initTrigger : function(){
11174 onDestroy : function(){
11176 this.trigger.removeAllListeners();
11177 // this.trigger.remove();
11180 // this.wrap.remove();
11182 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
11186 onFocus : function(){
11187 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
11189 if(!this.mimicing){
11190 this.wrap.addClass('x-trigger-wrap-focus');
11191 this.mimicing = true;
11192 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
11193 if(this.monitorTab){
11194 this.el.on("keydown", this.checkTab, this);
11201 checkTab : function(e){
11202 if(e.getKey() == e.TAB){
11203 this.triggerBlur();
11208 onBlur : function(){
11213 mimicBlur : function(e, t){
11215 if(!this.wrap.contains(t) && this.validateBlur()){
11216 this.triggerBlur();
11222 triggerBlur : function(){
11223 this.mimicing = false;
11224 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
11225 if(this.monitorTab){
11226 this.el.un("keydown", this.checkTab, this);
11228 //this.wrap.removeClass('x-trigger-wrap-focus');
11229 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
11233 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
11234 validateBlur : function(e, t){
11239 onDisable : function(){
11240 this.inputEl().dom.disabled = true;
11241 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11243 // this.wrap.addClass('x-item-disabled');
11248 onEnable : function(){
11249 this.inputEl().dom.disabled = false;
11250 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11252 // this.el.removeClass('x-item-disabled');
11257 onShow : function(){
11258 var ae = this.getActionEl();
11261 ae.dom.style.display = '';
11262 ae.dom.style.visibility = 'visible';
11268 onHide : function(){
11269 var ae = this.getActionEl();
11270 ae.dom.style.display = 'none';
11274 * The function that should handle the trigger's click event. This method does nothing by default until overridden
11275 * by an implementing function.
11277 * @param {EventObject} e
11279 onTriggerClick : Roo.emptyFn
11283 * Ext JS Library 1.1.1
11284 * Copyright(c) 2006-2007, Ext JS, LLC.
11286 * Originally Released Under LGPL - original licence link has changed is not relivant.
11289 * <script type="text/javascript">
11294 * @class Roo.data.SortTypes
11296 * Defines the default sorting (casting?) comparison functions used when sorting data.
11298 Roo.data.SortTypes = {
11300 * Default sort that does nothing
11301 * @param {Mixed} s The value being converted
11302 * @return {Mixed} The comparison value
11304 none : function(s){
11309 * The regular expression used to strip tags
11313 stripTagsRE : /<\/?[^>]+>/gi,
11316 * Strips all HTML tags to sort on text only
11317 * @param {Mixed} s The value being converted
11318 * @return {String} The comparison value
11320 asText : function(s){
11321 return String(s).replace(this.stripTagsRE, "");
11325 * Strips all HTML tags to sort on text only - Case insensitive
11326 * @param {Mixed} s The value being converted
11327 * @return {String} The comparison value
11329 asUCText : function(s){
11330 return String(s).toUpperCase().replace(this.stripTagsRE, "");
11334 * Case insensitive string
11335 * @param {Mixed} s The value being converted
11336 * @return {String} The comparison value
11338 asUCString : function(s) {
11339 return String(s).toUpperCase();
11344 * @param {Mixed} s The value being converted
11345 * @return {Number} The comparison value
11347 asDate : function(s) {
11351 if(s instanceof Date){
11352 return s.getTime();
11354 return Date.parse(String(s));
11359 * @param {Mixed} s The value being converted
11360 * @return {Float} The comparison value
11362 asFloat : function(s) {
11363 var val = parseFloat(String(s).replace(/,/g, ""));
11372 * @param {Mixed} s The value being converted
11373 * @return {Number} The comparison value
11375 asInt : function(s) {
11376 var val = parseInt(String(s).replace(/,/g, ""));
11384 * Ext JS Library 1.1.1
11385 * Copyright(c) 2006-2007, Ext JS, LLC.
11387 * Originally Released Under LGPL - original licence link has changed is not relivant.
11390 * <script type="text/javascript">
11394 * @class Roo.data.Record
11395 * Instances of this class encapsulate both record <em>definition</em> information, and record
11396 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11397 * to access Records cached in an {@link Roo.data.Store} object.<br>
11399 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11400 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11403 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11405 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11406 * {@link #create}. The parameters are the same.
11407 * @param {Array} data An associative Array of data values keyed by the field name.
11408 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11409 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11410 * not specified an integer id is generated.
11412 Roo.data.Record = function(data, id){
11413 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11418 * Generate a constructor for a specific record layout.
11419 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11420 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11421 * Each field definition object may contain the following properties: <ul>
11422 * <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,
11423 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11424 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11425 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11426 * is being used, then this is a string containing the javascript expression to reference the data relative to
11427 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11428 * to the data item relative to the record element. If the mapping expression is the same as the field name,
11429 * this may be omitted.</p></li>
11430 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11431 * <ul><li>auto (Default, implies no conversion)</li>
11436 * <li>date</li></ul></p></li>
11437 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11438 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11439 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11440 * by the Reader into an object that will be stored in the Record. It is passed the
11441 * following parameters:<ul>
11442 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11444 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11446 * <br>usage:<br><pre><code>
11447 var TopicRecord = Roo.data.Record.create(
11448 {name: 'title', mapping: 'topic_title'},
11449 {name: 'author', mapping: 'username'},
11450 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11451 {name: 'lastPost', mapping: 'post_time', type: 'date'},
11452 {name: 'lastPoster', mapping: 'user2'},
11453 {name: 'excerpt', mapping: 'post_text'}
11456 var myNewRecord = new TopicRecord({
11457 title: 'Do my job please',
11460 lastPost: new Date(),
11461 lastPoster: 'Animal',
11462 excerpt: 'No way dude!'
11464 myStore.add(myNewRecord);
11469 Roo.data.Record.create = function(o){
11470 var f = function(){
11471 f.superclass.constructor.apply(this, arguments);
11473 Roo.extend(f, Roo.data.Record);
11474 var p = f.prototype;
11475 p.fields = new Roo.util.MixedCollection(false, function(field){
11478 for(var i = 0, len = o.length; i < len; i++){
11479 p.fields.add(new Roo.data.Field(o[i]));
11481 f.getField = function(name){
11482 return p.fields.get(name);
11487 Roo.data.Record.AUTO_ID = 1000;
11488 Roo.data.Record.EDIT = 'edit';
11489 Roo.data.Record.REJECT = 'reject';
11490 Roo.data.Record.COMMIT = 'commit';
11492 Roo.data.Record.prototype = {
11494 * Readonly flag - true if this record has been modified.
11503 join : function(store){
11504 this.store = store;
11508 * Set the named field to the specified value.
11509 * @param {String} name The name of the field to set.
11510 * @param {Object} value The value to set the field to.
11512 set : function(name, value){
11513 if(this.data[name] == value){
11517 if(!this.modified){
11518 this.modified = {};
11520 if(typeof this.modified[name] == 'undefined'){
11521 this.modified[name] = this.data[name];
11523 this.data[name] = value;
11524 if(!this.editing && this.store){
11525 this.store.afterEdit(this);
11530 * Get the value of the named field.
11531 * @param {String} name The name of the field to get the value of.
11532 * @return {Object} The value of the field.
11534 get : function(name){
11535 return this.data[name];
11539 beginEdit : function(){
11540 this.editing = true;
11541 this.modified = {};
11545 cancelEdit : function(){
11546 this.editing = false;
11547 delete this.modified;
11551 endEdit : function(){
11552 this.editing = false;
11553 if(this.dirty && this.store){
11554 this.store.afterEdit(this);
11559 * Usually called by the {@link Roo.data.Store} which owns the Record.
11560 * Rejects all changes made to the Record since either creation, or the last commit operation.
11561 * Modified fields are reverted to their original values.
11563 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11564 * of reject operations.
11566 reject : function(){
11567 var m = this.modified;
11569 if(typeof m[n] != "function"){
11570 this.data[n] = m[n];
11573 this.dirty = false;
11574 delete this.modified;
11575 this.editing = false;
11577 this.store.afterReject(this);
11582 * Usually called by the {@link Roo.data.Store} which owns the Record.
11583 * Commits all changes made to the Record since either creation, or the last commit operation.
11585 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11586 * of commit operations.
11588 commit : function(){
11589 this.dirty = false;
11590 delete this.modified;
11591 this.editing = false;
11593 this.store.afterCommit(this);
11598 hasError : function(){
11599 return this.error != null;
11603 clearError : function(){
11608 * Creates a copy of this record.
11609 * @param {String} id (optional) A new record id if you don't want to use this record's id
11612 copy : function(newId) {
11613 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11617 * Ext JS Library 1.1.1
11618 * Copyright(c) 2006-2007, Ext JS, LLC.
11620 * Originally Released Under LGPL - original licence link has changed is not relivant.
11623 * <script type="text/javascript">
11629 * @class Roo.data.Store
11630 * @extends Roo.util.Observable
11631 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11632 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11634 * 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
11635 * has no knowledge of the format of the data returned by the Proxy.<br>
11637 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11638 * instances from the data object. These records are cached and made available through accessor functions.
11640 * Creates a new Store.
11641 * @param {Object} config A config object containing the objects needed for the Store to access data,
11642 * and read the data into Records.
11644 Roo.data.Store = function(config){
11645 this.data = new Roo.util.MixedCollection(false);
11646 this.data.getKey = function(o){
11649 this.baseParams = {};
11651 this.paramNames = {
11656 "multisort" : "_multisort"
11659 if(config && config.data){
11660 this.inlineData = config.data;
11661 delete config.data;
11664 Roo.apply(this, config);
11666 if(this.reader){ // reader passed
11667 this.reader = Roo.factory(this.reader, Roo.data);
11668 this.reader.xmodule = this.xmodule || false;
11669 if(!this.recordType){
11670 this.recordType = this.reader.recordType;
11672 if(this.reader.onMetaChange){
11673 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11677 if(this.recordType){
11678 this.fields = this.recordType.prototype.fields;
11680 this.modified = [];
11684 * @event datachanged
11685 * Fires when the data cache has changed, and a widget which is using this Store
11686 * as a Record cache should refresh its view.
11687 * @param {Store} this
11689 datachanged : true,
11691 * @event metachange
11692 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11693 * @param {Store} this
11694 * @param {Object} meta The JSON metadata
11699 * Fires when Records have been added to the Store
11700 * @param {Store} this
11701 * @param {Roo.data.Record[]} records The array of Records added
11702 * @param {Number} index The index at which the record(s) were added
11707 * Fires when a Record has been removed from the Store
11708 * @param {Store} this
11709 * @param {Roo.data.Record} record The Record that was removed
11710 * @param {Number} index The index at which the record was removed
11715 * Fires when a Record has been updated
11716 * @param {Store} this
11717 * @param {Roo.data.Record} record The Record that was updated
11718 * @param {String} operation The update operation being performed. Value may be one of:
11720 Roo.data.Record.EDIT
11721 Roo.data.Record.REJECT
11722 Roo.data.Record.COMMIT
11728 * Fires when the data cache has been cleared.
11729 * @param {Store} this
11733 * @event beforeload
11734 * Fires before a request is made for a new data object. If the beforeload handler returns false
11735 * the load action will be canceled.
11736 * @param {Store} this
11737 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11741 * @event beforeloadadd
11742 * Fires after a new set of Records has been loaded.
11743 * @param {Store} this
11744 * @param {Roo.data.Record[]} records The Records that were loaded
11745 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11747 beforeloadadd : true,
11750 * Fires after a new set of Records has been loaded, before they are added to the store.
11751 * @param {Store} this
11752 * @param {Roo.data.Record[]} records The Records that were loaded
11753 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11754 * @params {Object} return from reader
11758 * @event loadexception
11759 * Fires if an exception occurs in the Proxy during loading.
11760 * Called with the signature of the Proxy's "loadexception" event.
11761 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11764 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11765 * @param {Object} load options
11766 * @param {Object} jsonData from your request (normally this contains the Exception)
11768 loadexception : true
11772 this.proxy = Roo.factory(this.proxy, Roo.data);
11773 this.proxy.xmodule = this.xmodule || false;
11774 this.relayEvents(this.proxy, ["loadexception"]);
11776 this.sortToggle = {};
11777 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11779 Roo.data.Store.superclass.constructor.call(this);
11781 if(this.inlineData){
11782 this.loadData(this.inlineData);
11783 delete this.inlineData;
11787 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11789 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11790 * without a remote query - used by combo/forms at present.
11794 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11797 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11800 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11801 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11804 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11805 * on any HTTP request
11808 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11811 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11815 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11816 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11818 remoteSort : false,
11821 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11822 * loaded or when a record is removed. (defaults to false).
11824 pruneModifiedRecords : false,
11827 lastOptions : null,
11830 * Add Records to the Store and fires the add event.
11831 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11833 add : function(records){
11834 records = [].concat(records);
11835 for(var i = 0, len = records.length; i < len; i++){
11836 records[i].join(this);
11838 var index = this.data.length;
11839 this.data.addAll(records);
11840 this.fireEvent("add", this, records, index);
11844 * Remove a Record from the Store and fires the remove event.
11845 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11847 remove : function(record){
11848 var index = this.data.indexOf(record);
11849 this.data.removeAt(index);
11851 if(this.pruneModifiedRecords){
11852 this.modified.remove(record);
11854 this.fireEvent("remove", this, record, index);
11858 * Remove all Records from the Store and fires the clear event.
11860 removeAll : function(){
11862 if(this.pruneModifiedRecords){
11863 this.modified = [];
11865 this.fireEvent("clear", this);
11869 * Inserts Records to the Store at the given index and fires the add event.
11870 * @param {Number} index The start index at which to insert the passed Records.
11871 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11873 insert : function(index, records){
11874 records = [].concat(records);
11875 for(var i = 0, len = records.length; i < len; i++){
11876 this.data.insert(index, records[i]);
11877 records[i].join(this);
11879 this.fireEvent("add", this, records, index);
11883 * Get the index within the cache of the passed Record.
11884 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11885 * @return {Number} The index of the passed Record. Returns -1 if not found.
11887 indexOf : function(record){
11888 return this.data.indexOf(record);
11892 * Get the index within the cache of the Record with the passed id.
11893 * @param {String} id The id of the Record to find.
11894 * @return {Number} The index of the Record. Returns -1 if not found.
11896 indexOfId : function(id){
11897 return this.data.indexOfKey(id);
11901 * Get the Record with the specified id.
11902 * @param {String} id The id of the Record to find.
11903 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11905 getById : function(id){
11906 return this.data.key(id);
11910 * Get the Record at the specified index.
11911 * @param {Number} index The index of the Record to find.
11912 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11914 getAt : function(index){
11915 return this.data.itemAt(index);
11919 * Returns a range of Records between specified indices.
11920 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11921 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11922 * @return {Roo.data.Record[]} An array of Records
11924 getRange : function(start, end){
11925 return this.data.getRange(start, end);
11929 storeOptions : function(o){
11930 o = Roo.apply({}, o);
11933 this.lastOptions = o;
11937 * Loads the Record cache from the configured Proxy using the configured Reader.
11939 * If using remote paging, then the first load call must specify the <em>start</em>
11940 * and <em>limit</em> properties in the options.params property to establish the initial
11941 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11943 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11944 * and this call will return before the new data has been loaded. Perform any post-processing
11945 * in a callback function, or in a "load" event handler.</strong>
11947 * @param {Object} options An object containing properties which control loading options:<ul>
11948 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11949 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11950 * passed the following arguments:<ul>
11951 * <li>r : Roo.data.Record[]</li>
11952 * <li>options: Options object from the load call</li>
11953 * <li>success: Boolean success indicator</li></ul></li>
11954 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11955 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11958 load : function(options){
11959 options = options || {};
11960 if(this.fireEvent("beforeload", this, options) !== false){
11961 this.storeOptions(options);
11962 var p = Roo.apply(options.params || {}, this.baseParams);
11963 // if meta was not loaded from remote source.. try requesting it.
11964 if (!this.reader.metaFromRemote) {
11965 p._requestMeta = 1;
11967 if(this.sortInfo && this.remoteSort){
11968 var pn = this.paramNames;
11969 p[pn["sort"]] = this.sortInfo.field;
11970 p[pn["dir"]] = this.sortInfo.direction;
11972 if (this.multiSort) {
11973 var pn = this.paramNames;
11974 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11977 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11982 * Reloads the Record cache from the configured Proxy using the configured Reader and
11983 * the options from the last load operation performed.
11984 * @param {Object} options (optional) An object containing properties which may override the options
11985 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11986 * the most recently used options are reused).
11988 reload : function(options){
11989 this.load(Roo.applyIf(options||{}, this.lastOptions));
11993 // Called as a callback by the Reader during a load operation.
11994 loadRecords : function(o, options, success){
11995 if(!o || success === false){
11996 if(success !== false){
11997 this.fireEvent("load", this, [], options, o);
11999 if(options.callback){
12000 options.callback.call(options.scope || this, [], options, false);
12004 // if data returned failure - throw an exception.
12005 if (o.success === false) {
12006 // show a message if no listener is registered.
12007 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
12008 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
12010 // loadmask wil be hooked into this..
12011 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
12014 var r = o.records, t = o.totalRecords || r.length;
12016 this.fireEvent("beforeloadadd", this, r, options, o);
12018 if(!options || options.add !== true){
12019 if(this.pruneModifiedRecords){
12020 this.modified = [];
12022 for(var i = 0, len = r.length; i < len; i++){
12026 this.data = this.snapshot;
12027 delete this.snapshot;
12030 this.data.addAll(r);
12031 this.totalLength = t;
12033 this.fireEvent("datachanged", this);
12035 this.totalLength = Math.max(t, this.data.length+r.length);
12039 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
12041 var e = new Roo.data.Record({});
12043 e.set(this.parent.displayField, this.parent.emptyTitle);
12044 e.set(this.parent.valueField, '');
12049 this.fireEvent("load", this, r, options, o);
12050 if(options.callback){
12051 options.callback.call(options.scope || this, r, options, true);
12057 * Loads data from a passed data block. A Reader which understands the format of the data
12058 * must have been configured in the constructor.
12059 * @param {Object} data The data block from which to read the Records. The format of the data expected
12060 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
12061 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
12063 loadData : function(o, append){
12064 var r = this.reader.readRecords(o);
12065 this.loadRecords(r, {add: append}, true);
12069 * using 'cn' the nested child reader read the child array into it's child stores.
12070 * @param {Object} rec The record with a 'children array
12072 loadDataFromChildren : function(rec)
12074 this.loadData(this.reader.toLoadData(rec));
12079 * Gets the number of cached records.
12081 * <em>If using paging, this may not be the total size of the dataset. If the data object
12082 * used by the Reader contains the dataset size, then the getTotalCount() function returns
12083 * the data set size</em>
12085 getCount : function(){
12086 return this.data.length || 0;
12090 * Gets the total number of records in the dataset as returned by the server.
12092 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
12093 * the dataset size</em>
12095 getTotalCount : function(){
12096 return this.totalLength || 0;
12100 * Returns the sort state of the Store as an object with two properties:
12102 field {String} The name of the field by which the Records are sorted
12103 direction {String} The sort order, "ASC" or "DESC"
12106 getSortState : function(){
12107 return this.sortInfo;
12111 applySort : function(){
12112 if(this.sortInfo && !this.remoteSort){
12113 var s = this.sortInfo, f = s.field;
12114 var st = this.fields.get(f).sortType;
12115 var fn = function(r1, r2){
12116 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
12117 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
12119 this.data.sort(s.direction, fn);
12120 if(this.snapshot && this.snapshot != this.data){
12121 this.snapshot.sort(s.direction, fn);
12127 * Sets the default sort column and order to be used by the next load operation.
12128 * @param {String} fieldName The name of the field to sort by.
12129 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12131 setDefaultSort : function(field, dir){
12132 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
12136 * Sort the Records.
12137 * If remote sorting is used, the sort is performed on the server, and the cache is
12138 * reloaded. If local sorting is used, the cache is sorted internally.
12139 * @param {String} fieldName The name of the field to sort by.
12140 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12142 sort : function(fieldName, dir){
12143 var f = this.fields.get(fieldName);
12145 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
12147 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
12148 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
12153 this.sortToggle[f.name] = dir;
12154 this.sortInfo = {field: f.name, direction: dir};
12155 if(!this.remoteSort){
12157 this.fireEvent("datachanged", this);
12159 this.load(this.lastOptions);
12164 * Calls the specified function for each of the Records in the cache.
12165 * @param {Function} fn The function to call. The Record is passed as the first parameter.
12166 * Returning <em>false</em> aborts and exits the iteration.
12167 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
12169 each : function(fn, scope){
12170 this.data.each(fn, scope);
12174 * Gets all records modified since the last commit. Modified records are persisted across load operations
12175 * (e.g., during paging).
12176 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
12178 getModifiedRecords : function(){
12179 return this.modified;
12183 createFilterFn : function(property, value, anyMatch){
12184 if(!value.exec){ // not a regex
12185 value = String(value);
12186 if(value.length == 0){
12189 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
12191 return function(r){
12192 return value.test(r.data[property]);
12197 * Sums the value of <i>property</i> for each record between start and end and returns the result.
12198 * @param {String} property A field on your records
12199 * @param {Number} start The record index to start at (defaults to 0)
12200 * @param {Number} end The last record index to include (defaults to length - 1)
12201 * @return {Number} The sum
12203 sum : function(property, start, end){
12204 var rs = this.data.items, v = 0;
12205 start = start || 0;
12206 end = (end || end === 0) ? end : rs.length-1;
12208 for(var i = start; i <= end; i++){
12209 v += (rs[i].data[property] || 0);
12215 * Filter the records by a specified property.
12216 * @param {String} field A field on your records
12217 * @param {String/RegExp} value Either a string that the field
12218 * should start with or a RegExp to test against the field
12219 * @param {Boolean} anyMatch True to match any part not just the beginning
12221 filter : function(property, value, anyMatch){
12222 var fn = this.createFilterFn(property, value, anyMatch);
12223 return fn ? this.filterBy(fn) : this.clearFilter();
12227 * Filter by a function. The specified function will be called with each
12228 * record in this data source. If the function returns true the record is included,
12229 * otherwise it is filtered.
12230 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12231 * @param {Object} scope (optional) The scope of the function (defaults to this)
12233 filterBy : function(fn, scope){
12234 this.snapshot = this.snapshot || this.data;
12235 this.data = this.queryBy(fn, scope||this);
12236 this.fireEvent("datachanged", this);
12240 * Query the records by a specified property.
12241 * @param {String} field A field on your records
12242 * @param {String/RegExp} value Either a string that the field
12243 * should start with or a RegExp to test against the field
12244 * @param {Boolean} anyMatch True to match any part not just the beginning
12245 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12247 query : function(property, value, anyMatch){
12248 var fn = this.createFilterFn(property, value, anyMatch);
12249 return fn ? this.queryBy(fn) : this.data.clone();
12253 * Query by a function. The specified function will be called with each
12254 * record in this data source. If the function returns true the record is included
12256 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12257 * @param {Object} scope (optional) The scope of the function (defaults to this)
12258 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12260 queryBy : function(fn, scope){
12261 var data = this.snapshot || this.data;
12262 return data.filterBy(fn, scope||this);
12266 * Collects unique values for a particular dataIndex from this store.
12267 * @param {String} dataIndex The property to collect
12268 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12269 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12270 * @return {Array} An array of the unique values
12272 collect : function(dataIndex, allowNull, bypassFilter){
12273 var d = (bypassFilter === true && this.snapshot) ?
12274 this.snapshot.items : this.data.items;
12275 var v, sv, r = [], l = {};
12276 for(var i = 0, len = d.length; i < len; i++){
12277 v = d[i].data[dataIndex];
12279 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12288 * Revert to a view of the Record cache with no filtering applied.
12289 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12291 clearFilter : function(suppressEvent){
12292 if(this.snapshot && this.snapshot != this.data){
12293 this.data = this.snapshot;
12294 delete this.snapshot;
12295 if(suppressEvent !== true){
12296 this.fireEvent("datachanged", this);
12302 afterEdit : function(record){
12303 if(this.modified.indexOf(record) == -1){
12304 this.modified.push(record);
12306 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12310 afterReject : function(record){
12311 this.modified.remove(record);
12312 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12316 afterCommit : function(record){
12317 this.modified.remove(record);
12318 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12322 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12323 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12325 commitChanges : function(){
12326 var m = this.modified.slice(0);
12327 this.modified = [];
12328 for(var i = 0, len = m.length; i < len; i++){
12334 * Cancel outstanding changes on all changed records.
12336 rejectChanges : function(){
12337 var m = this.modified.slice(0);
12338 this.modified = [];
12339 for(var i = 0, len = m.length; i < len; i++){
12344 onMetaChange : function(meta, rtype, o){
12345 this.recordType = rtype;
12346 this.fields = rtype.prototype.fields;
12347 delete this.snapshot;
12348 this.sortInfo = meta.sortInfo || this.sortInfo;
12349 this.modified = [];
12350 this.fireEvent('metachange', this, this.reader.meta);
12353 moveIndex : function(data, type)
12355 var index = this.indexOf(data);
12357 var newIndex = index + type;
12361 this.insert(newIndex, data);
12366 * Ext JS Library 1.1.1
12367 * Copyright(c) 2006-2007, Ext JS, LLC.
12369 * Originally Released Under LGPL - original licence link has changed is not relivant.
12372 * <script type="text/javascript">
12376 * @class Roo.data.SimpleStore
12377 * @extends Roo.data.Store
12378 * Small helper class to make creating Stores from Array data easier.
12379 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12380 * @cfg {Array} fields An array of field definition objects, or field name strings.
12381 * @cfg {Object} an existing reader (eg. copied from another store)
12382 * @cfg {Array} data The multi-dimensional array of data
12384 * @param {Object} config
12386 Roo.data.SimpleStore = function(config)
12388 Roo.data.SimpleStore.superclass.constructor.call(this, {
12390 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
12393 Roo.data.Record.create(config.fields)
12395 proxy : new Roo.data.MemoryProxy(config.data)
12399 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12401 * Ext JS Library 1.1.1
12402 * Copyright(c) 2006-2007, Ext JS, LLC.
12404 * Originally Released Under LGPL - original licence link has changed is not relivant.
12407 * <script type="text/javascript">
12412 * @extends Roo.data.Store
12413 * @class Roo.data.JsonStore
12414 * Small helper class to make creating Stores for JSON data easier. <br/>
12416 var store = new Roo.data.JsonStore({
12417 url: 'get-images.php',
12419 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12422 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12423 * JsonReader and HttpProxy (unless inline data is provided).</b>
12424 * @cfg {Array} fields An array of field definition objects, or field name strings.
12426 * @param {Object} config
12428 Roo.data.JsonStore = function(c){
12429 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12430 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12431 reader: new Roo.data.JsonReader(c, c.fields)
12434 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12436 * Ext JS Library 1.1.1
12437 * Copyright(c) 2006-2007, Ext JS, LLC.
12439 * Originally Released Under LGPL - original licence link has changed is not relivant.
12442 * <script type="text/javascript">
12446 Roo.data.Field = function(config){
12447 if(typeof config == "string"){
12448 config = {name: config};
12450 Roo.apply(this, config);
12453 this.type = "auto";
12456 var st = Roo.data.SortTypes;
12457 // named sortTypes are supported, here we look them up
12458 if(typeof this.sortType == "string"){
12459 this.sortType = st[this.sortType];
12462 // set default sortType for strings and dates
12463 if(!this.sortType){
12466 this.sortType = st.asUCString;
12469 this.sortType = st.asDate;
12472 this.sortType = st.none;
12477 var stripRe = /[\$,%]/g;
12479 // prebuilt conversion function for this field, instead of
12480 // switching every time we're reading a value
12482 var cv, dateFormat = this.dateFormat;
12487 cv = function(v){ return v; };
12490 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12494 return v !== undefined && v !== null && v !== '' ?
12495 parseInt(String(v).replace(stripRe, ""), 10) : '';
12500 return v !== undefined && v !== null && v !== '' ?
12501 parseFloat(String(v).replace(stripRe, ""), 10) : '';
12506 cv = function(v){ return v === true || v === "true" || v == 1; };
12513 if(v instanceof Date){
12517 if(dateFormat == "timestamp"){
12518 return new Date(v*1000);
12520 return Date.parseDate(v, dateFormat);
12522 var parsed = Date.parse(v);
12523 return parsed ? new Date(parsed) : null;
12532 Roo.data.Field.prototype = {
12540 * Ext JS Library 1.1.1
12541 * Copyright(c) 2006-2007, Ext JS, LLC.
12543 * Originally Released Under LGPL - original licence link has changed is not relivant.
12546 * <script type="text/javascript">
12549 // Base class for reading structured data from a data source. This class is intended to be
12550 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12553 * @class Roo.data.DataReader
12554 * Base class for reading structured data from a data source. This class is intended to be
12555 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12558 Roo.data.DataReader = function(meta, recordType){
12562 this.recordType = recordType instanceof Array ?
12563 Roo.data.Record.create(recordType) : recordType;
12566 Roo.data.DataReader.prototype = {
12569 readerType : 'Data',
12571 * Create an empty record
12572 * @param {Object} data (optional) - overlay some values
12573 * @return {Roo.data.Record} record created.
12575 newRow : function(d) {
12577 this.recordType.prototype.fields.each(function(c) {
12579 case 'int' : da[c.name] = 0; break;
12580 case 'date' : da[c.name] = new Date(); break;
12581 case 'float' : da[c.name] = 0.0; break;
12582 case 'boolean' : da[c.name] = false; break;
12583 default : da[c.name] = ""; break;
12587 return new this.recordType(Roo.apply(da, d));
12593 * Ext JS Library 1.1.1
12594 * Copyright(c) 2006-2007, Ext JS, LLC.
12596 * Originally Released Under LGPL - original licence link has changed is not relivant.
12599 * <script type="text/javascript">
12603 * @class Roo.data.DataProxy
12604 * @extends Roo.data.Observable
12605 * This class is an abstract base class for implementations which provide retrieval of
12606 * unformatted data objects.<br>
12608 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12609 * (of the appropriate type which knows how to parse the data object) to provide a block of
12610 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12612 * Custom implementations must implement the load method as described in
12613 * {@link Roo.data.HttpProxy#load}.
12615 Roo.data.DataProxy = function(){
12618 * @event beforeload
12619 * Fires before a network request is made to retrieve a data object.
12620 * @param {Object} This DataProxy object.
12621 * @param {Object} params The params parameter to the load function.
12626 * Fires before the load method's callback is called.
12627 * @param {Object} This DataProxy object.
12628 * @param {Object} o The data object.
12629 * @param {Object} arg The callback argument object passed to the load function.
12633 * @event loadexception
12634 * Fires if an Exception occurs during data retrieval.
12635 * @param {Object} This DataProxy object.
12636 * @param {Object} o The data object.
12637 * @param {Object} arg The callback argument object passed to the load function.
12638 * @param {Object} e The Exception.
12640 loadexception : true
12642 Roo.data.DataProxy.superclass.constructor.call(this);
12645 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12648 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12652 * Ext JS Library 1.1.1
12653 * Copyright(c) 2006-2007, Ext JS, LLC.
12655 * Originally Released Under LGPL - original licence link has changed is not relivant.
12658 * <script type="text/javascript">
12661 * @class Roo.data.MemoryProxy
12662 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12663 * to the Reader when its load method is called.
12665 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12667 Roo.data.MemoryProxy = function(data){
12671 Roo.data.MemoryProxy.superclass.constructor.call(this);
12675 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12678 * Load data from the requested source (in this case an in-memory
12679 * data object passed to the constructor), read the data object into
12680 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12681 * process that block using the passed callback.
12682 * @param {Object} params This parameter is not used by the MemoryProxy class.
12683 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12684 * object into a block of Roo.data.Records.
12685 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12686 * The function must be passed <ul>
12687 * <li>The Record block object</li>
12688 * <li>The "arg" argument from the load function</li>
12689 * <li>A boolean success indicator</li>
12691 * @param {Object} scope The scope in which to call the callback
12692 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12694 load : function(params, reader, callback, scope, arg){
12695 params = params || {};
12698 result = reader.readRecords(params.data ? params.data :this.data);
12700 this.fireEvent("loadexception", this, arg, null, e);
12701 callback.call(scope, null, arg, false);
12704 callback.call(scope, result, arg, true);
12708 update : function(params, records){
12713 * Ext JS Library 1.1.1
12714 * Copyright(c) 2006-2007, Ext JS, LLC.
12716 * Originally Released Under LGPL - original licence link has changed is not relivant.
12719 * <script type="text/javascript">
12722 * @class Roo.data.HttpProxy
12723 * @extends Roo.data.DataProxy
12724 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12725 * configured to reference a certain URL.<br><br>
12727 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12728 * from which the running page was served.<br><br>
12730 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12732 * Be aware that to enable the browser to parse an XML document, the server must set
12733 * the Content-Type header in the HTTP response to "text/xml".
12735 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12736 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12737 * will be used to make the request.
12739 Roo.data.HttpProxy = function(conn){
12740 Roo.data.HttpProxy.superclass.constructor.call(this);
12741 // is conn a conn config or a real conn?
12743 this.useAjax = !conn || !conn.events;
12747 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12748 // thse are take from connection...
12751 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12754 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12755 * extra parameters to each request made by this object. (defaults to undefined)
12758 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12759 * to each request made by this object. (defaults to undefined)
12762 * @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)
12765 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12768 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12774 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12778 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12779 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12780 * a finer-grained basis than the DataProxy events.
12782 getConnection : function(){
12783 return this.useAjax ? Roo.Ajax : this.conn;
12787 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12788 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12789 * process that block using the passed callback.
12790 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12791 * for the request to the remote server.
12792 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12793 * object into a block of Roo.data.Records.
12794 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12795 * The function must be passed <ul>
12796 * <li>The Record block object</li>
12797 * <li>The "arg" argument from the load function</li>
12798 * <li>A boolean success indicator</li>
12800 * @param {Object} scope The scope in which to call the callback
12801 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12803 load : function(params, reader, callback, scope, arg){
12804 if(this.fireEvent("beforeload", this, params) !== false){
12806 params : params || {},
12808 callback : callback,
12813 callback : this.loadResponse,
12817 Roo.applyIf(o, this.conn);
12818 if(this.activeRequest){
12819 Roo.Ajax.abort(this.activeRequest);
12821 this.activeRequest = Roo.Ajax.request(o);
12823 this.conn.request(o);
12826 callback.call(scope||this, null, arg, false);
12831 loadResponse : function(o, success, response){
12832 delete this.activeRequest;
12834 this.fireEvent("loadexception", this, o, response);
12835 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12840 result = o.reader.read(response);
12842 this.fireEvent("loadexception", this, o, response, e);
12843 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12847 this.fireEvent("load", this, o, o.request.arg);
12848 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12852 update : function(dataSet){
12857 updateResponse : function(dataSet){
12862 * Ext JS Library 1.1.1
12863 * Copyright(c) 2006-2007, Ext JS, LLC.
12865 * Originally Released Under LGPL - original licence link has changed is not relivant.
12868 * <script type="text/javascript">
12872 * @class Roo.data.ScriptTagProxy
12873 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12874 * other than the originating domain of the running page.<br><br>
12876 * <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
12877 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12879 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12880 * source code that is used as the source inside a <script> tag.<br><br>
12882 * In order for the browser to process the returned data, the server must wrap the data object
12883 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12884 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12885 * depending on whether the callback name was passed:
12888 boolean scriptTag = false;
12889 String cb = request.getParameter("callback");
12892 response.setContentType("text/javascript");
12894 response.setContentType("application/x-json");
12896 Writer out = response.getWriter();
12898 out.write(cb + "(");
12900 out.print(dataBlock.toJsonString());
12907 * @param {Object} config A configuration object.
12909 Roo.data.ScriptTagProxy = function(config){
12910 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12911 Roo.apply(this, config);
12912 this.head = document.getElementsByTagName("head")[0];
12915 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12917 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12919 * @cfg {String} url The URL from which to request the data object.
12922 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12926 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12927 * the server the name of the callback function set up by the load call to process the returned data object.
12928 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12929 * javascript output which calls this named function passing the data object as its only parameter.
12931 callbackParam : "callback",
12933 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12934 * name to the request.
12939 * Load data from the configured URL, read the data object into
12940 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12941 * process that block using the passed callback.
12942 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12943 * for the request to the remote server.
12944 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12945 * object into a block of Roo.data.Records.
12946 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12947 * The function must be passed <ul>
12948 * <li>The Record block object</li>
12949 * <li>The "arg" argument from the load function</li>
12950 * <li>A boolean success indicator</li>
12952 * @param {Object} scope The scope in which to call the callback
12953 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12955 load : function(params, reader, callback, scope, arg){
12956 if(this.fireEvent("beforeload", this, params) !== false){
12958 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12960 var url = this.url;
12961 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12963 url += "&_dc=" + (new Date().getTime());
12965 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12968 cb : "stcCallback"+transId,
12969 scriptId : "stcScript"+transId,
12973 callback : callback,
12979 window[trans.cb] = function(o){
12980 conn.handleResponse(o, trans);
12983 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12985 if(this.autoAbort !== false){
12989 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12991 var script = document.createElement("script");
12992 script.setAttribute("src", url);
12993 script.setAttribute("type", "text/javascript");
12994 script.setAttribute("id", trans.scriptId);
12995 this.head.appendChild(script);
12997 this.trans = trans;
12999 callback.call(scope||this, null, arg, false);
13004 isLoading : function(){
13005 return this.trans ? true : false;
13009 * Abort the current server request.
13011 abort : function(){
13012 if(this.isLoading()){
13013 this.destroyTrans(this.trans);
13018 destroyTrans : function(trans, isLoaded){
13019 this.head.removeChild(document.getElementById(trans.scriptId));
13020 clearTimeout(trans.timeoutId);
13022 window[trans.cb] = undefined;
13024 delete window[trans.cb];
13027 // if hasn't been loaded, wait for load to remove it to prevent script error
13028 window[trans.cb] = function(){
13029 window[trans.cb] = undefined;
13031 delete window[trans.cb];
13038 handleResponse : function(o, trans){
13039 this.trans = false;
13040 this.destroyTrans(trans, true);
13043 result = trans.reader.readRecords(o);
13045 this.fireEvent("loadexception", this, o, trans.arg, e);
13046 trans.callback.call(trans.scope||window, null, trans.arg, false);
13049 this.fireEvent("load", this, o, trans.arg);
13050 trans.callback.call(trans.scope||window, result, trans.arg, true);
13054 handleFailure : function(trans){
13055 this.trans = false;
13056 this.destroyTrans(trans, false);
13057 this.fireEvent("loadexception", this, null, trans.arg);
13058 trans.callback.call(trans.scope||window, null, trans.arg, false);
13062 * Ext JS Library 1.1.1
13063 * Copyright(c) 2006-2007, Ext JS, LLC.
13065 * Originally Released Under LGPL - original licence link has changed is not relivant.
13068 * <script type="text/javascript">
13072 * @class Roo.data.JsonReader
13073 * @extends Roo.data.DataReader
13074 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
13075 * based on mappings in a provided Roo.data.Record constructor.
13077 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
13078 * in the reply previously.
13083 var RecordDef = Roo.data.Record.create([
13084 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
13085 {name: 'occupation'} // This field will use "occupation" as the mapping.
13087 var myReader = new Roo.data.JsonReader({
13088 totalProperty: "results", // The property which contains the total dataset size (optional)
13089 root: "rows", // The property which contains an Array of row objects
13090 id: "id" // The property within each row object that provides an ID for the record (optional)
13094 * This would consume a JSON file like this:
13096 { 'results': 2, 'rows': [
13097 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
13098 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
13101 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
13102 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
13103 * paged from the remote server.
13104 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
13105 * @cfg {String} root name of the property which contains the Array of row objects.
13106 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13107 * @cfg {Array} fields Array of field definition objects
13109 * Create a new JsonReader
13110 * @param {Object} meta Metadata configuration options
13111 * @param {Object} recordType Either an Array of field definition objects,
13112 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
13114 Roo.data.JsonReader = function(meta, recordType){
13117 // set some defaults:
13118 Roo.applyIf(meta, {
13119 totalProperty: 'total',
13120 successProperty : 'success',
13125 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13127 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
13129 readerType : 'Json',
13132 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
13133 * Used by Store query builder to append _requestMeta to params.
13136 metaFromRemote : false,
13138 * This method is only used by a DataProxy which has retrieved data from a remote server.
13139 * @param {Object} response The XHR object which contains the JSON data in its responseText.
13140 * @return {Object} data A data block which is used by an Roo.data.Store object as
13141 * a cache of Roo.data.Records.
13143 read : function(response){
13144 var json = response.responseText;
13146 var o = /* eval:var:o */ eval("("+json+")");
13148 throw {message: "JsonReader.read: Json object not found"};
13154 this.metaFromRemote = true;
13155 this.meta = o.metaData;
13156 this.recordType = Roo.data.Record.create(o.metaData.fields);
13157 this.onMetaChange(this.meta, this.recordType, o);
13159 return this.readRecords(o);
13162 // private function a store will implement
13163 onMetaChange : function(meta, recordType, o){
13170 simpleAccess: function(obj, subsc) {
13177 getJsonAccessor: function(){
13179 return function(expr) {
13181 return(re.test(expr))
13182 ? new Function("obj", "return obj." + expr)
13187 return Roo.emptyFn;
13192 * Create a data block containing Roo.data.Records from an XML document.
13193 * @param {Object} o An object which contains an Array of row objects in the property specified
13194 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
13195 * which contains the total size of the dataset.
13196 * @return {Object} data A data block which is used by an Roo.data.Store object as
13197 * a cache of Roo.data.Records.
13199 readRecords : function(o){
13201 * After any data loads, the raw JSON data is available for further custom processing.
13205 var s = this.meta, Record = this.recordType,
13206 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
13208 // Generate extraction functions for the totalProperty, the root, the id, and for each field
13210 if(s.totalProperty) {
13211 this.getTotal = this.getJsonAccessor(s.totalProperty);
13213 if(s.successProperty) {
13214 this.getSuccess = this.getJsonAccessor(s.successProperty);
13216 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
13218 var g = this.getJsonAccessor(s.id);
13219 this.getId = function(rec) {
13221 return (r === undefined || r === "") ? null : r;
13224 this.getId = function(){return null;};
13227 for(var jj = 0; jj < fl; jj++){
13229 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
13230 this.ef[jj] = this.getJsonAccessor(map);
13234 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
13235 if(s.totalProperty){
13236 var vt = parseInt(this.getTotal(o), 10);
13241 if(s.successProperty){
13242 var vs = this.getSuccess(o);
13243 if(vs === false || vs === 'false'){
13248 for(var i = 0; i < c; i++){
13251 var id = this.getId(n);
13252 for(var j = 0; j < fl; j++){
13254 var v = this.ef[j](n);
13256 Roo.log('missing convert for ' + f.name);
13260 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13262 var record = new Record(values, id);
13264 records[i] = record;
13270 totalRecords : totalRecords
13273 // used when loading children.. @see loadDataFromChildren
13274 toLoadData: function(rec)
13276 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13277 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13278 return { data : data, total : data.length };
13283 * Ext JS Library 1.1.1
13284 * Copyright(c) 2006-2007, Ext JS, LLC.
13286 * Originally Released Under LGPL - original licence link has changed is not relivant.
13289 * <script type="text/javascript">
13293 * @class Roo.data.ArrayReader
13294 * @extends Roo.data.DataReader
13295 * Data reader class to create an Array of Roo.data.Record objects from an Array.
13296 * Each element of that Array represents a row of data fields. The
13297 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13298 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13302 var RecordDef = Roo.data.Record.create([
13303 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
13304 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
13306 var myReader = new Roo.data.ArrayReader({
13307 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
13311 * This would consume an Array like this:
13313 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13317 * Create a new JsonReader
13318 * @param {Object} meta Metadata configuration options.
13319 * @param {Object|Array} recordType Either an Array of field definition objects
13321 * @cfg {Array} fields Array of field definition objects
13322 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13323 * as specified to {@link Roo.data.Record#create},
13324 * or an {@link Roo.data.Record} object
13327 * created using {@link Roo.data.Record#create}.
13329 Roo.data.ArrayReader = function(meta, recordType)
13331 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13334 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13337 * Create a data block containing Roo.data.Records from an XML document.
13338 * @param {Object} o An Array of row objects which represents the dataset.
13339 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13340 * a cache of Roo.data.Records.
13342 readRecords : function(o)
13344 var sid = this.meta ? this.meta.id : null;
13345 var recordType = this.recordType, fields = recordType.prototype.fields;
13348 for(var i = 0; i < root.length; i++){
13351 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13352 for(var j = 0, jlen = fields.length; j < jlen; j++){
13353 var f = fields.items[j];
13354 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13355 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13357 values[f.name] = v;
13359 var record = new recordType(values, id);
13361 records[records.length] = record;
13365 totalRecords : records.length
13368 // used when loading children.. @see loadDataFromChildren
13369 toLoadData: function(rec)
13371 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13372 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13383 * @class Roo.bootstrap.ComboBox
13384 * @extends Roo.bootstrap.TriggerField
13385 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13386 * @cfg {Boolean} append (true|false) default false
13387 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13388 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13389 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13390 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13391 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13392 * @cfg {Boolean} animate default true
13393 * @cfg {Boolean} emptyResultText only for touch device
13394 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13395 * @cfg {String} emptyTitle default ''
13397 * Create a new ComboBox.
13398 * @param {Object} config Configuration options
13400 Roo.bootstrap.ComboBox = function(config){
13401 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13405 * Fires when the dropdown list is expanded
13406 * @param {Roo.bootstrap.ComboBox} combo This combo box
13411 * Fires when the dropdown list is collapsed
13412 * @param {Roo.bootstrap.ComboBox} combo This combo box
13416 * @event beforeselect
13417 * Fires before a list item is selected. Return false to cancel the selection.
13418 * @param {Roo.bootstrap.ComboBox} combo This combo box
13419 * @param {Roo.data.Record} record The data record returned from the underlying store
13420 * @param {Number} index The index of the selected item in the dropdown list
13422 'beforeselect' : true,
13425 * Fires when a list item is selected
13426 * @param {Roo.bootstrap.ComboBox} combo This combo box
13427 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13428 * @param {Number} index The index of the selected item in the dropdown list
13432 * @event beforequery
13433 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13434 * The event object passed has these properties:
13435 * @param {Roo.bootstrap.ComboBox} combo This combo box
13436 * @param {String} query The query
13437 * @param {Boolean} forceAll true to force "all" query
13438 * @param {Boolean} cancel true to cancel the query
13439 * @param {Object} e The query event object
13441 'beforequery': true,
13444 * Fires when the 'add' icon is pressed (add a listener to enable add button)
13445 * @param {Roo.bootstrap.ComboBox} combo This combo box
13450 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13451 * @param {Roo.bootstrap.ComboBox} combo This combo box
13452 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13457 * Fires when the remove value from the combobox array
13458 * @param {Roo.bootstrap.ComboBox} combo This combo box
13462 * @event afterremove
13463 * Fires when the remove value from the combobox array
13464 * @param {Roo.bootstrap.ComboBox} combo This combo box
13466 'afterremove' : true,
13468 * @event specialfilter
13469 * Fires when specialfilter
13470 * @param {Roo.bootstrap.ComboBox} combo This combo box
13472 'specialfilter' : true,
13475 * Fires when tick the element
13476 * @param {Roo.bootstrap.ComboBox} combo This combo box
13480 * @event touchviewdisplay
13481 * Fires when touch view require special display (default is using displayField)
13482 * @param {Roo.bootstrap.ComboBox} combo This combo box
13483 * @param {Object} cfg set html .
13485 'touchviewdisplay' : true
13490 this.tickItems = [];
13492 this.selectedIndex = -1;
13493 if(this.mode == 'local'){
13494 if(config.queryDelay === undefined){
13495 this.queryDelay = 10;
13497 if(config.minChars === undefined){
13503 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13506 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13507 * rendering into an Roo.Editor, defaults to false)
13510 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13511 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13514 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13517 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13518 * the dropdown list (defaults to undefined, with no header element)
13522 * @cfg {String/Roo.Template} tpl The template to use to render the output
13526 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13528 listWidth: undefined,
13530 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13531 * mode = 'remote' or 'text' if mode = 'local')
13533 displayField: undefined,
13536 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13537 * mode = 'remote' or 'value' if mode = 'local').
13538 * Note: use of a valueField requires the user make a selection
13539 * in order for a value to be mapped.
13541 valueField: undefined,
13543 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13548 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13549 * field's data value (defaults to the underlying DOM element's name)
13551 hiddenName: undefined,
13553 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13557 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13559 selectedClass: 'active',
13562 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13566 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13567 * anchor positions (defaults to 'tl-bl')
13569 listAlign: 'tl-bl?',
13571 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13575 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13576 * query specified by the allQuery config option (defaults to 'query')
13578 triggerAction: 'query',
13580 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13581 * (defaults to 4, does not apply if editable = false)
13585 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13586 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13590 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13591 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13595 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13596 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13600 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13601 * when editable = true (defaults to false)
13603 selectOnFocus:false,
13605 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13607 queryParam: 'query',
13609 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13610 * when mode = 'remote' (defaults to 'Loading...')
13612 loadingText: 'Loading...',
13614 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13618 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13622 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13623 * traditional select (defaults to true)
13627 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13631 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13635 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13636 * listWidth has a higher value)
13640 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13641 * allow the user to set arbitrary text into the field (defaults to false)
13643 forceSelection:false,
13645 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13646 * if typeAhead = true (defaults to 250)
13648 typeAheadDelay : 250,
13650 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13651 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13653 valueNotFoundText : undefined,
13655 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13657 blockFocus : false,
13660 * @cfg {Boolean} disableClear Disable showing of clear button.
13662 disableClear : false,
13664 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13666 alwaysQuery : false,
13669 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13674 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13676 invalidClass : "has-warning",
13679 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13681 validClass : "has-success",
13684 * @cfg {Boolean} specialFilter (true|false) special filter default false
13686 specialFilter : false,
13689 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13691 mobileTouchView : true,
13694 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13696 useNativeIOS : false,
13699 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13701 mobile_restrict_height : false,
13703 ios_options : false,
13715 btnPosition : 'right',
13716 triggerList : true,
13717 showToggleBtn : true,
13719 emptyResultText: 'Empty',
13720 triggerText : 'Select',
13723 // element that contains real text value.. (when hidden is used..)
13725 getAutoCreate : function()
13730 * Render classic select for iso
13733 if(Roo.isIOS && this.useNativeIOS){
13734 cfg = this.getAutoCreateNativeIOS();
13742 if(Roo.isTouch && this.mobileTouchView){
13743 cfg = this.getAutoCreateTouchView();
13750 if(!this.tickable){
13751 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13756 * ComboBox with tickable selections
13759 var align = this.labelAlign || this.parentLabelAlign();
13762 cls : 'form-group roo-combobox-tickable' //input-group
13765 var btn_text_select = '';
13766 var btn_text_done = '';
13767 var btn_text_cancel = '';
13769 if (this.btn_text_show) {
13770 btn_text_select = 'Select';
13771 btn_text_done = 'Done';
13772 btn_text_cancel = 'Cancel';
13777 cls : 'tickable-buttons',
13782 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13783 //html : this.triggerText
13784 html: btn_text_select
13790 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13792 html: btn_text_done
13798 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13800 html: btn_text_cancel
13806 buttons.cn.unshift({
13808 cls: 'roo-select2-search-field-input'
13814 Roo.each(buttons.cn, function(c){
13816 c.cls += ' btn-' + _this.size;
13819 if (_this.disabled) {
13826 style : 'display: contents',
13831 cls: 'form-hidden-field'
13835 cls: 'roo-select2-choices',
13839 cls: 'roo-select2-search-field',
13850 cls: 'roo-select2-container input-group roo-select2-container-multi',
13856 // cls: 'typeahead typeahead-long dropdown-menu',
13857 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13862 if(this.hasFeedback && !this.allowBlank){
13866 cls: 'glyphicon form-control-feedback'
13869 combobox.cn.push(feedback);
13874 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13875 tooltip : 'This field is required'
13877 if (Roo.bootstrap.version == 4) {
13880 style : 'display:none'
13883 if (align ==='left' && this.fieldLabel.length) {
13885 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13892 cls : 'control-label col-form-label',
13893 html : this.fieldLabel
13905 var labelCfg = cfg.cn[1];
13906 var contentCfg = cfg.cn[2];
13909 if(this.indicatorpos == 'right'){
13915 cls : 'control-label col-form-label',
13919 html : this.fieldLabel
13935 labelCfg = cfg.cn[0];
13936 contentCfg = cfg.cn[1];
13940 if(this.labelWidth > 12){
13941 labelCfg.style = "width: " + this.labelWidth + 'px';
13944 if(this.labelWidth < 13 && this.labelmd == 0){
13945 this.labelmd = this.labelWidth;
13948 if(this.labellg > 0){
13949 labelCfg.cls += ' col-lg-' + this.labellg;
13950 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13953 if(this.labelmd > 0){
13954 labelCfg.cls += ' col-md-' + this.labelmd;
13955 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13958 if(this.labelsm > 0){
13959 labelCfg.cls += ' col-sm-' + this.labelsm;
13960 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13963 if(this.labelxs > 0){
13964 labelCfg.cls += ' col-xs-' + this.labelxs;
13965 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13969 } else if ( this.fieldLabel.length) {
13970 // Roo.log(" label");
13975 //cls : 'input-group-addon',
13976 html : this.fieldLabel
13981 if(this.indicatorpos == 'right'){
13985 //cls : 'input-group-addon',
13986 html : this.fieldLabel
13996 // Roo.log(" no label && no align");
14003 ['xs','sm','md','lg'].map(function(size){
14004 if (settings[size]) {
14005 cfg.cls += ' col-' + size + '-' + settings[size];
14013 _initEventsCalled : false,
14016 initEvents: function()
14018 if (this._initEventsCalled) { // as we call render... prevent looping...
14021 this._initEventsCalled = true;
14024 throw "can not find store for combo";
14027 this.indicator = this.indicatorEl();
14029 this.store = Roo.factory(this.store, Roo.data);
14030 this.store.parent = this;
14032 // if we are building from html. then this element is so complex, that we can not really
14033 // use the rendered HTML.
14034 // so we have to trash and replace the previous code.
14035 if (Roo.XComponent.build_from_html) {
14036 // remove this element....
14037 var e = this.el.dom, k=0;
14038 while (e ) { e = e.previousSibling; ++k;}
14043 this.rendered = false;
14045 this.render(this.parent().getChildContainer(true), k);
14048 if(Roo.isIOS && this.useNativeIOS){
14049 this.initIOSView();
14057 if(Roo.isTouch && this.mobileTouchView){
14058 this.initTouchView();
14063 this.initTickableEvents();
14067 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
14069 if(this.hiddenName){
14071 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14073 this.hiddenField.dom.value =
14074 this.hiddenValue !== undefined ? this.hiddenValue :
14075 this.value !== undefined ? this.value : '';
14077 // prevent input submission
14078 this.el.dom.removeAttribute('name');
14079 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14084 // this.el.dom.setAttribute('autocomplete', 'off');
14087 var cls = 'x-combo-list';
14089 //this.list = new Roo.Layer({
14090 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
14096 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14097 _this.list.setWidth(lw);
14100 this.list.on('mouseover', this.onViewOver, this);
14101 this.list.on('mousemove', this.onViewMove, this);
14102 this.list.on('scroll', this.onViewScroll, this);
14105 this.list.swallowEvent('mousewheel');
14106 this.assetHeight = 0;
14109 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
14110 this.assetHeight += this.header.getHeight();
14113 this.innerList = this.list.createChild({cls:cls+'-inner'});
14114 this.innerList.on('mouseover', this.onViewOver, this);
14115 this.innerList.on('mousemove', this.onViewMove, this);
14116 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14118 if(this.allowBlank && !this.pageSize && !this.disableClear){
14119 this.footer = this.list.createChild({cls:cls+'-ft'});
14120 this.pageTb = new Roo.Toolbar(this.footer);
14124 this.footer = this.list.createChild({cls:cls+'-ft'});
14125 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
14126 {pageSize: this.pageSize});
14130 if (this.pageTb && this.allowBlank && !this.disableClear) {
14132 this.pageTb.add(new Roo.Toolbar.Fill(), {
14133 cls: 'x-btn-icon x-btn-clear',
14135 handler: function()
14138 _this.clearValue();
14139 _this.onSelect(false, -1);
14144 this.assetHeight += this.footer.getHeight();
14149 this.tpl = Roo.bootstrap.version == 4 ?
14150 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
14151 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
14154 this.view = new Roo.View(this.list, this.tpl, {
14155 singleSelect:true, store: this.store, selectedClass: this.selectedClass
14157 //this.view.wrapEl.setDisplayed(false);
14158 this.view.on('click', this.onViewClick, this);
14161 this.store.on('beforeload', this.onBeforeLoad, this);
14162 this.store.on('load', this.onLoad, this);
14163 this.store.on('loadexception', this.onLoadException, this);
14165 if(this.resizable){
14166 this.resizer = new Roo.Resizable(this.list, {
14167 pinned:true, handles:'se'
14169 this.resizer.on('resize', function(r, w, h){
14170 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
14171 this.listWidth = w;
14172 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
14173 this.restrictHeight();
14175 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
14178 if(!this.editable){
14179 this.editable = true;
14180 this.setEditable(false);
14185 if (typeof(this.events.add.listeners) != 'undefined') {
14187 this.addicon = this.wrap.createChild(
14188 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
14190 this.addicon.on('click', function(e) {
14191 this.fireEvent('add', this);
14194 if (typeof(this.events.edit.listeners) != 'undefined') {
14196 this.editicon = this.wrap.createChild(
14197 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
14198 if (this.addicon) {
14199 this.editicon.setStyle('margin-left', '40px');
14201 this.editicon.on('click', function(e) {
14203 // we fire even if inothing is selected..
14204 this.fireEvent('edit', this, this.lastData );
14210 this.keyNav = new Roo.KeyNav(this.inputEl(), {
14211 "up" : function(e){
14212 this.inKeyMode = true;
14216 "down" : function(e){
14217 if(!this.isExpanded()){
14218 this.onTriggerClick();
14220 this.inKeyMode = true;
14225 "enter" : function(e){
14226 // this.onViewClick();
14230 if(this.fireEvent("specialkey", this, e)){
14231 this.onViewClick(false);
14237 "esc" : function(e){
14241 "tab" : function(e){
14244 if(this.fireEvent("specialkey", this, e)){
14245 this.onViewClick(false);
14253 doRelay : function(foo, bar, hname){
14254 if(hname == 'down' || this.scope.isExpanded()){
14255 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14264 this.queryDelay = Math.max(this.queryDelay || 10,
14265 this.mode == 'local' ? 10 : 250);
14268 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14270 if(this.typeAhead){
14271 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14273 if(this.editable !== false){
14274 this.inputEl().on("keyup", this.onKeyUp, this);
14276 if(this.forceSelection){
14277 this.inputEl().on('blur', this.doForce, this);
14281 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14282 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14286 initTickableEvents: function()
14290 if(this.hiddenName){
14292 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14294 this.hiddenField.dom.value =
14295 this.hiddenValue !== undefined ? this.hiddenValue :
14296 this.value !== undefined ? this.value : '';
14298 // prevent input submission
14299 this.el.dom.removeAttribute('name');
14300 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14305 // this.list = this.el.select('ul.dropdown-menu',true).first();
14307 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14308 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14309 if(this.triggerList){
14310 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14313 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14314 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14316 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14317 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14319 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14320 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14322 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14323 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14324 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14327 this.cancelBtn.hide();
14332 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14333 _this.list.setWidth(lw);
14336 this.list.on('mouseover', this.onViewOver, this);
14337 this.list.on('mousemove', this.onViewMove, this);
14339 this.list.on('scroll', this.onViewScroll, this);
14342 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
14343 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14346 this.view = new Roo.View(this.list, this.tpl, {
14351 selectedClass: this.selectedClass
14354 //this.view.wrapEl.setDisplayed(false);
14355 this.view.on('click', this.onViewClick, this);
14359 this.store.on('beforeload', this.onBeforeLoad, this);
14360 this.store.on('load', this.onLoad, this);
14361 this.store.on('loadexception', this.onLoadException, this);
14364 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14365 "up" : function(e){
14366 this.inKeyMode = true;
14370 "down" : function(e){
14371 this.inKeyMode = true;
14375 "enter" : function(e){
14376 if(this.fireEvent("specialkey", this, e)){
14377 this.onViewClick(false);
14383 "esc" : function(e){
14384 this.onTickableFooterButtonClick(e, false, false);
14387 "tab" : function(e){
14388 this.fireEvent("specialkey", this, e);
14390 this.onTickableFooterButtonClick(e, false, false);
14397 doRelay : function(e, fn, key){
14398 if(this.scope.isExpanded()){
14399 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14408 this.queryDelay = Math.max(this.queryDelay || 10,
14409 this.mode == 'local' ? 10 : 250);
14412 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14414 if(this.typeAhead){
14415 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14418 if(this.editable !== false){
14419 this.tickableInputEl().on("keyup", this.onKeyUp, this);
14422 this.indicator = this.indicatorEl();
14424 if(this.indicator){
14425 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14426 this.indicator.hide();
14431 onDestroy : function(){
14433 this.view.setStore(null);
14434 this.view.el.removeAllListeners();
14435 this.view.el.remove();
14436 this.view.purgeListeners();
14439 this.list.dom.innerHTML = '';
14443 this.store.un('beforeload', this.onBeforeLoad, this);
14444 this.store.un('load', this.onLoad, this);
14445 this.store.un('loadexception', this.onLoadException, this);
14447 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14451 fireKey : function(e){
14452 if(e.isNavKeyPress() && !this.list.isVisible()){
14453 this.fireEvent("specialkey", this, e);
14458 onResize: function(w, h){
14459 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14461 // if(typeof w != 'number'){
14462 // // we do not handle it!?!?
14465 // var tw = this.trigger.getWidth();
14466 // // tw += this.addicon ? this.addicon.getWidth() : 0;
14467 // // tw += this.editicon ? this.editicon.getWidth() : 0;
14469 // this.inputEl().setWidth( this.adjustWidth('input', x));
14471 // //this.trigger.setStyle('left', x+'px');
14473 // if(this.list && this.listWidth === undefined){
14474 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14475 // this.list.setWidth(lw);
14476 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14484 * Allow or prevent the user from directly editing the field text. If false is passed,
14485 * the user will only be able to select from the items defined in the dropdown list. This method
14486 * is the runtime equivalent of setting the 'editable' config option at config time.
14487 * @param {Boolean} value True to allow the user to directly edit the field text
14489 setEditable : function(value){
14490 if(value == this.editable){
14493 this.editable = value;
14495 this.inputEl().dom.setAttribute('readOnly', true);
14496 this.inputEl().on('mousedown', this.onTriggerClick, this);
14497 this.inputEl().addClass('x-combo-noedit');
14499 this.inputEl().dom.setAttribute('readOnly', false);
14500 this.inputEl().un('mousedown', this.onTriggerClick, this);
14501 this.inputEl().removeClass('x-combo-noedit');
14507 onBeforeLoad : function(combo,opts){
14508 if(!this.hasFocus){
14512 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14514 this.restrictHeight();
14515 this.selectedIndex = -1;
14519 onLoad : function(){
14521 this.hasQuery = false;
14523 if(!this.hasFocus){
14527 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14528 this.loading.hide();
14531 if(this.store.getCount() > 0){
14534 this.restrictHeight();
14535 if(this.lastQuery == this.allQuery){
14536 if(this.editable && !this.tickable){
14537 this.inputEl().dom.select();
14541 !this.selectByValue(this.value, true) &&
14544 !this.store.lastOptions ||
14545 typeof(this.store.lastOptions.add) == 'undefined' ||
14546 this.store.lastOptions.add != true
14549 this.select(0, true);
14552 if(this.autoFocus){
14555 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14556 this.taTask.delay(this.typeAheadDelay);
14560 this.onEmptyResults();
14566 onLoadException : function()
14568 this.hasQuery = false;
14570 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14571 this.loading.hide();
14574 if(this.tickable && this.editable){
14579 // only causes errors at present
14580 //Roo.log(this.store.reader.jsonData);
14581 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14583 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14589 onTypeAhead : function(){
14590 if(this.store.getCount() > 0){
14591 var r = this.store.getAt(0);
14592 var newValue = r.data[this.displayField];
14593 var len = newValue.length;
14594 var selStart = this.getRawValue().length;
14596 if(selStart != len){
14597 this.setRawValue(newValue);
14598 this.selectText(selStart, newValue.length);
14604 onSelect : function(record, index){
14606 if(this.fireEvent('beforeselect', this, record, index) !== false){
14608 this.setFromData(index > -1 ? record.data : false);
14611 this.fireEvent('select', this, record, index);
14616 * Returns the currently selected field value or empty string if no value is set.
14617 * @return {String} value The selected value
14619 getValue : function()
14621 if(Roo.isIOS && this.useNativeIOS){
14622 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14626 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14629 if(this.valueField){
14630 return typeof this.value != 'undefined' ? this.value : '';
14632 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14636 getRawValue : function()
14638 if(Roo.isIOS && this.useNativeIOS){
14639 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14642 var v = this.inputEl().getValue();
14648 * Clears any text/value currently set in the field
14650 clearValue : function(){
14652 if(this.hiddenField){
14653 this.hiddenField.dom.value = '';
14656 this.setRawValue('');
14657 this.lastSelectionText = '';
14658 this.lastData = false;
14660 var close = this.closeTriggerEl();
14671 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14672 * will be displayed in the field. If the value does not match the data value of an existing item,
14673 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14674 * Otherwise the field will be blank (although the value will still be set).
14675 * @param {String} value The value to match
14677 setValue : function(v)
14679 if(Roo.isIOS && this.useNativeIOS){
14680 this.setIOSValue(v);
14690 if(this.valueField){
14691 var r = this.findRecord(this.valueField, v);
14693 text = r.data[this.displayField];
14694 }else if(this.valueNotFoundText !== undefined){
14695 text = this.valueNotFoundText;
14698 this.lastSelectionText = text;
14699 if(this.hiddenField){
14700 this.hiddenField.dom.value = v;
14702 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14705 var close = this.closeTriggerEl();
14708 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14714 * @property {Object} the last set data for the element
14719 * Sets the value of the field based on a object which is related to the record format for the store.
14720 * @param {Object} value the value to set as. or false on reset?
14722 setFromData : function(o){
14729 var dv = ''; // display value
14730 var vv = ''; // value value..
14732 if (this.displayField) {
14733 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14735 // this is an error condition!!!
14736 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14739 if(this.valueField){
14740 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14743 var close = this.closeTriggerEl();
14746 if(dv.length || vv * 1 > 0){
14748 this.blockFocus=true;
14754 if(this.hiddenField){
14755 this.hiddenField.dom.value = vv;
14757 this.lastSelectionText = dv;
14758 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14762 // no hidden field.. - we store the value in 'value', but still display
14763 // display field!!!!
14764 this.lastSelectionText = dv;
14765 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14772 reset : function(){
14773 // overridden so that last data is reset..
14780 this.setValue(this.originalValue);
14781 //this.clearInvalid();
14782 this.lastData = false;
14784 this.view.clearSelections();
14790 findRecord : function(prop, value){
14792 if(this.store.getCount() > 0){
14793 this.store.each(function(r){
14794 if(r.data[prop] == value){
14804 getName: function()
14806 // returns hidden if it's set..
14807 if (!this.rendered) {return ''};
14808 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14812 onViewMove : function(e, t){
14813 this.inKeyMode = false;
14817 onViewOver : function(e, t){
14818 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14821 var item = this.view.findItemFromChild(t);
14824 var index = this.view.indexOf(item);
14825 this.select(index, false);
14830 onViewClick : function(view, doFocus, el, e)
14832 var index = this.view.getSelectedIndexes()[0];
14834 var r = this.store.getAt(index);
14838 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14845 Roo.each(this.tickItems, function(v,k){
14847 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14849 _this.tickItems.splice(k, 1);
14851 if(typeof(e) == 'undefined' && view == false){
14852 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14864 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14865 this.tickItems.push(r.data);
14868 if(typeof(e) == 'undefined' && view == false){
14869 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14876 this.onSelect(r, index);
14878 if(doFocus !== false && !this.blockFocus){
14879 this.inputEl().focus();
14884 restrictHeight : function(){
14885 //this.innerList.dom.style.height = '';
14886 //var inner = this.innerList.dom;
14887 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14888 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14889 //this.list.beginUpdate();
14890 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14891 this.list.alignTo(this.inputEl(), this.listAlign);
14892 this.list.alignTo(this.inputEl(), this.listAlign);
14893 //this.list.endUpdate();
14897 onEmptyResults : function(){
14899 if(this.tickable && this.editable){
14900 this.hasFocus = false;
14901 this.restrictHeight();
14909 * Returns true if the dropdown list is expanded, else false.
14911 isExpanded : function(){
14912 return this.list.isVisible();
14916 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14917 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14918 * @param {String} value The data value of the item to select
14919 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14920 * selected item if it is not currently in view (defaults to true)
14921 * @return {Boolean} True if the value matched an item in the list, else false
14923 selectByValue : function(v, scrollIntoView){
14924 if(v !== undefined && v !== null){
14925 var r = this.findRecord(this.valueField || this.displayField, v);
14927 this.select(this.store.indexOf(r), scrollIntoView);
14935 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14936 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14937 * @param {Number} index The zero-based index of the list item to select
14938 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14939 * selected item if it is not currently in view (defaults to true)
14941 select : function(index, scrollIntoView){
14942 this.selectedIndex = index;
14943 this.view.select(index);
14944 if(scrollIntoView !== false){
14945 var el = this.view.getNode(index);
14947 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14950 this.list.scrollChildIntoView(el, false);
14956 selectNext : function(){
14957 var ct = this.store.getCount();
14959 if(this.selectedIndex == -1){
14961 }else if(this.selectedIndex < ct-1){
14962 this.select(this.selectedIndex+1);
14968 selectPrev : function(){
14969 var ct = this.store.getCount();
14971 if(this.selectedIndex == -1){
14973 }else if(this.selectedIndex != 0){
14974 this.select(this.selectedIndex-1);
14980 onKeyUp : function(e){
14981 if(this.editable !== false && !e.isSpecialKey()){
14982 this.lastKey = e.getKey();
14983 this.dqTask.delay(this.queryDelay);
14988 validateBlur : function(){
14989 return !this.list || !this.list.isVisible();
14993 initQuery : function(){
14995 var v = this.getRawValue();
14997 if(this.tickable && this.editable){
14998 v = this.tickableInputEl().getValue();
15005 doForce : function(){
15006 if(this.inputEl().dom.value.length > 0){
15007 this.inputEl().dom.value =
15008 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
15014 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
15015 * query allowing the query action to be canceled if needed.
15016 * @param {String} query The SQL query to execute
15017 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
15018 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
15019 * saved in the current store (defaults to false)
15021 doQuery : function(q, forceAll){
15023 if(q === undefined || q === null){
15028 forceAll: forceAll,
15032 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
15037 forceAll = qe.forceAll;
15038 if(forceAll === true || (q.length >= this.minChars)){
15040 this.hasQuery = true;
15042 if(this.lastQuery != q || this.alwaysQuery){
15043 this.lastQuery = q;
15044 if(this.mode == 'local'){
15045 this.selectedIndex = -1;
15047 this.store.clearFilter();
15050 if(this.specialFilter){
15051 this.fireEvent('specialfilter', this);
15056 this.store.filter(this.displayField, q);
15059 this.store.fireEvent("datachanged", this.store);
15066 this.store.baseParams[this.queryParam] = q;
15068 var options = {params : this.getParams(q)};
15071 options.add = true;
15072 options.params.start = this.page * this.pageSize;
15075 this.store.load(options);
15078 * this code will make the page width larger, at the beginning, the list not align correctly,
15079 * we should expand the list on onLoad
15080 * so command out it
15085 this.selectedIndex = -1;
15090 this.loadNext = false;
15094 getParams : function(q){
15096 //p[this.queryParam] = q;
15100 p.limit = this.pageSize;
15106 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
15108 collapse : function(){
15109 if(!this.isExpanded()){
15115 this.hasFocus = false;
15119 this.cancelBtn.hide();
15120 this.trigger.show();
15123 this.tickableInputEl().dom.value = '';
15124 this.tickableInputEl().blur();
15129 Roo.get(document).un('mousedown', this.collapseIf, this);
15130 Roo.get(document).un('mousewheel', this.collapseIf, this);
15131 if (!this.editable) {
15132 Roo.get(document).un('keydown', this.listKeyPress, this);
15134 this.fireEvent('collapse', this);
15140 collapseIf : function(e){
15141 var in_combo = e.within(this.el);
15142 var in_list = e.within(this.list);
15143 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
15145 if (in_combo || in_list || is_list) {
15146 //e.stopPropagation();
15151 this.onTickableFooterButtonClick(e, false, false);
15159 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
15161 expand : function(){
15163 if(this.isExpanded() || !this.hasFocus){
15167 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
15168 this.list.setWidth(lw);
15174 this.restrictHeight();
15178 this.tickItems = Roo.apply([], this.item);
15181 this.cancelBtn.show();
15182 this.trigger.hide();
15185 this.tickableInputEl().focus();
15190 Roo.get(document).on('mousedown', this.collapseIf, this);
15191 Roo.get(document).on('mousewheel', this.collapseIf, this);
15192 if (!this.editable) {
15193 Roo.get(document).on('keydown', this.listKeyPress, this);
15196 this.fireEvent('expand', this);
15200 // Implements the default empty TriggerField.onTriggerClick function
15201 onTriggerClick : function(e)
15203 Roo.log('trigger click');
15205 if(this.disabled || !this.triggerList){
15210 this.loadNext = false;
15212 if(this.isExpanded()){
15214 if (!this.blockFocus) {
15215 this.inputEl().focus();
15219 this.hasFocus = true;
15220 if(this.triggerAction == 'all') {
15221 this.doQuery(this.allQuery, true);
15223 this.doQuery(this.getRawValue());
15225 if (!this.blockFocus) {
15226 this.inputEl().focus();
15231 onTickableTriggerClick : function(e)
15238 this.loadNext = false;
15239 this.hasFocus = true;
15241 if(this.triggerAction == 'all') {
15242 this.doQuery(this.allQuery, true);
15244 this.doQuery(this.getRawValue());
15248 onSearchFieldClick : function(e)
15250 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
15251 this.onTickableFooterButtonClick(e, false, false);
15255 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
15260 this.loadNext = false;
15261 this.hasFocus = true;
15263 if(this.triggerAction == 'all') {
15264 this.doQuery(this.allQuery, true);
15266 this.doQuery(this.getRawValue());
15270 listKeyPress : function(e)
15272 //Roo.log('listkeypress');
15273 // scroll to first matching element based on key pres..
15274 if (e.isSpecialKey()) {
15277 var k = String.fromCharCode(e.getKey()).toUpperCase();
15280 var csel = this.view.getSelectedNodes();
15281 var cselitem = false;
15283 var ix = this.view.indexOf(csel[0]);
15284 cselitem = this.store.getAt(ix);
15285 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15291 this.store.each(function(v) {
15293 // start at existing selection.
15294 if (cselitem.id == v.id) {
15300 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15301 match = this.store.indexOf(v);
15307 if (match === false) {
15308 return true; // no more action?
15311 this.view.select(match);
15312 var sn = Roo.get(this.view.getSelectedNodes()[0]);
15313 sn.scrollIntoView(sn.dom.parentNode, false);
15316 onViewScroll : function(e, t){
15318 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){
15322 this.hasQuery = true;
15324 this.loading = this.list.select('.loading', true).first();
15326 if(this.loading === null){
15327 this.list.createChild({
15329 cls: 'loading roo-select2-more-results roo-select2-active',
15330 html: 'Loading more results...'
15333 this.loading = this.list.select('.loading', true).first();
15335 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15337 this.loading.hide();
15340 this.loading.show();
15345 this.loadNext = true;
15347 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15352 addItem : function(o)
15354 var dv = ''; // display value
15356 if (this.displayField) {
15357 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15359 // this is an error condition!!!
15360 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
15367 var choice = this.choices.createChild({
15369 cls: 'roo-select2-search-choice',
15378 cls: 'roo-select2-search-choice-close fa fa-times',
15383 }, this.searchField);
15385 var close = choice.select('a.roo-select2-search-choice-close', true).first();
15387 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15395 this.inputEl().dom.value = '';
15400 onRemoveItem : function(e, _self, o)
15402 e.preventDefault();
15404 this.lastItem = Roo.apply([], this.item);
15406 var index = this.item.indexOf(o.data) * 1;
15409 Roo.log('not this item?!');
15413 this.item.splice(index, 1);
15418 this.fireEvent('remove', this, e);
15424 syncValue : function()
15426 if(!this.item.length){
15433 Roo.each(this.item, function(i){
15434 if(_this.valueField){
15435 value.push(i[_this.valueField]);
15442 this.value = value.join(',');
15444 if(this.hiddenField){
15445 this.hiddenField.dom.value = this.value;
15448 this.store.fireEvent("datachanged", this.store);
15453 clearItem : function()
15455 if(!this.multiple){
15461 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15469 if(this.tickable && !Roo.isTouch){
15470 this.view.refresh();
15474 inputEl: function ()
15476 if(Roo.isIOS && this.useNativeIOS){
15477 return this.el.select('select.roo-ios-select', true).first();
15480 if(Roo.isTouch && this.mobileTouchView){
15481 return this.el.select('input.form-control',true).first();
15485 return this.searchField;
15488 return this.el.select('input.form-control',true).first();
15491 onTickableFooterButtonClick : function(e, btn, el)
15493 e.preventDefault();
15495 this.lastItem = Roo.apply([], this.item);
15497 if(btn && btn.name == 'cancel'){
15498 this.tickItems = Roo.apply([], this.item);
15507 Roo.each(this.tickItems, function(o){
15515 validate : function()
15517 if(this.getVisibilityEl().hasClass('hidden')){
15521 var v = this.getRawValue();
15524 v = this.getValue();
15527 if(this.disabled || this.allowBlank || v.length){
15532 this.markInvalid();
15536 tickableInputEl : function()
15538 if(!this.tickable || !this.editable){
15539 return this.inputEl();
15542 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15546 getAutoCreateTouchView : function()
15551 cls: 'form-group' //input-group
15557 type : this.inputType,
15558 cls : 'form-control x-combo-noedit',
15559 autocomplete: 'new-password',
15560 placeholder : this.placeholder || '',
15565 input.name = this.name;
15569 input.cls += ' input-' + this.size;
15572 if (this.disabled) {
15573 input.disabled = true;
15584 inputblock.cls += ' input-group';
15586 inputblock.cn.unshift({
15588 cls : 'input-group-addon input-group-prepend input-group-text',
15593 if(this.removable && !this.multiple){
15594 inputblock.cls += ' roo-removable';
15596 inputblock.cn.push({
15599 cls : 'roo-combo-removable-btn close'
15603 if(this.hasFeedback && !this.allowBlank){
15605 inputblock.cls += ' has-feedback';
15607 inputblock.cn.push({
15609 cls: 'glyphicon form-control-feedback'
15616 inputblock.cls += (this.before) ? '' : ' input-group';
15618 inputblock.cn.push({
15620 cls : 'input-group-addon input-group-append input-group-text',
15626 var ibwrap = inputblock;
15631 cls: 'roo-select2-choices',
15635 cls: 'roo-select2-search-field',
15648 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15653 cls: 'form-hidden-field'
15659 if(!this.multiple && this.showToggleBtn){
15665 if (this.caret != false) {
15668 cls: 'fa fa-' + this.caret
15675 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15677 Roo.bootstrap.version == 3 ? caret : '',
15680 cls: 'combobox-clear',
15694 combobox.cls += ' roo-select2-container-multi';
15697 var align = this.labelAlign || this.parentLabelAlign();
15699 if (align ==='left' && this.fieldLabel.length) {
15704 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15705 tooltip : 'This field is required'
15709 cls : 'control-label col-form-label',
15710 html : this.fieldLabel
15721 var labelCfg = cfg.cn[1];
15722 var contentCfg = cfg.cn[2];
15725 if(this.indicatorpos == 'right'){
15730 cls : 'control-label col-form-label',
15734 html : this.fieldLabel
15738 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15739 tooltip : 'This field is required'
15752 labelCfg = cfg.cn[0];
15753 contentCfg = cfg.cn[1];
15758 if(this.labelWidth > 12){
15759 labelCfg.style = "width: " + this.labelWidth + 'px';
15762 if(this.labelWidth < 13 && this.labelmd == 0){
15763 this.labelmd = this.labelWidth;
15766 if(this.labellg > 0){
15767 labelCfg.cls += ' col-lg-' + this.labellg;
15768 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15771 if(this.labelmd > 0){
15772 labelCfg.cls += ' col-md-' + this.labelmd;
15773 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15776 if(this.labelsm > 0){
15777 labelCfg.cls += ' col-sm-' + this.labelsm;
15778 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15781 if(this.labelxs > 0){
15782 labelCfg.cls += ' col-xs-' + this.labelxs;
15783 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15787 } else if ( this.fieldLabel.length) {
15791 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15792 tooltip : 'This field is required'
15796 cls : 'control-label',
15797 html : this.fieldLabel
15808 if(this.indicatorpos == 'right'){
15812 cls : 'control-label',
15813 html : this.fieldLabel,
15817 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15818 tooltip : 'This field is required'
15835 var settings = this;
15837 ['xs','sm','md','lg'].map(function(size){
15838 if (settings[size]) {
15839 cfg.cls += ' col-' + size + '-' + settings[size];
15846 initTouchView : function()
15848 this.renderTouchView();
15850 this.touchViewEl.on('scroll', function(){
15851 this.el.dom.scrollTop = 0;
15854 this.originalValue = this.getValue();
15856 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15858 this.inputEl().on("click", this.showTouchView, this);
15859 if (this.triggerEl) {
15860 this.triggerEl.on("click", this.showTouchView, this);
15864 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15865 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15867 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15869 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15870 this.store.on('load', this.onTouchViewLoad, this);
15871 this.store.on('loadexception', this.onTouchViewLoadException, this);
15873 if(this.hiddenName){
15875 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15877 this.hiddenField.dom.value =
15878 this.hiddenValue !== undefined ? this.hiddenValue :
15879 this.value !== undefined ? this.value : '';
15881 this.el.dom.removeAttribute('name');
15882 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15886 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15887 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15890 if(this.removable && !this.multiple){
15891 var close = this.closeTriggerEl();
15893 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15894 close.on('click', this.removeBtnClick, this, close);
15898 * fix the bug in Safari iOS8
15900 this.inputEl().on("focus", function(e){
15901 document.activeElement.blur();
15904 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15911 renderTouchView : function()
15913 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15914 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15916 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15917 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15919 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15920 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15921 this.touchViewBodyEl.setStyle('overflow', 'auto');
15923 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15924 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15926 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15927 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15931 showTouchView : function()
15937 this.touchViewHeaderEl.hide();
15939 if(this.modalTitle.length){
15940 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15941 this.touchViewHeaderEl.show();
15944 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15945 this.touchViewEl.show();
15947 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15949 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15950 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15952 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15954 if(this.modalTitle.length){
15955 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15958 this.touchViewBodyEl.setHeight(bodyHeight);
15962 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15964 this.touchViewEl.addClass('in');
15967 if(this._touchViewMask){
15968 Roo.get(document.body).addClass("x-body-masked");
15969 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15970 this._touchViewMask.setStyle('z-index', 10000);
15971 this._touchViewMask.addClass('show');
15974 this.doTouchViewQuery();
15978 hideTouchView : function()
15980 this.touchViewEl.removeClass('in');
15984 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15986 this.touchViewEl.setStyle('display', 'none');
15989 if(this._touchViewMask){
15990 this._touchViewMask.removeClass('show');
15991 Roo.get(document.body).removeClass("x-body-masked");
15995 setTouchViewValue : function()
16002 Roo.each(this.tickItems, function(o){
16007 this.hideTouchView();
16010 doTouchViewQuery : function()
16019 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
16023 if(!this.alwaysQuery || this.mode == 'local'){
16024 this.onTouchViewLoad();
16031 onTouchViewBeforeLoad : function(combo,opts)
16037 onTouchViewLoad : function()
16039 if(this.store.getCount() < 1){
16040 this.onTouchViewEmptyResults();
16044 this.clearTouchView();
16046 var rawValue = this.getRawValue();
16048 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
16050 this.tickItems = [];
16052 this.store.data.each(function(d, rowIndex){
16053 var row = this.touchViewListGroup.createChild(template);
16055 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
16056 row.addClass(d.data.cls);
16059 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16062 html : d.data[this.displayField]
16065 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
16066 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
16069 row.removeClass('selected');
16070 if(!this.multiple && this.valueField &&
16071 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
16074 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16075 row.addClass('selected');
16078 if(this.multiple && this.valueField &&
16079 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
16083 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16084 this.tickItems.push(d.data);
16087 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
16091 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
16093 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16095 if(this.modalTitle.length){
16096 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16099 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
16101 if(this.mobile_restrict_height && listHeight < bodyHeight){
16102 this.touchViewBodyEl.setHeight(listHeight);
16107 if(firstChecked && listHeight > bodyHeight){
16108 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
16113 onTouchViewLoadException : function()
16115 this.hideTouchView();
16118 onTouchViewEmptyResults : function()
16120 this.clearTouchView();
16122 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
16124 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
16128 clearTouchView : function()
16130 this.touchViewListGroup.dom.innerHTML = '';
16133 onTouchViewClick : function(e, el, o)
16135 e.preventDefault();
16138 var rowIndex = o.rowIndex;
16140 var r = this.store.getAt(rowIndex);
16142 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
16144 if(!this.multiple){
16145 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
16146 c.dom.removeAttribute('checked');
16149 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16151 this.setFromData(r.data);
16153 var close = this.closeTriggerEl();
16159 this.hideTouchView();
16161 this.fireEvent('select', this, r, rowIndex);
16166 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
16167 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
16168 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
16172 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16173 this.addItem(r.data);
16174 this.tickItems.push(r.data);
16178 getAutoCreateNativeIOS : function()
16181 cls: 'form-group' //input-group,
16186 cls : 'roo-ios-select'
16190 combobox.name = this.name;
16193 if (this.disabled) {
16194 combobox.disabled = true;
16197 var settings = this;
16199 ['xs','sm','md','lg'].map(function(size){
16200 if (settings[size]) {
16201 cfg.cls += ' col-' + size + '-' + settings[size];
16211 initIOSView : function()
16213 this.store.on('load', this.onIOSViewLoad, this);
16218 onIOSViewLoad : function()
16220 if(this.store.getCount() < 1){
16224 this.clearIOSView();
16226 if(this.allowBlank) {
16228 var default_text = '-- SELECT --';
16230 if(this.placeholder.length){
16231 default_text = this.placeholder;
16234 if(this.emptyTitle.length){
16235 default_text += ' - ' + this.emptyTitle + ' -';
16238 var opt = this.inputEl().createChild({
16241 html : default_text
16245 o[this.valueField] = 0;
16246 o[this.displayField] = default_text;
16248 this.ios_options.push({
16255 this.store.data.each(function(d, rowIndex){
16259 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16260 html = d.data[this.displayField];
16265 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16266 value = d.data[this.valueField];
16275 if(this.value == d.data[this.valueField]){
16276 option['selected'] = true;
16279 var opt = this.inputEl().createChild(option);
16281 this.ios_options.push({
16288 this.inputEl().on('change', function(){
16289 this.fireEvent('select', this);
16294 clearIOSView: function()
16296 this.inputEl().dom.innerHTML = '';
16298 this.ios_options = [];
16301 setIOSValue: function(v)
16305 if(!this.ios_options){
16309 Roo.each(this.ios_options, function(opts){
16311 opts.el.dom.removeAttribute('selected');
16313 if(opts.data[this.valueField] != v){
16317 opts.el.dom.setAttribute('selected', true);
16323 * @cfg {Boolean} grow
16327 * @cfg {Number} growMin
16331 * @cfg {Number} growMax
16340 Roo.apply(Roo.bootstrap.ComboBox, {
16344 cls: 'modal-header',
16366 cls: 'list-group-item',
16370 cls: 'roo-combobox-list-group-item-value'
16374 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16388 listItemCheckbox : {
16390 cls: 'list-group-item',
16394 cls: 'roo-combobox-list-group-item-value'
16398 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16414 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16419 cls: 'modal-footer',
16427 cls: 'col-xs-6 text-left',
16430 cls: 'btn btn-danger roo-touch-view-cancel',
16436 cls: 'col-xs-6 text-right',
16439 cls: 'btn btn-success roo-touch-view-ok',
16450 Roo.apply(Roo.bootstrap.ComboBox, {
16452 touchViewTemplate : {
16454 cls: 'modal fade roo-combobox-touch-view',
16458 cls: 'modal-dialog',
16459 style : 'position:fixed', // we have to fix position....
16463 cls: 'modal-content',
16465 Roo.bootstrap.ComboBox.header,
16466 Roo.bootstrap.ComboBox.body,
16467 Roo.bootstrap.ComboBox.footer
16476 * Ext JS Library 1.1.1
16477 * Copyright(c) 2006-2007, Ext JS, LLC.
16479 * Originally Released Under LGPL - original licence link has changed is not relivant.
16482 * <script type="text/javascript">
16487 * @extends Roo.util.Observable
16488 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
16489 * This class also supports single and multi selection modes. <br>
16490 * Create a data model bound view:
16492 var store = new Roo.data.Store(...);
16494 var view = new Roo.View({
16496 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
16498 singleSelect: true,
16499 selectedClass: "ydataview-selected",
16503 // listen for node click?
16504 view.on("click", function(vw, index, node, e){
16505 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16509 dataModel.load("foobar.xml");
16511 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16513 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16514 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16516 * Note: old style constructor is still suported (container, template, config)
16519 * Create a new View
16520 * @param {Object} config The config object
16523 Roo.View = function(config, depreciated_tpl, depreciated_config){
16525 this.parent = false;
16527 if (typeof(depreciated_tpl) == 'undefined') {
16528 // new way.. - universal constructor.
16529 Roo.apply(this, config);
16530 this.el = Roo.get(this.el);
16533 this.el = Roo.get(config);
16534 this.tpl = depreciated_tpl;
16535 Roo.apply(this, depreciated_config);
16537 this.wrapEl = this.el.wrap().wrap();
16538 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16541 if(typeof(this.tpl) == "string"){
16542 this.tpl = new Roo.Template(this.tpl);
16544 // support xtype ctors..
16545 this.tpl = new Roo.factory(this.tpl, Roo);
16549 this.tpl.compile();
16554 * @event beforeclick
16555 * Fires before a click is processed. Returns false to cancel the default action.
16556 * @param {Roo.View} this
16557 * @param {Number} index The index of the target node
16558 * @param {HTMLElement} node The target node
16559 * @param {Roo.EventObject} e The raw event object
16561 "beforeclick" : true,
16564 * Fires when a template node is clicked.
16565 * @param {Roo.View} this
16566 * @param {Number} index The index of the target node
16567 * @param {HTMLElement} node The target node
16568 * @param {Roo.EventObject} e The raw event object
16573 * Fires when a template node is double clicked.
16574 * @param {Roo.View} this
16575 * @param {Number} index The index of the target node
16576 * @param {HTMLElement} node The target node
16577 * @param {Roo.EventObject} e The raw event object
16581 * @event contextmenu
16582 * Fires when a template node is right clicked.
16583 * @param {Roo.View} this
16584 * @param {Number} index The index of the target node
16585 * @param {HTMLElement} node The target node
16586 * @param {Roo.EventObject} e The raw event object
16588 "contextmenu" : true,
16590 * @event selectionchange
16591 * Fires when the selected nodes change.
16592 * @param {Roo.View} this
16593 * @param {Array} selections Array of the selected nodes
16595 "selectionchange" : true,
16598 * @event beforeselect
16599 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16600 * @param {Roo.View} this
16601 * @param {HTMLElement} node The node to be selected
16602 * @param {Array} selections Array of currently selected nodes
16604 "beforeselect" : true,
16606 * @event preparedata
16607 * Fires on every row to render, to allow you to change the data.
16608 * @param {Roo.View} this
16609 * @param {Object} data to be rendered (change this)
16611 "preparedata" : true
16619 "click": this.onClick,
16620 "dblclick": this.onDblClick,
16621 "contextmenu": this.onContextMenu,
16625 this.selections = [];
16627 this.cmp = new Roo.CompositeElementLite([]);
16629 this.store = Roo.factory(this.store, Roo.data);
16630 this.setStore(this.store, true);
16633 if ( this.footer && this.footer.xtype) {
16635 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16637 this.footer.dataSource = this.store;
16638 this.footer.container = fctr;
16639 this.footer = Roo.factory(this.footer, Roo);
16640 fctr.insertFirst(this.el);
16642 // this is a bit insane - as the paging toolbar seems to detach the el..
16643 // dom.parentNode.parentNode.parentNode
16644 // they get detached?
16648 Roo.View.superclass.constructor.call(this);
16653 Roo.extend(Roo.View, Roo.util.Observable, {
16656 * @cfg {Roo.data.Store} store Data store to load data from.
16661 * @cfg {String|Roo.Element} el The container element.
16666 * @cfg {String|Roo.Template} tpl The template used by this View
16670 * @cfg {String} dataName the named area of the template to use as the data area
16671 * Works with domtemplates roo-name="name"
16675 * @cfg {String} selectedClass The css class to add to selected nodes
16677 selectedClass : "x-view-selected",
16679 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16684 * @cfg {String} text to display on mask (default Loading)
16688 * @cfg {Boolean} multiSelect Allow multiple selection
16690 multiSelect : false,
16692 * @cfg {Boolean} singleSelect Allow single selection
16694 singleSelect: false,
16697 * @cfg {Boolean} toggleSelect - selecting
16699 toggleSelect : false,
16702 * @cfg {Boolean} tickable - selecting
16707 * Returns the element this view is bound to.
16708 * @return {Roo.Element}
16710 getEl : function(){
16711 return this.wrapEl;
16717 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16719 refresh : function(){
16720 //Roo.log('refresh');
16723 // if we are using something like 'domtemplate', then
16724 // the what gets used is:
16725 // t.applySubtemplate(NAME, data, wrapping data..)
16726 // the outer template then get' applied with
16727 // the store 'extra data'
16728 // and the body get's added to the
16729 // roo-name="data" node?
16730 // <span class='roo-tpl-{name}'></span> ?????
16734 this.clearSelections();
16735 this.el.update("");
16737 var records = this.store.getRange();
16738 if(records.length < 1) {
16740 // is this valid?? = should it render a template??
16742 this.el.update(this.emptyText);
16746 if (this.dataName) {
16747 this.el.update(t.apply(this.store.meta)); //????
16748 el = this.el.child('.roo-tpl-' + this.dataName);
16751 for(var i = 0, len = records.length; i < len; i++){
16752 var data = this.prepareData(records[i].data, i, records[i]);
16753 this.fireEvent("preparedata", this, data, i, records[i]);
16755 var d = Roo.apply({}, data);
16758 Roo.apply(d, {'roo-id' : Roo.id()});
16762 Roo.each(this.parent.item, function(item){
16763 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16766 Roo.apply(d, {'roo-data-checked' : 'checked'});
16770 html[html.length] = Roo.util.Format.trim(
16772 t.applySubtemplate(this.dataName, d, this.store.meta) :
16779 el.update(html.join(""));
16780 this.nodes = el.dom.childNodes;
16781 this.updateIndexes(0);
16786 * Function to override to reformat the data that is sent to
16787 * the template for each node.
16788 * DEPRICATED - use the preparedata event handler.
16789 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16790 * a JSON object for an UpdateManager bound view).
16792 prepareData : function(data, index, record)
16794 this.fireEvent("preparedata", this, data, index, record);
16798 onUpdate : function(ds, record){
16799 // Roo.log('on update');
16800 this.clearSelections();
16801 var index = this.store.indexOf(record);
16802 var n = this.nodes[index];
16803 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16804 n.parentNode.removeChild(n);
16805 this.updateIndexes(index, index);
16811 onAdd : function(ds, records, index)
16813 //Roo.log(['on Add', ds, records, index] );
16814 this.clearSelections();
16815 if(this.nodes.length == 0){
16819 var n = this.nodes[index];
16820 for(var i = 0, len = records.length; i < len; i++){
16821 var d = this.prepareData(records[i].data, i, records[i]);
16823 this.tpl.insertBefore(n, d);
16826 this.tpl.append(this.el, d);
16829 this.updateIndexes(index);
16832 onRemove : function(ds, record, index){
16833 // Roo.log('onRemove');
16834 this.clearSelections();
16835 var el = this.dataName ?
16836 this.el.child('.roo-tpl-' + this.dataName) :
16839 el.dom.removeChild(this.nodes[index]);
16840 this.updateIndexes(index);
16844 * Refresh an individual node.
16845 * @param {Number} index
16847 refreshNode : function(index){
16848 this.onUpdate(this.store, this.store.getAt(index));
16851 updateIndexes : function(startIndex, endIndex){
16852 var ns = this.nodes;
16853 startIndex = startIndex || 0;
16854 endIndex = endIndex || ns.length - 1;
16855 for(var i = startIndex; i <= endIndex; i++){
16856 ns[i].nodeIndex = i;
16861 * Changes the data store this view uses and refresh the view.
16862 * @param {Store} store
16864 setStore : function(store, initial){
16865 if(!initial && this.store){
16866 this.store.un("datachanged", this.refresh);
16867 this.store.un("add", this.onAdd);
16868 this.store.un("remove", this.onRemove);
16869 this.store.un("update", this.onUpdate);
16870 this.store.un("clear", this.refresh);
16871 this.store.un("beforeload", this.onBeforeLoad);
16872 this.store.un("load", this.onLoad);
16873 this.store.un("loadexception", this.onLoad);
16877 store.on("datachanged", this.refresh, this);
16878 store.on("add", this.onAdd, this);
16879 store.on("remove", this.onRemove, this);
16880 store.on("update", this.onUpdate, this);
16881 store.on("clear", this.refresh, this);
16882 store.on("beforeload", this.onBeforeLoad, this);
16883 store.on("load", this.onLoad, this);
16884 store.on("loadexception", this.onLoad, this);
16892 * onbeforeLoad - masks the loading area.
16895 onBeforeLoad : function(store,opts)
16897 //Roo.log('onBeforeLoad');
16899 this.el.update("");
16901 this.el.mask(this.mask ? this.mask : "Loading" );
16903 onLoad : function ()
16910 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16911 * @param {HTMLElement} node
16912 * @return {HTMLElement} The template node
16914 findItemFromChild : function(node){
16915 var el = this.dataName ?
16916 this.el.child('.roo-tpl-' + this.dataName,true) :
16919 if(!node || node.parentNode == el){
16922 var p = node.parentNode;
16923 while(p && p != el){
16924 if(p.parentNode == el){
16933 onClick : function(e){
16934 var item = this.findItemFromChild(e.getTarget());
16936 var index = this.indexOf(item);
16937 if(this.onItemClick(item, index, e) !== false){
16938 this.fireEvent("click", this, index, item, e);
16941 this.clearSelections();
16946 onContextMenu : function(e){
16947 var item = this.findItemFromChild(e.getTarget());
16949 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16954 onDblClick : function(e){
16955 var item = this.findItemFromChild(e.getTarget());
16957 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16961 onItemClick : function(item, index, e)
16963 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16966 if (this.toggleSelect) {
16967 var m = this.isSelected(item) ? 'unselect' : 'select';
16970 _t[m](item, true, false);
16973 if(this.multiSelect || this.singleSelect){
16974 if(this.multiSelect && e.shiftKey && this.lastSelection){
16975 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16977 this.select(item, this.multiSelect && e.ctrlKey);
16978 this.lastSelection = item;
16981 if(!this.tickable){
16982 e.preventDefault();
16990 * Get the number of selected nodes.
16993 getSelectionCount : function(){
16994 return this.selections.length;
16998 * Get the currently selected nodes.
16999 * @return {Array} An array of HTMLElements
17001 getSelectedNodes : function(){
17002 return this.selections;
17006 * Get the indexes of the selected nodes.
17009 getSelectedIndexes : function(){
17010 var indexes = [], s = this.selections;
17011 for(var i = 0, len = s.length; i < len; i++){
17012 indexes.push(s[i].nodeIndex);
17018 * Clear all selections
17019 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
17021 clearSelections : function(suppressEvent){
17022 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
17023 this.cmp.elements = this.selections;
17024 this.cmp.removeClass(this.selectedClass);
17025 this.selections = [];
17026 if(!suppressEvent){
17027 this.fireEvent("selectionchange", this, this.selections);
17033 * Returns true if the passed node is selected
17034 * @param {HTMLElement/Number} node The node or node index
17035 * @return {Boolean}
17037 isSelected : function(node){
17038 var s = this.selections;
17042 node = this.getNode(node);
17043 return s.indexOf(node) !== -1;
17048 * @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
17049 * @param {Boolean} keepExisting (optional) true to keep existing selections
17050 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17052 select : function(nodeInfo, keepExisting, suppressEvent){
17053 if(nodeInfo instanceof Array){
17055 this.clearSelections(true);
17057 for(var i = 0, len = nodeInfo.length; i < len; i++){
17058 this.select(nodeInfo[i], true, true);
17062 var node = this.getNode(nodeInfo);
17063 if(!node || this.isSelected(node)){
17064 return; // already selected.
17067 this.clearSelections(true);
17070 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
17071 Roo.fly(node).addClass(this.selectedClass);
17072 this.selections.push(node);
17073 if(!suppressEvent){
17074 this.fireEvent("selectionchange", this, this.selections);
17082 * @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
17083 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
17084 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17086 unselect : function(nodeInfo, keepExisting, suppressEvent)
17088 if(nodeInfo instanceof Array){
17089 Roo.each(this.selections, function(s) {
17090 this.unselect(s, nodeInfo);
17094 var node = this.getNode(nodeInfo);
17095 if(!node || !this.isSelected(node)){
17096 //Roo.log("not selected");
17097 return; // not selected.
17101 Roo.each(this.selections, function(s) {
17103 Roo.fly(node).removeClass(this.selectedClass);
17110 this.selections= ns;
17111 this.fireEvent("selectionchange", this, this.selections);
17115 * Gets a template node.
17116 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17117 * @return {HTMLElement} The node or null if it wasn't found
17119 getNode : function(nodeInfo){
17120 if(typeof nodeInfo == "string"){
17121 return document.getElementById(nodeInfo);
17122 }else if(typeof nodeInfo == "number"){
17123 return this.nodes[nodeInfo];
17129 * Gets a range template nodes.
17130 * @param {Number} startIndex
17131 * @param {Number} endIndex
17132 * @return {Array} An array of nodes
17134 getNodes : function(start, end){
17135 var ns = this.nodes;
17136 start = start || 0;
17137 end = typeof end == "undefined" ? ns.length - 1 : end;
17140 for(var i = start; i <= end; i++){
17144 for(var i = start; i >= end; i--){
17152 * Finds the index of the passed node
17153 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17154 * @return {Number} The index of the node or -1
17156 indexOf : function(node){
17157 node = this.getNode(node);
17158 if(typeof node.nodeIndex == "number"){
17159 return node.nodeIndex;
17161 var ns = this.nodes;
17162 for(var i = 0, len = ns.length; i < len; i++){
17173 * based on jquery fullcalendar
17177 Roo.bootstrap = Roo.bootstrap || {};
17179 * @class Roo.bootstrap.Calendar
17180 * @extends Roo.bootstrap.Component
17181 * Bootstrap Calendar class
17182 * @cfg {Boolean} loadMask (true|false) default false
17183 * @cfg {Object} header generate the user specific header of the calendar, default false
17186 * Create a new Container
17187 * @param {Object} config The config object
17192 Roo.bootstrap.Calendar = function(config){
17193 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
17197 * Fires when a date is selected
17198 * @param {DatePicker} this
17199 * @param {Date} date The selected date
17203 * @event monthchange
17204 * Fires when the displayed month changes
17205 * @param {DatePicker} this
17206 * @param {Date} date The selected month
17208 'monthchange': true,
17210 * @event evententer
17211 * Fires when mouse over an event
17212 * @param {Calendar} this
17213 * @param {event} Event
17215 'evententer': true,
17217 * @event eventleave
17218 * Fires when the mouse leaves an
17219 * @param {Calendar} this
17222 'eventleave': true,
17224 * @event eventclick
17225 * Fires when the mouse click an
17226 * @param {Calendar} this
17235 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
17238 * @cfg {Number} startDay
17239 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
17247 getAutoCreate : function(){
17250 var fc_button = function(name, corner, style, content ) {
17251 return Roo.apply({},{
17253 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
17255 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
17258 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
17269 style : 'width:100%',
17276 cls : 'fc-header-left',
17278 fc_button('prev', 'left', 'arrow', '‹' ),
17279 fc_button('next', 'right', 'arrow', '›' ),
17280 { tag: 'span', cls: 'fc-header-space' },
17281 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
17289 cls : 'fc-header-center',
17293 cls: 'fc-header-title',
17296 html : 'month / year'
17304 cls : 'fc-header-right',
17306 /* fc_button('month', 'left', '', 'month' ),
17307 fc_button('week', '', '', 'week' ),
17308 fc_button('day', 'right', '', 'day' )
17320 header = this.header;
17323 var cal_heads = function() {
17325 // fixme - handle this.
17327 for (var i =0; i < Date.dayNames.length; i++) {
17328 var d = Date.dayNames[i];
17331 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17332 html : d.substring(0,3)
17336 ret[0].cls += ' fc-first';
17337 ret[6].cls += ' fc-last';
17340 var cal_cell = function(n) {
17343 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17348 cls: 'fc-day-number',
17352 cls: 'fc-day-content',
17356 style: 'position: relative;' // height: 17px;
17368 var cal_rows = function() {
17371 for (var r = 0; r < 6; r++) {
17378 for (var i =0; i < Date.dayNames.length; i++) {
17379 var d = Date.dayNames[i];
17380 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17383 row.cn[0].cls+=' fc-first';
17384 row.cn[0].cn[0].style = 'min-height:90px';
17385 row.cn[6].cls+=' fc-last';
17389 ret[0].cls += ' fc-first';
17390 ret[4].cls += ' fc-prev-last';
17391 ret[5].cls += ' fc-last';
17398 cls: 'fc-border-separate',
17399 style : 'width:100%',
17407 cls : 'fc-first fc-last',
17425 cls : 'fc-content',
17426 style : "position: relative;",
17429 cls : 'fc-view fc-view-month fc-grid',
17430 style : 'position: relative',
17431 unselectable : 'on',
17434 cls : 'fc-event-container',
17435 style : 'position:absolute;z-index:8;top:0;left:0;'
17453 initEvents : function()
17456 throw "can not find store for calendar";
17462 style: "text-align:center",
17466 style: "background-color:white;width:50%;margin:250 auto",
17470 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
17481 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17483 var size = this.el.select('.fc-content', true).first().getSize();
17484 this.maskEl.setSize(size.width, size.height);
17485 this.maskEl.enableDisplayMode("block");
17486 if(!this.loadMask){
17487 this.maskEl.hide();
17490 this.store = Roo.factory(this.store, Roo.data);
17491 this.store.on('load', this.onLoad, this);
17492 this.store.on('beforeload', this.onBeforeLoad, this);
17496 this.cells = this.el.select('.fc-day',true);
17497 //Roo.log(this.cells);
17498 this.textNodes = this.el.query('.fc-day-number');
17499 this.cells.addClassOnOver('fc-state-hover');
17501 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17502 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17503 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17504 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17506 this.on('monthchange', this.onMonthChange, this);
17508 this.update(new Date().clearTime());
17511 resize : function() {
17512 var sz = this.el.getSize();
17514 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17515 this.el.select('.fc-day-content div',true).setHeight(34);
17520 showPrevMonth : function(e){
17521 this.update(this.activeDate.add("mo", -1));
17523 showToday : function(e){
17524 this.update(new Date().clearTime());
17527 showNextMonth : function(e){
17528 this.update(this.activeDate.add("mo", 1));
17532 showPrevYear : function(){
17533 this.update(this.activeDate.add("y", -1));
17537 showNextYear : function(){
17538 this.update(this.activeDate.add("y", 1));
17543 update : function(date)
17545 var vd = this.activeDate;
17546 this.activeDate = date;
17547 // if(vd && this.el){
17548 // var t = date.getTime();
17549 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17550 // Roo.log('using add remove');
17552 // this.fireEvent('monthchange', this, date);
17554 // this.cells.removeClass("fc-state-highlight");
17555 // this.cells.each(function(c){
17556 // if(c.dateValue == t){
17557 // c.addClass("fc-state-highlight");
17558 // setTimeout(function(){
17559 // try{c.dom.firstChild.focus();}catch(e){}
17569 var days = date.getDaysInMonth();
17571 var firstOfMonth = date.getFirstDateOfMonth();
17572 var startingPos = firstOfMonth.getDay()-this.startDay;
17574 if(startingPos < this.startDay){
17578 var pm = date.add(Date.MONTH, -1);
17579 var prevStart = pm.getDaysInMonth()-startingPos;
17581 this.cells = this.el.select('.fc-day',true);
17582 this.textNodes = this.el.query('.fc-day-number');
17583 this.cells.addClassOnOver('fc-state-hover');
17585 var cells = this.cells.elements;
17586 var textEls = this.textNodes;
17588 Roo.each(cells, function(cell){
17589 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17592 days += startingPos;
17594 // convert everything to numbers so it's fast
17595 var day = 86400000;
17596 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17599 //Roo.log(prevStart);
17601 var today = new Date().clearTime().getTime();
17602 var sel = date.clearTime().getTime();
17603 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17604 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17605 var ddMatch = this.disabledDatesRE;
17606 var ddText = this.disabledDatesText;
17607 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17608 var ddaysText = this.disabledDaysText;
17609 var format = this.format;
17611 var setCellClass = function(cal, cell){
17615 //Roo.log('set Cell Class');
17617 var t = d.getTime();
17621 cell.dateValue = t;
17623 cell.className += " fc-today";
17624 cell.className += " fc-state-highlight";
17625 cell.title = cal.todayText;
17628 // disable highlight in other month..
17629 //cell.className += " fc-state-highlight";
17634 cell.className = " fc-state-disabled";
17635 cell.title = cal.minText;
17639 cell.className = " fc-state-disabled";
17640 cell.title = cal.maxText;
17644 if(ddays.indexOf(d.getDay()) != -1){
17645 cell.title = ddaysText;
17646 cell.className = " fc-state-disabled";
17649 if(ddMatch && format){
17650 var fvalue = d.dateFormat(format);
17651 if(ddMatch.test(fvalue)){
17652 cell.title = ddText.replace("%0", fvalue);
17653 cell.className = " fc-state-disabled";
17657 if (!cell.initialClassName) {
17658 cell.initialClassName = cell.dom.className;
17661 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17666 for(; i < startingPos; i++) {
17667 textEls[i].innerHTML = (++prevStart);
17668 d.setDate(d.getDate()+1);
17670 cells[i].className = "fc-past fc-other-month";
17671 setCellClass(this, cells[i]);
17676 for(; i < days; i++){
17677 intDay = i - startingPos + 1;
17678 textEls[i].innerHTML = (intDay);
17679 d.setDate(d.getDate()+1);
17681 cells[i].className = ''; // "x-date-active";
17682 setCellClass(this, cells[i]);
17686 for(; i < 42; i++) {
17687 textEls[i].innerHTML = (++extraDays);
17688 d.setDate(d.getDate()+1);
17690 cells[i].className = "fc-future fc-other-month";
17691 setCellClass(this, cells[i]);
17694 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17696 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17698 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17699 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17701 if(totalRows != 6){
17702 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17703 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17706 this.fireEvent('monthchange', this, date);
17710 if(!this.internalRender){
17711 var main = this.el.dom.firstChild;
17712 var w = main.offsetWidth;
17713 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17714 Roo.fly(main).setWidth(w);
17715 this.internalRender = true;
17716 // opera does not respect the auto grow header center column
17717 // then, after it gets a width opera refuses to recalculate
17718 // without a second pass
17719 if(Roo.isOpera && !this.secondPass){
17720 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17721 this.secondPass = true;
17722 this.update.defer(10, this, [date]);
17729 findCell : function(dt) {
17730 dt = dt.clearTime().getTime();
17732 this.cells.each(function(c){
17733 //Roo.log("check " +c.dateValue + '?=' + dt);
17734 if(c.dateValue == dt){
17744 findCells : function(ev) {
17745 var s = ev.start.clone().clearTime().getTime();
17747 var e= ev.end.clone().clearTime().getTime();
17750 this.cells.each(function(c){
17751 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17753 if(c.dateValue > e){
17756 if(c.dateValue < s){
17765 // findBestRow: function(cells)
17769 // for (var i =0 ; i < cells.length;i++) {
17770 // ret = Math.max(cells[i].rows || 0,ret);
17777 addItem : function(ev)
17779 // look for vertical location slot in
17780 var cells = this.findCells(ev);
17782 // ev.row = this.findBestRow(cells);
17784 // work out the location.
17788 for(var i =0; i < cells.length; i++) {
17790 cells[i].row = cells[0].row;
17793 cells[i].row = cells[i].row + 1;
17803 if (crow.start.getY() == cells[i].getY()) {
17805 crow.end = cells[i];
17822 cells[0].events.push(ev);
17824 this.calevents.push(ev);
17827 clearEvents: function() {
17829 if(!this.calevents){
17833 Roo.each(this.cells.elements, function(c){
17839 Roo.each(this.calevents, function(e) {
17840 Roo.each(e.els, function(el) {
17841 el.un('mouseenter' ,this.onEventEnter, this);
17842 el.un('mouseleave' ,this.onEventLeave, this);
17847 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17853 renderEvents: function()
17857 this.cells.each(function(c) {
17866 if(c.row != c.events.length){
17867 r = 4 - (4 - (c.row - c.events.length));
17870 c.events = ev.slice(0, r);
17871 c.more = ev.slice(r);
17873 if(c.more.length && c.more.length == 1){
17874 c.events.push(c.more.pop());
17877 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17881 this.cells.each(function(c) {
17883 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17886 for (var e = 0; e < c.events.length; e++){
17887 var ev = c.events[e];
17888 var rows = ev.rows;
17890 for(var i = 0; i < rows.length; i++) {
17892 // how many rows should it span..
17895 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17896 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17898 unselectable : "on",
17901 cls: 'fc-event-inner',
17905 // cls: 'fc-event-time',
17906 // html : cells.length > 1 ? '' : ev.time
17910 cls: 'fc-event-title',
17911 html : String.format('{0}', ev.title)
17918 cls: 'ui-resizable-handle ui-resizable-e',
17919 html : '  '
17926 cfg.cls += ' fc-event-start';
17928 if ((i+1) == rows.length) {
17929 cfg.cls += ' fc-event-end';
17932 var ctr = _this.el.select('.fc-event-container',true).first();
17933 var cg = ctr.createChild(cfg);
17935 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17936 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17938 var r = (c.more.length) ? 1 : 0;
17939 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17940 cg.setWidth(ebox.right - sbox.x -2);
17942 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17943 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17944 cg.on('click', _this.onEventClick, _this, ev);
17955 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17956 style : 'position: absolute',
17957 unselectable : "on",
17960 cls: 'fc-event-inner',
17964 cls: 'fc-event-title',
17972 cls: 'ui-resizable-handle ui-resizable-e',
17973 html : '  '
17979 var ctr = _this.el.select('.fc-event-container',true).first();
17980 var cg = ctr.createChild(cfg);
17982 var sbox = c.select('.fc-day-content',true).first().getBox();
17983 var ebox = c.select('.fc-day-content',true).first().getBox();
17985 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17986 cg.setWidth(ebox.right - sbox.x -2);
17988 cg.on('click', _this.onMoreEventClick, _this, c.more);
17998 onEventEnter: function (e, el,event,d) {
17999 this.fireEvent('evententer', this, el, event);
18002 onEventLeave: function (e, el,event,d) {
18003 this.fireEvent('eventleave', this, el, event);
18006 onEventClick: function (e, el,event,d) {
18007 this.fireEvent('eventclick', this, el, event);
18010 onMonthChange: function () {
18014 onMoreEventClick: function(e, el, more)
18018 this.calpopover.placement = 'right';
18019 this.calpopover.setTitle('More');
18021 this.calpopover.setContent('');
18023 var ctr = this.calpopover.el.select('.popover-content', true).first();
18025 Roo.each(more, function(m){
18027 cls : 'fc-event-hori fc-event-draggable',
18030 var cg = ctr.createChild(cfg);
18032 cg.on('click', _this.onEventClick, _this, m);
18035 this.calpopover.show(el);
18040 onLoad: function ()
18042 this.calevents = [];
18045 if(this.store.getCount() > 0){
18046 this.store.data.each(function(d){
18049 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
18050 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
18051 time : d.data.start_time,
18052 title : d.data.title,
18053 description : d.data.description,
18054 venue : d.data.venue
18059 this.renderEvents();
18061 if(this.calevents.length && this.loadMask){
18062 this.maskEl.hide();
18066 onBeforeLoad: function()
18068 this.clearEvents();
18070 this.maskEl.show();
18084 * @class Roo.bootstrap.Popover
18085 * @extends Roo.bootstrap.Component
18086 * Bootstrap Popover class
18087 * @cfg {String} html contents of the popover (or false to use children..)
18088 * @cfg {String} title of popover (or false to hide)
18089 * @cfg {String} placement how it is placed
18090 * @cfg {String} trigger click || hover (or false to trigger manually)
18091 * @cfg {String} over what (parent or false to trigger manually.)
18092 * @cfg {Number} delay - delay before showing
18095 * Create a new Popover
18096 * @param {Object} config The config object
18099 Roo.bootstrap.Popover = function(config){
18100 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
18106 * After the popover show
18108 * @param {Roo.bootstrap.Popover} this
18113 * After the popover hide
18115 * @param {Roo.bootstrap.Popover} this
18121 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
18123 title: 'Fill in a title',
18126 placement : 'right',
18127 trigger : 'hover', // hover
18133 can_build_overlaid : false,
18135 getChildContainer : function()
18137 return this.el.select('.popover-content',true).first();
18140 getAutoCreate : function(){
18143 cls : 'popover roo-dynamic',
18144 style: 'display:block',
18150 cls : 'popover-inner',
18154 cls: 'popover-title popover-header',
18158 cls : 'popover-content popover-body',
18169 setTitle: function(str)
18172 this.el.select('.popover-title',true).first().dom.innerHTML = str;
18174 setContent: function(str)
18177 this.el.select('.popover-content',true).first().dom.innerHTML = str;
18179 // as it get's added to the bottom of the page.
18180 onRender : function(ct, position)
18182 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
18184 var cfg = Roo.apply({}, this.getAutoCreate());
18188 cfg.cls += ' ' + this.cls;
18191 cfg.style = this.style;
18193 //Roo.log("adding to ");
18194 this.el = Roo.get(document.body).createChild(cfg, position);
18195 // Roo.log(this.el);
18200 initEvents : function()
18202 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
18203 this.el.enableDisplayMode('block');
18205 if (this.over === false) {
18208 if (this.triggers === false) {
18211 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18212 var triggers = this.trigger ? this.trigger.split(' ') : [];
18213 Roo.each(triggers, function(trigger) {
18215 if (trigger == 'click') {
18216 on_el.on('click', this.toggle, this);
18217 } else if (trigger != 'manual') {
18218 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
18219 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
18221 on_el.on(eventIn ,this.enter, this);
18222 on_el.on(eventOut, this.leave, this);
18233 toggle : function () {
18234 this.hoverState == 'in' ? this.leave() : this.enter();
18237 enter : function () {
18239 clearTimeout(this.timeout);
18241 this.hoverState = 'in';
18243 if (!this.delay || !this.delay.show) {
18248 this.timeout = setTimeout(function () {
18249 if (_t.hoverState == 'in') {
18252 }, this.delay.show)
18255 leave : function() {
18256 clearTimeout(this.timeout);
18258 this.hoverState = 'out';
18260 if (!this.delay || !this.delay.hide) {
18265 this.timeout = setTimeout(function () {
18266 if (_t.hoverState == 'out') {
18269 }, this.delay.hide)
18272 show : function (on_el)
18275 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18279 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18280 if (this.html !== false) {
18281 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18283 this.el.removeClass([
18284 'fade','top','bottom', 'left', 'right','in',
18285 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18287 if (!this.title.length) {
18288 this.el.select('.popover-title',true).hide();
18291 var placement = typeof this.placement == 'function' ?
18292 this.placement.call(this, this.el, on_el) :
18295 var autoToken = /\s?auto?\s?/i;
18296 var autoPlace = autoToken.test(placement);
18298 placement = placement.replace(autoToken, '') || 'top';
18302 //this.el.setXY([0,0]);
18304 this.el.dom.style.display='block';
18305 this.el.addClass(placement);
18307 //this.el.appendTo(on_el);
18309 var p = this.getPosition();
18310 var box = this.el.getBox();
18315 var align = Roo.bootstrap.Popover.alignment[placement];
18318 this.el.alignTo(on_el, align[0],align[1]);
18319 //var arrow = this.el.select('.arrow',true).first();
18320 //arrow.set(align[2],
18322 this.el.addClass('in');
18325 if (this.el.hasClass('fade')) {
18329 this.hoverState = 'in';
18331 this.fireEvent('show', this);
18336 this.el.setXY([0,0]);
18337 this.el.removeClass('in');
18339 this.hoverState = null;
18341 this.fireEvent('hide', this);
18346 Roo.bootstrap.Popover.alignment = {
18347 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18348 'right' : ['l-r', [10,0], 'left bs-popover-left'],
18349 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18350 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18361 * @class Roo.bootstrap.Progress
18362 * @extends Roo.bootstrap.Component
18363 * Bootstrap Progress class
18364 * @cfg {Boolean} striped striped of the progress bar
18365 * @cfg {Boolean} active animated of the progress bar
18369 * Create a new Progress
18370 * @param {Object} config The config object
18373 Roo.bootstrap.Progress = function(config){
18374 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18377 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
18382 getAutoCreate : function(){
18390 cfg.cls += ' progress-striped';
18394 cfg.cls += ' active';
18413 * @class Roo.bootstrap.ProgressBar
18414 * @extends Roo.bootstrap.Component
18415 * Bootstrap ProgressBar class
18416 * @cfg {Number} aria_valuenow aria-value now
18417 * @cfg {Number} aria_valuemin aria-value min
18418 * @cfg {Number} aria_valuemax aria-value max
18419 * @cfg {String} label label for the progress bar
18420 * @cfg {String} panel (success | info | warning | danger )
18421 * @cfg {String} role role of the progress bar
18422 * @cfg {String} sr_only text
18426 * Create a new ProgressBar
18427 * @param {Object} config The config object
18430 Roo.bootstrap.ProgressBar = function(config){
18431 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18434 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
18438 aria_valuemax : 100,
18444 getAutoCreate : function()
18449 cls: 'progress-bar',
18450 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18462 cfg.role = this.role;
18465 if(this.aria_valuenow){
18466 cfg['aria-valuenow'] = this.aria_valuenow;
18469 if(this.aria_valuemin){
18470 cfg['aria-valuemin'] = this.aria_valuemin;
18473 if(this.aria_valuemax){
18474 cfg['aria-valuemax'] = this.aria_valuemax;
18477 if(this.label && !this.sr_only){
18478 cfg.html = this.label;
18482 cfg.cls += ' progress-bar-' + this.panel;
18488 update : function(aria_valuenow)
18490 this.aria_valuenow = aria_valuenow;
18492 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18507 * @class Roo.bootstrap.TabGroup
18508 * @extends Roo.bootstrap.Column
18509 * Bootstrap Column class
18510 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18511 * @cfg {Boolean} carousel true to make the group behave like a carousel
18512 * @cfg {Boolean} bullets show bullets for the panels
18513 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18514 * @cfg {Number} timer auto slide timer .. default 0 millisecond
18515 * @cfg {Boolean} showarrow (true|false) show arrow default true
18518 * Create a new TabGroup
18519 * @param {Object} config The config object
18522 Roo.bootstrap.TabGroup = function(config){
18523 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18525 this.navId = Roo.id();
18528 Roo.bootstrap.TabGroup.register(this);
18532 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18535 transition : false,
18540 slideOnTouch : false,
18543 getAutoCreate : function()
18545 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18547 cfg.cls += ' tab-content';
18549 if (this.carousel) {
18550 cfg.cls += ' carousel slide';
18553 cls : 'carousel-inner',
18557 if(this.bullets && !Roo.isTouch){
18560 cls : 'carousel-bullets',
18564 if(this.bullets_cls){
18565 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18572 cfg.cn[0].cn.push(bullets);
18575 if(this.showarrow){
18576 cfg.cn[0].cn.push({
18578 class : 'carousel-arrow',
18582 class : 'carousel-prev',
18586 class : 'fa fa-chevron-left'
18592 class : 'carousel-next',
18596 class : 'fa fa-chevron-right'
18609 initEvents: function()
18611 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18612 // this.el.on("touchstart", this.onTouchStart, this);
18615 if(this.autoslide){
18618 this.slideFn = window.setInterval(function() {
18619 _this.showPanelNext();
18623 if(this.showarrow){
18624 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18625 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18631 // onTouchStart : function(e, el, o)
18633 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18637 // this.showPanelNext();
18641 getChildContainer : function()
18643 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18647 * register a Navigation item
18648 * @param {Roo.bootstrap.NavItem} the navitem to add
18650 register : function(item)
18652 this.tabs.push( item);
18653 item.navId = this.navId; // not really needed..
18658 getActivePanel : function()
18661 Roo.each(this.tabs, function(t) {
18671 getPanelByName : function(n)
18674 Roo.each(this.tabs, function(t) {
18675 if (t.tabId == n) {
18683 indexOfPanel : function(p)
18686 Roo.each(this.tabs, function(t,i) {
18687 if (t.tabId == p.tabId) {
18696 * show a specific panel
18697 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18698 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18700 showPanel : function (pan)
18702 if(this.transition || typeof(pan) == 'undefined'){
18703 Roo.log("waiting for the transitionend");
18707 if (typeof(pan) == 'number') {
18708 pan = this.tabs[pan];
18711 if (typeof(pan) == 'string') {
18712 pan = this.getPanelByName(pan);
18715 var cur = this.getActivePanel();
18718 Roo.log('pan or acitve pan is undefined');
18722 if (pan.tabId == this.getActivePanel().tabId) {
18726 if (false === cur.fireEvent('beforedeactivate')) {
18730 if(this.bullets > 0 && !Roo.isTouch){
18731 this.setActiveBullet(this.indexOfPanel(pan));
18734 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18736 //class="carousel-item carousel-item-next carousel-item-left"
18738 this.transition = true;
18739 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18740 var lr = dir == 'next' ? 'left' : 'right';
18741 pan.el.addClass(dir); // or prev
18742 pan.el.addClass('carousel-item-' + dir); // or prev
18743 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18744 cur.el.addClass(lr); // or right
18745 pan.el.addClass(lr);
18746 cur.el.addClass('carousel-item-' +lr); // or right
18747 pan.el.addClass('carousel-item-' +lr);
18751 cur.el.on('transitionend', function() {
18752 Roo.log("trans end?");
18754 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18755 pan.setActive(true);
18757 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18758 cur.setActive(false);
18760 _this.transition = false;
18762 }, this, { single: true } );
18767 cur.setActive(false);
18768 pan.setActive(true);
18773 showPanelNext : function()
18775 var i = this.indexOfPanel(this.getActivePanel());
18777 if (i >= this.tabs.length - 1 && !this.autoslide) {
18781 if (i >= this.tabs.length - 1 && this.autoslide) {
18785 this.showPanel(this.tabs[i+1]);
18788 showPanelPrev : function()
18790 var i = this.indexOfPanel(this.getActivePanel());
18792 if (i < 1 && !this.autoslide) {
18796 if (i < 1 && this.autoslide) {
18797 i = this.tabs.length;
18800 this.showPanel(this.tabs[i-1]);
18804 addBullet: function()
18806 if(!this.bullets || Roo.isTouch){
18809 var ctr = this.el.select('.carousel-bullets',true).first();
18810 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18811 var bullet = ctr.createChild({
18812 cls : 'bullet bullet-' + i
18813 },ctr.dom.lastChild);
18818 bullet.on('click', (function(e, el, o, ii, t){
18820 e.preventDefault();
18822 this.showPanel(ii);
18824 if(this.autoslide && this.slideFn){
18825 clearInterval(this.slideFn);
18826 this.slideFn = window.setInterval(function() {
18827 _this.showPanelNext();
18831 }).createDelegate(this, [i, bullet], true));
18836 setActiveBullet : function(i)
18842 Roo.each(this.el.select('.bullet', true).elements, function(el){
18843 el.removeClass('selected');
18846 var bullet = this.el.select('.bullet-' + i, true).first();
18852 bullet.addClass('selected');
18863 Roo.apply(Roo.bootstrap.TabGroup, {
18867 * register a Navigation Group
18868 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18870 register : function(navgrp)
18872 this.groups[navgrp.navId] = navgrp;
18876 * fetch a Navigation Group based on the navigation ID
18877 * if one does not exist , it will get created.
18878 * @param {string} the navgroup to add
18879 * @returns {Roo.bootstrap.NavGroup} the navgroup
18881 get: function(navId) {
18882 if (typeof(this.groups[navId]) == 'undefined') {
18883 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18885 return this.groups[navId] ;
18900 * @class Roo.bootstrap.TabPanel
18901 * @extends Roo.bootstrap.Component
18902 * Bootstrap TabPanel class
18903 * @cfg {Boolean} active panel active
18904 * @cfg {String} html panel content
18905 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18906 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18907 * @cfg {String} href click to link..
18911 * Create a new TabPanel
18912 * @param {Object} config The config object
18915 Roo.bootstrap.TabPanel = function(config){
18916 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18920 * Fires when the active status changes
18921 * @param {Roo.bootstrap.TabPanel} this
18922 * @param {Boolean} state the new state
18927 * @event beforedeactivate
18928 * Fires before a tab is de-activated - can be used to do validation on a form.
18929 * @param {Roo.bootstrap.TabPanel} this
18930 * @return {Boolean} false if there is an error
18933 'beforedeactivate': true
18936 this.tabId = this.tabId || Roo.id();
18940 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18948 getAutoCreate : function(){
18953 // item is needed for carousel - not sure if it has any effect otherwise
18954 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18955 html: this.html || ''
18959 cfg.cls += ' active';
18963 cfg.tabId = this.tabId;
18971 initEvents: function()
18973 var p = this.parent();
18975 this.navId = this.navId || p.navId;
18977 if (typeof(this.navId) != 'undefined') {
18978 // not really needed.. but just in case.. parent should be a NavGroup.
18979 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18983 var i = tg.tabs.length - 1;
18985 if(this.active && tg.bullets > 0 && i < tg.bullets){
18986 tg.setActiveBullet(i);
18990 this.el.on('click', this.onClick, this);
18993 this.el.on("touchstart", this.onTouchStart, this);
18994 this.el.on("touchmove", this.onTouchMove, this);
18995 this.el.on("touchend", this.onTouchEnd, this);
19000 onRender : function(ct, position)
19002 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
19005 setActive : function(state)
19007 Roo.log("panel - set active " + this.tabId + "=" + state);
19009 this.active = state;
19011 this.el.removeClass('active');
19013 } else if (!this.el.hasClass('active')) {
19014 this.el.addClass('active');
19017 this.fireEvent('changed', this, state);
19020 onClick : function(e)
19022 e.preventDefault();
19024 if(!this.href.length){
19028 window.location.href = this.href;
19037 onTouchStart : function(e)
19039 this.swiping = false;
19041 this.startX = e.browserEvent.touches[0].clientX;
19042 this.startY = e.browserEvent.touches[0].clientY;
19045 onTouchMove : function(e)
19047 this.swiping = true;
19049 this.endX = e.browserEvent.touches[0].clientX;
19050 this.endY = e.browserEvent.touches[0].clientY;
19053 onTouchEnd : function(e)
19060 var tabGroup = this.parent();
19062 if(this.endX > this.startX){ // swiping right
19063 tabGroup.showPanelPrev();
19067 if(this.startX > this.endX){ // swiping left
19068 tabGroup.showPanelNext();
19087 * @class Roo.bootstrap.DateField
19088 * @extends Roo.bootstrap.Input
19089 * Bootstrap DateField class
19090 * @cfg {Number} weekStart default 0
19091 * @cfg {String} viewMode default empty, (months|years)
19092 * @cfg {String} minViewMode default empty, (months|years)
19093 * @cfg {Number} startDate default -Infinity
19094 * @cfg {Number} endDate default Infinity
19095 * @cfg {Boolean} todayHighlight default false
19096 * @cfg {Boolean} todayBtn default false
19097 * @cfg {Boolean} calendarWeeks default false
19098 * @cfg {Object} daysOfWeekDisabled default empty
19099 * @cfg {Boolean} singleMode default false (true | false)
19101 * @cfg {Boolean} keyboardNavigation default true
19102 * @cfg {String} language default en
19105 * Create a new DateField
19106 * @param {Object} config The config object
19109 Roo.bootstrap.DateField = function(config){
19110 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
19114 * Fires when this field show.
19115 * @param {Roo.bootstrap.DateField} this
19116 * @param {Mixed} date The date value
19121 * Fires when this field hide.
19122 * @param {Roo.bootstrap.DateField} this
19123 * @param {Mixed} date The date value
19128 * Fires when select a date.
19129 * @param {Roo.bootstrap.DateField} this
19130 * @param {Mixed} date The date value
19134 * @event beforeselect
19135 * Fires when before select a date.
19136 * @param {Roo.bootstrap.DateField} this
19137 * @param {Mixed} date The date value
19139 beforeselect : true
19143 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
19146 * @cfg {String} format
19147 * The default date format string which can be overriden for localization support. The format must be
19148 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
19152 * @cfg {String} altFormats
19153 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
19154 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
19156 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
19164 todayHighlight : false,
19170 keyboardNavigation: true,
19172 calendarWeeks: false,
19174 startDate: -Infinity,
19178 daysOfWeekDisabled: [],
19182 singleMode : false,
19184 UTCDate: function()
19186 return new Date(Date.UTC.apply(Date, arguments));
19189 UTCToday: function()
19191 var today = new Date();
19192 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
19195 getDate: function() {
19196 var d = this.getUTCDate();
19197 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
19200 getUTCDate: function() {
19204 setDate: function(d) {
19205 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
19208 setUTCDate: function(d) {
19210 this.setValue(this.formatDate(this.date));
19213 onRender: function(ct, position)
19216 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
19218 this.language = this.language || 'en';
19219 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
19220 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
19222 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
19223 this.format = this.format || 'm/d/y';
19224 this.isInline = false;
19225 this.isInput = true;
19226 this.component = this.el.select('.add-on', true).first() || false;
19227 this.component = (this.component && this.component.length === 0) ? false : this.component;
19228 this.hasInput = this.component && this.inputEl().length;
19230 if (typeof(this.minViewMode === 'string')) {
19231 switch (this.minViewMode) {
19233 this.minViewMode = 1;
19236 this.minViewMode = 2;
19239 this.minViewMode = 0;
19244 if (typeof(this.viewMode === 'string')) {
19245 switch (this.viewMode) {
19258 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
19260 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
19262 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19264 this.picker().on('mousedown', this.onMousedown, this);
19265 this.picker().on('click', this.onClick, this);
19267 this.picker().addClass('datepicker-dropdown');
19269 this.startViewMode = this.viewMode;
19271 if(this.singleMode){
19272 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19273 v.setVisibilityMode(Roo.Element.DISPLAY);
19277 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19278 v.setStyle('width', '189px');
19282 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19283 if(!this.calendarWeeks){
19288 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19289 v.attr('colspan', function(i, val){
19290 return parseInt(val) + 1;
19295 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19297 this.setStartDate(this.startDate);
19298 this.setEndDate(this.endDate);
19300 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19307 if(this.isInline) {
19312 picker : function()
19314 return this.pickerEl;
19315 // return this.el.select('.datepicker', true).first();
19318 fillDow: function()
19320 var dowCnt = this.weekStart;
19329 if(this.calendarWeeks){
19337 while (dowCnt < this.weekStart + 7) {
19341 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19345 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19348 fillMonths: function()
19351 var months = this.picker().select('>.datepicker-months td', true).first();
19353 months.dom.innerHTML = '';
19359 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19362 months.createChild(month);
19369 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;
19371 if (this.date < this.startDate) {
19372 this.viewDate = new Date(this.startDate);
19373 } else if (this.date > this.endDate) {
19374 this.viewDate = new Date(this.endDate);
19376 this.viewDate = new Date(this.date);
19384 var d = new Date(this.viewDate),
19385 year = d.getUTCFullYear(),
19386 month = d.getUTCMonth(),
19387 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19388 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19389 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19390 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19391 currentDate = this.date && this.date.valueOf(),
19392 today = this.UTCToday();
19394 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19396 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19398 // this.picker.select('>tfoot th.today').
19399 // .text(dates[this.language].today)
19400 // .toggle(this.todayBtn !== false);
19402 this.updateNavArrows();
19405 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19407 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19409 prevMonth.setUTCDate(day);
19411 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19413 var nextMonth = new Date(prevMonth);
19415 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19417 nextMonth = nextMonth.valueOf();
19419 var fillMonths = false;
19421 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19423 while(prevMonth.valueOf() <= nextMonth) {
19426 if (prevMonth.getUTCDay() === this.weekStart) {
19428 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19436 if(this.calendarWeeks){
19437 // ISO 8601: First week contains first thursday.
19438 // ISO also states week starts on Monday, but we can be more abstract here.
19440 // Start of current week: based on weekstart/current date
19441 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19442 // Thursday of this week
19443 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19444 // First Thursday of year, year from thursday
19445 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19446 // Calendar week: ms between thursdays, div ms per day, div 7 days
19447 calWeek = (th - yth) / 864e5 / 7 + 1;
19449 fillMonths.cn.push({
19457 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19459 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19462 if (this.todayHighlight &&
19463 prevMonth.getUTCFullYear() == today.getFullYear() &&
19464 prevMonth.getUTCMonth() == today.getMonth() &&
19465 prevMonth.getUTCDate() == today.getDate()) {
19466 clsName += ' today';
19469 if (currentDate && prevMonth.valueOf() === currentDate) {
19470 clsName += ' active';
19473 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19474 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19475 clsName += ' disabled';
19478 fillMonths.cn.push({
19480 cls: 'day ' + clsName,
19481 html: prevMonth.getDate()
19484 prevMonth.setDate(prevMonth.getDate()+1);
19487 var currentYear = this.date && this.date.getUTCFullYear();
19488 var currentMonth = this.date && this.date.getUTCMonth();
19490 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19492 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19493 v.removeClass('active');
19495 if(currentYear === year && k === currentMonth){
19496 v.addClass('active');
19499 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19500 v.addClass('disabled');
19506 year = parseInt(year/10, 10) * 10;
19508 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19510 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19513 for (var i = -1; i < 11; i++) {
19514 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19516 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19524 showMode: function(dir)
19527 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19530 Roo.each(this.picker().select('>div',true).elements, function(v){
19531 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19534 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19539 if(this.isInline) {
19543 this.picker().removeClass(['bottom', 'top']);
19545 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19547 * place to the top of element!
19551 this.picker().addClass('top');
19552 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19557 this.picker().addClass('bottom');
19559 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19562 parseDate : function(value)
19564 if(!value || value instanceof Date){
19567 var v = Date.parseDate(value, this.format);
19568 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19569 v = Date.parseDate(value, 'Y-m-d');
19571 if(!v && this.altFormats){
19572 if(!this.altFormatsArray){
19573 this.altFormatsArray = this.altFormats.split("|");
19575 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19576 v = Date.parseDate(value, this.altFormatsArray[i]);
19582 formatDate : function(date, fmt)
19584 return (!date || !(date instanceof Date)) ?
19585 date : date.dateFormat(fmt || this.format);
19588 onFocus : function()
19590 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19594 onBlur : function()
19596 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19598 var d = this.inputEl().getValue();
19605 showPopup : function()
19607 this.picker().show();
19611 this.fireEvent('showpopup', this, this.date);
19614 hidePopup : function()
19616 if(this.isInline) {
19619 this.picker().hide();
19620 this.viewMode = this.startViewMode;
19623 this.fireEvent('hidepopup', this, this.date);
19627 onMousedown: function(e)
19629 e.stopPropagation();
19630 e.preventDefault();
19635 Roo.bootstrap.DateField.superclass.keyup.call(this);
19639 setValue: function(v)
19641 if(this.fireEvent('beforeselect', this, v) !== false){
19642 var d = new Date(this.parseDate(v) ).clearTime();
19644 if(isNaN(d.getTime())){
19645 this.date = this.viewDate = '';
19646 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19650 v = this.formatDate(d);
19652 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19654 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19658 this.fireEvent('select', this, this.date);
19662 getValue: function()
19664 return this.formatDate(this.date);
19667 fireKey: function(e)
19669 if (!this.picker().isVisible()){
19670 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19676 var dateChanged = false,
19678 newDate, newViewDate;
19683 e.preventDefault();
19687 if (!this.keyboardNavigation) {
19690 dir = e.keyCode == 37 ? -1 : 1;
19693 newDate = this.moveYear(this.date, dir);
19694 newViewDate = this.moveYear(this.viewDate, dir);
19695 } else if (e.shiftKey){
19696 newDate = this.moveMonth(this.date, dir);
19697 newViewDate = this.moveMonth(this.viewDate, dir);
19699 newDate = new Date(this.date);
19700 newDate.setUTCDate(this.date.getUTCDate() + dir);
19701 newViewDate = new Date(this.viewDate);
19702 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19704 if (this.dateWithinRange(newDate)){
19705 this.date = newDate;
19706 this.viewDate = newViewDate;
19707 this.setValue(this.formatDate(this.date));
19709 e.preventDefault();
19710 dateChanged = true;
19715 if (!this.keyboardNavigation) {
19718 dir = e.keyCode == 38 ? -1 : 1;
19720 newDate = this.moveYear(this.date, dir);
19721 newViewDate = this.moveYear(this.viewDate, dir);
19722 } else if (e.shiftKey){
19723 newDate = this.moveMonth(this.date, dir);
19724 newViewDate = this.moveMonth(this.viewDate, dir);
19726 newDate = new Date(this.date);
19727 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19728 newViewDate = new Date(this.viewDate);
19729 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19731 if (this.dateWithinRange(newDate)){
19732 this.date = newDate;
19733 this.viewDate = newViewDate;
19734 this.setValue(this.formatDate(this.date));
19736 e.preventDefault();
19737 dateChanged = true;
19741 this.setValue(this.formatDate(this.date));
19743 e.preventDefault();
19746 this.setValue(this.formatDate(this.date));
19760 onClick: function(e)
19762 e.stopPropagation();
19763 e.preventDefault();
19765 var target = e.getTarget();
19767 if(target.nodeName.toLowerCase() === 'i'){
19768 target = Roo.get(target).dom.parentNode;
19771 var nodeName = target.nodeName;
19772 var className = target.className;
19773 var html = target.innerHTML;
19774 //Roo.log(nodeName);
19776 switch(nodeName.toLowerCase()) {
19778 switch(className) {
19784 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19785 switch(this.viewMode){
19787 this.viewDate = this.moveMonth(this.viewDate, dir);
19791 this.viewDate = this.moveYear(this.viewDate, dir);
19797 var date = new Date();
19798 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19800 this.setValue(this.formatDate(this.date));
19807 if (className.indexOf('disabled') < 0) {
19808 this.viewDate.setUTCDate(1);
19809 if (className.indexOf('month') > -1) {
19810 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19812 var year = parseInt(html, 10) || 0;
19813 this.viewDate.setUTCFullYear(year);
19817 if(this.singleMode){
19818 this.setValue(this.formatDate(this.viewDate));
19829 //Roo.log(className);
19830 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19831 var day = parseInt(html, 10) || 1;
19832 var year = this.viewDate.getUTCFullYear(),
19833 month = this.viewDate.getUTCMonth();
19835 if (className.indexOf('old') > -1) {
19842 } else if (className.indexOf('new') > -1) {
19850 //Roo.log([year,month,day]);
19851 this.date = this.UTCDate(year, month, day,0,0,0,0);
19852 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19854 //Roo.log(this.formatDate(this.date));
19855 this.setValue(this.formatDate(this.date));
19862 setStartDate: function(startDate)
19864 this.startDate = startDate || -Infinity;
19865 if (this.startDate !== -Infinity) {
19866 this.startDate = this.parseDate(this.startDate);
19869 this.updateNavArrows();
19872 setEndDate: function(endDate)
19874 this.endDate = endDate || Infinity;
19875 if (this.endDate !== Infinity) {
19876 this.endDate = this.parseDate(this.endDate);
19879 this.updateNavArrows();
19882 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19884 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19885 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19886 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19888 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19889 return parseInt(d, 10);
19892 this.updateNavArrows();
19895 updateNavArrows: function()
19897 if(this.singleMode){
19901 var d = new Date(this.viewDate),
19902 year = d.getUTCFullYear(),
19903 month = d.getUTCMonth();
19905 Roo.each(this.picker().select('.prev', true).elements, function(v){
19907 switch (this.viewMode) {
19910 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19916 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19923 Roo.each(this.picker().select('.next', true).elements, function(v){
19925 switch (this.viewMode) {
19928 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19934 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19942 moveMonth: function(date, dir)
19947 var new_date = new Date(date.valueOf()),
19948 day = new_date.getUTCDate(),
19949 month = new_date.getUTCMonth(),
19950 mag = Math.abs(dir),
19952 dir = dir > 0 ? 1 : -1;
19955 // If going back one month, make sure month is not current month
19956 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19958 return new_date.getUTCMonth() == month;
19960 // If going forward one month, make sure month is as expected
19961 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19963 return new_date.getUTCMonth() != new_month;
19965 new_month = month + dir;
19966 new_date.setUTCMonth(new_month);
19967 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19968 if (new_month < 0 || new_month > 11) {
19969 new_month = (new_month + 12) % 12;
19972 // For magnitudes >1, move one month at a time...
19973 for (var i=0; i<mag; i++) {
19974 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19975 new_date = this.moveMonth(new_date, dir);
19977 // ...then reset the day, keeping it in the new month
19978 new_month = new_date.getUTCMonth();
19979 new_date.setUTCDate(day);
19981 return new_month != new_date.getUTCMonth();
19984 // Common date-resetting loop -- if date is beyond end of month, make it
19987 new_date.setUTCDate(--day);
19988 new_date.setUTCMonth(new_month);
19993 moveYear: function(date, dir)
19995 return this.moveMonth(date, dir*12);
19998 dateWithinRange: function(date)
20000 return date >= this.startDate && date <= this.endDate;
20006 this.picker().remove();
20009 validateValue : function(value)
20011 if(this.getVisibilityEl().hasClass('hidden')){
20015 if(value.length < 1) {
20016 if(this.allowBlank){
20022 if(value.length < this.minLength){
20025 if(value.length > this.maxLength){
20029 var vt = Roo.form.VTypes;
20030 if(!vt[this.vtype](value, this)){
20034 if(typeof this.validator == "function"){
20035 var msg = this.validator(value);
20041 if(this.regex && !this.regex.test(value)){
20045 if(typeof(this.parseDate(value)) == 'undefined'){
20049 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
20053 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
20063 this.date = this.viewDate = '';
20065 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20070 Roo.apply(Roo.bootstrap.DateField, {
20081 html: '<i class="fa fa-arrow-left"/>'
20091 html: '<i class="fa fa-arrow-right"/>'
20133 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
20134 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
20135 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
20136 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20137 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
20150 navFnc: 'FullYear',
20155 navFnc: 'FullYear',
20160 Roo.apply(Roo.bootstrap.DateField, {
20164 cls: 'datepicker dropdown-menu roo-dynamic',
20168 cls: 'datepicker-days',
20172 cls: 'table-condensed',
20174 Roo.bootstrap.DateField.head,
20178 Roo.bootstrap.DateField.footer
20185 cls: 'datepicker-months',
20189 cls: 'table-condensed',
20191 Roo.bootstrap.DateField.head,
20192 Roo.bootstrap.DateField.content,
20193 Roo.bootstrap.DateField.footer
20200 cls: 'datepicker-years',
20204 cls: 'table-condensed',
20206 Roo.bootstrap.DateField.head,
20207 Roo.bootstrap.DateField.content,
20208 Roo.bootstrap.DateField.footer
20227 * @class Roo.bootstrap.TimeField
20228 * @extends Roo.bootstrap.Input
20229 * Bootstrap DateField class
20233 * Create a new TimeField
20234 * @param {Object} config The config object
20237 Roo.bootstrap.TimeField = function(config){
20238 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
20242 * Fires when this field show.
20243 * @param {Roo.bootstrap.DateField} thisthis
20244 * @param {Mixed} date The date value
20249 * Fires when this field hide.
20250 * @param {Roo.bootstrap.DateField} this
20251 * @param {Mixed} date The date value
20256 * Fires when select a date.
20257 * @param {Roo.bootstrap.DateField} this
20258 * @param {Mixed} date The date value
20264 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
20267 * @cfg {String} format
20268 * The default time format string which can be overriden for localization support. The format must be
20269 * valid according to {@link Date#parseDate} (defaults to 'H:i').
20273 onRender: function(ct, position)
20276 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20278 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20280 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20282 this.pop = this.picker().select('>.datepicker-time',true).first();
20283 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20285 this.picker().on('mousedown', this.onMousedown, this);
20286 this.picker().on('click', this.onClick, this);
20288 this.picker().addClass('datepicker-dropdown');
20293 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20294 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20295 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20296 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20297 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20298 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20302 fireKey: function(e){
20303 if (!this.picker().isVisible()){
20304 if (e.keyCode == 27) { // allow escape to hide and re-show picker
20310 e.preventDefault();
20318 this.onTogglePeriod();
20321 this.onIncrementMinutes();
20324 this.onDecrementMinutes();
20333 onClick: function(e) {
20334 e.stopPropagation();
20335 e.preventDefault();
20338 picker : function()
20340 return this.el.select('.datepicker', true).first();
20343 fillTime: function()
20345 var time = this.pop.select('tbody', true).first();
20347 time.dom.innerHTML = '';
20362 cls: 'hours-up glyphicon glyphicon-chevron-up'
20382 cls: 'minutes-up glyphicon glyphicon-chevron-up'
20403 cls: 'timepicker-hour',
20418 cls: 'timepicker-minute',
20433 cls: 'btn btn-primary period',
20455 cls: 'hours-down glyphicon glyphicon-chevron-down'
20475 cls: 'minutes-down glyphicon glyphicon-chevron-down'
20493 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20500 var hours = this.time.getHours();
20501 var minutes = this.time.getMinutes();
20514 hours = hours - 12;
20518 hours = '0' + hours;
20522 minutes = '0' + minutes;
20525 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20526 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20527 this.pop.select('button', true).first().dom.innerHTML = period;
20533 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20535 var cls = ['bottom'];
20537 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20544 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20549 this.picker().addClass(cls.join('-'));
20553 Roo.each(cls, function(c){
20555 _this.picker().setTop(_this.inputEl().getHeight());
20559 _this.picker().setTop(0 - _this.picker().getHeight());
20564 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20568 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20575 onFocus : function()
20577 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20581 onBlur : function()
20583 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20589 this.picker().show();
20594 this.fireEvent('show', this, this.date);
20599 this.picker().hide();
20602 this.fireEvent('hide', this, this.date);
20605 setTime : function()
20608 this.setValue(this.time.format(this.format));
20610 this.fireEvent('select', this, this.date);
20615 onMousedown: function(e){
20616 e.stopPropagation();
20617 e.preventDefault();
20620 onIncrementHours: function()
20622 Roo.log('onIncrementHours');
20623 this.time = this.time.add(Date.HOUR, 1);
20628 onDecrementHours: function()
20630 Roo.log('onDecrementHours');
20631 this.time = this.time.add(Date.HOUR, -1);
20635 onIncrementMinutes: function()
20637 Roo.log('onIncrementMinutes');
20638 this.time = this.time.add(Date.MINUTE, 1);
20642 onDecrementMinutes: function()
20644 Roo.log('onDecrementMinutes');
20645 this.time = this.time.add(Date.MINUTE, -1);
20649 onTogglePeriod: function()
20651 Roo.log('onTogglePeriod');
20652 this.time = this.time.add(Date.HOUR, 12);
20659 Roo.apply(Roo.bootstrap.TimeField, {
20689 cls: 'btn btn-info ok',
20701 Roo.apply(Roo.bootstrap.TimeField, {
20705 cls: 'datepicker dropdown-menu',
20709 cls: 'datepicker-time',
20713 cls: 'table-condensed',
20715 Roo.bootstrap.TimeField.content,
20716 Roo.bootstrap.TimeField.footer
20735 * @class Roo.bootstrap.MonthField
20736 * @extends Roo.bootstrap.Input
20737 * Bootstrap MonthField class
20739 * @cfg {String} language default en
20742 * Create a new MonthField
20743 * @param {Object} config The config object
20746 Roo.bootstrap.MonthField = function(config){
20747 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20752 * Fires when this field show.
20753 * @param {Roo.bootstrap.MonthField} this
20754 * @param {Mixed} date The date value
20759 * Fires when this field hide.
20760 * @param {Roo.bootstrap.MonthField} this
20761 * @param {Mixed} date The date value
20766 * Fires when select a date.
20767 * @param {Roo.bootstrap.MonthField} this
20768 * @param {String} oldvalue The old value
20769 * @param {String} newvalue The new value
20775 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20777 onRender: function(ct, position)
20780 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20782 this.language = this.language || 'en';
20783 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20784 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20786 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20787 this.isInline = false;
20788 this.isInput = true;
20789 this.component = this.el.select('.add-on', true).first() || false;
20790 this.component = (this.component && this.component.length === 0) ? false : this.component;
20791 this.hasInput = this.component && this.inputEL().length;
20793 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20795 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20797 this.picker().on('mousedown', this.onMousedown, this);
20798 this.picker().on('click', this.onClick, this);
20800 this.picker().addClass('datepicker-dropdown');
20802 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20803 v.setStyle('width', '189px');
20810 if(this.isInline) {
20816 setValue: function(v, suppressEvent)
20818 var o = this.getValue();
20820 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20824 if(suppressEvent !== true){
20825 this.fireEvent('select', this, o, v);
20830 getValue: function()
20835 onClick: function(e)
20837 e.stopPropagation();
20838 e.preventDefault();
20840 var target = e.getTarget();
20842 if(target.nodeName.toLowerCase() === 'i'){
20843 target = Roo.get(target).dom.parentNode;
20846 var nodeName = target.nodeName;
20847 var className = target.className;
20848 var html = target.innerHTML;
20850 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20854 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20856 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20862 picker : function()
20864 return this.pickerEl;
20867 fillMonths: function()
20870 var months = this.picker().select('>.datepicker-months td', true).first();
20872 months.dom.innerHTML = '';
20878 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20881 months.createChild(month);
20890 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20891 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20894 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20895 e.removeClass('active');
20897 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20898 e.addClass('active');
20905 if(this.isInline) {
20909 this.picker().removeClass(['bottom', 'top']);
20911 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20913 * place to the top of element!
20917 this.picker().addClass('top');
20918 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20923 this.picker().addClass('bottom');
20925 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20928 onFocus : function()
20930 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20934 onBlur : function()
20936 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20938 var d = this.inputEl().getValue();
20947 this.picker().show();
20948 this.picker().select('>.datepicker-months', true).first().show();
20952 this.fireEvent('show', this, this.date);
20957 if(this.isInline) {
20960 this.picker().hide();
20961 this.fireEvent('hide', this, this.date);
20965 onMousedown: function(e)
20967 e.stopPropagation();
20968 e.preventDefault();
20973 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20977 fireKey: function(e)
20979 if (!this.picker().isVisible()){
20980 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20991 e.preventDefault();
20995 dir = e.keyCode == 37 ? -1 : 1;
20997 this.vIndex = this.vIndex + dir;
20999 if(this.vIndex < 0){
21003 if(this.vIndex > 11){
21007 if(isNaN(this.vIndex)){
21011 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21017 dir = e.keyCode == 38 ? -1 : 1;
21019 this.vIndex = this.vIndex + dir * 4;
21021 if(this.vIndex < 0){
21025 if(this.vIndex > 11){
21029 if(isNaN(this.vIndex)){
21033 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21038 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21039 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21043 e.preventDefault();
21046 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21047 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21063 this.picker().remove();
21068 Roo.apply(Roo.bootstrap.MonthField, {
21087 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21088 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
21093 Roo.apply(Roo.bootstrap.MonthField, {
21097 cls: 'datepicker dropdown-menu roo-dynamic',
21101 cls: 'datepicker-months',
21105 cls: 'table-condensed',
21107 Roo.bootstrap.DateField.content
21127 * @class Roo.bootstrap.CheckBox
21128 * @extends Roo.bootstrap.Input
21129 * Bootstrap CheckBox class
21131 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
21132 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
21133 * @cfg {String} boxLabel The text that appears beside the checkbox
21134 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
21135 * @cfg {Boolean} checked initnal the element
21136 * @cfg {Boolean} inline inline the element (default false)
21137 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
21138 * @cfg {String} tooltip label tooltip
21141 * Create a new CheckBox
21142 * @param {Object} config The config object
21145 Roo.bootstrap.CheckBox = function(config){
21146 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
21151 * Fires when the element is checked or unchecked.
21152 * @param {Roo.bootstrap.CheckBox} this This input
21153 * @param {Boolean} checked The new checked value
21158 * Fires when the element is click.
21159 * @param {Roo.bootstrap.CheckBox} this This input
21166 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
21168 inputType: 'checkbox',
21177 // checkbox success does not make any sense really..
21182 getAutoCreate : function()
21184 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
21190 cfg.cls = 'form-group ' + this.inputType; //input-group
21193 cfg.cls += ' ' + this.inputType + '-inline';
21199 type : this.inputType,
21200 value : this.inputValue,
21201 cls : 'roo-' + this.inputType, //'form-box',
21202 placeholder : this.placeholder || ''
21206 if(this.inputType != 'radio'){
21210 cls : 'roo-hidden-value',
21211 value : this.checked ? this.inputValue : this.valueOff
21216 if (this.weight) { // Validity check?
21217 cfg.cls += " " + this.inputType + "-" + this.weight;
21220 if (this.disabled) {
21221 input.disabled=true;
21225 input.checked = this.checked;
21230 input.name = this.name;
21232 if(this.inputType != 'radio'){
21233 hidden.name = this.name;
21234 input.name = '_hidden_' + this.name;
21239 input.cls += ' input-' + this.size;
21244 ['xs','sm','md','lg'].map(function(size){
21245 if (settings[size]) {
21246 cfg.cls += ' col-' + size + '-' + settings[size];
21250 var inputblock = input;
21252 if (this.before || this.after) {
21255 cls : 'input-group',
21260 inputblock.cn.push({
21262 cls : 'input-group-addon',
21267 inputblock.cn.push(input);
21269 if(this.inputType != 'radio'){
21270 inputblock.cn.push(hidden);
21274 inputblock.cn.push({
21276 cls : 'input-group-addon',
21282 var boxLabelCfg = false;
21288 //'for': id, // box label is handled by onclick - so no for...
21290 html: this.boxLabel
21293 boxLabelCfg.tooltip = this.tooltip;
21299 if (align ==='left' && this.fieldLabel.length) {
21300 // Roo.log("left and has label");
21305 cls : 'control-label',
21306 html : this.fieldLabel
21317 cfg.cn[1].cn.push(boxLabelCfg);
21320 if(this.labelWidth > 12){
21321 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21324 if(this.labelWidth < 13 && this.labelmd == 0){
21325 this.labelmd = this.labelWidth;
21328 if(this.labellg > 0){
21329 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21330 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21333 if(this.labelmd > 0){
21334 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21335 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21338 if(this.labelsm > 0){
21339 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21340 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21343 if(this.labelxs > 0){
21344 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21345 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21348 } else if ( this.fieldLabel.length) {
21349 // Roo.log(" label");
21353 tag: this.boxLabel ? 'span' : 'label',
21355 cls: 'control-label box-input-label',
21356 //cls : 'input-group-addon',
21357 html : this.fieldLabel
21364 cfg.cn.push(boxLabelCfg);
21369 // Roo.log(" no label && no align");
21370 cfg.cn = [ inputblock ] ;
21372 cfg.cn.push(boxLabelCfg);
21380 if(this.inputType != 'radio'){
21381 cfg.cn.push(hidden);
21389 * return the real input element.
21391 inputEl: function ()
21393 return this.el.select('input.roo-' + this.inputType,true).first();
21395 hiddenEl: function ()
21397 return this.el.select('input.roo-hidden-value',true).first();
21400 labelEl: function()
21402 return this.el.select('label.control-label',true).first();
21404 /* depricated... */
21408 return this.labelEl();
21411 boxLabelEl: function()
21413 return this.el.select('label.box-label',true).first();
21416 initEvents : function()
21418 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21420 this.inputEl().on('click', this.onClick, this);
21422 if (this.boxLabel) {
21423 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
21426 this.startValue = this.getValue();
21429 Roo.bootstrap.CheckBox.register(this);
21433 onClick : function(e)
21435 if(this.fireEvent('click', this, e) !== false){
21436 this.setChecked(!this.checked);
21441 setChecked : function(state,suppressEvent)
21443 this.startValue = this.getValue();
21445 if(this.inputType == 'radio'){
21447 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21448 e.dom.checked = false;
21451 this.inputEl().dom.checked = true;
21453 this.inputEl().dom.value = this.inputValue;
21455 if(suppressEvent !== true){
21456 this.fireEvent('check', this, true);
21464 this.checked = state;
21466 this.inputEl().dom.checked = state;
21469 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21471 if(suppressEvent !== true){
21472 this.fireEvent('check', this, state);
21478 getValue : function()
21480 if(this.inputType == 'radio'){
21481 return this.getGroupValue();
21484 return this.hiddenEl().dom.value;
21488 getGroupValue : function()
21490 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21494 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21497 setValue : function(v,suppressEvent)
21499 if(this.inputType == 'radio'){
21500 this.setGroupValue(v, suppressEvent);
21504 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21509 setGroupValue : function(v, suppressEvent)
21511 this.startValue = this.getValue();
21513 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21514 e.dom.checked = false;
21516 if(e.dom.value == v){
21517 e.dom.checked = true;
21521 if(suppressEvent !== true){
21522 this.fireEvent('check', this, true);
21530 validate : function()
21532 if(this.getVisibilityEl().hasClass('hidden')){
21538 (this.inputType == 'radio' && this.validateRadio()) ||
21539 (this.inputType == 'checkbox' && this.validateCheckbox())
21545 this.markInvalid();
21549 validateRadio : function()
21551 if(this.getVisibilityEl().hasClass('hidden')){
21555 if(this.allowBlank){
21561 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21562 if(!e.dom.checked){
21574 validateCheckbox : function()
21577 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21578 //return (this.getValue() == this.inputValue) ? true : false;
21581 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21589 for(var i in group){
21590 if(group[i].el.isVisible(true)){
21598 for(var i in group){
21603 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21610 * Mark this field as valid
21612 markValid : function()
21616 this.fireEvent('valid', this);
21618 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21621 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21628 if(this.inputType == 'radio'){
21629 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21630 var fg = e.findParent('.form-group', false, true);
21631 if (Roo.bootstrap.version == 3) {
21632 fg.removeClass([_this.invalidClass, _this.validClass]);
21633 fg.addClass(_this.validClass);
21635 fg.removeClass(['is-valid', 'is-invalid']);
21636 fg.addClass('is-valid');
21644 var fg = this.el.findParent('.form-group', false, true);
21645 if (Roo.bootstrap.version == 3) {
21646 fg.removeClass([this.invalidClass, this.validClass]);
21647 fg.addClass(this.validClass);
21649 fg.removeClass(['is-valid', 'is-invalid']);
21650 fg.addClass('is-valid');
21655 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21661 for(var i in group){
21662 var fg = group[i].el.findParent('.form-group', false, true);
21663 if (Roo.bootstrap.version == 3) {
21664 fg.removeClass([this.invalidClass, this.validClass]);
21665 fg.addClass(this.validClass);
21667 fg.removeClass(['is-valid', 'is-invalid']);
21668 fg.addClass('is-valid');
21674 * Mark this field as invalid
21675 * @param {String} msg The validation message
21677 markInvalid : function(msg)
21679 if(this.allowBlank){
21685 this.fireEvent('invalid', this, msg);
21687 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21690 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21694 label.markInvalid();
21697 if(this.inputType == 'radio'){
21699 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21700 var fg = e.findParent('.form-group', false, true);
21701 if (Roo.bootstrap.version == 3) {
21702 fg.removeClass([_this.invalidClass, _this.validClass]);
21703 fg.addClass(_this.invalidClass);
21705 fg.removeClass(['is-invalid', 'is-valid']);
21706 fg.addClass('is-invalid');
21714 var fg = this.el.findParent('.form-group', false, true);
21715 if (Roo.bootstrap.version == 3) {
21716 fg.removeClass([_this.invalidClass, _this.validClass]);
21717 fg.addClass(_this.invalidClass);
21719 fg.removeClass(['is-invalid', 'is-valid']);
21720 fg.addClass('is-invalid');
21725 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21731 for(var i in group){
21732 var fg = group[i].el.findParent('.form-group', false, true);
21733 if (Roo.bootstrap.version == 3) {
21734 fg.removeClass([_this.invalidClass, _this.validClass]);
21735 fg.addClass(_this.invalidClass);
21737 fg.removeClass(['is-invalid', 'is-valid']);
21738 fg.addClass('is-invalid');
21744 clearInvalid : function()
21746 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21748 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21750 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21752 if (label && label.iconEl) {
21753 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21754 label.iconEl.removeClass(['is-invalid', 'is-valid']);
21758 disable : function()
21760 if(this.inputType != 'radio'){
21761 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21768 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21769 _this.getActionEl().addClass(this.disabledClass);
21770 e.dom.disabled = true;
21774 this.disabled = true;
21775 this.fireEvent("disable", this);
21779 enable : function()
21781 if(this.inputType != 'radio'){
21782 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21789 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21790 _this.getActionEl().removeClass(this.disabledClass);
21791 e.dom.disabled = false;
21795 this.disabled = false;
21796 this.fireEvent("enable", this);
21800 setBoxLabel : function(v)
21805 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21811 Roo.apply(Roo.bootstrap.CheckBox, {
21816 * register a CheckBox Group
21817 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21819 register : function(checkbox)
21821 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21822 this.groups[checkbox.groupId] = {};
21825 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21829 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21833 * fetch a CheckBox Group based on the group ID
21834 * @param {string} the group ID
21835 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21837 get: function(groupId) {
21838 if (typeof(this.groups[groupId]) == 'undefined') {
21842 return this.groups[groupId] ;
21855 * @class Roo.bootstrap.Radio
21856 * @extends Roo.bootstrap.Component
21857 * Bootstrap Radio class
21858 * @cfg {String} boxLabel - the label associated
21859 * @cfg {String} value - the value of radio
21862 * Create a new Radio
21863 * @param {Object} config The config object
21865 Roo.bootstrap.Radio = function(config){
21866 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21870 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21876 getAutoCreate : function()
21880 cls : 'form-group radio',
21885 html : this.boxLabel
21893 initEvents : function()
21895 this.parent().register(this);
21897 this.el.on('click', this.onClick, this);
21901 onClick : function(e)
21903 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21904 this.setChecked(true);
21908 setChecked : function(state, suppressEvent)
21910 this.parent().setValue(this.value, suppressEvent);
21914 setBoxLabel : function(v)
21919 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21934 * @class Roo.bootstrap.SecurePass
21935 * @extends Roo.bootstrap.Input
21936 * Bootstrap SecurePass class
21940 * Create a new SecurePass
21941 * @param {Object} config The config object
21944 Roo.bootstrap.SecurePass = function (config) {
21945 // these go here, so the translation tool can replace them..
21947 PwdEmpty: "Please type a password, and then retype it to confirm.",
21948 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21949 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21950 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21951 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21952 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21953 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21954 TooWeak: "Your password is Too Weak."
21956 this.meterLabel = "Password strength:";
21957 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21958 this.meterClass = [
21959 "roo-password-meter-tooweak",
21960 "roo-password-meter-weak",
21961 "roo-password-meter-medium",
21962 "roo-password-meter-strong",
21963 "roo-password-meter-grey"
21968 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21971 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21973 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21975 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21976 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21977 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21978 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21979 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21980 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21981 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21991 * @cfg {String/Object} Label for the strength meter (defaults to
21992 * 'Password strength:')
21997 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21998 * ['Weak', 'Medium', 'Strong'])
22001 pwdStrengths: false,
22014 initEvents: function ()
22016 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
22018 if (this.el.is('input[type=password]') && Roo.isSafari) {
22019 this.el.on('keydown', this.SafariOnKeyDown, this);
22022 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
22025 onRender: function (ct, position)
22027 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
22028 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
22029 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
22031 this.trigger.createChild({
22036 cls: 'roo-password-meter-grey col-xs-12',
22039 //width: this.meterWidth + 'px'
22043 cls: 'roo-password-meter-text'
22049 if (this.hideTrigger) {
22050 this.trigger.setDisplayed(false);
22052 this.setSize(this.width || '', this.height || '');
22055 onDestroy: function ()
22057 if (this.trigger) {
22058 this.trigger.removeAllListeners();
22059 this.trigger.remove();
22062 this.wrap.remove();
22064 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
22067 checkStrength: function ()
22069 var pwd = this.inputEl().getValue();
22070 if (pwd == this._lastPwd) {
22075 if (this.ClientSideStrongPassword(pwd)) {
22077 } else if (this.ClientSideMediumPassword(pwd)) {
22079 } else if (this.ClientSideWeakPassword(pwd)) {
22085 Roo.log('strength1: ' + strength);
22087 //var pm = this.trigger.child('div/div/div').dom;
22088 var pm = this.trigger.child('div/div');
22089 pm.removeClass(this.meterClass);
22090 pm.addClass(this.meterClass[strength]);
22093 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22095 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
22097 this._lastPwd = pwd;
22101 Roo.bootstrap.SecurePass.superclass.reset.call(this);
22103 this._lastPwd = '';
22105 var pm = this.trigger.child('div/div');
22106 pm.removeClass(this.meterClass);
22107 pm.addClass('roo-password-meter-grey');
22110 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22113 this.inputEl().dom.type='password';
22116 validateValue: function (value)
22119 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
22122 if (value.length == 0) {
22123 if (this.allowBlank) {
22124 this.clearInvalid();
22128 this.markInvalid(this.errors.PwdEmpty);
22129 this.errorMsg = this.errors.PwdEmpty;
22137 if ('[\x21-\x7e]*'.match(value)) {
22138 this.markInvalid(this.errors.PwdBadChar);
22139 this.errorMsg = this.errors.PwdBadChar;
22142 if (value.length < 6) {
22143 this.markInvalid(this.errors.PwdShort);
22144 this.errorMsg = this.errors.PwdShort;
22147 if (value.length > 16) {
22148 this.markInvalid(this.errors.PwdLong);
22149 this.errorMsg = this.errors.PwdLong;
22153 if (this.ClientSideStrongPassword(value)) {
22155 } else if (this.ClientSideMediumPassword(value)) {
22157 } else if (this.ClientSideWeakPassword(value)) {
22164 if (strength < 2) {
22165 //this.markInvalid(this.errors.TooWeak);
22166 this.errorMsg = this.errors.TooWeak;
22171 console.log('strength2: ' + strength);
22173 //var pm = this.trigger.child('div/div/div').dom;
22175 var pm = this.trigger.child('div/div');
22176 pm.removeClass(this.meterClass);
22177 pm.addClass(this.meterClass[strength]);
22179 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
22181 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
22183 this.errorMsg = '';
22187 CharacterSetChecks: function (type)
22190 this.fResult = false;
22193 isctype: function (character, type)
22196 case this.kCapitalLetter:
22197 if (character >= 'A' && character <= 'Z') {
22202 case this.kSmallLetter:
22203 if (character >= 'a' && character <= 'z') {
22209 if (character >= '0' && character <= '9') {
22214 case this.kPunctuation:
22215 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
22226 IsLongEnough: function (pwd, size)
22228 return !(pwd == null || isNaN(size) || pwd.length < size);
22231 SpansEnoughCharacterSets: function (word, nb)
22233 if (!this.IsLongEnough(word, nb))
22238 var characterSetChecks = new Array(
22239 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
22240 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
22243 for (var index = 0; index < word.length; ++index) {
22244 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22245 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
22246 characterSetChecks[nCharSet].fResult = true;
22253 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22254 if (characterSetChecks[nCharSet].fResult) {
22259 if (nCharSets < nb) {
22265 ClientSideStrongPassword: function (pwd)
22267 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22270 ClientSideMediumPassword: function (pwd)
22272 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22275 ClientSideWeakPassword: function (pwd)
22277 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22280 })//<script type="text/javascript">
22283 * Based Ext JS Library 1.1.1
22284 * Copyright(c) 2006-2007, Ext JS, LLC.
22290 * @class Roo.HtmlEditorCore
22291 * @extends Roo.Component
22292 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22294 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22297 Roo.HtmlEditorCore = function(config){
22300 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22305 * @event initialize
22306 * Fires when the editor is fully initialized (including the iframe)
22307 * @param {Roo.HtmlEditorCore} this
22312 * Fires when the editor is first receives the focus. Any insertion must wait
22313 * until after this event.
22314 * @param {Roo.HtmlEditorCore} this
22318 * @event beforesync
22319 * Fires before the textarea is updated with content from the editor iframe. Return false
22320 * to cancel the sync.
22321 * @param {Roo.HtmlEditorCore} this
22322 * @param {String} html
22326 * @event beforepush
22327 * Fires before the iframe editor is updated with content from the textarea. Return false
22328 * to cancel the push.
22329 * @param {Roo.HtmlEditorCore} this
22330 * @param {String} html
22335 * Fires when the textarea is updated with content from the editor iframe.
22336 * @param {Roo.HtmlEditorCore} this
22337 * @param {String} html
22342 * Fires when the iframe editor is updated with content from the textarea.
22343 * @param {Roo.HtmlEditorCore} this
22344 * @param {String} html
22349 * @event editorevent
22350 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22351 * @param {Roo.HtmlEditorCore} this
22357 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22359 // defaults : white / black...
22360 this.applyBlacklists();
22367 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
22371 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
22377 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22382 * @cfg {Number} height (in pixels)
22386 * @cfg {Number} width (in pixels)
22391 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22394 stylesheets: false,
22399 // private properties
22400 validationEvent : false,
22402 initialized : false,
22404 sourceEditMode : false,
22405 onFocus : Roo.emptyFn,
22407 hideMode:'offsets',
22411 // blacklist + whitelisted elements..
22418 * Protected method that will not generally be called directly. It
22419 * is called when the editor initializes the iframe with HTML contents. Override this method if you
22420 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22422 getDocMarkup : function(){
22426 // inherit styels from page...??
22427 if (this.stylesheets === false) {
22429 Roo.get(document.head).select('style').each(function(node) {
22430 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22433 Roo.get(document.head).select('link').each(function(node) {
22434 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22437 } else if (!this.stylesheets.length) {
22439 st = '<style type="text/css">' +
22440 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22443 st = '<style type="text/css">' +
22448 st += '<style type="text/css">' +
22449 'IMG { cursor: pointer } ' +
22452 var cls = 'roo-htmleditor-body';
22454 if(this.bodyCls.length){
22455 cls += ' ' + this.bodyCls;
22458 return '<html><head>' + st +
22459 //<style type="text/css">' +
22460 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22462 ' </head><body class="' + cls + '"></body></html>';
22466 onRender : function(ct, position)
22469 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22470 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22473 this.el.dom.style.border = '0 none';
22474 this.el.dom.setAttribute('tabIndex', -1);
22475 this.el.addClass('x-hidden hide');
22479 if(Roo.isIE){ // fix IE 1px bogus margin
22480 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22484 this.frameId = Roo.id();
22488 var iframe = this.owner.wrap.createChild({
22490 cls: 'form-control', // bootstrap..
22492 name: this.frameId,
22493 frameBorder : 'no',
22494 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
22499 this.iframe = iframe.dom;
22501 this.assignDocWin();
22503 this.doc.designMode = 'on';
22506 this.doc.write(this.getDocMarkup());
22510 var task = { // must defer to wait for browser to be ready
22512 //console.log("run task?" + this.doc.readyState);
22513 this.assignDocWin();
22514 if(this.doc.body || this.doc.readyState == 'complete'){
22516 this.doc.designMode="on";
22520 Roo.TaskMgr.stop(task);
22521 this.initEditor.defer(10, this);
22528 Roo.TaskMgr.start(task);
22533 onResize : function(w, h)
22535 Roo.log('resize: ' +w + ',' + h );
22536 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22540 if(typeof w == 'number'){
22542 this.iframe.style.width = w + 'px';
22544 if(typeof h == 'number'){
22546 this.iframe.style.height = h + 'px';
22548 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22555 * Toggles the editor between standard and source edit mode.
22556 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22558 toggleSourceEdit : function(sourceEditMode){
22560 this.sourceEditMode = sourceEditMode === true;
22562 if(this.sourceEditMode){
22564 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
22567 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22568 //this.iframe.className = '';
22571 //this.setSize(this.owner.wrap.getSize());
22572 //this.fireEvent('editmodechange', this, this.sourceEditMode);
22579 * Protected method that will not generally be called directly. If you need/want
22580 * custom HTML cleanup, this is the method you should override.
22581 * @param {String} html The HTML to be cleaned
22582 * return {String} The cleaned HTML
22584 cleanHtml : function(html){
22585 html = String(html);
22586 if(html.length > 5){
22587 if(Roo.isSafari){ // strip safari nonsense
22588 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22591 if(html == ' '){
22598 * HTML Editor -> Textarea
22599 * Protected method that will not generally be called directly. Syncs the contents
22600 * of the editor iframe with the textarea.
22602 syncValue : function(){
22603 if(this.initialized){
22604 var bd = (this.doc.body || this.doc.documentElement);
22605 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22606 var html = bd.innerHTML;
22608 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22609 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22611 html = '<div style="'+m[0]+'">' + html + '</div>';
22614 html = this.cleanHtml(html);
22615 // fix up the special chars.. normaly like back quotes in word...
22616 // however we do not want to do this with chinese..
22617 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
22619 var cc = match.charCodeAt();
22621 // Get the character value, handling surrogate pairs
22622 if (match.length == 2) {
22623 // It's a surrogate pair, calculate the Unicode code point
22624 var high = match.charCodeAt(0) - 0xD800;
22625 var low = match.charCodeAt(1) - 0xDC00;
22626 cc = (high * 0x400) + low + 0x10000;
22628 (cc >= 0x4E00 && cc < 0xA000 ) ||
22629 (cc >= 0x3400 && cc < 0x4E00 ) ||
22630 (cc >= 0xf900 && cc < 0xfb00 )
22635 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
22636 return "&#" + cc + ";";
22643 if(this.owner.fireEvent('beforesync', this, html) !== false){
22644 this.el.dom.value = html;
22645 this.owner.fireEvent('sync', this, html);
22651 * Protected method that will not generally be called directly. Pushes the value of the textarea
22652 * into the iframe editor.
22654 pushValue : function(){
22655 if(this.initialized){
22656 var v = this.el.dom.value.trim();
22658 // if(v.length < 1){
22662 if(this.owner.fireEvent('beforepush', this, v) !== false){
22663 var d = (this.doc.body || this.doc.documentElement);
22665 this.cleanUpPaste();
22666 this.el.dom.value = d.innerHTML;
22667 this.owner.fireEvent('push', this, v);
22673 deferFocus : function(){
22674 this.focus.defer(10, this);
22678 focus : function(){
22679 if(this.win && !this.sourceEditMode){
22686 assignDocWin: function()
22688 var iframe = this.iframe;
22691 this.doc = iframe.contentWindow.document;
22692 this.win = iframe.contentWindow;
22694 // if (!Roo.get(this.frameId)) {
22697 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22698 // this.win = Roo.get(this.frameId).dom.contentWindow;
22700 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22704 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22705 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22710 initEditor : function(){
22711 //console.log("INIT EDITOR");
22712 this.assignDocWin();
22716 this.doc.designMode="on";
22718 this.doc.write(this.getDocMarkup());
22721 var dbody = (this.doc.body || this.doc.documentElement);
22722 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22723 // this copies styles from the containing element into thsi one..
22724 // not sure why we need all of this..
22725 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22727 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22728 //ss['background-attachment'] = 'fixed'; // w3c
22729 dbody.bgProperties = 'fixed'; // ie
22730 //Roo.DomHelper.applyStyles(dbody, ss);
22731 Roo.EventManager.on(this.doc, {
22732 //'mousedown': this.onEditorEvent,
22733 'mouseup': this.onEditorEvent,
22734 'dblclick': this.onEditorEvent,
22735 'click': this.onEditorEvent,
22736 'keyup': this.onEditorEvent,
22741 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22743 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22744 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22746 this.initialized = true;
22748 this.owner.fireEvent('initialize', this);
22753 onDestroy : function(){
22759 //for (var i =0; i < this.toolbars.length;i++) {
22760 // // fixme - ask toolbars for heights?
22761 // this.toolbars[i].onDestroy();
22764 //this.wrap.dom.innerHTML = '';
22765 //this.wrap.remove();
22770 onFirstFocus : function(){
22772 this.assignDocWin();
22775 this.activated = true;
22778 if(Roo.isGecko){ // prevent silly gecko errors
22780 var s = this.win.getSelection();
22781 if(!s.focusNode || s.focusNode.nodeType != 3){
22782 var r = s.getRangeAt(0);
22783 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22788 this.execCmd('useCSS', true);
22789 this.execCmd('styleWithCSS', false);
22792 this.owner.fireEvent('activate', this);
22796 adjustFont: function(btn){
22797 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22798 //if(Roo.isSafari){ // safari
22801 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22802 if(Roo.isSafari){ // safari
22803 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22804 v = (v < 10) ? 10 : v;
22805 v = (v > 48) ? 48 : v;
22806 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22811 v = Math.max(1, v+adjust);
22813 this.execCmd('FontSize', v );
22816 onEditorEvent : function(e)
22818 this.owner.fireEvent('editorevent', this, e);
22819 // this.updateToolbar();
22820 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22823 insertTag : function(tg)
22825 // could be a bit smarter... -> wrap the current selected tRoo..
22826 if (tg.toLowerCase() == 'span' ||
22827 tg.toLowerCase() == 'code' ||
22828 tg.toLowerCase() == 'sup' ||
22829 tg.toLowerCase() == 'sub'
22832 range = this.createRange(this.getSelection());
22833 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22834 wrappingNode.appendChild(range.extractContents());
22835 range.insertNode(wrappingNode);
22842 this.execCmd("formatblock", tg);
22846 insertText : function(txt)
22850 var range = this.createRange();
22851 range.deleteContents();
22852 //alert(Sender.getAttribute('label'));
22854 range.insertNode(this.doc.createTextNode(txt));
22860 * Executes a Midas editor command on the editor document and performs necessary focus and
22861 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22862 * @param {String} cmd The Midas command
22863 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22865 relayCmd : function(cmd, value){
22867 this.execCmd(cmd, value);
22868 this.owner.fireEvent('editorevent', this);
22869 //this.updateToolbar();
22870 this.owner.deferFocus();
22874 * Executes a Midas editor command directly on the editor document.
22875 * For visual commands, you should use {@link #relayCmd} instead.
22876 * <b>This should only be called after the editor is initialized.</b>
22877 * @param {String} cmd The Midas command
22878 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22880 execCmd : function(cmd, value){
22881 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22888 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22890 * @param {String} text | dom node..
22892 insertAtCursor : function(text)
22895 if(!this.activated){
22901 var r = this.doc.selection.createRange();
22912 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22916 // from jquery ui (MIT licenced)
22918 var win = this.win;
22920 if (win.getSelection && win.getSelection().getRangeAt) {
22921 range = win.getSelection().getRangeAt(0);
22922 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22923 range.insertNode(node);
22924 } else if (win.document.selection && win.document.selection.createRange) {
22925 // no firefox support
22926 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22927 win.document.selection.createRange().pasteHTML(txt);
22929 // no firefox support
22930 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22931 this.execCmd('InsertHTML', txt);
22940 mozKeyPress : function(e){
22942 var c = e.getCharCode(), cmd;
22945 c = String.fromCharCode(c).toLowerCase();
22959 this.cleanUpPaste.defer(100, this);
22967 e.preventDefault();
22975 fixKeys : function(){ // load time branching for fastest keydown performance
22977 return function(e){
22978 var k = e.getKey(), r;
22981 r = this.doc.selection.createRange();
22984 r.pasteHTML('    ');
22991 r = this.doc.selection.createRange();
22993 var target = r.parentElement();
22994 if(!target || target.tagName.toLowerCase() != 'li'){
22996 r.pasteHTML('<br />');
23002 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23003 this.cleanUpPaste.defer(100, this);
23009 }else if(Roo.isOpera){
23010 return function(e){
23011 var k = e.getKey();
23015 this.execCmd('InsertHTML','    ');
23018 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23019 this.cleanUpPaste.defer(100, this);
23024 }else if(Roo.isSafari){
23025 return function(e){
23026 var k = e.getKey();
23030 this.execCmd('InsertText','\t');
23034 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23035 this.cleanUpPaste.defer(100, this);
23043 getAllAncestors: function()
23045 var p = this.getSelectedNode();
23048 a.push(p); // push blank onto stack..
23049 p = this.getParentElement();
23053 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
23057 a.push(this.doc.body);
23061 lastSelNode : false,
23064 getSelection : function()
23066 this.assignDocWin();
23067 return Roo.isIE ? this.doc.selection : this.win.getSelection();
23070 getSelectedNode: function()
23072 // this may only work on Gecko!!!
23074 // should we cache this!!!!
23079 var range = this.createRange(this.getSelection()).cloneRange();
23082 var parent = range.parentElement();
23084 var testRange = range.duplicate();
23085 testRange.moveToElementText(parent);
23086 if (testRange.inRange(range)) {
23089 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
23092 parent = parent.parentElement;
23097 // is ancestor a text element.
23098 var ac = range.commonAncestorContainer;
23099 if (ac.nodeType == 3) {
23100 ac = ac.parentNode;
23103 var ar = ac.childNodes;
23106 var other_nodes = [];
23107 var has_other_nodes = false;
23108 for (var i=0;i<ar.length;i++) {
23109 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
23112 // fullly contained node.
23114 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
23119 // probably selected..
23120 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
23121 other_nodes.push(ar[i]);
23125 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
23130 has_other_nodes = true;
23132 if (!nodes.length && other_nodes.length) {
23133 nodes= other_nodes;
23135 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
23141 createRange: function(sel)
23143 // this has strange effects when using with
23144 // top toolbar - not sure if it's a great idea.
23145 //this.editor.contentWindow.focus();
23146 if (typeof sel != "undefined") {
23148 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
23150 return this.doc.createRange();
23153 return this.doc.createRange();
23156 getParentElement: function()
23159 this.assignDocWin();
23160 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
23162 var range = this.createRange(sel);
23165 var p = range.commonAncestorContainer;
23166 while (p.nodeType == 3) { // text node
23177 * Range intersection.. the hard stuff...
23181 * [ -- selected range --- ]
23185 * if end is before start or hits it. fail.
23186 * if start is after end or hits it fail.
23188 * if either hits (but other is outside. - then it's not
23194 // @see http://www.thismuchiknow.co.uk/?p=64.
23195 rangeIntersectsNode : function(range, node)
23197 var nodeRange = node.ownerDocument.createRange();
23199 nodeRange.selectNode(node);
23201 nodeRange.selectNodeContents(node);
23204 var rangeStartRange = range.cloneRange();
23205 rangeStartRange.collapse(true);
23207 var rangeEndRange = range.cloneRange();
23208 rangeEndRange.collapse(false);
23210 var nodeStartRange = nodeRange.cloneRange();
23211 nodeStartRange.collapse(true);
23213 var nodeEndRange = nodeRange.cloneRange();
23214 nodeEndRange.collapse(false);
23216 return rangeStartRange.compareBoundaryPoints(
23217 Range.START_TO_START, nodeEndRange) == -1 &&
23218 rangeEndRange.compareBoundaryPoints(
23219 Range.START_TO_START, nodeStartRange) == 1;
23223 rangeCompareNode : function(range, node)
23225 var nodeRange = node.ownerDocument.createRange();
23227 nodeRange.selectNode(node);
23229 nodeRange.selectNodeContents(node);
23233 range.collapse(true);
23235 nodeRange.collapse(true);
23237 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23238 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
23240 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23242 var nodeIsBefore = ss == 1;
23243 var nodeIsAfter = ee == -1;
23245 if (nodeIsBefore && nodeIsAfter) {
23248 if (!nodeIsBefore && nodeIsAfter) {
23249 return 1; //right trailed.
23252 if (nodeIsBefore && !nodeIsAfter) {
23253 return 2; // left trailed.
23259 // private? - in a new class?
23260 cleanUpPaste : function()
23262 // cleans up the whole document..
23263 Roo.log('cleanuppaste');
23265 this.cleanUpChildren(this.doc.body);
23266 var clean = this.cleanWordChars(this.doc.body.innerHTML);
23267 if (clean != this.doc.body.innerHTML) {
23268 this.doc.body.innerHTML = clean;
23273 cleanWordChars : function(input) {// change the chars to hex code
23274 var he = Roo.HtmlEditorCore;
23276 var output = input;
23277 Roo.each(he.swapCodes, function(sw) {
23278 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23280 output = output.replace(swapper, sw[1]);
23287 cleanUpChildren : function (n)
23289 if (!n.childNodes.length) {
23292 for (var i = n.childNodes.length-1; i > -1 ; i--) {
23293 this.cleanUpChild(n.childNodes[i]);
23300 cleanUpChild : function (node)
23303 //console.log(node);
23304 if (node.nodeName == "#text") {
23305 // clean up silly Windows -- stuff?
23308 if (node.nodeName == "#comment") {
23309 node.parentNode.removeChild(node);
23310 // clean up silly Windows -- stuff?
23313 var lcname = node.tagName.toLowerCase();
23314 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23315 // whitelist of tags..
23317 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23319 node.parentNode.removeChild(node);
23324 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23326 // spans with no attributes - just remove them..
23327 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
23328 remove_keep_children = true;
23331 // remove <a name=....> as rendering on yahoo mailer is borked with this.
23332 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23334 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23335 // remove_keep_children = true;
23338 if (remove_keep_children) {
23339 this.cleanUpChildren(node);
23340 // inserts everything just before this node...
23341 while (node.childNodes.length) {
23342 var cn = node.childNodes[0];
23343 node.removeChild(cn);
23344 node.parentNode.insertBefore(cn, node);
23346 node.parentNode.removeChild(node);
23350 if (!node.attributes || !node.attributes.length) {
23355 this.cleanUpChildren(node);
23359 function cleanAttr(n,v)
23362 if (v.match(/^\./) || v.match(/^\//)) {
23365 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23368 if (v.match(/^#/)) {
23371 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23372 node.removeAttribute(n);
23376 var cwhite = this.cwhite;
23377 var cblack = this.cblack;
23379 function cleanStyle(n,v)
23381 if (v.match(/expression/)) { //XSS?? should we even bother..
23382 node.removeAttribute(n);
23386 var parts = v.split(/;/);
23389 Roo.each(parts, function(p) {
23390 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23394 var l = p.split(':').shift().replace(/\s+/g,'');
23395 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23397 if ( cwhite.length && cblack.indexOf(l) > -1) {
23398 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23399 //node.removeAttribute(n);
23403 // only allow 'c whitelisted system attributes'
23404 if ( cwhite.length && cwhite.indexOf(l) < 0) {
23405 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23406 //node.removeAttribute(n);
23416 if (clean.length) {
23417 node.setAttribute(n, clean.join(';'));
23419 node.removeAttribute(n);
23425 for (var i = node.attributes.length-1; i > -1 ; i--) {
23426 var a = node.attributes[i];
23429 if (a.name.toLowerCase().substr(0,2)=='on') {
23430 node.removeAttribute(a.name);
23433 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23434 node.removeAttribute(a.name);
23437 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23438 cleanAttr(a.name,a.value); // fixme..
23441 if (a.name == 'style') {
23442 cleanStyle(a.name,a.value);
23445 /// clean up MS crap..
23446 // tecnically this should be a list of valid class'es..
23449 if (a.name == 'class') {
23450 if (a.value.match(/^Mso/)) {
23451 node.removeAttribute('class');
23454 if (a.value.match(/^body$/)) {
23455 node.removeAttribute('class');
23466 this.cleanUpChildren(node);
23472 * Clean up MS wordisms...
23474 cleanWord : function(node)
23477 this.cleanWord(this.doc.body);
23482 node.nodeName == 'SPAN' &&
23483 !node.hasAttributes() &&
23484 node.childNodes.length == 1 &&
23485 node.firstChild.nodeName == "#text"
23487 var textNode = node.firstChild;
23488 node.removeChild(textNode);
23489 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
23490 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
23492 node.parentNode.insertBefore(textNode, node);
23493 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
23494 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
23496 node.parentNode.removeChild(node);
23499 if (node.nodeName == "#text") {
23500 // clean up silly Windows -- stuff?
23503 if (node.nodeName == "#comment") {
23504 node.parentNode.removeChild(node);
23505 // clean up silly Windows -- stuff?
23509 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23510 node.parentNode.removeChild(node);
23513 //Roo.log(node.tagName);
23514 // remove - but keep children..
23515 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
23516 //Roo.log('-- removed');
23517 while (node.childNodes.length) {
23518 var cn = node.childNodes[0];
23519 node.removeChild(cn);
23520 node.parentNode.insertBefore(cn, node);
23521 // move node to parent - and clean it..
23522 this.cleanWord(cn);
23524 node.parentNode.removeChild(node);
23525 /// no need to iterate chidlren = it's got none..
23526 //this.iterateChildren(node, this.cleanWord);
23530 if (node.className.length) {
23532 var cn = node.className.split(/\W+/);
23534 Roo.each(cn, function(cls) {
23535 if (cls.match(/Mso[a-zA-Z]+/)) {
23540 node.className = cna.length ? cna.join(' ') : '';
23542 node.removeAttribute("class");
23546 if (node.hasAttribute("lang")) {
23547 node.removeAttribute("lang");
23550 if (node.hasAttribute("style")) {
23552 var styles = node.getAttribute("style").split(";");
23554 Roo.each(styles, function(s) {
23555 if (!s.match(/:/)) {
23558 var kv = s.split(":");
23559 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23562 // what ever is left... we allow.
23565 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23566 if (!nstyle.length) {
23567 node.removeAttribute('style');
23570 this.iterateChildren(node, this.cleanWord);
23576 * iterateChildren of a Node, calling fn each time, using this as the scole..
23577 * @param {DomNode} node node to iterate children of.
23578 * @param {Function} fn method of this class to call on each item.
23580 iterateChildren : function(node, fn)
23582 if (!node.childNodes.length) {
23585 for (var i = node.childNodes.length-1; i > -1 ; i--) {
23586 fn.call(this, node.childNodes[i])
23592 * cleanTableWidths.
23594 * Quite often pasting from word etc.. results in tables with column and widths.
23595 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23598 cleanTableWidths : function(node)
23603 this.cleanTableWidths(this.doc.body);
23608 if (node.nodeName == "#text" || node.nodeName == "#comment") {
23611 Roo.log(node.tagName);
23612 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23613 this.iterateChildren(node, this.cleanTableWidths);
23616 if (node.hasAttribute('width')) {
23617 node.removeAttribute('width');
23621 if (node.hasAttribute("style")) {
23624 var styles = node.getAttribute("style").split(";");
23626 Roo.each(styles, function(s) {
23627 if (!s.match(/:/)) {
23630 var kv = s.split(":");
23631 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23634 // what ever is left... we allow.
23637 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23638 if (!nstyle.length) {
23639 node.removeAttribute('style');
23643 this.iterateChildren(node, this.cleanTableWidths);
23651 domToHTML : function(currentElement, depth, nopadtext) {
23653 depth = depth || 0;
23654 nopadtext = nopadtext || false;
23656 if (!currentElement) {
23657 return this.domToHTML(this.doc.body);
23660 //Roo.log(currentElement);
23662 var allText = false;
23663 var nodeName = currentElement.nodeName;
23664 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23666 if (nodeName == '#text') {
23668 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23673 if (nodeName != 'BODY') {
23676 // Prints the node tagName, such as <A>, <IMG>, etc
23679 for(i = 0; i < currentElement.attributes.length;i++) {
23681 var aname = currentElement.attributes.item(i).name;
23682 if (!currentElement.attributes.item(i).value.length) {
23685 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23688 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23697 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23700 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23705 // Traverse the tree
23707 var currentElementChild = currentElement.childNodes.item(i);
23708 var allText = true;
23709 var innerHTML = '';
23711 while (currentElementChild) {
23712 // Formatting code (indent the tree so it looks nice on the screen)
23713 var nopad = nopadtext;
23714 if (lastnode == 'SPAN') {
23718 if (currentElementChild.nodeName == '#text') {
23719 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23720 toadd = nopadtext ? toadd : toadd.trim();
23721 if (!nopad && toadd.length > 80) {
23722 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23724 innerHTML += toadd;
23727 currentElementChild = currentElement.childNodes.item(i);
23733 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23735 // Recursively traverse the tree structure of the child node
23736 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23737 lastnode = currentElementChild.nodeName;
23739 currentElementChild=currentElement.childNodes.item(i);
23745 // The remaining code is mostly for formatting the tree
23746 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23751 ret+= "</"+tagName+">";
23757 applyBlacklists : function()
23759 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23760 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23764 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23765 if (b.indexOf(tag) > -1) {
23768 this.white.push(tag);
23772 Roo.each(w, function(tag) {
23773 if (b.indexOf(tag) > -1) {
23776 if (this.white.indexOf(tag) > -1) {
23779 this.white.push(tag);
23784 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23785 if (w.indexOf(tag) > -1) {
23788 this.black.push(tag);
23792 Roo.each(b, function(tag) {
23793 if (w.indexOf(tag) > -1) {
23796 if (this.black.indexOf(tag) > -1) {
23799 this.black.push(tag);
23804 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23805 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23809 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23810 if (b.indexOf(tag) > -1) {
23813 this.cwhite.push(tag);
23817 Roo.each(w, function(tag) {
23818 if (b.indexOf(tag) > -1) {
23821 if (this.cwhite.indexOf(tag) > -1) {
23824 this.cwhite.push(tag);
23829 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23830 if (w.indexOf(tag) > -1) {
23833 this.cblack.push(tag);
23837 Roo.each(b, function(tag) {
23838 if (w.indexOf(tag) > -1) {
23841 if (this.cblack.indexOf(tag) > -1) {
23844 this.cblack.push(tag);
23849 setStylesheets : function(stylesheets)
23851 if(typeof(stylesheets) == 'string'){
23852 Roo.get(this.iframe.contentDocument.head).createChild({
23854 rel : 'stylesheet',
23863 Roo.each(stylesheets, function(s) {
23868 Roo.get(_this.iframe.contentDocument.head).createChild({
23870 rel : 'stylesheet',
23879 removeStylesheets : function()
23883 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23888 setStyle : function(style)
23890 Roo.get(this.iframe.contentDocument.head).createChild({
23899 // hide stuff that is not compatible
23913 * @event specialkey
23917 * @cfg {String} fieldClass @hide
23920 * @cfg {String} focusClass @hide
23923 * @cfg {String} autoCreate @hide
23926 * @cfg {String} inputType @hide
23929 * @cfg {String} invalidClass @hide
23932 * @cfg {String} invalidText @hide
23935 * @cfg {String} msgFx @hide
23938 * @cfg {String} validateOnBlur @hide
23942 Roo.HtmlEditorCore.white = [
23943 'area', 'br', 'img', 'input', 'hr', 'wbr',
23945 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23946 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23947 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23948 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23949 'table', 'ul', 'xmp',
23951 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23954 'dir', 'menu', 'ol', 'ul', 'dl',
23960 Roo.HtmlEditorCore.black = [
23961 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23963 'base', 'basefont', 'bgsound', 'blink', 'body',
23964 'frame', 'frameset', 'head', 'html', 'ilayer',
23965 'iframe', 'layer', 'link', 'meta', 'object',
23966 'script', 'style' ,'title', 'xml' // clean later..
23968 Roo.HtmlEditorCore.clean = [
23969 'script', 'style', 'title', 'xml'
23971 Roo.HtmlEditorCore.remove = [
23976 Roo.HtmlEditorCore.ablack = [
23980 Roo.HtmlEditorCore.aclean = [
23981 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23985 Roo.HtmlEditorCore.pwhite= [
23986 'http', 'https', 'mailto'
23989 // white listed style attributes.
23990 Roo.HtmlEditorCore.cwhite= [
23991 // 'text-align', /// default is to allow most things..
23997 // black listed style attributes.
23998 Roo.HtmlEditorCore.cblack= [
23999 // 'font-size' -- this can be set by the project
24003 Roo.HtmlEditorCore.swapCodes =[
24022 * @class Roo.bootstrap.HtmlEditor
24023 * @extends Roo.bootstrap.TextArea
24024 * Bootstrap HtmlEditor class
24027 * Create a new HtmlEditor
24028 * @param {Object} config The config object
24031 Roo.bootstrap.HtmlEditor = function(config){
24032 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
24033 if (!this.toolbars) {
24034 this.toolbars = [];
24037 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
24040 * @event initialize
24041 * Fires when the editor is fully initialized (including the iframe)
24042 * @param {HtmlEditor} this
24047 * Fires when the editor is first receives the focus. Any insertion must wait
24048 * until after this event.
24049 * @param {HtmlEditor} this
24053 * @event beforesync
24054 * Fires before the textarea is updated with content from the editor iframe. Return false
24055 * to cancel the sync.
24056 * @param {HtmlEditor} this
24057 * @param {String} html
24061 * @event beforepush
24062 * Fires before the iframe editor is updated with content from the textarea. Return false
24063 * to cancel the push.
24064 * @param {HtmlEditor} this
24065 * @param {String} html
24070 * Fires when the textarea is updated with content from the editor iframe.
24071 * @param {HtmlEditor} this
24072 * @param {String} html
24077 * Fires when the iframe editor is updated with content from the textarea.
24078 * @param {HtmlEditor} this
24079 * @param {String} html
24083 * @event editmodechange
24084 * Fires when the editor switches edit modes
24085 * @param {HtmlEditor} this
24086 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24088 editmodechange: true,
24090 * @event editorevent
24091 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24092 * @param {HtmlEditor} this
24096 * @event firstfocus
24097 * Fires when on first focus - needed by toolbars..
24098 * @param {HtmlEditor} this
24103 * Auto save the htmlEditor value as a file into Events
24104 * @param {HtmlEditor} this
24108 * @event savedpreview
24109 * preview the saved version of htmlEditor
24110 * @param {HtmlEditor} this
24117 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
24121 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24126 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
24131 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24136 * @cfg {Number} height (in pixels)
24140 * @cfg {Number} width (in pixels)
24145 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24148 stylesheets: false,
24153 // private properties
24154 validationEvent : false,
24156 initialized : false,
24159 onFocus : Roo.emptyFn,
24161 hideMode:'offsets',
24163 tbContainer : false,
24167 toolbarContainer :function() {
24168 return this.wrap.select('.x-html-editor-tb',true).first();
24172 * Protected method that will not generally be called directly. It
24173 * is called when the editor creates its toolbar. Override this method if you need to
24174 * add custom toolbar buttons.
24175 * @param {HtmlEditor} editor
24177 createToolbar : function(){
24178 Roo.log('renewing');
24179 Roo.log("create toolbars");
24181 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
24182 this.toolbars[0].render(this.toolbarContainer());
24186 // if (!editor.toolbars || !editor.toolbars.length) {
24187 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
24190 // for (var i =0 ; i < editor.toolbars.length;i++) {
24191 // editor.toolbars[i] = Roo.factory(
24192 // typeof(editor.toolbars[i]) == 'string' ?
24193 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
24194 // Roo.bootstrap.HtmlEditor);
24195 // editor.toolbars[i].init(editor);
24201 onRender : function(ct, position)
24203 // Roo.log("Call onRender: " + this.xtype);
24205 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
24207 this.wrap = this.inputEl().wrap({
24208 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24211 this.editorcore.onRender(ct, position);
24213 if (this.resizable) {
24214 this.resizeEl = new Roo.Resizable(this.wrap, {
24218 minHeight : this.height,
24219 height: this.height,
24220 handles : this.resizable,
24223 resize : function(r, w, h) {
24224 _t.onResize(w,h); // -something
24230 this.createToolbar(this);
24233 if(!this.width && this.resizable){
24234 this.setSize(this.wrap.getSize());
24236 if (this.resizeEl) {
24237 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24238 // should trigger onReize..
24244 onResize : function(w, h)
24246 Roo.log('resize: ' +w + ',' + h );
24247 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
24251 if(this.inputEl() ){
24252 if(typeof w == 'number'){
24253 var aw = w - this.wrap.getFrameWidth('lr');
24254 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
24257 if(typeof h == 'number'){
24258 var tbh = -11; // fixme it needs to tool bar size!
24259 for (var i =0; i < this.toolbars.length;i++) {
24260 // fixme - ask toolbars for heights?
24261 tbh += this.toolbars[i].el.getHeight();
24262 //if (this.toolbars[i].footer) {
24263 // tbh += this.toolbars[i].footer.el.getHeight();
24271 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24272 ah -= 5; // knock a few pixes off for look..
24273 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24277 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24278 this.editorcore.onResize(ew,eh);
24283 * Toggles the editor between standard and source edit mode.
24284 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24286 toggleSourceEdit : function(sourceEditMode)
24288 this.editorcore.toggleSourceEdit(sourceEditMode);
24290 if(this.editorcore.sourceEditMode){
24291 Roo.log('editor - showing textarea');
24294 // Roo.log(this.syncValue());
24296 this.inputEl().removeClass(['hide', 'x-hidden']);
24297 this.inputEl().dom.removeAttribute('tabIndex');
24298 this.inputEl().focus();
24300 Roo.log('editor - hiding textarea');
24302 // Roo.log(this.pushValue());
24305 this.inputEl().addClass(['hide', 'x-hidden']);
24306 this.inputEl().dom.setAttribute('tabIndex', -1);
24307 //this.deferFocus();
24310 if(this.resizable){
24311 this.setSize(this.wrap.getSize());
24314 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24317 // private (for BoxComponent)
24318 adjustSize : Roo.BoxComponent.prototype.adjustSize,
24320 // private (for BoxComponent)
24321 getResizeEl : function(){
24325 // private (for BoxComponent)
24326 getPositionEl : function(){
24331 initEvents : function(){
24332 this.originalValue = this.getValue();
24336 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24339 // markInvalid : Roo.emptyFn,
24341 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24344 // clearInvalid : Roo.emptyFn,
24346 setValue : function(v){
24347 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24348 this.editorcore.pushValue();
24353 deferFocus : function(){
24354 this.focus.defer(10, this);
24358 focus : function(){
24359 this.editorcore.focus();
24365 onDestroy : function(){
24371 for (var i =0; i < this.toolbars.length;i++) {
24372 // fixme - ask toolbars for heights?
24373 this.toolbars[i].onDestroy();
24376 this.wrap.dom.innerHTML = '';
24377 this.wrap.remove();
24382 onFirstFocus : function(){
24383 //Roo.log("onFirstFocus");
24384 this.editorcore.onFirstFocus();
24385 for (var i =0; i < this.toolbars.length;i++) {
24386 this.toolbars[i].onFirstFocus();
24392 syncValue : function()
24394 this.editorcore.syncValue();
24397 pushValue : function()
24399 this.editorcore.pushValue();
24403 // hide stuff that is not compatible
24417 * @event specialkey
24421 * @cfg {String} fieldClass @hide
24424 * @cfg {String} focusClass @hide
24427 * @cfg {String} autoCreate @hide
24430 * @cfg {String} inputType @hide
24434 * @cfg {String} invalidText @hide
24437 * @cfg {String} msgFx @hide
24440 * @cfg {String} validateOnBlur @hide
24449 Roo.namespace('Roo.bootstrap.htmleditor');
24451 * @class Roo.bootstrap.HtmlEditorToolbar1
24457 new Roo.bootstrap.HtmlEditor({
24460 new Roo.bootstrap.HtmlEditorToolbar1({
24461 disable : { fonts: 1 , format: 1, ..., ... , ...],
24467 * @cfg {Object} disable List of elements to disable..
24468 * @cfg {Array} btns List of additional buttons.
24472 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24475 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24478 Roo.apply(this, config);
24480 // default disabled, based on 'good practice'..
24481 this.disable = this.disable || {};
24482 Roo.applyIf(this.disable, {
24485 specialElements : true
24487 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24489 this.editor = config.editor;
24490 this.editorcore = config.editor.editorcore;
24492 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24494 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24495 // dont call parent... till later.
24497 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
24502 editorcore : false,
24507 "h1","h2","h3","h4","h5","h6",
24509 "abbr", "acronym", "address", "cite", "samp", "var",
24513 onRender : function(ct, position)
24515 // Roo.log("Call onRender: " + this.xtype);
24517 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24519 this.el.dom.style.marginBottom = '0';
24521 var editorcore = this.editorcore;
24522 var editor= this.editor;
24525 var btn = function(id,cmd , toggle, handler, html){
24527 var event = toggle ? 'toggle' : 'click';
24532 xns: Roo.bootstrap,
24536 enableToggle:toggle !== false,
24538 pressed : toggle ? false : null,
24541 a.listeners[toggle ? 'toggle' : 'click'] = function() {
24542 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
24548 // var cb_box = function...
24553 xns: Roo.bootstrap,
24558 xns: Roo.bootstrap,
24562 Roo.each(this.formats, function(f) {
24563 style.menu.items.push({
24565 xns: Roo.bootstrap,
24566 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24571 editorcore.insertTag(this.tagname);
24578 children.push(style);
24580 btn('bold',false,true);
24581 btn('italic',false,true);
24582 btn('align-left', 'justifyleft',true);
24583 btn('align-center', 'justifycenter',true);
24584 btn('align-right' , 'justifyright',true);
24585 btn('link', false, false, function(btn) {
24586 //Roo.log("create link?");
24587 var url = prompt(this.createLinkText, this.defaultLinkValue);
24588 if(url && url != 'http:/'+'/'){
24589 this.editorcore.relayCmd('createlink', url);
24592 btn('list','insertunorderedlist',true);
24593 btn('pencil', false,true, function(btn){
24595 this.toggleSourceEdit(btn.pressed);
24598 if (this.editor.btns.length > 0) {
24599 for (var i = 0; i<this.editor.btns.length; i++) {
24600 children.push(this.editor.btns[i]);
24608 xns: Roo.bootstrap,
24613 xns: Roo.bootstrap,
24618 cog.menu.items.push({
24620 xns: Roo.bootstrap,
24621 html : Clean styles,
24626 editorcore.insertTag(this.tagname);
24635 this.xtype = 'NavSimplebar';
24637 for(var i=0;i< children.length;i++) {
24639 this.buttons.add(this.addxtypeChild(children[i]));
24643 editor.on('editorevent', this.updateToolbar, this);
24645 onBtnClick : function(id)
24647 this.editorcore.relayCmd(id);
24648 this.editorcore.focus();
24652 * Protected method that will not generally be called directly. It triggers
24653 * a toolbar update by reading the markup state of the current selection in the editor.
24655 updateToolbar: function(){
24657 if(!this.editorcore.activated){
24658 this.editor.onFirstFocus(); // is this neeed?
24662 var btns = this.buttons;
24663 var doc = this.editorcore.doc;
24664 btns.get('bold').setActive(doc.queryCommandState('bold'));
24665 btns.get('italic').setActive(doc.queryCommandState('italic'));
24666 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24668 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24669 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24670 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24672 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24673 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24676 var ans = this.editorcore.getAllAncestors();
24677 if (this.formatCombo) {
24680 var store = this.formatCombo.store;
24681 this.formatCombo.setValue("");
24682 for (var i =0; i < ans.length;i++) {
24683 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24685 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24693 // hides menus... - so this cant be on a menu...
24694 Roo.bootstrap.MenuMgr.hideAll();
24696 Roo.bootstrap.MenuMgr.hideAll();
24697 //this.editorsyncValue();
24699 onFirstFocus: function() {
24700 this.buttons.each(function(item){
24704 toggleSourceEdit : function(sourceEditMode){
24707 if(sourceEditMode){
24708 Roo.log("disabling buttons");
24709 this.buttons.each( function(item){
24710 if(item.cmd != 'pencil'){
24716 Roo.log("enabling buttons");
24717 if(this.editorcore.initialized){
24718 this.buttons.each( function(item){
24724 Roo.log("calling toggole on editor");
24725 // tell the editor that it's been pressed..
24726 this.editor.toggleSourceEdit(sourceEditMode);
24736 * @class Roo.bootstrap.Table.AbstractSelectionModel
24737 * @extends Roo.util.Observable
24738 * Abstract base class for grid SelectionModels. It provides the interface that should be
24739 * implemented by descendant classes. This class should not be directly instantiated.
24742 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24743 this.locked = false;
24744 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24748 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24749 /** @ignore Called by the grid automatically. Do not call directly. */
24750 init : function(grid){
24756 * Locks the selections.
24759 this.locked = true;
24763 * Unlocks the selections.
24765 unlock : function(){
24766 this.locked = false;
24770 * Returns true if the selections are locked.
24771 * @return {Boolean}
24773 isLocked : function(){
24774 return this.locked;
24778 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24779 * @class Roo.bootstrap.Table.RowSelectionModel
24780 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24781 * It supports multiple selections and keyboard selection/navigation.
24783 * @param {Object} config
24786 Roo.bootstrap.Table.RowSelectionModel = function(config){
24787 Roo.apply(this, config);
24788 this.selections = new Roo.util.MixedCollection(false, function(o){
24793 this.lastActive = false;
24797 * @event selectionchange
24798 * Fires when the selection changes
24799 * @param {SelectionModel} this
24801 "selectionchange" : true,
24803 * @event afterselectionchange
24804 * Fires after the selection changes (eg. by key press or clicking)
24805 * @param {SelectionModel} this
24807 "afterselectionchange" : true,
24809 * @event beforerowselect
24810 * Fires when a row is selected being selected, return false to cancel.
24811 * @param {SelectionModel} this
24812 * @param {Number} rowIndex The selected index
24813 * @param {Boolean} keepExisting False if other selections will be cleared
24815 "beforerowselect" : true,
24818 * Fires when a row is selected.
24819 * @param {SelectionModel} this
24820 * @param {Number} rowIndex The selected index
24821 * @param {Roo.data.Record} r The record
24823 "rowselect" : true,
24825 * @event rowdeselect
24826 * Fires when a row is deselected.
24827 * @param {SelectionModel} this
24828 * @param {Number} rowIndex The selected index
24830 "rowdeselect" : true
24832 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24833 this.locked = false;
24836 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24838 * @cfg {Boolean} singleSelect
24839 * True to allow selection of only one row at a time (defaults to false)
24841 singleSelect : false,
24844 initEvents : function()
24847 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24848 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24849 //}else{ // allow click to work like normal
24850 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24852 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24853 this.grid.on("rowclick", this.handleMouseDown, this);
24855 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24856 "up" : function(e){
24858 this.selectPrevious(e.shiftKey);
24859 }else if(this.last !== false && this.lastActive !== false){
24860 var last = this.last;
24861 this.selectRange(this.last, this.lastActive-1);
24862 this.grid.getView().focusRow(this.lastActive);
24863 if(last !== false){
24867 this.selectFirstRow();
24869 this.fireEvent("afterselectionchange", this);
24871 "down" : function(e){
24873 this.selectNext(e.shiftKey);
24874 }else if(this.last !== false && this.lastActive !== false){
24875 var last = this.last;
24876 this.selectRange(this.last, this.lastActive+1);
24877 this.grid.getView().focusRow(this.lastActive);
24878 if(last !== false){
24882 this.selectFirstRow();
24884 this.fireEvent("afterselectionchange", this);
24888 this.grid.store.on('load', function(){
24889 this.selections.clear();
24892 var view = this.grid.view;
24893 view.on("refresh", this.onRefresh, this);
24894 view.on("rowupdated", this.onRowUpdated, this);
24895 view.on("rowremoved", this.onRemove, this);
24900 onRefresh : function()
24902 var ds = this.grid.store, i, v = this.grid.view;
24903 var s = this.selections;
24904 s.each(function(r){
24905 if((i = ds.indexOfId(r.id)) != -1){
24914 onRemove : function(v, index, r){
24915 this.selections.remove(r);
24919 onRowUpdated : function(v, index, r){
24920 if(this.isSelected(r)){
24921 v.onRowSelect(index);
24927 * @param {Array} records The records to select
24928 * @param {Boolean} keepExisting (optional) True to keep existing selections
24930 selectRecords : function(records, keepExisting)
24933 this.clearSelections();
24935 var ds = this.grid.store;
24936 for(var i = 0, len = records.length; i < len; i++){
24937 this.selectRow(ds.indexOf(records[i]), true);
24942 * Gets the number of selected rows.
24945 getCount : function(){
24946 return this.selections.length;
24950 * Selects the first row in the grid.
24952 selectFirstRow : function(){
24957 * Select the last row.
24958 * @param {Boolean} keepExisting (optional) True to keep existing selections
24960 selectLastRow : function(keepExisting){
24961 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24962 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24966 * Selects the row immediately following the last selected row.
24967 * @param {Boolean} keepExisting (optional) True to keep existing selections
24969 selectNext : function(keepExisting)
24971 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24972 this.selectRow(this.last+1, keepExisting);
24973 this.grid.getView().focusRow(this.last);
24978 * Selects the row that precedes the last selected row.
24979 * @param {Boolean} keepExisting (optional) True to keep existing selections
24981 selectPrevious : function(keepExisting){
24983 this.selectRow(this.last-1, keepExisting);
24984 this.grid.getView().focusRow(this.last);
24989 * Returns the selected records
24990 * @return {Array} Array of selected records
24992 getSelections : function(){
24993 return [].concat(this.selections.items);
24997 * Returns the first selected record.
25000 getSelected : function(){
25001 return this.selections.itemAt(0);
25006 * Clears all selections.
25008 clearSelections : function(fast)
25014 var ds = this.grid.store;
25015 var s = this.selections;
25016 s.each(function(r){
25017 this.deselectRow(ds.indexOfId(r.id));
25021 this.selections.clear();
25028 * Selects all rows.
25030 selectAll : function(){
25034 this.selections.clear();
25035 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
25036 this.selectRow(i, true);
25041 * Returns True if there is a selection.
25042 * @return {Boolean}
25044 hasSelection : function(){
25045 return this.selections.length > 0;
25049 * Returns True if the specified row is selected.
25050 * @param {Number/Record} record The record or index of the record to check
25051 * @return {Boolean}
25053 isSelected : function(index){
25054 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
25055 return (r && this.selections.key(r.id) ? true : false);
25059 * Returns True if the specified record id is selected.
25060 * @param {String} id The id of record to check
25061 * @return {Boolean}
25063 isIdSelected : function(id){
25064 return (this.selections.key(id) ? true : false);
25069 handleMouseDBClick : function(e, t){
25073 handleMouseDown : function(e, t)
25075 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
25076 if(this.isLocked() || rowIndex < 0 ){
25079 if(e.shiftKey && this.last !== false){
25080 var last = this.last;
25081 this.selectRange(last, rowIndex, e.ctrlKey);
25082 this.last = last; // reset the last
25086 var isSelected = this.isSelected(rowIndex);
25087 //Roo.log("select row:" + rowIndex);
25089 this.deselectRow(rowIndex);
25091 this.selectRow(rowIndex, true);
25095 if(e.button !== 0 && isSelected){
25096 alert('rowIndex 2: ' + rowIndex);
25097 view.focusRow(rowIndex);
25098 }else if(e.ctrlKey && isSelected){
25099 this.deselectRow(rowIndex);
25100 }else if(!isSelected){
25101 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
25102 view.focusRow(rowIndex);
25106 this.fireEvent("afterselectionchange", this);
25109 handleDragableRowClick : function(grid, rowIndex, e)
25111 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
25112 this.selectRow(rowIndex, false);
25113 grid.view.focusRow(rowIndex);
25114 this.fireEvent("afterselectionchange", this);
25119 * Selects multiple rows.
25120 * @param {Array} rows Array of the indexes of the row to select
25121 * @param {Boolean} keepExisting (optional) True to keep existing selections
25123 selectRows : function(rows, keepExisting){
25125 this.clearSelections();
25127 for(var i = 0, len = rows.length; i < len; i++){
25128 this.selectRow(rows[i], true);
25133 * Selects a range of rows. All rows in between startRow and endRow are also selected.
25134 * @param {Number} startRow The index of the first row in the range
25135 * @param {Number} endRow The index of the last row in the range
25136 * @param {Boolean} keepExisting (optional) True to retain existing selections
25138 selectRange : function(startRow, endRow, keepExisting){
25143 this.clearSelections();
25145 if(startRow <= endRow){
25146 for(var i = startRow; i <= endRow; i++){
25147 this.selectRow(i, true);
25150 for(var i = startRow; i >= endRow; i--){
25151 this.selectRow(i, true);
25157 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
25158 * @param {Number} startRow The index of the first row in the range
25159 * @param {Number} endRow The index of the last row in the range
25161 deselectRange : function(startRow, endRow, preventViewNotify){
25165 for(var i = startRow; i <= endRow; i++){
25166 this.deselectRow(i, preventViewNotify);
25172 * @param {Number} row The index of the row to select
25173 * @param {Boolean} keepExisting (optional) True to keep existing selections
25175 selectRow : function(index, keepExisting, preventViewNotify)
25177 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
25180 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
25181 if(!keepExisting || this.singleSelect){
25182 this.clearSelections();
25185 var r = this.grid.store.getAt(index);
25186 //console.log('selectRow - record id :' + r.id);
25188 this.selections.add(r);
25189 this.last = this.lastActive = index;
25190 if(!preventViewNotify){
25191 var proxy = new Roo.Element(
25192 this.grid.getRowDom(index)
25194 proxy.addClass('bg-info info');
25196 this.fireEvent("rowselect", this, index, r);
25197 this.fireEvent("selectionchange", this);
25203 * @param {Number} row The index of the row to deselect
25205 deselectRow : function(index, preventViewNotify)
25210 if(this.last == index){
25213 if(this.lastActive == index){
25214 this.lastActive = false;
25217 var r = this.grid.store.getAt(index);
25222 this.selections.remove(r);
25223 //.console.log('deselectRow - record id :' + r.id);
25224 if(!preventViewNotify){
25226 var proxy = new Roo.Element(
25227 this.grid.getRowDom(index)
25229 proxy.removeClass('bg-info info');
25231 this.fireEvent("rowdeselect", this, index);
25232 this.fireEvent("selectionchange", this);
25236 restoreLast : function(){
25238 this.last = this._last;
25243 acceptsNav : function(row, col, cm){
25244 return !cm.isHidden(col) && cm.isCellEditable(col, row);
25248 onEditorKey : function(field, e){
25249 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
25254 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
25256 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
25258 }else if(k == e.ENTER && !e.ctrlKey){
25262 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
25264 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25266 }else if(k == e.ESC){
25270 g.startEditing(newCell[0], newCell[1]);
25276 * Ext JS Library 1.1.1
25277 * Copyright(c) 2006-2007, Ext JS, LLC.
25279 * Originally Released Under LGPL - original licence link has changed is not relivant.
25282 * <script type="text/javascript">
25286 * @class Roo.bootstrap.PagingToolbar
25287 * @extends Roo.bootstrap.NavSimplebar
25288 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
25290 * Create a new PagingToolbar
25291 * @param {Object} config The config object
25292 * @param {Roo.data.Store} store
25294 Roo.bootstrap.PagingToolbar = function(config)
25296 // old args format still supported... - xtype is prefered..
25297 // created from xtype...
25299 this.ds = config.dataSource;
25301 if (config.store && !this.ds) {
25302 this.store= Roo.factory(config.store, Roo.data);
25303 this.ds = this.store;
25304 this.ds.xmodule = this.xmodule || false;
25307 this.toolbarItems = [];
25308 if (config.items) {
25309 this.toolbarItems = config.items;
25312 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
25317 this.bind(this.ds);
25320 if (Roo.bootstrap.version == 4) {
25321 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
25323 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
25328 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
25330 * @cfg {Roo.data.Store} dataSource
25331 * The underlying data store providing the paged data
25334 * @cfg {String/HTMLElement/Element} container
25335 * container The id or element that will contain the toolbar
25338 * @cfg {Boolean} displayInfo
25339 * True to display the displayMsg (defaults to false)
25342 * @cfg {Number} pageSize
25343 * The number of records to display per page (defaults to 20)
25347 * @cfg {String} displayMsg
25348 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25350 displayMsg : 'Displaying {0} - {1} of {2}',
25352 * @cfg {String} emptyMsg
25353 * The message to display when no records are found (defaults to "No data to display")
25355 emptyMsg : 'No data to display',
25357 * Customizable piece of the default paging text (defaults to "Page")
25360 beforePageText : "Page",
25362 * Customizable piece of the default paging text (defaults to "of %0")
25365 afterPageText : "of {0}",
25367 * Customizable piece of the default paging text (defaults to "First Page")
25370 firstText : "First Page",
25372 * Customizable piece of the default paging text (defaults to "Previous Page")
25375 prevText : "Previous Page",
25377 * Customizable piece of the default paging text (defaults to "Next Page")
25380 nextText : "Next Page",
25382 * Customizable piece of the default paging text (defaults to "Last Page")
25385 lastText : "Last Page",
25387 * Customizable piece of the default paging text (defaults to "Refresh")
25390 refreshText : "Refresh",
25394 onRender : function(ct, position)
25396 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25397 this.navgroup.parentId = this.id;
25398 this.navgroup.onRender(this.el, null);
25399 // add the buttons to the navgroup
25401 if(this.displayInfo){
25402 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25403 this.displayEl = this.el.select('.x-paging-info', true).first();
25404 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25405 // this.displayEl = navel.el.select('span',true).first();
25411 Roo.each(_this.buttons, function(e){ // this might need to use render????
25412 Roo.factory(e).render(_this.el);
25416 Roo.each(_this.toolbarItems, function(e) {
25417 _this.navgroup.addItem(e);
25421 this.first = this.navgroup.addItem({
25422 tooltip: this.firstText,
25423 cls: "prev btn-outline-secondary",
25424 html : ' <i class="fa fa-step-backward"></i>',
25426 preventDefault: true,
25427 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25430 this.prev = this.navgroup.addItem({
25431 tooltip: this.prevText,
25432 cls: "prev btn-outline-secondary",
25433 html : ' <i class="fa fa-backward"></i>',
25435 preventDefault: true,
25436 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
25438 //this.addSeparator();
25441 var field = this.navgroup.addItem( {
25443 cls : 'x-paging-position btn-outline-secondary',
25445 html : this.beforePageText +
25446 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25447 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
25450 this.field = field.el.select('input', true).first();
25451 this.field.on("keydown", this.onPagingKeydown, this);
25452 this.field.on("focus", function(){this.dom.select();});
25455 this.afterTextEl = field.el.select('.x-paging-after',true).first();
25456 //this.field.setHeight(18);
25457 //this.addSeparator();
25458 this.next = this.navgroup.addItem({
25459 tooltip: this.nextText,
25460 cls: "next btn-outline-secondary",
25461 html : ' <i class="fa fa-forward"></i>',
25463 preventDefault: true,
25464 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
25466 this.last = this.navgroup.addItem({
25467 tooltip: this.lastText,
25468 html : ' <i class="fa fa-step-forward"></i>',
25469 cls: "next btn-outline-secondary",
25471 preventDefault: true,
25472 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
25474 //this.addSeparator();
25475 this.loading = this.navgroup.addItem({
25476 tooltip: this.refreshText,
25477 cls: "btn-outline-secondary",
25478 html : ' <i class="fa fa-refresh"></i>',
25479 preventDefault: true,
25480 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25486 updateInfo : function(){
25487 if(this.displayEl){
25488 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25489 var msg = count == 0 ?
25493 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
25495 this.displayEl.update(msg);
25500 onLoad : function(ds, r, o)
25502 this.cursor = o.params.start ? o.params.start : 0;
25504 var d = this.getPageData(),
25509 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25510 this.field.dom.value = ap;
25511 this.first.setDisabled(ap == 1);
25512 this.prev.setDisabled(ap == 1);
25513 this.next.setDisabled(ap == ps);
25514 this.last.setDisabled(ap == ps);
25515 this.loading.enable();
25520 getPageData : function(){
25521 var total = this.ds.getTotalCount();
25524 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25525 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25530 onLoadError : function(){
25531 this.loading.enable();
25535 onPagingKeydown : function(e){
25536 var k = e.getKey();
25537 var d = this.getPageData();
25539 var v = this.field.dom.value, pageNum;
25540 if(!v || isNaN(pageNum = parseInt(v, 10))){
25541 this.field.dom.value = d.activePage;
25544 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25545 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25548 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))
25550 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25551 this.field.dom.value = pageNum;
25552 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25555 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25557 var v = this.field.dom.value, pageNum;
25558 var increment = (e.shiftKey) ? 10 : 1;
25559 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25562 if(!v || isNaN(pageNum = parseInt(v, 10))) {
25563 this.field.dom.value = d.activePage;
25566 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25568 this.field.dom.value = parseInt(v, 10) + increment;
25569 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25570 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25577 beforeLoad : function(){
25579 this.loading.disable();
25584 onClick : function(which){
25593 ds.load({params:{start: 0, limit: this.pageSize}});
25596 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25599 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25602 var total = ds.getTotalCount();
25603 var extra = total % this.pageSize;
25604 var lastStart = extra ? (total - extra) : total-this.pageSize;
25605 ds.load({params:{start: lastStart, limit: this.pageSize}});
25608 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25614 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25615 * @param {Roo.data.Store} store The data store to unbind
25617 unbind : function(ds){
25618 ds.un("beforeload", this.beforeLoad, this);
25619 ds.un("load", this.onLoad, this);
25620 ds.un("loadexception", this.onLoadError, this);
25621 ds.un("remove", this.updateInfo, this);
25622 ds.un("add", this.updateInfo, this);
25623 this.ds = undefined;
25627 * Binds the paging toolbar to the specified {@link Roo.data.Store}
25628 * @param {Roo.data.Store} store The data store to bind
25630 bind : function(ds){
25631 ds.on("beforeload", this.beforeLoad, this);
25632 ds.on("load", this.onLoad, this);
25633 ds.on("loadexception", this.onLoadError, this);
25634 ds.on("remove", this.updateInfo, this);
25635 ds.on("add", this.updateInfo, this);
25646 * @class Roo.bootstrap.MessageBar
25647 * @extends Roo.bootstrap.Component
25648 * Bootstrap MessageBar class
25649 * @cfg {String} html contents of the MessageBar
25650 * @cfg {String} weight (info | success | warning | danger) default info
25651 * @cfg {String} beforeClass insert the bar before the given class
25652 * @cfg {Boolean} closable (true | false) default false
25653 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25656 * Create a new Element
25657 * @param {Object} config The config object
25660 Roo.bootstrap.MessageBar = function(config){
25661 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25664 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25670 beforeClass: 'bootstrap-sticky-wrap',
25672 getAutoCreate : function(){
25676 cls: 'alert alert-dismissable alert-' + this.weight,
25681 html: this.html || ''
25687 cfg.cls += ' alert-messages-fixed';
25701 onRender : function(ct, position)
25703 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25706 var cfg = Roo.apply({}, this.getAutoCreate());
25710 cfg.cls += ' ' + this.cls;
25713 cfg.style = this.style;
25715 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25717 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25720 this.el.select('>button.close').on('click', this.hide, this);
25726 if (!this.rendered) {
25732 this.fireEvent('show', this);
25738 if (!this.rendered) {
25744 this.fireEvent('hide', this);
25747 update : function()
25749 // var e = this.el.dom.firstChild;
25751 // if(this.closable){
25752 // e = e.nextSibling;
25755 // e.data = this.html || '';
25757 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25773 * @class Roo.bootstrap.Graph
25774 * @extends Roo.bootstrap.Component
25775 * Bootstrap Graph class
25779 @cfg {String} graphtype bar | vbar | pie
25780 @cfg {number} g_x coodinator | centre x (pie)
25781 @cfg {number} g_y coodinator | centre y (pie)
25782 @cfg {number} g_r radius (pie)
25783 @cfg {number} g_height height of the chart (respected by all elements in the set)
25784 @cfg {number} g_width width of the chart (respected by all elements in the set)
25785 @cfg {Object} title The title of the chart
25788 -opts (object) options for the chart
25790 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25791 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25793 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.
25794 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25796 o stretch (boolean)
25798 -opts (object) options for the pie
25801 o startAngle (number)
25802 o endAngle (number)
25806 * Create a new Input
25807 * @param {Object} config The config object
25810 Roo.bootstrap.Graph = function(config){
25811 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25817 * The img click event for the img.
25818 * @param {Roo.EventObject} e
25824 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25835 //g_colors: this.colors,
25842 getAutoCreate : function(){
25853 onRender : function(ct,position){
25856 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25858 if (typeof(Raphael) == 'undefined') {
25859 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25863 this.raphael = Raphael(this.el.dom);
25865 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25866 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25867 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25868 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25870 r.text(160, 10, "Single Series Chart").attr(txtattr);
25871 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25872 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25873 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25875 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25876 r.barchart(330, 10, 300, 220, data1);
25877 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25878 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25881 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25882 // r.barchart(30, 30, 560, 250, xdata, {
25883 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25884 // axis : "0 0 1 1",
25885 // axisxlabels : xdata
25886 // //yvalues : cols,
25889 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25891 // this.load(null,xdata,{
25892 // axis : "0 0 1 1",
25893 // axisxlabels : xdata
25898 load : function(graphtype,xdata,opts)
25900 this.raphael.clear();
25902 graphtype = this.graphtype;
25907 var r = this.raphael,
25908 fin = function () {
25909 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25911 fout = function () {
25912 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25914 pfin = function() {
25915 this.sector.stop();
25916 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25919 this.label[0].stop();
25920 this.label[0].attr({ r: 7.5 });
25921 this.label[1].attr({ "font-weight": 800 });
25924 pfout = function() {
25925 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25928 this.label[0].animate({ r: 5 }, 500, "bounce");
25929 this.label[1].attr({ "font-weight": 400 });
25935 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25938 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25941 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25942 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25944 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25951 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25956 setTitle: function(o)
25961 initEvents: function() {
25964 this.el.on('click', this.onClick, this);
25968 onClick : function(e)
25970 Roo.log('img onclick');
25971 this.fireEvent('click', this, e);
25983 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25986 * @class Roo.bootstrap.dash.NumberBox
25987 * @extends Roo.bootstrap.Component
25988 * Bootstrap NumberBox class
25989 * @cfg {String} headline Box headline
25990 * @cfg {String} content Box content
25991 * @cfg {String} icon Box icon
25992 * @cfg {String} footer Footer text
25993 * @cfg {String} fhref Footer href
25996 * Create a new NumberBox
25997 * @param {Object} config The config object
26001 Roo.bootstrap.dash.NumberBox = function(config){
26002 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
26006 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
26015 getAutoCreate : function(){
26019 cls : 'small-box ',
26027 cls : 'roo-headline',
26028 html : this.headline
26032 cls : 'roo-content',
26033 html : this.content
26047 cls : 'ion ' + this.icon
26056 cls : 'small-box-footer',
26057 href : this.fhref || '#',
26061 cfg.cn.push(footer);
26068 onRender : function(ct,position){
26069 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
26076 setHeadline: function (value)
26078 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
26081 setFooter: function (value, href)
26083 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
26086 this.el.select('a.small-box-footer',true).first().attr('href', href);
26091 setContent: function (value)
26093 this.el.select('.roo-content',true).first().dom.innerHTML = value;
26096 initEvents: function()
26110 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26113 * @class Roo.bootstrap.dash.TabBox
26114 * @extends Roo.bootstrap.Component
26115 * Bootstrap TabBox class
26116 * @cfg {String} title Title of the TabBox
26117 * @cfg {String} icon Icon of the TabBox
26118 * @cfg {Boolean} showtabs (true|false) show the tabs default true
26119 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
26122 * Create a new TabBox
26123 * @param {Object} config The config object
26127 Roo.bootstrap.dash.TabBox = function(config){
26128 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
26133 * When a pane is added
26134 * @param {Roo.bootstrap.dash.TabPane} pane
26138 * @event activatepane
26139 * When a pane is activated
26140 * @param {Roo.bootstrap.dash.TabPane} pane
26142 "activatepane" : true
26150 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
26155 tabScrollable : false,
26157 getChildContainer : function()
26159 return this.el.select('.tab-content', true).first();
26162 getAutoCreate : function(){
26166 cls: 'pull-left header',
26174 cls: 'fa ' + this.icon
26180 cls: 'nav nav-tabs pull-right',
26186 if(this.tabScrollable){
26193 cls: 'nav nav-tabs pull-right',
26204 cls: 'nav-tabs-custom',
26209 cls: 'tab-content no-padding',
26217 initEvents : function()
26219 //Roo.log('add add pane handler');
26220 this.on('addpane', this.onAddPane, this);
26223 * Updates the box title
26224 * @param {String} html to set the title to.
26226 setTitle : function(value)
26228 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
26230 onAddPane : function(pane)
26232 this.panes.push(pane);
26233 //Roo.log('addpane');
26235 // tabs are rendere left to right..
26236 if(!this.showtabs){
26240 var ctr = this.el.select('.nav-tabs', true).first();
26243 var existing = ctr.select('.nav-tab',true);
26244 var qty = existing.getCount();;
26247 var tab = ctr.createChild({
26249 cls : 'nav-tab' + (qty ? '' : ' active'),
26257 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
26260 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
26262 pane.el.addClass('active');
26267 onTabClick : function(ev,un,ob,pane)
26269 //Roo.log('tab - prev default');
26270 ev.preventDefault();
26273 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26274 pane.tab.addClass('active');
26275 //Roo.log(pane.title);
26276 this.getChildContainer().select('.tab-pane',true).removeClass('active');
26277 // technically we should have a deactivate event.. but maybe add later.
26278 // and it should not de-activate the selected tab...
26279 this.fireEvent('activatepane', pane);
26280 pane.el.addClass('active');
26281 pane.fireEvent('activate');
26286 getActivePane : function()
26289 Roo.each(this.panes, function(p) {
26290 if(p.el.hasClass('active')){
26311 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26313 * @class Roo.bootstrap.TabPane
26314 * @extends Roo.bootstrap.Component
26315 * Bootstrap TabPane class
26316 * @cfg {Boolean} active (false | true) Default false
26317 * @cfg {String} title title of panel
26321 * Create a new TabPane
26322 * @param {Object} config The config object
26325 Roo.bootstrap.dash.TabPane = function(config){
26326 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
26332 * When a pane is activated
26333 * @param {Roo.bootstrap.dash.TabPane} pane
26340 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
26345 // the tabBox that this is attached to.
26348 getAutoCreate : function()
26356 cfg.cls += ' active';
26361 initEvents : function()
26363 //Roo.log('trigger add pane handler');
26364 this.parent().fireEvent('addpane', this)
26368 * Updates the tab title
26369 * @param {String} html to set the title to.
26371 setTitle: function(str)
26377 this.tab.select('a', true).first().dom.innerHTML = str;
26394 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26397 * @class Roo.bootstrap.menu.Menu
26398 * @extends Roo.bootstrap.Component
26399 * Bootstrap Menu class - container for Menu
26400 * @cfg {String} html Text of the menu
26401 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26402 * @cfg {String} icon Font awesome icon
26403 * @cfg {String} pos Menu align to (top | bottom) default bottom
26407 * Create a new Menu
26408 * @param {Object} config The config object
26412 Roo.bootstrap.menu.Menu = function(config){
26413 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26417 * @event beforeshow
26418 * Fires before this menu is displayed
26419 * @param {Roo.bootstrap.menu.Menu} this
26423 * @event beforehide
26424 * Fires before this menu is hidden
26425 * @param {Roo.bootstrap.menu.Menu} this
26430 * Fires after this menu is displayed
26431 * @param {Roo.bootstrap.menu.Menu} this
26436 * Fires after this menu is hidden
26437 * @param {Roo.bootstrap.menu.Menu} this
26442 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26443 * @param {Roo.bootstrap.menu.Menu} this
26444 * @param {Roo.EventObject} e
26451 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
26455 weight : 'default',
26460 getChildContainer : function() {
26461 if(this.isSubMenu){
26465 return this.el.select('ul.dropdown-menu', true).first();
26468 getAutoCreate : function()
26473 cls : 'roo-menu-text',
26481 cls : 'fa ' + this.icon
26492 cls : 'dropdown-button btn btn-' + this.weight,
26497 cls : 'dropdown-toggle btn btn-' + this.weight,
26507 cls : 'dropdown-menu'
26513 if(this.pos == 'top'){
26514 cfg.cls += ' dropup';
26517 if(this.isSubMenu){
26520 cls : 'dropdown-menu'
26527 onRender : function(ct, position)
26529 this.isSubMenu = ct.hasClass('dropdown-submenu');
26531 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26534 initEvents : function()
26536 if(this.isSubMenu){
26540 this.hidden = true;
26542 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26543 this.triggerEl.on('click', this.onTriggerPress, this);
26545 this.buttonEl = this.el.select('button.dropdown-button', true).first();
26546 this.buttonEl.on('click', this.onClick, this);
26552 if(this.isSubMenu){
26556 return this.el.select('ul.dropdown-menu', true).first();
26559 onClick : function(e)
26561 this.fireEvent("click", this, e);
26564 onTriggerPress : function(e)
26566 if (this.isVisible()) {
26573 isVisible : function(){
26574 return !this.hidden;
26579 this.fireEvent("beforeshow", this);
26581 this.hidden = false;
26582 this.el.addClass('open');
26584 Roo.get(document).on("mouseup", this.onMouseUp, this);
26586 this.fireEvent("show", this);
26593 this.fireEvent("beforehide", this);
26595 this.hidden = true;
26596 this.el.removeClass('open');
26598 Roo.get(document).un("mouseup", this.onMouseUp);
26600 this.fireEvent("hide", this);
26603 onMouseUp : function()
26617 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26620 * @class Roo.bootstrap.menu.Item
26621 * @extends Roo.bootstrap.Component
26622 * Bootstrap MenuItem class
26623 * @cfg {Boolean} submenu (true | false) default false
26624 * @cfg {String} html text of the item
26625 * @cfg {String} href the link
26626 * @cfg {Boolean} disable (true | false) default false
26627 * @cfg {Boolean} preventDefault (true | false) default true
26628 * @cfg {String} icon Font awesome icon
26629 * @cfg {String} pos Submenu align to (left | right) default right
26633 * Create a new Item
26634 * @param {Object} config The config object
26638 Roo.bootstrap.menu.Item = function(config){
26639 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26643 * Fires when the mouse is hovering over this menu
26644 * @param {Roo.bootstrap.menu.Item} this
26645 * @param {Roo.EventObject} e
26650 * Fires when the mouse exits this menu
26651 * @param {Roo.bootstrap.menu.Item} this
26652 * @param {Roo.EventObject} e
26658 * The raw click event for the entire grid.
26659 * @param {Roo.EventObject} e
26665 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26670 preventDefault: true,
26675 getAutoCreate : function()
26680 cls : 'roo-menu-item-text',
26688 cls : 'fa ' + this.icon
26697 href : this.href || '#',
26704 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26708 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26710 if(this.pos == 'left'){
26711 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26718 initEvents : function()
26720 this.el.on('mouseover', this.onMouseOver, this);
26721 this.el.on('mouseout', this.onMouseOut, this);
26723 this.el.select('a', true).first().on('click', this.onClick, this);
26727 onClick : function(e)
26729 if(this.preventDefault){
26730 e.preventDefault();
26733 this.fireEvent("click", this, e);
26736 onMouseOver : function(e)
26738 if(this.submenu && this.pos == 'left'){
26739 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26742 this.fireEvent("mouseover", this, e);
26745 onMouseOut : function(e)
26747 this.fireEvent("mouseout", this, e);
26759 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26762 * @class Roo.bootstrap.menu.Separator
26763 * @extends Roo.bootstrap.Component
26764 * Bootstrap Separator class
26767 * Create a new Separator
26768 * @param {Object} config The config object
26772 Roo.bootstrap.menu.Separator = function(config){
26773 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26776 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26778 getAutoCreate : function(){
26799 * @class Roo.bootstrap.Tooltip
26800 * Bootstrap Tooltip class
26801 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26802 * to determine which dom element triggers the tooltip.
26804 * It needs to add support for additional attributes like tooltip-position
26807 * Create a new Toolti
26808 * @param {Object} config The config object
26811 Roo.bootstrap.Tooltip = function(config){
26812 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26814 this.alignment = Roo.bootstrap.Tooltip.alignment;
26816 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26817 this.alignment = config.alignment;
26822 Roo.apply(Roo.bootstrap.Tooltip, {
26824 * @function init initialize tooltip monitoring.
26828 currentTip : false,
26829 currentRegion : false,
26835 Roo.get(document).on('mouseover', this.enter ,this);
26836 Roo.get(document).on('mouseout', this.leave, this);
26839 this.currentTip = new Roo.bootstrap.Tooltip();
26842 enter : function(ev)
26844 var dom = ev.getTarget();
26846 //Roo.log(['enter',dom]);
26847 var el = Roo.fly(dom);
26848 if (this.currentEl) {
26850 //Roo.log(this.currentEl);
26851 //Roo.log(this.currentEl.contains(dom));
26852 if (this.currentEl == el) {
26855 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26861 if (this.currentTip.el) {
26862 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26866 if(!el || el.dom == document){
26872 // you can not look for children, as if el is the body.. then everythign is the child..
26873 if (!el.attr('tooltip')) { //
26874 if (!el.select("[tooltip]").elements.length) {
26877 // is the mouse over this child...?
26878 bindEl = el.select("[tooltip]").first();
26879 var xy = ev.getXY();
26880 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26881 //Roo.log("not in region.");
26884 //Roo.log("child element over..");
26887 this.currentEl = bindEl;
26888 this.currentTip.bind(bindEl);
26889 this.currentRegion = Roo.lib.Region.getRegion(dom);
26890 this.currentTip.enter();
26893 leave : function(ev)
26895 var dom = ev.getTarget();
26896 //Roo.log(['leave',dom]);
26897 if (!this.currentEl) {
26902 if (dom != this.currentEl.dom) {
26905 var xy = ev.getXY();
26906 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26909 // only activate leave if mouse cursor is outside... bounding box..
26914 if (this.currentTip) {
26915 this.currentTip.leave();
26917 //Roo.log('clear currentEl');
26918 this.currentEl = false;
26923 'left' : ['r-l', [-2,0], 'right'],
26924 'right' : ['l-r', [2,0], 'left'],
26925 'bottom' : ['t-b', [0,2], 'top'],
26926 'top' : [ 'b-t', [0,-2], 'bottom']
26932 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26937 delay : null, // can be { show : 300 , hide: 500}
26941 hoverState : null, //???
26943 placement : 'bottom',
26947 getAutoCreate : function(){
26954 cls : 'tooltip-arrow'
26957 cls : 'tooltip-inner'
26964 bind : function(el)
26970 enter : function () {
26972 if (this.timeout != null) {
26973 clearTimeout(this.timeout);
26976 this.hoverState = 'in';
26977 //Roo.log("enter - show");
26978 if (!this.delay || !this.delay.show) {
26983 this.timeout = setTimeout(function () {
26984 if (_t.hoverState == 'in') {
26987 }, this.delay.show);
26991 clearTimeout(this.timeout);
26993 this.hoverState = 'out';
26994 if (!this.delay || !this.delay.hide) {
27000 this.timeout = setTimeout(function () {
27001 //Roo.log("leave - timeout");
27003 if (_t.hoverState == 'out') {
27005 Roo.bootstrap.Tooltip.currentEl = false;
27010 show : function (msg)
27013 this.render(document.body);
27016 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
27018 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
27020 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
27022 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
27024 var placement = typeof this.placement == 'function' ?
27025 this.placement.call(this, this.el, on_el) :
27028 var autoToken = /\s?auto?\s?/i;
27029 var autoPlace = autoToken.test(placement);
27031 placement = placement.replace(autoToken, '') || 'top';
27035 //this.el.setXY([0,0]);
27037 //this.el.dom.style.display='block';
27039 //this.el.appendTo(on_el);
27041 var p = this.getPosition();
27042 var box = this.el.getBox();
27048 var align = this.alignment[placement];
27050 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
27052 if(placement == 'top' || placement == 'bottom'){
27054 placement = 'right';
27057 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
27058 placement = 'left';
27061 var scroll = Roo.select('body', true).first().getScroll();
27063 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
27067 align = this.alignment[placement];
27070 this.el.alignTo(this.bindEl, align[0],align[1]);
27071 //var arrow = this.el.select('.arrow',true).first();
27072 //arrow.set(align[2],
27074 this.el.addClass(placement);
27076 this.el.addClass('in fade');
27078 this.hoverState = null;
27080 if (this.el.hasClass('fade')) {
27091 //this.el.setXY([0,0]);
27092 this.el.removeClass('in');
27108 * @class Roo.bootstrap.LocationPicker
27109 * @extends Roo.bootstrap.Component
27110 * Bootstrap LocationPicker class
27111 * @cfg {Number} latitude Position when init default 0
27112 * @cfg {Number} longitude Position when init default 0
27113 * @cfg {Number} zoom default 15
27114 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
27115 * @cfg {Boolean} mapTypeControl default false
27116 * @cfg {Boolean} disableDoubleClickZoom default false
27117 * @cfg {Boolean} scrollwheel default true
27118 * @cfg {Boolean} streetViewControl default false
27119 * @cfg {Number} radius default 0
27120 * @cfg {String} locationName
27121 * @cfg {Boolean} draggable default true
27122 * @cfg {Boolean} enableAutocomplete default false
27123 * @cfg {Boolean} enableReverseGeocode default true
27124 * @cfg {String} markerTitle
27127 * Create a new LocationPicker
27128 * @param {Object} config The config object
27132 Roo.bootstrap.LocationPicker = function(config){
27134 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
27139 * Fires when the picker initialized.
27140 * @param {Roo.bootstrap.LocationPicker} this
27141 * @param {Google Location} location
27145 * @event positionchanged
27146 * Fires when the picker position changed.
27147 * @param {Roo.bootstrap.LocationPicker} this
27148 * @param {Google Location} location
27150 positionchanged : true,
27153 * Fires when the map resize.
27154 * @param {Roo.bootstrap.LocationPicker} this
27159 * Fires when the map show.
27160 * @param {Roo.bootstrap.LocationPicker} this
27165 * Fires when the map hide.
27166 * @param {Roo.bootstrap.LocationPicker} this
27171 * Fires when click the map.
27172 * @param {Roo.bootstrap.LocationPicker} this
27173 * @param {Map event} e
27177 * @event mapRightClick
27178 * Fires when right click the map.
27179 * @param {Roo.bootstrap.LocationPicker} this
27180 * @param {Map event} e
27182 mapRightClick : true,
27184 * @event markerClick
27185 * Fires when click the marker.
27186 * @param {Roo.bootstrap.LocationPicker} this
27187 * @param {Map event} e
27189 markerClick : true,
27191 * @event markerRightClick
27192 * Fires when right click the marker.
27193 * @param {Roo.bootstrap.LocationPicker} this
27194 * @param {Map event} e
27196 markerRightClick : true,
27198 * @event OverlayViewDraw
27199 * Fires when OverlayView Draw
27200 * @param {Roo.bootstrap.LocationPicker} this
27202 OverlayViewDraw : true,
27204 * @event OverlayViewOnAdd
27205 * Fires when OverlayView Draw
27206 * @param {Roo.bootstrap.LocationPicker} this
27208 OverlayViewOnAdd : true,
27210 * @event OverlayViewOnRemove
27211 * Fires when OverlayView Draw
27212 * @param {Roo.bootstrap.LocationPicker} this
27214 OverlayViewOnRemove : true,
27216 * @event OverlayViewShow
27217 * Fires when OverlayView Draw
27218 * @param {Roo.bootstrap.LocationPicker} this
27219 * @param {Pixel} cpx
27221 OverlayViewShow : true,
27223 * @event OverlayViewHide
27224 * Fires when OverlayView Draw
27225 * @param {Roo.bootstrap.LocationPicker} this
27227 OverlayViewHide : true,
27229 * @event loadexception
27230 * Fires when load google lib failed.
27231 * @param {Roo.bootstrap.LocationPicker} this
27233 loadexception : true
27238 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
27240 gMapContext: false,
27246 mapTypeControl: false,
27247 disableDoubleClickZoom: false,
27249 streetViewControl: false,
27253 enableAutocomplete: false,
27254 enableReverseGeocode: true,
27257 getAutoCreate: function()
27262 cls: 'roo-location-picker'
27268 initEvents: function(ct, position)
27270 if(!this.el.getWidth() || this.isApplied()){
27274 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27279 initial: function()
27281 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27282 this.fireEvent('loadexception', this);
27286 if(!this.mapTypeId){
27287 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
27290 this.gMapContext = this.GMapContext();
27292 this.initOverlayView();
27294 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
27298 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
27299 _this.setPosition(_this.gMapContext.marker.position);
27302 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
27303 _this.fireEvent('mapClick', this, event);
27307 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
27308 _this.fireEvent('mapRightClick', this, event);
27312 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
27313 _this.fireEvent('markerClick', this, event);
27317 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
27318 _this.fireEvent('markerRightClick', this, event);
27322 this.setPosition(this.gMapContext.location);
27324 this.fireEvent('initial', this, this.gMapContext.location);
27327 initOverlayView: function()
27331 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
27335 _this.fireEvent('OverlayViewDraw', _this);
27340 _this.fireEvent('OverlayViewOnAdd', _this);
27343 onRemove: function()
27345 _this.fireEvent('OverlayViewOnRemove', _this);
27348 show: function(cpx)
27350 _this.fireEvent('OverlayViewShow', _this, cpx);
27355 _this.fireEvent('OverlayViewHide', _this);
27361 fromLatLngToContainerPixel: function(event)
27363 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27366 isApplied: function()
27368 return this.getGmapContext() == false ? false : true;
27371 getGmapContext: function()
27373 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27376 GMapContext: function()
27378 var position = new google.maps.LatLng(this.latitude, this.longitude);
27380 var _map = new google.maps.Map(this.el.dom, {
27383 mapTypeId: this.mapTypeId,
27384 mapTypeControl: this.mapTypeControl,
27385 disableDoubleClickZoom: this.disableDoubleClickZoom,
27386 scrollwheel: this.scrollwheel,
27387 streetViewControl: this.streetViewControl,
27388 locationName: this.locationName,
27389 draggable: this.draggable,
27390 enableAutocomplete: this.enableAutocomplete,
27391 enableReverseGeocode: this.enableReverseGeocode
27394 var _marker = new google.maps.Marker({
27395 position: position,
27397 title: this.markerTitle,
27398 draggable: this.draggable
27405 location: position,
27406 radius: this.radius,
27407 locationName: this.locationName,
27408 addressComponents: {
27409 formatted_address: null,
27410 addressLine1: null,
27411 addressLine2: null,
27413 streetNumber: null,
27417 stateOrProvince: null
27420 domContainer: this.el.dom,
27421 geodecoder: new google.maps.Geocoder()
27425 drawCircle: function(center, radius, options)
27427 if (this.gMapContext.circle != null) {
27428 this.gMapContext.circle.setMap(null);
27432 options = Roo.apply({}, options, {
27433 strokeColor: "#0000FF",
27434 strokeOpacity: .35,
27436 fillColor: "#0000FF",
27440 options.map = this.gMapContext.map;
27441 options.radius = radius;
27442 options.center = center;
27443 this.gMapContext.circle = new google.maps.Circle(options);
27444 return this.gMapContext.circle;
27450 setPosition: function(location)
27452 this.gMapContext.location = location;
27453 this.gMapContext.marker.setPosition(location);
27454 this.gMapContext.map.panTo(location);
27455 this.drawCircle(location, this.gMapContext.radius, {});
27459 if (this.gMapContext.settings.enableReverseGeocode) {
27460 this.gMapContext.geodecoder.geocode({
27461 latLng: this.gMapContext.location
27462 }, function(results, status) {
27464 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27465 _this.gMapContext.locationName = results[0].formatted_address;
27466 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27468 _this.fireEvent('positionchanged', this, location);
27475 this.fireEvent('positionchanged', this, location);
27480 google.maps.event.trigger(this.gMapContext.map, "resize");
27482 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27484 this.fireEvent('resize', this);
27487 setPositionByLatLng: function(latitude, longitude)
27489 this.setPosition(new google.maps.LatLng(latitude, longitude));
27492 getCurrentPosition: function()
27495 latitude: this.gMapContext.location.lat(),
27496 longitude: this.gMapContext.location.lng()
27500 getAddressName: function()
27502 return this.gMapContext.locationName;
27505 getAddressComponents: function()
27507 return this.gMapContext.addressComponents;
27510 address_component_from_google_geocode: function(address_components)
27514 for (var i = 0; i < address_components.length; i++) {
27515 var component = address_components[i];
27516 if (component.types.indexOf("postal_code") >= 0) {
27517 result.postalCode = component.short_name;
27518 } else if (component.types.indexOf("street_number") >= 0) {
27519 result.streetNumber = component.short_name;
27520 } else if (component.types.indexOf("route") >= 0) {
27521 result.streetName = component.short_name;
27522 } else if (component.types.indexOf("neighborhood") >= 0) {
27523 result.city = component.short_name;
27524 } else if (component.types.indexOf("locality") >= 0) {
27525 result.city = component.short_name;
27526 } else if (component.types.indexOf("sublocality") >= 0) {
27527 result.district = component.short_name;
27528 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27529 result.stateOrProvince = component.short_name;
27530 } else if (component.types.indexOf("country") >= 0) {
27531 result.country = component.short_name;
27535 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27536 result.addressLine2 = "";
27540 setZoomLevel: function(zoom)
27542 this.gMapContext.map.setZoom(zoom);
27555 this.fireEvent('show', this);
27566 this.fireEvent('hide', this);
27571 Roo.apply(Roo.bootstrap.LocationPicker, {
27573 OverlayView : function(map, options)
27575 options = options || {};
27582 * @class Roo.bootstrap.Alert
27583 * @extends Roo.bootstrap.Component
27584 * Bootstrap Alert class - shows an alert area box
27586 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27587 Enter a valid email address
27590 * @cfg {String} title The title of alert
27591 * @cfg {String} html The content of alert
27592 * @cfg {String} weight ( success | info | warning | danger )
27593 * @cfg {String} faicon font-awesomeicon
27596 * Create a new alert
27597 * @param {Object} config The config object
27601 Roo.bootstrap.Alert = function(config){
27602 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27606 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
27613 getAutoCreate : function()
27622 cls : 'roo-alert-icon'
27627 cls : 'roo-alert-title',
27632 cls : 'roo-alert-text',
27639 cfg.cn[0].cls += ' fa ' + this.faicon;
27643 cfg.cls += ' alert-' + this.weight;
27649 initEvents: function()
27651 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27654 setTitle : function(str)
27656 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27659 setText : function(str)
27661 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27664 setWeight : function(weight)
27667 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27670 this.weight = weight;
27672 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27675 setIcon : function(icon)
27678 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27681 this.faicon = icon;
27683 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27704 * @class Roo.bootstrap.UploadCropbox
27705 * @extends Roo.bootstrap.Component
27706 * Bootstrap UploadCropbox class
27707 * @cfg {String} emptyText show when image has been loaded
27708 * @cfg {String} rotateNotify show when image too small to rotate
27709 * @cfg {Number} errorTimeout default 3000
27710 * @cfg {Number} minWidth default 300
27711 * @cfg {Number} minHeight default 300
27712 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27713 * @cfg {Boolean} isDocument (true|false) default false
27714 * @cfg {String} url action url
27715 * @cfg {String} paramName default 'imageUpload'
27716 * @cfg {String} method default POST
27717 * @cfg {Boolean} loadMask (true|false) default true
27718 * @cfg {Boolean} loadingText default 'Loading...'
27721 * Create a new UploadCropbox
27722 * @param {Object} config The config object
27725 Roo.bootstrap.UploadCropbox = function(config){
27726 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27730 * @event beforeselectfile
27731 * Fire before select file
27732 * @param {Roo.bootstrap.UploadCropbox} this
27734 "beforeselectfile" : true,
27737 * Fire after initEvent
27738 * @param {Roo.bootstrap.UploadCropbox} this
27743 * Fire after initEvent
27744 * @param {Roo.bootstrap.UploadCropbox} this
27745 * @param {String} data
27750 * Fire when preparing the file data
27751 * @param {Roo.bootstrap.UploadCropbox} this
27752 * @param {Object} file
27757 * Fire when get exception
27758 * @param {Roo.bootstrap.UploadCropbox} this
27759 * @param {XMLHttpRequest} xhr
27761 "exception" : true,
27763 * @event beforeloadcanvas
27764 * Fire before load the canvas
27765 * @param {Roo.bootstrap.UploadCropbox} this
27766 * @param {String} src
27768 "beforeloadcanvas" : true,
27771 * Fire when trash image
27772 * @param {Roo.bootstrap.UploadCropbox} this
27777 * Fire when download the image
27778 * @param {Roo.bootstrap.UploadCropbox} this
27782 * @event footerbuttonclick
27783 * Fire when footerbuttonclick
27784 * @param {Roo.bootstrap.UploadCropbox} this
27785 * @param {String} type
27787 "footerbuttonclick" : true,
27791 * @param {Roo.bootstrap.UploadCropbox} this
27796 * Fire when rotate the image
27797 * @param {Roo.bootstrap.UploadCropbox} this
27798 * @param {String} pos
27803 * Fire when inspect the file
27804 * @param {Roo.bootstrap.UploadCropbox} this
27805 * @param {Object} file
27810 * Fire when xhr upload the file
27811 * @param {Roo.bootstrap.UploadCropbox} this
27812 * @param {Object} data
27817 * Fire when arrange the file data
27818 * @param {Roo.bootstrap.UploadCropbox} this
27819 * @param {Object} formData
27824 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27827 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27829 emptyText : 'Click to upload image',
27830 rotateNotify : 'Image is too small to rotate',
27831 errorTimeout : 3000,
27845 cropType : 'image/jpeg',
27847 canvasLoaded : false,
27848 isDocument : false,
27850 paramName : 'imageUpload',
27852 loadingText : 'Loading...',
27855 getAutoCreate : function()
27859 cls : 'roo-upload-cropbox',
27863 cls : 'roo-upload-cropbox-selector',
27868 cls : 'roo-upload-cropbox-body',
27869 style : 'cursor:pointer',
27873 cls : 'roo-upload-cropbox-preview'
27877 cls : 'roo-upload-cropbox-thumb'
27881 cls : 'roo-upload-cropbox-empty-notify',
27882 html : this.emptyText
27886 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27887 html : this.rotateNotify
27893 cls : 'roo-upload-cropbox-footer',
27896 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27906 onRender : function(ct, position)
27908 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27910 if (this.buttons.length) {
27912 Roo.each(this.buttons, function(bb) {
27914 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27916 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27922 this.maskEl = this.el;
27926 initEvents : function()
27928 this.urlAPI = (window.createObjectURL && window) ||
27929 (window.URL && URL.revokeObjectURL && URL) ||
27930 (window.webkitURL && webkitURL);
27932 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27933 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27935 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27936 this.selectorEl.hide();
27938 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27939 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27941 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27942 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27943 this.thumbEl.hide();
27945 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27946 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27948 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27949 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27950 this.errorEl.hide();
27952 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27953 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27954 this.footerEl.hide();
27956 this.setThumbBoxSize();
27962 this.fireEvent('initial', this);
27969 window.addEventListener("resize", function() { _this.resize(); } );
27971 this.bodyEl.on('click', this.beforeSelectFile, this);
27974 this.bodyEl.on('touchstart', this.onTouchStart, this);
27975 this.bodyEl.on('touchmove', this.onTouchMove, this);
27976 this.bodyEl.on('touchend', this.onTouchEnd, this);
27980 this.bodyEl.on('mousedown', this.onMouseDown, this);
27981 this.bodyEl.on('mousemove', this.onMouseMove, this);
27982 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27983 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27984 Roo.get(document).on('mouseup', this.onMouseUp, this);
27987 this.selectorEl.on('change', this.onFileSelected, this);
27993 this.baseScale = 1;
27995 this.baseRotate = 1;
27996 this.dragable = false;
27997 this.pinching = false;
28000 this.cropData = false;
28001 this.notifyEl.dom.innerHTML = this.emptyText;
28003 this.selectorEl.dom.value = '';
28007 resize : function()
28009 if(this.fireEvent('resize', this) != false){
28010 this.setThumbBoxPosition();
28011 this.setCanvasPosition();
28015 onFooterButtonClick : function(e, el, o, type)
28018 case 'rotate-left' :
28019 this.onRotateLeft(e);
28021 case 'rotate-right' :
28022 this.onRotateRight(e);
28025 this.beforeSelectFile(e);
28040 this.fireEvent('footerbuttonclick', this, type);
28043 beforeSelectFile : function(e)
28045 e.preventDefault();
28047 if(this.fireEvent('beforeselectfile', this) != false){
28048 this.selectorEl.dom.click();
28052 onFileSelected : function(e)
28054 e.preventDefault();
28056 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28060 var file = this.selectorEl.dom.files[0];
28062 if(this.fireEvent('inspect', this, file) != false){
28063 this.prepare(file);
28068 trash : function(e)
28070 this.fireEvent('trash', this);
28073 download : function(e)
28075 this.fireEvent('download', this);
28078 loadCanvas : function(src)
28080 if(this.fireEvent('beforeloadcanvas', this, src) != false){
28084 this.imageEl = document.createElement('img');
28088 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
28090 this.imageEl.src = src;
28094 onLoadCanvas : function()
28096 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
28097 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
28099 this.bodyEl.un('click', this.beforeSelectFile, this);
28101 this.notifyEl.hide();
28102 this.thumbEl.show();
28103 this.footerEl.show();
28105 this.baseRotateLevel();
28107 if(this.isDocument){
28108 this.setThumbBoxSize();
28111 this.setThumbBoxPosition();
28113 this.baseScaleLevel();
28119 this.canvasLoaded = true;
28122 this.maskEl.unmask();
28127 setCanvasPosition : function()
28129 if(!this.canvasEl){
28133 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
28134 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
28136 this.previewEl.setLeft(pw);
28137 this.previewEl.setTop(ph);
28141 onMouseDown : function(e)
28145 this.dragable = true;
28146 this.pinching = false;
28148 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
28149 this.dragable = false;
28153 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28154 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28158 onMouseMove : function(e)
28162 if(!this.canvasLoaded){
28166 if (!this.dragable){
28170 var minX = Math.ceil(this.thumbEl.getLeft(true));
28171 var minY = Math.ceil(this.thumbEl.getTop(true));
28173 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
28174 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
28176 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28177 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28179 x = x - this.mouseX;
28180 y = y - this.mouseY;
28182 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
28183 var bgY = Math.ceil(y + this.previewEl.getTop(true));
28185 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
28186 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
28188 this.previewEl.setLeft(bgX);
28189 this.previewEl.setTop(bgY);
28191 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28192 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28195 onMouseUp : function(e)
28199 this.dragable = false;
28202 onMouseWheel : function(e)
28206 this.startScale = this.scale;
28208 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
28210 if(!this.zoomable()){
28211 this.scale = this.startScale;
28220 zoomable : function()
28222 var minScale = this.thumbEl.getWidth() / this.minWidth;
28224 if(this.minWidth < this.minHeight){
28225 minScale = this.thumbEl.getHeight() / this.minHeight;
28228 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
28229 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
28233 (this.rotate == 0 || this.rotate == 180) &&
28235 width > this.imageEl.OriginWidth ||
28236 height > this.imageEl.OriginHeight ||
28237 (width < this.minWidth && height < this.minHeight)
28245 (this.rotate == 90 || this.rotate == 270) &&
28247 width > this.imageEl.OriginWidth ||
28248 height > this.imageEl.OriginHeight ||
28249 (width < this.minHeight && height < this.minWidth)
28256 !this.isDocument &&
28257 (this.rotate == 0 || this.rotate == 180) &&
28259 width < this.minWidth ||
28260 width > this.imageEl.OriginWidth ||
28261 height < this.minHeight ||
28262 height > this.imageEl.OriginHeight
28269 !this.isDocument &&
28270 (this.rotate == 90 || this.rotate == 270) &&
28272 width < this.minHeight ||
28273 width > this.imageEl.OriginWidth ||
28274 height < this.minWidth ||
28275 height > this.imageEl.OriginHeight
28285 onRotateLeft : function(e)
28287 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28289 var minScale = this.thumbEl.getWidth() / this.minWidth;
28291 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28292 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28294 this.startScale = this.scale;
28296 while (this.getScaleLevel() < minScale){
28298 this.scale = this.scale + 1;
28300 if(!this.zoomable()){
28305 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28306 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28311 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28318 this.scale = this.startScale;
28320 this.onRotateFail();
28325 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28327 if(this.isDocument){
28328 this.setThumbBoxSize();
28329 this.setThumbBoxPosition();
28330 this.setCanvasPosition();
28335 this.fireEvent('rotate', this, 'left');
28339 onRotateRight : function(e)
28341 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28343 var minScale = this.thumbEl.getWidth() / this.minWidth;
28345 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28346 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28348 this.startScale = this.scale;
28350 while (this.getScaleLevel() < minScale){
28352 this.scale = this.scale + 1;
28354 if(!this.zoomable()){
28359 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28360 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28365 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28372 this.scale = this.startScale;
28374 this.onRotateFail();
28379 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28381 if(this.isDocument){
28382 this.setThumbBoxSize();
28383 this.setThumbBoxPosition();
28384 this.setCanvasPosition();
28389 this.fireEvent('rotate', this, 'right');
28392 onRotateFail : function()
28394 this.errorEl.show(true);
28398 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28403 this.previewEl.dom.innerHTML = '';
28405 var canvasEl = document.createElement("canvas");
28407 var contextEl = canvasEl.getContext("2d");
28409 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28410 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28411 var center = this.imageEl.OriginWidth / 2;
28413 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28414 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28415 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28416 center = this.imageEl.OriginHeight / 2;
28419 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28421 contextEl.translate(center, center);
28422 contextEl.rotate(this.rotate * Math.PI / 180);
28424 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28426 this.canvasEl = document.createElement("canvas");
28428 this.contextEl = this.canvasEl.getContext("2d");
28430 switch (this.rotate) {
28433 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28434 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28436 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28441 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28442 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28444 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28445 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);
28449 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28454 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28455 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28457 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28458 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);
28462 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);
28467 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28468 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28470 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28471 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28475 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);
28482 this.previewEl.appendChild(this.canvasEl);
28484 this.setCanvasPosition();
28489 if(!this.canvasLoaded){
28493 var imageCanvas = document.createElement("canvas");
28495 var imageContext = imageCanvas.getContext("2d");
28497 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28498 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28500 var center = imageCanvas.width / 2;
28502 imageContext.translate(center, center);
28504 imageContext.rotate(this.rotate * Math.PI / 180);
28506 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28508 var canvas = document.createElement("canvas");
28510 var context = canvas.getContext("2d");
28512 canvas.width = this.minWidth;
28513 canvas.height = this.minHeight;
28515 switch (this.rotate) {
28518 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28519 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28521 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28522 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28524 var targetWidth = this.minWidth - 2 * x;
28525 var targetHeight = this.minHeight - 2 * y;
28529 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28530 scale = targetWidth / width;
28533 if(x > 0 && y == 0){
28534 scale = targetHeight / height;
28537 if(x > 0 && y > 0){
28538 scale = targetWidth / width;
28540 if(width < height){
28541 scale = targetHeight / height;
28545 context.scale(scale, scale);
28547 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28548 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28550 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28551 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28553 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28558 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28559 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28561 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28562 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28564 var targetWidth = this.minWidth - 2 * x;
28565 var targetHeight = this.minHeight - 2 * y;
28569 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28570 scale = targetWidth / width;
28573 if(x > 0 && y == 0){
28574 scale = targetHeight / height;
28577 if(x > 0 && y > 0){
28578 scale = targetWidth / width;
28580 if(width < height){
28581 scale = targetHeight / height;
28585 context.scale(scale, scale);
28587 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28588 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28590 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28591 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28593 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28595 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28600 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28601 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28603 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28604 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28606 var targetWidth = this.minWidth - 2 * x;
28607 var targetHeight = this.minHeight - 2 * y;
28611 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28612 scale = targetWidth / width;
28615 if(x > 0 && y == 0){
28616 scale = targetHeight / height;
28619 if(x > 0 && y > 0){
28620 scale = targetWidth / width;
28622 if(width < height){
28623 scale = targetHeight / height;
28627 context.scale(scale, scale);
28629 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28630 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28632 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28633 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28635 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28636 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28638 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28643 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28644 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28646 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28647 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28649 var targetWidth = this.minWidth - 2 * x;
28650 var targetHeight = this.minHeight - 2 * y;
28654 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28655 scale = targetWidth / width;
28658 if(x > 0 && y == 0){
28659 scale = targetHeight / height;
28662 if(x > 0 && y > 0){
28663 scale = targetWidth / width;
28665 if(width < height){
28666 scale = targetHeight / height;
28670 context.scale(scale, scale);
28672 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28673 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28675 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28676 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28678 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28680 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28687 this.cropData = canvas.toDataURL(this.cropType);
28689 if(this.fireEvent('crop', this, this.cropData) !== false){
28690 this.process(this.file, this.cropData);
28697 setThumbBoxSize : function()
28701 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28702 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28703 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28705 this.minWidth = width;
28706 this.minHeight = height;
28708 if(this.rotate == 90 || this.rotate == 270){
28709 this.minWidth = height;
28710 this.minHeight = width;
28715 width = Math.ceil(this.minWidth * height / this.minHeight);
28717 if(this.minWidth > this.minHeight){
28719 height = Math.ceil(this.minHeight * width / this.minWidth);
28722 this.thumbEl.setStyle({
28723 width : width + 'px',
28724 height : height + 'px'
28731 setThumbBoxPosition : function()
28733 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28734 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28736 this.thumbEl.setLeft(x);
28737 this.thumbEl.setTop(y);
28741 baseRotateLevel : function()
28743 this.baseRotate = 1;
28746 typeof(this.exif) != 'undefined' &&
28747 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28748 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28750 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28753 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28757 baseScaleLevel : function()
28761 if(this.isDocument){
28763 if(this.baseRotate == 6 || this.baseRotate == 8){
28765 height = this.thumbEl.getHeight();
28766 this.baseScale = height / this.imageEl.OriginWidth;
28768 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28769 width = this.thumbEl.getWidth();
28770 this.baseScale = width / this.imageEl.OriginHeight;
28776 height = this.thumbEl.getHeight();
28777 this.baseScale = height / this.imageEl.OriginHeight;
28779 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28780 width = this.thumbEl.getWidth();
28781 this.baseScale = width / this.imageEl.OriginWidth;
28787 if(this.baseRotate == 6 || this.baseRotate == 8){
28789 width = this.thumbEl.getHeight();
28790 this.baseScale = width / this.imageEl.OriginHeight;
28792 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28793 height = this.thumbEl.getWidth();
28794 this.baseScale = height / this.imageEl.OriginHeight;
28797 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28798 height = this.thumbEl.getWidth();
28799 this.baseScale = height / this.imageEl.OriginHeight;
28801 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28802 width = this.thumbEl.getHeight();
28803 this.baseScale = width / this.imageEl.OriginWidth;
28810 width = this.thumbEl.getWidth();
28811 this.baseScale = width / this.imageEl.OriginWidth;
28813 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28814 height = this.thumbEl.getHeight();
28815 this.baseScale = height / this.imageEl.OriginHeight;
28818 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28820 height = this.thumbEl.getHeight();
28821 this.baseScale = height / this.imageEl.OriginHeight;
28823 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28824 width = this.thumbEl.getWidth();
28825 this.baseScale = width / this.imageEl.OriginWidth;
28833 getScaleLevel : function()
28835 return this.baseScale * Math.pow(1.1, this.scale);
28838 onTouchStart : function(e)
28840 if(!this.canvasLoaded){
28841 this.beforeSelectFile(e);
28845 var touches = e.browserEvent.touches;
28851 if(touches.length == 1){
28852 this.onMouseDown(e);
28856 if(touches.length != 2){
28862 for(var i = 0, finger; finger = touches[i]; i++){
28863 coords.push(finger.pageX, finger.pageY);
28866 var x = Math.pow(coords[0] - coords[2], 2);
28867 var y = Math.pow(coords[1] - coords[3], 2);
28869 this.startDistance = Math.sqrt(x + y);
28871 this.startScale = this.scale;
28873 this.pinching = true;
28874 this.dragable = false;
28878 onTouchMove : function(e)
28880 if(!this.pinching && !this.dragable){
28884 var touches = e.browserEvent.touches;
28891 this.onMouseMove(e);
28897 for(var i = 0, finger; finger = touches[i]; i++){
28898 coords.push(finger.pageX, finger.pageY);
28901 var x = Math.pow(coords[0] - coords[2], 2);
28902 var y = Math.pow(coords[1] - coords[3], 2);
28904 this.endDistance = Math.sqrt(x + y);
28906 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28908 if(!this.zoomable()){
28909 this.scale = this.startScale;
28917 onTouchEnd : function(e)
28919 this.pinching = false;
28920 this.dragable = false;
28924 process : function(file, crop)
28927 this.maskEl.mask(this.loadingText);
28930 this.xhr = new XMLHttpRequest();
28932 file.xhr = this.xhr;
28934 this.xhr.open(this.method, this.url, true);
28937 "Accept": "application/json",
28938 "Cache-Control": "no-cache",
28939 "X-Requested-With": "XMLHttpRequest"
28942 for (var headerName in headers) {
28943 var headerValue = headers[headerName];
28945 this.xhr.setRequestHeader(headerName, headerValue);
28951 this.xhr.onload = function()
28953 _this.xhrOnLoad(_this.xhr);
28956 this.xhr.onerror = function()
28958 _this.xhrOnError(_this.xhr);
28961 var formData = new FormData();
28963 formData.append('returnHTML', 'NO');
28966 formData.append('crop', crop);
28969 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28970 formData.append(this.paramName, file, file.name);
28973 if(typeof(file.filename) != 'undefined'){
28974 formData.append('filename', file.filename);
28977 if(typeof(file.mimetype) != 'undefined'){
28978 formData.append('mimetype', file.mimetype);
28981 if(this.fireEvent('arrange', this, formData) != false){
28982 this.xhr.send(formData);
28986 xhrOnLoad : function(xhr)
28989 this.maskEl.unmask();
28992 if (xhr.readyState !== 4) {
28993 this.fireEvent('exception', this, xhr);
28997 var response = Roo.decode(xhr.responseText);
28999 if(!response.success){
29000 this.fireEvent('exception', this, xhr);
29004 var response = Roo.decode(xhr.responseText);
29006 this.fireEvent('upload', this, response);
29010 xhrOnError : function()
29013 this.maskEl.unmask();
29016 Roo.log('xhr on error');
29018 var response = Roo.decode(xhr.responseText);
29024 prepare : function(file)
29027 this.maskEl.mask(this.loadingText);
29033 if(typeof(file) === 'string'){
29034 this.loadCanvas(file);
29038 if(!file || !this.urlAPI){
29043 this.cropType = file.type;
29047 if(this.fireEvent('prepare', this, this.file) != false){
29049 var reader = new FileReader();
29051 reader.onload = function (e) {
29052 if (e.target.error) {
29053 Roo.log(e.target.error);
29057 var buffer = e.target.result,
29058 dataView = new DataView(buffer),
29060 maxOffset = dataView.byteLength - 4,
29064 if (dataView.getUint16(0) === 0xffd8) {
29065 while (offset < maxOffset) {
29066 markerBytes = dataView.getUint16(offset);
29068 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
29069 markerLength = dataView.getUint16(offset + 2) + 2;
29070 if (offset + markerLength > dataView.byteLength) {
29071 Roo.log('Invalid meta data: Invalid segment size.');
29075 if(markerBytes == 0xffe1){
29076 _this.parseExifData(
29083 offset += markerLength;
29093 var url = _this.urlAPI.createObjectURL(_this.file);
29095 _this.loadCanvas(url);
29100 reader.readAsArrayBuffer(this.file);
29106 parseExifData : function(dataView, offset, length)
29108 var tiffOffset = offset + 10,
29112 if (dataView.getUint32(offset + 4) !== 0x45786966) {
29113 // No Exif data, might be XMP data instead
29117 // Check for the ASCII code for "Exif" (0x45786966):
29118 if (dataView.getUint32(offset + 4) !== 0x45786966) {
29119 // No Exif data, might be XMP data instead
29122 if (tiffOffset + 8 > dataView.byteLength) {
29123 Roo.log('Invalid Exif data: Invalid segment size.');
29126 // Check for the two null bytes:
29127 if (dataView.getUint16(offset + 8) !== 0x0000) {
29128 Roo.log('Invalid Exif data: Missing byte alignment offset.');
29131 // Check the byte alignment:
29132 switch (dataView.getUint16(tiffOffset)) {
29134 littleEndian = true;
29137 littleEndian = false;
29140 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
29143 // Check for the TIFF tag marker (0x002A):
29144 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
29145 Roo.log('Invalid Exif data: Missing TIFF marker.');
29148 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
29149 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
29151 this.parseExifTags(
29154 tiffOffset + dirOffset,
29159 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
29164 if (dirOffset + 6 > dataView.byteLength) {
29165 Roo.log('Invalid Exif data: Invalid directory offset.');
29168 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
29169 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
29170 if (dirEndOffset + 4 > dataView.byteLength) {
29171 Roo.log('Invalid Exif data: Invalid directory size.');
29174 for (i = 0; i < tagsNumber; i += 1) {
29178 dirOffset + 2 + 12 * i, // tag offset
29182 // Return the offset to the next directory:
29183 return dataView.getUint32(dirEndOffset, littleEndian);
29186 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
29188 var tag = dataView.getUint16(offset, littleEndian);
29190 this.exif[tag] = this.getExifValue(
29194 dataView.getUint16(offset + 2, littleEndian), // tag type
29195 dataView.getUint32(offset + 4, littleEndian), // tag length
29200 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
29202 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
29211 Roo.log('Invalid Exif data: Invalid tag type.');
29215 tagSize = tagType.size * length;
29216 // Determine if the value is contained in the dataOffset bytes,
29217 // or if the value at the dataOffset is a pointer to the actual data:
29218 dataOffset = tagSize > 4 ?
29219 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
29220 if (dataOffset + tagSize > dataView.byteLength) {
29221 Roo.log('Invalid Exif data: Invalid data offset.');
29224 if (length === 1) {
29225 return tagType.getValue(dataView, dataOffset, littleEndian);
29228 for (i = 0; i < length; i += 1) {
29229 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
29232 if (tagType.ascii) {
29234 // Concatenate the chars:
29235 for (i = 0; i < values.length; i += 1) {
29237 // Ignore the terminating NULL byte(s):
29238 if (c === '\u0000') {
29250 Roo.apply(Roo.bootstrap.UploadCropbox, {
29252 'Orientation': 0x0112
29256 1: 0, //'top-left',
29258 3: 180, //'bottom-right',
29259 // 4: 'bottom-left',
29261 6: 90, //'right-top',
29262 // 7: 'right-bottom',
29263 8: 270 //'left-bottom'
29267 // byte, 8-bit unsigned int:
29269 getValue: function (dataView, dataOffset) {
29270 return dataView.getUint8(dataOffset);
29274 // ascii, 8-bit byte:
29276 getValue: function (dataView, dataOffset) {
29277 return String.fromCharCode(dataView.getUint8(dataOffset));
29282 // short, 16 bit int:
29284 getValue: function (dataView, dataOffset, littleEndian) {
29285 return dataView.getUint16(dataOffset, littleEndian);
29289 // long, 32 bit int:
29291 getValue: function (dataView, dataOffset, littleEndian) {
29292 return dataView.getUint32(dataOffset, littleEndian);
29296 // rational = two long values, first is numerator, second is denominator:
29298 getValue: function (dataView, dataOffset, littleEndian) {
29299 return dataView.getUint32(dataOffset, littleEndian) /
29300 dataView.getUint32(dataOffset + 4, littleEndian);
29304 // slong, 32 bit signed int:
29306 getValue: function (dataView, dataOffset, littleEndian) {
29307 return dataView.getInt32(dataOffset, littleEndian);
29311 // srational, two slongs, first is numerator, second is denominator:
29313 getValue: function (dataView, dataOffset, littleEndian) {
29314 return dataView.getInt32(dataOffset, littleEndian) /
29315 dataView.getInt32(dataOffset + 4, littleEndian);
29325 cls : 'btn-group roo-upload-cropbox-rotate-left',
29326 action : 'rotate-left',
29330 cls : 'btn btn-default',
29331 html : '<i class="fa fa-undo"></i>'
29337 cls : 'btn-group roo-upload-cropbox-picture',
29338 action : 'picture',
29342 cls : 'btn btn-default',
29343 html : '<i class="fa fa-picture-o"></i>'
29349 cls : 'btn-group roo-upload-cropbox-rotate-right',
29350 action : 'rotate-right',
29354 cls : 'btn btn-default',
29355 html : '<i class="fa fa-repeat"></i>'
29363 cls : 'btn-group roo-upload-cropbox-rotate-left',
29364 action : 'rotate-left',
29368 cls : 'btn btn-default',
29369 html : '<i class="fa fa-undo"></i>'
29375 cls : 'btn-group roo-upload-cropbox-download',
29376 action : 'download',
29380 cls : 'btn btn-default',
29381 html : '<i class="fa fa-download"></i>'
29387 cls : 'btn-group roo-upload-cropbox-crop',
29392 cls : 'btn btn-default',
29393 html : '<i class="fa fa-crop"></i>'
29399 cls : 'btn-group roo-upload-cropbox-trash',
29404 cls : 'btn btn-default',
29405 html : '<i class="fa fa-trash"></i>'
29411 cls : 'btn-group roo-upload-cropbox-rotate-right',
29412 action : 'rotate-right',
29416 cls : 'btn btn-default',
29417 html : '<i class="fa fa-repeat"></i>'
29425 cls : 'btn-group roo-upload-cropbox-rotate-left',
29426 action : 'rotate-left',
29430 cls : 'btn btn-default',
29431 html : '<i class="fa fa-undo"></i>'
29437 cls : 'btn-group roo-upload-cropbox-rotate-right',
29438 action : 'rotate-right',
29442 cls : 'btn btn-default',
29443 html : '<i class="fa fa-repeat"></i>'
29456 * @class Roo.bootstrap.DocumentManager
29457 * @extends Roo.bootstrap.Component
29458 * Bootstrap DocumentManager class
29459 * @cfg {String} paramName default 'imageUpload'
29460 * @cfg {String} toolTipName default 'filename'
29461 * @cfg {String} method default POST
29462 * @cfg {String} url action url
29463 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29464 * @cfg {Boolean} multiple multiple upload default true
29465 * @cfg {Number} thumbSize default 300
29466 * @cfg {String} fieldLabel
29467 * @cfg {Number} labelWidth default 4
29468 * @cfg {String} labelAlign (left|top) default left
29469 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29470 * @cfg {Number} labellg set the width of label (1-12)
29471 * @cfg {Number} labelmd set the width of label (1-12)
29472 * @cfg {Number} labelsm set the width of label (1-12)
29473 * @cfg {Number} labelxs set the width of label (1-12)
29476 * Create a new DocumentManager
29477 * @param {Object} config The config object
29480 Roo.bootstrap.DocumentManager = function(config){
29481 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29484 this.delegates = [];
29489 * Fire when initial the DocumentManager
29490 * @param {Roo.bootstrap.DocumentManager} this
29495 * inspect selected file
29496 * @param {Roo.bootstrap.DocumentManager} this
29497 * @param {File} file
29502 * Fire when xhr load exception
29503 * @param {Roo.bootstrap.DocumentManager} this
29504 * @param {XMLHttpRequest} xhr
29506 "exception" : true,
29508 * @event afterupload
29509 * Fire when xhr load exception
29510 * @param {Roo.bootstrap.DocumentManager} this
29511 * @param {XMLHttpRequest} xhr
29513 "afterupload" : true,
29516 * prepare the form data
29517 * @param {Roo.bootstrap.DocumentManager} this
29518 * @param {Object} formData
29523 * Fire when remove the file
29524 * @param {Roo.bootstrap.DocumentManager} this
29525 * @param {Object} file
29530 * Fire after refresh the file
29531 * @param {Roo.bootstrap.DocumentManager} this
29536 * Fire after click the image
29537 * @param {Roo.bootstrap.DocumentManager} this
29538 * @param {Object} file
29543 * Fire when upload a image and editable set to true
29544 * @param {Roo.bootstrap.DocumentManager} this
29545 * @param {Object} file
29549 * @event beforeselectfile
29550 * Fire before select file
29551 * @param {Roo.bootstrap.DocumentManager} this
29553 "beforeselectfile" : true,
29556 * Fire before process file
29557 * @param {Roo.bootstrap.DocumentManager} this
29558 * @param {Object} file
29562 * @event previewrendered
29563 * Fire when preview rendered
29564 * @param {Roo.bootstrap.DocumentManager} this
29565 * @param {Object} file
29567 "previewrendered" : true,
29570 "previewResize" : true
29575 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
29584 paramName : 'imageUpload',
29585 toolTipName : 'filename',
29588 labelAlign : 'left',
29598 getAutoCreate : function()
29600 var managerWidget = {
29602 cls : 'roo-document-manager',
29606 cls : 'roo-document-manager-selector',
29611 cls : 'roo-document-manager-uploader',
29615 cls : 'roo-document-manager-upload-btn',
29616 html : '<i class="fa fa-plus"></i>'
29627 cls : 'column col-md-12',
29632 if(this.fieldLabel.length){
29637 cls : 'column col-md-12',
29638 html : this.fieldLabel
29642 cls : 'column col-md-12',
29647 if(this.labelAlign == 'left'){
29652 html : this.fieldLabel
29661 if(this.labelWidth > 12){
29662 content[0].style = "width: " + this.labelWidth + 'px';
29665 if(this.labelWidth < 13 && this.labelmd == 0){
29666 this.labelmd = this.labelWidth;
29669 if(this.labellg > 0){
29670 content[0].cls += ' col-lg-' + this.labellg;
29671 content[1].cls += ' col-lg-' + (12 - this.labellg);
29674 if(this.labelmd > 0){
29675 content[0].cls += ' col-md-' + this.labelmd;
29676 content[1].cls += ' col-md-' + (12 - this.labelmd);
29679 if(this.labelsm > 0){
29680 content[0].cls += ' col-sm-' + this.labelsm;
29681 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29684 if(this.labelxs > 0){
29685 content[0].cls += ' col-xs-' + this.labelxs;
29686 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29694 cls : 'row clearfix',
29702 initEvents : function()
29704 this.managerEl = this.el.select('.roo-document-manager', true).first();
29705 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29707 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29708 this.selectorEl.hide();
29711 this.selectorEl.attr('multiple', 'multiple');
29714 this.selectorEl.on('change', this.onFileSelected, this);
29716 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29717 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29719 this.uploader.on('click', this.onUploaderClick, this);
29721 this.renderProgressDialog();
29725 window.addEventListener("resize", function() { _this.refresh(); } );
29727 this.fireEvent('initial', this);
29730 renderProgressDialog : function()
29734 this.progressDialog = new Roo.bootstrap.Modal({
29735 cls : 'roo-document-manager-progress-dialog',
29736 allow_close : false,
29747 btnclick : function() {
29748 _this.uploadCancel();
29754 this.progressDialog.render(Roo.get(document.body));
29756 this.progress = new Roo.bootstrap.Progress({
29757 cls : 'roo-document-manager-progress',
29762 this.progress.render(this.progressDialog.getChildContainer());
29764 this.progressBar = new Roo.bootstrap.ProgressBar({
29765 cls : 'roo-document-manager-progress-bar',
29768 aria_valuemax : 12,
29772 this.progressBar.render(this.progress.getChildContainer());
29775 onUploaderClick : function(e)
29777 e.preventDefault();
29779 if(this.fireEvent('beforeselectfile', this) != false){
29780 this.selectorEl.dom.click();
29785 onFileSelected : function(e)
29787 e.preventDefault();
29789 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29793 Roo.each(this.selectorEl.dom.files, function(file){
29794 if(this.fireEvent('inspect', this, file) != false){
29795 this.files.push(file);
29805 this.selectorEl.dom.value = '';
29807 if(!this.files || !this.files.length){
29811 if(this.boxes > 0 && this.files.length > this.boxes){
29812 this.files = this.files.slice(0, this.boxes);
29815 this.uploader.show();
29817 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29818 this.uploader.hide();
29827 Roo.each(this.files, function(file){
29829 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29830 var f = this.renderPreview(file);
29835 if(file.type.indexOf('image') != -1){
29836 this.delegates.push(
29838 _this.process(file);
29839 }).createDelegate(this)
29847 _this.process(file);
29848 }).createDelegate(this)
29853 this.files = files;
29855 this.delegates = this.delegates.concat(docs);
29857 if(!this.delegates.length){
29862 this.progressBar.aria_valuemax = this.delegates.length;
29869 arrange : function()
29871 if(!this.delegates.length){
29872 this.progressDialog.hide();
29877 var delegate = this.delegates.shift();
29879 this.progressDialog.show();
29881 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29883 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29888 refresh : function()
29890 this.uploader.show();
29892 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29893 this.uploader.hide();
29896 Roo.isTouch ? this.closable(false) : this.closable(true);
29898 this.fireEvent('refresh', this);
29901 onRemove : function(e, el, o)
29903 e.preventDefault();
29905 this.fireEvent('remove', this, o);
29909 remove : function(o)
29913 Roo.each(this.files, function(file){
29914 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29923 this.files = files;
29930 Roo.each(this.files, function(file){
29935 file.target.remove();
29944 onClick : function(e, el, o)
29946 e.preventDefault();
29948 this.fireEvent('click', this, o);
29952 closable : function(closable)
29954 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29956 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29968 xhrOnLoad : function(xhr)
29970 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29974 if (xhr.readyState !== 4) {
29976 this.fireEvent('exception', this, xhr);
29980 var response = Roo.decode(xhr.responseText);
29982 if(!response.success){
29984 this.fireEvent('exception', this, xhr);
29988 var file = this.renderPreview(response.data);
29990 this.files.push(file);
29994 this.fireEvent('afterupload', this, xhr);
29998 xhrOnError : function(xhr)
30000 Roo.log('xhr on error');
30002 var response = Roo.decode(xhr.responseText);
30009 process : function(file)
30011 if(this.fireEvent('process', this, file) !== false){
30012 if(this.editable && file.type.indexOf('image') != -1){
30013 this.fireEvent('edit', this, file);
30017 this.uploadStart(file, false);
30024 uploadStart : function(file, crop)
30026 this.xhr = new XMLHttpRequest();
30028 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30033 file.xhr = this.xhr;
30035 this.managerEl.createChild({
30037 cls : 'roo-document-manager-loading',
30041 tooltip : file.name,
30042 cls : 'roo-document-manager-thumb',
30043 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30049 this.xhr.open(this.method, this.url, true);
30052 "Accept": "application/json",
30053 "Cache-Control": "no-cache",
30054 "X-Requested-With": "XMLHttpRequest"
30057 for (var headerName in headers) {
30058 var headerValue = headers[headerName];
30060 this.xhr.setRequestHeader(headerName, headerValue);
30066 this.xhr.onload = function()
30068 _this.xhrOnLoad(_this.xhr);
30071 this.xhr.onerror = function()
30073 _this.xhrOnError(_this.xhr);
30076 var formData = new FormData();
30078 formData.append('returnHTML', 'NO');
30081 formData.append('crop', crop);
30084 formData.append(this.paramName, file, file.name);
30091 if(this.fireEvent('prepare', this, formData, options) != false){
30093 if(options.manually){
30097 this.xhr.send(formData);
30101 this.uploadCancel();
30104 uploadCancel : function()
30110 this.delegates = [];
30112 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30119 renderPreview : function(file)
30121 if(typeof(file.target) != 'undefined' && file.target){
30125 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
30127 var previewEl = this.managerEl.createChild({
30129 cls : 'roo-document-manager-preview',
30133 tooltip : file[this.toolTipName],
30134 cls : 'roo-document-manager-thumb',
30135 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
30140 html : '<i class="fa fa-times-circle"></i>'
30145 var close = previewEl.select('button.close', true).first();
30147 close.on('click', this.onRemove, this, file);
30149 file.target = previewEl;
30151 var image = previewEl.select('img', true).first();
30155 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
30157 image.on('click', this.onClick, this, file);
30159 this.fireEvent('previewrendered', this, file);
30165 onPreviewLoad : function(file, image)
30167 if(typeof(file.target) == 'undefined' || !file.target){
30171 var width = image.dom.naturalWidth || image.dom.width;
30172 var height = image.dom.naturalHeight || image.dom.height;
30174 if(!this.previewResize) {
30178 if(width > height){
30179 file.target.addClass('wide');
30183 file.target.addClass('tall');
30188 uploadFromSource : function(file, crop)
30190 this.xhr = new XMLHttpRequest();
30192 this.managerEl.createChild({
30194 cls : 'roo-document-manager-loading',
30198 tooltip : file.name,
30199 cls : 'roo-document-manager-thumb',
30200 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30206 this.xhr.open(this.method, this.url, true);
30209 "Accept": "application/json",
30210 "Cache-Control": "no-cache",
30211 "X-Requested-With": "XMLHttpRequest"
30214 for (var headerName in headers) {
30215 var headerValue = headers[headerName];
30217 this.xhr.setRequestHeader(headerName, headerValue);
30223 this.xhr.onload = function()
30225 _this.xhrOnLoad(_this.xhr);
30228 this.xhr.onerror = function()
30230 _this.xhrOnError(_this.xhr);
30233 var formData = new FormData();
30235 formData.append('returnHTML', 'NO');
30237 formData.append('crop', crop);
30239 if(typeof(file.filename) != 'undefined'){
30240 formData.append('filename', file.filename);
30243 if(typeof(file.mimetype) != 'undefined'){
30244 formData.append('mimetype', file.mimetype);
30249 if(this.fireEvent('prepare', this, formData) != false){
30250 this.xhr.send(formData);
30260 * @class Roo.bootstrap.DocumentViewer
30261 * @extends Roo.bootstrap.Component
30262 * Bootstrap DocumentViewer class
30263 * @cfg {Boolean} showDownload (true|false) show download button (default true)
30264 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30267 * Create a new DocumentViewer
30268 * @param {Object} config The config object
30271 Roo.bootstrap.DocumentViewer = function(config){
30272 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30277 * Fire after initEvent
30278 * @param {Roo.bootstrap.DocumentViewer} this
30284 * @param {Roo.bootstrap.DocumentViewer} this
30289 * Fire after download button
30290 * @param {Roo.bootstrap.DocumentViewer} this
30295 * Fire after trash button
30296 * @param {Roo.bootstrap.DocumentViewer} this
30303 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
30305 showDownload : true,
30309 getAutoCreate : function()
30313 cls : 'roo-document-viewer',
30317 cls : 'roo-document-viewer-body',
30321 cls : 'roo-document-viewer-thumb',
30325 cls : 'roo-document-viewer-image'
30333 cls : 'roo-document-viewer-footer',
30336 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
30340 cls : 'btn-group roo-document-viewer-download',
30344 cls : 'btn btn-default',
30345 html : '<i class="fa fa-download"></i>'
30351 cls : 'btn-group roo-document-viewer-trash',
30355 cls : 'btn btn-default',
30356 html : '<i class="fa fa-trash"></i>'
30369 initEvents : function()
30371 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30372 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30374 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30375 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30377 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30378 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30380 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30381 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30383 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30384 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30386 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30387 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30389 this.bodyEl.on('click', this.onClick, this);
30390 this.downloadBtn.on('click', this.onDownload, this);
30391 this.trashBtn.on('click', this.onTrash, this);
30393 this.downloadBtn.hide();
30394 this.trashBtn.hide();
30396 if(this.showDownload){
30397 this.downloadBtn.show();
30400 if(this.showTrash){
30401 this.trashBtn.show();
30404 if(!this.showDownload && !this.showTrash) {
30405 this.footerEl.hide();
30410 initial : function()
30412 this.fireEvent('initial', this);
30416 onClick : function(e)
30418 e.preventDefault();
30420 this.fireEvent('click', this);
30423 onDownload : function(e)
30425 e.preventDefault();
30427 this.fireEvent('download', this);
30430 onTrash : function(e)
30432 e.preventDefault();
30434 this.fireEvent('trash', this);
30446 * @class Roo.bootstrap.NavProgressBar
30447 * @extends Roo.bootstrap.Component
30448 * Bootstrap NavProgressBar class
30451 * Create a new nav progress bar
30452 * @param {Object} config The config object
30455 Roo.bootstrap.NavProgressBar = function(config){
30456 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30458 this.bullets = this.bullets || [];
30460 // Roo.bootstrap.NavProgressBar.register(this);
30464 * Fires when the active item changes
30465 * @param {Roo.bootstrap.NavProgressBar} this
30466 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30467 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
30474 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
30479 getAutoCreate : function()
30481 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30485 cls : 'roo-navigation-bar-group',
30489 cls : 'roo-navigation-top-bar'
30493 cls : 'roo-navigation-bullets-bar',
30497 cls : 'roo-navigation-bar'
30504 cls : 'roo-navigation-bottom-bar'
30514 initEvents: function()
30519 onRender : function(ct, position)
30521 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30523 if(this.bullets.length){
30524 Roo.each(this.bullets, function(b){
30533 addItem : function(cfg)
30535 var item = new Roo.bootstrap.NavProgressItem(cfg);
30537 item.parentId = this.id;
30538 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30541 var top = new Roo.bootstrap.Element({
30543 cls : 'roo-navigation-bar-text'
30546 var bottom = new Roo.bootstrap.Element({
30548 cls : 'roo-navigation-bar-text'
30551 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30552 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30554 var topText = new Roo.bootstrap.Element({
30556 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30559 var bottomText = new Roo.bootstrap.Element({
30561 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30564 topText.onRender(top.el, null);
30565 bottomText.onRender(bottom.el, null);
30568 item.bottomEl = bottom;
30571 this.barItems.push(item);
30576 getActive : function()
30578 var active = false;
30580 Roo.each(this.barItems, function(v){
30582 if (!v.isActive()) {
30594 setActiveItem : function(item)
30598 Roo.each(this.barItems, function(v){
30599 if (v.rid == item.rid) {
30603 if (v.isActive()) {
30604 v.setActive(false);
30609 item.setActive(true);
30611 this.fireEvent('changed', this, item, prev);
30614 getBarItem: function(rid)
30618 Roo.each(this.barItems, function(e) {
30619 if (e.rid != rid) {
30630 indexOfItem : function(item)
30634 Roo.each(this.barItems, function(v, i){
30636 if (v.rid != item.rid) {
30647 setActiveNext : function()
30649 var i = this.indexOfItem(this.getActive());
30651 if (i > this.barItems.length) {
30655 this.setActiveItem(this.barItems[i+1]);
30658 setActivePrev : function()
30660 var i = this.indexOfItem(this.getActive());
30666 this.setActiveItem(this.barItems[i-1]);
30669 format : function()
30671 if(!this.barItems.length){
30675 var width = 100 / this.barItems.length;
30677 Roo.each(this.barItems, function(i){
30678 i.el.setStyle('width', width + '%');
30679 i.topEl.el.setStyle('width', width + '%');
30680 i.bottomEl.el.setStyle('width', width + '%');
30689 * Nav Progress Item
30694 * @class Roo.bootstrap.NavProgressItem
30695 * @extends Roo.bootstrap.Component
30696 * Bootstrap NavProgressItem class
30697 * @cfg {String} rid the reference id
30698 * @cfg {Boolean} active (true|false) Is item active default false
30699 * @cfg {Boolean} disabled (true|false) Is item active default false
30700 * @cfg {String} html
30701 * @cfg {String} position (top|bottom) text position default bottom
30702 * @cfg {String} icon show icon instead of number
30705 * Create a new NavProgressItem
30706 * @param {Object} config The config object
30708 Roo.bootstrap.NavProgressItem = function(config){
30709 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30714 * The raw click event for the entire grid.
30715 * @param {Roo.bootstrap.NavProgressItem} this
30716 * @param {Roo.EventObject} e
30723 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30729 position : 'bottom',
30732 getAutoCreate : function()
30734 var iconCls = 'roo-navigation-bar-item-icon';
30736 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30740 cls: 'roo-navigation-bar-item',
30750 cfg.cls += ' active';
30753 cfg.cls += ' disabled';
30759 disable : function()
30761 this.setDisabled(true);
30764 enable : function()
30766 this.setDisabled(false);
30769 initEvents: function()
30771 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30773 this.iconEl.on('click', this.onClick, this);
30776 onClick : function(e)
30778 e.preventDefault();
30784 if(this.fireEvent('click', this, e) === false){
30788 this.parent().setActiveItem(this);
30791 isActive: function ()
30793 return this.active;
30796 setActive : function(state)
30798 if(this.active == state){
30802 this.active = state;
30805 this.el.addClass('active');
30809 this.el.removeClass('active');
30814 setDisabled : function(state)
30816 if(this.disabled == state){
30820 this.disabled = state;
30823 this.el.addClass('disabled');
30827 this.el.removeClass('disabled');
30830 tooltipEl : function()
30832 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30845 * @class Roo.bootstrap.FieldLabel
30846 * @extends Roo.bootstrap.Component
30847 * Bootstrap FieldLabel class
30848 * @cfg {String} html contents of the element
30849 * @cfg {String} tag tag of the element default label
30850 * @cfg {String} cls class of the element
30851 * @cfg {String} target label target
30852 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30853 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30854 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30855 * @cfg {String} iconTooltip default "This field is required"
30856 * @cfg {String} indicatorpos (left|right) default left
30859 * Create a new FieldLabel
30860 * @param {Object} config The config object
30863 Roo.bootstrap.FieldLabel = function(config){
30864 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30869 * Fires after the field has been marked as invalid.
30870 * @param {Roo.form.FieldLabel} this
30871 * @param {String} msg The validation message
30876 * Fires after the field has been validated with no errors.
30877 * @param {Roo.form.FieldLabel} this
30883 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30890 invalidClass : 'has-warning',
30891 validClass : 'has-success',
30892 iconTooltip : 'This field is required',
30893 indicatorpos : 'left',
30895 getAutoCreate : function(){
30898 if (!this.allowBlank) {
30904 cls : 'roo-bootstrap-field-label ' + this.cls,
30909 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30910 tooltip : this.iconTooltip
30919 if(this.indicatorpos == 'right'){
30922 cls : 'roo-bootstrap-field-label ' + this.cls,
30931 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30932 tooltip : this.iconTooltip
30941 initEvents: function()
30943 Roo.bootstrap.Element.superclass.initEvents.call(this);
30945 this.indicator = this.indicatorEl();
30947 if(this.indicator){
30948 this.indicator.removeClass('visible');
30949 this.indicator.addClass('invisible');
30952 Roo.bootstrap.FieldLabel.register(this);
30955 indicatorEl : function()
30957 var indicator = this.el.select('i.roo-required-indicator',true).first();
30968 * Mark this field as valid
30970 markValid : function()
30972 if(this.indicator){
30973 this.indicator.removeClass('visible');
30974 this.indicator.addClass('invisible');
30976 if (Roo.bootstrap.version == 3) {
30977 this.el.removeClass(this.invalidClass);
30978 this.el.addClass(this.validClass);
30980 this.el.removeClass('is-invalid');
30981 this.el.addClass('is-valid');
30985 this.fireEvent('valid', this);
30989 * Mark this field as invalid
30990 * @param {String} msg The validation message
30992 markInvalid : function(msg)
30994 if(this.indicator){
30995 this.indicator.removeClass('invisible');
30996 this.indicator.addClass('visible');
30998 if (Roo.bootstrap.version == 3) {
30999 this.el.removeClass(this.validClass);
31000 this.el.addClass(this.invalidClass);
31002 this.el.removeClass('is-valid');
31003 this.el.addClass('is-invalid');
31007 this.fireEvent('invalid', this, msg);
31013 Roo.apply(Roo.bootstrap.FieldLabel, {
31018 * register a FieldLabel Group
31019 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
31021 register : function(label)
31023 if(this.groups.hasOwnProperty(label.target)){
31027 this.groups[label.target] = label;
31031 * fetch a FieldLabel Group based on the target
31032 * @param {string} target
31033 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
31035 get: function(target) {
31036 if (typeof(this.groups[target]) == 'undefined') {
31040 return this.groups[target] ;
31049 * page DateSplitField.
31055 * @class Roo.bootstrap.DateSplitField
31056 * @extends Roo.bootstrap.Component
31057 * Bootstrap DateSplitField class
31058 * @cfg {string} fieldLabel - the label associated
31059 * @cfg {Number} labelWidth set the width of label (0-12)
31060 * @cfg {String} labelAlign (top|left)
31061 * @cfg {Boolean} dayAllowBlank (true|false) default false
31062 * @cfg {Boolean} monthAllowBlank (true|false) default false
31063 * @cfg {Boolean} yearAllowBlank (true|false) default false
31064 * @cfg {string} dayPlaceholder
31065 * @cfg {string} monthPlaceholder
31066 * @cfg {string} yearPlaceholder
31067 * @cfg {string} dayFormat default 'd'
31068 * @cfg {string} monthFormat default 'm'
31069 * @cfg {string} yearFormat default 'Y'
31070 * @cfg {Number} labellg set the width of label (1-12)
31071 * @cfg {Number} labelmd set the width of label (1-12)
31072 * @cfg {Number} labelsm set the width of label (1-12)
31073 * @cfg {Number} labelxs set the width of label (1-12)
31077 * Create a new DateSplitField
31078 * @param {Object} config The config object
31081 Roo.bootstrap.DateSplitField = function(config){
31082 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
31088 * getting the data of years
31089 * @param {Roo.bootstrap.DateSplitField} this
31090 * @param {Object} years
31095 * getting the data of days
31096 * @param {Roo.bootstrap.DateSplitField} this
31097 * @param {Object} days
31102 * Fires after the field has been marked as invalid.
31103 * @param {Roo.form.Field} this
31104 * @param {String} msg The validation message
31109 * Fires after the field has been validated with no errors.
31110 * @param {Roo.form.Field} this
31116 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
31119 labelAlign : 'top',
31121 dayAllowBlank : false,
31122 monthAllowBlank : false,
31123 yearAllowBlank : false,
31124 dayPlaceholder : '',
31125 monthPlaceholder : '',
31126 yearPlaceholder : '',
31130 isFormField : true,
31136 getAutoCreate : function()
31140 cls : 'row roo-date-split-field-group',
31145 cls : 'form-hidden-field roo-date-split-field-group-value',
31151 var labelCls = 'col-md-12';
31152 var contentCls = 'col-md-4';
31154 if(this.fieldLabel){
31158 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
31162 html : this.fieldLabel
31167 if(this.labelAlign == 'left'){
31169 if(this.labelWidth > 12){
31170 label.style = "width: " + this.labelWidth + 'px';
31173 if(this.labelWidth < 13 && this.labelmd == 0){
31174 this.labelmd = this.labelWidth;
31177 if(this.labellg > 0){
31178 labelCls = ' col-lg-' + this.labellg;
31179 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
31182 if(this.labelmd > 0){
31183 labelCls = ' col-md-' + this.labelmd;
31184 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
31187 if(this.labelsm > 0){
31188 labelCls = ' col-sm-' + this.labelsm;
31189 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
31192 if(this.labelxs > 0){
31193 labelCls = ' col-xs-' + this.labelxs;
31194 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
31198 label.cls += ' ' + labelCls;
31200 cfg.cn.push(label);
31203 Roo.each(['day', 'month', 'year'], function(t){
31206 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
31213 inputEl: function ()
31215 return this.el.select('.roo-date-split-field-group-value', true).first();
31218 onRender : function(ct, position)
31222 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31224 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
31226 this.dayField = new Roo.bootstrap.ComboBox({
31227 allowBlank : this.dayAllowBlank,
31228 alwaysQuery : true,
31229 displayField : 'value',
31232 forceSelection : true,
31234 placeholder : this.dayPlaceholder,
31235 selectOnFocus : true,
31236 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31237 triggerAction : 'all',
31239 valueField : 'value',
31240 store : new Roo.data.SimpleStore({
31241 data : (function() {
31243 _this.fireEvent('days', _this, days);
31246 fields : [ 'value' ]
31249 select : function (_self, record, index)
31251 _this.setValue(_this.getValue());
31256 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
31258 this.monthField = new Roo.bootstrap.MonthField({
31259 after : '<i class=\"fa fa-calendar\"></i>',
31260 allowBlank : this.monthAllowBlank,
31261 placeholder : this.monthPlaceholder,
31264 render : function (_self)
31266 this.el.select('span.input-group-addon', true).first().on('click', function(e){
31267 e.preventDefault();
31271 select : function (_self, oldvalue, newvalue)
31273 _this.setValue(_this.getValue());
31278 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31280 this.yearField = new Roo.bootstrap.ComboBox({
31281 allowBlank : this.yearAllowBlank,
31282 alwaysQuery : true,
31283 displayField : 'value',
31286 forceSelection : true,
31288 placeholder : this.yearPlaceholder,
31289 selectOnFocus : true,
31290 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31291 triggerAction : 'all',
31293 valueField : 'value',
31294 store : new Roo.data.SimpleStore({
31295 data : (function() {
31297 _this.fireEvent('years', _this, years);
31300 fields : [ 'value' ]
31303 select : function (_self, record, index)
31305 _this.setValue(_this.getValue());
31310 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
31313 setValue : function(v, format)
31315 this.inputEl.dom.value = v;
31317 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
31319 var d = Date.parseDate(v, f);
31326 this.setDay(d.format(this.dayFormat));
31327 this.setMonth(d.format(this.monthFormat));
31328 this.setYear(d.format(this.yearFormat));
31335 setDay : function(v)
31337 this.dayField.setValue(v);
31338 this.inputEl.dom.value = this.getValue();
31343 setMonth : function(v)
31345 this.monthField.setValue(v, true);
31346 this.inputEl.dom.value = this.getValue();
31351 setYear : function(v)
31353 this.yearField.setValue(v);
31354 this.inputEl.dom.value = this.getValue();
31359 getDay : function()
31361 return this.dayField.getValue();
31364 getMonth : function()
31366 return this.monthField.getValue();
31369 getYear : function()
31371 return this.yearField.getValue();
31374 getValue : function()
31376 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31378 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31388 this.inputEl.dom.value = '';
31393 validate : function()
31395 var d = this.dayField.validate();
31396 var m = this.monthField.validate();
31397 var y = this.yearField.validate();
31402 (!this.dayAllowBlank && !d) ||
31403 (!this.monthAllowBlank && !m) ||
31404 (!this.yearAllowBlank && !y)
31409 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31418 this.markInvalid();
31423 markValid : function()
31426 var label = this.el.select('label', true).first();
31427 var icon = this.el.select('i.fa-star', true).first();
31433 this.fireEvent('valid', this);
31437 * Mark this field as invalid
31438 * @param {String} msg The validation message
31440 markInvalid : function(msg)
31443 var label = this.el.select('label', true).first();
31444 var icon = this.el.select('i.fa-star', true).first();
31446 if(label && !icon){
31447 this.el.select('.roo-date-split-field-label', true).createChild({
31449 cls : 'text-danger fa fa-lg fa-star',
31450 tooltip : 'This field is required',
31451 style : 'margin-right:5px;'
31455 this.fireEvent('invalid', this, msg);
31458 clearInvalid : function()
31460 var label = this.el.select('label', true).first();
31461 var icon = this.el.select('i.fa-star', true).first();
31467 this.fireEvent('valid', this);
31470 getName: function()
31480 * http://masonry.desandro.com
31482 * The idea is to render all the bricks based on vertical width...
31484 * The original code extends 'outlayer' - we might need to use that....
31490 * @class Roo.bootstrap.LayoutMasonry
31491 * @extends Roo.bootstrap.Component
31492 * Bootstrap Layout Masonry class
31495 * Create a new Element
31496 * @param {Object} config The config object
31499 Roo.bootstrap.LayoutMasonry = function(config){
31501 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31505 Roo.bootstrap.LayoutMasonry.register(this);
31511 * Fire after layout the items
31512 * @param {Roo.bootstrap.LayoutMasonry} this
31513 * @param {Roo.EventObject} e
31520 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
31523 * @cfg {Boolean} isLayoutInstant = no animation?
31525 isLayoutInstant : false, // needed?
31528 * @cfg {Number} boxWidth width of the columns
31533 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
31538 * @cfg {Number} padWidth padding below box..
31543 * @cfg {Number} gutter gutter width..
31548 * @cfg {Number} maxCols maximum number of columns
31554 * @cfg {Boolean} isAutoInitial defalut true
31556 isAutoInitial : true,
31561 * @cfg {Boolean} isHorizontal defalut false
31563 isHorizontal : false,
31565 currentSize : null,
31571 bricks: null, //CompositeElement
31575 _isLayoutInited : false,
31577 // isAlternative : false, // only use for vertical layout...
31580 * @cfg {Number} alternativePadWidth padding below box..
31582 alternativePadWidth : 50,
31584 selectedBrick : [],
31586 getAutoCreate : function(){
31588 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31592 cls: 'blog-masonary-wrapper ' + this.cls,
31594 cls : 'mas-boxes masonary'
31601 getChildContainer: function( )
31603 if (this.boxesEl) {
31604 return this.boxesEl;
31607 this.boxesEl = this.el.select('.mas-boxes').first();
31609 return this.boxesEl;
31613 initEvents : function()
31617 if(this.isAutoInitial){
31618 Roo.log('hook children rendered');
31619 this.on('childrenrendered', function() {
31620 Roo.log('children rendered');
31626 initial : function()
31628 this.selectedBrick = [];
31630 this.currentSize = this.el.getBox(true);
31632 Roo.EventManager.onWindowResize(this.resize, this);
31634 if(!this.isAutoInitial){
31642 //this.layout.defer(500,this);
31646 resize : function()
31648 var cs = this.el.getBox(true);
31651 this.currentSize.width == cs.width &&
31652 this.currentSize.x == cs.x &&
31653 this.currentSize.height == cs.height &&
31654 this.currentSize.y == cs.y
31656 Roo.log("no change in with or X or Y");
31660 this.currentSize = cs;
31666 layout : function()
31668 this._resetLayout();
31670 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31672 this.layoutItems( isInstant );
31674 this._isLayoutInited = true;
31676 this.fireEvent('layout', this);
31680 _resetLayout : function()
31682 if(this.isHorizontal){
31683 this.horizontalMeasureColumns();
31687 this.verticalMeasureColumns();
31691 verticalMeasureColumns : function()
31693 this.getContainerWidth();
31695 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31696 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31700 var boxWidth = this.boxWidth + this.padWidth;
31702 if(this.containerWidth < this.boxWidth){
31703 boxWidth = this.containerWidth
31706 var containerWidth = this.containerWidth;
31708 var cols = Math.floor(containerWidth / boxWidth);
31710 this.cols = Math.max( cols, 1 );
31712 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31714 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31716 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31718 this.colWidth = boxWidth + avail - this.padWidth;
31720 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31721 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31724 horizontalMeasureColumns : function()
31726 this.getContainerWidth();
31728 var boxWidth = this.boxWidth;
31730 if(this.containerWidth < boxWidth){
31731 boxWidth = this.containerWidth;
31734 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31736 this.el.setHeight(boxWidth);
31740 getContainerWidth : function()
31742 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31745 layoutItems : function( isInstant )
31747 Roo.log(this.bricks);
31749 var items = Roo.apply([], this.bricks);
31751 if(this.isHorizontal){
31752 this._horizontalLayoutItems( items , isInstant );
31756 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31757 // this._verticalAlternativeLayoutItems( items , isInstant );
31761 this._verticalLayoutItems( items , isInstant );
31765 _verticalLayoutItems : function ( items , isInstant)
31767 if ( !items || !items.length ) {
31772 ['xs', 'xs', 'xs', 'tall'],
31773 ['xs', 'xs', 'tall'],
31774 ['xs', 'xs', 'sm'],
31775 ['xs', 'xs', 'xs'],
31781 ['sm', 'xs', 'xs'],
31785 ['tall', 'xs', 'xs', 'xs'],
31786 ['tall', 'xs', 'xs'],
31798 Roo.each(items, function(item, k){
31800 switch (item.size) {
31801 // these layouts take up a full box,
31812 boxes.push([item]);
31835 var filterPattern = function(box, length)
31843 var pattern = box.slice(0, length);
31847 Roo.each(pattern, function(i){
31848 format.push(i.size);
31851 Roo.each(standard, function(s){
31853 if(String(s) != String(format)){
31862 if(!match && length == 1){
31867 filterPattern(box, length - 1);
31871 queue.push(pattern);
31873 box = box.slice(length, box.length);
31875 filterPattern(box, 4);
31881 Roo.each(boxes, function(box, k){
31887 if(box.length == 1){
31892 filterPattern(box, 4);
31896 this._processVerticalLayoutQueue( queue, isInstant );
31900 // _verticalAlternativeLayoutItems : function( items , isInstant )
31902 // if ( !items || !items.length ) {
31906 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31910 _horizontalLayoutItems : function ( items , isInstant)
31912 if ( !items || !items.length || items.length < 3) {
31918 var eItems = items.slice(0, 3);
31920 items = items.slice(3, items.length);
31923 ['xs', 'xs', 'xs', 'wide'],
31924 ['xs', 'xs', 'wide'],
31925 ['xs', 'xs', 'sm'],
31926 ['xs', 'xs', 'xs'],
31932 ['sm', 'xs', 'xs'],
31936 ['wide', 'xs', 'xs', 'xs'],
31937 ['wide', 'xs', 'xs'],
31950 Roo.each(items, function(item, k){
31952 switch (item.size) {
31963 boxes.push([item]);
31987 var filterPattern = function(box, length)
31995 var pattern = box.slice(0, length);
31999 Roo.each(pattern, function(i){
32000 format.push(i.size);
32003 Roo.each(standard, function(s){
32005 if(String(s) != String(format)){
32014 if(!match && length == 1){
32019 filterPattern(box, length - 1);
32023 queue.push(pattern);
32025 box = box.slice(length, box.length);
32027 filterPattern(box, 4);
32033 Roo.each(boxes, function(box, k){
32039 if(box.length == 1){
32044 filterPattern(box, 4);
32051 var pos = this.el.getBox(true);
32055 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32057 var hit_end = false;
32059 Roo.each(queue, function(box){
32063 Roo.each(box, function(b){
32065 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32075 Roo.each(box, function(b){
32077 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32080 mx = Math.max(mx, b.x);
32084 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
32088 Roo.each(box, function(b){
32090 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32104 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
32107 /** Sets position of item in DOM
32108 * @param {Element} item
32109 * @param {Number} x - horizontal position
32110 * @param {Number} y - vertical position
32111 * @param {Boolean} isInstant - disables transitions
32113 _processVerticalLayoutQueue : function( queue, isInstant )
32115 var pos = this.el.getBox(true);
32120 for (var i = 0; i < this.cols; i++){
32124 Roo.each(queue, function(box, k){
32126 var col = k % this.cols;
32128 Roo.each(box, function(b,kk){
32130 b.el.position('absolute');
32132 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32133 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32135 if(b.size == 'md-left' || b.size == 'md-right'){
32136 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32137 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32140 b.el.setWidth(width);
32141 b.el.setHeight(height);
32143 b.el.select('iframe',true).setSize(width,height);
32147 for (var i = 0; i < this.cols; i++){
32149 if(maxY[i] < maxY[col]){
32154 col = Math.min(col, i);
32158 x = pos.x + col * (this.colWidth + this.padWidth);
32162 var positions = [];
32164 switch (box.length){
32166 positions = this.getVerticalOneBoxColPositions(x, y, box);
32169 positions = this.getVerticalTwoBoxColPositions(x, y, box);
32172 positions = this.getVerticalThreeBoxColPositions(x, y, box);
32175 positions = this.getVerticalFourBoxColPositions(x, y, box);
32181 Roo.each(box, function(b,kk){
32183 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32185 var sz = b.el.getSize();
32187 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
32195 for (var i = 0; i < this.cols; i++){
32196 mY = Math.max(mY, maxY[i]);
32199 this.el.setHeight(mY - pos.y);
32203 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
32205 // var pos = this.el.getBox(true);
32208 // var maxX = pos.right;
32210 // var maxHeight = 0;
32212 // Roo.each(items, function(item, k){
32216 // item.el.position('absolute');
32218 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
32220 // item.el.setWidth(width);
32222 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
32224 // item.el.setHeight(height);
32227 // item.el.setXY([x, y], isInstant ? false : true);
32229 // item.el.setXY([maxX - width, y], isInstant ? false : true);
32232 // y = y + height + this.alternativePadWidth;
32234 // maxHeight = maxHeight + height + this.alternativePadWidth;
32238 // this.el.setHeight(maxHeight);
32242 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
32244 var pos = this.el.getBox(true);
32249 var maxX = pos.right;
32251 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
32253 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32255 Roo.each(queue, function(box, k){
32257 Roo.each(box, function(b, kk){
32259 b.el.position('absolute');
32261 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32262 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32264 if(b.size == 'md-left' || b.size == 'md-right'){
32265 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32266 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32269 b.el.setWidth(width);
32270 b.el.setHeight(height);
32278 var positions = [];
32280 switch (box.length){
32282 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32285 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
32288 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
32291 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
32297 Roo.each(box, function(b,kk){
32299 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32301 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
32309 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
32311 Roo.each(eItems, function(b,k){
32313 b.size = (k == 0) ? 'sm' : 'xs';
32314 b.x = (k == 0) ? 2 : 1;
32315 b.y = (k == 0) ? 2 : 1;
32317 b.el.position('absolute');
32319 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32321 b.el.setWidth(width);
32323 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32325 b.el.setHeight(height);
32329 var positions = [];
32332 x : maxX - this.unitWidth * 2 - this.gutter,
32337 x : maxX - this.unitWidth,
32338 y : minY + (this.unitWidth + this.gutter) * 2
32342 x : maxX - this.unitWidth * 3 - this.gutter * 2,
32346 Roo.each(eItems, function(b,k){
32348 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32354 getVerticalOneBoxColPositions : function(x, y, box)
32358 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32360 if(box[0].size == 'md-left'){
32364 if(box[0].size == 'md-right'){
32369 x : x + (this.unitWidth + this.gutter) * rand,
32376 getVerticalTwoBoxColPositions : function(x, y, box)
32380 if(box[0].size == 'xs'){
32384 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32388 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32402 x : x + (this.unitWidth + this.gutter) * 2,
32403 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32410 getVerticalThreeBoxColPositions : function(x, y, box)
32414 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32422 x : x + (this.unitWidth + this.gutter) * 1,
32427 x : x + (this.unitWidth + this.gutter) * 2,
32435 if(box[0].size == 'xs' && box[1].size == 'xs'){
32444 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32448 x : x + (this.unitWidth + this.gutter) * 1,
32462 x : x + (this.unitWidth + this.gutter) * 2,
32467 x : x + (this.unitWidth + this.gutter) * 2,
32468 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32475 getVerticalFourBoxColPositions : function(x, y, box)
32479 if(box[0].size == 'xs'){
32488 y : y + (this.unitHeight + this.gutter) * 1
32493 y : y + (this.unitHeight + this.gutter) * 2
32497 x : x + (this.unitWidth + this.gutter) * 1,
32511 x : x + (this.unitWidth + this.gutter) * 2,
32516 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32517 y : y + (this.unitHeight + this.gutter) * 1
32521 x : x + (this.unitWidth + this.gutter) * 2,
32522 y : y + (this.unitWidth + this.gutter) * 2
32529 getHorizontalOneBoxColPositions : function(maxX, minY, box)
32533 if(box[0].size == 'md-left'){
32535 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32542 if(box[0].size == 'md-right'){
32544 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32545 y : minY + (this.unitWidth + this.gutter) * 1
32551 var rand = Math.floor(Math.random() * (4 - box[0].y));
32554 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32555 y : minY + (this.unitWidth + this.gutter) * rand
32562 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32566 if(box[0].size == 'xs'){
32569 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32574 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32575 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32583 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32588 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32589 y : minY + (this.unitWidth + this.gutter) * 2
32596 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32600 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32603 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32608 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32609 y : minY + (this.unitWidth + this.gutter) * 1
32613 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32614 y : minY + (this.unitWidth + this.gutter) * 2
32621 if(box[0].size == 'xs' && box[1].size == 'xs'){
32624 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32629 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32634 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32635 y : minY + (this.unitWidth + this.gutter) * 1
32643 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32648 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32649 y : minY + (this.unitWidth + this.gutter) * 2
32653 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32654 y : minY + (this.unitWidth + this.gutter) * 2
32661 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32665 if(box[0].size == 'xs'){
32668 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32673 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32678 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),
32683 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32684 y : minY + (this.unitWidth + this.gutter) * 1
32692 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32697 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32698 y : minY + (this.unitWidth + this.gutter) * 2
32702 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32703 y : minY + (this.unitWidth + this.gutter) * 2
32707 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),
32708 y : minY + (this.unitWidth + this.gutter) * 2
32716 * remove a Masonry Brick
32717 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32719 removeBrick : function(brick_id)
32725 for (var i = 0; i<this.bricks.length; i++) {
32726 if (this.bricks[i].id == brick_id) {
32727 this.bricks.splice(i,1);
32728 this.el.dom.removeChild(Roo.get(brick_id).dom);
32735 * adds a Masonry Brick
32736 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32738 addBrick : function(cfg)
32740 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32741 //this.register(cn);
32742 cn.parentId = this.id;
32743 cn.render(this.el);
32748 * register a Masonry Brick
32749 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32752 register : function(brick)
32754 this.bricks.push(brick);
32755 brick.masonryId = this.id;
32759 * clear all the Masonry Brick
32761 clearAll : function()
32764 //this.getChildContainer().dom.innerHTML = "";
32765 this.el.dom.innerHTML = '';
32768 getSelected : function()
32770 if (!this.selectedBrick) {
32774 return this.selectedBrick;
32778 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32782 * register a Masonry Layout
32783 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32786 register : function(layout)
32788 this.groups[layout.id] = layout;
32791 * fetch a Masonry Layout based on the masonry layout ID
32792 * @param {string} the masonry layout to add
32793 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32796 get: function(layout_id) {
32797 if (typeof(this.groups[layout_id]) == 'undefined') {
32800 return this.groups[layout_id] ;
32812 * http://masonry.desandro.com
32814 * The idea is to render all the bricks based on vertical width...
32816 * The original code extends 'outlayer' - we might need to use that....
32822 * @class Roo.bootstrap.LayoutMasonryAuto
32823 * @extends Roo.bootstrap.Component
32824 * Bootstrap Layout Masonry class
32827 * Create a new Element
32828 * @param {Object} config The config object
32831 Roo.bootstrap.LayoutMasonryAuto = function(config){
32832 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32835 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32838 * @cfg {Boolean} isFitWidth - resize the width..
32840 isFitWidth : false, // options..
32842 * @cfg {Boolean} isOriginLeft = left align?
32844 isOriginLeft : true,
32846 * @cfg {Boolean} isOriginTop = top align?
32848 isOriginTop : false,
32850 * @cfg {Boolean} isLayoutInstant = no animation?
32852 isLayoutInstant : false, // needed?
32854 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32856 isResizingContainer : true,
32858 * @cfg {Number} columnWidth width of the columns
32864 * @cfg {Number} maxCols maximum number of columns
32869 * @cfg {Number} padHeight padding below box..
32875 * @cfg {Boolean} isAutoInitial defalut true
32878 isAutoInitial : true,
32884 initialColumnWidth : 0,
32885 currentSize : null,
32887 colYs : null, // array.
32894 bricks: null, //CompositeElement
32895 cols : 0, // array?
32896 // element : null, // wrapped now this.el
32897 _isLayoutInited : null,
32900 getAutoCreate : function(){
32904 cls: 'blog-masonary-wrapper ' + this.cls,
32906 cls : 'mas-boxes masonary'
32913 getChildContainer: function( )
32915 if (this.boxesEl) {
32916 return this.boxesEl;
32919 this.boxesEl = this.el.select('.mas-boxes').first();
32921 return this.boxesEl;
32925 initEvents : function()
32929 if(this.isAutoInitial){
32930 Roo.log('hook children rendered');
32931 this.on('childrenrendered', function() {
32932 Roo.log('children rendered');
32939 initial : function()
32941 this.reloadItems();
32943 this.currentSize = this.el.getBox(true);
32945 /// was window resize... - let's see if this works..
32946 Roo.EventManager.onWindowResize(this.resize, this);
32948 if(!this.isAutoInitial){
32953 this.layout.defer(500,this);
32956 reloadItems: function()
32958 this.bricks = this.el.select('.masonry-brick', true);
32960 this.bricks.each(function(b) {
32961 //Roo.log(b.getSize());
32962 if (!b.attr('originalwidth')) {
32963 b.attr('originalwidth', b.getSize().width);
32968 Roo.log(this.bricks.elements.length);
32971 resize : function()
32974 var cs = this.el.getBox(true);
32976 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32977 Roo.log("no change in with or X");
32980 this.currentSize = cs;
32984 layout : function()
32987 this._resetLayout();
32988 //this._manageStamps();
32990 // don't animate first layout
32991 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32992 this.layoutItems( isInstant );
32994 // flag for initalized
32995 this._isLayoutInited = true;
32998 layoutItems : function( isInstant )
33000 //var items = this._getItemsForLayout( this.items );
33001 // original code supports filtering layout items.. we just ignore it..
33003 this._layoutItems( this.bricks , isInstant );
33005 this._postLayout();
33007 _layoutItems : function ( items , isInstant)
33009 //this.fireEvent( 'layout', this, items );
33012 if ( !items || !items.elements.length ) {
33013 // no items, emit event with empty array
33018 items.each(function(item) {
33019 Roo.log("layout item");
33021 // get x/y object from method
33022 var position = this._getItemLayoutPosition( item );
33024 position.item = item;
33025 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
33026 queue.push( position );
33029 this._processLayoutQueue( queue );
33031 /** Sets position of item in DOM
33032 * @param {Element} item
33033 * @param {Number} x - horizontal position
33034 * @param {Number} y - vertical position
33035 * @param {Boolean} isInstant - disables transitions
33037 _processLayoutQueue : function( queue )
33039 for ( var i=0, len = queue.length; i < len; i++ ) {
33040 var obj = queue[i];
33041 obj.item.position('absolute');
33042 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
33048 * Any logic you want to do after each layout,
33049 * i.e. size the container
33051 _postLayout : function()
33053 this.resizeContainer();
33056 resizeContainer : function()
33058 if ( !this.isResizingContainer ) {
33061 var size = this._getContainerSize();
33063 this.el.setSize(size.width,size.height);
33064 this.boxesEl.setSize(size.width,size.height);
33070 _resetLayout : function()
33072 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
33073 this.colWidth = this.el.getWidth();
33074 //this.gutter = this.el.getWidth();
33076 this.measureColumns();
33082 this.colYs.push( 0 );
33088 measureColumns : function()
33090 this.getContainerWidth();
33091 // if columnWidth is 0, default to outerWidth of first item
33092 if ( !this.columnWidth ) {
33093 var firstItem = this.bricks.first();
33094 Roo.log(firstItem);
33095 this.columnWidth = this.containerWidth;
33096 if (firstItem && firstItem.attr('originalwidth') ) {
33097 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
33099 // columnWidth fall back to item of first element
33100 Roo.log("set column width?");
33101 this.initialColumnWidth = this.columnWidth ;
33103 // if first elem has no width, default to size of container
33108 if (this.initialColumnWidth) {
33109 this.columnWidth = this.initialColumnWidth;
33114 // column width is fixed at the top - however if container width get's smaller we should
33117 // this bit calcs how man columns..
33119 var columnWidth = this.columnWidth += this.gutter;
33121 // calculate columns
33122 var containerWidth = this.containerWidth + this.gutter;
33124 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
33125 // fix rounding errors, typically with gutters
33126 var excess = columnWidth - containerWidth % columnWidth;
33129 // if overshoot is less than a pixel, round up, otherwise floor it
33130 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
33131 cols = Math[ mathMethod ]( cols );
33132 this.cols = Math.max( cols, 1 );
33133 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33135 // padding positioning..
33136 var totalColWidth = this.cols * this.columnWidth;
33137 var padavail = this.containerWidth - totalColWidth;
33138 // so for 2 columns - we need 3 'pads'
33140 var padNeeded = (1+this.cols) * this.padWidth;
33142 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
33144 this.columnWidth += padExtra
33145 //this.padWidth = Math.floor(padavail / ( this.cols));
33147 // adjust colum width so that padding is fixed??
33149 // we have 3 columns ... total = width * 3
33150 // we have X left over... that should be used by
33152 //if (this.expandC) {
33160 getContainerWidth : function()
33162 /* // container is parent if fit width
33163 var container = this.isFitWidth ? this.element.parentNode : this.element;
33164 // check that this.size and size are there
33165 // IE8 triggers resize on body size change, so they might not be
33167 var size = getSize( container ); //FIXME
33168 this.containerWidth = size && size.innerWidth; //FIXME
33171 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33175 _getItemLayoutPosition : function( item ) // what is item?
33177 // we resize the item to our columnWidth..
33179 item.setWidth(this.columnWidth);
33180 item.autoBoxAdjust = false;
33182 var sz = item.getSize();
33184 // how many columns does this brick span
33185 var remainder = this.containerWidth % this.columnWidth;
33187 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
33188 // round if off by 1 pixel, otherwise use ceil
33189 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
33190 colSpan = Math.min( colSpan, this.cols );
33192 // normally this should be '1' as we dont' currently allow multi width columns..
33194 var colGroup = this._getColGroup( colSpan );
33195 // get the minimum Y value from the columns
33196 var minimumY = Math.min.apply( Math, colGroup );
33197 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
33199 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
33201 // position the brick
33203 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
33204 y: this.currentSize.y + minimumY + this.padHeight
33208 // apply setHeight to necessary columns
33209 var setHeight = minimumY + sz.height + this.padHeight;
33210 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
33212 var setSpan = this.cols + 1 - colGroup.length;
33213 for ( var i = 0; i < setSpan; i++ ) {
33214 this.colYs[ shortColIndex + i ] = setHeight ;
33221 * @param {Number} colSpan - number of columns the element spans
33222 * @returns {Array} colGroup
33224 _getColGroup : function( colSpan )
33226 if ( colSpan < 2 ) {
33227 // if brick spans only one column, use all the column Ys
33232 // how many different places could this brick fit horizontally
33233 var groupCount = this.cols + 1 - colSpan;
33234 // for each group potential horizontal position
33235 for ( var i = 0; i < groupCount; i++ ) {
33236 // make an array of colY values for that one group
33237 var groupColYs = this.colYs.slice( i, i + colSpan );
33238 // and get the max value of the array
33239 colGroup[i] = Math.max.apply( Math, groupColYs );
33244 _manageStamp : function( stamp )
33246 var stampSize = stamp.getSize();
33247 var offset = stamp.getBox();
33248 // get the columns that this stamp affects
33249 var firstX = this.isOriginLeft ? offset.x : offset.right;
33250 var lastX = firstX + stampSize.width;
33251 var firstCol = Math.floor( firstX / this.columnWidth );
33252 firstCol = Math.max( 0, firstCol );
33254 var lastCol = Math.floor( lastX / this.columnWidth );
33255 // lastCol should not go over if multiple of columnWidth #425
33256 lastCol -= lastX % this.columnWidth ? 0 : 1;
33257 lastCol = Math.min( this.cols - 1, lastCol );
33259 // set colYs to bottom of the stamp
33260 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
33263 for ( var i = firstCol; i <= lastCol; i++ ) {
33264 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33269 _getContainerSize : function()
33271 this.maxY = Math.max.apply( Math, this.colYs );
33276 if ( this.isFitWidth ) {
33277 size.width = this._getContainerFitWidth();
33283 _getContainerFitWidth : function()
33285 var unusedCols = 0;
33286 // count unused columns
33289 if ( this.colYs[i] !== 0 ) {
33294 // fit container to columns that have been used
33295 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
33298 needsResizeLayout : function()
33300 var previousWidth = this.containerWidth;
33301 this.getContainerWidth();
33302 return previousWidth !== this.containerWidth;
33317 * @class Roo.bootstrap.MasonryBrick
33318 * @extends Roo.bootstrap.Component
33319 * Bootstrap MasonryBrick class
33322 * Create a new MasonryBrick
33323 * @param {Object} config The config object
33326 Roo.bootstrap.MasonryBrick = function(config){
33328 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
33330 Roo.bootstrap.MasonryBrick.register(this);
33336 * When a MasonryBrick is clcik
33337 * @param {Roo.bootstrap.MasonryBrick} this
33338 * @param {Roo.EventObject} e
33344 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
33347 * @cfg {String} title
33351 * @cfg {String} html
33355 * @cfg {String} bgimage
33359 * @cfg {String} videourl
33363 * @cfg {String} cls
33367 * @cfg {String} href
33371 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33376 * @cfg {String} placetitle (center|bottom)
33381 * @cfg {Boolean} isFitContainer defalut true
33383 isFitContainer : true,
33386 * @cfg {Boolean} preventDefault defalut false
33388 preventDefault : false,
33391 * @cfg {Boolean} inverse defalut false
33393 maskInverse : false,
33395 getAutoCreate : function()
33397 if(!this.isFitContainer){
33398 return this.getSplitAutoCreate();
33401 var cls = 'masonry-brick masonry-brick-full';
33403 if(this.href.length){
33404 cls += ' masonry-brick-link';
33407 if(this.bgimage.length){
33408 cls += ' masonry-brick-image';
33411 if(this.maskInverse){
33412 cls += ' mask-inverse';
33415 if(!this.html.length && !this.maskInverse && !this.videourl.length){
33416 cls += ' enable-mask';
33420 cls += ' masonry-' + this.size + '-brick';
33423 if(this.placetitle.length){
33425 switch (this.placetitle) {
33427 cls += ' masonry-center-title';
33430 cls += ' masonry-bottom-title';
33437 if(!this.html.length && !this.bgimage.length){
33438 cls += ' masonry-center-title';
33441 if(!this.html.length && this.bgimage.length){
33442 cls += ' masonry-bottom-title';
33447 cls += ' ' + this.cls;
33451 tag: (this.href.length) ? 'a' : 'div',
33456 cls: 'masonry-brick-mask'
33460 cls: 'masonry-brick-paragraph',
33466 if(this.href.length){
33467 cfg.href = this.href;
33470 var cn = cfg.cn[1].cn;
33472 if(this.title.length){
33475 cls: 'masonry-brick-title',
33480 if(this.html.length){
33483 cls: 'masonry-brick-text',
33488 if (!this.title.length && !this.html.length) {
33489 cfg.cn[1].cls += ' hide';
33492 if(this.bgimage.length){
33495 cls: 'masonry-brick-image-view',
33500 if(this.videourl.length){
33501 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33502 // youtube support only?
33505 cls: 'masonry-brick-image-view',
33508 allowfullscreen : true
33516 getSplitAutoCreate : function()
33518 var cls = 'masonry-brick masonry-brick-split';
33520 if(this.href.length){
33521 cls += ' masonry-brick-link';
33524 if(this.bgimage.length){
33525 cls += ' masonry-brick-image';
33529 cls += ' masonry-' + this.size + '-brick';
33532 switch (this.placetitle) {
33534 cls += ' masonry-center-title';
33537 cls += ' masonry-bottom-title';
33540 if(!this.bgimage.length){
33541 cls += ' masonry-center-title';
33544 if(this.bgimage.length){
33545 cls += ' masonry-bottom-title';
33551 cls += ' ' + this.cls;
33555 tag: (this.href.length) ? 'a' : 'div',
33560 cls: 'masonry-brick-split-head',
33564 cls: 'masonry-brick-paragraph',
33571 cls: 'masonry-brick-split-body',
33577 if(this.href.length){
33578 cfg.href = this.href;
33581 if(this.title.length){
33582 cfg.cn[0].cn[0].cn.push({
33584 cls: 'masonry-brick-title',
33589 if(this.html.length){
33590 cfg.cn[1].cn.push({
33592 cls: 'masonry-brick-text',
33597 if(this.bgimage.length){
33598 cfg.cn[0].cn.push({
33600 cls: 'masonry-brick-image-view',
33605 if(this.videourl.length){
33606 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33607 // youtube support only?
33608 cfg.cn[0].cn.cn.push({
33610 cls: 'masonry-brick-image-view',
33613 allowfullscreen : true
33620 initEvents: function()
33622 switch (this.size) {
33655 this.el.on('touchstart', this.onTouchStart, this);
33656 this.el.on('touchmove', this.onTouchMove, this);
33657 this.el.on('touchend', this.onTouchEnd, this);
33658 this.el.on('contextmenu', this.onContextMenu, this);
33660 this.el.on('mouseenter' ,this.enter, this);
33661 this.el.on('mouseleave', this.leave, this);
33662 this.el.on('click', this.onClick, this);
33665 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33666 this.parent().bricks.push(this);
33671 onClick: function(e, el)
33673 var time = this.endTimer - this.startTimer;
33674 // Roo.log(e.preventDefault());
33677 e.preventDefault();
33682 if(!this.preventDefault){
33686 e.preventDefault();
33688 if (this.activeClass != '') {
33689 this.selectBrick();
33692 this.fireEvent('click', this, e);
33695 enter: function(e, el)
33697 e.preventDefault();
33699 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33703 if(this.bgimage.length && this.html.length){
33704 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33708 leave: function(e, el)
33710 e.preventDefault();
33712 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33716 if(this.bgimage.length && this.html.length){
33717 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33721 onTouchStart: function(e, el)
33723 // e.preventDefault();
33725 this.touchmoved = false;
33727 if(!this.isFitContainer){
33731 if(!this.bgimage.length || !this.html.length){
33735 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33737 this.timer = new Date().getTime();
33741 onTouchMove: function(e, el)
33743 this.touchmoved = true;
33746 onContextMenu : function(e,el)
33748 e.preventDefault();
33749 e.stopPropagation();
33753 onTouchEnd: function(e, el)
33755 // e.preventDefault();
33757 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33764 if(!this.bgimage.length || !this.html.length){
33766 if(this.href.length){
33767 window.location.href = this.href;
33773 if(!this.isFitContainer){
33777 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33779 window.location.href = this.href;
33782 //selection on single brick only
33783 selectBrick : function() {
33785 if (!this.parentId) {
33789 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33790 var index = m.selectedBrick.indexOf(this.id);
33793 m.selectedBrick.splice(index,1);
33794 this.el.removeClass(this.activeClass);
33798 for(var i = 0; i < m.selectedBrick.length; i++) {
33799 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33800 b.el.removeClass(b.activeClass);
33803 m.selectedBrick = [];
33805 m.selectedBrick.push(this.id);
33806 this.el.addClass(this.activeClass);
33810 isSelected : function(){
33811 return this.el.hasClass(this.activeClass);
33816 Roo.apply(Roo.bootstrap.MasonryBrick, {
33819 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33821 * register a Masonry Brick
33822 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33825 register : function(brick)
33827 //this.groups[brick.id] = brick;
33828 this.groups.add(brick.id, brick);
33831 * fetch a masonry brick based on the masonry brick ID
33832 * @param {string} the masonry brick to add
33833 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33836 get: function(brick_id)
33838 // if (typeof(this.groups[brick_id]) == 'undefined') {
33841 // return this.groups[brick_id] ;
33843 if(this.groups.key(brick_id)) {
33844 return this.groups.key(brick_id);
33862 * @class Roo.bootstrap.Brick
33863 * @extends Roo.bootstrap.Component
33864 * Bootstrap Brick class
33867 * Create a new Brick
33868 * @param {Object} config The config object
33871 Roo.bootstrap.Brick = function(config){
33872 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33878 * When a Brick is click
33879 * @param {Roo.bootstrap.Brick} this
33880 * @param {Roo.EventObject} e
33886 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33889 * @cfg {String} title
33893 * @cfg {String} html
33897 * @cfg {String} bgimage
33901 * @cfg {String} cls
33905 * @cfg {String} href
33909 * @cfg {String} video
33913 * @cfg {Boolean} square
33917 getAutoCreate : function()
33919 var cls = 'roo-brick';
33921 if(this.href.length){
33922 cls += ' roo-brick-link';
33925 if(this.bgimage.length){
33926 cls += ' roo-brick-image';
33929 if(!this.html.length && !this.bgimage.length){
33930 cls += ' roo-brick-center-title';
33933 if(!this.html.length && this.bgimage.length){
33934 cls += ' roo-brick-bottom-title';
33938 cls += ' ' + this.cls;
33942 tag: (this.href.length) ? 'a' : 'div',
33947 cls: 'roo-brick-paragraph',
33953 if(this.href.length){
33954 cfg.href = this.href;
33957 var cn = cfg.cn[0].cn;
33959 if(this.title.length){
33962 cls: 'roo-brick-title',
33967 if(this.html.length){
33970 cls: 'roo-brick-text',
33977 if(this.bgimage.length){
33980 cls: 'roo-brick-image-view',
33988 initEvents: function()
33990 if(this.title.length || this.html.length){
33991 this.el.on('mouseenter' ,this.enter, this);
33992 this.el.on('mouseleave', this.leave, this);
33995 Roo.EventManager.onWindowResize(this.resize, this);
33997 if(this.bgimage.length){
33998 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33999 this.imageEl.on('load', this.onImageLoad, this);
34006 onImageLoad : function()
34011 resize : function()
34013 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
34015 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
34017 if(this.bgimage.length){
34018 var image = this.el.select('.roo-brick-image-view', true).first();
34020 image.setWidth(paragraph.getWidth());
34023 image.setHeight(paragraph.getWidth());
34026 this.el.setHeight(image.getHeight());
34027 paragraph.setHeight(image.getHeight());
34033 enter: function(e, el)
34035 e.preventDefault();
34037 if(this.bgimage.length){
34038 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
34039 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
34043 leave: function(e, el)
34045 e.preventDefault();
34047 if(this.bgimage.length){
34048 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
34049 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
34064 * @class Roo.bootstrap.NumberField
34065 * @extends Roo.bootstrap.Input
34066 * Bootstrap NumberField class
34072 * Create a new NumberField
34073 * @param {Object} config The config object
34076 Roo.bootstrap.NumberField = function(config){
34077 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
34080 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
34083 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
34085 allowDecimals : true,
34087 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
34089 decimalSeparator : ".",
34091 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
34093 decimalPrecision : 2,
34095 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
34097 allowNegative : true,
34100 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
34104 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
34106 minValue : Number.NEGATIVE_INFINITY,
34108 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
34110 maxValue : Number.MAX_VALUE,
34112 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
34114 minText : "The minimum value for this field is {0}",
34116 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
34118 maxText : "The maximum value for this field is {0}",
34120 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
34121 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
34123 nanText : "{0} is not a valid number",
34125 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
34127 thousandsDelimiter : false,
34129 * @cfg {String} valueAlign alignment of value
34131 valueAlign : "left",
34133 getAutoCreate : function()
34135 var hiddenInput = {
34139 cls: 'hidden-number-input'
34143 hiddenInput.name = this.name;
34148 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
34150 this.name = hiddenInput.name;
34152 if(cfg.cn.length > 0) {
34153 cfg.cn.push(hiddenInput);
34160 initEvents : function()
34162 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
34164 var allowed = "0123456789";
34166 if(this.allowDecimals){
34167 allowed += this.decimalSeparator;
34170 if(this.allowNegative){
34174 if(this.thousandsDelimiter) {
34178 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
34180 var keyPress = function(e){
34182 var k = e.getKey();
34184 var c = e.getCharCode();
34187 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
34188 allowed.indexOf(String.fromCharCode(c)) === -1
34194 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
34198 if(allowed.indexOf(String.fromCharCode(c)) === -1){
34203 this.el.on("keypress", keyPress, this);
34206 validateValue : function(value)
34209 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
34213 var num = this.parseValue(value);
34216 this.markInvalid(String.format(this.nanText, value));
34220 if(num < this.minValue){
34221 this.markInvalid(String.format(this.minText, this.minValue));
34225 if(num > this.maxValue){
34226 this.markInvalid(String.format(this.maxText, this.maxValue));
34233 getValue : function()
34235 var v = this.hiddenEl().getValue();
34237 return this.fixPrecision(this.parseValue(v));
34240 parseValue : function(value)
34242 if(this.thousandsDelimiter) {
34244 r = new RegExp(",", "g");
34245 value = value.replace(r, "");
34248 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
34249 return isNaN(value) ? '' : value;
34252 fixPrecision : function(value)
34254 if(this.thousandsDelimiter) {
34256 r = new RegExp(",", "g");
34257 value = value.replace(r, "");
34260 var nan = isNaN(value);
34262 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
34263 return nan ? '' : value;
34265 return parseFloat(value).toFixed(this.decimalPrecision);
34268 setValue : function(v)
34270 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34276 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34278 this.inputEl().dom.value = (v == '') ? '' :
34279 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34281 if(!this.allowZero && v === '0') {
34282 this.hiddenEl().dom.value = '';
34283 this.inputEl().dom.value = '';
34290 decimalPrecisionFcn : function(v)
34292 return Math.floor(v);
34295 beforeBlur : function()
34297 var v = this.parseValue(this.getRawValue());
34299 if(v || v === 0 || v === ''){
34304 hiddenEl : function()
34306 return this.el.select('input.hidden-number-input',true).first();
34318 * @class Roo.bootstrap.DocumentSlider
34319 * @extends Roo.bootstrap.Component
34320 * Bootstrap DocumentSlider class
34323 * Create a new DocumentViewer
34324 * @param {Object} config The config object
34327 Roo.bootstrap.DocumentSlider = function(config){
34328 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
34335 * Fire after initEvent
34336 * @param {Roo.bootstrap.DocumentSlider} this
34341 * Fire after update
34342 * @param {Roo.bootstrap.DocumentSlider} this
34348 * @param {Roo.bootstrap.DocumentSlider} this
34354 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
34360 getAutoCreate : function()
34364 cls : 'roo-document-slider',
34368 cls : 'roo-document-slider-header',
34372 cls : 'roo-document-slider-header-title'
34378 cls : 'roo-document-slider-body',
34382 cls : 'roo-document-slider-prev',
34386 cls : 'fa fa-chevron-left'
34392 cls : 'roo-document-slider-thumb',
34396 cls : 'roo-document-slider-image'
34402 cls : 'roo-document-slider-next',
34406 cls : 'fa fa-chevron-right'
34418 initEvents : function()
34420 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34421 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34423 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34424 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34426 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34427 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34429 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34430 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34432 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34433 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34435 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34436 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34438 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34439 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34441 this.thumbEl.on('click', this.onClick, this);
34443 this.prevIndicator.on('click', this.prev, this);
34445 this.nextIndicator.on('click', this.next, this);
34449 initial : function()
34451 if(this.files.length){
34452 this.indicator = 1;
34456 this.fireEvent('initial', this);
34459 update : function()
34461 this.imageEl.attr('src', this.files[this.indicator - 1]);
34463 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34465 this.prevIndicator.show();
34467 if(this.indicator == 1){
34468 this.prevIndicator.hide();
34471 this.nextIndicator.show();
34473 if(this.indicator == this.files.length){
34474 this.nextIndicator.hide();
34477 this.thumbEl.scrollTo('top');
34479 this.fireEvent('update', this);
34482 onClick : function(e)
34484 e.preventDefault();
34486 this.fireEvent('click', this);
34491 e.preventDefault();
34493 this.indicator = Math.max(1, this.indicator - 1);
34500 e.preventDefault();
34502 this.indicator = Math.min(this.files.length, this.indicator + 1);
34516 * @class Roo.bootstrap.RadioSet
34517 * @extends Roo.bootstrap.Input
34518 * Bootstrap RadioSet class
34519 * @cfg {String} indicatorpos (left|right) default left
34520 * @cfg {Boolean} inline (true|false) inline the element (default true)
34521 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34523 * Create a new RadioSet
34524 * @param {Object} config The config object
34527 Roo.bootstrap.RadioSet = function(config){
34529 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34533 Roo.bootstrap.RadioSet.register(this);
34538 * Fires when the element is checked or unchecked.
34539 * @param {Roo.bootstrap.RadioSet} this This radio
34540 * @param {Roo.bootstrap.Radio} item The checked item
34545 * Fires when the element is click.
34546 * @param {Roo.bootstrap.RadioSet} this This radio set
34547 * @param {Roo.bootstrap.Radio} item The checked item
34548 * @param {Roo.EventObject} e The event object
34555 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
34563 indicatorpos : 'left',
34565 getAutoCreate : function()
34569 cls : 'roo-radio-set-label',
34573 html : this.fieldLabel
34577 if (Roo.bootstrap.version == 3) {
34580 if(this.indicatorpos == 'left'){
34583 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34584 tooltip : 'This field is required'
34589 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34590 tooltip : 'This field is required'
34596 cls : 'roo-radio-set-items'
34599 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34601 if (align === 'left' && this.fieldLabel.length) {
34604 cls : "roo-radio-set-right",
34610 if(this.labelWidth > 12){
34611 label.style = "width: " + this.labelWidth + 'px';
34614 if(this.labelWidth < 13 && this.labelmd == 0){
34615 this.labelmd = this.labelWidth;
34618 if(this.labellg > 0){
34619 label.cls += ' col-lg-' + this.labellg;
34620 items.cls += ' col-lg-' + (12 - this.labellg);
34623 if(this.labelmd > 0){
34624 label.cls += ' col-md-' + this.labelmd;
34625 items.cls += ' col-md-' + (12 - this.labelmd);
34628 if(this.labelsm > 0){
34629 label.cls += ' col-sm-' + this.labelsm;
34630 items.cls += ' col-sm-' + (12 - this.labelsm);
34633 if(this.labelxs > 0){
34634 label.cls += ' col-xs-' + this.labelxs;
34635 items.cls += ' col-xs-' + (12 - this.labelxs);
34641 cls : 'roo-radio-set',
34645 cls : 'roo-radio-set-input',
34648 value : this.value ? this.value : ''
34655 if(this.weight.length){
34656 cfg.cls += ' roo-radio-' + this.weight;
34660 cfg.cls += ' roo-radio-set-inline';
34664 ['xs','sm','md','lg'].map(function(size){
34665 if (settings[size]) {
34666 cfg.cls += ' col-' + size + '-' + settings[size];
34674 initEvents : function()
34676 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34677 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34679 if(!this.fieldLabel.length){
34680 this.labelEl.hide();
34683 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34684 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34686 this.indicator = this.indicatorEl();
34688 if(this.indicator){
34689 this.indicator.addClass('invisible');
34692 this.originalValue = this.getValue();
34696 inputEl: function ()
34698 return this.el.select('.roo-radio-set-input', true).first();
34701 getChildContainer : function()
34703 return this.itemsEl;
34706 register : function(item)
34708 this.radioes.push(item);
34712 validate : function()
34714 if(this.getVisibilityEl().hasClass('hidden')){
34720 Roo.each(this.radioes, function(i){
34729 if(this.allowBlank) {
34733 if(this.disabled || valid){
34738 this.markInvalid();
34743 markValid : function()
34745 if(this.labelEl.isVisible(true) && this.indicatorEl()){
34746 this.indicatorEl().removeClass('visible');
34747 this.indicatorEl().addClass('invisible');
34751 if (Roo.bootstrap.version == 3) {
34752 this.el.removeClass([this.invalidClass, this.validClass]);
34753 this.el.addClass(this.validClass);
34755 this.el.removeClass(['is-invalid','is-valid']);
34756 this.el.addClass(['is-valid']);
34758 this.fireEvent('valid', this);
34761 markInvalid : function(msg)
34763 if(this.allowBlank || this.disabled){
34767 if(this.labelEl.isVisible(true) && this.indicatorEl()){
34768 this.indicatorEl().removeClass('invisible');
34769 this.indicatorEl().addClass('visible');
34771 if (Roo.bootstrap.version == 3) {
34772 this.el.removeClass([this.invalidClass, this.validClass]);
34773 this.el.addClass(this.invalidClass);
34775 this.el.removeClass(['is-invalid','is-valid']);
34776 this.el.addClass(['is-invalid']);
34779 this.fireEvent('invalid', this, msg);
34783 setValue : function(v, suppressEvent)
34785 if(this.value === v){
34792 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34795 Roo.each(this.radioes, function(i){
34797 i.el.removeClass('checked');
34800 Roo.each(this.radioes, function(i){
34802 if(i.value === v || i.value.toString() === v.toString()){
34804 i.el.addClass('checked');
34806 if(suppressEvent !== true){
34807 this.fireEvent('check', this, i);
34818 clearInvalid : function(){
34820 if(!this.el || this.preventMark){
34824 this.el.removeClass([this.invalidClass]);
34826 this.fireEvent('valid', this);
34831 Roo.apply(Roo.bootstrap.RadioSet, {
34835 register : function(set)
34837 this.groups[set.name] = set;
34840 get: function(name)
34842 if (typeof(this.groups[name]) == 'undefined') {
34846 return this.groups[name] ;
34852 * Ext JS Library 1.1.1
34853 * Copyright(c) 2006-2007, Ext JS, LLC.
34855 * Originally Released Under LGPL - original licence link has changed is not relivant.
34858 * <script type="text/javascript">
34863 * @class Roo.bootstrap.SplitBar
34864 * @extends Roo.util.Observable
34865 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34869 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34870 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34871 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34872 split.minSize = 100;
34873 split.maxSize = 600;
34874 split.animate = true;
34875 split.on('moved', splitterMoved);
34878 * Create a new SplitBar
34879 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34880 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34881 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34882 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34883 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34884 position of the SplitBar).
34886 Roo.bootstrap.SplitBar = function(cfg){
34891 // dragElement : elm
34892 // resizingElement: el,
34894 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34895 // placement : Roo.bootstrap.SplitBar.LEFT ,
34896 // existingProxy ???
34899 this.el = Roo.get(cfg.dragElement, true);
34900 this.el.dom.unselectable = "on";
34902 this.resizingEl = Roo.get(cfg.resizingElement, true);
34906 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34907 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34910 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34913 * The minimum size of the resizing element. (Defaults to 0)
34919 * The maximum size of the resizing element. (Defaults to 2000)
34922 this.maxSize = 2000;
34925 * Whether to animate the transition to the new size
34928 this.animate = false;
34931 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34934 this.useShim = false;
34939 if(!cfg.existingProxy){
34941 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34943 this.proxy = Roo.get(cfg.existingProxy).dom;
34946 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34949 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34952 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34955 this.dragSpecs = {};
34958 * @private The adapter to use to positon and resize elements
34960 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34961 this.adapter.init(this);
34963 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34965 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34966 this.el.addClass("roo-splitbar-h");
34969 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34970 this.el.addClass("roo-splitbar-v");
34976 * Fires when the splitter is moved (alias for {@link #event-moved})
34977 * @param {Roo.bootstrap.SplitBar} this
34978 * @param {Number} newSize the new width or height
34983 * Fires when the splitter is moved
34984 * @param {Roo.bootstrap.SplitBar} this
34985 * @param {Number} newSize the new width or height
34989 * @event beforeresize
34990 * Fires before the splitter is dragged
34991 * @param {Roo.bootstrap.SplitBar} this
34993 "beforeresize" : true,
34995 "beforeapply" : true
34998 Roo.util.Observable.call(this);
35001 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
35002 onStartProxyDrag : function(x, y){
35003 this.fireEvent("beforeresize", this);
35005 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
35007 o.enableDisplayMode("block");
35008 // all splitbars share the same overlay
35009 Roo.bootstrap.SplitBar.prototype.overlay = o;
35011 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
35012 this.overlay.show();
35013 Roo.get(this.proxy).setDisplayed("block");
35014 var size = this.adapter.getElementSize(this);
35015 this.activeMinSize = this.getMinimumSize();;
35016 this.activeMaxSize = this.getMaximumSize();;
35017 var c1 = size - this.activeMinSize;
35018 var c2 = Math.max(this.activeMaxSize - size, 0);
35019 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35020 this.dd.resetConstraints();
35021 this.dd.setXConstraint(
35022 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
35023 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
35025 this.dd.setYConstraint(0, 0);
35027 this.dd.resetConstraints();
35028 this.dd.setXConstraint(0, 0);
35029 this.dd.setYConstraint(
35030 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
35031 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
35034 this.dragSpecs.startSize = size;
35035 this.dragSpecs.startPoint = [x, y];
35036 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
35040 * @private Called after the drag operation by the DDProxy
35042 onEndProxyDrag : function(e){
35043 Roo.get(this.proxy).setDisplayed(false);
35044 var endPoint = Roo.lib.Event.getXY(e);
35046 this.overlay.hide();
35049 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35050 newSize = this.dragSpecs.startSize +
35051 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
35052 endPoint[0] - this.dragSpecs.startPoint[0] :
35053 this.dragSpecs.startPoint[0] - endPoint[0]
35056 newSize = this.dragSpecs.startSize +
35057 (this.placement == Roo.bootstrap.SplitBar.TOP ?
35058 endPoint[1] - this.dragSpecs.startPoint[1] :
35059 this.dragSpecs.startPoint[1] - endPoint[1]
35062 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
35063 if(newSize != this.dragSpecs.startSize){
35064 if(this.fireEvent('beforeapply', this, newSize) !== false){
35065 this.adapter.setElementSize(this, newSize);
35066 this.fireEvent("moved", this, newSize);
35067 this.fireEvent("resize", this, newSize);
35073 * Get the adapter this SplitBar uses
35074 * @return The adapter object
35076 getAdapter : function(){
35077 return this.adapter;
35081 * Set the adapter this SplitBar uses
35082 * @param {Object} adapter A SplitBar adapter object
35084 setAdapter : function(adapter){
35085 this.adapter = adapter;
35086 this.adapter.init(this);
35090 * Gets the minimum size for the resizing element
35091 * @return {Number} The minimum size
35093 getMinimumSize : function(){
35094 return this.minSize;
35098 * Sets the minimum size for the resizing element
35099 * @param {Number} minSize The minimum size
35101 setMinimumSize : function(minSize){
35102 this.minSize = minSize;
35106 * Gets the maximum size for the resizing element
35107 * @return {Number} The maximum size
35109 getMaximumSize : function(){
35110 return this.maxSize;
35114 * Sets the maximum size for the resizing element
35115 * @param {Number} maxSize The maximum size
35117 setMaximumSize : function(maxSize){
35118 this.maxSize = maxSize;
35122 * Sets the initialize size for the resizing element
35123 * @param {Number} size The initial size
35125 setCurrentSize : function(size){
35126 var oldAnimate = this.animate;
35127 this.animate = false;
35128 this.adapter.setElementSize(this, size);
35129 this.animate = oldAnimate;
35133 * Destroy this splitbar.
35134 * @param {Boolean} removeEl True to remove the element
35136 destroy : function(removeEl){
35138 this.shim.remove();
35141 this.proxy.parentNode.removeChild(this.proxy);
35149 * @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.
35151 Roo.bootstrap.SplitBar.createProxy = function(dir){
35152 var proxy = new Roo.Element(document.createElement("div"));
35153 proxy.unselectable();
35154 var cls = 'roo-splitbar-proxy';
35155 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
35156 document.body.appendChild(proxy.dom);
35161 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
35162 * Default Adapter. It assumes the splitter and resizing element are not positioned
35163 * elements and only gets/sets the width of the element. Generally used for table based layouts.
35165 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
35168 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
35169 // do nothing for now
35170 init : function(s){
35174 * Called before drag operations to get the current size of the resizing element.
35175 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35177 getElementSize : function(s){
35178 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35179 return s.resizingEl.getWidth();
35181 return s.resizingEl.getHeight();
35186 * Called after drag operations to set the size of the resizing element.
35187 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35188 * @param {Number} newSize The new size to set
35189 * @param {Function} onComplete A function to be invoked when resizing is complete
35191 setElementSize : function(s, newSize, onComplete){
35192 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35194 s.resizingEl.setWidth(newSize);
35196 onComplete(s, newSize);
35199 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
35204 s.resizingEl.setHeight(newSize);
35206 onComplete(s, newSize);
35209 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
35216 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
35217 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
35218 * Adapter that moves the splitter element to align with the resized sizing element.
35219 * Used with an absolute positioned SplitBar.
35220 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
35221 * document.body, make sure you assign an id to the body element.
35223 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
35224 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35225 this.container = Roo.get(container);
35228 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
35229 init : function(s){
35230 this.basic.init(s);
35233 getElementSize : function(s){
35234 return this.basic.getElementSize(s);
35237 setElementSize : function(s, newSize, onComplete){
35238 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
35241 moveSplitter : function(s){
35242 var yes = Roo.bootstrap.SplitBar;
35243 switch(s.placement){
35245 s.el.setX(s.resizingEl.getRight());
35248 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
35251 s.el.setY(s.resizingEl.getBottom());
35254 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
35261 * Orientation constant - Create a vertical SplitBar
35265 Roo.bootstrap.SplitBar.VERTICAL = 1;
35268 * Orientation constant - Create a horizontal SplitBar
35272 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35275 * Placement constant - The resizing element is to the left of the splitter element
35279 Roo.bootstrap.SplitBar.LEFT = 1;
35282 * Placement constant - The resizing element is to the right of the splitter element
35286 Roo.bootstrap.SplitBar.RIGHT = 2;
35289 * Placement constant - The resizing element is positioned above the splitter element
35293 Roo.bootstrap.SplitBar.TOP = 3;
35296 * Placement constant - The resizing element is positioned under splitter element
35300 Roo.bootstrap.SplitBar.BOTTOM = 4;
35301 Roo.namespace("Roo.bootstrap.layout");/*
35303 * Ext JS Library 1.1.1
35304 * Copyright(c) 2006-2007, Ext JS, LLC.
35306 * Originally Released Under LGPL - original licence link has changed is not relivant.
35309 * <script type="text/javascript">
35313 * @class Roo.bootstrap.layout.Manager
35314 * @extends Roo.bootstrap.Component
35315 * Base class for layout managers.
35317 Roo.bootstrap.layout.Manager = function(config)
35319 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
35325 /** false to disable window resize monitoring @type Boolean */
35326 this.monitorWindowResize = true;
35331 * Fires when a layout is performed.
35332 * @param {Roo.LayoutManager} this
35336 * @event regionresized
35337 * Fires when the user resizes a region.
35338 * @param {Roo.LayoutRegion} region The resized region
35339 * @param {Number} newSize The new size (width for east/west, height for north/south)
35341 "regionresized" : true,
35343 * @event regioncollapsed
35344 * Fires when a region is collapsed.
35345 * @param {Roo.LayoutRegion} region The collapsed region
35347 "regioncollapsed" : true,
35349 * @event regionexpanded
35350 * Fires when a region is expanded.
35351 * @param {Roo.LayoutRegion} region The expanded region
35353 "regionexpanded" : true
35355 this.updating = false;
35358 this.el = Roo.get(config.el);
35364 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35369 monitorWindowResize : true,
35375 onRender : function(ct, position)
35378 this.el = Roo.get(ct);
35381 //this.fireEvent('render',this);
35385 initEvents: function()
35389 // ie scrollbar fix
35390 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35391 document.body.scroll = "no";
35392 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35393 this.el.position('relative');
35395 this.id = this.el.id;
35396 this.el.addClass("roo-layout-container");
35397 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35398 if(this.el.dom != document.body ) {
35399 this.el.on('resize', this.layout,this);
35400 this.el.on('show', this.layout,this);
35406 * Returns true if this layout is currently being updated
35407 * @return {Boolean}
35409 isUpdating : function(){
35410 return this.updating;
35414 * Suspend the LayoutManager from doing auto-layouts while
35415 * making multiple add or remove calls
35417 beginUpdate : function(){
35418 this.updating = true;
35422 * Restore auto-layouts and optionally disable the manager from performing a layout
35423 * @param {Boolean} noLayout true to disable a layout update
35425 endUpdate : function(noLayout){
35426 this.updating = false;
35432 layout: function(){
35436 onRegionResized : function(region, newSize){
35437 this.fireEvent("regionresized", region, newSize);
35441 onRegionCollapsed : function(region){
35442 this.fireEvent("regioncollapsed", region);
35445 onRegionExpanded : function(region){
35446 this.fireEvent("regionexpanded", region);
35450 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35451 * performs box-model adjustments.
35452 * @return {Object} The size as an object {width: (the width), height: (the height)}
35454 getViewSize : function()
35457 if(this.el.dom != document.body){
35458 size = this.el.getSize();
35460 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35462 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35463 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35468 * Returns the Element this layout is bound to.
35469 * @return {Roo.Element}
35471 getEl : function(){
35476 * Returns the specified region.
35477 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35478 * @return {Roo.LayoutRegion}
35480 getRegion : function(target){
35481 return this.regions[target.toLowerCase()];
35484 onWindowResize : function(){
35485 if(this.monitorWindowResize){
35492 * Ext JS Library 1.1.1
35493 * Copyright(c) 2006-2007, Ext JS, LLC.
35495 * Originally Released Under LGPL - original licence link has changed is not relivant.
35498 * <script type="text/javascript">
35501 * @class Roo.bootstrap.layout.Border
35502 * @extends Roo.bootstrap.layout.Manager
35503 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35504 * please see: examples/bootstrap/nested.html<br><br>
35506 <b>The container the layout is rendered into can be either the body element or any other element.
35507 If it is not the body element, the container needs to either be an absolute positioned element,
35508 or you will need to add "position:relative" to the css of the container. You will also need to specify
35509 the container size if it is not the body element.</b>
35512 * Create a new Border
35513 * @param {Object} config Configuration options
35515 Roo.bootstrap.layout.Border = function(config){
35516 config = config || {};
35517 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35521 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35522 if(config[region]){
35523 config[region].region = region;
35524 this.addRegion(config[region]);
35530 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
35532 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35534 parent : false, // this might point to a 'nest' or a ???
35537 * Creates and adds a new region if it doesn't already exist.
35538 * @param {String} target The target region key (north, south, east, west or center).
35539 * @param {Object} config The regions config object
35540 * @return {BorderLayoutRegion} The new region
35542 addRegion : function(config)
35544 if(!this.regions[config.region]){
35545 var r = this.factory(config);
35546 this.bindRegion(r);
35548 return this.regions[config.region];
35552 bindRegion : function(r){
35553 this.regions[r.config.region] = r;
35555 r.on("visibilitychange", this.layout, this);
35556 r.on("paneladded", this.layout, this);
35557 r.on("panelremoved", this.layout, this);
35558 r.on("invalidated", this.layout, this);
35559 r.on("resized", this.onRegionResized, this);
35560 r.on("collapsed", this.onRegionCollapsed, this);
35561 r.on("expanded", this.onRegionExpanded, this);
35565 * Performs a layout update.
35567 layout : function()
35569 if(this.updating) {
35573 // render all the rebions if they have not been done alreayd?
35574 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35575 if(this.regions[region] && !this.regions[region].bodyEl){
35576 this.regions[region].onRender(this.el)
35580 var size = this.getViewSize();
35581 var w = size.width;
35582 var h = size.height;
35587 //var x = 0, y = 0;
35589 var rs = this.regions;
35590 var north = rs["north"];
35591 var south = rs["south"];
35592 var west = rs["west"];
35593 var east = rs["east"];
35594 var center = rs["center"];
35595 //if(this.hideOnLayout){ // not supported anymore
35596 //c.el.setStyle("display", "none");
35598 if(north && north.isVisible()){
35599 var b = north.getBox();
35600 var m = north.getMargins();
35601 b.width = w - (m.left+m.right);
35604 centerY = b.height + b.y + m.bottom;
35605 centerH -= centerY;
35606 north.updateBox(this.safeBox(b));
35608 if(south && south.isVisible()){
35609 var b = south.getBox();
35610 var m = south.getMargins();
35611 b.width = w - (m.left+m.right);
35613 var totalHeight = (b.height + m.top + m.bottom);
35614 b.y = h - totalHeight + m.top;
35615 centerH -= totalHeight;
35616 south.updateBox(this.safeBox(b));
35618 if(west && west.isVisible()){
35619 var b = west.getBox();
35620 var m = west.getMargins();
35621 b.height = centerH - (m.top+m.bottom);
35623 b.y = centerY + m.top;
35624 var totalWidth = (b.width + m.left + m.right);
35625 centerX += totalWidth;
35626 centerW -= totalWidth;
35627 west.updateBox(this.safeBox(b));
35629 if(east && east.isVisible()){
35630 var b = east.getBox();
35631 var m = east.getMargins();
35632 b.height = centerH - (m.top+m.bottom);
35633 var totalWidth = (b.width + m.left + m.right);
35634 b.x = w - totalWidth + m.left;
35635 b.y = centerY + m.top;
35636 centerW -= totalWidth;
35637 east.updateBox(this.safeBox(b));
35640 var m = center.getMargins();
35642 x: centerX + m.left,
35643 y: centerY + m.top,
35644 width: centerW - (m.left+m.right),
35645 height: centerH - (m.top+m.bottom)
35647 //if(this.hideOnLayout){
35648 //center.el.setStyle("display", "block");
35650 center.updateBox(this.safeBox(centerBox));
35653 this.fireEvent("layout", this);
35657 safeBox : function(box){
35658 box.width = Math.max(0, box.width);
35659 box.height = Math.max(0, box.height);
35664 * Adds a ContentPanel (or subclass) to this layout.
35665 * @param {String} target The target region key (north, south, east, west or center).
35666 * @param {Roo.ContentPanel} panel The panel to add
35667 * @return {Roo.ContentPanel} The added panel
35669 add : function(target, panel){
35671 target = target.toLowerCase();
35672 return this.regions[target].add(panel);
35676 * Remove a ContentPanel (or subclass) to this layout.
35677 * @param {String} target The target region key (north, south, east, west or center).
35678 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35679 * @return {Roo.ContentPanel} The removed panel
35681 remove : function(target, panel){
35682 target = target.toLowerCase();
35683 return this.regions[target].remove(panel);
35687 * Searches all regions for a panel with the specified id
35688 * @param {String} panelId
35689 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35691 findPanel : function(panelId){
35692 var rs = this.regions;
35693 for(var target in rs){
35694 if(typeof rs[target] != "function"){
35695 var p = rs[target].getPanel(panelId);
35705 * Searches all regions for a panel with the specified id and activates (shows) it.
35706 * @param {String/ContentPanel} panelId The panels id or the panel itself
35707 * @return {Roo.ContentPanel} The shown panel or null
35709 showPanel : function(panelId) {
35710 var rs = this.regions;
35711 for(var target in rs){
35712 var r = rs[target];
35713 if(typeof r != "function"){
35714 if(r.hasPanel(panelId)){
35715 return r.showPanel(panelId);
35723 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35724 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35727 restoreState : function(provider){
35729 provider = Roo.state.Manager;
35731 var sm = new Roo.LayoutStateManager();
35732 sm.init(this, provider);
35738 * Adds a xtype elements to the layout.
35742 xtype : 'ContentPanel',
35749 xtype : 'NestedLayoutPanel',
35755 items : [ ... list of content panels or nested layout panels.. ]
35759 * @param {Object} cfg Xtype definition of item to add.
35761 addxtype : function(cfg)
35763 // basically accepts a pannel...
35764 // can accept a layout region..!?!?
35765 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35768 // theory? children can only be panels??
35770 //if (!cfg.xtype.match(/Panel$/)) {
35775 if (typeof(cfg.region) == 'undefined') {
35776 Roo.log("Failed to add Panel, region was not set");
35780 var region = cfg.region;
35786 xitems = cfg.items;
35791 if ( region == 'center') {
35792 Roo.log("Center: " + cfg.title);
35798 case 'Content': // ContentPanel (el, cfg)
35799 case 'Scroll': // ContentPanel (el, cfg)
35801 cfg.autoCreate = cfg.autoCreate || true;
35802 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35804 // var el = this.el.createChild();
35805 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35808 this.add(region, ret);
35812 case 'TreePanel': // our new panel!
35813 cfg.el = this.el.createChild();
35814 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35815 this.add(region, ret);
35820 // create a new Layout (which is a Border Layout...
35822 var clayout = cfg.layout;
35823 clayout.el = this.el.createChild();
35824 clayout.items = clayout.items || [];
35828 // replace this exitems with the clayout ones..
35829 xitems = clayout.items;
35831 // force background off if it's in center...
35832 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35833 cfg.background = false;
35835 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35838 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35839 //console.log('adding nested layout panel ' + cfg.toSource());
35840 this.add(region, ret);
35841 nb = {}; /// find first...
35846 // needs grid and region
35848 //var el = this.getRegion(region).el.createChild();
35850 *var el = this.el.createChild();
35851 // create the grid first...
35852 cfg.grid.container = el;
35853 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35856 if (region == 'center' && this.active ) {
35857 cfg.background = false;
35860 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35862 this.add(region, ret);
35864 if (cfg.background) {
35865 // render grid on panel activation (if panel background)
35866 ret.on('activate', function(gp) {
35867 if (!gp.grid.rendered) {
35868 // gp.grid.render(el);
35872 // cfg.grid.render(el);
35878 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35879 // it was the old xcomponent building that caused this before.
35880 // espeically if border is the top element in the tree.
35890 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35892 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35893 this.add(region, ret);
35897 throw "Can not add '" + cfg.xtype + "' to Border";
35903 this.beginUpdate();
35907 Roo.each(xitems, function(i) {
35908 region = nb && i.region ? i.region : false;
35910 var add = ret.addxtype(i);
35913 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35914 if (!i.background) {
35915 abn[region] = nb[region] ;
35922 // make the last non-background panel active..
35923 //if (nb) { Roo.log(abn); }
35926 for(var r in abn) {
35927 region = this.getRegion(r);
35929 // tried using nb[r], but it does not work..
35931 region.showPanel(abn[r]);
35942 factory : function(cfg)
35945 var validRegions = Roo.bootstrap.layout.Border.regions;
35947 var target = cfg.region;
35950 var r = Roo.bootstrap.layout;
35954 return new r.North(cfg);
35956 return new r.South(cfg);
35958 return new r.East(cfg);
35960 return new r.West(cfg);
35962 return new r.Center(cfg);
35964 throw 'Layout region "'+target+'" not supported.';
35971 * Ext JS Library 1.1.1
35972 * Copyright(c) 2006-2007, Ext JS, LLC.
35974 * Originally Released Under LGPL - original licence link has changed is not relivant.
35977 * <script type="text/javascript">
35981 * @class Roo.bootstrap.layout.Basic
35982 * @extends Roo.util.Observable
35983 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35984 * and does not have a titlebar, tabs or any other features. All it does is size and position
35985 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35986 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35987 * @cfg {string} region the region that it inhabits..
35988 * @cfg {bool} skipConfig skip config?
35992 Roo.bootstrap.layout.Basic = function(config){
35994 this.mgr = config.mgr;
35996 this.position = config.region;
35998 var skipConfig = config.skipConfig;
36002 * @scope Roo.BasicLayoutRegion
36006 * @event beforeremove
36007 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
36008 * @param {Roo.LayoutRegion} this
36009 * @param {Roo.ContentPanel} panel The panel
36010 * @param {Object} e The cancel event object
36012 "beforeremove" : true,
36014 * @event invalidated
36015 * Fires when the layout for this region is changed.
36016 * @param {Roo.LayoutRegion} this
36018 "invalidated" : true,
36020 * @event visibilitychange
36021 * Fires when this region is shown or hidden
36022 * @param {Roo.LayoutRegion} this
36023 * @param {Boolean} visibility true or false
36025 "visibilitychange" : true,
36027 * @event paneladded
36028 * Fires when a panel is added.
36029 * @param {Roo.LayoutRegion} this
36030 * @param {Roo.ContentPanel} panel The panel
36032 "paneladded" : true,
36034 * @event panelremoved
36035 * Fires when a panel is removed.
36036 * @param {Roo.LayoutRegion} this
36037 * @param {Roo.ContentPanel} panel The panel
36039 "panelremoved" : true,
36041 * @event beforecollapse
36042 * Fires when this region before collapse.
36043 * @param {Roo.LayoutRegion} this
36045 "beforecollapse" : true,
36048 * Fires when this region is collapsed.
36049 * @param {Roo.LayoutRegion} this
36051 "collapsed" : true,
36054 * Fires when this region is expanded.
36055 * @param {Roo.LayoutRegion} this
36060 * Fires when this region is slid into view.
36061 * @param {Roo.LayoutRegion} this
36063 "slideshow" : true,
36066 * Fires when this region slides out of view.
36067 * @param {Roo.LayoutRegion} this
36069 "slidehide" : true,
36071 * @event panelactivated
36072 * Fires when a panel is activated.
36073 * @param {Roo.LayoutRegion} this
36074 * @param {Roo.ContentPanel} panel The activated panel
36076 "panelactivated" : true,
36079 * Fires when the user resizes this region.
36080 * @param {Roo.LayoutRegion} this
36081 * @param {Number} newSize The new size (width for east/west, height for north/south)
36085 /** A collection of panels in this region. @type Roo.util.MixedCollection */
36086 this.panels = new Roo.util.MixedCollection();
36087 this.panels.getKey = this.getPanelId.createDelegate(this);
36089 this.activePanel = null;
36090 // ensure listeners are added...
36092 if (config.listeners || config.events) {
36093 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
36094 listeners : config.listeners || {},
36095 events : config.events || {}
36099 if(skipConfig !== true){
36100 this.applyConfig(config);
36104 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
36106 getPanelId : function(p){
36110 applyConfig : function(config){
36111 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36112 this.config = config;
36117 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
36118 * the width, for horizontal (north, south) the height.
36119 * @param {Number} newSize The new width or height
36121 resizeTo : function(newSize){
36122 var el = this.el ? this.el :
36123 (this.activePanel ? this.activePanel.getEl() : null);
36125 switch(this.position){
36128 el.setWidth(newSize);
36129 this.fireEvent("resized", this, newSize);
36133 el.setHeight(newSize);
36134 this.fireEvent("resized", this, newSize);
36140 getBox : function(){
36141 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
36144 getMargins : function(){
36145 return this.margins;
36148 updateBox : function(box){
36150 var el = this.activePanel.getEl();
36151 el.dom.style.left = box.x + "px";
36152 el.dom.style.top = box.y + "px";
36153 this.activePanel.setSize(box.width, box.height);
36157 * Returns the container element for this region.
36158 * @return {Roo.Element}
36160 getEl : function(){
36161 return this.activePanel;
36165 * Returns true if this region is currently visible.
36166 * @return {Boolean}
36168 isVisible : function(){
36169 return this.activePanel ? true : false;
36172 setActivePanel : function(panel){
36173 panel = this.getPanel(panel);
36174 if(this.activePanel && this.activePanel != panel){
36175 this.activePanel.setActiveState(false);
36176 this.activePanel.getEl().setLeftTop(-10000,-10000);
36178 this.activePanel = panel;
36179 panel.setActiveState(true);
36181 panel.setSize(this.box.width, this.box.height);
36183 this.fireEvent("panelactivated", this, panel);
36184 this.fireEvent("invalidated");
36188 * Show the specified panel.
36189 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
36190 * @return {Roo.ContentPanel} The shown panel or null
36192 showPanel : function(panel){
36193 panel = this.getPanel(panel);
36195 this.setActivePanel(panel);
36201 * Get the active panel for this region.
36202 * @return {Roo.ContentPanel} The active panel or null
36204 getActivePanel : function(){
36205 return this.activePanel;
36209 * Add the passed ContentPanel(s)
36210 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36211 * @return {Roo.ContentPanel} The panel added (if only one was added)
36213 add : function(panel){
36214 if(arguments.length > 1){
36215 for(var i = 0, len = arguments.length; i < len; i++) {
36216 this.add(arguments[i]);
36220 if(this.hasPanel(panel)){
36221 this.showPanel(panel);
36224 var el = panel.getEl();
36225 if(el.dom.parentNode != this.mgr.el.dom){
36226 this.mgr.el.dom.appendChild(el.dom);
36228 if(panel.setRegion){
36229 panel.setRegion(this);
36231 this.panels.add(panel);
36232 el.setStyle("position", "absolute");
36233 if(!panel.background){
36234 this.setActivePanel(panel);
36235 if(this.config.initialSize && this.panels.getCount()==1){
36236 this.resizeTo(this.config.initialSize);
36239 this.fireEvent("paneladded", this, panel);
36244 * Returns true if the panel is in this region.
36245 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36246 * @return {Boolean}
36248 hasPanel : function(panel){
36249 if(typeof panel == "object"){ // must be panel obj
36250 panel = panel.getId();
36252 return this.getPanel(panel) ? true : false;
36256 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36257 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36258 * @param {Boolean} preservePanel Overrides the config preservePanel option
36259 * @return {Roo.ContentPanel} The panel that was removed
36261 remove : function(panel, preservePanel){
36262 panel = this.getPanel(panel);
36267 this.fireEvent("beforeremove", this, panel, e);
36268 if(e.cancel === true){
36271 var panelId = panel.getId();
36272 this.panels.removeKey(panelId);
36277 * Returns the panel specified or null if it's not in this region.
36278 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36279 * @return {Roo.ContentPanel}
36281 getPanel : function(id){
36282 if(typeof id == "object"){ // must be panel obj
36285 return this.panels.get(id);
36289 * Returns this regions position (north/south/east/west/center).
36292 getPosition: function(){
36293 return this.position;
36297 * Ext JS Library 1.1.1
36298 * Copyright(c) 2006-2007, Ext JS, LLC.
36300 * Originally Released Under LGPL - original licence link has changed is not relivant.
36303 * <script type="text/javascript">
36307 * @class Roo.bootstrap.layout.Region
36308 * @extends Roo.bootstrap.layout.Basic
36309 * This class represents a region in a layout manager.
36311 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
36312 * @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})
36313 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
36314 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
36315 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
36316 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
36317 * @cfg {String} title The title for the region (overrides panel titles)
36318 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
36319 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36320 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
36321 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
36322 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
36323 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
36324 * the space available, similar to FireFox 1.5 tabs (defaults to false)
36325 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
36326 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
36327 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
36329 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
36330 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
36331 * @cfg {Boolean} disableTabTips True to disable tab tooltips
36332 * @cfg {Number} width For East/West panels
36333 * @cfg {Number} height For North/South panels
36334 * @cfg {Boolean} split To show the splitter
36335 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
36337 * @cfg {string} cls Extra CSS classes to add to region
36339 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
36340 * @cfg {string} region the region that it inhabits..
36343 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
36344 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
36346 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
36347 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
36348 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
36350 Roo.bootstrap.layout.Region = function(config)
36352 this.applyConfig(config);
36354 var mgr = config.mgr;
36355 var pos = config.region;
36356 config.skipConfig = true;
36357 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36360 this.onRender(mgr.el);
36363 this.visible = true;
36364 this.collapsed = false;
36365 this.unrendered_panels = [];
36368 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36370 position: '', // set by wrapper (eg. north/south etc..)
36371 unrendered_panels : null, // unrendered panels.
36373 tabPosition : false,
36375 mgr: false, // points to 'Border'
36378 createBody : function(){
36379 /** This region's body element
36380 * @type Roo.Element */
36381 this.bodyEl = this.el.createChild({
36383 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36387 onRender: function(ctr, pos)
36389 var dh = Roo.DomHelper;
36390 /** This region's container element
36391 * @type Roo.Element */
36392 this.el = dh.append(ctr.dom, {
36394 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36396 /** This region's title element
36397 * @type Roo.Element */
36399 this.titleEl = dh.append(this.el.dom, {
36401 unselectable: "on",
36402 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36404 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
36405 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36409 this.titleEl.enableDisplayMode();
36410 /** This region's title text element
36411 * @type HTMLElement */
36412 this.titleTextEl = this.titleEl.dom.firstChild;
36413 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36415 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36416 this.closeBtn.enableDisplayMode();
36417 this.closeBtn.on("click", this.closeClicked, this);
36418 this.closeBtn.hide();
36420 this.createBody(this.config);
36421 if(this.config.hideWhenEmpty){
36423 this.on("paneladded", this.validateVisibility, this);
36424 this.on("panelremoved", this.validateVisibility, this);
36426 if(this.autoScroll){
36427 this.bodyEl.setStyle("overflow", "auto");
36429 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36431 //if(c.titlebar !== false){
36432 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36433 this.titleEl.hide();
36435 this.titleEl.show();
36436 if(this.config.title){
36437 this.titleTextEl.innerHTML = this.config.title;
36441 if(this.config.collapsed){
36442 this.collapse(true);
36444 if(this.config.hidden){
36448 if (this.unrendered_panels && this.unrendered_panels.length) {
36449 for (var i =0;i< this.unrendered_panels.length; i++) {
36450 this.add(this.unrendered_panels[i]);
36452 this.unrendered_panels = null;
36458 applyConfig : function(c)
36461 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36462 var dh = Roo.DomHelper;
36463 if(c.titlebar !== false){
36464 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36465 this.collapseBtn.on("click", this.collapse, this);
36466 this.collapseBtn.enableDisplayMode();
36468 if(c.showPin === true || this.showPin){
36469 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36470 this.stickBtn.enableDisplayMode();
36471 this.stickBtn.on("click", this.expand, this);
36472 this.stickBtn.hide();
36477 /** This region's collapsed element
36478 * @type Roo.Element */
36481 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36482 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36485 if(c.floatable !== false){
36486 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36487 this.collapsedEl.on("click", this.collapseClick, this);
36490 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36491 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36492 id: "message", unselectable: "on", style:{"float":"left"}});
36493 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36495 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36496 this.expandBtn.on("click", this.expand, this);
36500 if(this.collapseBtn){
36501 this.collapseBtn.setVisible(c.collapsible == true);
36504 this.cmargins = c.cmargins || this.cmargins ||
36505 (this.position == "west" || this.position == "east" ?
36506 {top: 0, left: 2, right:2, bottom: 0} :
36507 {top: 2, left: 0, right:0, bottom: 2});
36509 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36512 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36514 this.autoScroll = c.autoScroll || false;
36519 this.duration = c.duration || .30;
36520 this.slideDuration = c.slideDuration || .45;
36525 * Returns true if this region is currently visible.
36526 * @return {Boolean}
36528 isVisible : function(){
36529 return this.visible;
36533 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36534 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
36536 //setCollapsedTitle : function(title){
36537 // title = title || " ";
36538 // if(this.collapsedTitleTextEl){
36539 // this.collapsedTitleTextEl.innerHTML = title;
36543 getBox : function(){
36545 // if(!this.collapsed){
36546 b = this.el.getBox(false, true);
36548 // b = this.collapsedEl.getBox(false, true);
36553 getMargins : function(){
36554 return this.margins;
36555 //return this.collapsed ? this.cmargins : this.margins;
36558 highlight : function(){
36559 this.el.addClass("x-layout-panel-dragover");
36562 unhighlight : function(){
36563 this.el.removeClass("x-layout-panel-dragover");
36566 updateBox : function(box)
36568 if (!this.bodyEl) {
36569 return; // not rendered yet..
36573 if(!this.collapsed){
36574 this.el.dom.style.left = box.x + "px";
36575 this.el.dom.style.top = box.y + "px";
36576 this.updateBody(box.width, box.height);
36578 this.collapsedEl.dom.style.left = box.x + "px";
36579 this.collapsedEl.dom.style.top = box.y + "px";
36580 this.collapsedEl.setSize(box.width, box.height);
36583 this.tabs.autoSizeTabs();
36587 updateBody : function(w, h)
36590 this.el.setWidth(w);
36591 w -= this.el.getBorderWidth("rl");
36592 if(this.config.adjustments){
36593 w += this.config.adjustments[0];
36596 if(h !== null && h > 0){
36597 this.el.setHeight(h);
36598 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36599 h -= this.el.getBorderWidth("tb");
36600 if(this.config.adjustments){
36601 h += this.config.adjustments[1];
36603 this.bodyEl.setHeight(h);
36605 h = this.tabs.syncHeight(h);
36608 if(this.panelSize){
36609 w = w !== null ? w : this.panelSize.width;
36610 h = h !== null ? h : this.panelSize.height;
36612 if(this.activePanel){
36613 var el = this.activePanel.getEl();
36614 w = w !== null ? w : el.getWidth();
36615 h = h !== null ? h : el.getHeight();
36616 this.panelSize = {width: w, height: h};
36617 this.activePanel.setSize(w, h);
36619 if(Roo.isIE && this.tabs){
36620 this.tabs.el.repaint();
36625 * Returns the container element for this region.
36626 * @return {Roo.Element}
36628 getEl : function(){
36633 * Hides this region.
36636 //if(!this.collapsed){
36637 this.el.dom.style.left = "-2000px";
36640 // this.collapsedEl.dom.style.left = "-2000px";
36641 // this.collapsedEl.hide();
36643 this.visible = false;
36644 this.fireEvent("visibilitychange", this, false);
36648 * Shows this region if it was previously hidden.
36651 //if(!this.collapsed){
36654 // this.collapsedEl.show();
36656 this.visible = true;
36657 this.fireEvent("visibilitychange", this, true);
36660 closeClicked : function(){
36661 if(this.activePanel){
36662 this.remove(this.activePanel);
36666 collapseClick : function(e){
36668 e.stopPropagation();
36671 e.stopPropagation();
36677 * Collapses this region.
36678 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36681 collapse : function(skipAnim, skipCheck = false){
36682 if(this.collapsed) {
36686 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36688 this.collapsed = true;
36690 this.split.el.hide();
36692 if(this.config.animate && skipAnim !== true){
36693 this.fireEvent("invalidated", this);
36694 this.animateCollapse();
36696 this.el.setLocation(-20000,-20000);
36698 this.collapsedEl.show();
36699 this.fireEvent("collapsed", this);
36700 this.fireEvent("invalidated", this);
36706 animateCollapse : function(){
36711 * Expands this region if it was previously collapsed.
36712 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36713 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36716 expand : function(e, skipAnim){
36718 e.stopPropagation();
36720 if(!this.collapsed || this.el.hasActiveFx()) {
36724 this.afterSlideIn();
36727 this.collapsed = false;
36728 if(this.config.animate && skipAnim !== true){
36729 this.animateExpand();
36733 this.split.el.show();
36735 this.collapsedEl.setLocation(-2000,-2000);
36736 this.collapsedEl.hide();
36737 this.fireEvent("invalidated", this);
36738 this.fireEvent("expanded", this);
36742 animateExpand : function(){
36746 initTabs : function()
36748 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36750 var ts = new Roo.bootstrap.panel.Tabs({
36751 el: this.bodyEl.dom,
36753 tabPosition: this.tabPosition ? this.tabPosition : 'top',
36754 disableTooltips: this.config.disableTabTips,
36755 toolbar : this.config.toolbar
36758 if(this.config.hideTabs){
36759 ts.stripWrap.setDisplayed(false);
36762 ts.resizeTabs = this.config.resizeTabs === true;
36763 ts.minTabWidth = this.config.minTabWidth || 40;
36764 ts.maxTabWidth = this.config.maxTabWidth || 250;
36765 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36766 ts.monitorResize = false;
36767 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36768 ts.bodyEl.addClass('roo-layout-tabs-body');
36769 this.panels.each(this.initPanelAsTab, this);
36772 initPanelAsTab : function(panel){
36773 var ti = this.tabs.addTab(
36777 this.config.closeOnTab && panel.isClosable(),
36780 if(panel.tabTip !== undefined){
36781 ti.setTooltip(panel.tabTip);
36783 ti.on("activate", function(){
36784 this.setActivePanel(panel);
36787 if(this.config.closeOnTab){
36788 ti.on("beforeclose", function(t, e){
36790 this.remove(panel);
36794 panel.tabItem = ti;
36799 updatePanelTitle : function(panel, title)
36801 if(this.activePanel == panel){
36802 this.updateTitle(title);
36805 var ti = this.tabs.getTab(panel.getEl().id);
36807 if(panel.tabTip !== undefined){
36808 ti.setTooltip(panel.tabTip);
36813 updateTitle : function(title){
36814 if(this.titleTextEl && !this.config.title){
36815 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36819 setActivePanel : function(panel)
36821 panel = this.getPanel(panel);
36822 if(this.activePanel && this.activePanel != panel){
36823 if(this.activePanel.setActiveState(false) === false){
36827 this.activePanel = panel;
36828 panel.setActiveState(true);
36829 if(this.panelSize){
36830 panel.setSize(this.panelSize.width, this.panelSize.height);
36833 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36835 this.updateTitle(panel.getTitle());
36837 this.fireEvent("invalidated", this);
36839 this.fireEvent("panelactivated", this, panel);
36843 * Shows the specified panel.
36844 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36845 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36847 showPanel : function(panel)
36849 panel = this.getPanel(panel);
36852 var tab = this.tabs.getTab(panel.getEl().id);
36853 if(tab.isHidden()){
36854 this.tabs.unhideTab(tab.id);
36858 this.setActivePanel(panel);
36865 * Get the active panel for this region.
36866 * @return {Roo.ContentPanel} The active panel or null
36868 getActivePanel : function(){
36869 return this.activePanel;
36872 validateVisibility : function(){
36873 if(this.panels.getCount() < 1){
36874 this.updateTitle(" ");
36875 this.closeBtn.hide();
36878 if(!this.isVisible()){
36885 * Adds the passed ContentPanel(s) to this region.
36886 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36887 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36889 add : function(panel)
36891 if(arguments.length > 1){
36892 for(var i = 0, len = arguments.length; i < len; i++) {
36893 this.add(arguments[i]);
36898 // if we have not been rendered yet, then we can not really do much of this..
36899 if (!this.bodyEl) {
36900 this.unrendered_panels.push(panel);
36907 if(this.hasPanel(panel)){
36908 this.showPanel(panel);
36911 panel.setRegion(this);
36912 this.panels.add(panel);
36913 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36914 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36915 // and hide them... ???
36916 this.bodyEl.dom.appendChild(panel.getEl().dom);
36917 if(panel.background !== true){
36918 this.setActivePanel(panel);
36920 this.fireEvent("paneladded", this, panel);
36927 this.initPanelAsTab(panel);
36931 if(panel.background !== true){
36932 this.tabs.activate(panel.getEl().id);
36934 this.fireEvent("paneladded", this, panel);
36939 * Hides the tab for the specified panel.
36940 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36942 hidePanel : function(panel){
36943 if(this.tabs && (panel = this.getPanel(panel))){
36944 this.tabs.hideTab(panel.getEl().id);
36949 * Unhides the tab for a previously hidden panel.
36950 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36952 unhidePanel : function(panel){
36953 if(this.tabs && (panel = this.getPanel(panel))){
36954 this.tabs.unhideTab(panel.getEl().id);
36958 clearPanels : function(){
36959 while(this.panels.getCount() > 0){
36960 this.remove(this.panels.first());
36965 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36966 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36967 * @param {Boolean} preservePanel Overrides the config preservePanel option
36968 * @return {Roo.ContentPanel} The panel that was removed
36970 remove : function(panel, preservePanel)
36972 panel = this.getPanel(panel);
36977 this.fireEvent("beforeremove", this, panel, e);
36978 if(e.cancel === true){
36981 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36982 var panelId = panel.getId();
36983 this.panels.removeKey(panelId);
36985 document.body.appendChild(panel.getEl().dom);
36988 this.tabs.removeTab(panel.getEl().id);
36989 }else if (!preservePanel){
36990 this.bodyEl.dom.removeChild(panel.getEl().dom);
36992 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36993 var p = this.panels.first();
36994 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36995 tempEl.appendChild(p.getEl().dom);
36996 this.bodyEl.update("");
36997 this.bodyEl.dom.appendChild(p.getEl().dom);
36999 this.updateTitle(p.getTitle());
37001 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
37002 this.setActivePanel(p);
37004 panel.setRegion(null);
37005 if(this.activePanel == panel){
37006 this.activePanel = null;
37008 if(this.config.autoDestroy !== false && preservePanel !== true){
37009 try{panel.destroy();}catch(e){}
37011 this.fireEvent("panelremoved", this, panel);
37016 * Returns the TabPanel component used by this region
37017 * @return {Roo.TabPanel}
37019 getTabs : function(){
37023 createTool : function(parentEl, className){
37024 var btn = Roo.DomHelper.append(parentEl, {
37026 cls: "x-layout-tools-button",
37029 cls: "roo-layout-tools-button-inner " + className,
37033 btn.addClassOnOver("roo-layout-tools-button-over");
37038 * Ext JS Library 1.1.1
37039 * Copyright(c) 2006-2007, Ext JS, LLC.
37041 * Originally Released Under LGPL - original licence link has changed is not relivant.
37044 * <script type="text/javascript">
37050 * @class Roo.SplitLayoutRegion
37051 * @extends Roo.LayoutRegion
37052 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
37054 Roo.bootstrap.layout.Split = function(config){
37055 this.cursor = config.cursor;
37056 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
37059 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
37061 splitTip : "Drag to resize.",
37062 collapsibleSplitTip : "Drag to resize. Double click to hide.",
37063 useSplitTips : false,
37065 applyConfig : function(config){
37066 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
37069 onRender : function(ctr,pos) {
37071 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
37072 if(!this.config.split){
37077 var splitEl = Roo.DomHelper.append(ctr.dom, {
37079 id: this.el.id + "-split",
37080 cls: "roo-layout-split roo-layout-split-"+this.position,
37083 /** The SplitBar for this region
37084 * @type Roo.SplitBar */
37085 // does not exist yet...
37086 Roo.log([this.position, this.orientation]);
37088 this.split = new Roo.bootstrap.SplitBar({
37089 dragElement : splitEl,
37090 resizingElement: this.el,
37091 orientation : this.orientation
37094 this.split.on("moved", this.onSplitMove, this);
37095 this.split.useShim = this.config.useShim === true;
37096 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
37097 if(this.useSplitTips){
37098 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
37100 //if(config.collapsible){
37101 // this.split.el.on("dblclick", this.collapse, this);
37104 if(typeof this.config.minSize != "undefined"){
37105 this.split.minSize = this.config.minSize;
37107 if(typeof this.config.maxSize != "undefined"){
37108 this.split.maxSize = this.config.maxSize;
37110 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
37111 this.hideSplitter();
37116 getHMaxSize : function(){
37117 var cmax = this.config.maxSize || 10000;
37118 var center = this.mgr.getRegion("center");
37119 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
37122 getVMaxSize : function(){
37123 var cmax = this.config.maxSize || 10000;
37124 var center = this.mgr.getRegion("center");
37125 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
37128 onSplitMove : function(split, newSize){
37129 this.fireEvent("resized", this, newSize);
37133 * Returns the {@link Roo.SplitBar} for this region.
37134 * @return {Roo.SplitBar}
37136 getSplitBar : function(){
37141 this.hideSplitter();
37142 Roo.bootstrap.layout.Split.superclass.hide.call(this);
37145 hideSplitter : function(){
37147 this.split.el.setLocation(-2000,-2000);
37148 this.split.el.hide();
37154 this.split.el.show();
37156 Roo.bootstrap.layout.Split.superclass.show.call(this);
37159 beforeSlide: function(){
37160 if(Roo.isGecko){// firefox overflow auto bug workaround
37161 this.bodyEl.clip();
37163 this.tabs.bodyEl.clip();
37165 if(this.activePanel){
37166 this.activePanel.getEl().clip();
37168 if(this.activePanel.beforeSlide){
37169 this.activePanel.beforeSlide();
37175 afterSlide : function(){
37176 if(Roo.isGecko){// firefox overflow auto bug workaround
37177 this.bodyEl.unclip();
37179 this.tabs.bodyEl.unclip();
37181 if(this.activePanel){
37182 this.activePanel.getEl().unclip();
37183 if(this.activePanel.afterSlide){
37184 this.activePanel.afterSlide();
37190 initAutoHide : function(){
37191 if(this.autoHide !== false){
37192 if(!this.autoHideHd){
37193 var st = new Roo.util.DelayedTask(this.slideIn, this);
37194 this.autoHideHd = {
37195 "mouseout": function(e){
37196 if(!e.within(this.el, true)){
37200 "mouseover" : function(e){
37206 this.el.on(this.autoHideHd);
37210 clearAutoHide : function(){
37211 if(this.autoHide !== false){
37212 this.el.un("mouseout", this.autoHideHd.mouseout);
37213 this.el.un("mouseover", this.autoHideHd.mouseover);
37217 clearMonitor : function(){
37218 Roo.get(document).un("click", this.slideInIf, this);
37221 // these names are backwards but not changed for compat
37222 slideOut : function(){
37223 if(this.isSlid || this.el.hasActiveFx()){
37226 this.isSlid = true;
37227 if(this.collapseBtn){
37228 this.collapseBtn.hide();
37230 this.closeBtnState = this.closeBtn.getStyle('display');
37231 this.closeBtn.hide();
37233 this.stickBtn.show();
37236 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
37237 this.beforeSlide();
37238 this.el.setStyle("z-index", 10001);
37239 this.el.slideIn(this.getSlideAnchor(), {
37240 callback: function(){
37242 this.initAutoHide();
37243 Roo.get(document).on("click", this.slideInIf, this);
37244 this.fireEvent("slideshow", this);
37251 afterSlideIn : function(){
37252 this.clearAutoHide();
37253 this.isSlid = false;
37254 this.clearMonitor();
37255 this.el.setStyle("z-index", "");
37256 if(this.collapseBtn){
37257 this.collapseBtn.show();
37259 this.closeBtn.setStyle('display', this.closeBtnState);
37261 this.stickBtn.hide();
37263 this.fireEvent("slidehide", this);
37266 slideIn : function(cb){
37267 if(!this.isSlid || this.el.hasActiveFx()){
37271 this.isSlid = false;
37272 this.beforeSlide();
37273 this.el.slideOut(this.getSlideAnchor(), {
37274 callback: function(){
37275 this.el.setLeftTop(-10000, -10000);
37277 this.afterSlideIn();
37285 slideInIf : function(e){
37286 if(!e.within(this.el)){
37291 animateCollapse : function(){
37292 this.beforeSlide();
37293 this.el.setStyle("z-index", 20000);
37294 var anchor = this.getSlideAnchor();
37295 this.el.slideOut(anchor, {
37296 callback : function(){
37297 this.el.setStyle("z-index", "");
37298 this.collapsedEl.slideIn(anchor, {duration:.3});
37300 this.el.setLocation(-10000,-10000);
37302 this.fireEvent("collapsed", this);
37309 animateExpand : function(){
37310 this.beforeSlide();
37311 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
37312 this.el.setStyle("z-index", 20000);
37313 this.collapsedEl.hide({
37316 this.el.slideIn(this.getSlideAnchor(), {
37317 callback : function(){
37318 this.el.setStyle("z-index", "");
37321 this.split.el.show();
37323 this.fireEvent("invalidated", this);
37324 this.fireEvent("expanded", this);
37352 getAnchor : function(){
37353 return this.anchors[this.position];
37356 getCollapseAnchor : function(){
37357 return this.canchors[this.position];
37360 getSlideAnchor : function(){
37361 return this.sanchors[this.position];
37364 getAlignAdj : function(){
37365 var cm = this.cmargins;
37366 switch(this.position){
37382 getExpandAdj : function(){
37383 var c = this.collapsedEl, cm = this.cmargins;
37384 switch(this.position){
37386 return [-(cm.right+c.getWidth()+cm.left), 0];
37389 return [cm.right+c.getWidth()+cm.left, 0];
37392 return [0, -(cm.top+cm.bottom+c.getHeight())];
37395 return [0, cm.top+cm.bottom+c.getHeight()];
37401 * Ext JS Library 1.1.1
37402 * Copyright(c) 2006-2007, Ext JS, LLC.
37404 * Originally Released Under LGPL - original licence link has changed is not relivant.
37407 * <script type="text/javascript">
37410 * These classes are private internal classes
37412 Roo.bootstrap.layout.Center = function(config){
37413 config.region = "center";
37414 Roo.bootstrap.layout.Region.call(this, config);
37415 this.visible = true;
37416 this.minWidth = config.minWidth || 20;
37417 this.minHeight = config.minHeight || 20;
37420 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37422 // center panel can't be hidden
37426 // center panel can't be hidden
37429 getMinWidth: function(){
37430 return this.minWidth;
37433 getMinHeight: function(){
37434 return this.minHeight;
37448 Roo.bootstrap.layout.North = function(config)
37450 config.region = 'north';
37451 config.cursor = 'n-resize';
37453 Roo.bootstrap.layout.Split.call(this, config);
37457 this.split.placement = Roo.bootstrap.SplitBar.TOP;
37458 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37459 this.split.el.addClass("roo-layout-split-v");
37461 var size = config.initialSize || config.height;
37462 if(typeof size != "undefined"){
37463 this.el.setHeight(size);
37466 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37468 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37472 getBox : function(){
37473 if(this.collapsed){
37474 return this.collapsedEl.getBox();
37476 var box = this.el.getBox();
37478 box.height += this.split.el.getHeight();
37483 updateBox : function(box){
37484 if(this.split && !this.collapsed){
37485 box.height -= this.split.el.getHeight();
37486 this.split.el.setLeft(box.x);
37487 this.split.el.setTop(box.y+box.height);
37488 this.split.el.setWidth(box.width);
37490 if(this.collapsed){
37491 this.updateBody(box.width, null);
37493 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37501 Roo.bootstrap.layout.South = function(config){
37502 config.region = 'south';
37503 config.cursor = 's-resize';
37504 Roo.bootstrap.layout.Split.call(this, config);
37506 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37507 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37508 this.split.el.addClass("roo-layout-split-v");
37510 var size = config.initialSize || config.height;
37511 if(typeof size != "undefined"){
37512 this.el.setHeight(size);
37516 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37517 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37518 getBox : function(){
37519 if(this.collapsed){
37520 return this.collapsedEl.getBox();
37522 var box = this.el.getBox();
37524 var sh = this.split.el.getHeight();
37531 updateBox : function(box){
37532 if(this.split && !this.collapsed){
37533 var sh = this.split.el.getHeight();
37536 this.split.el.setLeft(box.x);
37537 this.split.el.setTop(box.y-sh);
37538 this.split.el.setWidth(box.width);
37540 if(this.collapsed){
37541 this.updateBody(box.width, null);
37543 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37547 Roo.bootstrap.layout.East = function(config){
37548 config.region = "east";
37549 config.cursor = "e-resize";
37550 Roo.bootstrap.layout.Split.call(this, config);
37552 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37553 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37554 this.split.el.addClass("roo-layout-split-h");
37556 var size = config.initialSize || config.width;
37557 if(typeof size != "undefined"){
37558 this.el.setWidth(size);
37561 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37562 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37563 getBox : function(){
37564 if(this.collapsed){
37565 return this.collapsedEl.getBox();
37567 var box = this.el.getBox();
37569 var sw = this.split.el.getWidth();
37576 updateBox : function(box){
37577 if(this.split && !this.collapsed){
37578 var sw = this.split.el.getWidth();
37580 this.split.el.setLeft(box.x);
37581 this.split.el.setTop(box.y);
37582 this.split.el.setHeight(box.height);
37585 if(this.collapsed){
37586 this.updateBody(null, box.height);
37588 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37592 Roo.bootstrap.layout.West = function(config){
37593 config.region = "west";
37594 config.cursor = "w-resize";
37596 Roo.bootstrap.layout.Split.call(this, config);
37598 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37599 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37600 this.split.el.addClass("roo-layout-split-h");
37604 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37605 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37607 onRender: function(ctr, pos)
37609 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37610 var size = this.config.initialSize || this.config.width;
37611 if(typeof size != "undefined"){
37612 this.el.setWidth(size);
37616 getBox : function(){
37617 if(this.collapsed){
37618 return this.collapsedEl.getBox();
37620 var box = this.el.getBox();
37622 box.width += this.split.el.getWidth();
37627 updateBox : function(box){
37628 if(this.split && !this.collapsed){
37629 var sw = this.split.el.getWidth();
37631 this.split.el.setLeft(box.x+box.width);
37632 this.split.el.setTop(box.y);
37633 this.split.el.setHeight(box.height);
37635 if(this.collapsed){
37636 this.updateBody(null, box.height);
37638 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37640 });Roo.namespace("Roo.bootstrap.panel");/*
37642 * Ext JS Library 1.1.1
37643 * Copyright(c) 2006-2007, Ext JS, LLC.
37645 * Originally Released Under LGPL - original licence link has changed is not relivant.
37648 * <script type="text/javascript">
37651 * @class Roo.ContentPanel
37652 * @extends Roo.util.Observable
37653 * A basic ContentPanel element.
37654 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
37655 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
37656 * @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
37657 * @cfg {Boolean} closable True if the panel can be closed/removed
37658 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
37659 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37660 * @cfg {Toolbar} toolbar A toolbar for this panel
37661 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37662 * @cfg {String} title The title for this panel
37663 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37664 * @cfg {String} url Calls {@link #setUrl} with this value
37665 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37666 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37667 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37668 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37669 * @cfg {Boolean} badges render the badges
37672 * Create a new ContentPanel.
37673 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37674 * @param {String/Object} config A string to set only the title or a config object
37675 * @param {String} content (optional) Set the HTML content for this panel
37676 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37678 Roo.bootstrap.panel.Content = function( config){
37680 this.tpl = config.tpl || false;
37682 var el = config.el;
37683 var content = config.content;
37685 if(config.autoCreate){ // xtype is available if this is called from factory
37688 this.el = Roo.get(el);
37689 if(!this.el && config && config.autoCreate){
37690 if(typeof config.autoCreate == "object"){
37691 if(!config.autoCreate.id){
37692 config.autoCreate.id = config.id||el;
37694 this.el = Roo.DomHelper.append(document.body,
37695 config.autoCreate, true);
37697 var elcfg = { tag: "div",
37698 cls: "roo-layout-inactive-content",
37702 elcfg.html = config.html;
37706 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37709 this.closable = false;
37710 this.loaded = false;
37711 this.active = false;
37714 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37716 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37718 this.wrapEl = this.el; //this.el.wrap();
37720 if (config.toolbar.items) {
37721 ti = config.toolbar.items ;
37722 delete config.toolbar.items ;
37726 this.toolbar.render(this.wrapEl, 'before');
37727 for(var i =0;i < ti.length;i++) {
37728 // Roo.log(['add child', items[i]]);
37729 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37731 this.toolbar.items = nitems;
37732 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37733 delete config.toolbar;
37737 // xtype created footer. - not sure if will work as we normally have to render first..
37738 if (this.footer && !this.footer.el && this.footer.xtype) {
37739 if (!this.wrapEl) {
37740 this.wrapEl = this.el.wrap();
37743 this.footer.container = this.wrapEl.createChild();
37745 this.footer = Roo.factory(this.footer, Roo);
37750 if(typeof config == "string"){
37751 this.title = config;
37753 Roo.apply(this, config);
37757 this.resizeEl = Roo.get(this.resizeEl, true);
37759 this.resizeEl = this.el;
37761 // handle view.xtype
37769 * Fires when this panel is activated.
37770 * @param {Roo.ContentPanel} this
37774 * @event deactivate
37775 * Fires when this panel is activated.
37776 * @param {Roo.ContentPanel} this
37778 "deactivate" : true,
37782 * Fires when this panel is resized if fitToFrame is true.
37783 * @param {Roo.ContentPanel} this
37784 * @param {Number} width The width after any component adjustments
37785 * @param {Number} height The height after any component adjustments
37791 * Fires when this tab is created
37792 * @param {Roo.ContentPanel} this
37803 if(this.autoScroll){
37804 this.resizeEl.setStyle("overflow", "auto");
37806 // fix randome scrolling
37807 //this.el.on('scroll', function() {
37808 // Roo.log('fix random scolling');
37809 // this.scrollTo('top',0);
37812 content = content || this.content;
37814 this.setContent(content);
37816 if(config && config.url){
37817 this.setUrl(this.url, this.params, this.loadOnce);
37822 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37824 if (this.view && typeof(this.view.xtype) != 'undefined') {
37825 this.view.el = this.el.appendChild(document.createElement("div"));
37826 this.view = Roo.factory(this.view);
37827 this.view.render && this.view.render(false, '');
37831 this.fireEvent('render', this);
37834 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37838 setRegion : function(region){
37839 this.region = region;
37840 this.setActiveClass(region && !this.background);
37844 setActiveClass: function(state)
37847 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37848 this.el.setStyle('position','relative');
37850 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37851 this.el.setStyle('position', 'absolute');
37856 * Returns the toolbar for this Panel if one was configured.
37857 * @return {Roo.Toolbar}
37859 getToolbar : function(){
37860 return this.toolbar;
37863 setActiveState : function(active)
37865 this.active = active;
37866 this.setActiveClass(active);
37868 if(this.fireEvent("deactivate", this) === false){
37873 this.fireEvent("activate", this);
37877 * Updates this panel's element
37878 * @param {String} content The new content
37879 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37881 setContent : function(content, loadScripts){
37882 this.el.update(content, loadScripts);
37885 ignoreResize : function(w, h){
37886 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37889 this.lastSize = {width: w, height: h};
37894 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37895 * @return {Roo.UpdateManager} The UpdateManager
37897 getUpdateManager : function(){
37898 return this.el.getUpdateManager();
37901 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37902 * @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:
37905 url: "your-url.php",
37906 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37907 callback: yourFunction,
37908 scope: yourObject, //(optional scope)
37911 text: "Loading...",
37916 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37917 * 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.
37918 * @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}
37919 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37920 * @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.
37921 * @return {Roo.ContentPanel} this
37924 var um = this.el.getUpdateManager();
37925 um.update.apply(um, arguments);
37931 * 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.
37932 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37933 * @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)
37934 * @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)
37935 * @return {Roo.UpdateManager} The UpdateManager
37937 setUrl : function(url, params, loadOnce){
37938 if(this.refreshDelegate){
37939 this.removeListener("activate", this.refreshDelegate);
37941 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37942 this.on("activate", this.refreshDelegate);
37943 return this.el.getUpdateManager();
37946 _handleRefresh : function(url, params, loadOnce){
37947 if(!loadOnce || !this.loaded){
37948 var updater = this.el.getUpdateManager();
37949 updater.update(url, params, this._setLoaded.createDelegate(this));
37953 _setLoaded : function(){
37954 this.loaded = true;
37958 * Returns this panel's id
37961 getId : function(){
37966 * Returns this panel's element - used by regiosn to add.
37967 * @return {Roo.Element}
37969 getEl : function(){
37970 return this.wrapEl || this.el;
37975 adjustForComponents : function(width, height)
37977 //Roo.log('adjustForComponents ');
37978 if(this.resizeEl != this.el){
37979 width -= this.el.getFrameWidth('lr');
37980 height -= this.el.getFrameWidth('tb');
37983 var te = this.toolbar.getEl();
37984 te.setWidth(width);
37985 height -= te.getHeight();
37988 var te = this.footer.getEl();
37989 te.setWidth(width);
37990 height -= te.getHeight();
37994 if(this.adjustments){
37995 width += this.adjustments[0];
37996 height += this.adjustments[1];
37998 return {"width": width, "height": height};
38001 setSize : function(width, height){
38002 if(this.fitToFrame && !this.ignoreResize(width, height)){
38003 if(this.fitContainer && this.resizeEl != this.el){
38004 this.el.setSize(width, height);
38006 var size = this.adjustForComponents(width, height);
38007 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
38008 this.fireEvent('resize', this, size.width, size.height);
38013 * Returns this panel's title
38016 getTitle : function(){
38018 if (typeof(this.title) != 'object') {
38023 for (var k in this.title) {
38024 if (!this.title.hasOwnProperty(k)) {
38028 if (k.indexOf('-') >= 0) {
38029 var s = k.split('-');
38030 for (var i = 0; i<s.length; i++) {
38031 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
38034 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
38041 * Set this panel's title
38042 * @param {String} title
38044 setTitle : function(title){
38045 this.title = title;
38047 this.region.updatePanelTitle(this, title);
38052 * Returns true is this panel was configured to be closable
38053 * @return {Boolean}
38055 isClosable : function(){
38056 return this.closable;
38059 beforeSlide : function(){
38061 this.resizeEl.clip();
38064 afterSlide : function(){
38066 this.resizeEl.unclip();
38070 * Force a content refresh from the URL specified in the {@link #setUrl} method.
38071 * Will fail silently if the {@link #setUrl} method has not been called.
38072 * This does not activate the panel, just updates its content.
38074 refresh : function(){
38075 if(this.refreshDelegate){
38076 this.loaded = false;
38077 this.refreshDelegate();
38082 * Destroys this panel
38084 destroy : function(){
38085 this.el.removeAllListeners();
38086 var tempEl = document.createElement("span");
38087 tempEl.appendChild(this.el.dom);
38088 tempEl.innerHTML = "";
38094 * form - if the content panel contains a form - this is a reference to it.
38095 * @type {Roo.form.Form}
38099 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
38100 * This contains a reference to it.
38106 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
38116 * @param {Object} cfg Xtype definition of item to add.
38120 getChildContainer: function () {
38121 return this.getEl();
38126 var ret = new Roo.factory(cfg);
38131 if (cfg.xtype.match(/^Form$/)) {
38134 //if (this.footer) {
38135 // el = this.footer.container.insertSibling(false, 'before');
38137 el = this.el.createChild();
38140 this.form = new Roo.form.Form(cfg);
38143 if ( this.form.allItems.length) {
38144 this.form.render(el.dom);
38148 // should only have one of theses..
38149 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
38150 // views.. should not be just added - used named prop 'view''
38152 cfg.el = this.el.appendChild(document.createElement("div"));
38155 var ret = new Roo.factory(cfg);
38157 ret.render && ret.render(false, ''); // render blank..
38167 * @class Roo.bootstrap.panel.Grid
38168 * @extends Roo.bootstrap.panel.Content
38170 * Create a new GridPanel.
38171 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
38172 * @param {Object} config A the config object
38178 Roo.bootstrap.panel.Grid = function(config)
38182 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
38183 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
38185 config.el = this.wrapper;
38186 //this.el = this.wrapper;
38188 if (config.container) {
38189 // ctor'ed from a Border/panel.grid
38192 this.wrapper.setStyle("overflow", "hidden");
38193 this.wrapper.addClass('roo-grid-container');
38198 if(config.toolbar){
38199 var tool_el = this.wrapper.createChild();
38200 this.toolbar = Roo.factory(config.toolbar);
38202 if (config.toolbar.items) {
38203 ti = config.toolbar.items ;
38204 delete config.toolbar.items ;
38208 this.toolbar.render(tool_el);
38209 for(var i =0;i < ti.length;i++) {
38210 // Roo.log(['add child', items[i]]);
38211 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38213 this.toolbar.items = nitems;
38215 delete config.toolbar;
38218 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
38219 config.grid.scrollBody = true;;
38220 config.grid.monitorWindowResize = false; // turn off autosizing
38221 config.grid.autoHeight = false;
38222 config.grid.autoWidth = false;
38224 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
38226 if (config.background) {
38227 // render grid on panel activation (if panel background)
38228 this.on('activate', function(gp) {
38229 if (!gp.grid.rendered) {
38230 gp.grid.render(this.wrapper);
38231 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
38236 this.grid.render(this.wrapper);
38237 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
38240 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
38241 // ??? needed ??? config.el = this.wrapper;
38246 // xtype created footer. - not sure if will work as we normally have to render first..
38247 if (this.footer && !this.footer.el && this.footer.xtype) {
38249 var ctr = this.grid.getView().getFooterPanel(true);
38250 this.footer.dataSource = this.grid.dataSource;
38251 this.footer = Roo.factory(this.footer, Roo);
38252 this.footer.render(ctr);
38262 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
38263 getId : function(){
38264 return this.grid.id;
38268 * Returns the grid for this panel
38269 * @return {Roo.bootstrap.Table}
38271 getGrid : function(){
38275 setSize : function(width, height){
38276 if(!this.ignoreResize(width, height)){
38277 var grid = this.grid;
38278 var size = this.adjustForComponents(width, height);
38279 var gridel = grid.getGridEl();
38280 gridel.setSize(size.width, size.height);
38282 var thd = grid.getGridEl().select('thead',true).first();
38283 var tbd = grid.getGridEl().select('tbody', true).first();
38285 tbd.setSize(width, height - thd.getHeight());
38294 beforeSlide : function(){
38295 this.grid.getView().scroller.clip();
38298 afterSlide : function(){
38299 this.grid.getView().scroller.unclip();
38302 destroy : function(){
38303 this.grid.destroy();
38305 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
38310 * @class Roo.bootstrap.panel.Nest
38311 * @extends Roo.bootstrap.panel.Content
38313 * Create a new Panel, that can contain a layout.Border.
38316 * @param {Roo.BorderLayout} layout The layout for this panel
38317 * @param {String/Object} config A string to set only the title or a config object
38319 Roo.bootstrap.panel.Nest = function(config)
38321 // construct with only one argument..
38322 /* FIXME - implement nicer consturctors
38323 if (layout.layout) {
38325 layout = config.layout;
38326 delete config.layout;
38328 if (layout.xtype && !layout.getEl) {
38329 // then layout needs constructing..
38330 layout = Roo.factory(layout, Roo);
38334 config.el = config.layout.getEl();
38336 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
38338 config.layout.monitorWindowResize = false; // turn off autosizing
38339 this.layout = config.layout;
38340 this.layout.getEl().addClass("roo-layout-nested-layout");
38341 this.layout.parent = this;
38348 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38350 setSize : function(width, height){
38351 if(!this.ignoreResize(width, height)){
38352 var size = this.adjustForComponents(width, height);
38353 var el = this.layout.getEl();
38354 if (size.height < 1) {
38355 el.setWidth(size.width);
38357 el.setSize(size.width, size.height);
38359 var touch = el.dom.offsetWidth;
38360 this.layout.layout();
38361 // ie requires a double layout on the first pass
38362 if(Roo.isIE && !this.initialized){
38363 this.initialized = true;
38364 this.layout.layout();
38369 // activate all subpanels if not currently active..
38371 setActiveState : function(active){
38372 this.active = active;
38373 this.setActiveClass(active);
38376 this.fireEvent("deactivate", this);
38380 this.fireEvent("activate", this);
38381 // not sure if this should happen before or after..
38382 if (!this.layout) {
38383 return; // should not happen..
38386 for (var r in this.layout.regions) {
38387 reg = this.layout.getRegion(r);
38388 if (reg.getActivePanel()) {
38389 //reg.showPanel(reg.getActivePanel()); // force it to activate..
38390 reg.setActivePanel(reg.getActivePanel());
38393 if (!reg.panels.length) {
38396 reg.showPanel(reg.getPanel(0));
38405 * Returns the nested BorderLayout for this panel
38406 * @return {Roo.BorderLayout}
38408 getLayout : function(){
38409 return this.layout;
38413 * Adds a xtype elements to the layout of the nested panel
38417 xtype : 'ContentPanel',
38424 xtype : 'NestedLayoutPanel',
38430 items : [ ... list of content panels or nested layout panels.. ]
38434 * @param {Object} cfg Xtype definition of item to add.
38436 addxtype : function(cfg) {
38437 return this.layout.addxtype(cfg);
38442 * Ext JS Library 1.1.1
38443 * Copyright(c) 2006-2007, Ext JS, LLC.
38445 * Originally Released Under LGPL - original licence link has changed is not relivant.
38448 * <script type="text/javascript">
38451 * @class Roo.TabPanel
38452 * @extends Roo.util.Observable
38453 * A lightweight tab container.
38457 // basic tabs 1, built from existing content
38458 var tabs = new Roo.TabPanel("tabs1");
38459 tabs.addTab("script", "View Script");
38460 tabs.addTab("markup", "View Markup");
38461 tabs.activate("script");
38463 // more advanced tabs, built from javascript
38464 var jtabs = new Roo.TabPanel("jtabs");
38465 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38467 // set up the UpdateManager
38468 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38469 var updater = tab2.getUpdateManager();
38470 updater.setDefaultUrl("ajax1.htm");
38471 tab2.on('activate', updater.refresh, updater, true);
38473 // Use setUrl for Ajax loading
38474 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38475 tab3.setUrl("ajax2.htm", null, true);
38478 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38481 jtabs.activate("jtabs-1");
38484 * Create a new TabPanel.
38485 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38486 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38488 Roo.bootstrap.panel.Tabs = function(config){
38490 * The container element for this TabPanel.
38491 * @type Roo.Element
38493 this.el = Roo.get(config.el);
38496 if(typeof config == "boolean"){
38497 this.tabPosition = config ? "bottom" : "top";
38499 Roo.apply(this, config);
38503 if(this.tabPosition == "bottom"){
38504 // if tabs are at the bottom = create the body first.
38505 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38506 this.el.addClass("roo-tabs-bottom");
38508 // next create the tabs holders
38510 if (this.tabPosition == "west"){
38512 var reg = this.region; // fake it..
38514 if (!reg.mgr.parent) {
38517 reg = reg.mgr.parent.region;
38519 Roo.log("got nest?");
38521 if (reg.mgr.getRegion('west')) {
38522 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38523 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38524 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38525 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38526 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38534 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38535 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38536 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38537 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38542 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38545 // finally - if tabs are at the top, then create the body last..
38546 if(this.tabPosition != "bottom"){
38547 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38548 * @type Roo.Element
38550 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38551 this.el.addClass("roo-tabs-top");
38555 this.bodyEl.setStyle("position", "relative");
38557 this.active = null;
38558 this.activateDelegate = this.activate.createDelegate(this);
38563 * Fires when the active tab changes
38564 * @param {Roo.TabPanel} this
38565 * @param {Roo.TabPanelItem} activePanel The new active tab
38569 * @event beforetabchange
38570 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38571 * @param {Roo.TabPanel} this
38572 * @param {Object} e Set cancel to true on this object to cancel the tab change
38573 * @param {Roo.TabPanelItem} tab The tab being changed to
38575 "beforetabchange" : true
38578 Roo.EventManager.onWindowResize(this.onResize, this);
38579 this.cpad = this.el.getPadding("lr");
38580 this.hiddenCount = 0;
38583 // toolbar on the tabbar support...
38584 if (this.toolbar) {
38585 alert("no toolbar support yet");
38586 this.toolbar = false;
38588 var tcfg = this.toolbar;
38589 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
38590 this.toolbar = new Roo.Toolbar(tcfg);
38591 if (Roo.isSafari) {
38592 var tbl = tcfg.container.child('table', true);
38593 tbl.setAttribute('width', '100%');
38601 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38604 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38606 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38608 tabPosition : "top",
38610 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38612 currentTabWidth : 0,
38614 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38618 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38622 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38624 preferredTabWidth : 175,
38626 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38628 resizeTabs : false,
38630 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38632 monitorResize : true,
38634 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
38636 toolbar : false, // set by caller..
38638 region : false, /// set by caller
38640 disableTooltips : true, // not used yet...
38643 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38644 * @param {String} id The id of the div to use <b>or create</b>
38645 * @param {String} text The text for the tab
38646 * @param {String} content (optional) Content to put in the TabPanelItem body
38647 * @param {Boolean} closable (optional) True to create a close icon on the tab
38648 * @return {Roo.TabPanelItem} The created TabPanelItem
38650 addTab : function(id, text, content, closable, tpl)
38652 var item = new Roo.bootstrap.panel.TabItem({
38656 closable : closable,
38659 this.addTabItem(item);
38661 item.setContent(content);
38667 * Returns the {@link Roo.TabPanelItem} with the specified id/index
38668 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38669 * @return {Roo.TabPanelItem}
38671 getTab : function(id){
38672 return this.items[id];
38676 * Hides the {@link Roo.TabPanelItem} with the specified id/index
38677 * @param {String/Number} id The id or index of the TabPanelItem to hide.
38679 hideTab : function(id){
38680 var t = this.items[id];
38683 this.hiddenCount++;
38684 this.autoSizeTabs();
38689 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38690 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38692 unhideTab : function(id){
38693 var t = this.items[id];
38695 t.setHidden(false);
38696 this.hiddenCount--;
38697 this.autoSizeTabs();
38702 * Adds an existing {@link Roo.TabPanelItem}.
38703 * @param {Roo.TabPanelItem} item The TabPanelItem to add
38705 addTabItem : function(item)
38707 this.items[item.id] = item;
38708 this.items.push(item);
38709 this.autoSizeTabs();
38710 // if(this.resizeTabs){
38711 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38712 // this.autoSizeTabs();
38714 // item.autoSize();
38719 * Removes a {@link Roo.TabPanelItem}.
38720 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38722 removeTab : function(id){
38723 var items = this.items;
38724 var tab = items[id];
38725 if(!tab) { return; }
38726 var index = items.indexOf(tab);
38727 if(this.active == tab && items.length > 1){
38728 var newTab = this.getNextAvailable(index);
38733 this.stripEl.dom.removeChild(tab.pnode.dom);
38734 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38735 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38737 items.splice(index, 1);
38738 delete this.items[tab.id];
38739 tab.fireEvent("close", tab);
38740 tab.purgeListeners();
38741 this.autoSizeTabs();
38744 getNextAvailable : function(start){
38745 var items = this.items;
38747 // look for a next tab that will slide over to
38748 // replace the one being removed
38749 while(index < items.length){
38750 var item = items[++index];
38751 if(item && !item.isHidden()){
38755 // if one isn't found select the previous tab (on the left)
38758 var item = items[--index];
38759 if(item && !item.isHidden()){
38767 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38768 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38770 disableTab : function(id){
38771 var tab = this.items[id];
38772 if(tab && this.active != tab){
38778 * Enables a {@link Roo.TabPanelItem} that is disabled.
38779 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38781 enableTab : function(id){
38782 var tab = this.items[id];
38787 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38788 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38789 * @return {Roo.TabPanelItem} The TabPanelItem.
38791 activate : function(id)
38793 //Roo.log('activite:' + id);
38795 var tab = this.items[id];
38799 if(tab == this.active || tab.disabled){
38803 this.fireEvent("beforetabchange", this, e, tab);
38804 if(e.cancel !== true && !tab.disabled){
38806 this.active.hide();
38808 this.active = this.items[id];
38809 this.active.show();
38810 this.fireEvent("tabchange", this, this.active);
38816 * Gets the active {@link Roo.TabPanelItem}.
38817 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38819 getActiveTab : function(){
38820 return this.active;
38824 * Updates the tab body element to fit the height of the container element
38825 * for overflow scrolling
38826 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38828 syncHeight : function(targetHeight){
38829 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38830 var bm = this.bodyEl.getMargins();
38831 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38832 this.bodyEl.setHeight(newHeight);
38836 onResize : function(){
38837 if(this.monitorResize){
38838 this.autoSizeTabs();
38843 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38845 beginUpdate : function(){
38846 this.updating = true;
38850 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38852 endUpdate : function(){
38853 this.updating = false;
38854 this.autoSizeTabs();
38858 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38860 autoSizeTabs : function()
38862 var count = this.items.length;
38863 var vcount = count - this.hiddenCount;
38866 this.stripEl.hide();
38868 this.stripEl.show();
38871 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38876 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38877 var availWidth = Math.floor(w / vcount);
38878 var b = this.stripBody;
38879 if(b.getWidth() > w){
38880 var tabs = this.items;
38881 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38882 if(availWidth < this.minTabWidth){
38883 /*if(!this.sleft){ // incomplete scrolling code
38884 this.createScrollButtons();
38887 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38890 if(this.currentTabWidth < this.preferredTabWidth){
38891 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38897 * Returns the number of tabs in this TabPanel.
38900 getCount : function(){
38901 return this.items.length;
38905 * Resizes all the tabs to the passed width
38906 * @param {Number} The new width
38908 setTabWidth : function(width){
38909 this.currentTabWidth = width;
38910 for(var i = 0, len = this.items.length; i < len; i++) {
38911 if(!this.items[i].isHidden()) {
38912 this.items[i].setWidth(width);
38918 * Destroys this TabPanel
38919 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38921 destroy : function(removeEl){
38922 Roo.EventManager.removeResizeListener(this.onResize, this);
38923 for(var i = 0, len = this.items.length; i < len; i++){
38924 this.items[i].purgeListeners();
38926 if(removeEl === true){
38927 this.el.update("");
38932 createStrip : function(container)
38934 var strip = document.createElement("nav");
38935 strip.className = Roo.bootstrap.version == 4 ?
38936 "navbar-light bg-light" :
38937 "navbar navbar-default"; //"x-tabs-wrap";
38938 container.appendChild(strip);
38942 createStripList : function(strip)
38944 // div wrapper for retard IE
38945 // returns the "tr" element.
38946 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38947 //'<div class="x-tabs-strip-wrap">'+
38948 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38949 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38950 return strip.firstChild; //.firstChild.firstChild.firstChild;
38952 createBody : function(container)
38954 var body = document.createElement("div");
38955 Roo.id(body, "tab-body");
38956 //Roo.fly(body).addClass("x-tabs-body");
38957 Roo.fly(body).addClass("tab-content");
38958 container.appendChild(body);
38961 createItemBody :function(bodyEl, id){
38962 var body = Roo.getDom(id);
38964 body = document.createElement("div");
38967 //Roo.fly(body).addClass("x-tabs-item-body");
38968 Roo.fly(body).addClass("tab-pane");
38969 bodyEl.insertBefore(body, bodyEl.firstChild);
38973 createStripElements : function(stripEl, text, closable, tpl)
38975 var td = document.createElement("li"); // was td..
38976 td.className = 'nav-item';
38978 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38981 stripEl.appendChild(td);
38983 td.className = "x-tabs-closable";
38984 if(!this.closeTpl){
38985 this.closeTpl = new Roo.Template(
38986 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38987 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38988 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38991 var el = this.closeTpl.overwrite(td, {"text": text});
38992 var close = el.getElementsByTagName("div")[0];
38993 var inner = el.getElementsByTagName("em")[0];
38994 return {"el": el, "close": close, "inner": inner};
38997 // not sure what this is..
38998 // if(!this.tabTpl){
38999 //this.tabTpl = new Roo.Template(
39000 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39001 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
39003 // this.tabTpl = new Roo.Template(
39004 // '<a href="#">' +
39005 // '<span unselectable="on"' +
39006 // (this.disableTooltips ? '' : ' title="{text}"') +
39007 // ' >{text}</span></a>'
39013 var template = tpl || this.tabTpl || false;
39016 template = new Roo.Template(
39017 Roo.bootstrap.version == 4 ?
39019 '<a class="nav-link" href="#" unselectable="on"' +
39020 (this.disableTooltips ? '' : ' title="{text}"') +
39023 '<a class="nav-link" href="#">' +
39024 '<span unselectable="on"' +
39025 (this.disableTooltips ? '' : ' title="{text}"') +
39026 ' >{text}</span></a>'
39031 switch (typeof(template)) {
39035 template = new Roo.Template(template);
39041 var el = template.overwrite(td, {"text": text});
39043 var inner = el.getElementsByTagName("span")[0];
39045 return {"el": el, "inner": inner};
39053 * @class Roo.TabPanelItem
39054 * @extends Roo.util.Observable
39055 * Represents an individual item (tab plus body) in a TabPanel.
39056 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
39057 * @param {String} id The id of this TabPanelItem
39058 * @param {String} text The text for the tab of this TabPanelItem
39059 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
39061 Roo.bootstrap.panel.TabItem = function(config){
39063 * The {@link Roo.TabPanel} this TabPanelItem belongs to
39064 * @type Roo.TabPanel
39066 this.tabPanel = config.panel;
39068 * The id for this TabPanelItem
39071 this.id = config.id;
39073 this.disabled = false;
39075 this.text = config.text;
39077 this.loaded = false;
39078 this.closable = config.closable;
39081 * The body element for this TabPanelItem.
39082 * @type Roo.Element
39084 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
39085 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
39086 this.bodyEl.setStyle("display", "block");
39087 this.bodyEl.setStyle("zoom", "1");
39088 //this.hideAction();
39090 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
39092 this.el = Roo.get(els.el);
39093 this.inner = Roo.get(els.inner, true);
39094 this.textEl = Roo.bootstrap.version == 4 ?
39095 this.el : Roo.get(this.el.dom.firstChild, true);
39097 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
39098 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
39101 // this.el.on("mousedown", this.onTabMouseDown, this);
39102 this.el.on("click", this.onTabClick, this);
39104 if(config.closable){
39105 var c = Roo.get(els.close, true);
39106 c.dom.title = this.closeText;
39107 c.addClassOnOver("close-over");
39108 c.on("click", this.closeClick, this);
39114 * Fires when this tab becomes the active tab.
39115 * @param {Roo.TabPanel} tabPanel The parent TabPanel
39116 * @param {Roo.TabPanelItem} this
39120 * @event beforeclose
39121 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
39122 * @param {Roo.TabPanelItem} this
39123 * @param {Object} e Set cancel to true on this object to cancel the close.
39125 "beforeclose": true,
39128 * Fires when this tab is closed.
39129 * @param {Roo.TabPanelItem} this
39133 * @event deactivate
39134 * Fires when this tab is no longer the active tab.
39135 * @param {Roo.TabPanel} tabPanel The parent TabPanel
39136 * @param {Roo.TabPanelItem} this
39138 "deactivate" : true
39140 this.hidden = false;
39142 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
39145 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
39147 purgeListeners : function(){
39148 Roo.util.Observable.prototype.purgeListeners.call(this);
39149 this.el.removeAllListeners();
39152 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
39155 this.status_node.addClass("active");
39158 this.tabPanel.stripWrap.repaint();
39160 this.fireEvent("activate", this.tabPanel, this);
39164 * Returns true if this tab is the active tab.
39165 * @return {Boolean}
39167 isActive : function(){
39168 return this.tabPanel.getActiveTab() == this;
39172 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
39175 this.status_node.removeClass("active");
39177 this.fireEvent("deactivate", this.tabPanel, this);
39180 hideAction : function(){
39181 this.bodyEl.hide();
39182 this.bodyEl.setStyle("position", "absolute");
39183 this.bodyEl.setLeft("-20000px");
39184 this.bodyEl.setTop("-20000px");
39187 showAction : function(){
39188 this.bodyEl.setStyle("position", "relative");
39189 this.bodyEl.setTop("");
39190 this.bodyEl.setLeft("");
39191 this.bodyEl.show();
39195 * Set the tooltip for the tab.
39196 * @param {String} tooltip The tab's tooltip
39198 setTooltip : function(text){
39199 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
39200 this.textEl.dom.qtip = text;
39201 this.textEl.dom.removeAttribute('title');
39203 this.textEl.dom.title = text;
39207 onTabClick : function(e){
39208 e.preventDefault();
39209 this.tabPanel.activate(this.id);
39212 onTabMouseDown : function(e){
39213 e.preventDefault();
39214 this.tabPanel.activate(this.id);
39217 getWidth : function(){
39218 return this.inner.getWidth();
39221 setWidth : function(width){
39222 var iwidth = width - this.linode.getPadding("lr");
39223 this.inner.setWidth(iwidth);
39224 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
39225 this.linode.setWidth(width);
39229 * Show or hide the tab
39230 * @param {Boolean} hidden True to hide or false to show.
39232 setHidden : function(hidden){
39233 this.hidden = hidden;
39234 this.linode.setStyle("display", hidden ? "none" : "");
39238 * Returns true if this tab is "hidden"
39239 * @return {Boolean}
39241 isHidden : function(){
39242 return this.hidden;
39246 * Returns the text for this tab
39249 getText : function(){
39253 autoSize : function(){
39254 //this.el.beginMeasure();
39255 this.textEl.setWidth(1);
39257 * #2804 [new] Tabs in Roojs
39258 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
39260 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
39261 //this.el.endMeasure();
39265 * Sets the text for the tab (Note: this also sets the tooltip text)
39266 * @param {String} text The tab's text and tooltip
39268 setText : function(text){
39270 this.textEl.update(text);
39271 this.setTooltip(text);
39272 //if(!this.tabPanel.resizeTabs){
39273 // this.autoSize();
39277 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39279 activate : function(){
39280 this.tabPanel.activate(this.id);
39284 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
39286 disable : function(){
39287 if(this.tabPanel.active != this){
39288 this.disabled = true;
39289 this.status_node.addClass("disabled");
39294 * Enables this TabPanelItem if it was previously disabled.
39296 enable : function(){
39297 this.disabled = false;
39298 this.status_node.removeClass("disabled");
39302 * Sets the content for this TabPanelItem.
39303 * @param {String} content The content
39304 * @param {Boolean} loadScripts true to look for and load scripts
39306 setContent : function(content, loadScripts){
39307 this.bodyEl.update(content, loadScripts);
39311 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
39312 * @return {Roo.UpdateManager} The UpdateManager
39314 getUpdateManager : function(){
39315 return this.bodyEl.getUpdateManager();
39319 * Set a URL to be used to load the content for this TabPanelItem.
39320 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
39321 * @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)
39322 * @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)
39323 * @return {Roo.UpdateManager} The UpdateManager
39325 setUrl : function(url, params, loadOnce){
39326 if(this.refreshDelegate){
39327 this.un('activate', this.refreshDelegate);
39329 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39330 this.on("activate", this.refreshDelegate);
39331 return this.bodyEl.getUpdateManager();
39335 _handleRefresh : function(url, params, loadOnce){
39336 if(!loadOnce || !this.loaded){
39337 var updater = this.bodyEl.getUpdateManager();
39338 updater.update(url, params, this._setLoaded.createDelegate(this));
39343 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
39344 * Will fail silently if the setUrl method has not been called.
39345 * This does not activate the panel, just updates its content.
39347 refresh : function(){
39348 if(this.refreshDelegate){
39349 this.loaded = false;
39350 this.refreshDelegate();
39355 _setLoaded : function(){
39356 this.loaded = true;
39360 closeClick : function(e){
39363 this.fireEvent("beforeclose", this, o);
39364 if(o.cancel !== true){
39365 this.tabPanel.removeTab(this.id);
39369 * The text displayed in the tooltip for the close icon.
39372 closeText : "Close this tab"
39375 * This script refer to:
39376 * Title: International Telephone Input
39377 * Author: Jack O'Connor
39378 * Code version: v12.1.12
39379 * Availability: https://github.com/jackocnr/intl-tel-input.git
39382 Roo.bootstrap.PhoneInputData = function() {
39385 "Afghanistan (افغانستان)",
39390 "Albania (Shqipëri)",
39395 "Algeria (الجزائر)",
39420 "Antigua and Barbuda",
39430 "Armenia (Հայաստան)",
39446 "Austria (Österreich)",
39451 "Azerbaijan (Azərbaycan)",
39461 "Bahrain (البحرين)",
39466 "Bangladesh (বাংলাদেশ)",
39476 "Belarus (Беларусь)",
39481 "Belgium (België)",
39511 "Bosnia and Herzegovina (Босна и Херцеговина)",
39526 "British Indian Ocean Territory",
39531 "British Virgin Islands",
39541 "Bulgaria (България)",
39551 "Burundi (Uburundi)",
39556 "Cambodia (កម្ពុជា)",
39561 "Cameroon (Cameroun)",
39570 ["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"]
39573 "Cape Verde (Kabu Verdi)",
39578 "Caribbean Netherlands",
39589 "Central African Republic (République centrafricaine)",
39609 "Christmas Island",
39615 "Cocos (Keeling) Islands",
39626 "Comoros (جزر القمر)",
39631 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39636 "Congo (Republic) (Congo-Brazzaville)",
39656 "Croatia (Hrvatska)",
39677 "Czech Republic (Česká republika)",
39682 "Denmark (Danmark)",
39697 "Dominican Republic (República Dominicana)",
39701 ["809", "829", "849"]
39719 "Equatorial Guinea (Guinea Ecuatorial)",
39739 "Falkland Islands (Islas Malvinas)",
39744 "Faroe Islands (Føroyar)",
39765 "French Guiana (Guyane française)",
39770 "French Polynesia (Polynésie française)",
39785 "Georgia (საქართველო)",
39790 "Germany (Deutschland)",
39810 "Greenland (Kalaallit Nunaat)",
39847 "Guinea-Bissau (Guiné Bissau)",
39872 "Hungary (Magyarország)",
39877 "Iceland (Ísland)",
39897 "Iraq (العراق)",
39913 "Israel (ישראל)",
39940 "Jordan (الأردن)",
39945 "Kazakhstan (Казахстан)",
39966 "Kuwait (الكويت)",
39971 "Kyrgyzstan (Кыргызстан)",
39981 "Latvia (Latvija)",
39986 "Lebanon (لبنان)",
40001 "Libya (ليبيا)",
40011 "Lithuania (Lietuva)",
40026 "Macedonia (FYROM) (Македонија)",
40031 "Madagascar (Madagasikara)",
40061 "Marshall Islands",
40071 "Mauritania (موريتانيا)",
40076 "Mauritius (Moris)",
40097 "Moldova (Republica Moldova)",
40107 "Mongolia (Монгол)",
40112 "Montenegro (Crna Gora)",
40122 "Morocco (المغرب)",
40128 "Mozambique (Moçambique)",
40133 "Myanmar (Burma) (မြန်မာ)",
40138 "Namibia (Namibië)",
40153 "Netherlands (Nederland)",
40158 "New Caledonia (Nouvelle-Calédonie)",
40193 "North Korea (조선 민주주의 인민 공화국)",
40198 "Northern Mariana Islands",
40214 "Pakistan (پاکستان)",
40224 "Palestine (فلسطين)",
40234 "Papua New Guinea",
40276 "Réunion (La Réunion)",
40282 "Romania (România)",
40298 "Saint Barthélemy",
40309 "Saint Kitts and Nevis",
40319 "Saint Martin (Saint-Martin (partie française))",
40325 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
40330 "Saint Vincent and the Grenadines",
40345 "São Tomé and Príncipe (São Tomé e Príncipe)",
40350 "Saudi Arabia (المملكة العربية السعودية)",
40355 "Senegal (Sénégal)",
40385 "Slovakia (Slovensko)",
40390 "Slovenia (Slovenija)",
40400 "Somalia (Soomaaliya)",
40410 "South Korea (대한민국)",
40415 "South Sudan (جنوب السودان)",
40425 "Sri Lanka (ශ්රී ලංකාව)",
40430 "Sudan (السودان)",
40440 "Svalbard and Jan Mayen",
40451 "Sweden (Sverige)",
40456 "Switzerland (Schweiz)",
40461 "Syria (سوريا)",
40506 "Trinidad and Tobago",
40511 "Tunisia (تونس)",
40516 "Turkey (Türkiye)",
40526 "Turks and Caicos Islands",
40536 "U.S. Virgin Islands",
40546 "Ukraine (Україна)",
40551 "United Arab Emirates (الإمارات العربية المتحدة)",
40573 "Uzbekistan (Oʻzbekiston)",
40583 "Vatican City (Città del Vaticano)",
40594 "Vietnam (Việt Nam)",
40599 "Wallis and Futuna (Wallis-et-Futuna)",
40604 "Western Sahara (الصحراء الغربية)",
40610 "Yemen (اليمن)",
40634 * This script refer to:
40635 * Title: International Telephone Input
40636 * Author: Jack O'Connor
40637 * Code version: v12.1.12
40638 * Availability: https://github.com/jackocnr/intl-tel-input.git
40642 * @class Roo.bootstrap.PhoneInput
40643 * @extends Roo.bootstrap.TriggerField
40644 * An input with International dial-code selection
40646 * @cfg {String} defaultDialCode default '+852'
40647 * @cfg {Array} preferedCountries default []
40650 * Create a new PhoneInput.
40651 * @param {Object} config Configuration options
40654 Roo.bootstrap.PhoneInput = function(config) {
40655 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40658 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40660 listWidth: undefined,
40662 selectedClass: 'active',
40664 invalidClass : "has-warning",
40666 validClass: 'has-success',
40668 allowed: '0123456789',
40673 * @cfg {String} defaultDialCode The default dial code when initializing the input
40675 defaultDialCode: '+852',
40678 * @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
40680 preferedCountries: false,
40682 getAutoCreate : function()
40684 var data = Roo.bootstrap.PhoneInputData();
40685 var align = this.labelAlign || this.parentLabelAlign();
40688 this.allCountries = [];
40689 this.dialCodeMapping = [];
40691 for (var i = 0; i < data.length; i++) {
40693 this.allCountries[i] = {
40697 priority: c[3] || 0,
40698 areaCodes: c[4] || null
40700 this.dialCodeMapping[c[2]] = {
40703 priority: c[3] || 0,
40704 areaCodes: c[4] || null
40716 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40717 maxlength: this.max_length,
40718 cls : 'form-control tel-input',
40719 autocomplete: 'new-password'
40722 var hiddenInput = {
40725 cls: 'hidden-tel-input'
40729 hiddenInput.name = this.name;
40732 if (this.disabled) {
40733 input.disabled = true;
40736 var flag_container = {
40753 cls: this.hasFeedback ? 'has-feedback' : '',
40759 cls: 'dial-code-holder',
40766 cls: 'roo-select2-container input-group',
40773 if (this.fieldLabel.length) {
40776 tooltip: 'This field is required'
40782 cls: 'control-label',
40788 html: this.fieldLabel
40791 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40797 if(this.indicatorpos == 'right') {
40798 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40805 if(align == 'left') {
40813 if(this.labelWidth > 12){
40814 label.style = "width: " + this.labelWidth + 'px';
40816 if(this.labelWidth < 13 && this.labelmd == 0){
40817 this.labelmd = this.labelWidth;
40819 if(this.labellg > 0){
40820 label.cls += ' col-lg-' + this.labellg;
40821 input.cls += ' col-lg-' + (12 - this.labellg);
40823 if(this.labelmd > 0){
40824 label.cls += ' col-md-' + this.labelmd;
40825 container.cls += ' col-md-' + (12 - this.labelmd);
40827 if(this.labelsm > 0){
40828 label.cls += ' col-sm-' + this.labelsm;
40829 container.cls += ' col-sm-' + (12 - this.labelsm);
40831 if(this.labelxs > 0){
40832 label.cls += ' col-xs-' + this.labelxs;
40833 container.cls += ' col-xs-' + (12 - this.labelxs);
40843 var settings = this;
40845 ['xs','sm','md','lg'].map(function(size){
40846 if (settings[size]) {
40847 cfg.cls += ' col-' + size + '-' + settings[size];
40851 this.store = new Roo.data.Store({
40852 proxy : new Roo.data.MemoryProxy({}),
40853 reader : new Roo.data.JsonReader({
40864 'name' : 'dialCode',
40868 'name' : 'priority',
40872 'name' : 'areaCodes',
40879 if(!this.preferedCountries) {
40880 this.preferedCountries = [
40887 var p = this.preferedCountries.reverse();
40890 for (var i = 0; i < p.length; i++) {
40891 for (var j = 0; j < this.allCountries.length; j++) {
40892 if(this.allCountries[j].iso2 == p[i]) {
40893 var t = this.allCountries[j];
40894 this.allCountries.splice(j,1);
40895 this.allCountries.unshift(t);
40901 this.store.proxy.data = {
40903 data: this.allCountries
40909 initEvents : function()
40912 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40914 this.indicator = this.indicatorEl();
40915 this.flag = this.flagEl();
40916 this.dialCodeHolder = this.dialCodeHolderEl();
40918 this.trigger = this.el.select('div.flag-box',true).first();
40919 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40924 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40925 _this.list.setWidth(lw);
40928 this.list.on('mouseover', this.onViewOver, this);
40929 this.list.on('mousemove', this.onViewMove, this);
40930 this.inputEl().on("keyup", this.onKeyUp, this);
40931 this.inputEl().on("keypress", this.onKeyPress, this);
40933 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40935 this.view = new Roo.View(this.list, this.tpl, {
40936 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40939 this.view.on('click', this.onViewClick, this);
40940 this.setValue(this.defaultDialCode);
40943 onTriggerClick : function(e)
40945 Roo.log('trigger click');
40950 if(this.isExpanded()){
40952 this.hasFocus = false;
40954 this.store.load({});
40955 this.hasFocus = true;
40960 isExpanded : function()
40962 return this.list.isVisible();
40965 collapse : function()
40967 if(!this.isExpanded()){
40971 Roo.get(document).un('mousedown', this.collapseIf, this);
40972 Roo.get(document).un('mousewheel', this.collapseIf, this);
40973 this.fireEvent('collapse', this);
40977 expand : function()
40981 if(this.isExpanded() || !this.hasFocus){
40985 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40986 this.list.setWidth(lw);
40989 this.restrictHeight();
40991 Roo.get(document).on('mousedown', this.collapseIf, this);
40992 Roo.get(document).on('mousewheel', this.collapseIf, this);
40994 this.fireEvent('expand', this);
40997 restrictHeight : function()
40999 this.list.alignTo(this.inputEl(), this.listAlign);
41000 this.list.alignTo(this.inputEl(), this.listAlign);
41003 onViewOver : function(e, t)
41005 if(this.inKeyMode){
41008 var item = this.view.findItemFromChild(t);
41011 var index = this.view.indexOf(item);
41012 this.select(index, false);
41017 onViewClick : function(view, doFocus, el, e)
41019 var index = this.view.getSelectedIndexes()[0];
41021 var r = this.store.getAt(index);
41024 this.onSelect(r, index);
41026 if(doFocus !== false && !this.blockFocus){
41027 this.inputEl().focus();
41031 onViewMove : function(e, t)
41033 this.inKeyMode = false;
41036 select : function(index, scrollIntoView)
41038 this.selectedIndex = index;
41039 this.view.select(index);
41040 if(scrollIntoView !== false){
41041 var el = this.view.getNode(index);
41043 this.list.scrollChildIntoView(el, false);
41048 createList : function()
41050 this.list = Roo.get(document.body).createChild({
41052 cls: 'typeahead typeahead-long dropdown-menu tel-list',
41053 style: 'display:none'
41056 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
41059 collapseIf : function(e)
41061 var in_combo = e.within(this.el);
41062 var in_list = e.within(this.list);
41063 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
41065 if (in_combo || in_list || is_list) {
41071 onSelect : function(record, index)
41073 if(this.fireEvent('beforeselect', this, record, index) !== false){
41075 this.setFlagClass(record.data.iso2);
41076 this.setDialCode(record.data.dialCode);
41077 this.hasFocus = false;
41079 this.fireEvent('select', this, record, index);
41083 flagEl : function()
41085 var flag = this.el.select('div.flag',true).first();
41092 dialCodeHolderEl : function()
41094 var d = this.el.select('input.dial-code-holder',true).first();
41101 setDialCode : function(v)
41103 this.dialCodeHolder.dom.value = '+'+v;
41106 setFlagClass : function(n)
41108 this.flag.dom.className = 'flag '+n;
41111 getValue : function()
41113 var v = this.inputEl().getValue();
41114 if(this.dialCodeHolder) {
41115 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
41120 setValue : function(v)
41122 var d = this.getDialCode(v);
41124 //invalid dial code
41125 if(v.length == 0 || !d || d.length == 0) {
41127 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41128 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41134 this.setFlagClass(this.dialCodeMapping[d].iso2);
41135 this.setDialCode(d);
41136 this.inputEl().dom.value = v.replace('+'+d,'');
41137 this.hiddenEl().dom.value = this.getValue();
41142 getDialCode : function(v)
41146 if (v.length == 0) {
41147 return this.dialCodeHolder.dom.value;
41151 if (v.charAt(0) != "+") {
41154 var numericChars = "";
41155 for (var i = 1; i < v.length; i++) {
41156 var c = v.charAt(i);
41159 if (this.dialCodeMapping[numericChars]) {
41160 dialCode = v.substr(1, i);
41162 if (numericChars.length == 4) {
41172 this.setValue(this.defaultDialCode);
41176 hiddenEl : function()
41178 return this.el.select('input.hidden-tel-input',true).first();
41181 // after setting val
41182 onKeyUp : function(e){
41183 this.setValue(this.getValue());
41186 onKeyPress : function(e){
41187 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
41194 * @class Roo.bootstrap.MoneyField
41195 * @extends Roo.bootstrap.ComboBox
41196 * Bootstrap MoneyField class
41199 * Create a new MoneyField.
41200 * @param {Object} config Configuration options
41203 Roo.bootstrap.MoneyField = function(config) {
41205 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
41209 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
41212 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41214 allowDecimals : true,
41216 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41218 decimalSeparator : ".",
41220 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41222 decimalPrecision : 0,
41224 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41226 allowNegative : true,
41228 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41232 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41234 minValue : Number.NEGATIVE_INFINITY,
41236 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41238 maxValue : Number.MAX_VALUE,
41240 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41242 minText : "The minimum value for this field is {0}",
41244 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41246 maxText : "The maximum value for this field is {0}",
41248 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
41249 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41251 nanText : "{0} is not a valid number",
41253 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
41257 * @cfg {String} defaults currency of the MoneyField
41258 * value should be in lkey
41260 defaultCurrency : false,
41262 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41264 thousandsDelimiter : false,
41266 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41277 getAutoCreate : function()
41279 var align = this.labelAlign || this.parentLabelAlign();
41291 cls : 'form-control roo-money-amount-input',
41292 autocomplete: 'new-password'
41295 var hiddenInput = {
41299 cls: 'hidden-number-input'
41302 if(this.max_length) {
41303 input.maxlength = this.max_length;
41307 hiddenInput.name = this.name;
41310 if (this.disabled) {
41311 input.disabled = true;
41314 var clg = 12 - this.inputlg;
41315 var cmd = 12 - this.inputmd;
41316 var csm = 12 - this.inputsm;
41317 var cxs = 12 - this.inputxs;
41321 cls : 'row roo-money-field',
41325 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
41329 cls: 'roo-select2-container input-group',
41333 cls : 'form-control roo-money-currency-input',
41334 autocomplete: 'new-password',
41336 name : this.currencyName
41340 cls : 'input-group-addon',
41354 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41358 cls: this.hasFeedback ? 'has-feedback' : '',
41369 if (this.fieldLabel.length) {
41372 tooltip: 'This field is required'
41378 cls: 'control-label',
41384 html: this.fieldLabel
41387 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41393 if(this.indicatorpos == 'right') {
41394 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41401 if(align == 'left') {
41409 if(this.labelWidth > 12){
41410 label.style = "width: " + this.labelWidth + 'px';
41412 if(this.labelWidth < 13 && this.labelmd == 0){
41413 this.labelmd = this.labelWidth;
41415 if(this.labellg > 0){
41416 label.cls += ' col-lg-' + this.labellg;
41417 input.cls += ' col-lg-' + (12 - this.labellg);
41419 if(this.labelmd > 0){
41420 label.cls += ' col-md-' + this.labelmd;
41421 container.cls += ' col-md-' + (12 - this.labelmd);
41423 if(this.labelsm > 0){
41424 label.cls += ' col-sm-' + this.labelsm;
41425 container.cls += ' col-sm-' + (12 - this.labelsm);
41427 if(this.labelxs > 0){
41428 label.cls += ' col-xs-' + this.labelxs;
41429 container.cls += ' col-xs-' + (12 - this.labelxs);
41440 var settings = this;
41442 ['xs','sm','md','lg'].map(function(size){
41443 if (settings[size]) {
41444 cfg.cls += ' col-' + size + '-' + settings[size];
41451 initEvents : function()
41453 this.indicator = this.indicatorEl();
41455 this.initCurrencyEvent();
41457 this.initNumberEvent();
41460 initCurrencyEvent : function()
41463 throw "can not find store for combo";
41466 this.store = Roo.factory(this.store, Roo.data);
41467 this.store.parent = this;
41471 this.triggerEl = this.el.select('.input-group-addon', true).first();
41473 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41478 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41479 _this.list.setWidth(lw);
41482 this.list.on('mouseover', this.onViewOver, this);
41483 this.list.on('mousemove', this.onViewMove, this);
41484 this.list.on('scroll', this.onViewScroll, this);
41487 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41490 this.view = new Roo.View(this.list, this.tpl, {
41491 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41494 this.view.on('click', this.onViewClick, this);
41496 this.store.on('beforeload', this.onBeforeLoad, this);
41497 this.store.on('load', this.onLoad, this);
41498 this.store.on('loadexception', this.onLoadException, this);
41500 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41501 "up" : function(e){
41502 this.inKeyMode = true;
41506 "down" : function(e){
41507 if(!this.isExpanded()){
41508 this.onTriggerClick();
41510 this.inKeyMode = true;
41515 "enter" : function(e){
41518 if(this.fireEvent("specialkey", this, e)){
41519 this.onViewClick(false);
41525 "esc" : function(e){
41529 "tab" : function(e){
41532 if(this.fireEvent("specialkey", this, e)){
41533 this.onViewClick(false);
41541 doRelay : function(foo, bar, hname){
41542 if(hname == 'down' || this.scope.isExpanded()){
41543 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41551 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41555 initNumberEvent : function(e)
41557 this.inputEl().on("keydown" , this.fireKey, this);
41558 this.inputEl().on("focus", this.onFocus, this);
41559 this.inputEl().on("blur", this.onBlur, this);
41561 this.inputEl().relayEvent('keyup', this);
41563 if(this.indicator){
41564 this.indicator.addClass('invisible');
41567 this.originalValue = this.getValue();
41569 if(this.validationEvent == 'keyup'){
41570 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41571 this.inputEl().on('keyup', this.filterValidation, this);
41573 else if(this.validationEvent !== false){
41574 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41577 if(this.selectOnFocus){
41578 this.on("focus", this.preFocus, this);
41581 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41582 this.inputEl().on("keypress", this.filterKeys, this);
41584 this.inputEl().relayEvent('keypress', this);
41587 var allowed = "0123456789";
41589 if(this.allowDecimals){
41590 allowed += this.decimalSeparator;
41593 if(this.allowNegative){
41597 if(this.thousandsDelimiter) {
41601 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41603 var keyPress = function(e){
41605 var k = e.getKey();
41607 var c = e.getCharCode();
41610 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41611 allowed.indexOf(String.fromCharCode(c)) === -1
41617 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41621 if(allowed.indexOf(String.fromCharCode(c)) === -1){
41626 this.inputEl().on("keypress", keyPress, this);
41630 onTriggerClick : function(e)
41637 this.loadNext = false;
41639 if(this.isExpanded()){
41644 this.hasFocus = true;
41646 if(this.triggerAction == 'all') {
41647 this.doQuery(this.allQuery, true);
41651 this.doQuery(this.getRawValue());
41654 getCurrency : function()
41656 var v = this.currencyEl().getValue();
41661 restrictHeight : function()
41663 this.list.alignTo(this.currencyEl(), this.listAlign);
41664 this.list.alignTo(this.currencyEl(), this.listAlign);
41667 onViewClick : function(view, doFocus, el, e)
41669 var index = this.view.getSelectedIndexes()[0];
41671 var r = this.store.getAt(index);
41674 this.onSelect(r, index);
41678 onSelect : function(record, index){
41680 if(this.fireEvent('beforeselect', this, record, index) !== false){
41682 this.setFromCurrencyData(index > -1 ? record.data : false);
41686 this.fireEvent('select', this, record, index);
41690 setFromCurrencyData : function(o)
41694 this.lastCurrency = o;
41696 if (this.currencyField) {
41697 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41699 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
41702 this.lastSelectionText = currency;
41704 //setting default currency
41705 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41706 this.setCurrency(this.defaultCurrency);
41710 this.setCurrency(currency);
41713 setFromData : function(o)
41717 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41719 this.setFromCurrencyData(c);
41724 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41726 Roo.log('no value set for '+ (this.name ? this.name : this.id));
41729 this.setValue(value);
41733 setCurrency : function(v)
41735 this.currencyValue = v;
41738 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41743 setValue : function(v)
41745 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41751 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41753 this.inputEl().dom.value = (v == '') ? '' :
41754 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41756 if(!this.allowZero && v === '0') {
41757 this.hiddenEl().dom.value = '';
41758 this.inputEl().dom.value = '';
41765 getRawValue : function()
41767 var v = this.inputEl().getValue();
41772 getValue : function()
41774 return this.fixPrecision(this.parseValue(this.getRawValue()));
41777 parseValue : function(value)
41779 if(this.thousandsDelimiter) {
41781 r = new RegExp(",", "g");
41782 value = value.replace(r, "");
41785 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41786 return isNaN(value) ? '' : value;
41790 fixPrecision : function(value)
41792 if(this.thousandsDelimiter) {
41794 r = new RegExp(",", "g");
41795 value = value.replace(r, "");
41798 var nan = isNaN(value);
41800 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41801 return nan ? '' : value;
41803 return parseFloat(value).toFixed(this.decimalPrecision);
41806 decimalPrecisionFcn : function(v)
41808 return Math.floor(v);
41811 validateValue : function(value)
41813 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41817 var num = this.parseValue(value);
41820 this.markInvalid(String.format(this.nanText, value));
41824 if(num < this.minValue){
41825 this.markInvalid(String.format(this.minText, this.minValue));
41829 if(num > this.maxValue){
41830 this.markInvalid(String.format(this.maxText, this.maxValue));
41837 validate : function()
41839 if(this.disabled || this.allowBlank){
41844 var currency = this.getCurrency();
41846 if(this.validateValue(this.getRawValue()) && currency.length){
41851 this.markInvalid();
41855 getName: function()
41860 beforeBlur : function()
41866 var v = this.parseValue(this.getRawValue());
41873 onBlur : function()
41877 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41878 //this.el.removeClass(this.focusClass);
41881 this.hasFocus = false;
41883 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41887 var v = this.getValue();
41889 if(String(v) !== String(this.startValue)){
41890 this.fireEvent('change', this, v, this.startValue);
41893 this.fireEvent("blur", this);
41896 inputEl : function()
41898 return this.el.select('.roo-money-amount-input', true).first();
41901 currencyEl : function()
41903 return this.el.select('.roo-money-currency-input', true).first();
41906 hiddenEl : function()
41908 return this.el.select('input.hidden-number-input',true).first();
41912 * @class Roo.bootstrap.BezierSignature
41913 * @extends Roo.bootstrap.Component
41914 * Bootstrap BezierSignature class
41915 * This script refer to:
41916 * Title: Signature Pad
41918 * Availability: https://github.com/szimek/signature_pad
41921 * Create a new BezierSignature
41922 * @param {Object} config The config object
41925 Roo.bootstrap.BezierSignature = function(config){
41926 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41932 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
41939 mouse_btn_down: true,
41942 * @cfg {int} canvas height
41944 canvas_height: '200px',
41947 * @cfg {float|function} Radius of a single dot.
41952 * @cfg {float} Minimum width of a line. Defaults to 0.5.
41957 * @cfg {float} Maximum width of a line. Defaults to 2.5.
41962 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41967 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41972 * @cfg {string} Color used to clear the background. Can be any color format accepted by context.fillStyle. Defaults to "rgba(0,0,0,0)" (transparent black). Use a non-transparent color e.g. "rgb(255,255,255)" (opaque white) if you'd like to save signatures as JPEG images.
41974 bg_color: 'rgba(0, 0, 0, 0)',
41977 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41979 dot_color: 'black',
41982 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41984 velocity_filter_weight: 0.7,
41987 * @cfg {function} Callback when stroke begin.
41992 * @cfg {function} Callback when stroke end.
41996 getAutoCreate : function()
41998 var cls = 'roo-signature column';
42001 cls += ' ' + this.cls;
42011 for(var i = 0; i < col_sizes.length; i++) {
42012 if(this[col_sizes[i]]) {
42013 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
42023 cls: 'roo-signature-body',
42027 cls: 'roo-signature-body-canvas',
42028 height: this.canvas_height,
42029 width: this.canvas_width
42036 style: 'display: none'
42044 initEvents: function()
42046 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
42048 var canvas = this.canvasEl();
42050 // mouse && touch event swapping...
42051 canvas.dom.style.touchAction = 'none';
42052 canvas.dom.style.msTouchAction = 'none';
42054 this.mouse_btn_down = false;
42055 canvas.on('mousedown', this._handleMouseDown, this);
42056 canvas.on('mousemove', this._handleMouseMove, this);
42057 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
42059 if (window.PointerEvent) {
42060 canvas.on('pointerdown', this._handleMouseDown, this);
42061 canvas.on('pointermove', this._handleMouseMove, this);
42062 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
42065 if ('ontouchstart' in window) {
42066 canvas.on('touchstart', this._handleTouchStart, this);
42067 canvas.on('touchmove', this._handleTouchMove, this);
42068 canvas.on('touchend', this._handleTouchEnd, this);
42071 Roo.EventManager.onWindowResize(this.resize, this, true);
42073 // file input event
42074 this.fileEl().on('change', this.uploadImage, this);
42081 resize: function(){
42083 var canvas = this.canvasEl().dom;
42084 var ctx = this.canvasElCtx();
42085 var img_data = false;
42087 if(canvas.width > 0) {
42088 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
42090 // setting canvas width will clean img data
42093 var style = window.getComputedStyle ?
42094 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
42096 var padding_left = parseInt(style.paddingLeft) || 0;
42097 var padding_right = parseInt(style.paddingRight) || 0;
42099 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
42102 ctx.putImageData(img_data, 0, 0);
42106 _handleMouseDown: function(e)
42108 if (e.browserEvent.which === 1) {
42109 this.mouse_btn_down = true;
42110 this.strokeBegin(e);
42114 _handleMouseMove: function (e)
42116 if (this.mouse_btn_down) {
42117 this.strokeMoveUpdate(e);
42121 _handleMouseUp: function (e)
42123 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
42124 this.mouse_btn_down = false;
42129 _handleTouchStart: function (e) {
42131 e.preventDefault();
42132 if (e.browserEvent.targetTouches.length === 1) {
42133 // var touch = e.browserEvent.changedTouches[0];
42134 // this.strokeBegin(touch);
42136 this.strokeBegin(e); // assume e catching the correct xy...
42140 _handleTouchMove: function (e) {
42141 e.preventDefault();
42142 // var touch = event.targetTouches[0];
42143 // _this._strokeMoveUpdate(touch);
42144 this.strokeMoveUpdate(e);
42147 _handleTouchEnd: function (e) {
42148 var wasCanvasTouched = e.target === this.canvasEl().dom;
42149 if (wasCanvasTouched) {
42150 e.preventDefault();
42151 // var touch = event.changedTouches[0];
42152 // _this._strokeEnd(touch);
42157 reset: function () {
42158 this._lastPoints = [];
42159 this._lastVelocity = 0;
42160 this._lastWidth = (this.min_width + this.max_width) / 2;
42161 this.canvasElCtx().fillStyle = this.dot_color;
42164 strokeMoveUpdate: function(e)
42166 this.strokeUpdate(e);
42168 if (this.throttle) {
42169 this.throttleStroke(this.strokeUpdate, this.throttle);
42172 this.strokeUpdate(e);
42176 strokeBegin: function(e)
42178 var newPointGroup = {
42179 color: this.dot_color,
42183 if (typeof this.onBegin === 'function') {
42187 this.curve_data.push(newPointGroup);
42189 this.strokeUpdate(e);
42192 strokeUpdate: function(e)
42194 var rect = this.canvasEl().dom.getBoundingClientRect();
42195 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
42196 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
42197 var lastPoints = lastPointGroup.points;
42198 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
42199 var isLastPointTooClose = lastPoint
42200 ? point.distanceTo(lastPoint) <= this.min_distance
42202 var color = lastPointGroup.color;
42203 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
42204 var curve = this.addPoint(point);
42206 this.drawDot({color: color, point: point});
42209 this.drawCurve({color: color, curve: curve});
42219 strokeEnd: function(e)
42221 this.strokeUpdate(e);
42222 if (typeof this.onEnd === 'function') {
42227 addPoint: function (point) {
42228 var _lastPoints = this._lastPoints;
42229 _lastPoints.push(point);
42230 if (_lastPoints.length > 2) {
42231 if (_lastPoints.length === 3) {
42232 _lastPoints.unshift(_lastPoints[0]);
42234 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
42235 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
42236 _lastPoints.shift();
42242 calculateCurveWidths: function (startPoint, endPoint) {
42243 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
42244 (1 - this.velocity_filter_weight) * this._lastVelocity;
42246 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
42249 start: this._lastWidth
42252 this._lastVelocity = velocity;
42253 this._lastWidth = newWidth;
42257 drawDot: function (_a) {
42258 var color = _a.color, point = _a.point;
42259 var ctx = this.canvasElCtx();
42260 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
42262 this.drawCurveSegment(point.x, point.y, width);
42264 ctx.fillStyle = color;
42268 drawCurve: function (_a) {
42269 var color = _a.color, curve = _a.curve;
42270 var ctx = this.canvasElCtx();
42271 var widthDelta = curve.endWidth - curve.startWidth;
42272 var drawSteps = Math.floor(curve.length()) * 2;
42274 ctx.fillStyle = color;
42275 for (var i = 0; i < drawSteps; i += 1) {
42276 var t = i / drawSteps;
42282 var x = uuu * curve.startPoint.x;
42283 x += 3 * uu * t * curve.control1.x;
42284 x += 3 * u * tt * curve.control2.x;
42285 x += ttt * curve.endPoint.x;
42286 var y = uuu * curve.startPoint.y;
42287 y += 3 * uu * t * curve.control1.y;
42288 y += 3 * u * tt * curve.control2.y;
42289 y += ttt * curve.endPoint.y;
42290 var width = curve.startWidth + ttt * widthDelta;
42291 this.drawCurveSegment(x, y, width);
42297 drawCurveSegment: function (x, y, width) {
42298 var ctx = this.canvasElCtx();
42300 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
42301 this.is_empty = false;
42306 var ctx = this.canvasElCtx();
42307 var canvas = this.canvasEl().dom;
42308 ctx.fillStyle = this.bg_color;
42309 ctx.clearRect(0, 0, canvas.width, canvas.height);
42310 ctx.fillRect(0, 0, canvas.width, canvas.height);
42311 this.curve_data = [];
42313 this.is_empty = true;
42318 return this.el.select('input',true).first();
42321 canvasEl: function()
42323 return this.el.select('canvas',true).first();
42326 canvasElCtx: function()
42328 return this.el.select('canvas',true).first().dom.getContext('2d');
42331 getImage: function(type)
42333 if(this.is_empty) {
42338 return this.canvasEl().dom.toDataURL('image/'+type, 1);
42341 drawFromImage: function(img_src)
42343 var img = new Image();
42345 img.onload = function(){
42346 this.canvasElCtx().drawImage(img, 0, 0);
42351 this.is_empty = false;
42354 selectImage: function()
42356 this.fileEl().dom.click();
42359 uploadImage: function(e)
42361 var reader = new FileReader();
42363 reader.onload = function(e){
42364 var img = new Image();
42365 img.onload = function(){
42367 this.canvasElCtx().drawImage(img, 0, 0);
42369 img.src = e.target.result;
42372 reader.readAsDataURL(e.target.files[0]);
42375 // Bezier Point Constructor
42376 Point: (function () {
42377 function Point(x, y, time) {
42380 this.time = time || Date.now();
42382 Point.prototype.distanceTo = function (start) {
42383 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42385 Point.prototype.equals = function (other) {
42386 return this.x === other.x && this.y === other.y && this.time === other.time;
42388 Point.prototype.velocityFrom = function (start) {
42389 return this.time !== start.time
42390 ? this.distanceTo(start) / (this.time - start.time)
42397 // Bezier Constructor
42398 Bezier: (function () {
42399 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42400 this.startPoint = startPoint;
42401 this.control2 = control2;
42402 this.control1 = control1;
42403 this.endPoint = endPoint;
42404 this.startWidth = startWidth;
42405 this.endWidth = endWidth;
42407 Bezier.fromPoints = function (points, widths, scope) {
42408 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42409 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42410 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42412 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42413 var dx1 = s1.x - s2.x;
42414 var dy1 = s1.y - s2.y;
42415 var dx2 = s2.x - s3.x;
42416 var dy2 = s2.y - s3.y;
42417 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42418 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42419 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42420 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42421 var dxm = m1.x - m2.x;
42422 var dym = m1.y - m2.y;
42423 var k = l2 / (l1 + l2);
42424 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42425 var tx = s2.x - cm.x;
42426 var ty = s2.y - cm.y;
42428 c1: new scope.Point(m1.x + tx, m1.y + ty),
42429 c2: new scope.Point(m2.x + tx, m2.y + ty)
42432 Bezier.prototype.length = function () {
42437 for (var i = 0; i <= steps; i += 1) {
42439 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42440 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42442 var xdiff = cx - px;
42443 var ydiff = cy - py;
42444 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42451 Bezier.prototype.point = function (t, start, c1, c2, end) {
42452 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42453 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42454 + (3.0 * c2 * (1.0 - t) * t * t)
42455 + (end * t * t * t);
42460 throttleStroke: function(fn, wait) {
42461 if (wait === void 0) { wait = 250; }
42463 var timeout = null;
42467 var later = function () {
42468 previous = Date.now();
42470 result = fn.apply(storedContext, storedArgs);
42472 storedContext = null;
42476 return function wrapper() {
42478 for (var _i = 0; _i < arguments.length; _i++) {
42479 args[_i] = arguments[_i];
42481 var now = Date.now();
42482 var remaining = wait - (now - previous);
42483 storedContext = this;
42485 if (remaining <= 0 || remaining > wait) {
42487 clearTimeout(timeout);
42491 result = fn.apply(storedContext, storedArgs);
42493 storedContext = null;
42497 else if (!timeout) {
42498 timeout = window.setTimeout(later, remaining);