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);
1499 * @class Roo.bootstrap.Img
1500 * @extends Roo.bootstrap.Component
1501 * Bootstrap Img class
1502 * @cfg {Boolean} imgResponsive false | true
1503 * @cfg {String} border rounded | circle | thumbnail
1504 * @cfg {String} src image source
1505 * @cfg {String} alt image alternative text
1506 * @cfg {String} href a tag href
1507 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1508 * @cfg {String} xsUrl xs image source
1509 * @cfg {String} smUrl sm image source
1510 * @cfg {String} mdUrl md image source
1511 * @cfg {String} lgUrl lg image source
1514 * Create a new Input
1515 * @param {Object} config The config object
1518 Roo.bootstrap.Img = function(config){
1519 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1525 * The img click event for the img.
1526 * @param {Roo.EventObject} e
1532 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1534 imgResponsive: true,
1544 getAutoCreate : function()
1546 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1547 return this.createSingleImg();
1552 cls: 'roo-image-responsive-group',
1557 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1559 if(!_this[size + 'Url']){
1565 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1566 html: _this.html || cfg.html,
1567 src: _this[size + 'Url']
1570 img.cls += ' roo-image-responsive-' + size;
1572 var s = ['xs', 'sm', 'md', 'lg'];
1574 s.splice(s.indexOf(size), 1);
1576 Roo.each(s, function(ss){
1577 img.cls += ' hidden-' + ss;
1580 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1581 cfg.cls += ' img-' + _this.border;
1585 cfg.alt = _this.alt;
1598 a.target = _this.target;
1602 cfg.cn.push((_this.href) ? a : img);
1609 createSingleImg : function()
1613 cls: (this.imgResponsive) ? 'img-responsive' : '',
1615 src : 'about:blank' // just incase src get's set to undefined?!?
1618 cfg.html = this.html || cfg.html;
1620 cfg.src = this.src || cfg.src;
1622 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1623 cfg.cls += ' img-' + this.border;
1640 a.target = this.target;
1645 return (this.href) ? a : cfg;
1648 initEvents: function()
1651 this.el.on('click', this.onClick, this);
1656 onClick : function(e)
1658 Roo.log('img onclick');
1659 this.fireEvent('click', this, e);
1662 * Sets the url of the image - used to update it
1663 * @param {String} url the url of the image
1666 setSrc : function(url)
1670 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1671 this.el.dom.src = url;
1675 this.el.select('img', true).first().dom.src = url;
1691 * @class Roo.bootstrap.Link
1692 * @extends Roo.bootstrap.Component
1693 * Bootstrap Link Class
1694 * @cfg {String} alt image alternative text
1695 * @cfg {String} href a tag href
1696 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1697 * @cfg {String} html the content of the link.
1698 * @cfg {String} anchor name for the anchor link
1699 * @cfg {String} fa - favicon
1701 * @cfg {Boolean} preventDefault (true | false) default false
1705 * Create a new Input
1706 * @param {Object} config The config object
1709 Roo.bootstrap.Link = function(config){
1710 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1716 * The img click event for the img.
1717 * @param {Roo.EventObject} e
1723 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1727 preventDefault: false,
1733 getAutoCreate : function()
1735 var html = this.html || '';
1737 if (this.fa !== false) {
1738 html = '<i class="fa fa-' + this.fa + '"></i>';
1743 // anchor's do not require html/href...
1744 if (this.anchor === false) {
1746 cfg.href = this.href || '#';
1748 cfg.name = this.anchor;
1749 if (this.html !== false || this.fa !== false) {
1752 if (this.href !== false) {
1753 cfg.href = this.href;
1757 if(this.alt !== false){
1762 if(this.target !== false) {
1763 cfg.target = this.target;
1769 initEvents: function() {
1771 if(!this.href || this.preventDefault){
1772 this.el.on('click', this.onClick, this);
1776 onClick : function(e)
1778 if(this.preventDefault){
1781 //Roo.log('img onclick');
1782 this.fireEvent('click', this, e);
1795 * @class Roo.bootstrap.Header
1796 * @extends Roo.bootstrap.Component
1797 * Bootstrap Header class
1798 * @cfg {String} html content of header
1799 * @cfg {Number} level (1|2|3|4|5|6) default 1
1802 * Create a new Header
1803 * @param {Object} config The config object
1807 Roo.bootstrap.Header = function(config){
1808 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1811 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1819 getAutoCreate : function(){
1824 tag: 'h' + (1 *this.level),
1825 html: this.html || ''
1837 * Ext JS Library 1.1.1
1838 * Copyright(c) 2006-2007, Ext JS, LLC.
1840 * Originally Released Under LGPL - original licence link has changed is not relivant.
1843 * <script type="text/javascript">
1847 * @class Roo.bootstrap.MenuMgr
1848 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1851 Roo.bootstrap.MenuMgr = function(){
1852 var menus, active, groups = {}, attached = false, lastShow = new Date();
1854 // private - called when first menu is created
1857 active = new Roo.util.MixedCollection();
1858 Roo.get(document).addKeyListener(27, function(){
1859 if(active.length > 0){
1867 if(active && active.length > 0){
1868 var c = active.clone();
1878 if(active.length < 1){
1879 Roo.get(document).un("mouseup", onMouseDown);
1887 var last = active.last();
1888 lastShow = new Date();
1891 Roo.get(document).on("mouseup", onMouseDown);
1896 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1897 m.parentMenu.activeChild = m;
1898 }else if(last && last.isVisible()){
1899 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1904 function onBeforeHide(m){
1906 m.activeChild.hide();
1908 if(m.autoHideTimer){
1909 clearTimeout(m.autoHideTimer);
1910 delete m.autoHideTimer;
1915 function onBeforeShow(m){
1916 var pm = m.parentMenu;
1917 if(!pm && !m.allowOtherMenus){
1919 }else if(pm && pm.activeChild && active != m){
1920 pm.activeChild.hide();
1924 // private this should really trigger on mouseup..
1925 function onMouseDown(e){
1926 Roo.log("on Mouse Up");
1928 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1929 Roo.log("MenuManager hideAll");
1938 function onBeforeCheck(mi, state){
1940 var g = groups[mi.group];
1941 for(var i = 0, l = g.length; i < l; i++){
1943 g[i].setChecked(false);
1952 * Hides all menus that are currently visible
1954 hideAll : function(){
1959 register : function(menu){
1963 menus[menu.id] = menu;
1964 menu.on("beforehide", onBeforeHide);
1965 menu.on("hide", onHide);
1966 menu.on("beforeshow", onBeforeShow);
1967 menu.on("show", onShow);
1969 if(g && menu.events["checkchange"]){
1973 groups[g].push(menu);
1974 menu.on("checkchange", onCheck);
1979 * Returns a {@link Roo.menu.Menu} object
1980 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1981 * be used to generate and return a new Menu instance.
1983 get : function(menu){
1984 if(typeof menu == "string"){ // menu id
1986 }else if(menu.events){ // menu instance
1989 /*else if(typeof menu.length == 'number'){ // array of menu items?
1990 return new Roo.bootstrap.Menu({items:menu});
1991 }else{ // otherwise, must be a config
1992 return new Roo.bootstrap.Menu(menu);
1999 unregister : function(menu){
2000 delete menus[menu.id];
2001 menu.un("beforehide", onBeforeHide);
2002 menu.un("hide", onHide);
2003 menu.un("beforeshow", onBeforeShow);
2004 menu.un("show", onShow);
2006 if(g && menu.events["checkchange"]){
2007 groups[g].remove(menu);
2008 menu.un("checkchange", onCheck);
2013 registerCheckable : function(menuItem){
2014 var g = menuItem.group;
2019 groups[g].push(menuItem);
2020 menuItem.on("beforecheckchange", onBeforeCheck);
2025 unregisterCheckable : function(menuItem){
2026 var g = menuItem.group;
2028 groups[g].remove(menuItem);
2029 menuItem.un("beforecheckchange", onBeforeCheck);
2041 * @class Roo.bootstrap.Menu
2042 * @extends Roo.bootstrap.Component
2043 * Bootstrap Menu class - container for MenuItems
2044 * @cfg {String} type (dropdown|treeview|submenu) type of menu
2045 * @cfg {bool} hidden if the menu should be hidden when rendered.
2046 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
2047 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
2051 * @param {Object} config The config object
2055 Roo.bootstrap.Menu = function(config){
2056 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2057 if (this.registerMenu && this.type != 'treeview') {
2058 Roo.bootstrap.MenuMgr.register(this);
2065 * Fires before this menu is displayed (return false to block)
2066 * @param {Roo.menu.Menu} this
2071 * Fires before this menu is hidden (return false to block)
2072 * @param {Roo.menu.Menu} this
2077 * Fires after this menu is displayed
2078 * @param {Roo.menu.Menu} this
2083 * Fires after this menu is hidden
2084 * @param {Roo.menu.Menu} this
2089 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2090 * @param {Roo.menu.Menu} this
2091 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2092 * @param {Roo.EventObject} e
2097 * Fires when the mouse is hovering over this menu
2098 * @param {Roo.menu.Menu} this
2099 * @param {Roo.EventObject} e
2100 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2105 * Fires when the mouse exits this menu
2106 * @param {Roo.menu.Menu} this
2107 * @param {Roo.EventObject} e
2108 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2113 * Fires when a menu item contained in this menu is clicked
2114 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2115 * @param {Roo.EventObject} e
2119 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2122 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2126 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2129 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2131 registerMenu : true,
2133 menuItems :false, // stores the menu items..
2143 getChildContainer : function() {
2147 getAutoCreate : function(){
2149 //if (['right'].indexOf(this.align)!==-1) {
2150 // cfg.cn[1].cls += ' pull-right'
2156 cls : 'dropdown-menu' ,
2157 style : 'z-index:1000'
2161 if (this.type === 'submenu') {
2162 cfg.cls = 'submenu active';
2164 if (this.type === 'treeview') {
2165 cfg.cls = 'treeview-menu';
2170 initEvents : function() {
2172 // Roo.log("ADD event");
2173 // Roo.log(this.triggerEl.dom);
2175 this.triggerEl.on('click', this.onTriggerClick, this);
2177 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2180 if (this.triggerEl.hasClass('nav-item')) {
2181 // dropdown toggle on the 'a' in BS4?
2182 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2184 this.triggerEl.addClass('dropdown-toggle');
2187 this.el.on('touchstart' , this.onTouch, this);
2189 this.el.on('click' , this.onClick, this);
2191 this.el.on("mouseover", this.onMouseOver, this);
2192 this.el.on("mouseout", this.onMouseOut, this);
2196 findTargetItem : function(e)
2198 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2202 //Roo.log(t); Roo.log(t.id);
2204 //Roo.log(this.menuitems);
2205 return this.menuitems.get(t.id);
2207 //return this.items.get(t.menuItemId);
2213 onTouch : function(e)
2215 Roo.log("menu.onTouch");
2216 //e.stopEvent(); this make the user popdown broken
2220 onClick : function(e)
2222 Roo.log("menu.onClick");
2224 var t = this.findTargetItem(e);
2225 if(!t || t.isContainer){
2230 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2231 if(t == this.activeItem && t.shouldDeactivate(e)){
2232 this.activeItem.deactivate();
2233 delete this.activeItem;
2237 this.setActiveItem(t, true);
2245 Roo.log('pass click event');
2249 this.fireEvent("click", this, t, e);
2253 if(!t.href.length || t.href == '#'){
2254 (function() { _this.hide(); }).defer(100);
2259 onMouseOver : function(e){
2260 var t = this.findTargetItem(e);
2263 // if(t.canActivate && !t.disabled){
2264 // this.setActiveItem(t, true);
2268 this.fireEvent("mouseover", this, e, t);
2270 isVisible : function(){
2271 return !this.hidden;
2273 onMouseOut : function(e){
2274 var t = this.findTargetItem(e);
2277 // if(t == this.activeItem && t.shouldDeactivate(e)){
2278 // this.activeItem.deactivate();
2279 // delete this.activeItem;
2282 this.fireEvent("mouseout", this, e, t);
2287 * Displays this menu relative to another element
2288 * @param {String/HTMLElement/Roo.Element} element The element to align to
2289 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2290 * the element (defaults to this.defaultAlign)
2291 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2293 show : function(el, pos, parentMenu)
2295 if (false === this.fireEvent("beforeshow", this)) {
2296 Roo.log("show canceled");
2299 this.parentMenu = parentMenu;
2304 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2307 * Displays this menu at a specific xy position
2308 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2309 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2311 showAt : function(xy, parentMenu, /* private: */_e){
2312 this.parentMenu = parentMenu;
2317 this.fireEvent("beforeshow", this);
2318 //xy = this.el.adjustForConstraints(xy);
2322 this.hideMenuItems();
2323 this.hidden = false;
2324 this.triggerEl.addClass('open');
2325 this.el.addClass('show');
2327 // reassign x when hitting right
2328 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2329 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2332 // reassign y when hitting bottom
2333 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2334 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2337 // but the list may align on trigger left or trigger top... should it be a properity?
2339 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2344 this.fireEvent("show", this);
2350 this.doFocus.defer(50, this);
2354 doFocus : function(){
2356 this.focusEl.focus();
2361 * Hides this menu and optionally all parent menus
2362 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2364 hide : function(deep)
2366 if (false === this.fireEvent("beforehide", this)) {
2367 Roo.log("hide canceled");
2370 this.hideMenuItems();
2371 if(this.el && this.isVisible()){
2373 if(this.activeItem){
2374 this.activeItem.deactivate();
2375 this.activeItem = null;
2377 this.triggerEl.removeClass('open');;
2378 this.el.removeClass('show');
2380 this.fireEvent("hide", this);
2382 if(deep === true && this.parentMenu){
2383 this.parentMenu.hide(true);
2387 onTriggerClick : function(e)
2389 Roo.log('trigger click');
2391 var target = e.getTarget();
2393 Roo.log(target.nodeName.toLowerCase());
2395 if(target.nodeName.toLowerCase() === 'i'){
2401 onTriggerPress : function(e)
2403 Roo.log('trigger press');
2404 //Roo.log(e.getTarget());
2405 // Roo.log(this.triggerEl.dom);
2407 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2408 var pel = Roo.get(e.getTarget());
2409 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2410 Roo.log('is treeview or dropdown?');
2414 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2418 if (this.isVisible()) {
2423 this.show(this.triggerEl, '?', false);
2426 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2433 hideMenuItems : function()
2435 Roo.log("hide Menu Items");
2440 this.el.select('.open',true).each(function(aa) {
2442 aa.removeClass('open');
2446 addxtypeChild : function (tree, cntr) {
2447 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2449 this.menuitems.add(comp);
2461 this.getEl().dom.innerHTML = '';
2462 this.menuitems.clear();
2476 * @class Roo.bootstrap.MenuItem
2477 * @extends Roo.bootstrap.Component
2478 * Bootstrap MenuItem class
2479 * @cfg {String} html the menu label
2480 * @cfg {String} href the link
2481 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2482 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2483 * @cfg {Boolean} active used on sidebars to highlight active itesm
2484 * @cfg {String} fa favicon to show on left of menu item.
2485 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2489 * Create a new MenuItem
2490 * @param {Object} config The config object
2494 Roo.bootstrap.MenuItem = function(config){
2495 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2500 * The raw click event for the entire grid.
2501 * @param {Roo.bootstrap.MenuItem} this
2502 * @param {Roo.EventObject} e
2508 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2512 preventDefault: false,
2513 isContainer : false,
2517 getAutoCreate : function(){
2519 if(this.isContainer){
2522 cls: 'dropdown-menu-item '
2532 cls : 'dropdown-item',
2537 if (this.fa !== false) {
2540 cls : 'fa fa-' + this.fa
2549 cls: 'dropdown-menu-item',
2552 if (this.parent().type == 'treeview') {
2553 cfg.cls = 'treeview-menu';
2556 cfg.cls += ' active';
2561 anc.href = this.href || cfg.cn[0].href ;
2562 ctag.html = this.html || cfg.cn[0].html ;
2566 initEvents: function()
2568 if (this.parent().type == 'treeview') {
2569 this.el.select('a').on('click', this.onClick, this);
2573 this.menu.parentType = this.xtype;
2574 this.menu.triggerEl = this.el;
2575 this.menu = this.addxtype(Roo.apply({}, this.menu));
2579 onClick : function(e)
2581 Roo.log('item on click ');
2583 if(this.preventDefault){
2586 //this.parent().hideMenuItems();
2588 this.fireEvent('click', this, e);
2607 * @class Roo.bootstrap.MenuSeparator
2608 * @extends Roo.bootstrap.Component
2609 * Bootstrap MenuSeparator class
2612 * Create a new MenuItem
2613 * @param {Object} config The config object
2617 Roo.bootstrap.MenuSeparator = function(config){
2618 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2621 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2623 getAutoCreate : function(){
2642 * @class Roo.bootstrap.Modal
2643 * @extends Roo.bootstrap.Component
2644 * Bootstrap Modal class
2645 * @cfg {String} title Title of dialog
2646 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2647 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2648 * @cfg {Boolean} specificTitle default false
2649 * @cfg {Array} buttons Array of buttons or standard button set..
2650 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2651 * @cfg {Boolean} animate default true
2652 * @cfg {Boolean} allow_close default true
2653 * @cfg {Boolean} fitwindow default false
2654 * @cfg {Number} width fixed width - usefull for chrome extension only really.
2655 * @cfg {Number} height fixed height - usefull for chrome extension only really.
2656 * @cfg {String} size (sm|lg) default empty
2657 * @cfg {Number} max_width set the max width of modal
2661 * Create a new Modal Dialog
2662 * @param {Object} config The config object
2665 Roo.bootstrap.Modal = function(config){
2666 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2671 * The raw btnclick event for the button
2672 * @param {Roo.EventObject} e
2677 * Fire when dialog resize
2678 * @param {Roo.bootstrap.Modal} this
2679 * @param {Roo.EventObject} e
2683 this.buttons = this.buttons || [];
2686 this.tmpl = Roo.factory(this.tmpl);
2691 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2693 title : 'test dialog',
2703 specificTitle: false,
2705 buttonPosition: 'right',
2728 onRender : function(ct, position)
2730 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2733 var cfg = Roo.apply({}, this.getAutoCreate());
2736 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2738 //if (!cfg.name.length) {
2742 cfg.cls += ' ' + this.cls;
2745 cfg.style = this.style;
2747 this.el = Roo.get(document.body).createChild(cfg, position);
2749 //var type = this.el.dom.type;
2752 if(this.tabIndex !== undefined){
2753 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2756 this.dialogEl = this.el.select('.modal-dialog',true).first();
2757 this.bodyEl = this.el.select('.modal-body',true).first();
2758 this.closeEl = this.el.select('.modal-header .close', true).first();
2759 this.headerEl = this.el.select('.modal-header',true).first();
2760 this.titleEl = this.el.select('.modal-title',true).first();
2761 this.footerEl = this.el.select('.modal-footer',true).first();
2763 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2765 //this.el.addClass("x-dlg-modal");
2767 if (this.buttons.length) {
2768 Roo.each(this.buttons, function(bb) {
2769 var b = Roo.apply({}, bb);
2770 b.xns = b.xns || Roo.bootstrap;
2771 b.xtype = b.xtype || 'Button';
2772 if (typeof(b.listeners) == 'undefined') {
2773 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2776 var btn = Roo.factory(b);
2778 btn.render(this.getButtonContainer());
2782 // render the children.
2785 if(typeof(this.items) != 'undefined'){
2786 var items = this.items;
2789 for(var i =0;i < items.length;i++) {
2790 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2794 this.items = nitems;
2796 // where are these used - they used to be body/close/footer
2800 //this.el.addClass([this.fieldClass, this.cls]);
2804 getAutoCreate : function()
2806 // we will default to modal-body-overflow - might need to remove or make optional later.
2808 cls : 'modal-body enable-modal-body-overflow ',
2809 html : this.html || ''
2814 cls : 'modal-title',
2818 if(this.specificTitle){
2824 if (this.allow_close && Roo.bootstrap.version == 3) {
2834 if (this.allow_close && Roo.bootstrap.version == 4) {
2844 if(this.size.length){
2845 size = 'modal-' + this.size;
2848 var footer = Roo.bootstrap.version == 3 ?
2850 cls : 'modal-footer',
2854 cls: 'btn-' + this.buttonPosition
2859 { // BS4 uses mr-auto on left buttons....
2860 cls : 'modal-footer'
2871 cls: "modal-dialog " + size,
2874 cls : "modal-content",
2877 cls : 'modal-header',
2892 modal.cls += ' fade';
2898 getChildContainer : function() {
2903 getButtonContainer : function() {
2905 return Roo.bootstrap.version == 4 ?
2906 this.el.select('.modal-footer',true).first()
2907 : this.el.select('.modal-footer div',true).first();
2910 initEvents : function()
2912 if (this.allow_close) {
2913 this.closeEl.on('click', this.hide, this);
2915 Roo.EventManager.onWindowResize(this.resize, this, true);
2923 this.maskEl.setSize(
2924 Roo.lib.Dom.getViewWidth(true),
2925 Roo.lib.Dom.getViewHeight(true)
2928 if (this.fitwindow) {
2932 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2933 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
2938 if(this.max_width !== 0) {
2940 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2943 this.setSize(w, this.height);
2947 if(this.max_height) {
2948 this.setSize(w,Math.min(
2950 Roo.lib.Dom.getViewportHeight(true) - 60
2956 if(!this.fit_content) {
2957 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2961 this.setSize(w, Math.min(
2963 this.headerEl.getHeight() +
2964 this.footerEl.getHeight() +
2965 this.getChildHeight(this.bodyEl.dom.childNodes),
2966 Roo.lib.Dom.getViewportHeight(true) - 60)
2972 setSize : function(w,h)
2983 if (!this.rendered) {
2987 //this.el.setStyle('display', 'block');
2988 this.el.removeClass('hideing');
2989 this.el.dom.style.display='block';
2991 Roo.get(document.body).addClass('modal-open');
2993 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2996 this.el.addClass('show');
2997 this.el.addClass('in');
3000 this.el.addClass('show');
3001 this.el.addClass('in');
3004 // not sure how we can show data in here..
3006 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3009 Roo.get(document.body).addClass("x-body-masked");
3011 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3012 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3013 this.maskEl.dom.style.display = 'block';
3014 this.maskEl.addClass('show');
3019 this.fireEvent('show', this);
3021 // set zindex here - otherwise it appears to be ignored...
3022 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3025 this.items.forEach( function(e) {
3026 e.layout ? e.layout() : false;
3034 if(this.fireEvent("beforehide", this) !== false){
3036 this.maskEl.removeClass('show');
3038 this.maskEl.dom.style.display = '';
3039 Roo.get(document.body).removeClass("x-body-masked");
3040 this.el.removeClass('in');
3041 this.el.select('.modal-dialog', true).first().setStyle('transform','');
3043 if(this.animate){ // why
3044 this.el.addClass('hideing');
3045 this.el.removeClass('show');
3047 if (!this.el.hasClass('hideing')) {
3048 return; // it's been shown again...
3051 this.el.dom.style.display='';
3053 Roo.get(document.body).removeClass('modal-open');
3054 this.el.removeClass('hideing');
3058 this.el.removeClass('show');
3059 this.el.dom.style.display='';
3060 Roo.get(document.body).removeClass('modal-open');
3063 this.fireEvent('hide', this);
3066 isVisible : function()
3069 return this.el.hasClass('show') && !this.el.hasClass('hideing');
3073 addButton : function(str, cb)
3077 var b = Roo.apply({}, { html : str } );
3078 b.xns = b.xns || Roo.bootstrap;
3079 b.xtype = b.xtype || 'Button';
3080 if (typeof(b.listeners) == 'undefined') {
3081 b.listeners = { click : cb.createDelegate(this) };
3084 var btn = Roo.factory(b);
3086 btn.render(this.getButtonContainer());
3092 setDefaultButton : function(btn)
3094 //this.el.select('.modal-footer').()
3097 resizeTo: function(w,h)
3099 this.dialogEl.setWidth(w);
3101 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
3103 this.bodyEl.setHeight(h - diff);
3105 this.fireEvent('resize', this);
3108 setContentSize : function(w, h)
3112 onButtonClick: function(btn,e)
3115 this.fireEvent('btnclick', btn.name, e);
3118 * Set the title of the Dialog
3119 * @param {String} str new Title
3121 setTitle: function(str) {
3122 this.titleEl.dom.innerHTML = str;
3125 * Set the body of the Dialog
3126 * @param {String} str new Title
3128 setBody: function(str) {
3129 this.bodyEl.dom.innerHTML = str;
3132 * Set the body of the Dialog using the template
3133 * @param {Obj} data - apply this data to the template and replace the body contents.
3135 applyBody: function(obj)
3138 Roo.log("Error - using apply Body without a template");
3141 this.tmpl.overwrite(this.bodyEl, obj);
3144 getChildHeight : function(child_nodes)
3148 child_nodes.length == 0
3153 var child_height = 0;
3155 for(var i = 0; i < child_nodes.length; i++) {
3158 * for modal with tabs...
3159 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3161 var layout_childs = child_nodes[i].childNodes;
3163 for(var j = 0; j < layout_childs.length; j++) {
3165 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3167 var layout_body_childs = layout_childs[j].childNodes;
3169 for(var k = 0; k < layout_body_childs.length; k++) {
3171 if(layout_body_childs[k].classList.contains('navbar')) {
3172 child_height += layout_body_childs[k].offsetHeight;
3176 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3178 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3180 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3182 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3183 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3198 child_height += child_nodes[i].offsetHeight;
3199 // Roo.log(child_nodes[i].offsetHeight);
3202 return child_height;
3208 Roo.apply(Roo.bootstrap.Modal, {
3210 * Button config that displays a single OK button
3219 * Button config that displays Yes and No buttons
3235 * Button config that displays OK and Cancel buttons
3250 * Button config that displays Yes, No and Cancel buttons
3274 * messagebox - can be used as a replace
3278 * @class Roo.MessageBox
3279 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3283 Roo.Msg.alert('Status', 'Changes saved successfully.');
3285 // Prompt for user data:
3286 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3288 // process text value...
3292 // Show a dialog using config options:
3294 title:'Save Changes?',
3295 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3296 buttons: Roo.Msg.YESNOCANCEL,
3303 Roo.bootstrap.MessageBox = function(){
3304 var dlg, opt, mask, waitTimer;
3305 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3306 var buttons, activeTextEl, bwidth;
3310 var handleButton = function(button){
3312 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3316 var handleHide = function(){
3318 dlg.el.removeClass(opt.cls);
3321 // Roo.TaskMgr.stop(waitTimer);
3322 // waitTimer = null;
3327 var updateButtons = function(b){
3330 buttons["ok"].hide();
3331 buttons["cancel"].hide();
3332 buttons["yes"].hide();
3333 buttons["no"].hide();
3334 dlg.footerEl.hide();
3338 dlg.footerEl.show();
3339 for(var k in buttons){
3340 if(typeof buttons[k] != "function"){
3343 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3344 width += buttons[k].el.getWidth()+15;
3354 var handleEsc = function(d, k, e){
3355 if(opt && opt.closable !== false){
3365 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3366 * @return {Roo.BasicDialog} The BasicDialog element
3368 getDialog : function(){
3370 dlg = new Roo.bootstrap.Modal( {
3373 //constraintoviewport:false,
3375 //collapsible : false,
3380 //buttonAlign:"center",
3381 closeClick : function(){
3382 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3385 handleButton("cancel");
3390 dlg.on("hide", handleHide);
3392 //dlg.addKeyListener(27, handleEsc);
3394 this.buttons = buttons;
3395 var bt = this.buttonText;
3396 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3397 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3398 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3399 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3401 bodyEl = dlg.bodyEl.createChild({
3403 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3404 '<textarea class="roo-mb-textarea"></textarea>' +
3405 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3407 msgEl = bodyEl.dom.firstChild;
3408 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3409 textboxEl.enableDisplayMode();
3410 textboxEl.addKeyListener([10,13], function(){
3411 if(dlg.isVisible() && opt && opt.buttons){
3414 }else if(opt.buttons.yes){
3415 handleButton("yes");
3419 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3420 textareaEl.enableDisplayMode();
3421 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3422 progressEl.enableDisplayMode();
3424 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3425 var pf = progressEl.dom.firstChild;
3427 pp = Roo.get(pf.firstChild);
3428 pp.setHeight(pf.offsetHeight);
3436 * Updates the message box body text
3437 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3438 * the XHTML-compliant non-breaking space character '&#160;')
3439 * @return {Roo.MessageBox} This message box
3441 updateText : function(text)
3443 if(!dlg.isVisible() && !opt.width){
3444 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3445 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3447 msgEl.innerHTML = text || ' ';
3449 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3450 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3452 Math.min(opt.width || cw , this.maxWidth),
3453 Math.max(opt.minWidth || this.minWidth, bwidth)
3456 activeTextEl.setWidth(w);
3458 if(dlg.isVisible()){
3459 dlg.fixedcenter = false;
3461 // to big, make it scroll. = But as usual stupid IE does not support
3464 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3465 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3466 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3468 bodyEl.dom.style.height = '';
3469 bodyEl.dom.style.overflowY = '';
3472 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3474 bodyEl.dom.style.overflowX = '';
3477 dlg.setContentSize(w, bodyEl.getHeight());
3478 if(dlg.isVisible()){
3479 dlg.fixedcenter = true;
3485 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3486 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3487 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3488 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3489 * @return {Roo.MessageBox} This message box
3491 updateProgress : function(value, text){
3493 this.updateText(text);
3496 if (pp) { // weird bug on my firefox - for some reason this is not defined
3497 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3498 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3504 * Returns true if the message box is currently displayed
3505 * @return {Boolean} True if the message box is visible, else false
3507 isVisible : function(){
3508 return dlg && dlg.isVisible();
3512 * Hides the message box if it is displayed
3515 if(this.isVisible()){
3521 * Displays a new message box, or reinitializes an existing message box, based on the config options
3522 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3523 * The following config object properties are supported:
3525 Property Type Description
3526 ---------- --------------- ------------------------------------------------------------------------------------
3527 animEl String/Element An id or Element from which the message box should animate as it opens and
3528 closes (defaults to undefined)
3529 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3530 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3531 closable Boolean False to hide the top-right close button (defaults to true). Note that
3532 progress and wait dialogs will ignore this property and always hide the
3533 close button as they can only be closed programmatically.
3534 cls String A custom CSS class to apply to the message box element
3535 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3536 displayed (defaults to 75)
3537 fn Function A callback function to execute after closing the dialog. The arguments to the
3538 function will be btn (the name of the button that was clicked, if applicable,
3539 e.g. "ok"), and text (the value of the active text field, if applicable).
3540 Progress and wait dialogs will ignore this option since they do not respond to
3541 user actions and can only be closed programmatically, so any required function
3542 should be called by the same code after it closes the dialog.
3543 icon String A CSS class that provides a background image to be used as an icon for
3544 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3545 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3546 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3547 modal Boolean False to allow user interaction with the page while the message box is
3548 displayed (defaults to true)
3549 msg String A string that will replace the existing message box body text (defaults
3550 to the XHTML-compliant non-breaking space character ' ')
3551 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3552 progress Boolean True to display a progress bar (defaults to false)
3553 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3554 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3555 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3556 title String The title text
3557 value String The string value to set into the active textbox element if displayed
3558 wait Boolean True to display a progress bar (defaults to false)
3559 width Number The width of the dialog in pixels
3566 msg: 'Please enter your address:',
3568 buttons: Roo.MessageBox.OKCANCEL,
3571 animEl: 'addAddressBtn'
3574 * @param {Object} config Configuration options
3575 * @return {Roo.MessageBox} This message box
3577 show : function(options)
3580 // this causes nightmares if you show one dialog after another
3581 // especially on callbacks..
3583 if(this.isVisible()){
3586 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3587 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3588 Roo.log("New Dialog Message:" + options.msg )
3589 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3590 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3593 var d = this.getDialog();
3595 d.setTitle(opt.title || " ");
3596 d.closeEl.setDisplayed(opt.closable !== false);
3597 activeTextEl = textboxEl;
3598 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3603 textareaEl.setHeight(typeof opt.multiline == "number" ?
3604 opt.multiline : this.defaultTextHeight);
3605 activeTextEl = textareaEl;
3614 progressEl.setDisplayed(opt.progress === true);
3616 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3618 this.updateProgress(0);
3619 activeTextEl.dom.value = opt.value || "";
3621 dlg.setDefaultButton(activeTextEl);
3623 var bs = opt.buttons;
3627 }else if(bs && bs.yes){
3628 db = buttons["yes"];
3630 dlg.setDefaultButton(db);
3632 bwidth = updateButtons(opt.buttons);
3633 this.updateText(opt.msg);
3635 d.el.addClass(opt.cls);
3637 d.proxyDrag = opt.proxyDrag === true;
3638 d.modal = opt.modal !== false;
3639 d.mask = opt.modal !== false ? mask : false;
3641 // force it to the end of the z-index stack so it gets a cursor in FF
3642 document.body.appendChild(dlg.el.dom);
3643 d.animateTarget = null;
3644 d.show(options.animEl);
3650 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3651 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3652 * and closing the message box when the process is complete.
3653 * @param {String} title The title bar text
3654 * @param {String} msg The message box body text
3655 * @return {Roo.MessageBox} This message box
3657 progress : function(title, msg){
3664 minWidth: this.minProgressWidth,
3671 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3672 * If a callback function is passed it will be called after the user clicks the button, and the
3673 * id of the button that was clicked will be passed as the only parameter to the callback
3674 * (could also be the top-right close button).
3675 * @param {String} title The title bar text
3676 * @param {String} msg The message box body text
3677 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3678 * @param {Object} scope (optional) The scope of the callback function
3679 * @return {Roo.MessageBox} This message box
3681 alert : function(title, msg, fn, scope)
3696 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3697 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3698 * You are responsible for closing the message box when the process is complete.
3699 * @param {String} msg The message box body text
3700 * @param {String} title (optional) The title bar text
3701 * @return {Roo.MessageBox} This message box
3703 wait : function(msg, title){
3714 waitTimer = Roo.TaskMgr.start({
3716 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3724 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3725 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3726 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3727 * @param {String} title The title bar text
3728 * @param {String} msg The message box body text
3729 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3730 * @param {Object} scope (optional) The scope of the callback function
3731 * @return {Roo.MessageBox} This message box
3733 confirm : function(title, msg, fn, scope){
3737 buttons: this.YESNO,
3746 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3747 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3748 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3749 * (could also be the top-right close button) and the text that was entered will be passed as the two
3750 * parameters to the callback.
3751 * @param {String} title The title bar text
3752 * @param {String} msg The message box body text
3753 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3754 * @param {Object} scope (optional) The scope of the callback function
3755 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3756 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3757 * @return {Roo.MessageBox} This message box
3759 prompt : function(title, msg, fn, scope, multiline){
3763 buttons: this.OKCANCEL,
3768 multiline: multiline,
3775 * Button config that displays a single OK button
3780 * Button config that displays Yes and No buttons
3783 YESNO : {yes:true, no:true},
3785 * Button config that displays OK and Cancel buttons
3788 OKCANCEL : {ok:true, cancel:true},
3790 * Button config that displays Yes, No and Cancel buttons
3793 YESNOCANCEL : {yes:true, no:true, cancel:true},
3796 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3799 defaultTextHeight : 75,
3801 * The maximum width in pixels of the message box (defaults to 600)
3806 * The minimum width in pixels of the message box (defaults to 100)
3811 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3812 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3815 minProgressWidth : 250,
3817 * An object containing the default button text strings that can be overriden for localized language support.
3818 * Supported properties are: ok, cancel, yes and no.
3819 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3832 * Shorthand for {@link Roo.MessageBox}
3834 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3835 Roo.Msg = Roo.Msg || Roo.MessageBox;
3844 * @class Roo.bootstrap.Navbar
3845 * @extends Roo.bootstrap.Component
3846 * Bootstrap Navbar class
3849 * Create a new Navbar
3850 * @param {Object} config The config object
3854 Roo.bootstrap.Navbar = function(config){
3855 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3859 * @event beforetoggle
3860 * Fire before toggle the menu
3861 * @param {Roo.EventObject} e
3863 "beforetoggle" : true
3867 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3876 getAutoCreate : function(){
3879 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3883 initEvents :function ()
3885 //Roo.log(this.el.select('.navbar-toggle',true));
3886 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
3893 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3895 var size = this.el.getSize();
3896 this.maskEl.setSize(size.width, size.height);
3897 this.maskEl.enableDisplayMode("block");
3906 getChildContainer : function()
3908 if (this.el && this.el.select('.collapse').getCount()) {
3909 return this.el.select('.collapse',true).first();
3924 onToggle : function()
3927 if(this.fireEvent('beforetoggle', this) === false){
3930 var ce = this.el.select('.navbar-collapse',true).first();
3932 if (!ce.hasClass('show')) {
3942 * Expand the navbar pulldown
3944 expand : function ()
3947 var ce = this.el.select('.navbar-collapse',true).first();
3948 if (ce.hasClass('collapsing')) {
3951 ce.dom.style.height = '';
3953 ce.addClass('in'); // old...
3954 ce.removeClass('collapse');
3955 ce.addClass('show');
3956 var h = ce.getHeight();
3958 ce.removeClass('show');
3959 // at this point we should be able to see it..
3960 ce.addClass('collapsing');
3962 ce.setHeight(0); // resize it ...
3963 ce.on('transitionend', function() {
3964 //Roo.log('done transition');
3965 ce.removeClass('collapsing');
3966 ce.addClass('show');
3967 ce.removeClass('collapse');
3969 ce.dom.style.height = '';
3970 }, this, { single: true} );
3972 ce.dom.scrollTop = 0;
3975 * Collapse the navbar pulldown
3977 collapse : function()
3979 var ce = this.el.select('.navbar-collapse',true).first();
3981 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
3982 // it's collapsed or collapsing..
3985 ce.removeClass('in'); // old...
3986 ce.setHeight(ce.getHeight());
3987 ce.removeClass('show');
3988 ce.addClass('collapsing');
3990 ce.on('transitionend', function() {
3991 ce.dom.style.height = '';
3992 ce.removeClass('collapsing');
3993 ce.addClass('collapse');
3994 }, this, { single: true} );
4014 * @class Roo.bootstrap.NavSimplebar
4015 * @extends Roo.bootstrap.Navbar
4016 * Bootstrap Sidebar class
4018 * @cfg {Boolean} inverse is inverted color
4020 * @cfg {String} type (nav | pills | tabs)
4021 * @cfg {Boolean} arrangement stacked | justified
4022 * @cfg {String} align (left | right) alignment
4024 * @cfg {Boolean} main (true|false) main nav bar? default false
4025 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4027 * @cfg {String} tag (header|footer|nav|div) default is nav
4029 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4033 * Create a new Sidebar
4034 * @param {Object} config The config object
4038 Roo.bootstrap.NavSimplebar = function(config){
4039 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4042 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
4058 getAutoCreate : function(){
4062 tag : this.tag || 'div',
4063 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
4065 if (['light','white'].indexOf(this.weight) > -1) {
4066 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4068 cfg.cls += ' bg-' + this.weight;
4071 cfg.cls += ' navbar-inverse';
4075 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4077 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
4086 cls: 'nav nav-' + this.xtype,
4092 this.type = this.type || 'nav';
4093 if (['tabs','pills'].indexOf(this.type) != -1) {
4094 cfg.cn[0].cls += ' nav-' + this.type
4098 if (this.type!=='nav') {
4099 Roo.log('nav type must be nav/tabs/pills')
4101 cfg.cn[0].cls += ' navbar-nav'
4107 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4108 cfg.cn[0].cls += ' nav-' + this.arrangement;
4112 if (this.align === 'right') {
4113 cfg.cn[0].cls += ' navbar-right';
4138 * navbar-expand-md fixed-top
4142 * @class Roo.bootstrap.NavHeaderbar
4143 * @extends Roo.bootstrap.NavSimplebar
4144 * Bootstrap Sidebar class
4146 * @cfg {String} brand what is brand
4147 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4148 * @cfg {String} brand_href href of the brand
4149 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
4150 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4151 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4152 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4155 * Create a new Sidebar
4156 * @param {Object} config The config object
4160 Roo.bootstrap.NavHeaderbar = function(config){
4161 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4165 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
4172 desktopCenter : false,
4175 getAutoCreate : function(){
4178 tag: this.nav || 'nav',
4179 cls: 'navbar navbar-expand-md',
4185 if (this.desktopCenter) {
4186 cn.push({cls : 'container', cn : []});
4194 cls: 'navbar-toggle navbar-toggler',
4195 'data-toggle': 'collapse',
4200 html: 'Toggle navigation'
4204 cls: 'icon-bar navbar-toggler-icon'
4217 cn.push( Roo.bootstrap.version == 4 ? btn : {
4219 cls: 'navbar-header',
4228 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
4232 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4234 if (['light','white'].indexOf(this.weight) > -1) {
4235 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4237 cfg.cls += ' bg-' + this.weight;
4240 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4241 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4243 // tag can override this..
4245 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4248 if (this.brand !== '') {
4249 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4250 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4252 href: this.brand_href ? this.brand_href : '#',
4253 cls: 'navbar-brand',
4261 cfg.cls += ' main-nav';
4269 getHeaderChildContainer : function()
4271 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4272 return this.el.select('.navbar-header',true).first();
4275 return this.getChildContainer();
4279 initEvents : function()
4281 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4283 if (this.autohide) {
4288 Roo.get(document).on('scroll',function(e) {
4289 var ns = Roo.get(document).getScroll().top;
4290 var os = prevScroll;
4294 ft.removeClass('slideDown');
4295 ft.addClass('slideUp');
4298 ft.removeClass('slideUp');
4299 ft.addClass('slideDown');
4320 * @class Roo.bootstrap.NavSidebar
4321 * @extends Roo.bootstrap.Navbar
4322 * Bootstrap Sidebar class
4325 * Create a new Sidebar
4326 * @param {Object} config The config object
4330 Roo.bootstrap.NavSidebar = function(config){
4331 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4334 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4336 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4338 getAutoCreate : function(){
4343 cls: 'sidebar sidebar-nav'
4365 * @class Roo.bootstrap.NavGroup
4366 * @extends Roo.bootstrap.Component
4367 * Bootstrap NavGroup class
4368 * @cfg {String} align (left|right)
4369 * @cfg {Boolean} inverse
4370 * @cfg {String} type (nav|pills|tab) default nav
4371 * @cfg {String} navId - reference Id for navbar.
4375 * Create a new nav group
4376 * @param {Object} config The config object
4379 Roo.bootstrap.NavGroup = function(config){
4380 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4383 Roo.bootstrap.NavGroup.register(this);
4387 * Fires when the active item changes
4388 * @param {Roo.bootstrap.NavGroup} this
4389 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4390 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4397 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4408 getAutoCreate : function()
4410 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4416 if (Roo.bootstrap.version == 4) {
4417 if (['tabs','pills'].indexOf(this.type) != -1) {
4418 cfg.cls += ' nav-' + this.type;
4420 // trying to remove so header bar can right align top?
4421 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
4422 // do not use on header bar...
4423 cfg.cls += ' navbar-nav';
4428 if (['tabs','pills'].indexOf(this.type) != -1) {
4429 cfg.cls += ' nav-' + this.type
4431 if (this.type !== 'nav') {
4432 Roo.log('nav type must be nav/tabs/pills')
4434 cfg.cls += ' navbar-nav'
4438 if (this.parent() && this.parent().sidebar) {
4441 cls: 'dashboard-menu sidebar-menu'
4447 if (this.form === true) {
4450 cls: 'navbar-form form-inline'
4452 //nav navbar-right ml-md-auto
4453 if (this.align === 'right') {
4454 cfg.cls += ' navbar-right ml-md-auto';
4456 cfg.cls += ' navbar-left';
4460 if (this.align === 'right') {
4461 cfg.cls += ' navbar-right ml-md-auto';
4463 cfg.cls += ' mr-auto';
4467 cfg.cls += ' navbar-inverse';
4475 * sets the active Navigation item
4476 * @param {Roo.bootstrap.NavItem} the new current navitem
4478 setActiveItem : function(item)
4481 Roo.each(this.navItems, function(v){
4486 v.setActive(false, true);
4493 item.setActive(true, true);
4494 this.fireEvent('changed', this, item, prev);
4499 * gets the active Navigation item
4500 * @return {Roo.bootstrap.NavItem} the current navitem
4502 getActive : function()
4506 Roo.each(this.navItems, function(v){
4517 indexOfNav : function()
4521 Roo.each(this.navItems, function(v,i){
4532 * adds a Navigation item
4533 * @param {Roo.bootstrap.NavItem} the navitem to add
4535 addItem : function(cfg)
4537 if (this.form && Roo.bootstrap.version == 4) {
4540 var cn = new Roo.bootstrap.NavItem(cfg);
4542 cn.parentId = this.id;
4543 cn.onRender(this.el, null);
4547 * register a Navigation item
4548 * @param {Roo.bootstrap.NavItem} the navitem to add
4550 register : function(item)
4552 this.navItems.push( item);
4553 item.navId = this.navId;
4558 * clear all the Navigation item
4561 clearAll : function()
4564 this.el.dom.innerHTML = '';
4567 getNavItem: function(tabId)
4570 Roo.each(this.navItems, function(e) {
4571 if (e.tabId == tabId) {
4581 setActiveNext : function()
4583 var i = this.indexOfNav(this.getActive());
4584 if (i > this.navItems.length) {
4587 this.setActiveItem(this.navItems[i+1]);
4589 setActivePrev : function()
4591 var i = this.indexOfNav(this.getActive());
4595 this.setActiveItem(this.navItems[i-1]);
4597 clearWasActive : function(except) {
4598 Roo.each(this.navItems, function(e) {
4599 if (e.tabId != except.tabId && e.was_active) {
4600 e.was_active = false;
4607 getWasActive : function ()
4610 Roo.each(this.navItems, function(e) {
4625 Roo.apply(Roo.bootstrap.NavGroup, {
4629 * register a Navigation Group
4630 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4632 register : function(navgrp)
4634 this.groups[navgrp.navId] = navgrp;
4638 * fetch a Navigation Group based on the navigation ID
4639 * @param {string} the navgroup to add
4640 * @returns {Roo.bootstrap.NavGroup} the navgroup
4642 get: function(navId) {
4643 if (typeof(this.groups[navId]) == 'undefined') {
4645 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4647 return this.groups[navId] ;
4662 * @class Roo.bootstrap.NavItem
4663 * @extends Roo.bootstrap.Component
4664 * Bootstrap Navbar.NavItem class
4665 * @cfg {String} href link to
4666 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4668 * @cfg {String} html content of button
4669 * @cfg {String} badge text inside badge
4670 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4671 * @cfg {String} glyphicon DEPRICATED - use fa
4672 * @cfg {String} icon DEPRICATED - use fa
4673 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4674 * @cfg {Boolean} active Is item active
4675 * @cfg {Boolean} disabled Is item disabled
4677 * @cfg {Boolean} preventDefault (true | false) default false
4678 * @cfg {String} tabId the tab that this item activates.
4679 * @cfg {String} tagtype (a|span) render as a href or span?
4680 * @cfg {Boolean} animateRef (true|false) link to element default false
4683 * Create a new Navbar Item
4684 * @param {Object} config The config object
4686 Roo.bootstrap.NavItem = function(config){
4687 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4692 * The raw click event for the entire grid.
4693 * @param {Roo.EventObject} e
4698 * Fires when the active item active state changes
4699 * @param {Roo.bootstrap.NavItem} this
4700 * @param {boolean} state the new state
4706 * Fires when scroll to element
4707 * @param {Roo.bootstrap.NavItem} this
4708 * @param {Object} options
4709 * @param {Roo.EventObject} e
4717 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4726 preventDefault : false,
4734 button_outline : false,
4738 getAutoCreate : function(){
4746 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4748 if (this.disabled) {
4749 cfg.cls += ' disabled';
4753 if (this.button_weight.length) {
4754 cfg.tag = this.href ? 'a' : 'button';
4755 cfg.html = this.html || '';
4756 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
4758 cfg.href = this.href;
4761 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
4764 // menu .. should add dropdown-menu class - so no need for carat..
4766 if (this.badge !== '') {
4768 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4773 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4777 href : this.href || "#",
4778 html: this.html || ''
4781 if (this.tagtype == 'a') {
4782 cfg.cn[0].cls = 'nav-link';
4785 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
4788 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
4790 if(this.glyphicon) {
4791 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4796 cfg.cn[0].html += " <span class='caret'></span>";
4800 if (this.badge !== '') {
4802 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4810 onRender : function(ct, position)
4812 // Roo.log("Call onRender: " + this.xtype);
4813 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4817 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4818 this.navLink = this.el.select('.nav-link',true).first();
4823 initEvents: function()
4825 if (typeof (this.menu) != 'undefined') {
4826 this.menu.parentType = this.xtype;
4827 this.menu.triggerEl = this.el;
4828 this.menu = this.addxtype(Roo.apply({}, this.menu));
4831 this.el.select('a',true).on('click', this.onClick, this);
4833 if(this.tagtype == 'span'){
4834 this.el.select('span',true).on('click', this.onClick, this);
4837 // at this point parent should be available..
4838 this.parent().register(this);
4841 onClick : function(e)
4843 if (e.getTarget('.dropdown-menu-item')) {
4844 // did you click on a menu itemm.... - then don't trigger onclick..
4849 this.preventDefault ||
4852 Roo.log("NavItem - prevent Default?");
4856 if (this.disabled) {
4860 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4861 if (tg && tg.transition) {
4862 Roo.log("waiting for the transitionend");
4868 //Roo.log("fire event clicked");
4869 if(this.fireEvent('click', this, e) === false){
4873 if(this.tagtype == 'span'){
4877 //Roo.log(this.href);
4878 var ael = this.el.select('a',true).first();
4881 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4882 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4883 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4884 return; // ignore... - it's a 'hash' to another page.
4886 Roo.log("NavItem - prevent Default?");
4888 this.scrollToElement(e);
4892 var p = this.parent();
4894 if (['tabs','pills'].indexOf(p.type)!==-1) {
4895 if (typeof(p.setActiveItem) !== 'undefined') {
4896 p.setActiveItem(this);
4900 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4901 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4902 // remove the collapsed menu expand...
4903 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
4907 isActive: function () {
4910 setActive : function(state, fire, is_was_active)
4912 if (this.active && !state && this.navId) {
4913 this.was_active = true;
4914 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4916 nv.clearWasActive(this);
4920 this.active = state;
4923 this.el.removeClass('active');
4924 this.navLink ? this.navLink.removeClass('active') : false;
4925 } else if (!this.el.hasClass('active')) {
4927 this.el.addClass('active');
4928 if (Roo.bootstrap.version == 4 && this.navLink ) {
4929 this.navLink.addClass('active');
4934 this.fireEvent('changed', this, state);
4937 // show a panel if it's registered and related..
4939 if (!this.navId || !this.tabId || !state || is_was_active) {
4943 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4947 var pan = tg.getPanelByName(this.tabId);
4951 // if we can not flip to new panel - go back to old nav highlight..
4952 if (false == tg.showPanel(pan)) {
4953 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4955 var onav = nv.getWasActive();
4957 onav.setActive(true, false, true);
4966 // this should not be here...
4967 setDisabled : function(state)
4969 this.disabled = state;
4971 this.el.removeClass('disabled');
4972 } else if (!this.el.hasClass('disabled')) {
4973 this.el.addClass('disabled');
4979 * Fetch the element to display the tooltip on.
4980 * @return {Roo.Element} defaults to this.el
4982 tooltipEl : function()
4984 return this.el.select('' + this.tagtype + '', true).first();
4987 scrollToElement : function(e)
4989 var c = document.body;
4992 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4994 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4995 c = document.documentElement;
4998 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
5004 var o = target.calcOffsetsTo(c);
5011 this.fireEvent('scrollto', this, options, e);
5013 Roo.get(c).scrollTo('top', options.value, true);
5026 * <span> icon </span>
5027 * <span> text </span>
5028 * <span>badge </span>
5032 * @class Roo.bootstrap.NavSidebarItem
5033 * @extends Roo.bootstrap.NavItem
5034 * Bootstrap Navbar.NavSidebarItem class
5035 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5036 * {Boolean} open is the menu open
5037 * {Boolean} buttonView use button as the tigger el rather that a (default false)
5038 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5039 * {String} buttonSize (sm|md|lg)the extra classes for the button
5040 * {Boolean} showArrow show arrow next to the text (default true)
5042 * Create a new Navbar Button
5043 * @param {Object} config The config object
5045 Roo.bootstrap.NavSidebarItem = function(config){
5046 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5051 * The raw click event for the entire grid.
5052 * @param {Roo.EventObject} e
5057 * Fires when the active item active state changes
5058 * @param {Roo.bootstrap.NavSidebarItem} this
5059 * @param {boolean} state the new state
5067 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
5069 badgeWeight : 'default',
5075 buttonWeight : 'default',
5081 getAutoCreate : function(){
5086 href : this.href || '#',
5092 if(this.buttonView){
5095 href : this.href || '#',
5096 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5109 cfg.cls += ' active';
5112 if (this.disabled) {
5113 cfg.cls += ' disabled';
5116 cfg.cls += ' open x-open';
5119 if (this.glyphicon || this.icon) {
5120 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
5121 a.cn.push({ tag : 'i', cls : c }) ;
5124 if(!this.buttonView){
5127 html : this.html || ''
5134 if (this.badge !== '') {
5135 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
5141 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5144 a.cls += ' dropdown-toggle treeview' ;
5150 initEvents : function()
5152 if (typeof (this.menu) != 'undefined') {
5153 this.menu.parentType = this.xtype;
5154 this.menu.triggerEl = this.el;
5155 this.menu = this.addxtype(Roo.apply({}, this.menu));
5158 this.el.on('click', this.onClick, this);
5160 if(this.badge !== ''){
5161 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5166 onClick : function(e)
5173 if(this.preventDefault){
5177 this.fireEvent('click', this, e);
5180 disable : function()
5182 this.setDisabled(true);
5187 this.setDisabled(false);
5190 setDisabled : function(state)
5192 if(this.disabled == state){
5196 this.disabled = state;
5199 this.el.addClass('disabled');
5203 this.el.removeClass('disabled');
5208 setActive : function(state)
5210 if(this.active == state){
5214 this.active = state;
5217 this.el.addClass('active');
5221 this.el.removeClass('active');
5226 isActive: function ()
5231 setBadge : function(str)
5237 this.badgeEl.dom.innerHTML = str;
5254 * @class Roo.bootstrap.Row
5255 * @extends Roo.bootstrap.Component
5256 * Bootstrap Row class (contains columns...)
5260 * @param {Object} config The config object
5263 Roo.bootstrap.Row = function(config){
5264 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5267 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5269 getAutoCreate : function(){
5288 * @class Roo.bootstrap.Element
5289 * @extends Roo.bootstrap.Component
5290 * Bootstrap Element class
5291 * @cfg {String} html contents of the element
5292 * @cfg {String} tag tag of the element
5293 * @cfg {String} cls class of the element
5294 * @cfg {Boolean} preventDefault (true|false) default false
5295 * @cfg {Boolean} clickable (true|false) default false
5298 * Create a new Element
5299 * @param {Object} config The config object
5302 Roo.bootstrap.Element = function(config){
5303 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5309 * When a element is chick
5310 * @param {Roo.bootstrap.Element} this
5311 * @param {Roo.EventObject} e
5317 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5322 preventDefault: false,
5325 getAutoCreate : function(){
5329 // cls: this.cls, double assign in parent class Component.js :: onRender
5336 initEvents: function()
5338 Roo.bootstrap.Element.superclass.initEvents.call(this);
5341 this.el.on('click', this.onClick, this);
5346 onClick : function(e)
5348 if(this.preventDefault){
5352 this.fireEvent('click', this, e);
5355 getValue : function()
5357 return this.el.dom.innerHTML;
5360 setValue : function(value)
5362 this.el.dom.innerHTML = value;
5377 * @class Roo.bootstrap.Pagination
5378 * @extends Roo.bootstrap.Component
5379 * Bootstrap Pagination class
5380 * @cfg {String} size xs | sm | md | lg
5381 * @cfg {Boolean} inverse false | true
5384 * Create a new Pagination
5385 * @param {Object} config The config object
5388 Roo.bootstrap.Pagination = function(config){
5389 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5392 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5398 getAutoCreate : function(){
5404 cfg.cls += ' inverse';
5410 cfg.cls += " " + this.cls;
5428 * @class Roo.bootstrap.PaginationItem
5429 * @extends Roo.bootstrap.Component
5430 * Bootstrap PaginationItem class
5431 * @cfg {String} html text
5432 * @cfg {String} href the link
5433 * @cfg {Boolean} preventDefault (true | false) default true
5434 * @cfg {Boolean} active (true | false) default false
5435 * @cfg {Boolean} disabled default false
5439 * Create a new PaginationItem
5440 * @param {Object} config The config object
5444 Roo.bootstrap.PaginationItem = function(config){
5445 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5450 * The raw click event for the entire grid.
5451 * @param {Roo.EventObject} e
5457 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5461 preventDefault: true,
5466 getAutoCreate : function(){
5472 href : this.href ? this.href : '#',
5473 html : this.html ? this.html : ''
5483 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5487 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5493 initEvents: function() {
5495 this.el.on('click', this.onClick, this);
5498 onClick : function(e)
5500 Roo.log('PaginationItem on click ');
5501 if(this.preventDefault){
5509 this.fireEvent('click', this, e);
5525 * @class Roo.bootstrap.Slider
5526 * @extends Roo.bootstrap.Component
5527 * Bootstrap Slider class
5530 * Create a new Slider
5531 * @param {Object} config The config object
5534 Roo.bootstrap.Slider = function(config){
5535 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5538 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5540 getAutoCreate : function(){
5544 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5548 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5560 * Ext JS Library 1.1.1
5561 * Copyright(c) 2006-2007, Ext JS, LLC.
5563 * Originally Released Under LGPL - original licence link has changed is not relivant.
5566 * <script type="text/javascript">
5571 * @class Roo.grid.ColumnModel
5572 * @extends Roo.util.Observable
5573 * This is the default implementation of a ColumnModel used by the Grid. It defines
5574 * the columns in the grid.
5577 var colModel = new Roo.grid.ColumnModel([
5578 {header: "Ticker", width: 60, sortable: true, locked: true},
5579 {header: "Company Name", width: 150, sortable: true},
5580 {header: "Market Cap.", width: 100, sortable: true},
5581 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5582 {header: "Employees", width: 100, sortable: true, resizable: false}
5587 * The config options listed for this class are options which may appear in each
5588 * individual column definition.
5589 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5591 * @param {Object} config An Array of column config objects. See this class's
5592 * config objects for details.
5594 Roo.grid.ColumnModel = function(config){
5596 * The config passed into the constructor
5598 this.config = config;
5601 // if no id, create one
5602 // if the column does not have a dataIndex mapping,
5603 // map it to the order it is in the config
5604 for(var i = 0, len = config.length; i < len; i++){
5606 if(typeof c.dataIndex == "undefined"){
5609 if(typeof c.renderer == "string"){
5610 c.renderer = Roo.util.Format[c.renderer];
5612 if(typeof c.id == "undefined"){
5615 if(c.editor && c.editor.xtype){
5616 c.editor = Roo.factory(c.editor, Roo.grid);
5618 if(c.editor && c.editor.isFormField){
5619 c.editor = new Roo.grid.GridEditor(c.editor);
5621 this.lookup[c.id] = c;
5625 * The width of columns which have no width specified (defaults to 100)
5628 this.defaultWidth = 100;
5631 * Default sortable of columns which have no sortable specified (defaults to false)
5634 this.defaultSortable = false;
5638 * @event widthchange
5639 * Fires when the width of a column changes.
5640 * @param {ColumnModel} this
5641 * @param {Number} columnIndex The column index
5642 * @param {Number} newWidth The new width
5644 "widthchange": true,
5646 * @event headerchange
5647 * Fires when the text of a header changes.
5648 * @param {ColumnModel} this
5649 * @param {Number} columnIndex The column index
5650 * @param {Number} newText The new header text
5652 "headerchange": true,
5654 * @event hiddenchange
5655 * Fires when a column is hidden or "unhidden".
5656 * @param {ColumnModel} this
5657 * @param {Number} columnIndex The column index
5658 * @param {Boolean} hidden true if hidden, false otherwise
5660 "hiddenchange": true,
5662 * @event columnmoved
5663 * Fires when a column is moved.
5664 * @param {ColumnModel} this
5665 * @param {Number} oldIndex
5666 * @param {Number} newIndex
5668 "columnmoved" : true,
5670 * @event columlockchange
5671 * Fires when a column's locked state is changed
5672 * @param {ColumnModel} this
5673 * @param {Number} colIndex
5674 * @param {Boolean} locked true if locked
5676 "columnlockchange" : true
5678 Roo.grid.ColumnModel.superclass.constructor.call(this);
5680 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5682 * @cfg {String} header The header text to display in the Grid view.
5685 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5686 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5687 * specified, the column's index is used as an index into the Record's data Array.
5690 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5691 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5694 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5695 * Defaults to the value of the {@link #defaultSortable} property.
5696 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5699 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5702 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5705 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5708 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5711 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5712 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5713 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5714 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5717 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5720 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5723 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5726 * @cfg {String} cursor (Optional)
5729 * @cfg {String} tooltip (Optional)
5732 * @cfg {Number} xs (Optional)
5735 * @cfg {Number} sm (Optional)
5738 * @cfg {Number} md (Optional)
5741 * @cfg {Number} lg (Optional)
5744 * Returns the id of the column at the specified index.
5745 * @param {Number} index The column index
5746 * @return {String} the id
5748 getColumnId : function(index){
5749 return this.config[index].id;
5753 * Returns the column for a specified id.
5754 * @param {String} id The column id
5755 * @return {Object} the column
5757 getColumnById : function(id){
5758 return this.lookup[id];
5763 * Returns the column for a specified dataIndex.
5764 * @param {String} dataIndex The column dataIndex
5765 * @return {Object|Boolean} the column or false if not found
5767 getColumnByDataIndex: function(dataIndex){
5768 var index = this.findColumnIndex(dataIndex);
5769 return index > -1 ? this.config[index] : false;
5773 * Returns the index for a specified column id.
5774 * @param {String} id The column id
5775 * @return {Number} the index, or -1 if not found
5777 getIndexById : function(id){
5778 for(var i = 0, len = this.config.length; i < len; i++){
5779 if(this.config[i].id == id){
5787 * Returns the index for a specified column dataIndex.
5788 * @param {String} dataIndex The column dataIndex
5789 * @return {Number} the index, or -1 if not found
5792 findColumnIndex : function(dataIndex){
5793 for(var i = 0, len = this.config.length; i < len; i++){
5794 if(this.config[i].dataIndex == dataIndex){
5802 moveColumn : function(oldIndex, newIndex){
5803 var c = this.config[oldIndex];
5804 this.config.splice(oldIndex, 1);
5805 this.config.splice(newIndex, 0, c);
5806 this.dataMap = null;
5807 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5810 isLocked : function(colIndex){
5811 return this.config[colIndex].locked === true;
5814 setLocked : function(colIndex, value, suppressEvent){
5815 if(this.isLocked(colIndex) == value){
5818 this.config[colIndex].locked = value;
5820 this.fireEvent("columnlockchange", this, colIndex, value);
5824 getTotalLockedWidth : function(){
5826 for(var i = 0; i < this.config.length; i++){
5827 if(this.isLocked(i) && !this.isHidden(i)){
5828 this.totalWidth += this.getColumnWidth(i);
5834 getLockedCount : function(){
5835 for(var i = 0, len = this.config.length; i < len; i++){
5836 if(!this.isLocked(i)){
5841 return this.config.length;
5845 * Returns the number of columns.
5848 getColumnCount : function(visibleOnly){
5849 if(visibleOnly === true){
5851 for(var i = 0, len = this.config.length; i < len; i++){
5852 if(!this.isHidden(i)){
5858 return this.config.length;
5862 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5863 * @param {Function} fn
5864 * @param {Object} scope (optional)
5865 * @return {Array} result
5867 getColumnsBy : function(fn, scope){
5869 for(var i = 0, len = this.config.length; i < len; i++){
5870 var c = this.config[i];
5871 if(fn.call(scope||this, c, i) === true){
5879 * Returns true if the specified column is sortable.
5880 * @param {Number} col The column index
5883 isSortable : function(col){
5884 if(typeof this.config[col].sortable == "undefined"){
5885 return this.defaultSortable;
5887 return this.config[col].sortable;
5891 * Returns the rendering (formatting) function defined for the column.
5892 * @param {Number} col The column index.
5893 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5895 getRenderer : function(col){
5896 if(!this.config[col].renderer){
5897 return Roo.grid.ColumnModel.defaultRenderer;
5899 return this.config[col].renderer;
5903 * Sets the rendering (formatting) function for a column.
5904 * @param {Number} col The column index
5905 * @param {Function} fn The function to use to process the cell's raw data
5906 * to return HTML markup for the grid view. The render function is called with
5907 * the following parameters:<ul>
5908 * <li>Data value.</li>
5909 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5910 * <li>css A CSS style string to apply to the table cell.</li>
5911 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5912 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5913 * <li>Row index</li>
5914 * <li>Column index</li>
5915 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5917 setRenderer : function(col, fn){
5918 this.config[col].renderer = fn;
5922 * Returns the width for the specified column.
5923 * @param {Number} col The column index
5926 getColumnWidth : function(col){
5927 return this.config[col].width * 1 || this.defaultWidth;
5931 * Sets the width for a column.
5932 * @param {Number} col The column index
5933 * @param {Number} width The new width
5935 setColumnWidth : function(col, width, suppressEvent){
5936 this.config[col].width = width;
5937 this.totalWidth = null;
5939 this.fireEvent("widthchange", this, col, width);
5944 * Returns the total width of all columns.
5945 * @param {Boolean} includeHidden True to include hidden column widths
5948 getTotalWidth : function(includeHidden){
5949 if(!this.totalWidth){
5950 this.totalWidth = 0;
5951 for(var i = 0, len = this.config.length; i < len; i++){
5952 if(includeHidden || !this.isHidden(i)){
5953 this.totalWidth += this.getColumnWidth(i);
5957 return this.totalWidth;
5961 * Returns the header for the specified column.
5962 * @param {Number} col The column index
5965 getColumnHeader : function(col){
5966 return this.config[col].header;
5970 * Sets the header for a column.
5971 * @param {Number} col The column index
5972 * @param {String} header The new header
5974 setColumnHeader : function(col, header){
5975 this.config[col].header = header;
5976 this.fireEvent("headerchange", this, col, header);
5980 * Returns the tooltip for the specified column.
5981 * @param {Number} col The column index
5984 getColumnTooltip : function(col){
5985 return this.config[col].tooltip;
5988 * Sets the tooltip for a column.
5989 * @param {Number} col The column index
5990 * @param {String} tooltip The new tooltip
5992 setColumnTooltip : function(col, tooltip){
5993 this.config[col].tooltip = tooltip;
5997 * Returns the dataIndex for the specified column.
5998 * @param {Number} col The column index
6001 getDataIndex : function(col){
6002 return this.config[col].dataIndex;
6006 * Sets the dataIndex for a column.
6007 * @param {Number} col The column index
6008 * @param {Number} dataIndex The new dataIndex
6010 setDataIndex : function(col, dataIndex){
6011 this.config[col].dataIndex = dataIndex;
6017 * Returns true if the cell is editable.
6018 * @param {Number} colIndex The column index
6019 * @param {Number} rowIndex The row index - this is nto actually used..?
6022 isCellEditable : function(colIndex, rowIndex){
6023 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6027 * Returns the editor defined for the cell/column.
6028 * return false or null to disable editing.
6029 * @param {Number} colIndex The column index
6030 * @param {Number} rowIndex The row index
6033 getCellEditor : function(colIndex, rowIndex){
6034 return this.config[colIndex].editor;
6038 * Sets if a column is editable.
6039 * @param {Number} col The column index
6040 * @param {Boolean} editable True if the column is editable
6042 setEditable : function(col, editable){
6043 this.config[col].editable = editable;
6048 * Returns true if the column is hidden.
6049 * @param {Number} colIndex The column index
6052 isHidden : function(colIndex){
6053 return this.config[colIndex].hidden;
6058 * Returns true if the column width cannot be changed
6060 isFixed : function(colIndex){
6061 return this.config[colIndex].fixed;
6065 * Returns true if the column can be resized
6068 isResizable : function(colIndex){
6069 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6072 * Sets if a column is hidden.
6073 * @param {Number} colIndex The column index
6074 * @param {Boolean} hidden True if the column is hidden
6076 setHidden : function(colIndex, hidden){
6077 this.config[colIndex].hidden = hidden;
6078 this.totalWidth = null;
6079 this.fireEvent("hiddenchange", this, colIndex, hidden);
6083 * Sets the editor for a column.
6084 * @param {Number} col The column index
6085 * @param {Object} editor The editor object
6087 setEditor : function(col, editor){
6088 this.config[col].editor = editor;
6092 Roo.grid.ColumnModel.defaultRenderer = function(value)
6094 if(typeof value == "object") {
6097 if(typeof value == "string" && value.length < 1){
6101 return String.format("{0}", value);
6104 // Alias for backwards compatibility
6105 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6108 * Ext JS Library 1.1.1
6109 * Copyright(c) 2006-2007, Ext JS, LLC.
6111 * Originally Released Under LGPL - original licence link has changed is not relivant.
6114 * <script type="text/javascript">
6118 * @class Roo.LoadMask
6119 * A simple utility class for generically masking elements while loading data. If the element being masked has
6120 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6121 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
6122 * element's UpdateManager load indicator and will be destroyed after the initial load.
6124 * Create a new LoadMask
6125 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6126 * @param {Object} config The config object
6128 Roo.LoadMask = function(el, config){
6129 this.el = Roo.get(el);
6130 Roo.apply(this, config);
6132 this.store.on('beforeload', this.onBeforeLoad, this);
6133 this.store.on('load', this.onLoad, this);
6134 this.store.on('loadexception', this.onLoadException, this);
6135 this.removeMask = false;
6137 var um = this.el.getUpdateManager();
6138 um.showLoadIndicator = false; // disable the default indicator
6139 um.on('beforeupdate', this.onBeforeLoad, this);
6140 um.on('update', this.onLoad, this);
6141 um.on('failure', this.onLoad, this);
6142 this.removeMask = true;
6146 Roo.LoadMask.prototype = {
6148 * @cfg {Boolean} removeMask
6149 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6150 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
6154 * The text to display in a centered loading message box (defaults to 'Loading...')
6158 * @cfg {String} msgCls
6159 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6161 msgCls : 'x-mask-loading',
6164 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6170 * Disables the mask to prevent it from being displayed
6172 disable : function(){
6173 this.disabled = true;
6177 * Enables the mask so that it can be displayed
6179 enable : function(){
6180 this.disabled = false;
6183 onLoadException : function()
6187 if (typeof(arguments[3]) != 'undefined') {
6188 Roo.MessageBox.alert("Error loading",arguments[3]);
6192 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6193 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6200 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6205 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6209 onBeforeLoad : function(){
6211 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6216 destroy : function(){
6218 this.store.un('beforeload', this.onBeforeLoad, this);
6219 this.store.un('load', this.onLoad, this);
6220 this.store.un('loadexception', this.onLoadException, this);
6222 var um = this.el.getUpdateManager();
6223 um.un('beforeupdate', this.onBeforeLoad, this);
6224 um.un('update', this.onLoad, this);
6225 um.un('failure', this.onLoad, this);
6236 * @class Roo.bootstrap.Table
6237 * @extends Roo.bootstrap.Component
6238 * Bootstrap Table class
6239 * @cfg {String} cls table class
6240 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6241 * @cfg {String} bgcolor Specifies the background color for a table
6242 * @cfg {Number} border Specifies whether the table cells should have borders or not
6243 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6244 * @cfg {Number} cellspacing Specifies the space between cells
6245 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6246 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6247 * @cfg {String} sortable Specifies that the table should be sortable
6248 * @cfg {String} summary Specifies a summary of the content of a table
6249 * @cfg {Number} width Specifies the width of a table
6250 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6252 * @cfg {boolean} striped Should the rows be alternative striped
6253 * @cfg {boolean} bordered Add borders to the table
6254 * @cfg {boolean} hover Add hover highlighting
6255 * @cfg {boolean} condensed Format condensed
6256 * @cfg {boolean} responsive Format condensed
6257 * @cfg {Boolean} loadMask (true|false) default false
6258 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6259 * @cfg {Boolean} headerShow (true|false) generate thead, default true
6260 * @cfg {Boolean} rowSelection (true|false) default false
6261 * @cfg {Boolean} cellSelection (true|false) default false
6262 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6263 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
6264 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
6265 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6269 * Create a new Table
6270 * @param {Object} config The config object
6273 Roo.bootstrap.Table = function(config){
6274 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6279 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6280 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6281 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6282 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6284 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6286 this.sm.grid = this;
6287 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6288 this.sm = this.selModel;
6289 this.sm.xmodule = this.xmodule || false;
6292 if (this.cm && typeof(this.cm.config) == 'undefined') {
6293 this.colModel = new Roo.grid.ColumnModel(this.cm);
6294 this.cm = this.colModel;
6295 this.cm.xmodule = this.xmodule || false;
6298 this.store= Roo.factory(this.store, Roo.data);
6299 this.ds = this.store;
6300 this.ds.xmodule = this.xmodule || false;
6303 if (this.footer && this.store) {
6304 this.footer.dataSource = this.ds;
6305 this.footer = Roo.factory(this.footer);
6312 * Fires when a cell is clicked
6313 * @param {Roo.bootstrap.Table} this
6314 * @param {Roo.Element} el
6315 * @param {Number} rowIndex
6316 * @param {Number} columnIndex
6317 * @param {Roo.EventObject} e
6321 * @event celldblclick
6322 * Fires when a cell is double clicked
6323 * @param {Roo.bootstrap.Table} this
6324 * @param {Roo.Element} el
6325 * @param {Number} rowIndex
6326 * @param {Number} columnIndex
6327 * @param {Roo.EventObject} e
6329 "celldblclick" : true,
6332 * Fires when a row is clicked
6333 * @param {Roo.bootstrap.Table} this
6334 * @param {Roo.Element} el
6335 * @param {Number} rowIndex
6336 * @param {Roo.EventObject} e
6340 * @event rowdblclick
6341 * Fires when a row is double clicked
6342 * @param {Roo.bootstrap.Table} this
6343 * @param {Roo.Element} el
6344 * @param {Number} rowIndex
6345 * @param {Roo.EventObject} e
6347 "rowdblclick" : true,
6350 * Fires when a mouseover occur
6351 * @param {Roo.bootstrap.Table} this
6352 * @param {Roo.Element} el
6353 * @param {Number} rowIndex
6354 * @param {Number} columnIndex
6355 * @param {Roo.EventObject} e
6360 * Fires when a mouseout occur
6361 * @param {Roo.bootstrap.Table} this
6362 * @param {Roo.Element} el
6363 * @param {Number} rowIndex
6364 * @param {Number} columnIndex
6365 * @param {Roo.EventObject} e
6370 * Fires when a row is rendered, so you can change add a style to it.
6371 * @param {Roo.bootstrap.Table} this
6372 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6376 * @event rowsrendered
6377 * Fires when all the rows have been rendered
6378 * @param {Roo.bootstrap.Table} this
6380 'rowsrendered' : true,
6382 * @event contextmenu
6383 * The raw contextmenu event for the entire grid.
6384 * @param {Roo.EventObject} e
6386 "contextmenu" : true,
6388 * @event rowcontextmenu
6389 * Fires when a row is right clicked
6390 * @param {Roo.bootstrap.Table} this
6391 * @param {Number} rowIndex
6392 * @param {Roo.EventObject} e
6394 "rowcontextmenu" : true,
6396 * @event cellcontextmenu
6397 * Fires when a cell is right clicked
6398 * @param {Roo.bootstrap.Table} this
6399 * @param {Number} rowIndex
6400 * @param {Number} cellIndex
6401 * @param {Roo.EventObject} e
6403 "cellcontextmenu" : true,
6405 * @event headercontextmenu
6406 * Fires when a header is right clicked
6407 * @param {Roo.bootstrap.Table} this
6408 * @param {Number} columnIndex
6409 * @param {Roo.EventObject} e
6411 "headercontextmenu" : true
6415 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6441 rowSelection : false,
6442 cellSelection : false,
6445 // Roo.Element - the tbody
6447 // Roo.Element - thead element
6450 container: false, // used by gridpanel...
6456 auto_hide_footer : false,
6458 getAutoCreate : function()
6460 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6467 if (this.scrollBody) {
6468 cfg.cls += ' table-body-fixed';
6471 cfg.cls += ' table-striped';
6475 cfg.cls += ' table-hover';
6477 if (this.bordered) {
6478 cfg.cls += ' table-bordered';
6480 if (this.condensed) {
6481 cfg.cls += ' table-condensed';
6483 if (this.responsive) {
6484 cfg.cls += ' table-responsive';
6488 cfg.cls+= ' ' +this.cls;
6491 // this lot should be simplifed...
6504 ].forEach(function(k) {
6512 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6515 if(this.store || this.cm){
6516 if(this.headerShow){
6517 cfg.cn.push(this.renderHeader());
6520 cfg.cn.push(this.renderBody());
6522 if(this.footerShow){
6523 cfg.cn.push(this.renderFooter());
6525 // where does this come from?
6526 //cfg.cls+= ' TableGrid';
6529 return { cn : [ cfg ] };
6532 initEvents : function()
6534 if(!this.store || !this.cm){
6537 if (this.selModel) {
6538 this.selModel.initEvents();
6542 //Roo.log('initEvents with ds!!!!');
6544 this.mainBody = this.el.select('tbody', true).first();
6545 this.mainHead = this.el.select('thead', true).first();
6546 this.mainFoot = this.el.select('tfoot', true).first();
6552 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6553 e.on('click', _this.sort, _this);
6556 this.mainBody.on("click", this.onClick, this);
6557 this.mainBody.on("dblclick", this.onDblClick, this);
6559 // why is this done????? = it breaks dialogs??
6560 //this.parent().el.setStyle('position', 'relative');
6564 this.footer.parentId = this.id;
6565 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6568 this.el.select('tfoot tr td').first().addClass('hide');
6573 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6576 this.store.on('load', this.onLoad, this);
6577 this.store.on('beforeload', this.onBeforeLoad, this);
6578 this.store.on('update', this.onUpdate, this);
6579 this.store.on('add', this.onAdd, this);
6580 this.store.on("clear", this.clear, this);
6582 this.el.on("contextmenu", this.onContextMenu, this);
6584 this.mainBody.on('scroll', this.onBodyScroll, this);
6586 this.cm.on("headerchange", this.onHeaderChange, this);
6588 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6592 onContextMenu : function(e, t)
6594 this.processEvent("contextmenu", e);
6597 processEvent : function(name, e)
6599 if (name != 'touchstart' ) {
6600 this.fireEvent(name, e);
6603 var t = e.getTarget();
6605 var cell = Roo.get(t);
6611 if(cell.findParent('tfoot', false, true)){
6615 if(cell.findParent('thead', false, true)){
6617 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6618 cell = Roo.get(t).findParent('th', false, true);
6620 Roo.log("failed to find th in thead?");
6621 Roo.log(e.getTarget());
6626 var cellIndex = cell.dom.cellIndex;
6628 var ename = name == 'touchstart' ? 'click' : name;
6629 this.fireEvent("header" + ename, this, cellIndex, e);
6634 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6635 cell = Roo.get(t).findParent('td', false, true);
6637 Roo.log("failed to find th in tbody?");
6638 Roo.log(e.getTarget());
6643 var row = cell.findParent('tr', false, true);
6644 var cellIndex = cell.dom.cellIndex;
6645 var rowIndex = row.dom.rowIndex - 1;
6649 this.fireEvent("row" + name, this, rowIndex, e);
6653 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6659 onMouseover : function(e, el)
6661 var cell = Roo.get(el);
6667 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6668 cell = cell.findParent('td', false, true);
6671 var row = cell.findParent('tr', false, true);
6672 var cellIndex = cell.dom.cellIndex;
6673 var rowIndex = row.dom.rowIndex - 1; // start from 0
6675 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6679 onMouseout : function(e, el)
6681 var cell = Roo.get(el);
6687 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6688 cell = cell.findParent('td', false, true);
6691 var row = cell.findParent('tr', false, true);
6692 var cellIndex = cell.dom.cellIndex;
6693 var rowIndex = row.dom.rowIndex - 1; // start from 0
6695 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6699 onClick : function(e, el)
6701 var cell = Roo.get(el);
6703 if(!cell || (!this.cellSelection && !this.rowSelection)){
6707 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6708 cell = cell.findParent('td', false, true);
6711 if(!cell || typeof(cell) == 'undefined'){
6715 var row = cell.findParent('tr', false, true);
6717 if(!row || typeof(row) == 'undefined'){
6721 var cellIndex = cell.dom.cellIndex;
6722 var rowIndex = this.getRowIndex(row);
6724 // why??? - should these not be based on SelectionModel?
6725 if(this.cellSelection){
6726 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6729 if(this.rowSelection){
6730 this.fireEvent('rowclick', this, row, rowIndex, e);
6736 onDblClick : function(e,el)
6738 var cell = Roo.get(el);
6740 if(!cell || (!this.cellSelection && !this.rowSelection)){
6744 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6745 cell = cell.findParent('td', false, true);
6748 if(!cell || typeof(cell) == 'undefined'){
6752 var row = cell.findParent('tr', false, true);
6754 if(!row || typeof(row) == 'undefined'){
6758 var cellIndex = cell.dom.cellIndex;
6759 var rowIndex = this.getRowIndex(row);
6761 if(this.cellSelection){
6762 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6765 if(this.rowSelection){
6766 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6770 sort : function(e,el)
6772 var col = Roo.get(el);
6774 if(!col.hasClass('sortable')){
6778 var sort = col.attr('sort');
6781 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6785 this.store.sortInfo = {field : sort, direction : dir};
6788 Roo.log("calling footer first");
6789 this.footer.onClick('first');
6792 this.store.load({ params : { start : 0 } });
6796 renderHeader : function()
6804 this.totalWidth = 0;
6806 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6808 var config = cm.config[i];
6812 cls : 'x-hcol-' + i,
6814 html: cm.getColumnHeader(i)
6819 if(typeof(config.sortable) != 'undefined' && config.sortable){
6821 c.html = '<i class="glyphicon"></i>' + c.html;
6824 // could use BS4 hidden-..-down
6826 if(typeof(config.lgHeader) != 'undefined'){
6827 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
6830 if(typeof(config.mdHeader) != 'undefined'){
6831 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6834 if(typeof(config.smHeader) != 'undefined'){
6835 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6838 if(typeof(config.xsHeader) != 'undefined'){
6839 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6846 if(typeof(config.tooltip) != 'undefined'){
6847 c.tooltip = config.tooltip;
6850 if(typeof(config.colspan) != 'undefined'){
6851 c.colspan = config.colspan;
6854 if(typeof(config.hidden) != 'undefined' && config.hidden){
6855 c.style += ' display:none;';
6858 if(typeof(config.dataIndex) != 'undefined'){
6859 c.sort = config.dataIndex;
6864 if(typeof(config.align) != 'undefined' && config.align.length){
6865 c.style += ' text-align:' + config.align + ';';
6868 if(typeof(config.width) != 'undefined'){
6869 c.style += ' width:' + config.width + 'px;';
6870 this.totalWidth += config.width;
6872 this.totalWidth += 100; // assume minimum of 100 per column?
6875 if(typeof(config.cls) != 'undefined'){
6876 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6879 ['xs','sm','md','lg'].map(function(size){
6881 if(typeof(config[size]) == 'undefined'){
6885 if (!config[size]) { // 0 = hidden
6886 // BS 4 '0' is treated as hide that column and below.
6887 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
6891 c.cls += ' col-' + size + '-' + config[size] + (
6892 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
6904 renderBody : function()
6914 colspan : this.cm.getColumnCount()
6924 renderFooter : function()
6934 colspan : this.cm.getColumnCount()
6948 // Roo.log('ds onload');
6953 var ds = this.store;
6955 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6956 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6957 if (_this.store.sortInfo) {
6959 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6960 e.select('i', true).addClass(['glyphicon-arrow-up']);
6963 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6964 e.select('i', true).addClass(['glyphicon-arrow-down']);
6969 var tbody = this.mainBody;
6971 if(ds.getCount() > 0){
6972 ds.data.each(function(d,rowIndex){
6973 var row = this.renderRow(cm, ds, rowIndex);
6975 tbody.createChild(row);
6979 if(row.cellObjects.length){
6980 Roo.each(row.cellObjects, function(r){
6981 _this.renderCellObject(r);
6988 var tfoot = this.el.select('tfoot', true).first();
6990 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6992 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6994 var total = this.ds.getTotalCount();
6996 if(this.footer.pageSize < total){
6997 this.mainFoot.show();
7001 Roo.each(this.el.select('tbody td', true).elements, function(e){
7002 e.on('mouseover', _this.onMouseover, _this);
7005 Roo.each(this.el.select('tbody td', true).elements, function(e){
7006 e.on('mouseout', _this.onMouseout, _this);
7008 this.fireEvent('rowsrendered', this);
7014 onUpdate : function(ds,record)
7016 this.refreshRow(record);
7020 onRemove : function(ds, record, index, isUpdate){
7021 if(isUpdate !== true){
7022 this.fireEvent("beforerowremoved", this, index, record);
7024 var bt = this.mainBody.dom;
7026 var rows = this.el.select('tbody > tr', true).elements;
7028 if(typeof(rows[index]) != 'undefined'){
7029 bt.removeChild(rows[index].dom);
7032 // if(bt.rows[index]){
7033 // bt.removeChild(bt.rows[index]);
7036 if(isUpdate !== true){
7037 //this.stripeRows(index);
7038 //this.syncRowHeights(index, index);
7040 this.fireEvent("rowremoved", this, index, record);
7044 onAdd : function(ds, records, rowIndex)
7046 //Roo.log('on Add called');
7047 // - note this does not handle multiple adding very well..
7048 var bt = this.mainBody.dom;
7049 for (var i =0 ; i < records.length;i++) {
7050 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7051 //Roo.log(records[i]);
7052 //Roo.log(this.store.getAt(rowIndex+i));
7053 this.insertRow(this.store, rowIndex + i, false);
7060 refreshRow : function(record){
7061 var ds = this.store, index;
7062 if(typeof record == 'number'){
7064 record = ds.getAt(index);
7066 index = ds.indexOf(record);
7068 this.insertRow(ds, index, true);
7070 this.onRemove(ds, record, index+1, true);
7072 //this.syncRowHeights(index, index);
7074 this.fireEvent("rowupdated", this, index, record);
7077 insertRow : function(dm, rowIndex, isUpdate){
7080 this.fireEvent("beforerowsinserted", this, rowIndex);
7082 //var s = this.getScrollState();
7083 var row = this.renderRow(this.cm, this.store, rowIndex);
7084 // insert before rowIndex..
7085 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7089 if(row.cellObjects.length){
7090 Roo.each(row.cellObjects, function(r){
7091 _this.renderCellObject(r);
7096 this.fireEvent("rowsinserted", this, rowIndex);
7097 //this.syncRowHeights(firstRow, lastRow);
7098 //this.stripeRows(firstRow);
7105 getRowDom : function(rowIndex)
7107 var rows = this.el.select('tbody > tr', true).elements;
7109 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7112 // returns the object tree for a tr..
7115 renderRow : function(cm, ds, rowIndex)
7117 var d = ds.getAt(rowIndex);
7121 cls : 'x-row-' + rowIndex,
7125 var cellObjects = [];
7127 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7128 var config = cm.config[i];
7130 var renderer = cm.getRenderer(i);
7134 if(typeof(renderer) !== 'undefined'){
7135 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7137 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7138 // and are rendered into the cells after the row is rendered - using the id for the element.
7140 if(typeof(value) === 'object'){
7150 rowIndex : rowIndex,
7155 this.fireEvent('rowclass', this, rowcfg);
7159 cls : rowcfg.rowClass + ' x-col-' + i,
7161 html: (typeof(value) === 'object') ? '' : value
7168 if(typeof(config.colspan) != 'undefined'){
7169 td.colspan = config.colspan;
7172 if(typeof(config.hidden) != 'undefined' && config.hidden){
7173 td.style += ' display:none;';
7176 if(typeof(config.align) != 'undefined' && config.align.length){
7177 td.style += ' text-align:' + config.align + ';';
7179 if(typeof(config.valign) != 'undefined' && config.valign.length){
7180 td.style += ' vertical-align:' + config.valign + ';';
7183 if(typeof(config.width) != 'undefined'){
7184 td.style += ' width:' + config.width + 'px;';
7187 if(typeof(config.cursor) != 'undefined'){
7188 td.style += ' cursor:' + config.cursor + ';';
7191 if(typeof(config.cls) != 'undefined'){
7192 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7195 ['xs','sm','md','lg'].map(function(size){
7197 if(typeof(config[size]) == 'undefined'){
7203 if (!config[size]) { // 0 = hidden
7204 // BS 4 '0' is treated as hide that column and below.
7205 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
7209 td.cls += ' col-' + size + '-' + config[size] + (
7210 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7220 row.cellObjects = cellObjects;
7228 onBeforeLoad : function()
7237 this.el.select('tbody', true).first().dom.innerHTML = '';
7240 * Show or hide a row.
7241 * @param {Number} rowIndex to show or hide
7242 * @param {Boolean} state hide
7244 setRowVisibility : function(rowIndex, state)
7246 var bt = this.mainBody.dom;
7248 var rows = this.el.select('tbody > tr', true).elements;
7250 if(typeof(rows[rowIndex]) == 'undefined'){
7253 rows[rowIndex].dom.style.display = state ? '' : 'none';
7257 getSelectionModel : function(){
7259 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7261 return this.selModel;
7264 * Render the Roo.bootstrap object from renderder
7266 renderCellObject : function(r)
7270 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7272 var t = r.cfg.render(r.container);
7275 Roo.each(r.cfg.cn, function(c){
7277 container: t.getChildContainer(),
7280 _this.renderCellObject(child);
7285 getRowIndex : function(row)
7289 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7300 * Returns the grid's underlying element = used by panel.Grid
7301 * @return {Element} The element
7303 getGridEl : function(){
7307 * Forces a resize - used by panel.Grid
7308 * @return {Element} The element
7310 autoSize : function()
7312 //var ctr = Roo.get(this.container.dom.parentElement);
7313 var ctr = Roo.get(this.el.dom);
7315 var thd = this.getGridEl().select('thead',true).first();
7316 var tbd = this.getGridEl().select('tbody', true).first();
7317 var tfd = this.getGridEl().select('tfoot', true).first();
7319 var cw = ctr.getWidth();
7323 tbd.setSize(ctr.getWidth(),
7324 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7326 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7329 cw = Math.max(cw, this.totalWidth);
7330 this.getGridEl().select('tr',true).setWidth(cw);
7331 // resize 'expandable coloumn?
7333 return; // we doe not have a view in this design..
7336 onBodyScroll: function()
7338 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7340 this.mainHead.setStyle({
7341 'position' : 'relative',
7342 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7348 var scrollHeight = this.mainBody.dom.scrollHeight;
7350 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7352 var height = this.mainBody.getHeight();
7354 if(scrollHeight - height == scrollTop) {
7356 var total = this.ds.getTotalCount();
7358 if(this.footer.cursor + this.footer.pageSize < total){
7360 this.footer.ds.load({
7362 start : this.footer.cursor + this.footer.pageSize,
7363 limit : this.footer.pageSize
7373 onHeaderChange : function()
7375 var header = this.renderHeader();
7376 var table = this.el.select('table', true).first();
7378 this.mainHead.remove();
7379 this.mainHead = table.createChild(header, this.mainBody, false);
7382 onHiddenChange : function(colModel, colIndex, hidden)
7384 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7385 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7387 this.CSS.updateRule(thSelector, "display", "");
7388 this.CSS.updateRule(tdSelector, "display", "");
7391 this.CSS.updateRule(thSelector, "display", "none");
7392 this.CSS.updateRule(tdSelector, "display", "none");
7395 this.onHeaderChange();
7399 setColumnWidth: function(col_index, width)
7401 // width = "md-2 xs-2..."
7402 if(!this.colModel.config[col_index]) {
7406 var w = width.split(" ");
7408 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7410 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7413 for(var j = 0; j < w.length; j++) {
7419 var size_cls = w[j].split("-");
7421 if(!Number.isInteger(size_cls[1] * 1)) {
7425 if(!this.colModel.config[col_index][size_cls[0]]) {
7429 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7433 h_row[0].classList.replace(
7434 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7435 "col-"+size_cls[0]+"-"+size_cls[1]
7438 for(var i = 0; i < rows.length; i++) {
7440 var size_cls = w[j].split("-");
7442 if(!Number.isInteger(size_cls[1] * 1)) {
7446 if(!this.colModel.config[col_index][size_cls[0]]) {
7450 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7454 rows[i].classList.replace(
7455 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7456 "col-"+size_cls[0]+"-"+size_cls[1]
7460 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7475 * @class Roo.bootstrap.TableCell
7476 * @extends Roo.bootstrap.Component
7477 * Bootstrap TableCell class
7478 * @cfg {String} html cell contain text
7479 * @cfg {String} cls cell class
7480 * @cfg {String} tag cell tag (td|th) default td
7481 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7482 * @cfg {String} align Aligns the content in a cell
7483 * @cfg {String} axis Categorizes cells
7484 * @cfg {String} bgcolor Specifies the background color of a cell
7485 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7486 * @cfg {Number} colspan Specifies the number of columns a cell should span
7487 * @cfg {String} headers Specifies one or more header cells a cell is related to
7488 * @cfg {Number} height Sets the height of a cell
7489 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7490 * @cfg {Number} rowspan Sets the number of rows a cell should span
7491 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7492 * @cfg {String} valign Vertical aligns the content in a cell
7493 * @cfg {Number} width Specifies the width of a cell
7496 * Create a new TableCell
7497 * @param {Object} config The config object
7500 Roo.bootstrap.TableCell = function(config){
7501 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7504 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7524 getAutoCreate : function(){
7525 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7545 cfg.align=this.align
7551 cfg.bgcolor=this.bgcolor
7554 cfg.charoff=this.charoff
7557 cfg.colspan=this.colspan
7560 cfg.headers=this.headers
7563 cfg.height=this.height
7566 cfg.nowrap=this.nowrap
7569 cfg.rowspan=this.rowspan
7572 cfg.scope=this.scope
7575 cfg.valign=this.valign
7578 cfg.width=this.width
7597 * @class Roo.bootstrap.TableRow
7598 * @extends Roo.bootstrap.Component
7599 * Bootstrap TableRow class
7600 * @cfg {String} cls row class
7601 * @cfg {String} align Aligns the content in a table row
7602 * @cfg {String} bgcolor Specifies a background color for a table row
7603 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7604 * @cfg {String} valign Vertical aligns the content in a table row
7607 * Create a new TableRow
7608 * @param {Object} config The config object
7611 Roo.bootstrap.TableRow = function(config){
7612 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7615 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7623 getAutoCreate : function(){
7624 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7634 cfg.align = this.align;
7637 cfg.bgcolor = this.bgcolor;
7640 cfg.charoff = this.charoff;
7643 cfg.valign = this.valign;
7661 * @class Roo.bootstrap.TableBody
7662 * @extends Roo.bootstrap.Component
7663 * Bootstrap TableBody class
7664 * @cfg {String} cls element class
7665 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7666 * @cfg {String} align Aligns the content inside the element
7667 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7668 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7671 * Create a new TableBody
7672 * @param {Object} config The config object
7675 Roo.bootstrap.TableBody = function(config){
7676 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7679 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7687 getAutoCreate : function(){
7688 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7702 cfg.align = this.align;
7705 cfg.charoff = this.charoff;
7708 cfg.valign = this.valign;
7715 // initEvents : function()
7722 // this.store = Roo.factory(this.store, Roo.data);
7723 // this.store.on('load', this.onLoad, this);
7725 // this.store.load();
7729 // onLoad: function ()
7731 // this.fireEvent('load', this);
7741 * Ext JS Library 1.1.1
7742 * Copyright(c) 2006-2007, Ext JS, LLC.
7744 * Originally Released Under LGPL - original licence link has changed is not relivant.
7747 * <script type="text/javascript">
7750 // as we use this in bootstrap.
7751 Roo.namespace('Roo.form');
7753 * @class Roo.form.Action
7754 * Internal Class used to handle form actions
7756 * @param {Roo.form.BasicForm} el The form element or its id
7757 * @param {Object} config Configuration options
7762 // define the action interface
7763 Roo.form.Action = function(form, options){
7765 this.options = options || {};
7768 * Client Validation Failed
7771 Roo.form.Action.CLIENT_INVALID = 'client';
7773 * Server Validation Failed
7776 Roo.form.Action.SERVER_INVALID = 'server';
7778 * Connect to Server Failed
7781 Roo.form.Action.CONNECT_FAILURE = 'connect';
7783 * Reading Data from Server Failed
7786 Roo.form.Action.LOAD_FAILURE = 'load';
7788 Roo.form.Action.prototype = {
7790 failureType : undefined,
7791 response : undefined,
7795 run : function(options){
7800 success : function(response){
7805 handleResponse : function(response){
7809 // default connection failure
7810 failure : function(response){
7812 this.response = response;
7813 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7814 this.form.afterAction(this, false);
7817 processResponse : function(response){
7818 this.response = response;
7819 if(!response.responseText){
7822 this.result = this.handleResponse(response);
7826 // utility functions used internally
7827 getUrl : function(appendParams){
7828 var url = this.options.url || this.form.url || this.form.el.dom.action;
7830 var p = this.getParams();
7832 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7838 getMethod : function(){
7839 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7842 getParams : function(){
7843 var bp = this.form.baseParams;
7844 var p = this.options.params;
7846 if(typeof p == "object"){
7847 p = Roo.urlEncode(Roo.applyIf(p, bp));
7848 }else if(typeof p == 'string' && bp){
7849 p += '&' + Roo.urlEncode(bp);
7852 p = Roo.urlEncode(bp);
7857 createCallback : function(){
7859 success: this.success,
7860 failure: this.failure,
7862 timeout: (this.form.timeout*1000),
7863 upload: this.form.fileUpload ? this.success : undefined
7868 Roo.form.Action.Submit = function(form, options){
7869 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7872 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7875 haveProgress : false,
7876 uploadComplete : false,
7878 // uploadProgress indicator.
7879 uploadProgress : function()
7881 if (!this.form.progressUrl) {
7885 if (!this.haveProgress) {
7886 Roo.MessageBox.progress("Uploading", "Uploading");
7888 if (this.uploadComplete) {
7889 Roo.MessageBox.hide();
7893 this.haveProgress = true;
7895 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7897 var c = new Roo.data.Connection();
7899 url : this.form.progressUrl,
7904 success : function(req){
7905 //console.log(data);
7909 rdata = Roo.decode(req.responseText)
7911 Roo.log("Invalid data from server..");
7915 if (!rdata || !rdata.success) {
7917 Roo.MessageBox.alert(Roo.encode(rdata));
7920 var data = rdata.data;
7922 if (this.uploadComplete) {
7923 Roo.MessageBox.hide();
7928 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7929 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7932 this.uploadProgress.defer(2000,this);
7935 failure: function(data) {
7936 Roo.log('progress url failed ');
7947 // run get Values on the form, so it syncs any secondary forms.
7948 this.form.getValues();
7950 var o = this.options;
7951 var method = this.getMethod();
7952 var isPost = method == 'POST';
7953 if(o.clientValidation === false || this.form.isValid()){
7955 if (this.form.progressUrl) {
7956 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7957 (new Date() * 1) + '' + Math.random());
7962 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7963 form:this.form.el.dom,
7964 url:this.getUrl(!isPost),
7966 params:isPost ? this.getParams() : null,
7967 isUpload: this.form.fileUpload,
7968 formData : this.form.formData
7971 this.uploadProgress();
7973 }else if (o.clientValidation !== false){ // client validation failed
7974 this.failureType = Roo.form.Action.CLIENT_INVALID;
7975 this.form.afterAction(this, false);
7979 success : function(response)
7981 this.uploadComplete= true;
7982 if (this.haveProgress) {
7983 Roo.MessageBox.hide();
7987 var result = this.processResponse(response);
7988 if(result === true || result.success){
7989 this.form.afterAction(this, true);
7993 this.form.markInvalid(result.errors);
7994 this.failureType = Roo.form.Action.SERVER_INVALID;
7996 this.form.afterAction(this, false);
7998 failure : function(response)
8000 this.uploadComplete= true;
8001 if (this.haveProgress) {
8002 Roo.MessageBox.hide();
8005 this.response = response;
8006 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8007 this.form.afterAction(this, false);
8010 handleResponse : function(response){
8011 if(this.form.errorReader){
8012 var rs = this.form.errorReader.read(response);
8015 for(var i = 0, len = rs.records.length; i < len; i++) {
8016 var r = rs.records[i];
8020 if(errors.length < 1){
8024 success : rs.success,
8030 ret = Roo.decode(response.responseText);
8034 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8044 Roo.form.Action.Load = function(form, options){
8045 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8046 this.reader = this.form.reader;
8049 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8054 Roo.Ajax.request(Roo.apply(
8055 this.createCallback(), {
8056 method:this.getMethod(),
8057 url:this.getUrl(false),
8058 params:this.getParams()
8062 success : function(response){
8064 var result = this.processResponse(response);
8065 if(result === true || !result.success || !result.data){
8066 this.failureType = Roo.form.Action.LOAD_FAILURE;
8067 this.form.afterAction(this, false);
8070 this.form.clearInvalid();
8071 this.form.setValues(result.data);
8072 this.form.afterAction(this, true);
8075 handleResponse : function(response){
8076 if(this.form.reader){
8077 var rs = this.form.reader.read(response);
8078 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8080 success : rs.success,
8084 return Roo.decode(response.responseText);
8088 Roo.form.Action.ACTION_TYPES = {
8089 'load' : Roo.form.Action.Load,
8090 'submit' : Roo.form.Action.Submit
8099 * @class Roo.bootstrap.Form
8100 * @extends Roo.bootstrap.Component
8101 * Bootstrap Form class
8102 * @cfg {String} method GET | POST (default POST)
8103 * @cfg {String} labelAlign top | left (default top)
8104 * @cfg {String} align left | right - for navbars
8105 * @cfg {Boolean} loadMask load mask when submit (default true)
8110 * @param {Object} config The config object
8114 Roo.bootstrap.Form = function(config){
8116 Roo.bootstrap.Form.superclass.constructor.call(this, config);
8118 Roo.bootstrap.Form.popover.apply();
8122 * @event clientvalidation
8123 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8124 * @param {Form} this
8125 * @param {Boolean} valid true if the form has passed client-side validation
8127 clientvalidation: true,
8129 * @event beforeaction
8130 * Fires before any action is performed. Return false to cancel the action.
8131 * @param {Form} this
8132 * @param {Action} action The action to be performed
8136 * @event actionfailed
8137 * Fires when an action fails.
8138 * @param {Form} this
8139 * @param {Action} action The action that failed
8141 actionfailed : true,
8143 * @event actioncomplete
8144 * Fires when an action is completed.
8145 * @param {Form} this
8146 * @param {Action} action The action that completed
8148 actioncomplete : true
8152 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
8155 * @cfg {String} method
8156 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8161 * The URL to use for form actions if one isn't supplied in the action options.
8164 * @cfg {Boolean} fileUpload
8165 * Set to true if this form is a file upload.
8169 * @cfg {Object} baseParams
8170 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8174 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8178 * @cfg {Sting} align (left|right) for navbar forms
8183 activeAction : null,
8186 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8187 * element by passing it or its id or mask the form itself by passing in true.
8190 waitMsgTarget : false,
8195 * @cfg {Boolean} errorMask (true|false) default false
8200 * @cfg {Number} maskOffset Default 100
8205 * @cfg {Boolean} maskBody
8209 getAutoCreate : function(){
8213 method : this.method || 'POST',
8214 id : this.id || Roo.id(),
8217 if (this.parent().xtype.match(/^Nav/)) {
8218 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8222 if (this.labelAlign == 'left' ) {
8223 cfg.cls += ' form-horizontal';
8229 initEvents : function()
8231 this.el.on('submit', this.onSubmit, this);
8232 // this was added as random key presses on the form where triggering form submit.
8233 this.el.on('keypress', function(e) {
8234 if (e.getCharCode() != 13) {
8237 // we might need to allow it for textareas.. and some other items.
8238 // check e.getTarget().
8240 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8244 Roo.log("keypress blocked");
8252 onSubmit : function(e){
8257 * Returns true if client-side validation on the form is successful.
8260 isValid : function(){
8261 var items = this.getItems();
8265 items.each(function(f){
8271 Roo.log('invalid field: ' + f.name);
8275 if(!target && f.el.isVisible(true)){
8281 if(this.errorMask && !valid){
8282 Roo.bootstrap.Form.popover.mask(this, target);
8289 * Returns true if any fields in this form have changed since their original load.
8292 isDirty : function(){
8294 var items = this.getItems();
8295 items.each(function(f){
8305 * Performs a predefined action (submit or load) or custom actions you define on this form.
8306 * @param {String} actionName The name of the action type
8307 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8308 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8309 * accept other config options):
8311 Property Type Description
8312 ---------------- --------------- ----------------------------------------------------------------------------------
8313 url String The url for the action (defaults to the form's url)
8314 method String The form method to use (defaults to the form's method, or POST if not defined)
8315 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8316 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8317 validate the form on the client (defaults to false)
8319 * @return {BasicForm} this
8321 doAction : function(action, options){
8322 if(typeof action == 'string'){
8323 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8325 if(this.fireEvent('beforeaction', this, action) !== false){
8326 this.beforeAction(action);
8327 action.run.defer(100, action);
8333 beforeAction : function(action){
8334 var o = action.options;
8339 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8341 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8344 // not really supported yet.. ??
8346 //if(this.waitMsgTarget === true){
8347 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8348 //}else if(this.waitMsgTarget){
8349 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8350 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8352 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8358 afterAction : function(action, success){
8359 this.activeAction = null;
8360 var o = action.options;
8365 Roo.get(document.body).unmask();
8371 //if(this.waitMsgTarget === true){
8372 // this.el.unmask();
8373 //}else if(this.waitMsgTarget){
8374 // this.waitMsgTarget.unmask();
8376 // Roo.MessageBox.updateProgress(1);
8377 // Roo.MessageBox.hide();
8384 Roo.callback(o.success, o.scope, [this, action]);
8385 this.fireEvent('actioncomplete', this, action);
8389 // failure condition..
8390 // we have a scenario where updates need confirming.
8391 // eg. if a locking scenario exists..
8392 // we look for { errors : { needs_confirm : true }} in the response.
8394 (typeof(action.result) != 'undefined') &&
8395 (typeof(action.result.errors) != 'undefined') &&
8396 (typeof(action.result.errors.needs_confirm) != 'undefined')
8399 Roo.log("not supported yet");
8402 Roo.MessageBox.confirm(
8403 "Change requires confirmation",
8404 action.result.errorMsg,
8409 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8419 Roo.callback(o.failure, o.scope, [this, action]);
8420 // show an error message if no failed handler is set..
8421 if (!this.hasListener('actionfailed')) {
8422 Roo.log("need to add dialog support");
8424 Roo.MessageBox.alert("Error",
8425 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8426 action.result.errorMsg :
8427 "Saving Failed, please check your entries or try again"
8432 this.fireEvent('actionfailed', this, action);
8437 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8438 * @param {String} id The value to search for
8441 findField : function(id){
8442 var items = this.getItems();
8443 var field = items.get(id);
8445 items.each(function(f){
8446 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8453 return field || null;
8456 * Mark fields in this form invalid in bulk.
8457 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8458 * @return {BasicForm} this
8460 markInvalid : function(errors){
8461 if(errors instanceof Array){
8462 for(var i = 0, len = errors.length; i < len; i++){
8463 var fieldError = errors[i];
8464 var f = this.findField(fieldError.id);
8466 f.markInvalid(fieldError.msg);
8472 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8473 field.markInvalid(errors[id]);
8477 //Roo.each(this.childForms || [], function (f) {
8478 // f.markInvalid(errors);
8485 * Set values for fields in this form in bulk.
8486 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8487 * @return {BasicForm} this
8489 setValues : function(values){
8490 if(values instanceof Array){ // array of objects
8491 for(var i = 0, len = values.length; i < len; i++){
8493 var f = this.findField(v.id);
8495 f.setValue(v.value);
8496 if(this.trackResetOnLoad){
8497 f.originalValue = f.getValue();
8501 }else{ // object hash
8504 if(typeof values[id] != 'function' && (field = this.findField(id))){
8506 if (field.setFromData &&
8508 field.displayField &&
8509 // combos' with local stores can
8510 // be queried via setValue()
8511 // to set their value..
8512 (field.store && !field.store.isLocal)
8516 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8517 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8518 field.setFromData(sd);
8520 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8522 field.setFromData(values);
8525 field.setValue(values[id]);
8529 if(this.trackResetOnLoad){
8530 field.originalValue = field.getValue();
8536 //Roo.each(this.childForms || [], function (f) {
8537 // f.setValues(values);
8544 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8545 * they are returned as an array.
8546 * @param {Boolean} asString
8549 getValues : function(asString){
8550 //if (this.childForms) {
8551 // copy values from the child forms
8552 // Roo.each(this.childForms, function (f) {
8553 // this.setValues(f.getValues());
8559 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8560 if(asString === true){
8563 return Roo.urlDecode(fs);
8567 * Returns the fields in this form as an object with key/value pairs.
8568 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8571 getFieldValues : function(with_hidden)
8573 var items = this.getItems();
8575 items.each(function(f){
8581 var v = f.getValue();
8583 if (f.inputType =='radio') {
8584 if (typeof(ret[f.getName()]) == 'undefined') {
8585 ret[f.getName()] = ''; // empty..
8588 if (!f.el.dom.checked) {
8596 if(f.xtype == 'MoneyField'){
8597 ret[f.currencyName] = f.getCurrency();
8600 // not sure if this supported any more..
8601 if ((typeof(v) == 'object') && f.getRawValue) {
8602 v = f.getRawValue() ; // dates..
8604 // combo boxes where name != hiddenName...
8605 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8606 ret[f.name] = f.getRawValue();
8608 ret[f.getName()] = v;
8615 * Clears all invalid messages in this form.
8616 * @return {BasicForm} this
8618 clearInvalid : function(){
8619 var items = this.getItems();
8621 items.each(function(f){
8630 * @return {BasicForm} this
8633 var items = this.getItems();
8634 items.each(function(f){
8638 Roo.each(this.childForms || [], function (f) {
8646 getItems : function()
8648 var r=new Roo.util.MixedCollection(false, function(o){
8649 return o.id || (o.id = Roo.id());
8651 var iter = function(el) {
8658 Roo.each(el.items,function(e) {
8667 hideFields : function(items)
8669 Roo.each(items, function(i){
8671 var f = this.findField(i);
8682 showFields : function(items)
8684 Roo.each(items, function(i){
8686 var f = this.findField(i);
8699 Roo.apply(Roo.bootstrap.Form, {
8726 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8727 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8728 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8729 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8732 this.maskEl.top.enableDisplayMode("block");
8733 this.maskEl.left.enableDisplayMode("block");
8734 this.maskEl.bottom.enableDisplayMode("block");
8735 this.maskEl.right.enableDisplayMode("block");
8737 this.toolTip = new Roo.bootstrap.Tooltip({
8738 cls : 'roo-form-error-popover',
8740 'left' : ['r-l', [-2,0], 'right'],
8741 'right' : ['l-r', [2,0], 'left'],
8742 'bottom' : ['tl-bl', [0,2], 'top'],
8743 'top' : [ 'bl-tl', [0,-2], 'bottom']
8747 this.toolTip.render(Roo.get(document.body));
8749 this.toolTip.el.enableDisplayMode("block");
8751 Roo.get(document.body).on('click', function(){
8755 Roo.get(document.body).on('touchstart', function(){
8759 this.isApplied = true
8762 mask : function(form, target)
8766 this.target = target;
8768 if(!this.form.errorMask || !target.el){
8772 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8774 Roo.log(scrollable);
8776 var ot = this.target.el.calcOffsetsTo(scrollable);
8778 var scrollTo = ot[1] - this.form.maskOffset;
8780 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8782 scrollable.scrollTo('top', scrollTo);
8784 var box = this.target.el.getBox();
8786 var zIndex = Roo.bootstrap.Modal.zIndex++;
8789 this.maskEl.top.setStyle('position', 'absolute');
8790 this.maskEl.top.setStyle('z-index', zIndex);
8791 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8792 this.maskEl.top.setLeft(0);
8793 this.maskEl.top.setTop(0);
8794 this.maskEl.top.show();
8796 this.maskEl.left.setStyle('position', 'absolute');
8797 this.maskEl.left.setStyle('z-index', zIndex);
8798 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8799 this.maskEl.left.setLeft(0);
8800 this.maskEl.left.setTop(box.y - this.padding);
8801 this.maskEl.left.show();
8803 this.maskEl.bottom.setStyle('position', 'absolute');
8804 this.maskEl.bottom.setStyle('z-index', zIndex);
8805 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8806 this.maskEl.bottom.setLeft(0);
8807 this.maskEl.bottom.setTop(box.bottom + this.padding);
8808 this.maskEl.bottom.show();
8810 this.maskEl.right.setStyle('position', 'absolute');
8811 this.maskEl.right.setStyle('z-index', zIndex);
8812 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8813 this.maskEl.right.setLeft(box.right + this.padding);
8814 this.maskEl.right.setTop(box.y - this.padding);
8815 this.maskEl.right.show();
8817 this.toolTip.bindEl = this.target.el;
8819 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8821 var tip = this.target.blankText;
8823 if(this.target.getValue() !== '' ) {
8825 if (this.target.invalidText.length) {
8826 tip = this.target.invalidText;
8827 } else if (this.target.regexText.length){
8828 tip = this.target.regexText;
8832 this.toolTip.show(tip);
8834 this.intervalID = window.setInterval(function() {
8835 Roo.bootstrap.Form.popover.unmask();
8838 window.onwheel = function(){ return false;};
8840 (function(){ this.isMasked = true; }).defer(500, this);
8846 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8850 this.maskEl.top.setStyle('position', 'absolute');
8851 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8852 this.maskEl.top.hide();
8854 this.maskEl.left.setStyle('position', 'absolute');
8855 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8856 this.maskEl.left.hide();
8858 this.maskEl.bottom.setStyle('position', 'absolute');
8859 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8860 this.maskEl.bottom.hide();
8862 this.maskEl.right.setStyle('position', 'absolute');
8863 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8864 this.maskEl.right.hide();
8866 this.toolTip.hide();
8868 this.toolTip.el.hide();
8870 window.onwheel = function(){ return true;};
8872 if(this.intervalID){
8873 window.clearInterval(this.intervalID);
8874 this.intervalID = false;
8877 this.isMasked = false;
8887 * Ext JS Library 1.1.1
8888 * Copyright(c) 2006-2007, Ext JS, LLC.
8890 * Originally Released Under LGPL - original licence link has changed is not relivant.
8893 * <script type="text/javascript">
8896 * @class Roo.form.VTypes
8897 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8900 Roo.form.VTypes = function(){
8901 // closure these in so they are only created once.
8902 var alpha = /^[a-zA-Z_]+$/;
8903 var alphanum = /^[a-zA-Z0-9_]+$/;
8904 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8905 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8907 // All these messages and functions are configurable
8910 * The function used to validate email addresses
8911 * @param {String} value The email address
8913 'email' : function(v){
8914 return email.test(v);
8917 * The error text to display when the email validation function returns false
8920 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8922 * The keystroke filter mask to be applied on email input
8925 'emailMask' : /[a-z0-9_\.\-@]/i,
8928 * The function used to validate URLs
8929 * @param {String} value The URL
8931 'url' : function(v){
8935 * The error text to display when the url validation function returns false
8938 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8941 * The function used to validate alpha values
8942 * @param {String} value The value
8944 'alpha' : function(v){
8945 return alpha.test(v);
8948 * The error text to display when the alpha validation function returns false
8951 'alphaText' : 'This field should only contain letters and _',
8953 * The keystroke filter mask to be applied on alpha input
8956 'alphaMask' : /[a-z_]/i,
8959 * The function used to validate alphanumeric values
8960 * @param {String} value The value
8962 'alphanum' : function(v){
8963 return alphanum.test(v);
8966 * The error text to display when the alphanumeric validation function returns false
8969 'alphanumText' : 'This field should only contain letters, numbers and _',
8971 * The keystroke filter mask to be applied on alphanumeric input
8974 'alphanumMask' : /[a-z0-9_]/i
8984 * @class Roo.bootstrap.Input
8985 * @extends Roo.bootstrap.Component
8986 * Bootstrap Input class
8987 * @cfg {Boolean} disabled is it disabled
8988 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8989 * @cfg {String} name name of the input
8990 * @cfg {string} fieldLabel - the label associated
8991 * @cfg {string} placeholder - placeholder to put in text.
8992 * @cfg {string} before - input group add on before
8993 * @cfg {string} after - input group add on after
8994 * @cfg {string} size - (lg|sm) or leave empty..
8995 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8996 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8997 * @cfg {Number} md colspan out of 12 for computer-sized screens
8998 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8999 * @cfg {string} value default value of the input
9000 * @cfg {Number} labelWidth set the width of label
9001 * @cfg {Number} labellg set the width of label (1-12)
9002 * @cfg {Number} labelmd set the width of label (1-12)
9003 * @cfg {Number} labelsm set the width of label (1-12)
9004 * @cfg {Number} labelxs set the width of label (1-12)
9005 * @cfg {String} labelAlign (top|left)
9006 * @cfg {Boolean} readOnly Specifies that the field should be read-only
9007 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9008 * @cfg {String} indicatorpos (left|right) default left
9009 * @cfg {String} capture (user|camera) use for file input only. (default empty)
9010 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9012 * @cfg {String} align (left|center|right) Default left
9013 * @cfg {Boolean} forceFeedback (true|false) Default false
9016 * Create a new Input
9017 * @param {Object} config The config object
9020 Roo.bootstrap.Input = function(config){
9022 Roo.bootstrap.Input.superclass.constructor.call(this, config);
9027 * Fires when this field receives input focus.
9028 * @param {Roo.form.Field} this
9033 * Fires when this field loses input focus.
9034 * @param {Roo.form.Field} this
9039 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
9040 * {@link Roo.EventObject#getKey} to determine which key was pressed.
9041 * @param {Roo.form.Field} this
9042 * @param {Roo.EventObject} e The event object
9047 * Fires just before the field blurs if the field value has changed.
9048 * @param {Roo.form.Field} this
9049 * @param {Mixed} newValue The new value
9050 * @param {Mixed} oldValue The original value
9055 * Fires after the field has been marked as invalid.
9056 * @param {Roo.form.Field} this
9057 * @param {String} msg The validation message
9062 * Fires after the field has been validated with no errors.
9063 * @param {Roo.form.Field} this
9068 * Fires after the key up
9069 * @param {Roo.form.Field} this
9070 * @param {Roo.EventObject} e The event Object
9076 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
9078 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9079 automatic validation (defaults to "keyup").
9081 validationEvent : "keyup",
9083 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9085 validateOnBlur : true,
9087 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9089 validationDelay : 250,
9091 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9093 focusClass : "x-form-focus", // not needed???
9097 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9099 invalidClass : "has-warning",
9102 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9104 validClass : "has-success",
9107 * @cfg {Boolean} hasFeedback (true|false) default true
9112 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9114 invalidFeedbackClass : "glyphicon-warning-sign",
9117 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9119 validFeedbackClass : "glyphicon-ok",
9122 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9124 selectOnFocus : false,
9127 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9131 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9136 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9138 disableKeyFilter : false,
9141 * @cfg {Boolean} disabled True to disable the field (defaults to false).
9145 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9149 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9151 blankText : "Please complete this mandatory field",
9154 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9158 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9160 maxLength : Number.MAX_VALUE,
9162 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9164 minLengthText : "The minimum length for this field is {0}",
9166 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9168 maxLengthText : "The maximum length for this field is {0}",
9172 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9173 * If available, this function will be called only after the basic validators all return true, and will be passed the
9174 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9178 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9179 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9180 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
9184 * @cfg {String} regexText -- Depricated - use Invalid Text
9189 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9195 autocomplete: false,
9214 formatedValue : false,
9215 forceFeedback : false,
9217 indicatorpos : 'left',
9227 parentLabelAlign : function()
9230 while (parent.parent()) {
9231 parent = parent.parent();
9232 if (typeof(parent.labelAlign) !='undefined') {
9233 return parent.labelAlign;
9240 getAutoCreate : function()
9242 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9248 if(this.inputType != 'hidden'){
9249 cfg.cls = 'form-group' //input-group
9255 type : this.inputType,
9257 cls : 'form-control',
9258 placeholder : this.placeholder || '',
9259 autocomplete : this.autocomplete || 'new-password'
9262 if(this.capture.length){
9263 input.capture = this.capture;
9266 if(this.accept.length){
9267 input.accept = this.accept + "/*";
9271 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9274 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9275 input.maxLength = this.maxLength;
9278 if (this.disabled) {
9279 input.disabled=true;
9282 if (this.readOnly) {
9283 input.readonly=true;
9287 input.name = this.name;
9291 input.cls += ' input-' + this.size;
9295 ['xs','sm','md','lg'].map(function(size){
9296 if (settings[size]) {
9297 cfg.cls += ' col-' + size + '-' + settings[size];
9301 var inputblock = input;
9305 cls: 'glyphicon form-control-feedback'
9308 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9311 cls : 'has-feedback',
9319 if (this.before || this.after) {
9322 cls : 'input-group',
9326 if (this.before && typeof(this.before) == 'string') {
9328 inputblock.cn.push({
9330 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9334 if (this.before && typeof(this.before) == 'object') {
9335 this.before = Roo.factory(this.before);
9337 inputblock.cn.push({
9339 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9340 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9344 inputblock.cn.push(input);
9346 if (this.after && typeof(this.after) == 'string') {
9347 inputblock.cn.push({
9349 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9353 if (this.after && typeof(this.after) == 'object') {
9354 this.after = Roo.factory(this.after);
9356 inputblock.cn.push({
9358 cls : 'roo-input-after input-group-append input-group-text input-group-' +
9359 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9363 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9364 inputblock.cls += ' has-feedback';
9365 inputblock.cn.push(feedback);
9370 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9371 tooltip : 'This field is required'
9373 if (Roo.bootstrap.version == 4) {
9376 style : 'display-none'
9379 if (align ==='left' && this.fieldLabel.length) {
9381 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
9388 cls : 'control-label col-form-label',
9389 html : this.fieldLabel
9400 var labelCfg = cfg.cn[1];
9401 var contentCfg = cfg.cn[2];
9403 if(this.indicatorpos == 'right'){
9408 cls : 'control-label col-form-label',
9412 html : this.fieldLabel
9426 labelCfg = cfg.cn[0];
9427 contentCfg = cfg.cn[1];
9431 if(this.labelWidth > 12){
9432 labelCfg.style = "width: " + this.labelWidth + 'px';
9435 if(this.labelWidth < 13 && this.labelmd == 0){
9436 this.labelmd = this.labelWidth;
9439 if(this.labellg > 0){
9440 labelCfg.cls += ' col-lg-' + this.labellg;
9441 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9444 if(this.labelmd > 0){
9445 labelCfg.cls += ' col-md-' + this.labelmd;
9446 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9449 if(this.labelsm > 0){
9450 labelCfg.cls += ' col-sm-' + this.labelsm;
9451 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9454 if(this.labelxs > 0){
9455 labelCfg.cls += ' col-xs-' + this.labelxs;
9456 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9460 } else if ( this.fieldLabel.length) {
9465 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9466 tooltip : 'This field is required'
9470 //cls : 'input-group-addon',
9471 html : this.fieldLabel
9479 if(this.indicatorpos == 'right'){
9484 //cls : 'input-group-addon',
9485 html : this.fieldLabel
9490 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9491 tooltip : 'This field is required'
9511 if (this.parentType === 'Navbar' && this.parent().bar) {
9512 cfg.cls += ' navbar-form';
9515 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9516 // on BS4 we do this only if not form
9517 cfg.cls += ' navbar-form';
9525 * return the real input element.
9527 inputEl: function ()
9529 return this.el.select('input.form-control',true).first();
9532 tooltipEl : function()
9534 return this.inputEl();
9537 indicatorEl : function()
9539 if (Roo.bootstrap.version == 4) {
9540 return false; // not enabled in v4 yet.
9543 var indicator = this.el.select('i.roo-required-indicator',true).first();
9553 setDisabled : function(v)
9555 var i = this.inputEl().dom;
9557 i.removeAttribute('disabled');
9561 i.setAttribute('disabled','true');
9563 initEvents : function()
9566 this.inputEl().on("keydown" , this.fireKey, this);
9567 this.inputEl().on("focus", this.onFocus, this);
9568 this.inputEl().on("blur", this.onBlur, this);
9570 this.inputEl().relayEvent('keyup', this);
9572 this.indicator = this.indicatorEl();
9575 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9578 // reference to original value for reset
9579 this.originalValue = this.getValue();
9580 //Roo.form.TextField.superclass.initEvents.call(this);
9581 if(this.validationEvent == 'keyup'){
9582 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9583 this.inputEl().on('keyup', this.filterValidation, this);
9585 else if(this.validationEvent !== false){
9586 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9589 if(this.selectOnFocus){
9590 this.on("focus", this.preFocus, this);
9593 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9594 this.inputEl().on("keypress", this.filterKeys, this);
9596 this.inputEl().relayEvent('keypress', this);
9599 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9600 this.el.on("click", this.autoSize, this);
9603 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9604 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9607 if (typeof(this.before) == 'object') {
9608 this.before.render(this.el.select('.roo-input-before',true).first());
9610 if (typeof(this.after) == 'object') {
9611 this.after.render(this.el.select('.roo-input-after',true).first());
9614 this.inputEl().on('change', this.onChange, this);
9617 filterValidation : function(e){
9618 if(!e.isNavKeyPress()){
9619 this.validationTask.delay(this.validationDelay);
9623 * Validates the field value
9624 * @return {Boolean} True if the value is valid, else false
9626 validate : function(){
9627 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9628 if(this.disabled || this.validateValue(this.getRawValue())){
9639 * Validates a value according to the field's validation rules and marks the field as invalid
9640 * if the validation fails
9641 * @param {Mixed} value The value to validate
9642 * @return {Boolean} True if the value is valid, else false
9644 validateValue : function(value)
9646 if(this.getVisibilityEl().hasClass('hidden')){
9650 if(value.length < 1) { // if it's blank
9651 if(this.allowBlank){
9657 if(value.length < this.minLength){
9660 if(value.length > this.maxLength){
9664 var vt = Roo.form.VTypes;
9665 if(!vt[this.vtype](value, this)){
9669 if(typeof this.validator == "function"){
9670 var msg = this.validator(value);
9674 if (typeof(msg) == 'string') {
9675 this.invalidText = msg;
9679 if(this.regex && !this.regex.test(value)){
9687 fireKey : function(e){
9688 //Roo.log('field ' + e.getKey());
9689 if(e.isNavKeyPress()){
9690 this.fireEvent("specialkey", this, e);
9693 focus : function (selectText){
9695 this.inputEl().focus();
9696 if(selectText === true){
9697 this.inputEl().dom.select();
9703 onFocus : function(){
9704 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9705 // this.el.addClass(this.focusClass);
9708 this.hasFocus = true;
9709 this.startValue = this.getValue();
9710 this.fireEvent("focus", this);
9714 beforeBlur : Roo.emptyFn,
9718 onBlur : function(){
9720 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9721 //this.el.removeClass(this.focusClass);
9723 this.hasFocus = false;
9724 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9727 var v = this.getValue();
9728 if(String(v) !== String(this.startValue)){
9729 this.fireEvent('change', this, v, this.startValue);
9731 this.fireEvent("blur", this);
9734 onChange : function(e)
9736 var v = this.getValue();
9737 if(String(v) !== String(this.startValue)){
9738 this.fireEvent('change', this, v, this.startValue);
9744 * Resets the current field value to the originally loaded value and clears any validation messages
9747 this.setValue(this.originalValue);
9751 * Returns the name of the field
9752 * @return {Mixed} name The name field
9754 getName: function(){
9758 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9759 * @return {Mixed} value The field value
9761 getValue : function(){
9763 var v = this.inputEl().getValue();
9768 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9769 * @return {Mixed} value The field value
9771 getRawValue : function(){
9772 var v = this.inputEl().getValue();
9778 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9779 * @param {Mixed} value The value to set
9781 setRawValue : function(v){
9782 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9785 selectText : function(start, end){
9786 var v = this.getRawValue();
9788 start = start === undefined ? 0 : start;
9789 end = end === undefined ? v.length : end;
9790 var d = this.inputEl().dom;
9791 if(d.setSelectionRange){
9792 d.setSelectionRange(start, end);
9793 }else if(d.createTextRange){
9794 var range = d.createTextRange();
9795 range.moveStart("character", start);
9796 range.moveEnd("character", v.length-end);
9803 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9804 * @param {Mixed} value The value to set
9806 setValue : function(v){
9809 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9815 processValue : function(value){
9816 if(this.stripCharsRe){
9817 var newValue = value.replace(this.stripCharsRe, '');
9818 if(newValue !== value){
9819 this.setRawValue(newValue);
9826 preFocus : function(){
9828 if(this.selectOnFocus){
9829 this.inputEl().dom.select();
9832 filterKeys : function(e){
9834 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9837 var c = e.getCharCode(), cc = String.fromCharCode(c);
9838 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9841 if(!this.maskRe.test(cc)){
9846 * Clear any invalid styles/messages for this field
9848 clearInvalid : function(){
9850 if(!this.el || this.preventMark){ // not rendered
9855 this.el.removeClass([this.invalidClass, 'is-invalid']);
9857 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9859 var feedback = this.el.select('.form-control-feedback', true).first();
9862 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9868 this.indicator.removeClass('visible');
9869 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9872 this.fireEvent('valid', this);
9876 * Mark this field as valid
9878 markValid : function()
9880 if(!this.el || this.preventMark){ // not rendered...
9884 this.el.removeClass([this.invalidClass, this.validClass]);
9885 this.inputEl().removeClass(['is-valid', 'is-invalid']);
9887 var feedback = this.el.select('.form-control-feedback', true).first();
9890 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9894 this.indicator.removeClass('visible');
9895 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9902 if(this.allowBlank && !this.getRawValue().length){
9905 if (Roo.bootstrap.version == 3) {
9906 this.el.addClass(this.validClass);
9908 this.inputEl().addClass('is-valid');
9911 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9913 var feedback = this.el.select('.form-control-feedback', true).first();
9916 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9917 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9922 this.fireEvent('valid', this);
9926 * Mark this field as invalid
9927 * @param {String} msg The validation message
9929 markInvalid : function(msg)
9931 if(!this.el || this.preventMark){ // not rendered
9935 this.el.removeClass([this.invalidClass, this.validClass]);
9936 this.inputEl().removeClass(['is-valid', 'is-invalid']);
9938 var feedback = this.el.select('.form-control-feedback', true).first();
9941 this.el.select('.form-control-feedback', true).first().removeClass(
9942 [this.invalidFeedbackClass, this.validFeedbackClass]);
9949 if(this.allowBlank && !this.getRawValue().length){
9954 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9955 this.indicator.addClass('visible');
9957 if (Roo.bootstrap.version == 3) {
9958 this.el.addClass(this.invalidClass);
9960 this.inputEl().addClass('is-invalid');
9965 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9967 var feedback = this.el.select('.form-control-feedback', true).first();
9970 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9972 if(this.getValue().length || this.forceFeedback){
9973 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9980 this.fireEvent('invalid', this, msg);
9983 SafariOnKeyDown : function(event)
9985 // this is a workaround for a password hang bug on chrome/ webkit.
9986 if (this.inputEl().dom.type != 'password') {
9990 var isSelectAll = false;
9992 if(this.inputEl().dom.selectionEnd > 0){
9993 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9995 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9996 event.preventDefault();
10001 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10003 event.preventDefault();
10004 // this is very hacky as keydown always get's upper case.
10006 var cc = String.fromCharCode(event.getCharCode());
10007 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
10011 adjustWidth : function(tag, w){
10012 tag = tag.toLowerCase();
10013 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10014 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10015 if(tag == 'input'){
10018 if(tag == 'textarea'){
10021 }else if(Roo.isOpera){
10022 if(tag == 'input'){
10025 if(tag == 'textarea'){
10033 setFieldLabel : function(v)
10035 if(!this.rendered){
10039 if(this.indicatorEl()){
10040 var ar = this.el.select('label > span',true);
10042 if (ar.elements.length) {
10043 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10044 this.fieldLabel = v;
10048 var br = this.el.select('label',true);
10050 if(br.elements.length) {
10051 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10052 this.fieldLabel = v;
10056 Roo.log('Cannot Found any of label > span || label in input');
10060 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10061 this.fieldLabel = v;
10076 * @class Roo.bootstrap.TextArea
10077 * @extends Roo.bootstrap.Input
10078 * Bootstrap TextArea class
10079 * @cfg {Number} cols Specifies the visible width of a text area
10080 * @cfg {Number} rows Specifies the visible number of lines in a text area
10081 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10082 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10083 * @cfg {string} html text
10086 * Create a new TextArea
10087 * @param {Object} config The config object
10090 Roo.bootstrap.TextArea = function(config){
10091 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10095 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
10105 getAutoCreate : function(){
10107 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10113 if(this.inputType != 'hidden'){
10114 cfg.cls = 'form-group' //input-group
10122 value : this.value || '',
10123 html: this.html || '',
10124 cls : 'form-control',
10125 placeholder : this.placeholder || ''
10129 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10130 input.maxLength = this.maxLength;
10134 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10138 input.cols = this.cols;
10141 if (this.readOnly) {
10142 input.readonly = true;
10146 input.name = this.name;
10150 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10154 ['xs','sm','md','lg'].map(function(size){
10155 if (settings[size]) {
10156 cfg.cls += ' col-' + size + '-' + settings[size];
10160 var inputblock = input;
10162 if(this.hasFeedback && !this.allowBlank){
10166 cls: 'glyphicon form-control-feedback'
10170 cls : 'has-feedback',
10179 if (this.before || this.after) {
10182 cls : 'input-group',
10186 inputblock.cn.push({
10188 cls : 'input-group-addon',
10193 inputblock.cn.push(input);
10195 if(this.hasFeedback && !this.allowBlank){
10196 inputblock.cls += ' has-feedback';
10197 inputblock.cn.push(feedback);
10201 inputblock.cn.push({
10203 cls : 'input-group-addon',
10210 if (align ==='left' && this.fieldLabel.length) {
10215 cls : 'control-label',
10216 html : this.fieldLabel
10227 if(this.labelWidth > 12){
10228 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10231 if(this.labelWidth < 13 && this.labelmd == 0){
10232 this.labelmd = this.labelWidth;
10235 if(this.labellg > 0){
10236 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10237 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10240 if(this.labelmd > 0){
10241 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10242 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10245 if(this.labelsm > 0){
10246 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10247 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10250 if(this.labelxs > 0){
10251 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10252 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10255 } else if ( this.fieldLabel.length) {
10260 //cls : 'input-group-addon',
10261 html : this.fieldLabel
10279 if (this.disabled) {
10280 input.disabled=true;
10287 * return the real textarea element.
10289 inputEl: function ()
10291 return this.el.select('textarea.form-control',true).first();
10295 * Clear any invalid styles/messages for this field
10297 clearInvalid : function()
10300 if(!this.el || this.preventMark){ // not rendered
10304 var label = this.el.select('label', true).first();
10305 var icon = this.el.select('i.fa-star', true).first();
10310 this.el.removeClass( this.validClass);
10311 this.inputEl().removeClass('is-invalid');
10313 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10315 var feedback = this.el.select('.form-control-feedback', true).first();
10318 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10323 this.fireEvent('valid', this);
10327 * Mark this field as valid
10329 markValid : function()
10331 if(!this.el || this.preventMark){ // not rendered
10335 this.el.removeClass([this.invalidClass, this.validClass]);
10336 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10338 var feedback = this.el.select('.form-control-feedback', true).first();
10341 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10344 if(this.disabled || this.allowBlank){
10348 var label = this.el.select('label', true).first();
10349 var icon = this.el.select('i.fa-star', true).first();
10354 if (Roo.bootstrap.version == 3) {
10355 this.el.addClass(this.validClass);
10357 this.inputEl().addClass('is-valid');
10361 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10363 var feedback = this.el.select('.form-control-feedback', true).first();
10366 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10367 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10372 this.fireEvent('valid', this);
10376 * Mark this field as invalid
10377 * @param {String} msg The validation message
10379 markInvalid : function(msg)
10381 if(!this.el || this.preventMark){ // not rendered
10385 this.el.removeClass([this.invalidClass, this.validClass]);
10386 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10388 var feedback = this.el.select('.form-control-feedback', true).first();
10391 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10394 if(this.disabled || this.allowBlank){
10398 var label = this.el.select('label', true).first();
10399 var icon = this.el.select('i.fa-star', true).first();
10401 if(!this.getValue().length && label && !icon){
10402 this.el.createChild({
10404 cls : 'text-danger fa fa-lg fa-star',
10405 tooltip : 'This field is required',
10406 style : 'margin-right:5px;'
10410 if (Roo.bootstrap.version == 3) {
10411 this.el.addClass(this.invalidClass);
10413 this.inputEl().addClass('is-invalid');
10416 // fixme ... this may be depricated need to test..
10417 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10419 var feedback = this.el.select('.form-control-feedback', true).first();
10422 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10424 if(this.getValue().length || this.forceFeedback){
10425 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10432 this.fireEvent('invalid', this, msg);
10440 * trigger field - base class for combo..
10445 * @class Roo.bootstrap.TriggerField
10446 * @extends Roo.bootstrap.Input
10447 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10448 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10449 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10450 * for which you can provide a custom implementation. For example:
10452 var trigger = new Roo.bootstrap.TriggerField();
10453 trigger.onTriggerClick = myTriggerFn;
10454 trigger.applyTo('my-field');
10457 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10458 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10459 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10460 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10461 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
10464 * Create a new TriggerField.
10465 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10466 * to the base TextField)
10468 Roo.bootstrap.TriggerField = function(config){
10469 this.mimicing = false;
10470 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10473 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10475 * @cfg {String} triggerClass A CSS class to apply to the trigger
10478 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10483 * @cfg {Boolean} removable (true|false) special filter default false
10487 /** @cfg {Boolean} grow @hide */
10488 /** @cfg {Number} growMin @hide */
10489 /** @cfg {Number} growMax @hide */
10495 autoSize: Roo.emptyFn,
10499 deferHeight : true,
10502 actionMode : 'wrap',
10507 getAutoCreate : function(){
10509 var align = this.labelAlign || this.parentLabelAlign();
10514 cls: 'form-group' //input-group
10521 type : this.inputType,
10522 cls : 'form-control',
10523 autocomplete: 'new-password',
10524 placeholder : this.placeholder || ''
10528 input.name = this.name;
10531 input.cls += ' input-' + this.size;
10534 if (this.disabled) {
10535 input.disabled=true;
10538 var inputblock = input;
10540 if(this.hasFeedback && !this.allowBlank){
10544 cls: 'glyphicon form-control-feedback'
10547 if(this.removable && !this.editable && !this.tickable){
10549 cls : 'has-feedback',
10555 cls : 'roo-combo-removable-btn close'
10562 cls : 'has-feedback',
10571 if(this.removable && !this.editable && !this.tickable){
10573 cls : 'roo-removable',
10579 cls : 'roo-combo-removable-btn close'
10586 if (this.before || this.after) {
10589 cls : 'input-group',
10593 inputblock.cn.push({
10595 cls : 'input-group-addon input-group-prepend input-group-text',
10600 inputblock.cn.push(input);
10602 if(this.hasFeedback && !this.allowBlank){
10603 inputblock.cls += ' has-feedback';
10604 inputblock.cn.push(feedback);
10608 inputblock.cn.push({
10610 cls : 'input-group-addon input-group-append input-group-text',
10619 var ibwrap = inputblock;
10624 cls: 'roo-select2-choices',
10628 cls: 'roo-select2-search-field',
10640 cls: 'roo-select2-container input-group',
10645 cls: 'form-hidden-field'
10651 if(!this.multiple && this.showToggleBtn){
10657 if (this.caret != false) {
10660 cls: 'fa fa-' + this.caret
10667 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10669 Roo.bootstrap.version == 3 ? caret : '',
10672 cls: 'combobox-clear',
10686 combobox.cls += ' roo-select2-container-multi';
10690 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10691 tooltip : 'This field is required'
10693 if (Roo.bootstrap.version == 4) {
10696 style : 'display:none'
10701 if (align ==='left' && this.fieldLabel.length) {
10703 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10710 cls : 'control-label',
10711 html : this.fieldLabel
10723 var labelCfg = cfg.cn[1];
10724 var contentCfg = cfg.cn[2];
10726 if(this.indicatorpos == 'right'){
10731 cls : 'control-label',
10735 html : this.fieldLabel
10749 labelCfg = cfg.cn[0];
10750 contentCfg = cfg.cn[1];
10753 if(this.labelWidth > 12){
10754 labelCfg.style = "width: " + this.labelWidth + 'px';
10757 if(this.labelWidth < 13 && this.labelmd == 0){
10758 this.labelmd = this.labelWidth;
10761 if(this.labellg > 0){
10762 labelCfg.cls += ' col-lg-' + this.labellg;
10763 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10766 if(this.labelmd > 0){
10767 labelCfg.cls += ' col-md-' + this.labelmd;
10768 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10771 if(this.labelsm > 0){
10772 labelCfg.cls += ' col-sm-' + this.labelsm;
10773 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10776 if(this.labelxs > 0){
10777 labelCfg.cls += ' col-xs-' + this.labelxs;
10778 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10781 } else if ( this.fieldLabel.length) {
10782 // Roo.log(" label");
10787 //cls : 'input-group-addon',
10788 html : this.fieldLabel
10796 if(this.indicatorpos == 'right'){
10804 html : this.fieldLabel
10818 // Roo.log(" no label && no align");
10825 ['xs','sm','md','lg'].map(function(size){
10826 if (settings[size]) {
10827 cfg.cls += ' col-' + size + '-' + settings[size];
10838 onResize : function(w, h){
10839 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10840 // if(typeof w == 'number'){
10841 // var x = w - this.trigger.getWidth();
10842 // this.inputEl().setWidth(this.adjustWidth('input', x));
10843 // this.trigger.setStyle('left', x+'px');
10848 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10851 getResizeEl : function(){
10852 return this.inputEl();
10856 getPositionEl : function(){
10857 return this.inputEl();
10861 alignErrorIcon : function(){
10862 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10866 initEvents : function(){
10870 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10871 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10872 if(!this.multiple && this.showToggleBtn){
10873 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10874 if(this.hideTrigger){
10875 this.trigger.setDisplayed(false);
10877 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10881 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10884 if(this.removable && !this.editable && !this.tickable){
10885 var close = this.closeTriggerEl();
10888 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10889 close.on('click', this.removeBtnClick, this, close);
10893 //this.trigger.addClassOnOver('x-form-trigger-over');
10894 //this.trigger.addClassOnClick('x-form-trigger-click');
10897 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10901 closeTriggerEl : function()
10903 var close = this.el.select('.roo-combo-removable-btn', true).first();
10904 return close ? close : false;
10907 removeBtnClick : function(e, h, el)
10909 e.preventDefault();
10911 if(this.fireEvent("remove", this) !== false){
10913 this.fireEvent("afterremove", this)
10917 createList : function()
10919 this.list = Roo.get(document.body).createChild({
10920 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10921 cls: 'typeahead typeahead-long dropdown-menu',
10922 style: 'display:none'
10925 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10930 initTrigger : function(){
10935 onDestroy : function(){
10937 this.trigger.removeAllListeners();
10938 // this.trigger.remove();
10941 // this.wrap.remove();
10943 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10947 onFocus : function(){
10948 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10950 if(!this.mimicing){
10951 this.wrap.addClass('x-trigger-wrap-focus');
10952 this.mimicing = true;
10953 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10954 if(this.monitorTab){
10955 this.el.on("keydown", this.checkTab, this);
10962 checkTab : function(e){
10963 if(e.getKey() == e.TAB){
10964 this.triggerBlur();
10969 onBlur : function(){
10974 mimicBlur : function(e, t){
10976 if(!this.wrap.contains(t) && this.validateBlur()){
10977 this.triggerBlur();
10983 triggerBlur : function(){
10984 this.mimicing = false;
10985 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10986 if(this.monitorTab){
10987 this.el.un("keydown", this.checkTab, this);
10989 //this.wrap.removeClass('x-trigger-wrap-focus');
10990 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10994 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10995 validateBlur : function(e, t){
11000 onDisable : function(){
11001 this.inputEl().dom.disabled = true;
11002 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11004 // this.wrap.addClass('x-item-disabled');
11009 onEnable : function(){
11010 this.inputEl().dom.disabled = false;
11011 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11013 // this.el.removeClass('x-item-disabled');
11018 onShow : function(){
11019 var ae = this.getActionEl();
11022 ae.dom.style.display = '';
11023 ae.dom.style.visibility = 'visible';
11029 onHide : function(){
11030 var ae = this.getActionEl();
11031 ae.dom.style.display = 'none';
11035 * The function that should handle the trigger's click event. This method does nothing by default until overridden
11036 * by an implementing function.
11038 * @param {EventObject} e
11040 onTriggerClick : Roo.emptyFn
11044 * Ext JS Library 1.1.1
11045 * Copyright(c) 2006-2007, Ext JS, LLC.
11047 * Originally Released Under LGPL - original licence link has changed is not relivant.
11050 * <script type="text/javascript">
11055 * @class Roo.data.SortTypes
11057 * Defines the default sorting (casting?) comparison functions used when sorting data.
11059 Roo.data.SortTypes = {
11061 * Default sort that does nothing
11062 * @param {Mixed} s The value being converted
11063 * @return {Mixed} The comparison value
11065 none : function(s){
11070 * The regular expression used to strip tags
11074 stripTagsRE : /<\/?[^>]+>/gi,
11077 * Strips all HTML tags to sort on text only
11078 * @param {Mixed} s The value being converted
11079 * @return {String} The comparison value
11081 asText : function(s){
11082 return String(s).replace(this.stripTagsRE, "");
11086 * Strips all HTML tags to sort on text only - Case insensitive
11087 * @param {Mixed} s The value being converted
11088 * @return {String} The comparison value
11090 asUCText : function(s){
11091 return String(s).toUpperCase().replace(this.stripTagsRE, "");
11095 * Case insensitive string
11096 * @param {Mixed} s The value being converted
11097 * @return {String} The comparison value
11099 asUCString : function(s) {
11100 return String(s).toUpperCase();
11105 * @param {Mixed} s The value being converted
11106 * @return {Number} The comparison value
11108 asDate : function(s) {
11112 if(s instanceof Date){
11113 return s.getTime();
11115 return Date.parse(String(s));
11120 * @param {Mixed} s The value being converted
11121 * @return {Float} The comparison value
11123 asFloat : function(s) {
11124 var val = parseFloat(String(s).replace(/,/g, ""));
11133 * @param {Mixed} s The value being converted
11134 * @return {Number} The comparison value
11136 asInt : function(s) {
11137 var val = parseInt(String(s).replace(/,/g, ""));
11145 * Ext JS Library 1.1.1
11146 * Copyright(c) 2006-2007, Ext JS, LLC.
11148 * Originally Released Under LGPL - original licence link has changed is not relivant.
11151 * <script type="text/javascript">
11155 * @class Roo.data.Record
11156 * Instances of this class encapsulate both record <em>definition</em> information, and record
11157 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11158 * to access Records cached in an {@link Roo.data.Store} object.<br>
11160 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11161 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11164 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11166 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11167 * {@link #create}. The parameters are the same.
11168 * @param {Array} data An associative Array of data values keyed by the field name.
11169 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11170 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11171 * not specified an integer id is generated.
11173 Roo.data.Record = function(data, id){
11174 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11179 * Generate a constructor for a specific record layout.
11180 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11181 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11182 * Each field definition object may contain the following properties: <ul>
11183 * <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,
11184 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11185 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11186 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11187 * is being used, then this is a string containing the javascript expression to reference the data relative to
11188 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11189 * to the data item relative to the record element. If the mapping expression is the same as the field name,
11190 * this may be omitted.</p></li>
11191 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11192 * <ul><li>auto (Default, implies no conversion)</li>
11197 * <li>date</li></ul></p></li>
11198 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11199 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11200 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11201 * by the Reader into an object that will be stored in the Record. It is passed the
11202 * following parameters:<ul>
11203 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11205 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11207 * <br>usage:<br><pre><code>
11208 var TopicRecord = Roo.data.Record.create(
11209 {name: 'title', mapping: 'topic_title'},
11210 {name: 'author', mapping: 'username'},
11211 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11212 {name: 'lastPost', mapping: 'post_time', type: 'date'},
11213 {name: 'lastPoster', mapping: 'user2'},
11214 {name: 'excerpt', mapping: 'post_text'}
11217 var myNewRecord = new TopicRecord({
11218 title: 'Do my job please',
11221 lastPost: new Date(),
11222 lastPoster: 'Animal',
11223 excerpt: 'No way dude!'
11225 myStore.add(myNewRecord);
11230 Roo.data.Record.create = function(o){
11231 var f = function(){
11232 f.superclass.constructor.apply(this, arguments);
11234 Roo.extend(f, Roo.data.Record);
11235 var p = f.prototype;
11236 p.fields = new Roo.util.MixedCollection(false, function(field){
11239 for(var i = 0, len = o.length; i < len; i++){
11240 p.fields.add(new Roo.data.Field(o[i]));
11242 f.getField = function(name){
11243 return p.fields.get(name);
11248 Roo.data.Record.AUTO_ID = 1000;
11249 Roo.data.Record.EDIT = 'edit';
11250 Roo.data.Record.REJECT = 'reject';
11251 Roo.data.Record.COMMIT = 'commit';
11253 Roo.data.Record.prototype = {
11255 * Readonly flag - true if this record has been modified.
11264 join : function(store){
11265 this.store = store;
11269 * Set the named field to the specified value.
11270 * @param {String} name The name of the field to set.
11271 * @param {Object} value The value to set the field to.
11273 set : function(name, value){
11274 if(this.data[name] == value){
11278 if(!this.modified){
11279 this.modified = {};
11281 if(typeof this.modified[name] == 'undefined'){
11282 this.modified[name] = this.data[name];
11284 this.data[name] = value;
11285 if(!this.editing && this.store){
11286 this.store.afterEdit(this);
11291 * Get the value of the named field.
11292 * @param {String} name The name of the field to get the value of.
11293 * @return {Object} The value of the field.
11295 get : function(name){
11296 return this.data[name];
11300 beginEdit : function(){
11301 this.editing = true;
11302 this.modified = {};
11306 cancelEdit : function(){
11307 this.editing = false;
11308 delete this.modified;
11312 endEdit : function(){
11313 this.editing = false;
11314 if(this.dirty && this.store){
11315 this.store.afterEdit(this);
11320 * Usually called by the {@link Roo.data.Store} which owns the Record.
11321 * Rejects all changes made to the Record since either creation, or the last commit operation.
11322 * Modified fields are reverted to their original values.
11324 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11325 * of reject operations.
11327 reject : function(){
11328 var m = this.modified;
11330 if(typeof m[n] != "function"){
11331 this.data[n] = m[n];
11334 this.dirty = false;
11335 delete this.modified;
11336 this.editing = false;
11338 this.store.afterReject(this);
11343 * Usually called by the {@link Roo.data.Store} which owns the Record.
11344 * Commits all changes made to the Record since either creation, or the last commit operation.
11346 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11347 * of commit operations.
11349 commit : function(){
11350 this.dirty = false;
11351 delete this.modified;
11352 this.editing = false;
11354 this.store.afterCommit(this);
11359 hasError : function(){
11360 return this.error != null;
11364 clearError : function(){
11369 * Creates a copy of this record.
11370 * @param {String} id (optional) A new record id if you don't want to use this record's id
11373 copy : function(newId) {
11374 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11378 * Ext JS Library 1.1.1
11379 * Copyright(c) 2006-2007, Ext JS, LLC.
11381 * Originally Released Under LGPL - original licence link has changed is not relivant.
11384 * <script type="text/javascript">
11390 * @class Roo.data.Store
11391 * @extends Roo.util.Observable
11392 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11393 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11395 * 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
11396 * has no knowledge of the format of the data returned by the Proxy.<br>
11398 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11399 * instances from the data object. These records are cached and made available through accessor functions.
11401 * Creates a new Store.
11402 * @param {Object} config A config object containing the objects needed for the Store to access data,
11403 * and read the data into Records.
11405 Roo.data.Store = function(config){
11406 this.data = new Roo.util.MixedCollection(false);
11407 this.data.getKey = function(o){
11410 this.baseParams = {};
11412 this.paramNames = {
11417 "multisort" : "_multisort"
11420 if(config && config.data){
11421 this.inlineData = config.data;
11422 delete config.data;
11425 Roo.apply(this, config);
11427 if(this.reader){ // reader passed
11428 this.reader = Roo.factory(this.reader, Roo.data);
11429 this.reader.xmodule = this.xmodule || false;
11430 if(!this.recordType){
11431 this.recordType = this.reader.recordType;
11433 if(this.reader.onMetaChange){
11434 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11438 if(this.recordType){
11439 this.fields = this.recordType.prototype.fields;
11441 this.modified = [];
11445 * @event datachanged
11446 * Fires when the data cache has changed, and a widget which is using this Store
11447 * as a Record cache should refresh its view.
11448 * @param {Store} this
11450 datachanged : true,
11452 * @event metachange
11453 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11454 * @param {Store} this
11455 * @param {Object} meta The JSON metadata
11460 * Fires when Records have been added to the Store
11461 * @param {Store} this
11462 * @param {Roo.data.Record[]} records The array of Records added
11463 * @param {Number} index The index at which the record(s) were added
11468 * Fires when a Record has been removed from the Store
11469 * @param {Store} this
11470 * @param {Roo.data.Record} record The Record that was removed
11471 * @param {Number} index The index at which the record was removed
11476 * Fires when a Record has been updated
11477 * @param {Store} this
11478 * @param {Roo.data.Record} record The Record that was updated
11479 * @param {String} operation The update operation being performed. Value may be one of:
11481 Roo.data.Record.EDIT
11482 Roo.data.Record.REJECT
11483 Roo.data.Record.COMMIT
11489 * Fires when the data cache has been cleared.
11490 * @param {Store} this
11494 * @event beforeload
11495 * Fires before a request is made for a new data object. If the beforeload handler returns false
11496 * the load action will be canceled.
11497 * @param {Store} this
11498 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11502 * @event beforeloadadd
11503 * Fires after a new set of Records has been loaded.
11504 * @param {Store} this
11505 * @param {Roo.data.Record[]} records The Records that were loaded
11506 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11508 beforeloadadd : true,
11511 * Fires after a new set of Records has been loaded, before they are added to the store.
11512 * @param {Store} this
11513 * @param {Roo.data.Record[]} records The Records that were loaded
11514 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11515 * @params {Object} return from reader
11519 * @event loadexception
11520 * Fires if an exception occurs in the Proxy during loading.
11521 * Called with the signature of the Proxy's "loadexception" event.
11522 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11525 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11526 * @param {Object} load options
11527 * @param {Object} jsonData from your request (normally this contains the Exception)
11529 loadexception : true
11533 this.proxy = Roo.factory(this.proxy, Roo.data);
11534 this.proxy.xmodule = this.xmodule || false;
11535 this.relayEvents(this.proxy, ["loadexception"]);
11537 this.sortToggle = {};
11538 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11540 Roo.data.Store.superclass.constructor.call(this);
11542 if(this.inlineData){
11543 this.loadData(this.inlineData);
11544 delete this.inlineData;
11548 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11550 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11551 * without a remote query - used by combo/forms at present.
11555 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11558 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11561 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11562 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11565 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11566 * on any HTTP request
11569 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11572 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11576 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11577 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11579 remoteSort : false,
11582 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11583 * loaded or when a record is removed. (defaults to false).
11585 pruneModifiedRecords : false,
11588 lastOptions : null,
11591 * Add Records to the Store and fires the add event.
11592 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11594 add : function(records){
11595 records = [].concat(records);
11596 for(var i = 0, len = records.length; i < len; i++){
11597 records[i].join(this);
11599 var index = this.data.length;
11600 this.data.addAll(records);
11601 this.fireEvent("add", this, records, index);
11605 * Remove a Record from the Store and fires the remove event.
11606 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11608 remove : function(record){
11609 var index = this.data.indexOf(record);
11610 this.data.removeAt(index);
11612 if(this.pruneModifiedRecords){
11613 this.modified.remove(record);
11615 this.fireEvent("remove", this, record, index);
11619 * Remove all Records from the Store and fires the clear event.
11621 removeAll : function(){
11623 if(this.pruneModifiedRecords){
11624 this.modified = [];
11626 this.fireEvent("clear", this);
11630 * Inserts Records to the Store at the given index and fires the add event.
11631 * @param {Number} index The start index at which to insert the passed Records.
11632 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11634 insert : function(index, records){
11635 records = [].concat(records);
11636 for(var i = 0, len = records.length; i < len; i++){
11637 this.data.insert(index, records[i]);
11638 records[i].join(this);
11640 this.fireEvent("add", this, records, index);
11644 * Get the index within the cache of the passed Record.
11645 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11646 * @return {Number} The index of the passed Record. Returns -1 if not found.
11648 indexOf : function(record){
11649 return this.data.indexOf(record);
11653 * Get the index within the cache of the Record with the passed id.
11654 * @param {String} id The id of the Record to find.
11655 * @return {Number} The index of the Record. Returns -1 if not found.
11657 indexOfId : function(id){
11658 return this.data.indexOfKey(id);
11662 * Get the Record with the specified id.
11663 * @param {String} id The id of the Record to find.
11664 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11666 getById : function(id){
11667 return this.data.key(id);
11671 * Get the Record at the specified index.
11672 * @param {Number} index The index of the Record to find.
11673 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11675 getAt : function(index){
11676 return this.data.itemAt(index);
11680 * Returns a range of Records between specified indices.
11681 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11682 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11683 * @return {Roo.data.Record[]} An array of Records
11685 getRange : function(start, end){
11686 return this.data.getRange(start, end);
11690 storeOptions : function(o){
11691 o = Roo.apply({}, o);
11694 this.lastOptions = o;
11698 * Loads the Record cache from the configured Proxy using the configured Reader.
11700 * If using remote paging, then the first load call must specify the <em>start</em>
11701 * and <em>limit</em> properties in the options.params property to establish the initial
11702 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11704 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11705 * and this call will return before the new data has been loaded. Perform any post-processing
11706 * in a callback function, or in a "load" event handler.</strong>
11708 * @param {Object} options An object containing properties which control loading options:<ul>
11709 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11710 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11711 * passed the following arguments:<ul>
11712 * <li>r : Roo.data.Record[]</li>
11713 * <li>options: Options object from the load call</li>
11714 * <li>success: Boolean success indicator</li></ul></li>
11715 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11716 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11719 load : function(options){
11720 options = options || {};
11721 if(this.fireEvent("beforeload", this, options) !== false){
11722 this.storeOptions(options);
11723 var p = Roo.apply(options.params || {}, this.baseParams);
11724 // if meta was not loaded from remote source.. try requesting it.
11725 if (!this.reader.metaFromRemote) {
11726 p._requestMeta = 1;
11728 if(this.sortInfo && this.remoteSort){
11729 var pn = this.paramNames;
11730 p[pn["sort"]] = this.sortInfo.field;
11731 p[pn["dir"]] = this.sortInfo.direction;
11733 if (this.multiSort) {
11734 var pn = this.paramNames;
11735 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11738 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11743 * Reloads the Record cache from the configured Proxy using the configured Reader and
11744 * the options from the last load operation performed.
11745 * @param {Object} options (optional) An object containing properties which may override the options
11746 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11747 * the most recently used options are reused).
11749 reload : function(options){
11750 this.load(Roo.applyIf(options||{}, this.lastOptions));
11754 // Called as a callback by the Reader during a load operation.
11755 loadRecords : function(o, options, success){
11756 if(!o || success === false){
11757 if(success !== false){
11758 this.fireEvent("load", this, [], options, o);
11760 if(options.callback){
11761 options.callback.call(options.scope || this, [], options, false);
11765 // if data returned failure - throw an exception.
11766 if (o.success === false) {
11767 // show a message if no listener is registered.
11768 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11769 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11771 // loadmask wil be hooked into this..
11772 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11775 var r = o.records, t = o.totalRecords || r.length;
11777 this.fireEvent("beforeloadadd", this, r, options, o);
11779 if(!options || options.add !== true){
11780 if(this.pruneModifiedRecords){
11781 this.modified = [];
11783 for(var i = 0, len = r.length; i < len; i++){
11787 this.data = this.snapshot;
11788 delete this.snapshot;
11791 this.data.addAll(r);
11792 this.totalLength = t;
11794 this.fireEvent("datachanged", this);
11796 this.totalLength = Math.max(t, this.data.length+r.length);
11800 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11802 var e = new Roo.data.Record({});
11804 e.set(this.parent.displayField, this.parent.emptyTitle);
11805 e.set(this.parent.valueField, '');
11810 this.fireEvent("load", this, r, options, o);
11811 if(options.callback){
11812 options.callback.call(options.scope || this, r, options, true);
11818 * Loads data from a passed data block. A Reader which understands the format of the data
11819 * must have been configured in the constructor.
11820 * @param {Object} data The data block from which to read the Records. The format of the data expected
11821 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11822 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11824 loadData : function(o, append){
11825 var r = this.reader.readRecords(o);
11826 this.loadRecords(r, {add: append}, true);
11830 * Gets the number of cached records.
11832 * <em>If using paging, this may not be the total size of the dataset. If the data object
11833 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11834 * the data set size</em>
11836 getCount : function(){
11837 return this.data.length || 0;
11841 * Gets the total number of records in the dataset as returned by the server.
11843 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11844 * the dataset size</em>
11846 getTotalCount : function(){
11847 return this.totalLength || 0;
11851 * Returns the sort state of the Store as an object with two properties:
11853 field {String} The name of the field by which the Records are sorted
11854 direction {String} The sort order, "ASC" or "DESC"
11857 getSortState : function(){
11858 return this.sortInfo;
11862 applySort : function(){
11863 if(this.sortInfo && !this.remoteSort){
11864 var s = this.sortInfo, f = s.field;
11865 var st = this.fields.get(f).sortType;
11866 var fn = function(r1, r2){
11867 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11868 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11870 this.data.sort(s.direction, fn);
11871 if(this.snapshot && this.snapshot != this.data){
11872 this.snapshot.sort(s.direction, fn);
11878 * Sets the default sort column and order to be used by the next load operation.
11879 * @param {String} fieldName The name of the field to sort by.
11880 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11882 setDefaultSort : function(field, dir){
11883 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11887 * Sort the Records.
11888 * If remote sorting is used, the sort is performed on the server, and the cache is
11889 * reloaded. If local sorting is used, the cache is sorted internally.
11890 * @param {String} fieldName The name of the field to sort by.
11891 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11893 sort : function(fieldName, dir){
11894 var f = this.fields.get(fieldName);
11896 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11898 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11899 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11904 this.sortToggle[f.name] = dir;
11905 this.sortInfo = {field: f.name, direction: dir};
11906 if(!this.remoteSort){
11908 this.fireEvent("datachanged", this);
11910 this.load(this.lastOptions);
11915 * Calls the specified function for each of the Records in the cache.
11916 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11917 * Returning <em>false</em> aborts and exits the iteration.
11918 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11920 each : function(fn, scope){
11921 this.data.each(fn, scope);
11925 * Gets all records modified since the last commit. Modified records are persisted across load operations
11926 * (e.g., during paging).
11927 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11929 getModifiedRecords : function(){
11930 return this.modified;
11934 createFilterFn : function(property, value, anyMatch){
11935 if(!value.exec){ // not a regex
11936 value = String(value);
11937 if(value.length == 0){
11940 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11942 return function(r){
11943 return value.test(r.data[property]);
11948 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11949 * @param {String} property A field on your records
11950 * @param {Number} start The record index to start at (defaults to 0)
11951 * @param {Number} end The last record index to include (defaults to length - 1)
11952 * @return {Number} The sum
11954 sum : function(property, start, end){
11955 var rs = this.data.items, v = 0;
11956 start = start || 0;
11957 end = (end || end === 0) ? end : rs.length-1;
11959 for(var i = start; i <= end; i++){
11960 v += (rs[i].data[property] || 0);
11966 * Filter the records by a specified property.
11967 * @param {String} field A field on your records
11968 * @param {String/RegExp} value Either a string that the field
11969 * should start with or a RegExp to test against the field
11970 * @param {Boolean} anyMatch True to match any part not just the beginning
11972 filter : function(property, value, anyMatch){
11973 var fn = this.createFilterFn(property, value, anyMatch);
11974 return fn ? this.filterBy(fn) : this.clearFilter();
11978 * Filter by a function. The specified function will be called with each
11979 * record in this data source. If the function returns true the record is included,
11980 * otherwise it is filtered.
11981 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11982 * @param {Object} scope (optional) The scope of the function (defaults to this)
11984 filterBy : function(fn, scope){
11985 this.snapshot = this.snapshot || this.data;
11986 this.data = this.queryBy(fn, scope||this);
11987 this.fireEvent("datachanged", this);
11991 * Query the records by a specified property.
11992 * @param {String} field A field on your records
11993 * @param {String/RegExp} value Either a string that the field
11994 * should start with or a RegExp to test against the field
11995 * @param {Boolean} anyMatch True to match any part not just the beginning
11996 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11998 query : function(property, value, anyMatch){
11999 var fn = this.createFilterFn(property, value, anyMatch);
12000 return fn ? this.queryBy(fn) : this.data.clone();
12004 * Query by a function. The specified function will be called with each
12005 * record in this data source. If the function returns true the record is included
12007 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12008 * @param {Object} scope (optional) The scope of the function (defaults to this)
12009 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12011 queryBy : function(fn, scope){
12012 var data = this.snapshot || this.data;
12013 return data.filterBy(fn, scope||this);
12017 * Collects unique values for a particular dataIndex from this store.
12018 * @param {String} dataIndex The property to collect
12019 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12020 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12021 * @return {Array} An array of the unique values
12023 collect : function(dataIndex, allowNull, bypassFilter){
12024 var d = (bypassFilter === true && this.snapshot) ?
12025 this.snapshot.items : this.data.items;
12026 var v, sv, r = [], l = {};
12027 for(var i = 0, len = d.length; i < len; i++){
12028 v = d[i].data[dataIndex];
12030 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12039 * Revert to a view of the Record cache with no filtering applied.
12040 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12042 clearFilter : function(suppressEvent){
12043 if(this.snapshot && this.snapshot != this.data){
12044 this.data = this.snapshot;
12045 delete this.snapshot;
12046 if(suppressEvent !== true){
12047 this.fireEvent("datachanged", this);
12053 afterEdit : function(record){
12054 if(this.modified.indexOf(record) == -1){
12055 this.modified.push(record);
12057 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12061 afterReject : function(record){
12062 this.modified.remove(record);
12063 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12067 afterCommit : function(record){
12068 this.modified.remove(record);
12069 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12073 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12074 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12076 commitChanges : function(){
12077 var m = this.modified.slice(0);
12078 this.modified = [];
12079 for(var i = 0, len = m.length; i < len; i++){
12085 * Cancel outstanding changes on all changed records.
12087 rejectChanges : function(){
12088 var m = this.modified.slice(0);
12089 this.modified = [];
12090 for(var i = 0, len = m.length; i < len; i++){
12095 onMetaChange : function(meta, rtype, o){
12096 this.recordType = rtype;
12097 this.fields = rtype.prototype.fields;
12098 delete this.snapshot;
12099 this.sortInfo = meta.sortInfo || this.sortInfo;
12100 this.modified = [];
12101 this.fireEvent('metachange', this, this.reader.meta);
12104 moveIndex : function(data, type)
12106 var index = this.indexOf(data);
12108 var newIndex = index + type;
12112 this.insert(newIndex, data);
12117 * Ext JS Library 1.1.1
12118 * Copyright(c) 2006-2007, Ext JS, LLC.
12120 * Originally Released Under LGPL - original licence link has changed is not relivant.
12123 * <script type="text/javascript">
12127 * @class Roo.data.SimpleStore
12128 * @extends Roo.data.Store
12129 * Small helper class to make creating Stores from Array data easier.
12130 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12131 * @cfg {Array} fields An array of field definition objects, or field name strings.
12132 * @cfg {Object} an existing reader (eg. copied from another store)
12133 * @cfg {Array} data The multi-dimensional array of data
12135 * @param {Object} config
12137 Roo.data.SimpleStore = function(config)
12139 Roo.data.SimpleStore.superclass.constructor.call(this, {
12141 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
12144 Roo.data.Record.create(config.fields)
12146 proxy : new Roo.data.MemoryProxy(config.data)
12150 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12152 * Ext JS Library 1.1.1
12153 * Copyright(c) 2006-2007, Ext JS, LLC.
12155 * Originally Released Under LGPL - original licence link has changed is not relivant.
12158 * <script type="text/javascript">
12163 * @extends Roo.data.Store
12164 * @class Roo.data.JsonStore
12165 * Small helper class to make creating Stores for JSON data easier. <br/>
12167 var store = new Roo.data.JsonStore({
12168 url: 'get-images.php',
12170 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12173 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12174 * JsonReader and HttpProxy (unless inline data is provided).</b>
12175 * @cfg {Array} fields An array of field definition objects, or field name strings.
12177 * @param {Object} config
12179 Roo.data.JsonStore = function(c){
12180 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12181 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12182 reader: new Roo.data.JsonReader(c, c.fields)
12185 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12187 * Ext JS Library 1.1.1
12188 * Copyright(c) 2006-2007, Ext JS, LLC.
12190 * Originally Released Under LGPL - original licence link has changed is not relivant.
12193 * <script type="text/javascript">
12197 Roo.data.Field = function(config){
12198 if(typeof config == "string"){
12199 config = {name: config};
12201 Roo.apply(this, config);
12204 this.type = "auto";
12207 var st = Roo.data.SortTypes;
12208 // named sortTypes are supported, here we look them up
12209 if(typeof this.sortType == "string"){
12210 this.sortType = st[this.sortType];
12213 // set default sortType for strings and dates
12214 if(!this.sortType){
12217 this.sortType = st.asUCString;
12220 this.sortType = st.asDate;
12223 this.sortType = st.none;
12228 var stripRe = /[\$,%]/g;
12230 // prebuilt conversion function for this field, instead of
12231 // switching every time we're reading a value
12233 var cv, dateFormat = this.dateFormat;
12238 cv = function(v){ return v; };
12241 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12245 return v !== undefined && v !== null && v !== '' ?
12246 parseInt(String(v).replace(stripRe, ""), 10) : '';
12251 return v !== undefined && v !== null && v !== '' ?
12252 parseFloat(String(v).replace(stripRe, ""), 10) : '';
12257 cv = function(v){ return v === true || v === "true" || v == 1; };
12264 if(v instanceof Date){
12268 if(dateFormat == "timestamp"){
12269 return new Date(v*1000);
12271 return Date.parseDate(v, dateFormat);
12273 var parsed = Date.parse(v);
12274 return parsed ? new Date(parsed) : null;
12283 Roo.data.Field.prototype = {
12291 * Ext JS Library 1.1.1
12292 * Copyright(c) 2006-2007, Ext JS, LLC.
12294 * Originally Released Under LGPL - original licence link has changed is not relivant.
12297 * <script type="text/javascript">
12300 // Base class for reading structured data from a data source. This class is intended to be
12301 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12304 * @class Roo.data.DataReader
12305 * Base class for reading structured data from a data source. This class is intended to be
12306 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12309 Roo.data.DataReader = function(meta, recordType){
12313 this.recordType = recordType instanceof Array ?
12314 Roo.data.Record.create(recordType) : recordType;
12317 Roo.data.DataReader.prototype = {
12320 readerType : 'Data',
12322 * Create an empty record
12323 * @param {Object} data (optional) - overlay some values
12324 * @return {Roo.data.Record} record created.
12326 newRow : function(d) {
12328 this.recordType.prototype.fields.each(function(c) {
12330 case 'int' : da[c.name] = 0; break;
12331 case 'date' : da[c.name] = new Date(); break;
12332 case 'float' : da[c.name] = 0.0; break;
12333 case 'boolean' : da[c.name] = false; break;
12334 default : da[c.name] = ""; break;
12338 return new this.recordType(Roo.apply(da, d));
12344 * Ext JS Library 1.1.1
12345 * Copyright(c) 2006-2007, Ext JS, LLC.
12347 * Originally Released Under LGPL - original licence link has changed is not relivant.
12350 * <script type="text/javascript">
12354 * @class Roo.data.DataProxy
12355 * @extends Roo.data.Observable
12356 * This class is an abstract base class for implementations which provide retrieval of
12357 * unformatted data objects.<br>
12359 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12360 * (of the appropriate type which knows how to parse the data object) to provide a block of
12361 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12363 * Custom implementations must implement the load method as described in
12364 * {@link Roo.data.HttpProxy#load}.
12366 Roo.data.DataProxy = function(){
12369 * @event beforeload
12370 * Fires before a network request is made to retrieve a data object.
12371 * @param {Object} This DataProxy object.
12372 * @param {Object} params The params parameter to the load function.
12377 * Fires before the load method's callback is called.
12378 * @param {Object} This DataProxy object.
12379 * @param {Object} o The data object.
12380 * @param {Object} arg The callback argument object passed to the load function.
12384 * @event loadexception
12385 * Fires if an Exception occurs during data retrieval.
12386 * @param {Object} This DataProxy object.
12387 * @param {Object} o The data object.
12388 * @param {Object} arg The callback argument object passed to the load function.
12389 * @param {Object} e The Exception.
12391 loadexception : true
12393 Roo.data.DataProxy.superclass.constructor.call(this);
12396 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12399 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12403 * Ext JS Library 1.1.1
12404 * Copyright(c) 2006-2007, Ext JS, LLC.
12406 * Originally Released Under LGPL - original licence link has changed is not relivant.
12409 * <script type="text/javascript">
12412 * @class Roo.data.MemoryProxy
12413 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12414 * to the Reader when its load method is called.
12416 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12418 Roo.data.MemoryProxy = function(data){
12422 Roo.data.MemoryProxy.superclass.constructor.call(this);
12426 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12429 * Load data from the requested source (in this case an in-memory
12430 * data object passed to the constructor), read the data object into
12431 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12432 * process that block using the passed callback.
12433 * @param {Object} params This parameter is not used by the MemoryProxy class.
12434 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12435 * object into a block of Roo.data.Records.
12436 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12437 * The function must be passed <ul>
12438 * <li>The Record block object</li>
12439 * <li>The "arg" argument from the load function</li>
12440 * <li>A boolean success indicator</li>
12442 * @param {Object} scope The scope in which to call the callback
12443 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12445 load : function(params, reader, callback, scope, arg){
12446 params = params || {};
12449 result = reader.readRecords(params.data ? params.data :this.data);
12451 this.fireEvent("loadexception", this, arg, null, e);
12452 callback.call(scope, null, arg, false);
12455 callback.call(scope, result, arg, true);
12459 update : function(params, records){
12464 * Ext JS Library 1.1.1
12465 * Copyright(c) 2006-2007, Ext JS, LLC.
12467 * Originally Released Under LGPL - original licence link has changed is not relivant.
12470 * <script type="text/javascript">
12473 * @class Roo.data.HttpProxy
12474 * @extends Roo.data.DataProxy
12475 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12476 * configured to reference a certain URL.<br><br>
12478 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12479 * from which the running page was served.<br><br>
12481 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12483 * Be aware that to enable the browser to parse an XML document, the server must set
12484 * the Content-Type header in the HTTP response to "text/xml".
12486 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12487 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12488 * will be used to make the request.
12490 Roo.data.HttpProxy = function(conn){
12491 Roo.data.HttpProxy.superclass.constructor.call(this);
12492 // is conn a conn config or a real conn?
12494 this.useAjax = !conn || !conn.events;
12498 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12499 // thse are take from connection...
12502 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12505 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12506 * extra parameters to each request made by this object. (defaults to undefined)
12509 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12510 * to each request made by this object. (defaults to undefined)
12513 * @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)
12516 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12519 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12525 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12529 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12530 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12531 * a finer-grained basis than the DataProxy events.
12533 getConnection : function(){
12534 return this.useAjax ? Roo.Ajax : this.conn;
12538 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12539 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12540 * process that block using the passed callback.
12541 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12542 * for the request to the remote server.
12543 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12544 * object into a block of Roo.data.Records.
12545 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12546 * The function must be passed <ul>
12547 * <li>The Record block object</li>
12548 * <li>The "arg" argument from the load function</li>
12549 * <li>A boolean success indicator</li>
12551 * @param {Object} scope The scope in which to call the callback
12552 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12554 load : function(params, reader, callback, scope, arg){
12555 if(this.fireEvent("beforeload", this, params) !== false){
12557 params : params || {},
12559 callback : callback,
12564 callback : this.loadResponse,
12568 Roo.applyIf(o, this.conn);
12569 if(this.activeRequest){
12570 Roo.Ajax.abort(this.activeRequest);
12572 this.activeRequest = Roo.Ajax.request(o);
12574 this.conn.request(o);
12577 callback.call(scope||this, null, arg, false);
12582 loadResponse : function(o, success, response){
12583 delete this.activeRequest;
12585 this.fireEvent("loadexception", this, o, response);
12586 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12591 result = o.reader.read(response);
12593 this.fireEvent("loadexception", this, o, response, e);
12594 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12598 this.fireEvent("load", this, o, o.request.arg);
12599 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12603 update : function(dataSet){
12608 updateResponse : function(dataSet){
12613 * Ext JS Library 1.1.1
12614 * Copyright(c) 2006-2007, Ext JS, LLC.
12616 * Originally Released Under LGPL - original licence link has changed is not relivant.
12619 * <script type="text/javascript">
12623 * @class Roo.data.ScriptTagProxy
12624 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12625 * other than the originating domain of the running page.<br><br>
12627 * <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
12628 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12630 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12631 * source code that is used as the source inside a <script> tag.<br><br>
12633 * In order for the browser to process the returned data, the server must wrap the data object
12634 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12635 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12636 * depending on whether the callback name was passed:
12639 boolean scriptTag = false;
12640 String cb = request.getParameter("callback");
12643 response.setContentType("text/javascript");
12645 response.setContentType("application/x-json");
12647 Writer out = response.getWriter();
12649 out.write(cb + "(");
12651 out.print(dataBlock.toJsonString());
12658 * @param {Object} config A configuration object.
12660 Roo.data.ScriptTagProxy = function(config){
12661 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12662 Roo.apply(this, config);
12663 this.head = document.getElementsByTagName("head")[0];
12666 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12668 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12670 * @cfg {String} url The URL from which to request the data object.
12673 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12677 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12678 * the server the name of the callback function set up by the load call to process the returned data object.
12679 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12680 * javascript output which calls this named function passing the data object as its only parameter.
12682 callbackParam : "callback",
12684 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12685 * name to the request.
12690 * Load data from the configured URL, read the data object into
12691 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12692 * process that block using the passed callback.
12693 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12694 * for the request to the remote server.
12695 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12696 * object into a block of Roo.data.Records.
12697 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12698 * The function must be passed <ul>
12699 * <li>The Record block object</li>
12700 * <li>The "arg" argument from the load function</li>
12701 * <li>A boolean success indicator</li>
12703 * @param {Object} scope The scope in which to call the callback
12704 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12706 load : function(params, reader, callback, scope, arg){
12707 if(this.fireEvent("beforeload", this, params) !== false){
12709 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12711 var url = this.url;
12712 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12714 url += "&_dc=" + (new Date().getTime());
12716 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12719 cb : "stcCallback"+transId,
12720 scriptId : "stcScript"+transId,
12724 callback : callback,
12730 window[trans.cb] = function(o){
12731 conn.handleResponse(o, trans);
12734 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12736 if(this.autoAbort !== false){
12740 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12742 var script = document.createElement("script");
12743 script.setAttribute("src", url);
12744 script.setAttribute("type", "text/javascript");
12745 script.setAttribute("id", trans.scriptId);
12746 this.head.appendChild(script);
12748 this.trans = trans;
12750 callback.call(scope||this, null, arg, false);
12755 isLoading : function(){
12756 return this.trans ? true : false;
12760 * Abort the current server request.
12762 abort : function(){
12763 if(this.isLoading()){
12764 this.destroyTrans(this.trans);
12769 destroyTrans : function(trans, isLoaded){
12770 this.head.removeChild(document.getElementById(trans.scriptId));
12771 clearTimeout(trans.timeoutId);
12773 window[trans.cb] = undefined;
12775 delete window[trans.cb];
12778 // if hasn't been loaded, wait for load to remove it to prevent script error
12779 window[trans.cb] = function(){
12780 window[trans.cb] = undefined;
12782 delete window[trans.cb];
12789 handleResponse : function(o, trans){
12790 this.trans = false;
12791 this.destroyTrans(trans, true);
12794 result = trans.reader.readRecords(o);
12796 this.fireEvent("loadexception", this, o, trans.arg, e);
12797 trans.callback.call(trans.scope||window, null, trans.arg, false);
12800 this.fireEvent("load", this, o, trans.arg);
12801 trans.callback.call(trans.scope||window, result, trans.arg, true);
12805 handleFailure : function(trans){
12806 this.trans = false;
12807 this.destroyTrans(trans, false);
12808 this.fireEvent("loadexception", this, null, trans.arg);
12809 trans.callback.call(trans.scope||window, null, trans.arg, false);
12813 * Ext JS Library 1.1.1
12814 * Copyright(c) 2006-2007, Ext JS, LLC.
12816 * Originally Released Under LGPL - original licence link has changed is not relivant.
12819 * <script type="text/javascript">
12823 * @class Roo.data.JsonReader
12824 * @extends Roo.data.DataReader
12825 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12826 * based on mappings in a provided Roo.data.Record constructor.
12828 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12829 * in the reply previously.
12834 var RecordDef = Roo.data.Record.create([
12835 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12836 {name: 'occupation'} // This field will use "occupation" as the mapping.
12838 var myReader = new Roo.data.JsonReader({
12839 totalProperty: "results", // The property which contains the total dataset size (optional)
12840 root: "rows", // The property which contains an Array of row objects
12841 id: "id" // The property within each row object that provides an ID for the record (optional)
12845 * This would consume a JSON file like this:
12847 { 'results': 2, 'rows': [
12848 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12849 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12852 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12853 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12854 * paged from the remote server.
12855 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12856 * @cfg {String} root name of the property which contains the Array of row objects.
12857 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12858 * @cfg {Array} fields Array of field definition objects
12860 * Create a new JsonReader
12861 * @param {Object} meta Metadata configuration options
12862 * @param {Object} recordType Either an Array of field definition objects,
12863 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12865 Roo.data.JsonReader = function(meta, recordType){
12868 // set some defaults:
12869 Roo.applyIf(meta, {
12870 totalProperty: 'total',
12871 successProperty : 'success',
12876 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12878 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12880 readerType : 'Json',
12883 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12884 * Used by Store query builder to append _requestMeta to params.
12887 metaFromRemote : false,
12889 * This method is only used by a DataProxy which has retrieved data from a remote server.
12890 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12891 * @return {Object} data A data block which is used by an Roo.data.Store object as
12892 * a cache of Roo.data.Records.
12894 read : function(response){
12895 var json = response.responseText;
12897 var o = /* eval:var:o */ eval("("+json+")");
12899 throw {message: "JsonReader.read: Json object not found"};
12905 this.metaFromRemote = true;
12906 this.meta = o.metaData;
12907 this.recordType = Roo.data.Record.create(o.metaData.fields);
12908 this.onMetaChange(this.meta, this.recordType, o);
12910 return this.readRecords(o);
12913 // private function a store will implement
12914 onMetaChange : function(meta, recordType, o){
12921 simpleAccess: function(obj, subsc) {
12928 getJsonAccessor: function(){
12930 return function(expr) {
12932 return(re.test(expr))
12933 ? new Function("obj", "return obj." + expr)
12938 return Roo.emptyFn;
12943 * Create a data block containing Roo.data.Records from an XML document.
12944 * @param {Object} o An object which contains an Array of row objects in the property specified
12945 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12946 * which contains the total size of the dataset.
12947 * @return {Object} data A data block which is used by an Roo.data.Store object as
12948 * a cache of Roo.data.Records.
12950 readRecords : function(o){
12952 * After any data loads, the raw JSON data is available for further custom processing.
12956 var s = this.meta, Record = this.recordType,
12957 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12959 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12961 if(s.totalProperty) {
12962 this.getTotal = this.getJsonAccessor(s.totalProperty);
12964 if(s.successProperty) {
12965 this.getSuccess = this.getJsonAccessor(s.successProperty);
12967 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12969 var g = this.getJsonAccessor(s.id);
12970 this.getId = function(rec) {
12972 return (r === undefined || r === "") ? null : r;
12975 this.getId = function(){return null;};
12978 for(var jj = 0; jj < fl; jj++){
12980 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12981 this.ef[jj] = this.getJsonAccessor(map);
12985 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12986 if(s.totalProperty){
12987 var vt = parseInt(this.getTotal(o), 10);
12992 if(s.successProperty){
12993 var vs = this.getSuccess(o);
12994 if(vs === false || vs === 'false'){
12999 for(var i = 0; i < c; i++){
13002 var id = this.getId(n);
13003 for(var j = 0; j < fl; j++){
13005 var v = this.ef[j](n);
13007 Roo.log('missing convert for ' + f.name);
13011 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13013 var record = new Record(values, id);
13015 records[i] = record;
13021 totalRecords : totalRecords
13026 * Ext JS Library 1.1.1
13027 * Copyright(c) 2006-2007, Ext JS, LLC.
13029 * Originally Released Under LGPL - original licence link has changed is not relivant.
13032 * <script type="text/javascript">
13036 * @class Roo.data.ArrayReader
13037 * @extends Roo.data.DataReader
13038 * Data reader class to create an Array of Roo.data.Record objects from an Array.
13039 * Each element of that Array represents a row of data fields. The
13040 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13041 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13045 var RecordDef = Roo.data.Record.create([
13046 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
13047 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
13049 var myReader = new Roo.data.ArrayReader({
13050 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
13054 * This would consume an Array like this:
13056 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13060 * Create a new JsonReader
13061 * @param {Object} meta Metadata configuration options.
13062 * @param {Object|Array} recordType Either an Array of field definition objects
13064 * @cfg {Array} fields Array of field definition objects
13065 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13066 * as specified to {@link Roo.data.Record#create},
13067 * or an {@link Roo.data.Record} object
13070 * created using {@link Roo.data.Record#create}.
13072 Roo.data.ArrayReader = function(meta, recordType)
13074 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13077 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13080 * Create a data block containing Roo.data.Records from an XML document.
13081 * @param {Object} o An Array of row objects which represents the dataset.
13082 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13083 * a cache of Roo.data.Records.
13085 readRecords : function(o)
13087 var sid = this.meta ? this.meta.id : null;
13088 var recordType = this.recordType, fields = recordType.prototype.fields;
13091 for(var i = 0; i < root.length; i++){
13094 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13095 for(var j = 0, jlen = fields.length; j < jlen; j++){
13096 var f = fields.items[j];
13097 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13098 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13100 values[f.name] = v;
13102 var record = new recordType(values, id);
13104 records[records.length] = record;
13108 totalRecords : records.length
13112 * using 'cn' the nested child reader read the child array into it's child stores.
13113 * @param {Object} rec The record with a 'children array
13115 loadDataFromChildren: function(rec)
13117 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13118 return this.loadData(typeof(rec.data.cn) == 'undefined' ? '' : rec.data.cn);
13129 * @class Roo.bootstrap.ComboBox
13130 * @extends Roo.bootstrap.TriggerField
13131 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13132 * @cfg {Boolean} append (true|false) default false
13133 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13134 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13135 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13136 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13137 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13138 * @cfg {Boolean} animate default true
13139 * @cfg {Boolean} emptyResultText only for touch device
13140 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13141 * @cfg {String} emptyTitle default ''
13143 * Create a new ComboBox.
13144 * @param {Object} config Configuration options
13146 Roo.bootstrap.ComboBox = function(config){
13147 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13151 * Fires when the dropdown list is expanded
13152 * @param {Roo.bootstrap.ComboBox} combo This combo box
13157 * Fires when the dropdown list is collapsed
13158 * @param {Roo.bootstrap.ComboBox} combo This combo box
13162 * @event beforeselect
13163 * Fires before a list item is selected. Return false to cancel the selection.
13164 * @param {Roo.bootstrap.ComboBox} combo This combo box
13165 * @param {Roo.data.Record} record The data record returned from the underlying store
13166 * @param {Number} index The index of the selected item in the dropdown list
13168 'beforeselect' : true,
13171 * Fires when a list item is selected
13172 * @param {Roo.bootstrap.ComboBox} combo This combo box
13173 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13174 * @param {Number} index The index of the selected item in the dropdown list
13178 * @event beforequery
13179 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13180 * The event object passed has these properties:
13181 * @param {Roo.bootstrap.ComboBox} combo This combo box
13182 * @param {String} query The query
13183 * @param {Boolean} forceAll true to force "all" query
13184 * @param {Boolean} cancel true to cancel the query
13185 * @param {Object} e The query event object
13187 'beforequery': true,
13190 * Fires when the 'add' icon is pressed (add a listener to enable add button)
13191 * @param {Roo.bootstrap.ComboBox} combo This combo box
13196 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13197 * @param {Roo.bootstrap.ComboBox} combo This combo box
13198 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13203 * Fires when the remove value from the combobox array
13204 * @param {Roo.bootstrap.ComboBox} combo This combo box
13208 * @event afterremove
13209 * Fires when the remove value from the combobox array
13210 * @param {Roo.bootstrap.ComboBox} combo This combo box
13212 'afterremove' : true,
13214 * @event specialfilter
13215 * Fires when specialfilter
13216 * @param {Roo.bootstrap.ComboBox} combo This combo box
13218 'specialfilter' : true,
13221 * Fires when tick the element
13222 * @param {Roo.bootstrap.ComboBox} combo This combo box
13226 * @event touchviewdisplay
13227 * Fires when touch view require special display (default is using displayField)
13228 * @param {Roo.bootstrap.ComboBox} combo This combo box
13229 * @param {Object} cfg set html .
13231 'touchviewdisplay' : true
13236 this.tickItems = [];
13238 this.selectedIndex = -1;
13239 if(this.mode == 'local'){
13240 if(config.queryDelay === undefined){
13241 this.queryDelay = 10;
13243 if(config.minChars === undefined){
13249 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13252 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13253 * rendering into an Roo.Editor, defaults to false)
13256 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13257 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13260 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13263 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13264 * the dropdown list (defaults to undefined, with no header element)
13268 * @cfg {String/Roo.Template} tpl The template to use to render the output
13272 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13274 listWidth: undefined,
13276 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13277 * mode = 'remote' or 'text' if mode = 'local')
13279 displayField: undefined,
13282 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13283 * mode = 'remote' or 'value' if mode = 'local').
13284 * Note: use of a valueField requires the user make a selection
13285 * in order for a value to be mapped.
13287 valueField: undefined,
13289 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13294 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13295 * field's data value (defaults to the underlying DOM element's name)
13297 hiddenName: undefined,
13299 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13303 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13305 selectedClass: 'active',
13308 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13312 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13313 * anchor positions (defaults to 'tl-bl')
13315 listAlign: 'tl-bl?',
13317 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13321 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13322 * query specified by the allQuery config option (defaults to 'query')
13324 triggerAction: 'query',
13326 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13327 * (defaults to 4, does not apply if editable = false)
13331 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13332 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13336 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13337 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13341 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13342 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13346 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13347 * when editable = true (defaults to false)
13349 selectOnFocus:false,
13351 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13353 queryParam: 'query',
13355 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13356 * when mode = 'remote' (defaults to 'Loading...')
13358 loadingText: 'Loading...',
13360 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13364 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13368 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13369 * traditional select (defaults to true)
13373 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13377 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13381 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13382 * listWidth has a higher value)
13386 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13387 * allow the user to set arbitrary text into the field (defaults to false)
13389 forceSelection:false,
13391 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13392 * if typeAhead = true (defaults to 250)
13394 typeAheadDelay : 250,
13396 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13397 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13399 valueNotFoundText : undefined,
13401 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13403 blockFocus : false,
13406 * @cfg {Boolean} disableClear Disable showing of clear button.
13408 disableClear : false,
13410 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13412 alwaysQuery : false,
13415 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13420 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13422 invalidClass : "has-warning",
13425 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13427 validClass : "has-success",
13430 * @cfg {Boolean} specialFilter (true|false) special filter default false
13432 specialFilter : false,
13435 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13437 mobileTouchView : true,
13440 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13442 useNativeIOS : false,
13445 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13447 mobile_restrict_height : false,
13449 ios_options : false,
13461 btnPosition : 'right',
13462 triggerList : true,
13463 showToggleBtn : true,
13465 emptyResultText: 'Empty',
13466 triggerText : 'Select',
13469 // element that contains real text value.. (when hidden is used..)
13471 getAutoCreate : function()
13476 * Render classic select for iso
13479 if(Roo.isIOS && this.useNativeIOS){
13480 cfg = this.getAutoCreateNativeIOS();
13488 if(Roo.isTouch && this.mobileTouchView){
13489 cfg = this.getAutoCreateTouchView();
13496 if(!this.tickable){
13497 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13502 * ComboBox with tickable selections
13505 var align = this.labelAlign || this.parentLabelAlign();
13508 cls : 'form-group roo-combobox-tickable' //input-group
13511 var btn_text_select = '';
13512 var btn_text_done = '';
13513 var btn_text_cancel = '';
13515 if (this.btn_text_show) {
13516 btn_text_select = 'Select';
13517 btn_text_done = 'Done';
13518 btn_text_cancel = 'Cancel';
13523 cls : 'tickable-buttons',
13528 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13529 //html : this.triggerText
13530 html: btn_text_select
13536 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13538 html: btn_text_done
13544 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13546 html: btn_text_cancel
13552 buttons.cn.unshift({
13554 cls: 'roo-select2-search-field-input'
13560 Roo.each(buttons.cn, function(c){
13562 c.cls += ' btn-' + _this.size;
13565 if (_this.disabled) {
13572 style : 'display: contents',
13577 cls: 'form-hidden-field'
13581 cls: 'roo-select2-choices',
13585 cls: 'roo-select2-search-field',
13596 cls: 'roo-select2-container input-group roo-select2-container-multi',
13602 // cls: 'typeahead typeahead-long dropdown-menu',
13603 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13608 if(this.hasFeedback && !this.allowBlank){
13612 cls: 'glyphicon form-control-feedback'
13615 combobox.cn.push(feedback);
13620 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13621 tooltip : 'This field is required'
13623 if (Roo.bootstrap.version == 4) {
13626 style : 'display:none'
13629 if (align ==='left' && this.fieldLabel.length) {
13631 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13638 cls : 'control-label col-form-label',
13639 html : this.fieldLabel
13651 var labelCfg = cfg.cn[1];
13652 var contentCfg = cfg.cn[2];
13655 if(this.indicatorpos == 'right'){
13661 cls : 'control-label col-form-label',
13665 html : this.fieldLabel
13681 labelCfg = cfg.cn[0];
13682 contentCfg = cfg.cn[1];
13686 if(this.labelWidth > 12){
13687 labelCfg.style = "width: " + this.labelWidth + 'px';
13690 if(this.labelWidth < 13 && this.labelmd == 0){
13691 this.labelmd = this.labelWidth;
13694 if(this.labellg > 0){
13695 labelCfg.cls += ' col-lg-' + this.labellg;
13696 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13699 if(this.labelmd > 0){
13700 labelCfg.cls += ' col-md-' + this.labelmd;
13701 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13704 if(this.labelsm > 0){
13705 labelCfg.cls += ' col-sm-' + this.labelsm;
13706 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13709 if(this.labelxs > 0){
13710 labelCfg.cls += ' col-xs-' + this.labelxs;
13711 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13715 } else if ( this.fieldLabel.length) {
13716 // Roo.log(" label");
13721 //cls : 'input-group-addon',
13722 html : this.fieldLabel
13727 if(this.indicatorpos == 'right'){
13731 //cls : 'input-group-addon',
13732 html : this.fieldLabel
13742 // Roo.log(" no label && no align");
13749 ['xs','sm','md','lg'].map(function(size){
13750 if (settings[size]) {
13751 cfg.cls += ' col-' + size + '-' + settings[size];
13759 _initEventsCalled : false,
13762 initEvents: function()
13764 if (this._initEventsCalled) { // as we call render... prevent looping...
13767 this._initEventsCalled = true;
13770 throw "can not find store for combo";
13773 this.indicator = this.indicatorEl();
13775 this.store = Roo.factory(this.store, Roo.data);
13776 this.store.parent = this;
13778 // if we are building from html. then this element is so complex, that we can not really
13779 // use the rendered HTML.
13780 // so we have to trash and replace the previous code.
13781 if (Roo.XComponent.build_from_html) {
13782 // remove this element....
13783 var e = this.el.dom, k=0;
13784 while (e ) { e = e.previousSibling; ++k;}
13789 this.rendered = false;
13791 this.render(this.parent().getChildContainer(true), k);
13794 if(Roo.isIOS && this.useNativeIOS){
13795 this.initIOSView();
13803 if(Roo.isTouch && this.mobileTouchView){
13804 this.initTouchView();
13809 this.initTickableEvents();
13813 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13815 if(this.hiddenName){
13817 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13819 this.hiddenField.dom.value =
13820 this.hiddenValue !== undefined ? this.hiddenValue :
13821 this.value !== undefined ? this.value : '';
13823 // prevent input submission
13824 this.el.dom.removeAttribute('name');
13825 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13830 // this.el.dom.setAttribute('autocomplete', 'off');
13833 var cls = 'x-combo-list';
13835 //this.list = new Roo.Layer({
13836 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13842 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13843 _this.list.setWidth(lw);
13846 this.list.on('mouseover', this.onViewOver, this);
13847 this.list.on('mousemove', this.onViewMove, this);
13848 this.list.on('scroll', this.onViewScroll, this);
13851 this.list.swallowEvent('mousewheel');
13852 this.assetHeight = 0;
13855 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13856 this.assetHeight += this.header.getHeight();
13859 this.innerList = this.list.createChild({cls:cls+'-inner'});
13860 this.innerList.on('mouseover', this.onViewOver, this);
13861 this.innerList.on('mousemove', this.onViewMove, this);
13862 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13864 if(this.allowBlank && !this.pageSize && !this.disableClear){
13865 this.footer = this.list.createChild({cls:cls+'-ft'});
13866 this.pageTb = new Roo.Toolbar(this.footer);
13870 this.footer = this.list.createChild({cls:cls+'-ft'});
13871 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13872 {pageSize: this.pageSize});
13876 if (this.pageTb && this.allowBlank && !this.disableClear) {
13878 this.pageTb.add(new Roo.Toolbar.Fill(), {
13879 cls: 'x-btn-icon x-btn-clear',
13881 handler: function()
13884 _this.clearValue();
13885 _this.onSelect(false, -1);
13890 this.assetHeight += this.footer.getHeight();
13895 this.tpl = Roo.bootstrap.version == 4 ?
13896 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
13897 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13900 this.view = new Roo.View(this.list, this.tpl, {
13901 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13903 //this.view.wrapEl.setDisplayed(false);
13904 this.view.on('click', this.onViewClick, this);
13907 this.store.on('beforeload', this.onBeforeLoad, this);
13908 this.store.on('load', this.onLoad, this);
13909 this.store.on('loadexception', this.onLoadException, this);
13911 if(this.resizable){
13912 this.resizer = new Roo.Resizable(this.list, {
13913 pinned:true, handles:'se'
13915 this.resizer.on('resize', function(r, w, h){
13916 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13917 this.listWidth = w;
13918 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13919 this.restrictHeight();
13921 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13924 if(!this.editable){
13925 this.editable = true;
13926 this.setEditable(false);
13931 if (typeof(this.events.add.listeners) != 'undefined') {
13933 this.addicon = this.wrap.createChild(
13934 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13936 this.addicon.on('click', function(e) {
13937 this.fireEvent('add', this);
13940 if (typeof(this.events.edit.listeners) != 'undefined') {
13942 this.editicon = this.wrap.createChild(
13943 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13944 if (this.addicon) {
13945 this.editicon.setStyle('margin-left', '40px');
13947 this.editicon.on('click', function(e) {
13949 // we fire even if inothing is selected..
13950 this.fireEvent('edit', this, this.lastData );
13956 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13957 "up" : function(e){
13958 this.inKeyMode = true;
13962 "down" : function(e){
13963 if(!this.isExpanded()){
13964 this.onTriggerClick();
13966 this.inKeyMode = true;
13971 "enter" : function(e){
13972 // this.onViewClick();
13976 if(this.fireEvent("specialkey", this, e)){
13977 this.onViewClick(false);
13983 "esc" : function(e){
13987 "tab" : function(e){
13990 if(this.fireEvent("specialkey", this, e)){
13991 this.onViewClick(false);
13999 doRelay : function(foo, bar, hname){
14000 if(hname == 'down' || this.scope.isExpanded()){
14001 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14010 this.queryDelay = Math.max(this.queryDelay || 10,
14011 this.mode == 'local' ? 10 : 250);
14014 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14016 if(this.typeAhead){
14017 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14019 if(this.editable !== false){
14020 this.inputEl().on("keyup", this.onKeyUp, this);
14022 if(this.forceSelection){
14023 this.inputEl().on('blur', this.doForce, this);
14027 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14028 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14032 initTickableEvents: function()
14036 if(this.hiddenName){
14038 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14040 this.hiddenField.dom.value =
14041 this.hiddenValue !== undefined ? this.hiddenValue :
14042 this.value !== undefined ? this.value : '';
14044 // prevent input submission
14045 this.el.dom.removeAttribute('name');
14046 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14051 // this.list = this.el.select('ul.dropdown-menu',true).first();
14053 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14054 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14055 if(this.triggerList){
14056 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14059 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14060 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14062 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14063 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14065 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14066 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14068 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14069 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14070 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14073 this.cancelBtn.hide();
14078 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14079 _this.list.setWidth(lw);
14082 this.list.on('mouseover', this.onViewOver, this);
14083 this.list.on('mousemove', this.onViewMove, this);
14085 this.list.on('scroll', this.onViewScroll, this);
14088 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
14089 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14092 this.view = new Roo.View(this.list, this.tpl, {
14097 selectedClass: this.selectedClass
14100 //this.view.wrapEl.setDisplayed(false);
14101 this.view.on('click', this.onViewClick, this);
14105 this.store.on('beforeload', this.onBeforeLoad, this);
14106 this.store.on('load', this.onLoad, this);
14107 this.store.on('loadexception', this.onLoadException, this);
14110 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14111 "up" : function(e){
14112 this.inKeyMode = true;
14116 "down" : function(e){
14117 this.inKeyMode = true;
14121 "enter" : function(e){
14122 if(this.fireEvent("specialkey", this, e)){
14123 this.onViewClick(false);
14129 "esc" : function(e){
14130 this.onTickableFooterButtonClick(e, false, false);
14133 "tab" : function(e){
14134 this.fireEvent("specialkey", this, e);
14136 this.onTickableFooterButtonClick(e, false, false);
14143 doRelay : function(e, fn, key){
14144 if(this.scope.isExpanded()){
14145 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14154 this.queryDelay = Math.max(this.queryDelay || 10,
14155 this.mode == 'local' ? 10 : 250);
14158 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14160 if(this.typeAhead){
14161 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14164 if(this.editable !== false){
14165 this.tickableInputEl().on("keyup", this.onKeyUp, this);
14168 this.indicator = this.indicatorEl();
14170 if(this.indicator){
14171 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14172 this.indicator.hide();
14177 onDestroy : function(){
14179 this.view.setStore(null);
14180 this.view.el.removeAllListeners();
14181 this.view.el.remove();
14182 this.view.purgeListeners();
14185 this.list.dom.innerHTML = '';
14189 this.store.un('beforeload', this.onBeforeLoad, this);
14190 this.store.un('load', this.onLoad, this);
14191 this.store.un('loadexception', this.onLoadException, this);
14193 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14197 fireKey : function(e){
14198 if(e.isNavKeyPress() && !this.list.isVisible()){
14199 this.fireEvent("specialkey", this, e);
14204 onResize: function(w, h){
14205 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14207 // if(typeof w != 'number'){
14208 // // we do not handle it!?!?
14211 // var tw = this.trigger.getWidth();
14212 // // tw += this.addicon ? this.addicon.getWidth() : 0;
14213 // // tw += this.editicon ? this.editicon.getWidth() : 0;
14215 // this.inputEl().setWidth( this.adjustWidth('input', x));
14217 // //this.trigger.setStyle('left', x+'px');
14219 // if(this.list && this.listWidth === undefined){
14220 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14221 // this.list.setWidth(lw);
14222 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14230 * Allow or prevent the user from directly editing the field text. If false is passed,
14231 * the user will only be able to select from the items defined in the dropdown list. This method
14232 * is the runtime equivalent of setting the 'editable' config option at config time.
14233 * @param {Boolean} value True to allow the user to directly edit the field text
14235 setEditable : function(value){
14236 if(value == this.editable){
14239 this.editable = value;
14241 this.inputEl().dom.setAttribute('readOnly', true);
14242 this.inputEl().on('mousedown', this.onTriggerClick, this);
14243 this.inputEl().addClass('x-combo-noedit');
14245 this.inputEl().dom.setAttribute('readOnly', false);
14246 this.inputEl().un('mousedown', this.onTriggerClick, this);
14247 this.inputEl().removeClass('x-combo-noedit');
14253 onBeforeLoad : function(combo,opts){
14254 if(!this.hasFocus){
14258 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14260 this.restrictHeight();
14261 this.selectedIndex = -1;
14265 onLoad : function(){
14267 this.hasQuery = false;
14269 if(!this.hasFocus){
14273 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14274 this.loading.hide();
14277 if(this.store.getCount() > 0){
14280 this.restrictHeight();
14281 if(this.lastQuery == this.allQuery){
14282 if(this.editable && !this.tickable){
14283 this.inputEl().dom.select();
14287 !this.selectByValue(this.value, true) &&
14290 !this.store.lastOptions ||
14291 typeof(this.store.lastOptions.add) == 'undefined' ||
14292 this.store.lastOptions.add != true
14295 this.select(0, true);
14298 if(this.autoFocus){
14301 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14302 this.taTask.delay(this.typeAheadDelay);
14306 this.onEmptyResults();
14312 onLoadException : function()
14314 this.hasQuery = false;
14316 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14317 this.loading.hide();
14320 if(this.tickable && this.editable){
14325 // only causes errors at present
14326 //Roo.log(this.store.reader.jsonData);
14327 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14329 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14335 onTypeAhead : function(){
14336 if(this.store.getCount() > 0){
14337 var r = this.store.getAt(0);
14338 var newValue = r.data[this.displayField];
14339 var len = newValue.length;
14340 var selStart = this.getRawValue().length;
14342 if(selStart != len){
14343 this.setRawValue(newValue);
14344 this.selectText(selStart, newValue.length);
14350 onSelect : function(record, index){
14352 if(this.fireEvent('beforeselect', this, record, index) !== false){
14354 this.setFromData(index > -1 ? record.data : false);
14357 this.fireEvent('select', this, record, index);
14362 * Returns the currently selected field value or empty string if no value is set.
14363 * @return {String} value The selected value
14365 getValue : function()
14367 if(Roo.isIOS && this.useNativeIOS){
14368 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14372 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14375 if(this.valueField){
14376 return typeof this.value != 'undefined' ? this.value : '';
14378 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14382 getRawValue : function()
14384 if(Roo.isIOS && this.useNativeIOS){
14385 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14388 var v = this.inputEl().getValue();
14394 * Clears any text/value currently set in the field
14396 clearValue : function(){
14398 if(this.hiddenField){
14399 this.hiddenField.dom.value = '';
14402 this.setRawValue('');
14403 this.lastSelectionText = '';
14404 this.lastData = false;
14406 var close = this.closeTriggerEl();
14417 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14418 * will be displayed in the field. If the value does not match the data value of an existing item,
14419 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14420 * Otherwise the field will be blank (although the value will still be set).
14421 * @param {String} value The value to match
14423 setValue : function(v)
14425 if(Roo.isIOS && this.useNativeIOS){
14426 this.setIOSValue(v);
14436 if(this.valueField){
14437 var r = this.findRecord(this.valueField, v);
14439 text = r.data[this.displayField];
14440 }else if(this.valueNotFoundText !== undefined){
14441 text = this.valueNotFoundText;
14444 this.lastSelectionText = text;
14445 if(this.hiddenField){
14446 this.hiddenField.dom.value = v;
14448 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14451 var close = this.closeTriggerEl();
14454 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14460 * @property {Object} the last set data for the element
14465 * Sets the value of the field based on a object which is related to the record format for the store.
14466 * @param {Object} value the value to set as. or false on reset?
14468 setFromData : function(o){
14475 var dv = ''; // display value
14476 var vv = ''; // value value..
14478 if (this.displayField) {
14479 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14481 // this is an error condition!!!
14482 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14485 if(this.valueField){
14486 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14489 var close = this.closeTriggerEl();
14492 if(dv.length || vv * 1 > 0){
14494 this.blockFocus=true;
14500 if(this.hiddenField){
14501 this.hiddenField.dom.value = vv;
14503 this.lastSelectionText = dv;
14504 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14508 // no hidden field.. - we store the value in 'value', but still display
14509 // display field!!!!
14510 this.lastSelectionText = dv;
14511 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14518 reset : function(){
14519 // overridden so that last data is reset..
14526 this.setValue(this.originalValue);
14527 //this.clearInvalid();
14528 this.lastData = false;
14530 this.view.clearSelections();
14536 findRecord : function(prop, value){
14538 if(this.store.getCount() > 0){
14539 this.store.each(function(r){
14540 if(r.data[prop] == value){
14550 getName: function()
14552 // returns hidden if it's set..
14553 if (!this.rendered) {return ''};
14554 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14558 onViewMove : function(e, t){
14559 this.inKeyMode = false;
14563 onViewOver : function(e, t){
14564 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14567 var item = this.view.findItemFromChild(t);
14570 var index = this.view.indexOf(item);
14571 this.select(index, false);
14576 onViewClick : function(view, doFocus, el, e)
14578 var index = this.view.getSelectedIndexes()[0];
14580 var r = this.store.getAt(index);
14584 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14591 Roo.each(this.tickItems, function(v,k){
14593 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14595 _this.tickItems.splice(k, 1);
14597 if(typeof(e) == 'undefined' && view == false){
14598 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14610 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14611 this.tickItems.push(r.data);
14614 if(typeof(e) == 'undefined' && view == false){
14615 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14622 this.onSelect(r, index);
14624 if(doFocus !== false && !this.blockFocus){
14625 this.inputEl().focus();
14630 restrictHeight : function(){
14631 //this.innerList.dom.style.height = '';
14632 //var inner = this.innerList.dom;
14633 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14634 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14635 //this.list.beginUpdate();
14636 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14637 this.list.alignTo(this.inputEl(), this.listAlign);
14638 this.list.alignTo(this.inputEl(), this.listAlign);
14639 //this.list.endUpdate();
14643 onEmptyResults : function(){
14645 if(this.tickable && this.editable){
14646 this.hasFocus = false;
14647 this.restrictHeight();
14655 * Returns true if the dropdown list is expanded, else false.
14657 isExpanded : function(){
14658 return this.list.isVisible();
14662 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14663 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14664 * @param {String} value The data value of the item to select
14665 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14666 * selected item if it is not currently in view (defaults to true)
14667 * @return {Boolean} True if the value matched an item in the list, else false
14669 selectByValue : function(v, scrollIntoView){
14670 if(v !== undefined && v !== null){
14671 var r = this.findRecord(this.valueField || this.displayField, v);
14673 this.select(this.store.indexOf(r), scrollIntoView);
14681 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14682 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14683 * @param {Number} index The zero-based index of the list item to select
14684 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14685 * selected item if it is not currently in view (defaults to true)
14687 select : function(index, scrollIntoView){
14688 this.selectedIndex = index;
14689 this.view.select(index);
14690 if(scrollIntoView !== false){
14691 var el = this.view.getNode(index);
14693 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14696 this.list.scrollChildIntoView(el, false);
14702 selectNext : function(){
14703 var ct = this.store.getCount();
14705 if(this.selectedIndex == -1){
14707 }else if(this.selectedIndex < ct-1){
14708 this.select(this.selectedIndex+1);
14714 selectPrev : function(){
14715 var ct = this.store.getCount();
14717 if(this.selectedIndex == -1){
14719 }else if(this.selectedIndex != 0){
14720 this.select(this.selectedIndex-1);
14726 onKeyUp : function(e){
14727 if(this.editable !== false && !e.isSpecialKey()){
14728 this.lastKey = e.getKey();
14729 this.dqTask.delay(this.queryDelay);
14734 validateBlur : function(){
14735 return !this.list || !this.list.isVisible();
14739 initQuery : function(){
14741 var v = this.getRawValue();
14743 if(this.tickable && this.editable){
14744 v = this.tickableInputEl().getValue();
14751 doForce : function(){
14752 if(this.inputEl().dom.value.length > 0){
14753 this.inputEl().dom.value =
14754 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14760 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14761 * query allowing the query action to be canceled if needed.
14762 * @param {String} query The SQL query to execute
14763 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14764 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14765 * saved in the current store (defaults to false)
14767 doQuery : function(q, forceAll){
14769 if(q === undefined || q === null){
14774 forceAll: forceAll,
14778 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14783 forceAll = qe.forceAll;
14784 if(forceAll === true || (q.length >= this.minChars)){
14786 this.hasQuery = true;
14788 if(this.lastQuery != q || this.alwaysQuery){
14789 this.lastQuery = q;
14790 if(this.mode == 'local'){
14791 this.selectedIndex = -1;
14793 this.store.clearFilter();
14796 if(this.specialFilter){
14797 this.fireEvent('specialfilter', this);
14802 this.store.filter(this.displayField, q);
14805 this.store.fireEvent("datachanged", this.store);
14812 this.store.baseParams[this.queryParam] = q;
14814 var options = {params : this.getParams(q)};
14817 options.add = true;
14818 options.params.start = this.page * this.pageSize;
14821 this.store.load(options);
14824 * this code will make the page width larger, at the beginning, the list not align correctly,
14825 * we should expand the list on onLoad
14826 * so command out it
14831 this.selectedIndex = -1;
14836 this.loadNext = false;
14840 getParams : function(q){
14842 //p[this.queryParam] = q;
14846 p.limit = this.pageSize;
14852 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14854 collapse : function(){
14855 if(!this.isExpanded()){
14861 this.hasFocus = false;
14865 this.cancelBtn.hide();
14866 this.trigger.show();
14869 this.tickableInputEl().dom.value = '';
14870 this.tickableInputEl().blur();
14875 Roo.get(document).un('mousedown', this.collapseIf, this);
14876 Roo.get(document).un('mousewheel', this.collapseIf, this);
14877 if (!this.editable) {
14878 Roo.get(document).un('keydown', this.listKeyPress, this);
14880 this.fireEvent('collapse', this);
14886 collapseIf : function(e){
14887 var in_combo = e.within(this.el);
14888 var in_list = e.within(this.list);
14889 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14891 if (in_combo || in_list || is_list) {
14892 //e.stopPropagation();
14897 this.onTickableFooterButtonClick(e, false, false);
14905 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14907 expand : function(){
14909 if(this.isExpanded() || !this.hasFocus){
14913 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14914 this.list.setWidth(lw);
14920 this.restrictHeight();
14924 this.tickItems = Roo.apply([], this.item);
14927 this.cancelBtn.show();
14928 this.trigger.hide();
14931 this.tickableInputEl().focus();
14936 Roo.get(document).on('mousedown', this.collapseIf, this);
14937 Roo.get(document).on('mousewheel', this.collapseIf, this);
14938 if (!this.editable) {
14939 Roo.get(document).on('keydown', this.listKeyPress, this);
14942 this.fireEvent('expand', this);
14946 // Implements the default empty TriggerField.onTriggerClick function
14947 onTriggerClick : function(e)
14949 Roo.log('trigger click');
14951 if(this.disabled || !this.triggerList){
14956 this.loadNext = false;
14958 if(this.isExpanded()){
14960 if (!this.blockFocus) {
14961 this.inputEl().focus();
14965 this.hasFocus = true;
14966 if(this.triggerAction == 'all') {
14967 this.doQuery(this.allQuery, true);
14969 this.doQuery(this.getRawValue());
14971 if (!this.blockFocus) {
14972 this.inputEl().focus();
14977 onTickableTriggerClick : function(e)
14984 this.loadNext = false;
14985 this.hasFocus = true;
14987 if(this.triggerAction == 'all') {
14988 this.doQuery(this.allQuery, true);
14990 this.doQuery(this.getRawValue());
14994 onSearchFieldClick : function(e)
14996 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14997 this.onTickableFooterButtonClick(e, false, false);
15001 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
15006 this.loadNext = false;
15007 this.hasFocus = true;
15009 if(this.triggerAction == 'all') {
15010 this.doQuery(this.allQuery, true);
15012 this.doQuery(this.getRawValue());
15016 listKeyPress : function(e)
15018 //Roo.log('listkeypress');
15019 // scroll to first matching element based on key pres..
15020 if (e.isSpecialKey()) {
15023 var k = String.fromCharCode(e.getKey()).toUpperCase();
15026 var csel = this.view.getSelectedNodes();
15027 var cselitem = false;
15029 var ix = this.view.indexOf(csel[0]);
15030 cselitem = this.store.getAt(ix);
15031 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15037 this.store.each(function(v) {
15039 // start at existing selection.
15040 if (cselitem.id == v.id) {
15046 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15047 match = this.store.indexOf(v);
15053 if (match === false) {
15054 return true; // no more action?
15057 this.view.select(match);
15058 var sn = Roo.get(this.view.getSelectedNodes()[0]);
15059 sn.scrollIntoView(sn.dom.parentNode, false);
15062 onViewScroll : function(e, t){
15064 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){
15068 this.hasQuery = true;
15070 this.loading = this.list.select('.loading', true).first();
15072 if(this.loading === null){
15073 this.list.createChild({
15075 cls: 'loading roo-select2-more-results roo-select2-active',
15076 html: 'Loading more results...'
15079 this.loading = this.list.select('.loading', true).first();
15081 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15083 this.loading.hide();
15086 this.loading.show();
15091 this.loadNext = true;
15093 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15098 addItem : function(o)
15100 var dv = ''; // display value
15102 if (this.displayField) {
15103 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15105 // this is an error condition!!!
15106 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
15113 var choice = this.choices.createChild({
15115 cls: 'roo-select2-search-choice',
15124 cls: 'roo-select2-search-choice-close fa fa-times',
15129 }, this.searchField);
15131 var close = choice.select('a.roo-select2-search-choice-close', true).first();
15133 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15141 this.inputEl().dom.value = '';
15146 onRemoveItem : function(e, _self, o)
15148 e.preventDefault();
15150 this.lastItem = Roo.apply([], this.item);
15152 var index = this.item.indexOf(o.data) * 1;
15155 Roo.log('not this item?!');
15159 this.item.splice(index, 1);
15164 this.fireEvent('remove', this, e);
15170 syncValue : function()
15172 if(!this.item.length){
15179 Roo.each(this.item, function(i){
15180 if(_this.valueField){
15181 value.push(i[_this.valueField]);
15188 this.value = value.join(',');
15190 if(this.hiddenField){
15191 this.hiddenField.dom.value = this.value;
15194 this.store.fireEvent("datachanged", this.store);
15199 clearItem : function()
15201 if(!this.multiple){
15207 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15215 if(this.tickable && !Roo.isTouch){
15216 this.view.refresh();
15220 inputEl: function ()
15222 if(Roo.isIOS && this.useNativeIOS){
15223 return this.el.select('select.roo-ios-select', true).first();
15226 if(Roo.isTouch && this.mobileTouchView){
15227 return this.el.select('input.form-control',true).first();
15231 return this.searchField;
15234 return this.el.select('input.form-control',true).first();
15237 onTickableFooterButtonClick : function(e, btn, el)
15239 e.preventDefault();
15241 this.lastItem = Roo.apply([], this.item);
15243 if(btn && btn.name == 'cancel'){
15244 this.tickItems = Roo.apply([], this.item);
15253 Roo.each(this.tickItems, function(o){
15261 validate : function()
15263 if(this.getVisibilityEl().hasClass('hidden')){
15267 var v = this.getRawValue();
15270 v = this.getValue();
15273 if(this.disabled || this.allowBlank || v.length){
15278 this.markInvalid();
15282 tickableInputEl : function()
15284 if(!this.tickable || !this.editable){
15285 return this.inputEl();
15288 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15292 getAutoCreateTouchView : function()
15297 cls: 'form-group' //input-group
15303 type : this.inputType,
15304 cls : 'form-control x-combo-noedit',
15305 autocomplete: 'new-password',
15306 placeholder : this.placeholder || '',
15311 input.name = this.name;
15315 input.cls += ' input-' + this.size;
15318 if (this.disabled) {
15319 input.disabled = true;
15330 inputblock.cls += ' input-group';
15332 inputblock.cn.unshift({
15334 cls : 'input-group-addon input-group-prepend input-group-text',
15339 if(this.removable && !this.multiple){
15340 inputblock.cls += ' roo-removable';
15342 inputblock.cn.push({
15345 cls : 'roo-combo-removable-btn close'
15349 if(this.hasFeedback && !this.allowBlank){
15351 inputblock.cls += ' has-feedback';
15353 inputblock.cn.push({
15355 cls: 'glyphicon form-control-feedback'
15362 inputblock.cls += (this.before) ? '' : ' input-group';
15364 inputblock.cn.push({
15366 cls : 'input-group-addon input-group-append input-group-text',
15372 var ibwrap = inputblock;
15377 cls: 'roo-select2-choices',
15381 cls: 'roo-select2-search-field',
15394 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15399 cls: 'form-hidden-field'
15405 if(!this.multiple && this.showToggleBtn){
15411 if (this.caret != false) {
15414 cls: 'fa fa-' + this.caret
15421 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15423 Roo.bootstrap.version == 3 ? caret : '',
15426 cls: 'combobox-clear',
15440 combobox.cls += ' roo-select2-container-multi';
15443 var align = this.labelAlign || this.parentLabelAlign();
15445 if (align ==='left' && this.fieldLabel.length) {
15450 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15451 tooltip : 'This field is required'
15455 cls : 'control-label col-form-label',
15456 html : this.fieldLabel
15467 var labelCfg = cfg.cn[1];
15468 var contentCfg = cfg.cn[2];
15471 if(this.indicatorpos == 'right'){
15476 cls : 'control-label col-form-label',
15480 html : this.fieldLabel
15484 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15485 tooltip : 'This field is required'
15498 labelCfg = cfg.cn[0];
15499 contentCfg = cfg.cn[1];
15504 if(this.labelWidth > 12){
15505 labelCfg.style = "width: " + this.labelWidth + 'px';
15508 if(this.labelWidth < 13 && this.labelmd == 0){
15509 this.labelmd = this.labelWidth;
15512 if(this.labellg > 0){
15513 labelCfg.cls += ' col-lg-' + this.labellg;
15514 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15517 if(this.labelmd > 0){
15518 labelCfg.cls += ' col-md-' + this.labelmd;
15519 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15522 if(this.labelsm > 0){
15523 labelCfg.cls += ' col-sm-' + this.labelsm;
15524 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15527 if(this.labelxs > 0){
15528 labelCfg.cls += ' col-xs-' + this.labelxs;
15529 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15533 } else if ( this.fieldLabel.length) {
15537 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15538 tooltip : 'This field is required'
15542 cls : 'control-label',
15543 html : this.fieldLabel
15554 if(this.indicatorpos == 'right'){
15558 cls : 'control-label',
15559 html : this.fieldLabel,
15563 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15564 tooltip : 'This field is required'
15581 var settings = this;
15583 ['xs','sm','md','lg'].map(function(size){
15584 if (settings[size]) {
15585 cfg.cls += ' col-' + size + '-' + settings[size];
15592 initTouchView : function()
15594 this.renderTouchView();
15596 this.touchViewEl.on('scroll', function(){
15597 this.el.dom.scrollTop = 0;
15600 this.originalValue = this.getValue();
15602 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15604 this.inputEl().on("click", this.showTouchView, this);
15605 if (this.triggerEl) {
15606 this.triggerEl.on("click", this.showTouchView, this);
15610 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15611 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15613 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15615 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15616 this.store.on('load', this.onTouchViewLoad, this);
15617 this.store.on('loadexception', this.onTouchViewLoadException, this);
15619 if(this.hiddenName){
15621 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15623 this.hiddenField.dom.value =
15624 this.hiddenValue !== undefined ? this.hiddenValue :
15625 this.value !== undefined ? this.value : '';
15627 this.el.dom.removeAttribute('name');
15628 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15632 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15633 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15636 if(this.removable && !this.multiple){
15637 var close = this.closeTriggerEl();
15639 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15640 close.on('click', this.removeBtnClick, this, close);
15644 * fix the bug in Safari iOS8
15646 this.inputEl().on("focus", function(e){
15647 document.activeElement.blur();
15650 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15657 renderTouchView : function()
15659 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15660 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15662 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15663 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15665 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15666 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15667 this.touchViewBodyEl.setStyle('overflow', 'auto');
15669 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15670 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15672 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15673 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15677 showTouchView : function()
15683 this.touchViewHeaderEl.hide();
15685 if(this.modalTitle.length){
15686 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15687 this.touchViewHeaderEl.show();
15690 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15691 this.touchViewEl.show();
15693 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15695 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15696 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15698 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15700 if(this.modalTitle.length){
15701 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15704 this.touchViewBodyEl.setHeight(bodyHeight);
15708 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15710 this.touchViewEl.addClass('in');
15713 if(this._touchViewMask){
15714 Roo.get(document.body).addClass("x-body-masked");
15715 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15716 this._touchViewMask.setStyle('z-index', 10000);
15717 this._touchViewMask.addClass('show');
15720 this.doTouchViewQuery();
15724 hideTouchView : function()
15726 this.touchViewEl.removeClass('in');
15730 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15732 this.touchViewEl.setStyle('display', 'none');
15735 if(this._touchViewMask){
15736 this._touchViewMask.removeClass('show');
15737 Roo.get(document.body).removeClass("x-body-masked");
15741 setTouchViewValue : function()
15748 Roo.each(this.tickItems, function(o){
15753 this.hideTouchView();
15756 doTouchViewQuery : function()
15765 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15769 if(!this.alwaysQuery || this.mode == 'local'){
15770 this.onTouchViewLoad();
15777 onTouchViewBeforeLoad : function(combo,opts)
15783 onTouchViewLoad : function()
15785 if(this.store.getCount() < 1){
15786 this.onTouchViewEmptyResults();
15790 this.clearTouchView();
15792 var rawValue = this.getRawValue();
15794 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15796 this.tickItems = [];
15798 this.store.data.each(function(d, rowIndex){
15799 var row = this.touchViewListGroup.createChild(template);
15801 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15802 row.addClass(d.data.cls);
15805 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15808 html : d.data[this.displayField]
15811 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15812 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15815 row.removeClass('selected');
15816 if(!this.multiple && this.valueField &&
15817 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15820 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15821 row.addClass('selected');
15824 if(this.multiple && this.valueField &&
15825 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15829 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15830 this.tickItems.push(d.data);
15833 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15837 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15839 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15841 if(this.modalTitle.length){
15842 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15845 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15847 if(this.mobile_restrict_height && listHeight < bodyHeight){
15848 this.touchViewBodyEl.setHeight(listHeight);
15853 if(firstChecked && listHeight > bodyHeight){
15854 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15859 onTouchViewLoadException : function()
15861 this.hideTouchView();
15864 onTouchViewEmptyResults : function()
15866 this.clearTouchView();
15868 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15870 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15874 clearTouchView : function()
15876 this.touchViewListGroup.dom.innerHTML = '';
15879 onTouchViewClick : function(e, el, o)
15881 e.preventDefault();
15884 var rowIndex = o.rowIndex;
15886 var r = this.store.getAt(rowIndex);
15888 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15890 if(!this.multiple){
15891 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15892 c.dom.removeAttribute('checked');
15895 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15897 this.setFromData(r.data);
15899 var close = this.closeTriggerEl();
15905 this.hideTouchView();
15907 this.fireEvent('select', this, r, rowIndex);
15912 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15913 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15914 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15918 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15919 this.addItem(r.data);
15920 this.tickItems.push(r.data);
15924 getAutoCreateNativeIOS : function()
15927 cls: 'form-group' //input-group,
15932 cls : 'roo-ios-select'
15936 combobox.name = this.name;
15939 if (this.disabled) {
15940 combobox.disabled = true;
15943 var settings = this;
15945 ['xs','sm','md','lg'].map(function(size){
15946 if (settings[size]) {
15947 cfg.cls += ' col-' + size + '-' + settings[size];
15957 initIOSView : function()
15959 this.store.on('load', this.onIOSViewLoad, this);
15964 onIOSViewLoad : function()
15966 if(this.store.getCount() < 1){
15970 this.clearIOSView();
15972 if(this.allowBlank) {
15974 var default_text = '-- SELECT --';
15976 if(this.placeholder.length){
15977 default_text = this.placeholder;
15980 if(this.emptyTitle.length){
15981 default_text += ' - ' + this.emptyTitle + ' -';
15984 var opt = this.inputEl().createChild({
15987 html : default_text
15991 o[this.valueField] = 0;
15992 o[this.displayField] = default_text;
15994 this.ios_options.push({
16001 this.store.data.each(function(d, rowIndex){
16005 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16006 html = d.data[this.displayField];
16011 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16012 value = d.data[this.valueField];
16021 if(this.value == d.data[this.valueField]){
16022 option['selected'] = true;
16025 var opt = this.inputEl().createChild(option);
16027 this.ios_options.push({
16034 this.inputEl().on('change', function(){
16035 this.fireEvent('select', this);
16040 clearIOSView: function()
16042 this.inputEl().dom.innerHTML = '';
16044 this.ios_options = [];
16047 setIOSValue: function(v)
16051 if(!this.ios_options){
16055 Roo.each(this.ios_options, function(opts){
16057 opts.el.dom.removeAttribute('selected');
16059 if(opts.data[this.valueField] != v){
16063 opts.el.dom.setAttribute('selected', true);
16069 * @cfg {Boolean} grow
16073 * @cfg {Number} growMin
16077 * @cfg {Number} growMax
16086 Roo.apply(Roo.bootstrap.ComboBox, {
16090 cls: 'modal-header',
16112 cls: 'list-group-item',
16116 cls: 'roo-combobox-list-group-item-value'
16120 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16134 listItemCheckbox : {
16136 cls: 'list-group-item',
16140 cls: 'roo-combobox-list-group-item-value'
16144 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16160 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16165 cls: 'modal-footer',
16173 cls: 'col-xs-6 text-left',
16176 cls: 'btn btn-danger roo-touch-view-cancel',
16182 cls: 'col-xs-6 text-right',
16185 cls: 'btn btn-success roo-touch-view-ok',
16196 Roo.apply(Roo.bootstrap.ComboBox, {
16198 touchViewTemplate : {
16200 cls: 'modal fade roo-combobox-touch-view',
16204 cls: 'modal-dialog',
16205 style : 'position:fixed', // we have to fix position....
16209 cls: 'modal-content',
16211 Roo.bootstrap.ComboBox.header,
16212 Roo.bootstrap.ComboBox.body,
16213 Roo.bootstrap.ComboBox.footer
16222 * Ext JS Library 1.1.1
16223 * Copyright(c) 2006-2007, Ext JS, LLC.
16225 * Originally Released Under LGPL - original licence link has changed is not relivant.
16228 * <script type="text/javascript">
16233 * @extends Roo.util.Observable
16234 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
16235 * This class also supports single and multi selection modes. <br>
16236 * Create a data model bound view:
16238 var store = new Roo.data.Store(...);
16240 var view = new Roo.View({
16242 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
16244 singleSelect: true,
16245 selectedClass: "ydataview-selected",
16249 // listen for node click?
16250 view.on("click", function(vw, index, node, e){
16251 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16255 dataModel.load("foobar.xml");
16257 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16259 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16260 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16262 * Note: old style constructor is still suported (container, template, config)
16265 * Create a new View
16266 * @param {Object} config The config object
16269 Roo.View = function(config, depreciated_tpl, depreciated_config){
16271 this.parent = false;
16273 if (typeof(depreciated_tpl) == 'undefined') {
16274 // new way.. - universal constructor.
16275 Roo.apply(this, config);
16276 this.el = Roo.get(this.el);
16279 this.el = Roo.get(config);
16280 this.tpl = depreciated_tpl;
16281 Roo.apply(this, depreciated_config);
16283 this.wrapEl = this.el.wrap().wrap();
16284 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16287 if(typeof(this.tpl) == "string"){
16288 this.tpl = new Roo.Template(this.tpl);
16290 // support xtype ctors..
16291 this.tpl = new Roo.factory(this.tpl, Roo);
16295 this.tpl.compile();
16300 * @event beforeclick
16301 * Fires before a click is processed. Returns false to cancel the default action.
16302 * @param {Roo.View} this
16303 * @param {Number} index The index of the target node
16304 * @param {HTMLElement} node The target node
16305 * @param {Roo.EventObject} e The raw event object
16307 "beforeclick" : true,
16310 * Fires when a template node is clicked.
16311 * @param {Roo.View} this
16312 * @param {Number} index The index of the target node
16313 * @param {HTMLElement} node The target node
16314 * @param {Roo.EventObject} e The raw event object
16319 * Fires when a template node is double clicked.
16320 * @param {Roo.View} this
16321 * @param {Number} index The index of the target node
16322 * @param {HTMLElement} node The target node
16323 * @param {Roo.EventObject} e The raw event object
16327 * @event contextmenu
16328 * Fires when a template node is right clicked.
16329 * @param {Roo.View} this
16330 * @param {Number} index The index of the target node
16331 * @param {HTMLElement} node The target node
16332 * @param {Roo.EventObject} e The raw event object
16334 "contextmenu" : true,
16336 * @event selectionchange
16337 * Fires when the selected nodes change.
16338 * @param {Roo.View} this
16339 * @param {Array} selections Array of the selected nodes
16341 "selectionchange" : true,
16344 * @event beforeselect
16345 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16346 * @param {Roo.View} this
16347 * @param {HTMLElement} node The node to be selected
16348 * @param {Array} selections Array of currently selected nodes
16350 "beforeselect" : true,
16352 * @event preparedata
16353 * Fires on every row to render, to allow you to change the data.
16354 * @param {Roo.View} this
16355 * @param {Object} data to be rendered (change this)
16357 "preparedata" : true
16365 "click": this.onClick,
16366 "dblclick": this.onDblClick,
16367 "contextmenu": this.onContextMenu,
16371 this.selections = [];
16373 this.cmp = new Roo.CompositeElementLite([]);
16375 this.store = Roo.factory(this.store, Roo.data);
16376 this.setStore(this.store, true);
16379 if ( this.footer && this.footer.xtype) {
16381 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16383 this.footer.dataSource = this.store;
16384 this.footer.container = fctr;
16385 this.footer = Roo.factory(this.footer, Roo);
16386 fctr.insertFirst(this.el);
16388 // this is a bit insane - as the paging toolbar seems to detach the el..
16389 // dom.parentNode.parentNode.parentNode
16390 // they get detached?
16394 Roo.View.superclass.constructor.call(this);
16399 Roo.extend(Roo.View, Roo.util.Observable, {
16402 * @cfg {Roo.data.Store} store Data store to load data from.
16407 * @cfg {String|Roo.Element} el The container element.
16412 * @cfg {String|Roo.Template} tpl The template used by this View
16416 * @cfg {String} dataName the named area of the template to use as the data area
16417 * Works with domtemplates roo-name="name"
16421 * @cfg {String} selectedClass The css class to add to selected nodes
16423 selectedClass : "x-view-selected",
16425 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16430 * @cfg {String} text to display on mask (default Loading)
16434 * @cfg {Boolean} multiSelect Allow multiple selection
16436 multiSelect : false,
16438 * @cfg {Boolean} singleSelect Allow single selection
16440 singleSelect: false,
16443 * @cfg {Boolean} toggleSelect - selecting
16445 toggleSelect : false,
16448 * @cfg {Boolean} tickable - selecting
16453 * Returns the element this view is bound to.
16454 * @return {Roo.Element}
16456 getEl : function(){
16457 return this.wrapEl;
16463 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16465 refresh : function(){
16466 //Roo.log('refresh');
16469 // if we are using something like 'domtemplate', then
16470 // the what gets used is:
16471 // t.applySubtemplate(NAME, data, wrapping data..)
16472 // the outer template then get' applied with
16473 // the store 'extra data'
16474 // and the body get's added to the
16475 // roo-name="data" node?
16476 // <span class='roo-tpl-{name}'></span> ?????
16480 this.clearSelections();
16481 this.el.update("");
16483 var records = this.store.getRange();
16484 if(records.length < 1) {
16486 // is this valid?? = should it render a template??
16488 this.el.update(this.emptyText);
16492 if (this.dataName) {
16493 this.el.update(t.apply(this.store.meta)); //????
16494 el = this.el.child('.roo-tpl-' + this.dataName);
16497 for(var i = 0, len = records.length; i < len; i++){
16498 var data = this.prepareData(records[i].data, i, records[i]);
16499 this.fireEvent("preparedata", this, data, i, records[i]);
16501 var d = Roo.apply({}, data);
16504 Roo.apply(d, {'roo-id' : Roo.id()});
16508 Roo.each(this.parent.item, function(item){
16509 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16512 Roo.apply(d, {'roo-data-checked' : 'checked'});
16516 html[html.length] = Roo.util.Format.trim(
16518 t.applySubtemplate(this.dataName, d, this.store.meta) :
16525 el.update(html.join(""));
16526 this.nodes = el.dom.childNodes;
16527 this.updateIndexes(0);
16532 * Function to override to reformat the data that is sent to
16533 * the template for each node.
16534 * DEPRICATED - use the preparedata event handler.
16535 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16536 * a JSON object for an UpdateManager bound view).
16538 prepareData : function(data, index, record)
16540 this.fireEvent("preparedata", this, data, index, record);
16544 onUpdate : function(ds, record){
16545 // Roo.log('on update');
16546 this.clearSelections();
16547 var index = this.store.indexOf(record);
16548 var n = this.nodes[index];
16549 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16550 n.parentNode.removeChild(n);
16551 this.updateIndexes(index, index);
16557 onAdd : function(ds, records, index)
16559 //Roo.log(['on Add', ds, records, index] );
16560 this.clearSelections();
16561 if(this.nodes.length == 0){
16565 var n = this.nodes[index];
16566 for(var i = 0, len = records.length; i < len; i++){
16567 var d = this.prepareData(records[i].data, i, records[i]);
16569 this.tpl.insertBefore(n, d);
16572 this.tpl.append(this.el, d);
16575 this.updateIndexes(index);
16578 onRemove : function(ds, record, index){
16579 // Roo.log('onRemove');
16580 this.clearSelections();
16581 var el = this.dataName ?
16582 this.el.child('.roo-tpl-' + this.dataName) :
16585 el.dom.removeChild(this.nodes[index]);
16586 this.updateIndexes(index);
16590 * Refresh an individual node.
16591 * @param {Number} index
16593 refreshNode : function(index){
16594 this.onUpdate(this.store, this.store.getAt(index));
16597 updateIndexes : function(startIndex, endIndex){
16598 var ns = this.nodes;
16599 startIndex = startIndex || 0;
16600 endIndex = endIndex || ns.length - 1;
16601 for(var i = startIndex; i <= endIndex; i++){
16602 ns[i].nodeIndex = i;
16607 * Changes the data store this view uses and refresh the view.
16608 * @param {Store} store
16610 setStore : function(store, initial){
16611 if(!initial && this.store){
16612 this.store.un("datachanged", this.refresh);
16613 this.store.un("add", this.onAdd);
16614 this.store.un("remove", this.onRemove);
16615 this.store.un("update", this.onUpdate);
16616 this.store.un("clear", this.refresh);
16617 this.store.un("beforeload", this.onBeforeLoad);
16618 this.store.un("load", this.onLoad);
16619 this.store.un("loadexception", this.onLoad);
16623 store.on("datachanged", this.refresh, this);
16624 store.on("add", this.onAdd, this);
16625 store.on("remove", this.onRemove, this);
16626 store.on("update", this.onUpdate, this);
16627 store.on("clear", this.refresh, this);
16628 store.on("beforeload", this.onBeforeLoad, this);
16629 store.on("load", this.onLoad, this);
16630 store.on("loadexception", this.onLoad, this);
16638 * onbeforeLoad - masks the loading area.
16641 onBeforeLoad : function(store,opts)
16643 //Roo.log('onBeforeLoad');
16645 this.el.update("");
16647 this.el.mask(this.mask ? this.mask : "Loading" );
16649 onLoad : function ()
16656 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16657 * @param {HTMLElement} node
16658 * @return {HTMLElement} The template node
16660 findItemFromChild : function(node){
16661 var el = this.dataName ?
16662 this.el.child('.roo-tpl-' + this.dataName,true) :
16665 if(!node || node.parentNode == el){
16668 var p = node.parentNode;
16669 while(p && p != el){
16670 if(p.parentNode == el){
16679 onClick : function(e){
16680 var item = this.findItemFromChild(e.getTarget());
16682 var index = this.indexOf(item);
16683 if(this.onItemClick(item, index, e) !== false){
16684 this.fireEvent("click", this, index, item, e);
16687 this.clearSelections();
16692 onContextMenu : function(e){
16693 var item = this.findItemFromChild(e.getTarget());
16695 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16700 onDblClick : function(e){
16701 var item = this.findItemFromChild(e.getTarget());
16703 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16707 onItemClick : function(item, index, e)
16709 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16712 if (this.toggleSelect) {
16713 var m = this.isSelected(item) ? 'unselect' : 'select';
16716 _t[m](item, true, false);
16719 if(this.multiSelect || this.singleSelect){
16720 if(this.multiSelect && e.shiftKey && this.lastSelection){
16721 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16723 this.select(item, this.multiSelect && e.ctrlKey);
16724 this.lastSelection = item;
16727 if(!this.tickable){
16728 e.preventDefault();
16736 * Get the number of selected nodes.
16739 getSelectionCount : function(){
16740 return this.selections.length;
16744 * Get the currently selected nodes.
16745 * @return {Array} An array of HTMLElements
16747 getSelectedNodes : function(){
16748 return this.selections;
16752 * Get the indexes of the selected nodes.
16755 getSelectedIndexes : function(){
16756 var indexes = [], s = this.selections;
16757 for(var i = 0, len = s.length; i < len; i++){
16758 indexes.push(s[i].nodeIndex);
16764 * Clear all selections
16765 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16767 clearSelections : function(suppressEvent){
16768 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16769 this.cmp.elements = this.selections;
16770 this.cmp.removeClass(this.selectedClass);
16771 this.selections = [];
16772 if(!suppressEvent){
16773 this.fireEvent("selectionchange", this, this.selections);
16779 * Returns true if the passed node is selected
16780 * @param {HTMLElement/Number} node The node or node index
16781 * @return {Boolean}
16783 isSelected : function(node){
16784 var s = this.selections;
16788 node = this.getNode(node);
16789 return s.indexOf(node) !== -1;
16794 * @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
16795 * @param {Boolean} keepExisting (optional) true to keep existing selections
16796 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16798 select : function(nodeInfo, keepExisting, suppressEvent){
16799 if(nodeInfo instanceof Array){
16801 this.clearSelections(true);
16803 for(var i = 0, len = nodeInfo.length; i < len; i++){
16804 this.select(nodeInfo[i], true, true);
16808 var node = this.getNode(nodeInfo);
16809 if(!node || this.isSelected(node)){
16810 return; // already selected.
16813 this.clearSelections(true);
16816 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16817 Roo.fly(node).addClass(this.selectedClass);
16818 this.selections.push(node);
16819 if(!suppressEvent){
16820 this.fireEvent("selectionchange", this, this.selections);
16828 * @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
16829 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16830 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16832 unselect : function(nodeInfo, keepExisting, suppressEvent)
16834 if(nodeInfo instanceof Array){
16835 Roo.each(this.selections, function(s) {
16836 this.unselect(s, nodeInfo);
16840 var node = this.getNode(nodeInfo);
16841 if(!node || !this.isSelected(node)){
16842 //Roo.log("not selected");
16843 return; // not selected.
16847 Roo.each(this.selections, function(s) {
16849 Roo.fly(node).removeClass(this.selectedClass);
16856 this.selections= ns;
16857 this.fireEvent("selectionchange", this, this.selections);
16861 * Gets a template node.
16862 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16863 * @return {HTMLElement} The node or null if it wasn't found
16865 getNode : function(nodeInfo){
16866 if(typeof nodeInfo == "string"){
16867 return document.getElementById(nodeInfo);
16868 }else if(typeof nodeInfo == "number"){
16869 return this.nodes[nodeInfo];
16875 * Gets a range template nodes.
16876 * @param {Number} startIndex
16877 * @param {Number} endIndex
16878 * @return {Array} An array of nodes
16880 getNodes : function(start, end){
16881 var ns = this.nodes;
16882 start = start || 0;
16883 end = typeof end == "undefined" ? ns.length - 1 : end;
16886 for(var i = start; i <= end; i++){
16890 for(var i = start; i >= end; i--){
16898 * Finds the index of the passed node
16899 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16900 * @return {Number} The index of the node or -1
16902 indexOf : function(node){
16903 node = this.getNode(node);
16904 if(typeof node.nodeIndex == "number"){
16905 return node.nodeIndex;
16907 var ns = this.nodes;
16908 for(var i = 0, len = ns.length; i < len; i++){
16919 * based on jquery fullcalendar
16923 Roo.bootstrap = Roo.bootstrap || {};
16925 * @class Roo.bootstrap.Calendar
16926 * @extends Roo.bootstrap.Component
16927 * Bootstrap Calendar class
16928 * @cfg {Boolean} loadMask (true|false) default false
16929 * @cfg {Object} header generate the user specific header of the calendar, default false
16932 * Create a new Container
16933 * @param {Object} config The config object
16938 Roo.bootstrap.Calendar = function(config){
16939 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16943 * Fires when a date is selected
16944 * @param {DatePicker} this
16945 * @param {Date} date The selected date
16949 * @event monthchange
16950 * Fires when the displayed month changes
16951 * @param {DatePicker} this
16952 * @param {Date} date The selected month
16954 'monthchange': true,
16956 * @event evententer
16957 * Fires when mouse over an event
16958 * @param {Calendar} this
16959 * @param {event} Event
16961 'evententer': true,
16963 * @event eventleave
16964 * Fires when the mouse leaves an
16965 * @param {Calendar} this
16968 'eventleave': true,
16970 * @event eventclick
16971 * Fires when the mouse click an
16972 * @param {Calendar} this
16981 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16984 * @cfg {Number} startDay
16985 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16993 getAutoCreate : function(){
16996 var fc_button = function(name, corner, style, content ) {
16997 return Roo.apply({},{
16999 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
17001 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
17004 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
17015 style : 'width:100%',
17022 cls : 'fc-header-left',
17024 fc_button('prev', 'left', 'arrow', '‹' ),
17025 fc_button('next', 'right', 'arrow', '›' ),
17026 { tag: 'span', cls: 'fc-header-space' },
17027 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
17035 cls : 'fc-header-center',
17039 cls: 'fc-header-title',
17042 html : 'month / year'
17050 cls : 'fc-header-right',
17052 /* fc_button('month', 'left', '', 'month' ),
17053 fc_button('week', '', '', 'week' ),
17054 fc_button('day', 'right', '', 'day' )
17066 header = this.header;
17069 var cal_heads = function() {
17071 // fixme - handle this.
17073 for (var i =0; i < Date.dayNames.length; i++) {
17074 var d = Date.dayNames[i];
17077 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17078 html : d.substring(0,3)
17082 ret[0].cls += ' fc-first';
17083 ret[6].cls += ' fc-last';
17086 var cal_cell = function(n) {
17089 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17094 cls: 'fc-day-number',
17098 cls: 'fc-day-content',
17102 style: 'position: relative;' // height: 17px;
17114 var cal_rows = function() {
17117 for (var r = 0; r < 6; r++) {
17124 for (var i =0; i < Date.dayNames.length; i++) {
17125 var d = Date.dayNames[i];
17126 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17129 row.cn[0].cls+=' fc-first';
17130 row.cn[0].cn[0].style = 'min-height:90px';
17131 row.cn[6].cls+=' fc-last';
17135 ret[0].cls += ' fc-first';
17136 ret[4].cls += ' fc-prev-last';
17137 ret[5].cls += ' fc-last';
17144 cls: 'fc-border-separate',
17145 style : 'width:100%',
17153 cls : 'fc-first fc-last',
17171 cls : 'fc-content',
17172 style : "position: relative;",
17175 cls : 'fc-view fc-view-month fc-grid',
17176 style : 'position: relative',
17177 unselectable : 'on',
17180 cls : 'fc-event-container',
17181 style : 'position:absolute;z-index:8;top:0;left:0;'
17199 initEvents : function()
17202 throw "can not find store for calendar";
17208 style: "text-align:center",
17212 style: "background-color:white;width:50%;margin:250 auto",
17216 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
17227 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17229 var size = this.el.select('.fc-content', true).first().getSize();
17230 this.maskEl.setSize(size.width, size.height);
17231 this.maskEl.enableDisplayMode("block");
17232 if(!this.loadMask){
17233 this.maskEl.hide();
17236 this.store = Roo.factory(this.store, Roo.data);
17237 this.store.on('load', this.onLoad, this);
17238 this.store.on('beforeload', this.onBeforeLoad, this);
17242 this.cells = this.el.select('.fc-day',true);
17243 //Roo.log(this.cells);
17244 this.textNodes = this.el.query('.fc-day-number');
17245 this.cells.addClassOnOver('fc-state-hover');
17247 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17248 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17249 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17250 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17252 this.on('monthchange', this.onMonthChange, this);
17254 this.update(new Date().clearTime());
17257 resize : function() {
17258 var sz = this.el.getSize();
17260 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17261 this.el.select('.fc-day-content div',true).setHeight(34);
17266 showPrevMonth : function(e){
17267 this.update(this.activeDate.add("mo", -1));
17269 showToday : function(e){
17270 this.update(new Date().clearTime());
17273 showNextMonth : function(e){
17274 this.update(this.activeDate.add("mo", 1));
17278 showPrevYear : function(){
17279 this.update(this.activeDate.add("y", -1));
17283 showNextYear : function(){
17284 this.update(this.activeDate.add("y", 1));
17289 update : function(date)
17291 var vd = this.activeDate;
17292 this.activeDate = date;
17293 // if(vd && this.el){
17294 // var t = date.getTime();
17295 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17296 // Roo.log('using add remove');
17298 // this.fireEvent('monthchange', this, date);
17300 // this.cells.removeClass("fc-state-highlight");
17301 // this.cells.each(function(c){
17302 // if(c.dateValue == t){
17303 // c.addClass("fc-state-highlight");
17304 // setTimeout(function(){
17305 // try{c.dom.firstChild.focus();}catch(e){}
17315 var days = date.getDaysInMonth();
17317 var firstOfMonth = date.getFirstDateOfMonth();
17318 var startingPos = firstOfMonth.getDay()-this.startDay;
17320 if(startingPos < this.startDay){
17324 var pm = date.add(Date.MONTH, -1);
17325 var prevStart = pm.getDaysInMonth()-startingPos;
17327 this.cells = this.el.select('.fc-day',true);
17328 this.textNodes = this.el.query('.fc-day-number');
17329 this.cells.addClassOnOver('fc-state-hover');
17331 var cells = this.cells.elements;
17332 var textEls = this.textNodes;
17334 Roo.each(cells, function(cell){
17335 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17338 days += startingPos;
17340 // convert everything to numbers so it's fast
17341 var day = 86400000;
17342 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17345 //Roo.log(prevStart);
17347 var today = new Date().clearTime().getTime();
17348 var sel = date.clearTime().getTime();
17349 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17350 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17351 var ddMatch = this.disabledDatesRE;
17352 var ddText = this.disabledDatesText;
17353 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17354 var ddaysText = this.disabledDaysText;
17355 var format = this.format;
17357 var setCellClass = function(cal, cell){
17361 //Roo.log('set Cell Class');
17363 var t = d.getTime();
17367 cell.dateValue = t;
17369 cell.className += " fc-today";
17370 cell.className += " fc-state-highlight";
17371 cell.title = cal.todayText;
17374 // disable highlight in other month..
17375 //cell.className += " fc-state-highlight";
17380 cell.className = " fc-state-disabled";
17381 cell.title = cal.minText;
17385 cell.className = " fc-state-disabled";
17386 cell.title = cal.maxText;
17390 if(ddays.indexOf(d.getDay()) != -1){
17391 cell.title = ddaysText;
17392 cell.className = " fc-state-disabled";
17395 if(ddMatch && format){
17396 var fvalue = d.dateFormat(format);
17397 if(ddMatch.test(fvalue)){
17398 cell.title = ddText.replace("%0", fvalue);
17399 cell.className = " fc-state-disabled";
17403 if (!cell.initialClassName) {
17404 cell.initialClassName = cell.dom.className;
17407 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17412 for(; i < startingPos; i++) {
17413 textEls[i].innerHTML = (++prevStart);
17414 d.setDate(d.getDate()+1);
17416 cells[i].className = "fc-past fc-other-month";
17417 setCellClass(this, cells[i]);
17422 for(; i < days; i++){
17423 intDay = i - startingPos + 1;
17424 textEls[i].innerHTML = (intDay);
17425 d.setDate(d.getDate()+1);
17427 cells[i].className = ''; // "x-date-active";
17428 setCellClass(this, cells[i]);
17432 for(; i < 42; i++) {
17433 textEls[i].innerHTML = (++extraDays);
17434 d.setDate(d.getDate()+1);
17436 cells[i].className = "fc-future fc-other-month";
17437 setCellClass(this, cells[i]);
17440 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17442 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17444 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17445 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17447 if(totalRows != 6){
17448 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17449 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17452 this.fireEvent('monthchange', this, date);
17456 if(!this.internalRender){
17457 var main = this.el.dom.firstChild;
17458 var w = main.offsetWidth;
17459 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17460 Roo.fly(main).setWidth(w);
17461 this.internalRender = true;
17462 // opera does not respect the auto grow header center column
17463 // then, after it gets a width opera refuses to recalculate
17464 // without a second pass
17465 if(Roo.isOpera && !this.secondPass){
17466 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17467 this.secondPass = true;
17468 this.update.defer(10, this, [date]);
17475 findCell : function(dt) {
17476 dt = dt.clearTime().getTime();
17478 this.cells.each(function(c){
17479 //Roo.log("check " +c.dateValue + '?=' + dt);
17480 if(c.dateValue == dt){
17490 findCells : function(ev) {
17491 var s = ev.start.clone().clearTime().getTime();
17493 var e= ev.end.clone().clearTime().getTime();
17496 this.cells.each(function(c){
17497 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17499 if(c.dateValue > e){
17502 if(c.dateValue < s){
17511 // findBestRow: function(cells)
17515 // for (var i =0 ; i < cells.length;i++) {
17516 // ret = Math.max(cells[i].rows || 0,ret);
17523 addItem : function(ev)
17525 // look for vertical location slot in
17526 var cells = this.findCells(ev);
17528 // ev.row = this.findBestRow(cells);
17530 // work out the location.
17534 for(var i =0; i < cells.length; i++) {
17536 cells[i].row = cells[0].row;
17539 cells[i].row = cells[i].row + 1;
17549 if (crow.start.getY() == cells[i].getY()) {
17551 crow.end = cells[i];
17568 cells[0].events.push(ev);
17570 this.calevents.push(ev);
17573 clearEvents: function() {
17575 if(!this.calevents){
17579 Roo.each(this.cells.elements, function(c){
17585 Roo.each(this.calevents, function(e) {
17586 Roo.each(e.els, function(el) {
17587 el.un('mouseenter' ,this.onEventEnter, this);
17588 el.un('mouseleave' ,this.onEventLeave, this);
17593 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17599 renderEvents: function()
17603 this.cells.each(function(c) {
17612 if(c.row != c.events.length){
17613 r = 4 - (4 - (c.row - c.events.length));
17616 c.events = ev.slice(0, r);
17617 c.more = ev.slice(r);
17619 if(c.more.length && c.more.length == 1){
17620 c.events.push(c.more.pop());
17623 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17627 this.cells.each(function(c) {
17629 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17632 for (var e = 0; e < c.events.length; e++){
17633 var ev = c.events[e];
17634 var rows = ev.rows;
17636 for(var i = 0; i < rows.length; i++) {
17638 // how many rows should it span..
17641 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17642 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17644 unselectable : "on",
17647 cls: 'fc-event-inner',
17651 // cls: 'fc-event-time',
17652 // html : cells.length > 1 ? '' : ev.time
17656 cls: 'fc-event-title',
17657 html : String.format('{0}', ev.title)
17664 cls: 'ui-resizable-handle ui-resizable-e',
17665 html : '  '
17672 cfg.cls += ' fc-event-start';
17674 if ((i+1) == rows.length) {
17675 cfg.cls += ' fc-event-end';
17678 var ctr = _this.el.select('.fc-event-container',true).first();
17679 var cg = ctr.createChild(cfg);
17681 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17682 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17684 var r = (c.more.length) ? 1 : 0;
17685 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17686 cg.setWidth(ebox.right - sbox.x -2);
17688 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17689 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17690 cg.on('click', _this.onEventClick, _this, ev);
17701 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17702 style : 'position: absolute',
17703 unselectable : "on",
17706 cls: 'fc-event-inner',
17710 cls: 'fc-event-title',
17718 cls: 'ui-resizable-handle ui-resizable-e',
17719 html : '  '
17725 var ctr = _this.el.select('.fc-event-container',true).first();
17726 var cg = ctr.createChild(cfg);
17728 var sbox = c.select('.fc-day-content',true).first().getBox();
17729 var ebox = c.select('.fc-day-content',true).first().getBox();
17731 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17732 cg.setWidth(ebox.right - sbox.x -2);
17734 cg.on('click', _this.onMoreEventClick, _this, c.more);
17744 onEventEnter: function (e, el,event,d) {
17745 this.fireEvent('evententer', this, el, event);
17748 onEventLeave: function (e, el,event,d) {
17749 this.fireEvent('eventleave', this, el, event);
17752 onEventClick: function (e, el,event,d) {
17753 this.fireEvent('eventclick', this, el, event);
17756 onMonthChange: function () {
17760 onMoreEventClick: function(e, el, more)
17764 this.calpopover.placement = 'right';
17765 this.calpopover.setTitle('More');
17767 this.calpopover.setContent('');
17769 var ctr = this.calpopover.el.select('.popover-content', true).first();
17771 Roo.each(more, function(m){
17773 cls : 'fc-event-hori fc-event-draggable',
17776 var cg = ctr.createChild(cfg);
17778 cg.on('click', _this.onEventClick, _this, m);
17781 this.calpopover.show(el);
17786 onLoad: function ()
17788 this.calevents = [];
17791 if(this.store.getCount() > 0){
17792 this.store.data.each(function(d){
17795 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17796 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17797 time : d.data.start_time,
17798 title : d.data.title,
17799 description : d.data.description,
17800 venue : d.data.venue
17805 this.renderEvents();
17807 if(this.calevents.length && this.loadMask){
17808 this.maskEl.hide();
17812 onBeforeLoad: function()
17814 this.clearEvents();
17816 this.maskEl.show();
17830 * @class Roo.bootstrap.Popover
17831 * @extends Roo.bootstrap.Component
17832 * Bootstrap Popover class
17833 * @cfg {String} html contents of the popover (or false to use children..)
17834 * @cfg {String} title of popover (or false to hide)
17835 * @cfg {String} placement how it is placed
17836 * @cfg {String} trigger click || hover (or false to trigger manually)
17837 * @cfg {String} over what (parent or false to trigger manually.)
17838 * @cfg {Number} delay - delay before showing
17841 * Create a new Popover
17842 * @param {Object} config The config object
17845 Roo.bootstrap.Popover = function(config){
17846 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17852 * After the popover show
17854 * @param {Roo.bootstrap.Popover} this
17859 * After the popover hide
17861 * @param {Roo.bootstrap.Popover} this
17867 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17869 title: 'Fill in a title',
17872 placement : 'right',
17873 trigger : 'hover', // hover
17879 can_build_overlaid : false,
17881 getChildContainer : function()
17883 return this.el.select('.popover-content',true).first();
17886 getAutoCreate : function(){
17889 cls : 'popover roo-dynamic',
17890 style: 'display:block',
17896 cls : 'popover-inner',
17900 cls: 'popover-title popover-header',
17904 cls : 'popover-content popover-body',
17915 setTitle: function(str)
17918 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17920 setContent: function(str)
17923 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17925 // as it get's added to the bottom of the page.
17926 onRender : function(ct, position)
17928 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17930 var cfg = Roo.apply({}, this.getAutoCreate());
17934 cfg.cls += ' ' + this.cls;
17937 cfg.style = this.style;
17939 //Roo.log("adding to ");
17940 this.el = Roo.get(document.body).createChild(cfg, position);
17941 // Roo.log(this.el);
17946 initEvents : function()
17948 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17949 this.el.enableDisplayMode('block');
17951 if (this.over === false) {
17954 if (this.triggers === false) {
17957 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17958 var triggers = this.trigger ? this.trigger.split(' ') : [];
17959 Roo.each(triggers, function(trigger) {
17961 if (trigger == 'click') {
17962 on_el.on('click', this.toggle, this);
17963 } else if (trigger != 'manual') {
17964 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17965 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17967 on_el.on(eventIn ,this.enter, this);
17968 on_el.on(eventOut, this.leave, this);
17979 toggle : function () {
17980 this.hoverState == 'in' ? this.leave() : this.enter();
17983 enter : function () {
17985 clearTimeout(this.timeout);
17987 this.hoverState = 'in';
17989 if (!this.delay || !this.delay.show) {
17994 this.timeout = setTimeout(function () {
17995 if (_t.hoverState == 'in') {
17998 }, this.delay.show)
18001 leave : function() {
18002 clearTimeout(this.timeout);
18004 this.hoverState = 'out';
18006 if (!this.delay || !this.delay.hide) {
18011 this.timeout = setTimeout(function () {
18012 if (_t.hoverState == 'out') {
18015 }, this.delay.hide)
18018 show : function (on_el)
18021 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18025 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18026 if (this.html !== false) {
18027 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18029 this.el.removeClass([
18030 'fade','top','bottom', 'left', 'right','in',
18031 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18033 if (!this.title.length) {
18034 this.el.select('.popover-title',true).hide();
18037 var placement = typeof this.placement == 'function' ?
18038 this.placement.call(this, this.el, on_el) :
18041 var autoToken = /\s?auto?\s?/i;
18042 var autoPlace = autoToken.test(placement);
18044 placement = placement.replace(autoToken, '') || 'top';
18048 //this.el.setXY([0,0]);
18050 this.el.dom.style.display='block';
18051 this.el.addClass(placement);
18053 //this.el.appendTo(on_el);
18055 var p = this.getPosition();
18056 var box = this.el.getBox();
18061 var align = Roo.bootstrap.Popover.alignment[placement];
18064 this.el.alignTo(on_el, align[0],align[1]);
18065 //var arrow = this.el.select('.arrow',true).first();
18066 //arrow.set(align[2],
18068 this.el.addClass('in');
18071 if (this.el.hasClass('fade')) {
18075 this.hoverState = 'in';
18077 this.fireEvent('show', this);
18082 this.el.setXY([0,0]);
18083 this.el.removeClass('in');
18085 this.hoverState = null;
18087 this.fireEvent('hide', this);
18092 Roo.bootstrap.Popover.alignment = {
18093 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18094 'right' : ['l-r', [10,0], 'left bs-popover-left'],
18095 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18096 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18107 * @class Roo.bootstrap.Progress
18108 * @extends Roo.bootstrap.Component
18109 * Bootstrap Progress class
18110 * @cfg {Boolean} striped striped of the progress bar
18111 * @cfg {Boolean} active animated of the progress bar
18115 * Create a new Progress
18116 * @param {Object} config The config object
18119 Roo.bootstrap.Progress = function(config){
18120 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18123 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
18128 getAutoCreate : function(){
18136 cfg.cls += ' progress-striped';
18140 cfg.cls += ' active';
18159 * @class Roo.bootstrap.ProgressBar
18160 * @extends Roo.bootstrap.Component
18161 * Bootstrap ProgressBar class
18162 * @cfg {Number} aria_valuenow aria-value now
18163 * @cfg {Number} aria_valuemin aria-value min
18164 * @cfg {Number} aria_valuemax aria-value max
18165 * @cfg {String} label label for the progress bar
18166 * @cfg {String} panel (success | info | warning | danger )
18167 * @cfg {String} role role of the progress bar
18168 * @cfg {String} sr_only text
18172 * Create a new ProgressBar
18173 * @param {Object} config The config object
18176 Roo.bootstrap.ProgressBar = function(config){
18177 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18180 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
18184 aria_valuemax : 100,
18190 getAutoCreate : function()
18195 cls: 'progress-bar',
18196 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18208 cfg.role = this.role;
18211 if(this.aria_valuenow){
18212 cfg['aria-valuenow'] = this.aria_valuenow;
18215 if(this.aria_valuemin){
18216 cfg['aria-valuemin'] = this.aria_valuemin;
18219 if(this.aria_valuemax){
18220 cfg['aria-valuemax'] = this.aria_valuemax;
18223 if(this.label && !this.sr_only){
18224 cfg.html = this.label;
18228 cfg.cls += ' progress-bar-' + this.panel;
18234 update : function(aria_valuenow)
18236 this.aria_valuenow = aria_valuenow;
18238 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18253 * @class Roo.bootstrap.TabGroup
18254 * @extends Roo.bootstrap.Column
18255 * Bootstrap Column class
18256 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18257 * @cfg {Boolean} carousel true to make the group behave like a carousel
18258 * @cfg {Boolean} bullets show bullets for the panels
18259 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18260 * @cfg {Number} timer auto slide timer .. default 0 millisecond
18261 * @cfg {Boolean} showarrow (true|false) show arrow default true
18264 * Create a new TabGroup
18265 * @param {Object} config The config object
18268 Roo.bootstrap.TabGroup = function(config){
18269 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18271 this.navId = Roo.id();
18274 Roo.bootstrap.TabGroup.register(this);
18278 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18281 transition : false,
18286 slideOnTouch : false,
18289 getAutoCreate : function()
18291 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18293 cfg.cls += ' tab-content';
18295 if (this.carousel) {
18296 cfg.cls += ' carousel slide';
18299 cls : 'carousel-inner',
18303 if(this.bullets && !Roo.isTouch){
18306 cls : 'carousel-bullets',
18310 if(this.bullets_cls){
18311 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18318 cfg.cn[0].cn.push(bullets);
18321 if(this.showarrow){
18322 cfg.cn[0].cn.push({
18324 class : 'carousel-arrow',
18328 class : 'carousel-prev',
18332 class : 'fa fa-chevron-left'
18338 class : 'carousel-next',
18342 class : 'fa fa-chevron-right'
18355 initEvents: function()
18357 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18358 // this.el.on("touchstart", this.onTouchStart, this);
18361 if(this.autoslide){
18364 this.slideFn = window.setInterval(function() {
18365 _this.showPanelNext();
18369 if(this.showarrow){
18370 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18371 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18377 // onTouchStart : function(e, el, o)
18379 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18383 // this.showPanelNext();
18387 getChildContainer : function()
18389 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18393 * register a Navigation item
18394 * @param {Roo.bootstrap.NavItem} the navitem to add
18396 register : function(item)
18398 this.tabs.push( item);
18399 item.navId = this.navId; // not really needed..
18404 getActivePanel : function()
18407 Roo.each(this.tabs, function(t) {
18417 getPanelByName : function(n)
18420 Roo.each(this.tabs, function(t) {
18421 if (t.tabId == n) {
18429 indexOfPanel : function(p)
18432 Roo.each(this.tabs, function(t,i) {
18433 if (t.tabId == p.tabId) {
18442 * show a specific panel
18443 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18444 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18446 showPanel : function (pan)
18448 if(this.transition || typeof(pan) == 'undefined'){
18449 Roo.log("waiting for the transitionend");
18453 if (typeof(pan) == 'number') {
18454 pan = this.tabs[pan];
18457 if (typeof(pan) == 'string') {
18458 pan = this.getPanelByName(pan);
18461 var cur = this.getActivePanel();
18464 Roo.log('pan or acitve pan is undefined');
18468 if (pan.tabId == this.getActivePanel().tabId) {
18472 if (false === cur.fireEvent('beforedeactivate')) {
18476 if(this.bullets > 0 && !Roo.isTouch){
18477 this.setActiveBullet(this.indexOfPanel(pan));
18480 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18482 //class="carousel-item carousel-item-next carousel-item-left"
18484 this.transition = true;
18485 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18486 var lr = dir == 'next' ? 'left' : 'right';
18487 pan.el.addClass(dir); // or prev
18488 pan.el.addClass('carousel-item-' + dir); // or prev
18489 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18490 cur.el.addClass(lr); // or right
18491 pan.el.addClass(lr);
18492 cur.el.addClass('carousel-item-' +lr); // or right
18493 pan.el.addClass('carousel-item-' +lr);
18497 cur.el.on('transitionend', function() {
18498 Roo.log("trans end?");
18500 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18501 pan.setActive(true);
18503 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18504 cur.setActive(false);
18506 _this.transition = false;
18508 }, this, { single: true } );
18513 cur.setActive(false);
18514 pan.setActive(true);
18519 showPanelNext : function()
18521 var i = this.indexOfPanel(this.getActivePanel());
18523 if (i >= this.tabs.length - 1 && !this.autoslide) {
18527 if (i >= this.tabs.length - 1 && this.autoslide) {
18531 this.showPanel(this.tabs[i+1]);
18534 showPanelPrev : function()
18536 var i = this.indexOfPanel(this.getActivePanel());
18538 if (i < 1 && !this.autoslide) {
18542 if (i < 1 && this.autoslide) {
18543 i = this.tabs.length;
18546 this.showPanel(this.tabs[i-1]);
18550 addBullet: function()
18552 if(!this.bullets || Roo.isTouch){
18555 var ctr = this.el.select('.carousel-bullets',true).first();
18556 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18557 var bullet = ctr.createChild({
18558 cls : 'bullet bullet-' + i
18559 },ctr.dom.lastChild);
18564 bullet.on('click', (function(e, el, o, ii, t){
18566 e.preventDefault();
18568 this.showPanel(ii);
18570 if(this.autoslide && this.slideFn){
18571 clearInterval(this.slideFn);
18572 this.slideFn = window.setInterval(function() {
18573 _this.showPanelNext();
18577 }).createDelegate(this, [i, bullet], true));
18582 setActiveBullet : function(i)
18588 Roo.each(this.el.select('.bullet', true).elements, function(el){
18589 el.removeClass('selected');
18592 var bullet = this.el.select('.bullet-' + i, true).first();
18598 bullet.addClass('selected');
18609 Roo.apply(Roo.bootstrap.TabGroup, {
18613 * register a Navigation Group
18614 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18616 register : function(navgrp)
18618 this.groups[navgrp.navId] = navgrp;
18622 * fetch a Navigation Group based on the navigation ID
18623 * if one does not exist , it will get created.
18624 * @param {string} the navgroup to add
18625 * @returns {Roo.bootstrap.NavGroup} the navgroup
18627 get: function(navId) {
18628 if (typeof(this.groups[navId]) == 'undefined') {
18629 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18631 return this.groups[navId] ;
18646 * @class Roo.bootstrap.TabPanel
18647 * @extends Roo.bootstrap.Component
18648 * Bootstrap TabPanel class
18649 * @cfg {Boolean} active panel active
18650 * @cfg {String} html panel content
18651 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18652 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18653 * @cfg {String} href click to link..
18657 * Create a new TabPanel
18658 * @param {Object} config The config object
18661 Roo.bootstrap.TabPanel = function(config){
18662 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18666 * Fires when the active status changes
18667 * @param {Roo.bootstrap.TabPanel} this
18668 * @param {Boolean} state the new state
18673 * @event beforedeactivate
18674 * Fires before a tab is de-activated - can be used to do validation on a form.
18675 * @param {Roo.bootstrap.TabPanel} this
18676 * @return {Boolean} false if there is an error
18679 'beforedeactivate': true
18682 this.tabId = this.tabId || Roo.id();
18686 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18694 getAutoCreate : function(){
18699 // item is needed for carousel - not sure if it has any effect otherwise
18700 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18701 html: this.html || ''
18705 cfg.cls += ' active';
18709 cfg.tabId = this.tabId;
18717 initEvents: function()
18719 var p = this.parent();
18721 this.navId = this.navId || p.navId;
18723 if (typeof(this.navId) != 'undefined') {
18724 // not really needed.. but just in case.. parent should be a NavGroup.
18725 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18729 var i = tg.tabs.length - 1;
18731 if(this.active && tg.bullets > 0 && i < tg.bullets){
18732 tg.setActiveBullet(i);
18736 this.el.on('click', this.onClick, this);
18739 this.el.on("touchstart", this.onTouchStart, this);
18740 this.el.on("touchmove", this.onTouchMove, this);
18741 this.el.on("touchend", this.onTouchEnd, this);
18746 onRender : function(ct, position)
18748 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18751 setActive : function(state)
18753 Roo.log("panel - set active " + this.tabId + "=" + state);
18755 this.active = state;
18757 this.el.removeClass('active');
18759 } else if (!this.el.hasClass('active')) {
18760 this.el.addClass('active');
18763 this.fireEvent('changed', this, state);
18766 onClick : function(e)
18768 e.preventDefault();
18770 if(!this.href.length){
18774 window.location.href = this.href;
18783 onTouchStart : function(e)
18785 this.swiping = false;
18787 this.startX = e.browserEvent.touches[0].clientX;
18788 this.startY = e.browserEvent.touches[0].clientY;
18791 onTouchMove : function(e)
18793 this.swiping = true;
18795 this.endX = e.browserEvent.touches[0].clientX;
18796 this.endY = e.browserEvent.touches[0].clientY;
18799 onTouchEnd : function(e)
18806 var tabGroup = this.parent();
18808 if(this.endX > this.startX){ // swiping right
18809 tabGroup.showPanelPrev();
18813 if(this.startX > this.endX){ // swiping left
18814 tabGroup.showPanelNext();
18833 * @class Roo.bootstrap.DateField
18834 * @extends Roo.bootstrap.Input
18835 * Bootstrap DateField class
18836 * @cfg {Number} weekStart default 0
18837 * @cfg {String} viewMode default empty, (months|years)
18838 * @cfg {String} minViewMode default empty, (months|years)
18839 * @cfg {Number} startDate default -Infinity
18840 * @cfg {Number} endDate default Infinity
18841 * @cfg {Boolean} todayHighlight default false
18842 * @cfg {Boolean} todayBtn default false
18843 * @cfg {Boolean} calendarWeeks default false
18844 * @cfg {Object} daysOfWeekDisabled default empty
18845 * @cfg {Boolean} singleMode default false (true | false)
18847 * @cfg {Boolean} keyboardNavigation default true
18848 * @cfg {String} language default en
18851 * Create a new DateField
18852 * @param {Object} config The config object
18855 Roo.bootstrap.DateField = function(config){
18856 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18860 * Fires when this field show.
18861 * @param {Roo.bootstrap.DateField} this
18862 * @param {Mixed} date The date value
18867 * Fires when this field hide.
18868 * @param {Roo.bootstrap.DateField} this
18869 * @param {Mixed} date The date value
18874 * Fires when select a date.
18875 * @param {Roo.bootstrap.DateField} this
18876 * @param {Mixed} date The date value
18880 * @event beforeselect
18881 * Fires when before select a date.
18882 * @param {Roo.bootstrap.DateField} this
18883 * @param {Mixed} date The date value
18885 beforeselect : true
18889 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18892 * @cfg {String} format
18893 * The default date format string which can be overriden for localization support. The format must be
18894 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18898 * @cfg {String} altFormats
18899 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18900 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18902 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18910 todayHighlight : false,
18916 keyboardNavigation: true,
18918 calendarWeeks: false,
18920 startDate: -Infinity,
18924 daysOfWeekDisabled: [],
18928 singleMode : false,
18930 UTCDate: function()
18932 return new Date(Date.UTC.apply(Date, arguments));
18935 UTCToday: function()
18937 var today = new Date();
18938 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18941 getDate: function() {
18942 var d = this.getUTCDate();
18943 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18946 getUTCDate: function() {
18950 setDate: function(d) {
18951 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18954 setUTCDate: function(d) {
18956 this.setValue(this.formatDate(this.date));
18959 onRender: function(ct, position)
18962 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18964 this.language = this.language || 'en';
18965 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18966 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18968 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18969 this.format = this.format || 'm/d/y';
18970 this.isInline = false;
18971 this.isInput = true;
18972 this.component = this.el.select('.add-on', true).first() || false;
18973 this.component = (this.component && this.component.length === 0) ? false : this.component;
18974 this.hasInput = this.component && this.inputEl().length;
18976 if (typeof(this.minViewMode === 'string')) {
18977 switch (this.minViewMode) {
18979 this.minViewMode = 1;
18982 this.minViewMode = 2;
18985 this.minViewMode = 0;
18990 if (typeof(this.viewMode === 'string')) {
18991 switch (this.viewMode) {
19004 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
19006 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
19008 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19010 this.picker().on('mousedown', this.onMousedown, this);
19011 this.picker().on('click', this.onClick, this);
19013 this.picker().addClass('datepicker-dropdown');
19015 this.startViewMode = this.viewMode;
19017 if(this.singleMode){
19018 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19019 v.setVisibilityMode(Roo.Element.DISPLAY);
19023 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19024 v.setStyle('width', '189px');
19028 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19029 if(!this.calendarWeeks){
19034 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19035 v.attr('colspan', function(i, val){
19036 return parseInt(val) + 1;
19041 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19043 this.setStartDate(this.startDate);
19044 this.setEndDate(this.endDate);
19046 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19053 if(this.isInline) {
19058 picker : function()
19060 return this.pickerEl;
19061 // return this.el.select('.datepicker', true).first();
19064 fillDow: function()
19066 var dowCnt = this.weekStart;
19075 if(this.calendarWeeks){
19083 while (dowCnt < this.weekStart + 7) {
19087 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19091 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19094 fillMonths: function()
19097 var months = this.picker().select('>.datepicker-months td', true).first();
19099 months.dom.innerHTML = '';
19105 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19108 months.createChild(month);
19115 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;
19117 if (this.date < this.startDate) {
19118 this.viewDate = new Date(this.startDate);
19119 } else if (this.date > this.endDate) {
19120 this.viewDate = new Date(this.endDate);
19122 this.viewDate = new Date(this.date);
19130 var d = new Date(this.viewDate),
19131 year = d.getUTCFullYear(),
19132 month = d.getUTCMonth(),
19133 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19134 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19135 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19136 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19137 currentDate = this.date && this.date.valueOf(),
19138 today = this.UTCToday();
19140 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19142 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19144 // this.picker.select('>tfoot th.today').
19145 // .text(dates[this.language].today)
19146 // .toggle(this.todayBtn !== false);
19148 this.updateNavArrows();
19151 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19153 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19155 prevMonth.setUTCDate(day);
19157 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19159 var nextMonth = new Date(prevMonth);
19161 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19163 nextMonth = nextMonth.valueOf();
19165 var fillMonths = false;
19167 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19169 while(prevMonth.valueOf() <= nextMonth) {
19172 if (prevMonth.getUTCDay() === this.weekStart) {
19174 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19182 if(this.calendarWeeks){
19183 // ISO 8601: First week contains first thursday.
19184 // ISO also states week starts on Monday, but we can be more abstract here.
19186 // Start of current week: based on weekstart/current date
19187 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19188 // Thursday of this week
19189 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19190 // First Thursday of year, year from thursday
19191 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19192 // Calendar week: ms between thursdays, div ms per day, div 7 days
19193 calWeek = (th - yth) / 864e5 / 7 + 1;
19195 fillMonths.cn.push({
19203 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19205 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19208 if (this.todayHighlight &&
19209 prevMonth.getUTCFullYear() == today.getFullYear() &&
19210 prevMonth.getUTCMonth() == today.getMonth() &&
19211 prevMonth.getUTCDate() == today.getDate()) {
19212 clsName += ' today';
19215 if (currentDate && prevMonth.valueOf() === currentDate) {
19216 clsName += ' active';
19219 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19220 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19221 clsName += ' disabled';
19224 fillMonths.cn.push({
19226 cls: 'day ' + clsName,
19227 html: prevMonth.getDate()
19230 prevMonth.setDate(prevMonth.getDate()+1);
19233 var currentYear = this.date && this.date.getUTCFullYear();
19234 var currentMonth = this.date && this.date.getUTCMonth();
19236 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19238 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19239 v.removeClass('active');
19241 if(currentYear === year && k === currentMonth){
19242 v.addClass('active');
19245 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19246 v.addClass('disabled');
19252 year = parseInt(year/10, 10) * 10;
19254 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19256 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19259 for (var i = -1; i < 11; i++) {
19260 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19262 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19270 showMode: function(dir)
19273 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19276 Roo.each(this.picker().select('>div',true).elements, function(v){
19277 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19280 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19285 if(this.isInline) {
19289 this.picker().removeClass(['bottom', 'top']);
19291 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19293 * place to the top of element!
19297 this.picker().addClass('top');
19298 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19303 this.picker().addClass('bottom');
19305 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19308 parseDate : function(value)
19310 if(!value || value instanceof Date){
19313 var v = Date.parseDate(value, this.format);
19314 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19315 v = Date.parseDate(value, 'Y-m-d');
19317 if(!v && this.altFormats){
19318 if(!this.altFormatsArray){
19319 this.altFormatsArray = this.altFormats.split("|");
19321 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19322 v = Date.parseDate(value, this.altFormatsArray[i]);
19328 formatDate : function(date, fmt)
19330 return (!date || !(date instanceof Date)) ?
19331 date : date.dateFormat(fmt || this.format);
19334 onFocus : function()
19336 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19340 onBlur : function()
19342 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19344 var d = this.inputEl().getValue();
19351 showPopup : function()
19353 this.picker().show();
19357 this.fireEvent('showpopup', this, this.date);
19360 hidePopup : function()
19362 if(this.isInline) {
19365 this.picker().hide();
19366 this.viewMode = this.startViewMode;
19369 this.fireEvent('hidepopup', this, this.date);
19373 onMousedown: function(e)
19375 e.stopPropagation();
19376 e.preventDefault();
19381 Roo.bootstrap.DateField.superclass.keyup.call(this);
19385 setValue: function(v)
19387 if(this.fireEvent('beforeselect', this, v) !== false){
19388 var d = new Date(this.parseDate(v) ).clearTime();
19390 if(isNaN(d.getTime())){
19391 this.date = this.viewDate = '';
19392 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19396 v = this.formatDate(d);
19398 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19400 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19404 this.fireEvent('select', this, this.date);
19408 getValue: function()
19410 return this.formatDate(this.date);
19413 fireKey: function(e)
19415 if (!this.picker().isVisible()){
19416 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19422 var dateChanged = false,
19424 newDate, newViewDate;
19429 e.preventDefault();
19433 if (!this.keyboardNavigation) {
19436 dir = e.keyCode == 37 ? -1 : 1;
19439 newDate = this.moveYear(this.date, dir);
19440 newViewDate = this.moveYear(this.viewDate, dir);
19441 } else if (e.shiftKey){
19442 newDate = this.moveMonth(this.date, dir);
19443 newViewDate = this.moveMonth(this.viewDate, dir);
19445 newDate = new Date(this.date);
19446 newDate.setUTCDate(this.date.getUTCDate() + dir);
19447 newViewDate = new Date(this.viewDate);
19448 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19450 if (this.dateWithinRange(newDate)){
19451 this.date = newDate;
19452 this.viewDate = newViewDate;
19453 this.setValue(this.formatDate(this.date));
19455 e.preventDefault();
19456 dateChanged = true;
19461 if (!this.keyboardNavigation) {
19464 dir = e.keyCode == 38 ? -1 : 1;
19466 newDate = this.moveYear(this.date, dir);
19467 newViewDate = this.moveYear(this.viewDate, dir);
19468 } else if (e.shiftKey){
19469 newDate = this.moveMonth(this.date, dir);
19470 newViewDate = this.moveMonth(this.viewDate, dir);
19472 newDate = new Date(this.date);
19473 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19474 newViewDate = new Date(this.viewDate);
19475 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19477 if (this.dateWithinRange(newDate)){
19478 this.date = newDate;
19479 this.viewDate = newViewDate;
19480 this.setValue(this.formatDate(this.date));
19482 e.preventDefault();
19483 dateChanged = true;
19487 this.setValue(this.formatDate(this.date));
19489 e.preventDefault();
19492 this.setValue(this.formatDate(this.date));
19506 onClick: function(e)
19508 e.stopPropagation();
19509 e.preventDefault();
19511 var target = e.getTarget();
19513 if(target.nodeName.toLowerCase() === 'i'){
19514 target = Roo.get(target).dom.parentNode;
19517 var nodeName = target.nodeName;
19518 var className = target.className;
19519 var html = target.innerHTML;
19520 //Roo.log(nodeName);
19522 switch(nodeName.toLowerCase()) {
19524 switch(className) {
19530 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19531 switch(this.viewMode){
19533 this.viewDate = this.moveMonth(this.viewDate, dir);
19537 this.viewDate = this.moveYear(this.viewDate, dir);
19543 var date = new Date();
19544 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19546 this.setValue(this.formatDate(this.date));
19553 if (className.indexOf('disabled') < 0) {
19554 this.viewDate.setUTCDate(1);
19555 if (className.indexOf('month') > -1) {
19556 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19558 var year = parseInt(html, 10) || 0;
19559 this.viewDate.setUTCFullYear(year);
19563 if(this.singleMode){
19564 this.setValue(this.formatDate(this.viewDate));
19575 //Roo.log(className);
19576 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19577 var day = parseInt(html, 10) || 1;
19578 var year = this.viewDate.getUTCFullYear(),
19579 month = this.viewDate.getUTCMonth();
19581 if (className.indexOf('old') > -1) {
19588 } else if (className.indexOf('new') > -1) {
19596 //Roo.log([year,month,day]);
19597 this.date = this.UTCDate(year, month, day,0,0,0,0);
19598 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19600 //Roo.log(this.formatDate(this.date));
19601 this.setValue(this.formatDate(this.date));
19608 setStartDate: function(startDate)
19610 this.startDate = startDate || -Infinity;
19611 if (this.startDate !== -Infinity) {
19612 this.startDate = this.parseDate(this.startDate);
19615 this.updateNavArrows();
19618 setEndDate: function(endDate)
19620 this.endDate = endDate || Infinity;
19621 if (this.endDate !== Infinity) {
19622 this.endDate = this.parseDate(this.endDate);
19625 this.updateNavArrows();
19628 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19630 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19631 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19632 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19634 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19635 return parseInt(d, 10);
19638 this.updateNavArrows();
19641 updateNavArrows: function()
19643 if(this.singleMode){
19647 var d = new Date(this.viewDate),
19648 year = d.getUTCFullYear(),
19649 month = d.getUTCMonth();
19651 Roo.each(this.picker().select('.prev', true).elements, function(v){
19653 switch (this.viewMode) {
19656 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19662 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19669 Roo.each(this.picker().select('.next', true).elements, function(v){
19671 switch (this.viewMode) {
19674 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19680 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19688 moveMonth: function(date, dir)
19693 var new_date = new Date(date.valueOf()),
19694 day = new_date.getUTCDate(),
19695 month = new_date.getUTCMonth(),
19696 mag = Math.abs(dir),
19698 dir = dir > 0 ? 1 : -1;
19701 // If going back one month, make sure month is not current month
19702 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19704 return new_date.getUTCMonth() == month;
19706 // If going forward one month, make sure month is as expected
19707 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19709 return new_date.getUTCMonth() != new_month;
19711 new_month = month + dir;
19712 new_date.setUTCMonth(new_month);
19713 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19714 if (new_month < 0 || new_month > 11) {
19715 new_month = (new_month + 12) % 12;
19718 // For magnitudes >1, move one month at a time...
19719 for (var i=0; i<mag; i++) {
19720 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19721 new_date = this.moveMonth(new_date, dir);
19723 // ...then reset the day, keeping it in the new month
19724 new_month = new_date.getUTCMonth();
19725 new_date.setUTCDate(day);
19727 return new_month != new_date.getUTCMonth();
19730 // Common date-resetting loop -- if date is beyond end of month, make it
19733 new_date.setUTCDate(--day);
19734 new_date.setUTCMonth(new_month);
19739 moveYear: function(date, dir)
19741 return this.moveMonth(date, dir*12);
19744 dateWithinRange: function(date)
19746 return date >= this.startDate && date <= this.endDate;
19752 this.picker().remove();
19755 validateValue : function(value)
19757 if(this.getVisibilityEl().hasClass('hidden')){
19761 if(value.length < 1) {
19762 if(this.allowBlank){
19768 if(value.length < this.minLength){
19771 if(value.length > this.maxLength){
19775 var vt = Roo.form.VTypes;
19776 if(!vt[this.vtype](value, this)){
19780 if(typeof this.validator == "function"){
19781 var msg = this.validator(value);
19787 if(this.regex && !this.regex.test(value)){
19791 if(typeof(this.parseDate(value)) == 'undefined'){
19795 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19799 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19809 this.date = this.viewDate = '';
19811 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19816 Roo.apply(Roo.bootstrap.DateField, {
19827 html: '<i class="fa fa-arrow-left"/>'
19837 html: '<i class="fa fa-arrow-right"/>'
19879 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19880 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19881 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19882 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19883 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19896 navFnc: 'FullYear',
19901 navFnc: 'FullYear',
19906 Roo.apply(Roo.bootstrap.DateField, {
19910 cls: 'datepicker dropdown-menu roo-dynamic',
19914 cls: 'datepicker-days',
19918 cls: 'table-condensed',
19920 Roo.bootstrap.DateField.head,
19924 Roo.bootstrap.DateField.footer
19931 cls: 'datepicker-months',
19935 cls: 'table-condensed',
19937 Roo.bootstrap.DateField.head,
19938 Roo.bootstrap.DateField.content,
19939 Roo.bootstrap.DateField.footer
19946 cls: 'datepicker-years',
19950 cls: 'table-condensed',
19952 Roo.bootstrap.DateField.head,
19953 Roo.bootstrap.DateField.content,
19954 Roo.bootstrap.DateField.footer
19973 * @class Roo.bootstrap.TimeField
19974 * @extends Roo.bootstrap.Input
19975 * Bootstrap DateField class
19979 * Create a new TimeField
19980 * @param {Object} config The config object
19983 Roo.bootstrap.TimeField = function(config){
19984 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19988 * Fires when this field show.
19989 * @param {Roo.bootstrap.DateField} thisthis
19990 * @param {Mixed} date The date value
19995 * Fires when this field hide.
19996 * @param {Roo.bootstrap.DateField} this
19997 * @param {Mixed} date The date value
20002 * Fires when select a date.
20003 * @param {Roo.bootstrap.DateField} this
20004 * @param {Mixed} date The date value
20010 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
20013 * @cfg {String} format
20014 * The default time format string which can be overriden for localization support. The format must be
20015 * valid according to {@link Date#parseDate} (defaults to 'H:i').
20019 onRender: function(ct, position)
20022 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20024 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20026 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20028 this.pop = this.picker().select('>.datepicker-time',true).first();
20029 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20031 this.picker().on('mousedown', this.onMousedown, this);
20032 this.picker().on('click', this.onClick, this);
20034 this.picker().addClass('datepicker-dropdown');
20039 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20040 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20041 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20042 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20043 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20044 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20048 fireKey: function(e){
20049 if (!this.picker().isVisible()){
20050 if (e.keyCode == 27) { // allow escape to hide and re-show picker
20056 e.preventDefault();
20064 this.onTogglePeriod();
20067 this.onIncrementMinutes();
20070 this.onDecrementMinutes();
20079 onClick: function(e) {
20080 e.stopPropagation();
20081 e.preventDefault();
20084 picker : function()
20086 return this.el.select('.datepicker', true).first();
20089 fillTime: function()
20091 var time = this.pop.select('tbody', true).first();
20093 time.dom.innerHTML = '';
20108 cls: 'hours-up glyphicon glyphicon-chevron-up'
20128 cls: 'minutes-up glyphicon glyphicon-chevron-up'
20149 cls: 'timepicker-hour',
20164 cls: 'timepicker-minute',
20179 cls: 'btn btn-primary period',
20201 cls: 'hours-down glyphicon glyphicon-chevron-down'
20221 cls: 'minutes-down glyphicon glyphicon-chevron-down'
20239 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20246 var hours = this.time.getHours();
20247 var minutes = this.time.getMinutes();
20260 hours = hours - 12;
20264 hours = '0' + hours;
20268 minutes = '0' + minutes;
20271 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20272 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20273 this.pop.select('button', true).first().dom.innerHTML = period;
20279 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20281 var cls = ['bottom'];
20283 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20290 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20295 this.picker().addClass(cls.join('-'));
20299 Roo.each(cls, function(c){
20301 _this.picker().setTop(_this.inputEl().getHeight());
20305 _this.picker().setTop(0 - _this.picker().getHeight());
20310 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20314 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20321 onFocus : function()
20323 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20327 onBlur : function()
20329 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20335 this.picker().show();
20340 this.fireEvent('show', this, this.date);
20345 this.picker().hide();
20348 this.fireEvent('hide', this, this.date);
20351 setTime : function()
20354 this.setValue(this.time.format(this.format));
20356 this.fireEvent('select', this, this.date);
20361 onMousedown: function(e){
20362 e.stopPropagation();
20363 e.preventDefault();
20366 onIncrementHours: function()
20368 Roo.log('onIncrementHours');
20369 this.time = this.time.add(Date.HOUR, 1);
20374 onDecrementHours: function()
20376 Roo.log('onDecrementHours');
20377 this.time = this.time.add(Date.HOUR, -1);
20381 onIncrementMinutes: function()
20383 Roo.log('onIncrementMinutes');
20384 this.time = this.time.add(Date.MINUTE, 1);
20388 onDecrementMinutes: function()
20390 Roo.log('onDecrementMinutes');
20391 this.time = this.time.add(Date.MINUTE, -1);
20395 onTogglePeriod: function()
20397 Roo.log('onTogglePeriod');
20398 this.time = this.time.add(Date.HOUR, 12);
20405 Roo.apply(Roo.bootstrap.TimeField, {
20435 cls: 'btn btn-info ok',
20447 Roo.apply(Roo.bootstrap.TimeField, {
20451 cls: 'datepicker dropdown-menu',
20455 cls: 'datepicker-time',
20459 cls: 'table-condensed',
20461 Roo.bootstrap.TimeField.content,
20462 Roo.bootstrap.TimeField.footer
20481 * @class Roo.bootstrap.MonthField
20482 * @extends Roo.bootstrap.Input
20483 * Bootstrap MonthField class
20485 * @cfg {String} language default en
20488 * Create a new MonthField
20489 * @param {Object} config The config object
20492 Roo.bootstrap.MonthField = function(config){
20493 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20498 * Fires when this field show.
20499 * @param {Roo.bootstrap.MonthField} this
20500 * @param {Mixed} date The date value
20505 * Fires when this field hide.
20506 * @param {Roo.bootstrap.MonthField} this
20507 * @param {Mixed} date The date value
20512 * Fires when select a date.
20513 * @param {Roo.bootstrap.MonthField} this
20514 * @param {String} oldvalue The old value
20515 * @param {String} newvalue The new value
20521 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20523 onRender: function(ct, position)
20526 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20528 this.language = this.language || 'en';
20529 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20530 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20532 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20533 this.isInline = false;
20534 this.isInput = true;
20535 this.component = this.el.select('.add-on', true).first() || false;
20536 this.component = (this.component && this.component.length === 0) ? false : this.component;
20537 this.hasInput = this.component && this.inputEL().length;
20539 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20541 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20543 this.picker().on('mousedown', this.onMousedown, this);
20544 this.picker().on('click', this.onClick, this);
20546 this.picker().addClass('datepicker-dropdown');
20548 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20549 v.setStyle('width', '189px');
20556 if(this.isInline) {
20562 setValue: function(v, suppressEvent)
20564 var o = this.getValue();
20566 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20570 if(suppressEvent !== true){
20571 this.fireEvent('select', this, o, v);
20576 getValue: function()
20581 onClick: function(e)
20583 e.stopPropagation();
20584 e.preventDefault();
20586 var target = e.getTarget();
20588 if(target.nodeName.toLowerCase() === 'i'){
20589 target = Roo.get(target).dom.parentNode;
20592 var nodeName = target.nodeName;
20593 var className = target.className;
20594 var html = target.innerHTML;
20596 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20600 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20602 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20608 picker : function()
20610 return this.pickerEl;
20613 fillMonths: function()
20616 var months = this.picker().select('>.datepicker-months td', true).first();
20618 months.dom.innerHTML = '';
20624 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20627 months.createChild(month);
20636 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20637 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20640 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20641 e.removeClass('active');
20643 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20644 e.addClass('active');
20651 if(this.isInline) {
20655 this.picker().removeClass(['bottom', 'top']);
20657 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20659 * place to the top of element!
20663 this.picker().addClass('top');
20664 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20669 this.picker().addClass('bottom');
20671 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20674 onFocus : function()
20676 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20680 onBlur : function()
20682 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20684 var d = this.inputEl().getValue();
20693 this.picker().show();
20694 this.picker().select('>.datepicker-months', true).first().show();
20698 this.fireEvent('show', this, this.date);
20703 if(this.isInline) {
20706 this.picker().hide();
20707 this.fireEvent('hide', this, this.date);
20711 onMousedown: function(e)
20713 e.stopPropagation();
20714 e.preventDefault();
20719 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20723 fireKey: function(e)
20725 if (!this.picker().isVisible()){
20726 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20737 e.preventDefault();
20741 dir = e.keyCode == 37 ? -1 : 1;
20743 this.vIndex = this.vIndex + dir;
20745 if(this.vIndex < 0){
20749 if(this.vIndex > 11){
20753 if(isNaN(this.vIndex)){
20757 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20763 dir = e.keyCode == 38 ? -1 : 1;
20765 this.vIndex = this.vIndex + dir * 4;
20767 if(this.vIndex < 0){
20771 if(this.vIndex > 11){
20775 if(isNaN(this.vIndex)){
20779 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20784 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20785 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20789 e.preventDefault();
20792 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20793 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20809 this.picker().remove();
20814 Roo.apply(Roo.bootstrap.MonthField, {
20833 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20834 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20839 Roo.apply(Roo.bootstrap.MonthField, {
20843 cls: 'datepicker dropdown-menu roo-dynamic',
20847 cls: 'datepicker-months',
20851 cls: 'table-condensed',
20853 Roo.bootstrap.DateField.content
20873 * @class Roo.bootstrap.CheckBox
20874 * @extends Roo.bootstrap.Input
20875 * Bootstrap CheckBox class
20877 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20878 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20879 * @cfg {String} boxLabel The text that appears beside the checkbox
20880 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20881 * @cfg {Boolean} checked initnal the element
20882 * @cfg {Boolean} inline inline the element (default false)
20883 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20884 * @cfg {String} tooltip label tooltip
20887 * Create a new CheckBox
20888 * @param {Object} config The config object
20891 Roo.bootstrap.CheckBox = function(config){
20892 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20897 * Fires when the element is checked or unchecked.
20898 * @param {Roo.bootstrap.CheckBox} this This input
20899 * @param {Boolean} checked The new checked value
20904 * Fires when the element is click.
20905 * @param {Roo.bootstrap.CheckBox} this This input
20912 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20914 inputType: 'checkbox',
20923 // checkbox success does not make any sense really..
20928 getAutoCreate : function()
20930 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20936 cfg.cls = 'form-group ' + this.inputType; //input-group
20939 cfg.cls += ' ' + this.inputType + '-inline';
20945 type : this.inputType,
20946 value : this.inputValue,
20947 cls : 'roo-' + this.inputType, //'form-box',
20948 placeholder : this.placeholder || ''
20952 if(this.inputType != 'radio'){
20956 cls : 'roo-hidden-value',
20957 value : this.checked ? this.inputValue : this.valueOff
20962 if (this.weight) { // Validity check?
20963 cfg.cls += " " + this.inputType + "-" + this.weight;
20966 if (this.disabled) {
20967 input.disabled=true;
20971 input.checked = this.checked;
20976 input.name = this.name;
20978 if(this.inputType != 'radio'){
20979 hidden.name = this.name;
20980 input.name = '_hidden_' + this.name;
20985 input.cls += ' input-' + this.size;
20990 ['xs','sm','md','lg'].map(function(size){
20991 if (settings[size]) {
20992 cfg.cls += ' col-' + size + '-' + settings[size];
20996 var inputblock = input;
20998 if (this.before || this.after) {
21001 cls : 'input-group',
21006 inputblock.cn.push({
21008 cls : 'input-group-addon',
21013 inputblock.cn.push(input);
21015 if(this.inputType != 'radio'){
21016 inputblock.cn.push(hidden);
21020 inputblock.cn.push({
21022 cls : 'input-group-addon',
21028 var boxLabelCfg = false;
21034 //'for': id, // box label is handled by onclick - so no for...
21036 html: this.boxLabel
21039 boxLabelCfg.tooltip = this.tooltip;
21045 if (align ==='left' && this.fieldLabel.length) {
21046 // Roo.log("left and has label");
21051 cls : 'control-label',
21052 html : this.fieldLabel
21063 cfg.cn[1].cn.push(boxLabelCfg);
21066 if(this.labelWidth > 12){
21067 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21070 if(this.labelWidth < 13 && this.labelmd == 0){
21071 this.labelmd = this.labelWidth;
21074 if(this.labellg > 0){
21075 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21076 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21079 if(this.labelmd > 0){
21080 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21081 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21084 if(this.labelsm > 0){
21085 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21086 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21089 if(this.labelxs > 0){
21090 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21091 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21094 } else if ( this.fieldLabel.length) {
21095 // Roo.log(" label");
21099 tag: this.boxLabel ? 'span' : 'label',
21101 cls: 'control-label box-input-label',
21102 //cls : 'input-group-addon',
21103 html : this.fieldLabel
21110 cfg.cn.push(boxLabelCfg);
21115 // Roo.log(" no label && no align");
21116 cfg.cn = [ inputblock ] ;
21118 cfg.cn.push(boxLabelCfg);
21126 if(this.inputType != 'radio'){
21127 cfg.cn.push(hidden);
21135 * return the real input element.
21137 inputEl: function ()
21139 return this.el.select('input.roo-' + this.inputType,true).first();
21141 hiddenEl: function ()
21143 return this.el.select('input.roo-hidden-value',true).first();
21146 labelEl: function()
21148 return this.el.select('label.control-label',true).first();
21150 /* depricated... */
21154 return this.labelEl();
21157 boxLabelEl: function()
21159 return this.el.select('label.box-label',true).first();
21162 initEvents : function()
21164 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21166 this.inputEl().on('click', this.onClick, this);
21168 if (this.boxLabel) {
21169 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
21172 this.startValue = this.getValue();
21175 Roo.bootstrap.CheckBox.register(this);
21179 onClick : function(e)
21181 if(this.fireEvent('click', this, e) !== false){
21182 this.setChecked(!this.checked);
21187 setChecked : function(state,suppressEvent)
21189 this.startValue = this.getValue();
21191 if(this.inputType == 'radio'){
21193 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21194 e.dom.checked = false;
21197 this.inputEl().dom.checked = true;
21199 this.inputEl().dom.value = this.inputValue;
21201 if(suppressEvent !== true){
21202 this.fireEvent('check', this, true);
21210 this.checked = state;
21212 this.inputEl().dom.checked = state;
21215 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21217 if(suppressEvent !== true){
21218 this.fireEvent('check', this, state);
21224 getValue : function()
21226 if(this.inputType == 'radio'){
21227 return this.getGroupValue();
21230 return this.hiddenEl().dom.value;
21234 getGroupValue : function()
21236 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21240 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21243 setValue : function(v,suppressEvent)
21245 if(this.inputType == 'radio'){
21246 this.setGroupValue(v, suppressEvent);
21250 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21255 setGroupValue : function(v, suppressEvent)
21257 this.startValue = this.getValue();
21259 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21260 e.dom.checked = false;
21262 if(e.dom.value == v){
21263 e.dom.checked = true;
21267 if(suppressEvent !== true){
21268 this.fireEvent('check', this, true);
21276 validate : function()
21278 if(this.getVisibilityEl().hasClass('hidden')){
21284 (this.inputType == 'radio' && this.validateRadio()) ||
21285 (this.inputType == 'checkbox' && this.validateCheckbox())
21291 this.markInvalid();
21295 validateRadio : function()
21297 if(this.getVisibilityEl().hasClass('hidden')){
21301 if(this.allowBlank){
21307 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21308 if(!e.dom.checked){
21320 validateCheckbox : function()
21323 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21324 //return (this.getValue() == this.inputValue) ? true : false;
21327 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21335 for(var i in group){
21336 if(group[i].el.isVisible(true)){
21344 for(var i in group){
21349 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21356 * Mark this field as valid
21358 markValid : function()
21362 this.fireEvent('valid', this);
21364 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21367 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21374 if(this.inputType == 'radio'){
21375 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21376 var fg = e.findParent('.form-group', false, true);
21377 if (Roo.bootstrap.version == 3) {
21378 fg.removeClass([_this.invalidClass, _this.validClass]);
21379 fg.addClass(_this.validClass);
21381 fg.removeClass(['is-valid', 'is-invalid']);
21382 fg.addClass('is-valid');
21390 var fg = this.el.findParent('.form-group', false, true);
21391 if (Roo.bootstrap.version == 3) {
21392 fg.removeClass([this.invalidClass, this.validClass]);
21393 fg.addClass(this.validClass);
21395 fg.removeClass(['is-valid', 'is-invalid']);
21396 fg.addClass('is-valid');
21401 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21407 for(var i in group){
21408 var fg = group[i].el.findParent('.form-group', false, true);
21409 if (Roo.bootstrap.version == 3) {
21410 fg.removeClass([this.invalidClass, this.validClass]);
21411 fg.addClass(this.validClass);
21413 fg.removeClass(['is-valid', 'is-invalid']);
21414 fg.addClass('is-valid');
21420 * Mark this field as invalid
21421 * @param {String} msg The validation message
21423 markInvalid : function(msg)
21425 if(this.allowBlank){
21431 this.fireEvent('invalid', this, msg);
21433 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21436 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21440 label.markInvalid();
21443 if(this.inputType == 'radio'){
21445 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21446 var fg = e.findParent('.form-group', false, true);
21447 if (Roo.bootstrap.version == 3) {
21448 fg.removeClass([_this.invalidClass, _this.validClass]);
21449 fg.addClass(_this.invalidClass);
21451 fg.removeClass(['is-invalid', 'is-valid']);
21452 fg.addClass('is-invalid');
21460 var fg = this.el.findParent('.form-group', false, true);
21461 if (Roo.bootstrap.version == 3) {
21462 fg.removeClass([_this.invalidClass, _this.validClass]);
21463 fg.addClass(_this.invalidClass);
21465 fg.removeClass(['is-invalid', 'is-valid']);
21466 fg.addClass('is-invalid');
21471 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21477 for(var i in group){
21478 var fg = group[i].el.findParent('.form-group', false, true);
21479 if (Roo.bootstrap.version == 3) {
21480 fg.removeClass([_this.invalidClass, _this.validClass]);
21481 fg.addClass(_this.invalidClass);
21483 fg.removeClass(['is-invalid', 'is-valid']);
21484 fg.addClass('is-invalid');
21490 clearInvalid : function()
21492 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21494 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21496 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21498 if (label && label.iconEl) {
21499 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21500 label.iconEl.removeClass(['is-invalid', 'is-valid']);
21504 disable : function()
21506 if(this.inputType != 'radio'){
21507 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21514 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21515 _this.getActionEl().addClass(this.disabledClass);
21516 e.dom.disabled = true;
21520 this.disabled = true;
21521 this.fireEvent("disable", this);
21525 enable : function()
21527 if(this.inputType != 'radio'){
21528 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21535 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21536 _this.getActionEl().removeClass(this.disabledClass);
21537 e.dom.disabled = false;
21541 this.disabled = false;
21542 this.fireEvent("enable", this);
21546 setBoxLabel : function(v)
21551 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21557 Roo.apply(Roo.bootstrap.CheckBox, {
21562 * register a CheckBox Group
21563 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21565 register : function(checkbox)
21567 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21568 this.groups[checkbox.groupId] = {};
21571 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21575 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21579 * fetch a CheckBox Group based on the group ID
21580 * @param {string} the group ID
21581 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21583 get: function(groupId) {
21584 if (typeof(this.groups[groupId]) == 'undefined') {
21588 return this.groups[groupId] ;
21601 * @class Roo.bootstrap.Radio
21602 * @extends Roo.bootstrap.Component
21603 * Bootstrap Radio class
21604 * @cfg {String} boxLabel - the label associated
21605 * @cfg {String} value - the value of radio
21608 * Create a new Radio
21609 * @param {Object} config The config object
21611 Roo.bootstrap.Radio = function(config){
21612 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21616 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21622 getAutoCreate : function()
21626 cls : 'form-group radio',
21631 html : this.boxLabel
21639 initEvents : function()
21641 this.parent().register(this);
21643 this.el.on('click', this.onClick, this);
21647 onClick : function(e)
21649 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21650 this.setChecked(true);
21654 setChecked : function(state, suppressEvent)
21656 this.parent().setValue(this.value, suppressEvent);
21660 setBoxLabel : function(v)
21665 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21680 * @class Roo.bootstrap.SecurePass
21681 * @extends Roo.bootstrap.Input
21682 * Bootstrap SecurePass class
21686 * Create a new SecurePass
21687 * @param {Object} config The config object
21690 Roo.bootstrap.SecurePass = function (config) {
21691 // these go here, so the translation tool can replace them..
21693 PwdEmpty: "Please type a password, and then retype it to confirm.",
21694 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21695 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21696 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21697 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21698 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21699 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21700 TooWeak: "Your password is Too Weak."
21702 this.meterLabel = "Password strength:";
21703 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21704 this.meterClass = [
21705 "roo-password-meter-tooweak",
21706 "roo-password-meter-weak",
21707 "roo-password-meter-medium",
21708 "roo-password-meter-strong",
21709 "roo-password-meter-grey"
21714 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21717 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21719 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21721 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21722 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21723 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21724 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21725 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21726 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21727 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21737 * @cfg {String/Object} Label for the strength meter (defaults to
21738 * 'Password strength:')
21743 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21744 * ['Weak', 'Medium', 'Strong'])
21747 pwdStrengths: false,
21760 initEvents: function ()
21762 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21764 if (this.el.is('input[type=password]') && Roo.isSafari) {
21765 this.el.on('keydown', this.SafariOnKeyDown, this);
21768 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21771 onRender: function (ct, position)
21773 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21774 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21775 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21777 this.trigger.createChild({
21782 cls: 'roo-password-meter-grey col-xs-12',
21785 //width: this.meterWidth + 'px'
21789 cls: 'roo-password-meter-text'
21795 if (this.hideTrigger) {
21796 this.trigger.setDisplayed(false);
21798 this.setSize(this.width || '', this.height || '');
21801 onDestroy: function ()
21803 if (this.trigger) {
21804 this.trigger.removeAllListeners();
21805 this.trigger.remove();
21808 this.wrap.remove();
21810 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21813 checkStrength: function ()
21815 var pwd = this.inputEl().getValue();
21816 if (pwd == this._lastPwd) {
21821 if (this.ClientSideStrongPassword(pwd)) {
21823 } else if (this.ClientSideMediumPassword(pwd)) {
21825 } else if (this.ClientSideWeakPassword(pwd)) {
21831 Roo.log('strength1: ' + strength);
21833 //var pm = this.trigger.child('div/div/div').dom;
21834 var pm = this.trigger.child('div/div');
21835 pm.removeClass(this.meterClass);
21836 pm.addClass(this.meterClass[strength]);
21839 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21841 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21843 this._lastPwd = pwd;
21847 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21849 this._lastPwd = '';
21851 var pm = this.trigger.child('div/div');
21852 pm.removeClass(this.meterClass);
21853 pm.addClass('roo-password-meter-grey');
21856 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21859 this.inputEl().dom.type='password';
21862 validateValue: function (value)
21865 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21868 if (value.length == 0) {
21869 if (this.allowBlank) {
21870 this.clearInvalid();
21874 this.markInvalid(this.errors.PwdEmpty);
21875 this.errorMsg = this.errors.PwdEmpty;
21883 if ('[\x21-\x7e]*'.match(value)) {
21884 this.markInvalid(this.errors.PwdBadChar);
21885 this.errorMsg = this.errors.PwdBadChar;
21888 if (value.length < 6) {
21889 this.markInvalid(this.errors.PwdShort);
21890 this.errorMsg = this.errors.PwdShort;
21893 if (value.length > 16) {
21894 this.markInvalid(this.errors.PwdLong);
21895 this.errorMsg = this.errors.PwdLong;
21899 if (this.ClientSideStrongPassword(value)) {
21901 } else if (this.ClientSideMediumPassword(value)) {
21903 } else if (this.ClientSideWeakPassword(value)) {
21910 if (strength < 2) {
21911 //this.markInvalid(this.errors.TooWeak);
21912 this.errorMsg = this.errors.TooWeak;
21917 console.log('strength2: ' + strength);
21919 //var pm = this.trigger.child('div/div/div').dom;
21921 var pm = this.trigger.child('div/div');
21922 pm.removeClass(this.meterClass);
21923 pm.addClass(this.meterClass[strength]);
21925 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21927 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21929 this.errorMsg = '';
21933 CharacterSetChecks: function (type)
21936 this.fResult = false;
21939 isctype: function (character, type)
21942 case this.kCapitalLetter:
21943 if (character >= 'A' && character <= 'Z') {
21948 case this.kSmallLetter:
21949 if (character >= 'a' && character <= 'z') {
21955 if (character >= '0' && character <= '9') {
21960 case this.kPunctuation:
21961 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21972 IsLongEnough: function (pwd, size)
21974 return !(pwd == null || isNaN(size) || pwd.length < size);
21977 SpansEnoughCharacterSets: function (word, nb)
21979 if (!this.IsLongEnough(word, nb))
21984 var characterSetChecks = new Array(
21985 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21986 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21989 for (var index = 0; index < word.length; ++index) {
21990 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21991 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21992 characterSetChecks[nCharSet].fResult = true;
21999 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22000 if (characterSetChecks[nCharSet].fResult) {
22005 if (nCharSets < nb) {
22011 ClientSideStrongPassword: function (pwd)
22013 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22016 ClientSideMediumPassword: function (pwd)
22018 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22021 ClientSideWeakPassword: function (pwd)
22023 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22026 })//<script type="text/javascript">
22029 * Based Ext JS Library 1.1.1
22030 * Copyright(c) 2006-2007, Ext JS, LLC.
22036 * @class Roo.HtmlEditorCore
22037 * @extends Roo.Component
22038 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22040 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22043 Roo.HtmlEditorCore = function(config){
22046 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22051 * @event initialize
22052 * Fires when the editor is fully initialized (including the iframe)
22053 * @param {Roo.HtmlEditorCore} this
22058 * Fires when the editor is first receives the focus. Any insertion must wait
22059 * until after this event.
22060 * @param {Roo.HtmlEditorCore} this
22064 * @event beforesync
22065 * Fires before the textarea is updated with content from the editor iframe. Return false
22066 * to cancel the sync.
22067 * @param {Roo.HtmlEditorCore} this
22068 * @param {String} html
22072 * @event beforepush
22073 * Fires before the iframe editor is updated with content from the textarea. Return false
22074 * to cancel the push.
22075 * @param {Roo.HtmlEditorCore} this
22076 * @param {String} html
22081 * Fires when the textarea is updated with content from the editor iframe.
22082 * @param {Roo.HtmlEditorCore} this
22083 * @param {String} html
22088 * Fires when the iframe editor is updated with content from the textarea.
22089 * @param {Roo.HtmlEditorCore} this
22090 * @param {String} html
22095 * @event editorevent
22096 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22097 * @param {Roo.HtmlEditorCore} this
22103 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22105 // defaults : white / black...
22106 this.applyBlacklists();
22113 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
22117 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
22123 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22128 * @cfg {Number} height (in pixels)
22132 * @cfg {Number} width (in pixels)
22137 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22140 stylesheets: false,
22145 // private properties
22146 validationEvent : false,
22148 initialized : false,
22150 sourceEditMode : false,
22151 onFocus : Roo.emptyFn,
22153 hideMode:'offsets',
22157 // blacklist + whitelisted elements..
22164 * Protected method that will not generally be called directly. It
22165 * is called when the editor initializes the iframe with HTML contents. Override this method if you
22166 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22168 getDocMarkup : function(){
22172 // inherit styels from page...??
22173 if (this.stylesheets === false) {
22175 Roo.get(document.head).select('style').each(function(node) {
22176 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22179 Roo.get(document.head).select('link').each(function(node) {
22180 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22183 } else if (!this.stylesheets.length) {
22185 st = '<style type="text/css">' +
22186 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22189 st = '<style type="text/css">' +
22194 st += '<style type="text/css">' +
22195 'IMG { cursor: pointer } ' +
22198 var cls = 'roo-htmleditor-body';
22200 if(this.bodyCls.length){
22201 cls += ' ' + this.bodyCls;
22204 return '<html><head>' + st +
22205 //<style type="text/css">' +
22206 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22208 ' </head><body class="' + cls + '"></body></html>';
22212 onRender : function(ct, position)
22215 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22216 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22219 this.el.dom.style.border = '0 none';
22220 this.el.dom.setAttribute('tabIndex', -1);
22221 this.el.addClass('x-hidden hide');
22225 if(Roo.isIE){ // fix IE 1px bogus margin
22226 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22230 this.frameId = Roo.id();
22234 var iframe = this.owner.wrap.createChild({
22236 cls: 'form-control', // bootstrap..
22238 name: this.frameId,
22239 frameBorder : 'no',
22240 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
22245 this.iframe = iframe.dom;
22247 this.assignDocWin();
22249 this.doc.designMode = 'on';
22252 this.doc.write(this.getDocMarkup());
22256 var task = { // must defer to wait for browser to be ready
22258 //console.log("run task?" + this.doc.readyState);
22259 this.assignDocWin();
22260 if(this.doc.body || this.doc.readyState == 'complete'){
22262 this.doc.designMode="on";
22266 Roo.TaskMgr.stop(task);
22267 this.initEditor.defer(10, this);
22274 Roo.TaskMgr.start(task);
22279 onResize : function(w, h)
22281 Roo.log('resize: ' +w + ',' + h );
22282 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22286 if(typeof w == 'number'){
22288 this.iframe.style.width = w + 'px';
22290 if(typeof h == 'number'){
22292 this.iframe.style.height = h + 'px';
22294 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22301 * Toggles the editor between standard and source edit mode.
22302 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22304 toggleSourceEdit : function(sourceEditMode){
22306 this.sourceEditMode = sourceEditMode === true;
22308 if(this.sourceEditMode){
22310 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
22313 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22314 //this.iframe.className = '';
22317 //this.setSize(this.owner.wrap.getSize());
22318 //this.fireEvent('editmodechange', this, this.sourceEditMode);
22325 * Protected method that will not generally be called directly. If you need/want
22326 * custom HTML cleanup, this is the method you should override.
22327 * @param {String} html The HTML to be cleaned
22328 * return {String} The cleaned HTML
22330 cleanHtml : function(html){
22331 html = String(html);
22332 if(html.length > 5){
22333 if(Roo.isSafari){ // strip safari nonsense
22334 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22337 if(html == ' '){
22344 * HTML Editor -> Textarea
22345 * Protected method that will not generally be called directly. Syncs the contents
22346 * of the editor iframe with the textarea.
22348 syncValue : function(){
22349 if(this.initialized){
22350 var bd = (this.doc.body || this.doc.documentElement);
22351 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22352 var html = bd.innerHTML;
22354 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22355 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22357 html = '<div style="'+m[0]+'">' + html + '</div>';
22360 html = this.cleanHtml(html);
22361 // fix up the special chars.. normaly like back quotes in word...
22362 // however we do not want to do this with chinese..
22363 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
22365 var cc = match.charCodeAt();
22367 // Get the character value, handling surrogate pairs
22368 if (match.length == 2) {
22369 // It's a surrogate pair, calculate the Unicode code point
22370 var high = match.charCodeAt(0) - 0xD800;
22371 var low = match.charCodeAt(1) - 0xDC00;
22372 cc = (high * 0x400) + low + 0x10000;
22374 (cc >= 0x4E00 && cc < 0xA000 ) ||
22375 (cc >= 0x3400 && cc < 0x4E00 ) ||
22376 (cc >= 0xf900 && cc < 0xfb00 )
22381 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
22382 return "&#" + cc + ";";
22389 if(this.owner.fireEvent('beforesync', this, html) !== false){
22390 this.el.dom.value = html;
22391 this.owner.fireEvent('sync', this, html);
22397 * Protected method that will not generally be called directly. Pushes the value of the textarea
22398 * into the iframe editor.
22400 pushValue : function(){
22401 if(this.initialized){
22402 var v = this.el.dom.value.trim();
22404 // if(v.length < 1){
22408 if(this.owner.fireEvent('beforepush', this, v) !== false){
22409 var d = (this.doc.body || this.doc.documentElement);
22411 this.cleanUpPaste();
22412 this.el.dom.value = d.innerHTML;
22413 this.owner.fireEvent('push', this, v);
22419 deferFocus : function(){
22420 this.focus.defer(10, this);
22424 focus : function(){
22425 if(this.win && !this.sourceEditMode){
22432 assignDocWin: function()
22434 var iframe = this.iframe;
22437 this.doc = iframe.contentWindow.document;
22438 this.win = iframe.contentWindow;
22440 // if (!Roo.get(this.frameId)) {
22443 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22444 // this.win = Roo.get(this.frameId).dom.contentWindow;
22446 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22450 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22451 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22456 initEditor : function(){
22457 //console.log("INIT EDITOR");
22458 this.assignDocWin();
22462 this.doc.designMode="on";
22464 this.doc.write(this.getDocMarkup());
22467 var dbody = (this.doc.body || this.doc.documentElement);
22468 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22469 // this copies styles from the containing element into thsi one..
22470 // not sure why we need all of this..
22471 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22473 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22474 //ss['background-attachment'] = 'fixed'; // w3c
22475 dbody.bgProperties = 'fixed'; // ie
22476 //Roo.DomHelper.applyStyles(dbody, ss);
22477 Roo.EventManager.on(this.doc, {
22478 //'mousedown': this.onEditorEvent,
22479 'mouseup': this.onEditorEvent,
22480 'dblclick': this.onEditorEvent,
22481 'click': this.onEditorEvent,
22482 'keyup': this.onEditorEvent,
22487 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22489 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22490 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22492 this.initialized = true;
22494 this.owner.fireEvent('initialize', this);
22499 onDestroy : function(){
22505 //for (var i =0; i < this.toolbars.length;i++) {
22506 // // fixme - ask toolbars for heights?
22507 // this.toolbars[i].onDestroy();
22510 //this.wrap.dom.innerHTML = '';
22511 //this.wrap.remove();
22516 onFirstFocus : function(){
22518 this.assignDocWin();
22521 this.activated = true;
22524 if(Roo.isGecko){ // prevent silly gecko errors
22526 var s = this.win.getSelection();
22527 if(!s.focusNode || s.focusNode.nodeType != 3){
22528 var r = s.getRangeAt(0);
22529 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22534 this.execCmd('useCSS', true);
22535 this.execCmd('styleWithCSS', false);
22538 this.owner.fireEvent('activate', this);
22542 adjustFont: function(btn){
22543 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22544 //if(Roo.isSafari){ // safari
22547 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22548 if(Roo.isSafari){ // safari
22549 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22550 v = (v < 10) ? 10 : v;
22551 v = (v > 48) ? 48 : v;
22552 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22557 v = Math.max(1, v+adjust);
22559 this.execCmd('FontSize', v );
22562 onEditorEvent : function(e)
22564 this.owner.fireEvent('editorevent', this, e);
22565 // this.updateToolbar();
22566 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22569 insertTag : function(tg)
22571 // could be a bit smarter... -> wrap the current selected tRoo..
22572 if (tg.toLowerCase() == 'span' ||
22573 tg.toLowerCase() == 'code' ||
22574 tg.toLowerCase() == 'sup' ||
22575 tg.toLowerCase() == 'sub'
22578 range = this.createRange(this.getSelection());
22579 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22580 wrappingNode.appendChild(range.extractContents());
22581 range.insertNode(wrappingNode);
22588 this.execCmd("formatblock", tg);
22592 insertText : function(txt)
22596 var range = this.createRange();
22597 range.deleteContents();
22598 //alert(Sender.getAttribute('label'));
22600 range.insertNode(this.doc.createTextNode(txt));
22606 * Executes a Midas editor command on the editor document and performs necessary focus and
22607 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22608 * @param {String} cmd The Midas command
22609 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22611 relayCmd : function(cmd, value){
22613 this.execCmd(cmd, value);
22614 this.owner.fireEvent('editorevent', this);
22615 //this.updateToolbar();
22616 this.owner.deferFocus();
22620 * Executes a Midas editor command directly on the editor document.
22621 * For visual commands, you should use {@link #relayCmd} instead.
22622 * <b>This should only be called after the editor is initialized.</b>
22623 * @param {String} cmd The Midas command
22624 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22626 execCmd : function(cmd, value){
22627 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22634 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22636 * @param {String} text | dom node..
22638 insertAtCursor : function(text)
22641 if(!this.activated){
22647 var r = this.doc.selection.createRange();
22658 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22662 // from jquery ui (MIT licenced)
22664 var win = this.win;
22666 if (win.getSelection && win.getSelection().getRangeAt) {
22667 range = win.getSelection().getRangeAt(0);
22668 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22669 range.insertNode(node);
22670 } else if (win.document.selection && win.document.selection.createRange) {
22671 // no firefox support
22672 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22673 win.document.selection.createRange().pasteHTML(txt);
22675 // no firefox support
22676 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22677 this.execCmd('InsertHTML', txt);
22686 mozKeyPress : function(e){
22688 var c = e.getCharCode(), cmd;
22691 c = String.fromCharCode(c).toLowerCase();
22705 this.cleanUpPaste.defer(100, this);
22713 e.preventDefault();
22721 fixKeys : function(){ // load time branching for fastest keydown performance
22723 return function(e){
22724 var k = e.getKey(), r;
22727 r = this.doc.selection.createRange();
22730 r.pasteHTML('    ');
22737 r = this.doc.selection.createRange();
22739 var target = r.parentElement();
22740 if(!target || target.tagName.toLowerCase() != 'li'){
22742 r.pasteHTML('<br />');
22748 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22749 this.cleanUpPaste.defer(100, this);
22755 }else if(Roo.isOpera){
22756 return function(e){
22757 var k = e.getKey();
22761 this.execCmd('InsertHTML','    ');
22764 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22765 this.cleanUpPaste.defer(100, this);
22770 }else if(Roo.isSafari){
22771 return function(e){
22772 var k = e.getKey();
22776 this.execCmd('InsertText','\t');
22780 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22781 this.cleanUpPaste.defer(100, this);
22789 getAllAncestors: function()
22791 var p = this.getSelectedNode();
22794 a.push(p); // push blank onto stack..
22795 p = this.getParentElement();
22799 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22803 a.push(this.doc.body);
22807 lastSelNode : false,
22810 getSelection : function()
22812 this.assignDocWin();
22813 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22816 getSelectedNode: function()
22818 // this may only work on Gecko!!!
22820 // should we cache this!!!!
22825 var range = this.createRange(this.getSelection()).cloneRange();
22828 var parent = range.parentElement();
22830 var testRange = range.duplicate();
22831 testRange.moveToElementText(parent);
22832 if (testRange.inRange(range)) {
22835 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22838 parent = parent.parentElement;
22843 // is ancestor a text element.
22844 var ac = range.commonAncestorContainer;
22845 if (ac.nodeType == 3) {
22846 ac = ac.parentNode;
22849 var ar = ac.childNodes;
22852 var other_nodes = [];
22853 var has_other_nodes = false;
22854 for (var i=0;i<ar.length;i++) {
22855 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22858 // fullly contained node.
22860 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22865 // probably selected..
22866 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22867 other_nodes.push(ar[i]);
22871 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22876 has_other_nodes = true;
22878 if (!nodes.length && other_nodes.length) {
22879 nodes= other_nodes;
22881 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22887 createRange: function(sel)
22889 // this has strange effects when using with
22890 // top toolbar - not sure if it's a great idea.
22891 //this.editor.contentWindow.focus();
22892 if (typeof sel != "undefined") {
22894 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22896 return this.doc.createRange();
22899 return this.doc.createRange();
22902 getParentElement: function()
22905 this.assignDocWin();
22906 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22908 var range = this.createRange(sel);
22911 var p = range.commonAncestorContainer;
22912 while (p.nodeType == 3) { // text node
22923 * Range intersection.. the hard stuff...
22927 * [ -- selected range --- ]
22931 * if end is before start or hits it. fail.
22932 * if start is after end or hits it fail.
22934 * if either hits (but other is outside. - then it's not
22940 // @see http://www.thismuchiknow.co.uk/?p=64.
22941 rangeIntersectsNode : function(range, node)
22943 var nodeRange = node.ownerDocument.createRange();
22945 nodeRange.selectNode(node);
22947 nodeRange.selectNodeContents(node);
22950 var rangeStartRange = range.cloneRange();
22951 rangeStartRange.collapse(true);
22953 var rangeEndRange = range.cloneRange();
22954 rangeEndRange.collapse(false);
22956 var nodeStartRange = nodeRange.cloneRange();
22957 nodeStartRange.collapse(true);
22959 var nodeEndRange = nodeRange.cloneRange();
22960 nodeEndRange.collapse(false);
22962 return rangeStartRange.compareBoundaryPoints(
22963 Range.START_TO_START, nodeEndRange) == -1 &&
22964 rangeEndRange.compareBoundaryPoints(
22965 Range.START_TO_START, nodeStartRange) == 1;
22969 rangeCompareNode : function(range, node)
22971 var nodeRange = node.ownerDocument.createRange();
22973 nodeRange.selectNode(node);
22975 nodeRange.selectNodeContents(node);
22979 range.collapse(true);
22981 nodeRange.collapse(true);
22983 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22984 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22986 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22988 var nodeIsBefore = ss == 1;
22989 var nodeIsAfter = ee == -1;
22991 if (nodeIsBefore && nodeIsAfter) {
22994 if (!nodeIsBefore && nodeIsAfter) {
22995 return 1; //right trailed.
22998 if (nodeIsBefore && !nodeIsAfter) {
22999 return 2; // left trailed.
23005 // private? - in a new class?
23006 cleanUpPaste : function()
23008 // cleans up the whole document..
23009 Roo.log('cleanuppaste');
23011 this.cleanUpChildren(this.doc.body);
23012 var clean = this.cleanWordChars(this.doc.body.innerHTML);
23013 if (clean != this.doc.body.innerHTML) {
23014 this.doc.body.innerHTML = clean;
23019 cleanWordChars : function(input) {// change the chars to hex code
23020 var he = Roo.HtmlEditorCore;
23022 var output = input;
23023 Roo.each(he.swapCodes, function(sw) {
23024 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23026 output = output.replace(swapper, sw[1]);
23033 cleanUpChildren : function (n)
23035 if (!n.childNodes.length) {
23038 for (var i = n.childNodes.length-1; i > -1 ; i--) {
23039 this.cleanUpChild(n.childNodes[i]);
23046 cleanUpChild : function (node)
23049 //console.log(node);
23050 if (node.nodeName == "#text") {
23051 // clean up silly Windows -- stuff?
23054 if (node.nodeName == "#comment") {
23055 node.parentNode.removeChild(node);
23056 // clean up silly Windows -- stuff?
23059 var lcname = node.tagName.toLowerCase();
23060 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23061 // whitelist of tags..
23063 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23065 node.parentNode.removeChild(node);
23070 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23072 // spans with no attributes - just remove them..
23073 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
23074 remove_keep_children = true;
23077 // remove <a name=....> as rendering on yahoo mailer is borked with this.
23078 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23080 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23081 // remove_keep_children = true;
23084 if (remove_keep_children) {
23085 this.cleanUpChildren(node);
23086 // inserts everything just before this node...
23087 while (node.childNodes.length) {
23088 var cn = node.childNodes[0];
23089 node.removeChild(cn);
23090 node.parentNode.insertBefore(cn, node);
23092 node.parentNode.removeChild(node);
23096 if (!node.attributes || !node.attributes.length) {
23101 this.cleanUpChildren(node);
23105 function cleanAttr(n,v)
23108 if (v.match(/^\./) || v.match(/^\//)) {
23111 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23114 if (v.match(/^#/)) {
23117 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23118 node.removeAttribute(n);
23122 var cwhite = this.cwhite;
23123 var cblack = this.cblack;
23125 function cleanStyle(n,v)
23127 if (v.match(/expression/)) { //XSS?? should we even bother..
23128 node.removeAttribute(n);
23132 var parts = v.split(/;/);
23135 Roo.each(parts, function(p) {
23136 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23140 var l = p.split(':').shift().replace(/\s+/g,'');
23141 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23143 if ( cwhite.length && cblack.indexOf(l) > -1) {
23144 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23145 //node.removeAttribute(n);
23149 // only allow 'c whitelisted system attributes'
23150 if ( cwhite.length && cwhite.indexOf(l) < 0) {
23151 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23152 //node.removeAttribute(n);
23162 if (clean.length) {
23163 node.setAttribute(n, clean.join(';'));
23165 node.removeAttribute(n);
23171 for (var i = node.attributes.length-1; i > -1 ; i--) {
23172 var a = node.attributes[i];
23175 if (a.name.toLowerCase().substr(0,2)=='on') {
23176 node.removeAttribute(a.name);
23179 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23180 node.removeAttribute(a.name);
23183 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23184 cleanAttr(a.name,a.value); // fixme..
23187 if (a.name == 'style') {
23188 cleanStyle(a.name,a.value);
23191 /// clean up MS crap..
23192 // tecnically this should be a list of valid class'es..
23195 if (a.name == 'class') {
23196 if (a.value.match(/^Mso/)) {
23197 node.removeAttribute('class');
23200 if (a.value.match(/^body$/)) {
23201 node.removeAttribute('class');
23212 this.cleanUpChildren(node);
23218 * Clean up MS wordisms...
23220 cleanWord : function(node)
23223 this.cleanWord(this.doc.body);
23228 node.nodeName == 'SPAN' &&
23229 !node.hasAttributes() &&
23230 node.childNodes.length == 1 &&
23231 node.firstChild.nodeName == "#text"
23233 var textNode = node.firstChild;
23234 node.removeChild(textNode);
23235 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
23236 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
23238 node.parentNode.insertBefore(textNode, node);
23239 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
23240 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
23242 node.parentNode.removeChild(node);
23245 if (node.nodeName == "#text") {
23246 // clean up silly Windows -- stuff?
23249 if (node.nodeName == "#comment") {
23250 node.parentNode.removeChild(node);
23251 // clean up silly Windows -- stuff?
23255 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23256 node.parentNode.removeChild(node);
23259 //Roo.log(node.tagName);
23260 // remove - but keep children..
23261 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
23262 //Roo.log('-- removed');
23263 while (node.childNodes.length) {
23264 var cn = node.childNodes[0];
23265 node.removeChild(cn);
23266 node.parentNode.insertBefore(cn, node);
23267 // move node to parent - and clean it..
23268 this.cleanWord(cn);
23270 node.parentNode.removeChild(node);
23271 /// no need to iterate chidlren = it's got none..
23272 //this.iterateChildren(node, this.cleanWord);
23276 if (node.className.length) {
23278 var cn = node.className.split(/\W+/);
23280 Roo.each(cn, function(cls) {
23281 if (cls.match(/Mso[a-zA-Z]+/)) {
23286 node.className = cna.length ? cna.join(' ') : '';
23288 node.removeAttribute("class");
23292 if (node.hasAttribute("lang")) {
23293 node.removeAttribute("lang");
23296 if (node.hasAttribute("style")) {
23298 var styles = node.getAttribute("style").split(";");
23300 Roo.each(styles, function(s) {
23301 if (!s.match(/:/)) {
23304 var kv = s.split(":");
23305 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23308 // what ever is left... we allow.
23311 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23312 if (!nstyle.length) {
23313 node.removeAttribute('style');
23316 this.iterateChildren(node, this.cleanWord);
23322 * iterateChildren of a Node, calling fn each time, using this as the scole..
23323 * @param {DomNode} node node to iterate children of.
23324 * @param {Function} fn method of this class to call on each item.
23326 iterateChildren : function(node, fn)
23328 if (!node.childNodes.length) {
23331 for (var i = node.childNodes.length-1; i > -1 ; i--) {
23332 fn.call(this, node.childNodes[i])
23338 * cleanTableWidths.
23340 * Quite often pasting from word etc.. results in tables with column and widths.
23341 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23344 cleanTableWidths : function(node)
23349 this.cleanTableWidths(this.doc.body);
23354 if (node.nodeName == "#text" || node.nodeName == "#comment") {
23357 Roo.log(node.tagName);
23358 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23359 this.iterateChildren(node, this.cleanTableWidths);
23362 if (node.hasAttribute('width')) {
23363 node.removeAttribute('width');
23367 if (node.hasAttribute("style")) {
23370 var styles = node.getAttribute("style").split(";");
23372 Roo.each(styles, function(s) {
23373 if (!s.match(/:/)) {
23376 var kv = s.split(":");
23377 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23380 // what ever is left... we allow.
23383 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23384 if (!nstyle.length) {
23385 node.removeAttribute('style');
23389 this.iterateChildren(node, this.cleanTableWidths);
23397 domToHTML : function(currentElement, depth, nopadtext) {
23399 depth = depth || 0;
23400 nopadtext = nopadtext || false;
23402 if (!currentElement) {
23403 return this.domToHTML(this.doc.body);
23406 //Roo.log(currentElement);
23408 var allText = false;
23409 var nodeName = currentElement.nodeName;
23410 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23412 if (nodeName == '#text') {
23414 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23419 if (nodeName != 'BODY') {
23422 // Prints the node tagName, such as <A>, <IMG>, etc
23425 for(i = 0; i < currentElement.attributes.length;i++) {
23427 var aname = currentElement.attributes.item(i).name;
23428 if (!currentElement.attributes.item(i).value.length) {
23431 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23434 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23443 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23446 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23451 // Traverse the tree
23453 var currentElementChild = currentElement.childNodes.item(i);
23454 var allText = true;
23455 var innerHTML = '';
23457 while (currentElementChild) {
23458 // Formatting code (indent the tree so it looks nice on the screen)
23459 var nopad = nopadtext;
23460 if (lastnode == 'SPAN') {
23464 if (currentElementChild.nodeName == '#text') {
23465 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23466 toadd = nopadtext ? toadd : toadd.trim();
23467 if (!nopad && toadd.length > 80) {
23468 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23470 innerHTML += toadd;
23473 currentElementChild = currentElement.childNodes.item(i);
23479 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23481 // Recursively traverse the tree structure of the child node
23482 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23483 lastnode = currentElementChild.nodeName;
23485 currentElementChild=currentElement.childNodes.item(i);
23491 // The remaining code is mostly for formatting the tree
23492 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23497 ret+= "</"+tagName+">";
23503 applyBlacklists : function()
23505 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23506 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23510 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23511 if (b.indexOf(tag) > -1) {
23514 this.white.push(tag);
23518 Roo.each(w, function(tag) {
23519 if (b.indexOf(tag) > -1) {
23522 if (this.white.indexOf(tag) > -1) {
23525 this.white.push(tag);
23530 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23531 if (w.indexOf(tag) > -1) {
23534 this.black.push(tag);
23538 Roo.each(b, function(tag) {
23539 if (w.indexOf(tag) > -1) {
23542 if (this.black.indexOf(tag) > -1) {
23545 this.black.push(tag);
23550 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23551 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23555 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23556 if (b.indexOf(tag) > -1) {
23559 this.cwhite.push(tag);
23563 Roo.each(w, function(tag) {
23564 if (b.indexOf(tag) > -1) {
23567 if (this.cwhite.indexOf(tag) > -1) {
23570 this.cwhite.push(tag);
23575 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23576 if (w.indexOf(tag) > -1) {
23579 this.cblack.push(tag);
23583 Roo.each(b, function(tag) {
23584 if (w.indexOf(tag) > -1) {
23587 if (this.cblack.indexOf(tag) > -1) {
23590 this.cblack.push(tag);
23595 setStylesheets : function(stylesheets)
23597 if(typeof(stylesheets) == 'string'){
23598 Roo.get(this.iframe.contentDocument.head).createChild({
23600 rel : 'stylesheet',
23609 Roo.each(stylesheets, function(s) {
23614 Roo.get(_this.iframe.contentDocument.head).createChild({
23616 rel : 'stylesheet',
23625 removeStylesheets : function()
23629 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23634 setStyle : function(style)
23636 Roo.get(this.iframe.contentDocument.head).createChild({
23645 // hide stuff that is not compatible
23659 * @event specialkey
23663 * @cfg {String} fieldClass @hide
23666 * @cfg {String} focusClass @hide
23669 * @cfg {String} autoCreate @hide
23672 * @cfg {String} inputType @hide
23675 * @cfg {String} invalidClass @hide
23678 * @cfg {String} invalidText @hide
23681 * @cfg {String} msgFx @hide
23684 * @cfg {String} validateOnBlur @hide
23688 Roo.HtmlEditorCore.white = [
23689 'area', 'br', 'img', 'input', 'hr', 'wbr',
23691 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23692 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23693 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23694 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23695 'table', 'ul', 'xmp',
23697 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23700 'dir', 'menu', 'ol', 'ul', 'dl',
23706 Roo.HtmlEditorCore.black = [
23707 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23709 'base', 'basefont', 'bgsound', 'blink', 'body',
23710 'frame', 'frameset', 'head', 'html', 'ilayer',
23711 'iframe', 'layer', 'link', 'meta', 'object',
23712 'script', 'style' ,'title', 'xml' // clean later..
23714 Roo.HtmlEditorCore.clean = [
23715 'script', 'style', 'title', 'xml'
23717 Roo.HtmlEditorCore.remove = [
23722 Roo.HtmlEditorCore.ablack = [
23726 Roo.HtmlEditorCore.aclean = [
23727 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23731 Roo.HtmlEditorCore.pwhite= [
23732 'http', 'https', 'mailto'
23735 // white listed style attributes.
23736 Roo.HtmlEditorCore.cwhite= [
23737 // 'text-align', /// default is to allow most things..
23743 // black listed style attributes.
23744 Roo.HtmlEditorCore.cblack= [
23745 // 'font-size' -- this can be set by the project
23749 Roo.HtmlEditorCore.swapCodes =[
23768 * @class Roo.bootstrap.HtmlEditor
23769 * @extends Roo.bootstrap.TextArea
23770 * Bootstrap HtmlEditor class
23773 * Create a new HtmlEditor
23774 * @param {Object} config The config object
23777 Roo.bootstrap.HtmlEditor = function(config){
23778 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23779 if (!this.toolbars) {
23780 this.toolbars = [];
23783 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23786 * @event initialize
23787 * Fires when the editor is fully initialized (including the iframe)
23788 * @param {HtmlEditor} this
23793 * Fires when the editor is first receives the focus. Any insertion must wait
23794 * until after this event.
23795 * @param {HtmlEditor} this
23799 * @event beforesync
23800 * Fires before the textarea is updated with content from the editor iframe. Return false
23801 * to cancel the sync.
23802 * @param {HtmlEditor} this
23803 * @param {String} html
23807 * @event beforepush
23808 * Fires before the iframe editor is updated with content from the textarea. Return false
23809 * to cancel the push.
23810 * @param {HtmlEditor} this
23811 * @param {String} html
23816 * Fires when the textarea is updated with content from the editor iframe.
23817 * @param {HtmlEditor} this
23818 * @param {String} html
23823 * Fires when the iframe editor is updated with content from the textarea.
23824 * @param {HtmlEditor} this
23825 * @param {String} html
23829 * @event editmodechange
23830 * Fires when the editor switches edit modes
23831 * @param {HtmlEditor} this
23832 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23834 editmodechange: true,
23836 * @event editorevent
23837 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23838 * @param {HtmlEditor} this
23842 * @event firstfocus
23843 * Fires when on first focus - needed by toolbars..
23844 * @param {HtmlEditor} this
23849 * Auto save the htmlEditor value as a file into Events
23850 * @param {HtmlEditor} this
23854 * @event savedpreview
23855 * preview the saved version of htmlEditor
23856 * @param {HtmlEditor} this
23863 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23867 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23872 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23877 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23882 * @cfg {Number} height (in pixels)
23886 * @cfg {Number} width (in pixels)
23891 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23894 stylesheets: false,
23899 // private properties
23900 validationEvent : false,
23902 initialized : false,
23905 onFocus : Roo.emptyFn,
23907 hideMode:'offsets',
23909 tbContainer : false,
23913 toolbarContainer :function() {
23914 return this.wrap.select('.x-html-editor-tb',true).first();
23918 * Protected method that will not generally be called directly. It
23919 * is called when the editor creates its toolbar. Override this method if you need to
23920 * add custom toolbar buttons.
23921 * @param {HtmlEditor} editor
23923 createToolbar : function(){
23924 Roo.log('renewing');
23925 Roo.log("create toolbars");
23927 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23928 this.toolbars[0].render(this.toolbarContainer());
23932 // if (!editor.toolbars || !editor.toolbars.length) {
23933 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23936 // for (var i =0 ; i < editor.toolbars.length;i++) {
23937 // editor.toolbars[i] = Roo.factory(
23938 // typeof(editor.toolbars[i]) == 'string' ?
23939 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23940 // Roo.bootstrap.HtmlEditor);
23941 // editor.toolbars[i].init(editor);
23947 onRender : function(ct, position)
23949 // Roo.log("Call onRender: " + this.xtype);
23951 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23953 this.wrap = this.inputEl().wrap({
23954 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23957 this.editorcore.onRender(ct, position);
23959 if (this.resizable) {
23960 this.resizeEl = new Roo.Resizable(this.wrap, {
23964 minHeight : this.height,
23965 height: this.height,
23966 handles : this.resizable,
23969 resize : function(r, w, h) {
23970 _t.onResize(w,h); // -something
23976 this.createToolbar(this);
23979 if(!this.width && this.resizable){
23980 this.setSize(this.wrap.getSize());
23982 if (this.resizeEl) {
23983 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23984 // should trigger onReize..
23990 onResize : function(w, h)
23992 Roo.log('resize: ' +w + ',' + h );
23993 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23997 if(this.inputEl() ){
23998 if(typeof w == 'number'){
23999 var aw = w - this.wrap.getFrameWidth('lr');
24000 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
24003 if(typeof h == 'number'){
24004 var tbh = -11; // fixme it needs to tool bar size!
24005 for (var i =0; i < this.toolbars.length;i++) {
24006 // fixme - ask toolbars for heights?
24007 tbh += this.toolbars[i].el.getHeight();
24008 //if (this.toolbars[i].footer) {
24009 // tbh += this.toolbars[i].footer.el.getHeight();
24017 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24018 ah -= 5; // knock a few pixes off for look..
24019 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24023 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24024 this.editorcore.onResize(ew,eh);
24029 * Toggles the editor between standard and source edit mode.
24030 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24032 toggleSourceEdit : function(sourceEditMode)
24034 this.editorcore.toggleSourceEdit(sourceEditMode);
24036 if(this.editorcore.sourceEditMode){
24037 Roo.log('editor - showing textarea');
24040 // Roo.log(this.syncValue());
24042 this.inputEl().removeClass(['hide', 'x-hidden']);
24043 this.inputEl().dom.removeAttribute('tabIndex');
24044 this.inputEl().focus();
24046 Roo.log('editor - hiding textarea');
24048 // Roo.log(this.pushValue());
24051 this.inputEl().addClass(['hide', 'x-hidden']);
24052 this.inputEl().dom.setAttribute('tabIndex', -1);
24053 //this.deferFocus();
24056 if(this.resizable){
24057 this.setSize(this.wrap.getSize());
24060 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24063 // private (for BoxComponent)
24064 adjustSize : Roo.BoxComponent.prototype.adjustSize,
24066 // private (for BoxComponent)
24067 getResizeEl : function(){
24071 // private (for BoxComponent)
24072 getPositionEl : function(){
24077 initEvents : function(){
24078 this.originalValue = this.getValue();
24082 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24085 // markInvalid : Roo.emptyFn,
24087 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24090 // clearInvalid : Roo.emptyFn,
24092 setValue : function(v){
24093 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24094 this.editorcore.pushValue();
24099 deferFocus : function(){
24100 this.focus.defer(10, this);
24104 focus : function(){
24105 this.editorcore.focus();
24111 onDestroy : function(){
24117 for (var i =0; i < this.toolbars.length;i++) {
24118 // fixme - ask toolbars for heights?
24119 this.toolbars[i].onDestroy();
24122 this.wrap.dom.innerHTML = '';
24123 this.wrap.remove();
24128 onFirstFocus : function(){
24129 //Roo.log("onFirstFocus");
24130 this.editorcore.onFirstFocus();
24131 for (var i =0; i < this.toolbars.length;i++) {
24132 this.toolbars[i].onFirstFocus();
24138 syncValue : function()
24140 this.editorcore.syncValue();
24143 pushValue : function()
24145 this.editorcore.pushValue();
24149 // hide stuff that is not compatible
24163 * @event specialkey
24167 * @cfg {String} fieldClass @hide
24170 * @cfg {String} focusClass @hide
24173 * @cfg {String} autoCreate @hide
24176 * @cfg {String} inputType @hide
24180 * @cfg {String} invalidText @hide
24183 * @cfg {String} msgFx @hide
24186 * @cfg {String} validateOnBlur @hide
24195 Roo.namespace('Roo.bootstrap.htmleditor');
24197 * @class Roo.bootstrap.HtmlEditorToolbar1
24203 new Roo.bootstrap.HtmlEditor({
24206 new Roo.bootstrap.HtmlEditorToolbar1({
24207 disable : { fonts: 1 , format: 1, ..., ... , ...],
24213 * @cfg {Object} disable List of elements to disable..
24214 * @cfg {Array} btns List of additional buttons.
24218 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24221 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24224 Roo.apply(this, config);
24226 // default disabled, based on 'good practice'..
24227 this.disable = this.disable || {};
24228 Roo.applyIf(this.disable, {
24231 specialElements : true
24233 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24235 this.editor = config.editor;
24236 this.editorcore = config.editor.editorcore;
24238 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24240 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24241 // dont call parent... till later.
24243 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
24248 editorcore : false,
24253 "h1","h2","h3","h4","h5","h6",
24255 "abbr", "acronym", "address", "cite", "samp", "var",
24259 onRender : function(ct, position)
24261 // Roo.log("Call onRender: " + this.xtype);
24263 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24265 this.el.dom.style.marginBottom = '0';
24267 var editorcore = this.editorcore;
24268 var editor= this.editor;
24271 var btn = function(id,cmd , toggle, handler, html){
24273 var event = toggle ? 'toggle' : 'click';
24278 xns: Roo.bootstrap,
24282 enableToggle:toggle !== false,
24284 pressed : toggle ? false : null,
24287 a.listeners[toggle ? 'toggle' : 'click'] = function() {
24288 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
24294 // var cb_box = function...
24299 xns: Roo.bootstrap,
24304 xns: Roo.bootstrap,
24308 Roo.each(this.formats, function(f) {
24309 style.menu.items.push({
24311 xns: Roo.bootstrap,
24312 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24317 editorcore.insertTag(this.tagname);
24324 children.push(style);
24326 btn('bold',false,true);
24327 btn('italic',false,true);
24328 btn('align-left', 'justifyleft',true);
24329 btn('align-center', 'justifycenter',true);
24330 btn('align-right' , 'justifyright',true);
24331 btn('link', false, false, function(btn) {
24332 //Roo.log("create link?");
24333 var url = prompt(this.createLinkText, this.defaultLinkValue);
24334 if(url && url != 'http:/'+'/'){
24335 this.editorcore.relayCmd('createlink', url);
24338 btn('list','insertunorderedlist',true);
24339 btn('pencil', false,true, function(btn){
24341 this.toggleSourceEdit(btn.pressed);
24344 if (this.editor.btns.length > 0) {
24345 for (var i = 0; i<this.editor.btns.length; i++) {
24346 children.push(this.editor.btns[i]);
24354 xns: Roo.bootstrap,
24359 xns: Roo.bootstrap,
24364 cog.menu.items.push({
24366 xns: Roo.bootstrap,
24367 html : Clean styles,
24372 editorcore.insertTag(this.tagname);
24381 this.xtype = 'NavSimplebar';
24383 for(var i=0;i< children.length;i++) {
24385 this.buttons.add(this.addxtypeChild(children[i]));
24389 editor.on('editorevent', this.updateToolbar, this);
24391 onBtnClick : function(id)
24393 this.editorcore.relayCmd(id);
24394 this.editorcore.focus();
24398 * Protected method that will not generally be called directly. It triggers
24399 * a toolbar update by reading the markup state of the current selection in the editor.
24401 updateToolbar: function(){
24403 if(!this.editorcore.activated){
24404 this.editor.onFirstFocus(); // is this neeed?
24408 var btns = this.buttons;
24409 var doc = this.editorcore.doc;
24410 btns.get('bold').setActive(doc.queryCommandState('bold'));
24411 btns.get('italic').setActive(doc.queryCommandState('italic'));
24412 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24414 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24415 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24416 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24418 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24419 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24422 var ans = this.editorcore.getAllAncestors();
24423 if (this.formatCombo) {
24426 var store = this.formatCombo.store;
24427 this.formatCombo.setValue("");
24428 for (var i =0; i < ans.length;i++) {
24429 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24431 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24439 // hides menus... - so this cant be on a menu...
24440 Roo.bootstrap.MenuMgr.hideAll();
24442 Roo.bootstrap.MenuMgr.hideAll();
24443 //this.editorsyncValue();
24445 onFirstFocus: function() {
24446 this.buttons.each(function(item){
24450 toggleSourceEdit : function(sourceEditMode){
24453 if(sourceEditMode){
24454 Roo.log("disabling buttons");
24455 this.buttons.each( function(item){
24456 if(item.cmd != 'pencil'){
24462 Roo.log("enabling buttons");
24463 if(this.editorcore.initialized){
24464 this.buttons.each( function(item){
24470 Roo.log("calling toggole on editor");
24471 // tell the editor that it's been pressed..
24472 this.editor.toggleSourceEdit(sourceEditMode);
24482 * @class Roo.bootstrap.Table.AbstractSelectionModel
24483 * @extends Roo.util.Observable
24484 * Abstract base class for grid SelectionModels. It provides the interface that should be
24485 * implemented by descendant classes. This class should not be directly instantiated.
24488 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24489 this.locked = false;
24490 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24494 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24495 /** @ignore Called by the grid automatically. Do not call directly. */
24496 init : function(grid){
24502 * Locks the selections.
24505 this.locked = true;
24509 * Unlocks the selections.
24511 unlock : function(){
24512 this.locked = false;
24516 * Returns true if the selections are locked.
24517 * @return {Boolean}
24519 isLocked : function(){
24520 return this.locked;
24524 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24525 * @class Roo.bootstrap.Table.RowSelectionModel
24526 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24527 * It supports multiple selections and keyboard selection/navigation.
24529 * @param {Object} config
24532 Roo.bootstrap.Table.RowSelectionModel = function(config){
24533 Roo.apply(this, config);
24534 this.selections = new Roo.util.MixedCollection(false, function(o){
24539 this.lastActive = false;
24543 * @event selectionchange
24544 * Fires when the selection changes
24545 * @param {SelectionModel} this
24547 "selectionchange" : true,
24549 * @event afterselectionchange
24550 * Fires after the selection changes (eg. by key press or clicking)
24551 * @param {SelectionModel} this
24553 "afterselectionchange" : true,
24555 * @event beforerowselect
24556 * Fires when a row is selected being selected, return false to cancel.
24557 * @param {SelectionModel} this
24558 * @param {Number} rowIndex The selected index
24559 * @param {Boolean} keepExisting False if other selections will be cleared
24561 "beforerowselect" : true,
24564 * Fires when a row is selected.
24565 * @param {SelectionModel} this
24566 * @param {Number} rowIndex The selected index
24567 * @param {Roo.data.Record} r The record
24569 "rowselect" : true,
24571 * @event rowdeselect
24572 * Fires when a row is deselected.
24573 * @param {SelectionModel} this
24574 * @param {Number} rowIndex The selected index
24576 "rowdeselect" : true
24578 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24579 this.locked = false;
24582 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24584 * @cfg {Boolean} singleSelect
24585 * True to allow selection of only one row at a time (defaults to false)
24587 singleSelect : false,
24590 initEvents : function()
24593 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24594 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24595 //}else{ // allow click to work like normal
24596 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24598 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24599 this.grid.on("rowclick", this.handleMouseDown, this);
24601 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24602 "up" : function(e){
24604 this.selectPrevious(e.shiftKey);
24605 }else if(this.last !== false && this.lastActive !== false){
24606 var last = this.last;
24607 this.selectRange(this.last, this.lastActive-1);
24608 this.grid.getView().focusRow(this.lastActive);
24609 if(last !== false){
24613 this.selectFirstRow();
24615 this.fireEvent("afterselectionchange", this);
24617 "down" : function(e){
24619 this.selectNext(e.shiftKey);
24620 }else if(this.last !== false && this.lastActive !== false){
24621 var last = this.last;
24622 this.selectRange(this.last, this.lastActive+1);
24623 this.grid.getView().focusRow(this.lastActive);
24624 if(last !== false){
24628 this.selectFirstRow();
24630 this.fireEvent("afterselectionchange", this);
24634 this.grid.store.on('load', function(){
24635 this.selections.clear();
24638 var view = this.grid.view;
24639 view.on("refresh", this.onRefresh, this);
24640 view.on("rowupdated", this.onRowUpdated, this);
24641 view.on("rowremoved", this.onRemove, this);
24646 onRefresh : function()
24648 var ds = this.grid.store, i, v = this.grid.view;
24649 var s = this.selections;
24650 s.each(function(r){
24651 if((i = ds.indexOfId(r.id)) != -1){
24660 onRemove : function(v, index, r){
24661 this.selections.remove(r);
24665 onRowUpdated : function(v, index, r){
24666 if(this.isSelected(r)){
24667 v.onRowSelect(index);
24673 * @param {Array} records The records to select
24674 * @param {Boolean} keepExisting (optional) True to keep existing selections
24676 selectRecords : function(records, keepExisting)
24679 this.clearSelections();
24681 var ds = this.grid.store;
24682 for(var i = 0, len = records.length; i < len; i++){
24683 this.selectRow(ds.indexOf(records[i]), true);
24688 * Gets the number of selected rows.
24691 getCount : function(){
24692 return this.selections.length;
24696 * Selects the first row in the grid.
24698 selectFirstRow : function(){
24703 * Select the last row.
24704 * @param {Boolean} keepExisting (optional) True to keep existing selections
24706 selectLastRow : function(keepExisting){
24707 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24708 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24712 * Selects the row immediately following the last selected row.
24713 * @param {Boolean} keepExisting (optional) True to keep existing selections
24715 selectNext : function(keepExisting)
24717 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24718 this.selectRow(this.last+1, keepExisting);
24719 this.grid.getView().focusRow(this.last);
24724 * Selects the row that precedes the last selected row.
24725 * @param {Boolean} keepExisting (optional) True to keep existing selections
24727 selectPrevious : function(keepExisting){
24729 this.selectRow(this.last-1, keepExisting);
24730 this.grid.getView().focusRow(this.last);
24735 * Returns the selected records
24736 * @return {Array} Array of selected records
24738 getSelections : function(){
24739 return [].concat(this.selections.items);
24743 * Returns the first selected record.
24746 getSelected : function(){
24747 return this.selections.itemAt(0);
24752 * Clears all selections.
24754 clearSelections : function(fast)
24760 var ds = this.grid.store;
24761 var s = this.selections;
24762 s.each(function(r){
24763 this.deselectRow(ds.indexOfId(r.id));
24767 this.selections.clear();
24774 * Selects all rows.
24776 selectAll : function(){
24780 this.selections.clear();
24781 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24782 this.selectRow(i, true);
24787 * Returns True if there is a selection.
24788 * @return {Boolean}
24790 hasSelection : function(){
24791 return this.selections.length > 0;
24795 * Returns True if the specified row is selected.
24796 * @param {Number/Record} record The record or index of the record to check
24797 * @return {Boolean}
24799 isSelected : function(index){
24800 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24801 return (r && this.selections.key(r.id) ? true : false);
24805 * Returns True if the specified record id is selected.
24806 * @param {String} id The id of record to check
24807 * @return {Boolean}
24809 isIdSelected : function(id){
24810 return (this.selections.key(id) ? true : false);
24815 handleMouseDBClick : function(e, t){
24819 handleMouseDown : function(e, t)
24821 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24822 if(this.isLocked() || rowIndex < 0 ){
24825 if(e.shiftKey && this.last !== false){
24826 var last = this.last;
24827 this.selectRange(last, rowIndex, e.ctrlKey);
24828 this.last = last; // reset the last
24832 var isSelected = this.isSelected(rowIndex);
24833 //Roo.log("select row:" + rowIndex);
24835 this.deselectRow(rowIndex);
24837 this.selectRow(rowIndex, true);
24841 if(e.button !== 0 && isSelected){
24842 alert('rowIndex 2: ' + rowIndex);
24843 view.focusRow(rowIndex);
24844 }else if(e.ctrlKey && isSelected){
24845 this.deselectRow(rowIndex);
24846 }else if(!isSelected){
24847 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24848 view.focusRow(rowIndex);
24852 this.fireEvent("afterselectionchange", this);
24855 handleDragableRowClick : function(grid, rowIndex, e)
24857 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24858 this.selectRow(rowIndex, false);
24859 grid.view.focusRow(rowIndex);
24860 this.fireEvent("afterselectionchange", this);
24865 * Selects multiple rows.
24866 * @param {Array} rows Array of the indexes of the row to select
24867 * @param {Boolean} keepExisting (optional) True to keep existing selections
24869 selectRows : function(rows, keepExisting){
24871 this.clearSelections();
24873 for(var i = 0, len = rows.length; i < len; i++){
24874 this.selectRow(rows[i], true);
24879 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24880 * @param {Number} startRow The index of the first row in the range
24881 * @param {Number} endRow The index of the last row in the range
24882 * @param {Boolean} keepExisting (optional) True to retain existing selections
24884 selectRange : function(startRow, endRow, keepExisting){
24889 this.clearSelections();
24891 if(startRow <= endRow){
24892 for(var i = startRow; i <= endRow; i++){
24893 this.selectRow(i, true);
24896 for(var i = startRow; i >= endRow; i--){
24897 this.selectRow(i, true);
24903 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24904 * @param {Number} startRow The index of the first row in the range
24905 * @param {Number} endRow The index of the last row in the range
24907 deselectRange : function(startRow, endRow, preventViewNotify){
24911 for(var i = startRow; i <= endRow; i++){
24912 this.deselectRow(i, preventViewNotify);
24918 * @param {Number} row The index of the row to select
24919 * @param {Boolean} keepExisting (optional) True to keep existing selections
24921 selectRow : function(index, keepExisting, preventViewNotify)
24923 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24926 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24927 if(!keepExisting || this.singleSelect){
24928 this.clearSelections();
24931 var r = this.grid.store.getAt(index);
24932 //console.log('selectRow - record id :' + r.id);
24934 this.selections.add(r);
24935 this.last = this.lastActive = index;
24936 if(!preventViewNotify){
24937 var proxy = new Roo.Element(
24938 this.grid.getRowDom(index)
24940 proxy.addClass('bg-info info');
24942 this.fireEvent("rowselect", this, index, r);
24943 this.fireEvent("selectionchange", this);
24949 * @param {Number} row The index of the row to deselect
24951 deselectRow : function(index, preventViewNotify)
24956 if(this.last == index){
24959 if(this.lastActive == index){
24960 this.lastActive = false;
24963 var r = this.grid.store.getAt(index);
24968 this.selections.remove(r);
24969 //.console.log('deselectRow - record id :' + r.id);
24970 if(!preventViewNotify){
24972 var proxy = new Roo.Element(
24973 this.grid.getRowDom(index)
24975 proxy.removeClass('bg-info info');
24977 this.fireEvent("rowdeselect", this, index);
24978 this.fireEvent("selectionchange", this);
24982 restoreLast : function(){
24984 this.last = this._last;
24989 acceptsNav : function(row, col, cm){
24990 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24994 onEditorKey : function(field, e){
24995 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
25000 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
25002 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
25004 }else if(k == e.ENTER && !e.ctrlKey){
25008 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
25010 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25012 }else if(k == e.ESC){
25016 g.startEditing(newCell[0], newCell[1]);
25022 * Ext JS Library 1.1.1
25023 * Copyright(c) 2006-2007, Ext JS, LLC.
25025 * Originally Released Under LGPL - original licence link has changed is not relivant.
25028 * <script type="text/javascript">
25032 * @class Roo.bootstrap.PagingToolbar
25033 * @extends Roo.bootstrap.NavSimplebar
25034 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
25036 * Create a new PagingToolbar
25037 * @param {Object} config The config object
25038 * @param {Roo.data.Store} store
25040 Roo.bootstrap.PagingToolbar = function(config)
25042 // old args format still supported... - xtype is prefered..
25043 // created from xtype...
25045 this.ds = config.dataSource;
25047 if (config.store && !this.ds) {
25048 this.store= Roo.factory(config.store, Roo.data);
25049 this.ds = this.store;
25050 this.ds.xmodule = this.xmodule || false;
25053 this.toolbarItems = [];
25054 if (config.items) {
25055 this.toolbarItems = config.items;
25058 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
25063 this.bind(this.ds);
25066 if (Roo.bootstrap.version == 4) {
25067 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
25069 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
25074 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
25076 * @cfg {Roo.data.Store} dataSource
25077 * The underlying data store providing the paged data
25080 * @cfg {String/HTMLElement/Element} container
25081 * container The id or element that will contain the toolbar
25084 * @cfg {Boolean} displayInfo
25085 * True to display the displayMsg (defaults to false)
25088 * @cfg {Number} pageSize
25089 * The number of records to display per page (defaults to 20)
25093 * @cfg {String} displayMsg
25094 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25096 displayMsg : 'Displaying {0} - {1} of {2}',
25098 * @cfg {String} emptyMsg
25099 * The message to display when no records are found (defaults to "No data to display")
25101 emptyMsg : 'No data to display',
25103 * Customizable piece of the default paging text (defaults to "Page")
25106 beforePageText : "Page",
25108 * Customizable piece of the default paging text (defaults to "of %0")
25111 afterPageText : "of {0}",
25113 * Customizable piece of the default paging text (defaults to "First Page")
25116 firstText : "First Page",
25118 * Customizable piece of the default paging text (defaults to "Previous Page")
25121 prevText : "Previous Page",
25123 * Customizable piece of the default paging text (defaults to "Next Page")
25126 nextText : "Next Page",
25128 * Customizable piece of the default paging text (defaults to "Last Page")
25131 lastText : "Last Page",
25133 * Customizable piece of the default paging text (defaults to "Refresh")
25136 refreshText : "Refresh",
25140 onRender : function(ct, position)
25142 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25143 this.navgroup.parentId = this.id;
25144 this.navgroup.onRender(this.el, null);
25145 // add the buttons to the navgroup
25147 if(this.displayInfo){
25148 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25149 this.displayEl = this.el.select('.x-paging-info', true).first();
25150 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25151 // this.displayEl = navel.el.select('span',true).first();
25157 Roo.each(_this.buttons, function(e){ // this might need to use render????
25158 Roo.factory(e).render(_this.el);
25162 Roo.each(_this.toolbarItems, function(e) {
25163 _this.navgroup.addItem(e);
25167 this.first = this.navgroup.addItem({
25168 tooltip: this.firstText,
25169 cls: "prev btn-outline-secondary",
25170 html : ' <i class="fa fa-step-backward"></i>',
25172 preventDefault: true,
25173 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25176 this.prev = this.navgroup.addItem({
25177 tooltip: this.prevText,
25178 cls: "prev btn-outline-secondary",
25179 html : ' <i class="fa fa-backward"></i>',
25181 preventDefault: true,
25182 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
25184 //this.addSeparator();
25187 var field = this.navgroup.addItem( {
25189 cls : 'x-paging-position btn-outline-secondary',
25191 html : this.beforePageText +
25192 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25193 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
25196 this.field = field.el.select('input', true).first();
25197 this.field.on("keydown", this.onPagingKeydown, this);
25198 this.field.on("focus", function(){this.dom.select();});
25201 this.afterTextEl = field.el.select('.x-paging-after',true).first();
25202 //this.field.setHeight(18);
25203 //this.addSeparator();
25204 this.next = this.navgroup.addItem({
25205 tooltip: this.nextText,
25206 cls: "next btn-outline-secondary",
25207 html : ' <i class="fa fa-forward"></i>',
25209 preventDefault: true,
25210 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
25212 this.last = this.navgroup.addItem({
25213 tooltip: this.lastText,
25214 html : ' <i class="fa fa-step-forward"></i>',
25215 cls: "next btn-outline-secondary",
25217 preventDefault: true,
25218 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
25220 //this.addSeparator();
25221 this.loading = this.navgroup.addItem({
25222 tooltip: this.refreshText,
25223 cls: "btn-outline-secondary",
25224 html : ' <i class="fa fa-refresh"></i>',
25225 preventDefault: true,
25226 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25232 updateInfo : function(){
25233 if(this.displayEl){
25234 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25235 var msg = count == 0 ?
25239 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
25241 this.displayEl.update(msg);
25246 onLoad : function(ds, r, o)
25248 this.cursor = o.params.start ? o.params.start : 0;
25250 var d = this.getPageData(),
25255 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25256 this.field.dom.value = ap;
25257 this.first.setDisabled(ap == 1);
25258 this.prev.setDisabled(ap == 1);
25259 this.next.setDisabled(ap == ps);
25260 this.last.setDisabled(ap == ps);
25261 this.loading.enable();
25266 getPageData : function(){
25267 var total = this.ds.getTotalCount();
25270 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25271 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25276 onLoadError : function(){
25277 this.loading.enable();
25281 onPagingKeydown : function(e){
25282 var k = e.getKey();
25283 var d = this.getPageData();
25285 var v = this.field.dom.value, pageNum;
25286 if(!v || isNaN(pageNum = parseInt(v, 10))){
25287 this.field.dom.value = d.activePage;
25290 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25291 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25294 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))
25296 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25297 this.field.dom.value = pageNum;
25298 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25301 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25303 var v = this.field.dom.value, pageNum;
25304 var increment = (e.shiftKey) ? 10 : 1;
25305 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25308 if(!v || isNaN(pageNum = parseInt(v, 10))) {
25309 this.field.dom.value = d.activePage;
25312 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25314 this.field.dom.value = parseInt(v, 10) + increment;
25315 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25316 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25323 beforeLoad : function(){
25325 this.loading.disable();
25330 onClick : function(which){
25339 ds.load({params:{start: 0, limit: this.pageSize}});
25342 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25345 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25348 var total = ds.getTotalCount();
25349 var extra = total % this.pageSize;
25350 var lastStart = extra ? (total - extra) : total-this.pageSize;
25351 ds.load({params:{start: lastStart, limit: this.pageSize}});
25354 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25360 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25361 * @param {Roo.data.Store} store The data store to unbind
25363 unbind : function(ds){
25364 ds.un("beforeload", this.beforeLoad, this);
25365 ds.un("load", this.onLoad, this);
25366 ds.un("loadexception", this.onLoadError, this);
25367 ds.un("remove", this.updateInfo, this);
25368 ds.un("add", this.updateInfo, this);
25369 this.ds = undefined;
25373 * Binds the paging toolbar to the specified {@link Roo.data.Store}
25374 * @param {Roo.data.Store} store The data store to bind
25376 bind : function(ds){
25377 ds.on("beforeload", this.beforeLoad, this);
25378 ds.on("load", this.onLoad, this);
25379 ds.on("loadexception", this.onLoadError, this);
25380 ds.on("remove", this.updateInfo, this);
25381 ds.on("add", this.updateInfo, this);
25392 * @class Roo.bootstrap.MessageBar
25393 * @extends Roo.bootstrap.Component
25394 * Bootstrap MessageBar class
25395 * @cfg {String} html contents of the MessageBar
25396 * @cfg {String} weight (info | success | warning | danger) default info
25397 * @cfg {String} beforeClass insert the bar before the given class
25398 * @cfg {Boolean} closable (true | false) default false
25399 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25402 * Create a new Element
25403 * @param {Object} config The config object
25406 Roo.bootstrap.MessageBar = function(config){
25407 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25410 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25416 beforeClass: 'bootstrap-sticky-wrap',
25418 getAutoCreate : function(){
25422 cls: 'alert alert-dismissable alert-' + this.weight,
25427 html: this.html || ''
25433 cfg.cls += ' alert-messages-fixed';
25447 onRender : function(ct, position)
25449 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25452 var cfg = Roo.apply({}, this.getAutoCreate());
25456 cfg.cls += ' ' + this.cls;
25459 cfg.style = this.style;
25461 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25463 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25466 this.el.select('>button.close').on('click', this.hide, this);
25472 if (!this.rendered) {
25478 this.fireEvent('show', this);
25484 if (!this.rendered) {
25490 this.fireEvent('hide', this);
25493 update : function()
25495 // var e = this.el.dom.firstChild;
25497 // if(this.closable){
25498 // e = e.nextSibling;
25501 // e.data = this.html || '';
25503 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25519 * @class Roo.bootstrap.Graph
25520 * @extends Roo.bootstrap.Component
25521 * Bootstrap Graph class
25525 @cfg {String} graphtype bar | vbar | pie
25526 @cfg {number} g_x coodinator | centre x (pie)
25527 @cfg {number} g_y coodinator | centre y (pie)
25528 @cfg {number} g_r radius (pie)
25529 @cfg {number} g_height height of the chart (respected by all elements in the set)
25530 @cfg {number} g_width width of the chart (respected by all elements in the set)
25531 @cfg {Object} title The title of the chart
25534 -opts (object) options for the chart
25536 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25537 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25539 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.
25540 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25542 o stretch (boolean)
25544 -opts (object) options for the pie
25547 o startAngle (number)
25548 o endAngle (number)
25552 * Create a new Input
25553 * @param {Object} config The config object
25556 Roo.bootstrap.Graph = function(config){
25557 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25563 * The img click event for the img.
25564 * @param {Roo.EventObject} e
25570 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25581 //g_colors: this.colors,
25588 getAutoCreate : function(){
25599 onRender : function(ct,position){
25602 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25604 if (typeof(Raphael) == 'undefined') {
25605 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25609 this.raphael = Raphael(this.el.dom);
25611 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25612 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25613 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25614 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25616 r.text(160, 10, "Single Series Chart").attr(txtattr);
25617 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25618 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25619 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25621 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25622 r.barchart(330, 10, 300, 220, data1);
25623 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25624 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25627 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25628 // r.barchart(30, 30, 560, 250, xdata, {
25629 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25630 // axis : "0 0 1 1",
25631 // axisxlabels : xdata
25632 // //yvalues : cols,
25635 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25637 // this.load(null,xdata,{
25638 // axis : "0 0 1 1",
25639 // axisxlabels : xdata
25644 load : function(graphtype,xdata,opts)
25646 this.raphael.clear();
25648 graphtype = this.graphtype;
25653 var r = this.raphael,
25654 fin = function () {
25655 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25657 fout = function () {
25658 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25660 pfin = function() {
25661 this.sector.stop();
25662 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25665 this.label[0].stop();
25666 this.label[0].attr({ r: 7.5 });
25667 this.label[1].attr({ "font-weight": 800 });
25670 pfout = function() {
25671 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25674 this.label[0].animate({ r: 5 }, 500, "bounce");
25675 this.label[1].attr({ "font-weight": 400 });
25681 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25684 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25687 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25688 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25690 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25697 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25702 setTitle: function(o)
25707 initEvents: function() {
25710 this.el.on('click', this.onClick, this);
25714 onClick : function(e)
25716 Roo.log('img onclick');
25717 this.fireEvent('click', this, e);
25729 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25732 * @class Roo.bootstrap.dash.NumberBox
25733 * @extends Roo.bootstrap.Component
25734 * Bootstrap NumberBox class
25735 * @cfg {String} headline Box headline
25736 * @cfg {String} content Box content
25737 * @cfg {String} icon Box icon
25738 * @cfg {String} footer Footer text
25739 * @cfg {String} fhref Footer href
25742 * Create a new NumberBox
25743 * @param {Object} config The config object
25747 Roo.bootstrap.dash.NumberBox = function(config){
25748 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25752 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25761 getAutoCreate : function(){
25765 cls : 'small-box ',
25773 cls : 'roo-headline',
25774 html : this.headline
25778 cls : 'roo-content',
25779 html : this.content
25793 cls : 'ion ' + this.icon
25802 cls : 'small-box-footer',
25803 href : this.fhref || '#',
25807 cfg.cn.push(footer);
25814 onRender : function(ct,position){
25815 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25822 setHeadline: function (value)
25824 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25827 setFooter: function (value, href)
25829 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25832 this.el.select('a.small-box-footer',true).first().attr('href', href);
25837 setContent: function (value)
25839 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25842 initEvents: function()
25856 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25859 * @class Roo.bootstrap.dash.TabBox
25860 * @extends Roo.bootstrap.Component
25861 * Bootstrap TabBox class
25862 * @cfg {String} title Title of the TabBox
25863 * @cfg {String} icon Icon of the TabBox
25864 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25865 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25868 * Create a new TabBox
25869 * @param {Object} config The config object
25873 Roo.bootstrap.dash.TabBox = function(config){
25874 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25879 * When a pane is added
25880 * @param {Roo.bootstrap.dash.TabPane} pane
25884 * @event activatepane
25885 * When a pane is activated
25886 * @param {Roo.bootstrap.dash.TabPane} pane
25888 "activatepane" : true
25896 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25901 tabScrollable : false,
25903 getChildContainer : function()
25905 return this.el.select('.tab-content', true).first();
25908 getAutoCreate : function(){
25912 cls: 'pull-left header',
25920 cls: 'fa ' + this.icon
25926 cls: 'nav nav-tabs pull-right',
25932 if(this.tabScrollable){
25939 cls: 'nav nav-tabs pull-right',
25950 cls: 'nav-tabs-custom',
25955 cls: 'tab-content no-padding',
25963 initEvents : function()
25965 //Roo.log('add add pane handler');
25966 this.on('addpane', this.onAddPane, this);
25969 * Updates the box title
25970 * @param {String} html to set the title to.
25972 setTitle : function(value)
25974 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25976 onAddPane : function(pane)
25978 this.panes.push(pane);
25979 //Roo.log('addpane');
25981 // tabs are rendere left to right..
25982 if(!this.showtabs){
25986 var ctr = this.el.select('.nav-tabs', true).first();
25989 var existing = ctr.select('.nav-tab',true);
25990 var qty = existing.getCount();;
25993 var tab = ctr.createChild({
25995 cls : 'nav-tab' + (qty ? '' : ' active'),
26003 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
26006 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
26008 pane.el.addClass('active');
26013 onTabClick : function(ev,un,ob,pane)
26015 //Roo.log('tab - prev default');
26016 ev.preventDefault();
26019 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26020 pane.tab.addClass('active');
26021 //Roo.log(pane.title);
26022 this.getChildContainer().select('.tab-pane',true).removeClass('active');
26023 // technically we should have a deactivate event.. but maybe add later.
26024 // and it should not de-activate the selected tab...
26025 this.fireEvent('activatepane', pane);
26026 pane.el.addClass('active');
26027 pane.fireEvent('activate');
26032 getActivePane : function()
26035 Roo.each(this.panes, function(p) {
26036 if(p.el.hasClass('active')){
26057 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26059 * @class Roo.bootstrap.TabPane
26060 * @extends Roo.bootstrap.Component
26061 * Bootstrap TabPane class
26062 * @cfg {Boolean} active (false | true) Default false
26063 * @cfg {String} title title of panel
26067 * Create a new TabPane
26068 * @param {Object} config The config object
26071 Roo.bootstrap.dash.TabPane = function(config){
26072 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
26078 * When a pane is activated
26079 * @param {Roo.bootstrap.dash.TabPane} pane
26086 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
26091 // the tabBox that this is attached to.
26094 getAutoCreate : function()
26102 cfg.cls += ' active';
26107 initEvents : function()
26109 //Roo.log('trigger add pane handler');
26110 this.parent().fireEvent('addpane', this)
26114 * Updates the tab title
26115 * @param {String} html to set the title to.
26117 setTitle: function(str)
26123 this.tab.select('a', true).first().dom.innerHTML = str;
26140 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26143 * @class Roo.bootstrap.menu.Menu
26144 * @extends Roo.bootstrap.Component
26145 * Bootstrap Menu class - container for Menu
26146 * @cfg {String} html Text of the menu
26147 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26148 * @cfg {String} icon Font awesome icon
26149 * @cfg {String} pos Menu align to (top | bottom) default bottom
26153 * Create a new Menu
26154 * @param {Object} config The config object
26158 Roo.bootstrap.menu.Menu = function(config){
26159 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26163 * @event beforeshow
26164 * Fires before this menu is displayed
26165 * @param {Roo.bootstrap.menu.Menu} this
26169 * @event beforehide
26170 * Fires before this menu is hidden
26171 * @param {Roo.bootstrap.menu.Menu} this
26176 * Fires after this menu is displayed
26177 * @param {Roo.bootstrap.menu.Menu} this
26182 * Fires after this menu is hidden
26183 * @param {Roo.bootstrap.menu.Menu} this
26188 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26189 * @param {Roo.bootstrap.menu.Menu} this
26190 * @param {Roo.EventObject} e
26197 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
26201 weight : 'default',
26206 getChildContainer : function() {
26207 if(this.isSubMenu){
26211 return this.el.select('ul.dropdown-menu', true).first();
26214 getAutoCreate : function()
26219 cls : 'roo-menu-text',
26227 cls : 'fa ' + this.icon
26238 cls : 'dropdown-button btn btn-' + this.weight,
26243 cls : 'dropdown-toggle btn btn-' + this.weight,
26253 cls : 'dropdown-menu'
26259 if(this.pos == 'top'){
26260 cfg.cls += ' dropup';
26263 if(this.isSubMenu){
26266 cls : 'dropdown-menu'
26273 onRender : function(ct, position)
26275 this.isSubMenu = ct.hasClass('dropdown-submenu');
26277 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26280 initEvents : function()
26282 if(this.isSubMenu){
26286 this.hidden = true;
26288 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26289 this.triggerEl.on('click', this.onTriggerPress, this);
26291 this.buttonEl = this.el.select('button.dropdown-button', true).first();
26292 this.buttonEl.on('click', this.onClick, this);
26298 if(this.isSubMenu){
26302 return this.el.select('ul.dropdown-menu', true).first();
26305 onClick : function(e)
26307 this.fireEvent("click", this, e);
26310 onTriggerPress : function(e)
26312 if (this.isVisible()) {
26319 isVisible : function(){
26320 return !this.hidden;
26325 this.fireEvent("beforeshow", this);
26327 this.hidden = false;
26328 this.el.addClass('open');
26330 Roo.get(document).on("mouseup", this.onMouseUp, this);
26332 this.fireEvent("show", this);
26339 this.fireEvent("beforehide", this);
26341 this.hidden = true;
26342 this.el.removeClass('open');
26344 Roo.get(document).un("mouseup", this.onMouseUp);
26346 this.fireEvent("hide", this);
26349 onMouseUp : function()
26363 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26366 * @class Roo.bootstrap.menu.Item
26367 * @extends Roo.bootstrap.Component
26368 * Bootstrap MenuItem class
26369 * @cfg {Boolean} submenu (true | false) default false
26370 * @cfg {String} html text of the item
26371 * @cfg {String} href the link
26372 * @cfg {Boolean} disable (true | false) default false
26373 * @cfg {Boolean} preventDefault (true | false) default true
26374 * @cfg {String} icon Font awesome icon
26375 * @cfg {String} pos Submenu align to (left | right) default right
26379 * Create a new Item
26380 * @param {Object} config The config object
26384 Roo.bootstrap.menu.Item = function(config){
26385 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26389 * Fires when the mouse is hovering over this menu
26390 * @param {Roo.bootstrap.menu.Item} this
26391 * @param {Roo.EventObject} e
26396 * Fires when the mouse exits this menu
26397 * @param {Roo.bootstrap.menu.Item} this
26398 * @param {Roo.EventObject} e
26404 * The raw click event for the entire grid.
26405 * @param {Roo.EventObject} e
26411 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26416 preventDefault: true,
26421 getAutoCreate : function()
26426 cls : 'roo-menu-item-text',
26434 cls : 'fa ' + this.icon
26443 href : this.href || '#',
26450 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26454 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26456 if(this.pos == 'left'){
26457 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26464 initEvents : function()
26466 this.el.on('mouseover', this.onMouseOver, this);
26467 this.el.on('mouseout', this.onMouseOut, this);
26469 this.el.select('a', true).first().on('click', this.onClick, this);
26473 onClick : function(e)
26475 if(this.preventDefault){
26476 e.preventDefault();
26479 this.fireEvent("click", this, e);
26482 onMouseOver : function(e)
26484 if(this.submenu && this.pos == 'left'){
26485 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26488 this.fireEvent("mouseover", this, e);
26491 onMouseOut : function(e)
26493 this.fireEvent("mouseout", this, e);
26505 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26508 * @class Roo.bootstrap.menu.Separator
26509 * @extends Roo.bootstrap.Component
26510 * Bootstrap Separator class
26513 * Create a new Separator
26514 * @param {Object} config The config object
26518 Roo.bootstrap.menu.Separator = function(config){
26519 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26522 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26524 getAutoCreate : function(){
26545 * @class Roo.bootstrap.Tooltip
26546 * Bootstrap Tooltip class
26547 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26548 * to determine which dom element triggers the tooltip.
26550 * It needs to add support for additional attributes like tooltip-position
26553 * Create a new Toolti
26554 * @param {Object} config The config object
26557 Roo.bootstrap.Tooltip = function(config){
26558 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26560 this.alignment = Roo.bootstrap.Tooltip.alignment;
26562 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26563 this.alignment = config.alignment;
26568 Roo.apply(Roo.bootstrap.Tooltip, {
26570 * @function init initialize tooltip monitoring.
26574 currentTip : false,
26575 currentRegion : false,
26581 Roo.get(document).on('mouseover', this.enter ,this);
26582 Roo.get(document).on('mouseout', this.leave, this);
26585 this.currentTip = new Roo.bootstrap.Tooltip();
26588 enter : function(ev)
26590 var dom = ev.getTarget();
26592 //Roo.log(['enter',dom]);
26593 var el = Roo.fly(dom);
26594 if (this.currentEl) {
26596 //Roo.log(this.currentEl);
26597 //Roo.log(this.currentEl.contains(dom));
26598 if (this.currentEl == el) {
26601 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26607 if (this.currentTip.el) {
26608 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26612 if(!el || el.dom == document){
26618 // you can not look for children, as if el is the body.. then everythign is the child..
26619 if (!el.attr('tooltip')) { //
26620 if (!el.select("[tooltip]").elements.length) {
26623 // is the mouse over this child...?
26624 bindEl = el.select("[tooltip]").first();
26625 var xy = ev.getXY();
26626 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26627 //Roo.log("not in region.");
26630 //Roo.log("child element over..");
26633 this.currentEl = bindEl;
26634 this.currentTip.bind(bindEl);
26635 this.currentRegion = Roo.lib.Region.getRegion(dom);
26636 this.currentTip.enter();
26639 leave : function(ev)
26641 var dom = ev.getTarget();
26642 //Roo.log(['leave',dom]);
26643 if (!this.currentEl) {
26648 if (dom != this.currentEl.dom) {
26651 var xy = ev.getXY();
26652 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26655 // only activate leave if mouse cursor is outside... bounding box..
26660 if (this.currentTip) {
26661 this.currentTip.leave();
26663 //Roo.log('clear currentEl');
26664 this.currentEl = false;
26669 'left' : ['r-l', [-2,0], 'right'],
26670 'right' : ['l-r', [2,0], 'left'],
26671 'bottom' : ['t-b', [0,2], 'top'],
26672 'top' : [ 'b-t', [0,-2], 'bottom']
26678 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26683 delay : null, // can be { show : 300 , hide: 500}
26687 hoverState : null, //???
26689 placement : 'bottom',
26693 getAutoCreate : function(){
26700 cls : 'tooltip-arrow'
26703 cls : 'tooltip-inner'
26710 bind : function(el)
26716 enter : function () {
26718 if (this.timeout != null) {
26719 clearTimeout(this.timeout);
26722 this.hoverState = 'in';
26723 //Roo.log("enter - show");
26724 if (!this.delay || !this.delay.show) {
26729 this.timeout = setTimeout(function () {
26730 if (_t.hoverState == 'in') {
26733 }, this.delay.show);
26737 clearTimeout(this.timeout);
26739 this.hoverState = 'out';
26740 if (!this.delay || !this.delay.hide) {
26746 this.timeout = setTimeout(function () {
26747 //Roo.log("leave - timeout");
26749 if (_t.hoverState == 'out') {
26751 Roo.bootstrap.Tooltip.currentEl = false;
26756 show : function (msg)
26759 this.render(document.body);
26762 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26764 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26766 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26768 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26770 var placement = typeof this.placement == 'function' ?
26771 this.placement.call(this, this.el, on_el) :
26774 var autoToken = /\s?auto?\s?/i;
26775 var autoPlace = autoToken.test(placement);
26777 placement = placement.replace(autoToken, '') || 'top';
26781 //this.el.setXY([0,0]);
26783 //this.el.dom.style.display='block';
26785 //this.el.appendTo(on_el);
26787 var p = this.getPosition();
26788 var box = this.el.getBox();
26794 var align = this.alignment[placement];
26796 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26798 if(placement == 'top' || placement == 'bottom'){
26800 placement = 'right';
26803 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26804 placement = 'left';
26807 var scroll = Roo.select('body', true).first().getScroll();
26809 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26813 align = this.alignment[placement];
26816 this.el.alignTo(this.bindEl, align[0],align[1]);
26817 //var arrow = this.el.select('.arrow',true).first();
26818 //arrow.set(align[2],
26820 this.el.addClass(placement);
26822 this.el.addClass('in fade');
26824 this.hoverState = null;
26826 if (this.el.hasClass('fade')) {
26837 //this.el.setXY([0,0]);
26838 this.el.removeClass('in');
26854 * @class Roo.bootstrap.LocationPicker
26855 * @extends Roo.bootstrap.Component
26856 * Bootstrap LocationPicker class
26857 * @cfg {Number} latitude Position when init default 0
26858 * @cfg {Number} longitude Position when init default 0
26859 * @cfg {Number} zoom default 15
26860 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26861 * @cfg {Boolean} mapTypeControl default false
26862 * @cfg {Boolean} disableDoubleClickZoom default false
26863 * @cfg {Boolean} scrollwheel default true
26864 * @cfg {Boolean} streetViewControl default false
26865 * @cfg {Number} radius default 0
26866 * @cfg {String} locationName
26867 * @cfg {Boolean} draggable default true
26868 * @cfg {Boolean} enableAutocomplete default false
26869 * @cfg {Boolean} enableReverseGeocode default true
26870 * @cfg {String} markerTitle
26873 * Create a new LocationPicker
26874 * @param {Object} config The config object
26878 Roo.bootstrap.LocationPicker = function(config){
26880 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26885 * Fires when the picker initialized.
26886 * @param {Roo.bootstrap.LocationPicker} this
26887 * @param {Google Location} location
26891 * @event positionchanged
26892 * Fires when the picker position changed.
26893 * @param {Roo.bootstrap.LocationPicker} this
26894 * @param {Google Location} location
26896 positionchanged : true,
26899 * Fires when the map resize.
26900 * @param {Roo.bootstrap.LocationPicker} this
26905 * Fires when the map show.
26906 * @param {Roo.bootstrap.LocationPicker} this
26911 * Fires when the map hide.
26912 * @param {Roo.bootstrap.LocationPicker} this
26917 * Fires when click the map.
26918 * @param {Roo.bootstrap.LocationPicker} this
26919 * @param {Map event} e
26923 * @event mapRightClick
26924 * Fires when right click the map.
26925 * @param {Roo.bootstrap.LocationPicker} this
26926 * @param {Map event} e
26928 mapRightClick : true,
26930 * @event markerClick
26931 * Fires when click the marker.
26932 * @param {Roo.bootstrap.LocationPicker} this
26933 * @param {Map event} e
26935 markerClick : true,
26937 * @event markerRightClick
26938 * Fires when right click the marker.
26939 * @param {Roo.bootstrap.LocationPicker} this
26940 * @param {Map event} e
26942 markerRightClick : true,
26944 * @event OverlayViewDraw
26945 * Fires when OverlayView Draw
26946 * @param {Roo.bootstrap.LocationPicker} this
26948 OverlayViewDraw : true,
26950 * @event OverlayViewOnAdd
26951 * Fires when OverlayView Draw
26952 * @param {Roo.bootstrap.LocationPicker} this
26954 OverlayViewOnAdd : true,
26956 * @event OverlayViewOnRemove
26957 * Fires when OverlayView Draw
26958 * @param {Roo.bootstrap.LocationPicker} this
26960 OverlayViewOnRemove : true,
26962 * @event OverlayViewShow
26963 * Fires when OverlayView Draw
26964 * @param {Roo.bootstrap.LocationPicker} this
26965 * @param {Pixel} cpx
26967 OverlayViewShow : true,
26969 * @event OverlayViewHide
26970 * Fires when OverlayView Draw
26971 * @param {Roo.bootstrap.LocationPicker} this
26973 OverlayViewHide : true,
26975 * @event loadexception
26976 * Fires when load google lib failed.
26977 * @param {Roo.bootstrap.LocationPicker} this
26979 loadexception : true
26984 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26986 gMapContext: false,
26992 mapTypeControl: false,
26993 disableDoubleClickZoom: false,
26995 streetViewControl: false,
26999 enableAutocomplete: false,
27000 enableReverseGeocode: true,
27003 getAutoCreate: function()
27008 cls: 'roo-location-picker'
27014 initEvents: function(ct, position)
27016 if(!this.el.getWidth() || this.isApplied()){
27020 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27025 initial: function()
27027 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27028 this.fireEvent('loadexception', this);
27032 if(!this.mapTypeId){
27033 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
27036 this.gMapContext = this.GMapContext();
27038 this.initOverlayView();
27040 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
27044 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
27045 _this.setPosition(_this.gMapContext.marker.position);
27048 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
27049 _this.fireEvent('mapClick', this, event);
27053 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
27054 _this.fireEvent('mapRightClick', this, event);
27058 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
27059 _this.fireEvent('markerClick', this, event);
27063 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
27064 _this.fireEvent('markerRightClick', this, event);
27068 this.setPosition(this.gMapContext.location);
27070 this.fireEvent('initial', this, this.gMapContext.location);
27073 initOverlayView: function()
27077 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
27081 _this.fireEvent('OverlayViewDraw', _this);
27086 _this.fireEvent('OverlayViewOnAdd', _this);
27089 onRemove: function()
27091 _this.fireEvent('OverlayViewOnRemove', _this);
27094 show: function(cpx)
27096 _this.fireEvent('OverlayViewShow', _this, cpx);
27101 _this.fireEvent('OverlayViewHide', _this);
27107 fromLatLngToContainerPixel: function(event)
27109 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27112 isApplied: function()
27114 return this.getGmapContext() == false ? false : true;
27117 getGmapContext: function()
27119 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27122 GMapContext: function()
27124 var position = new google.maps.LatLng(this.latitude, this.longitude);
27126 var _map = new google.maps.Map(this.el.dom, {
27129 mapTypeId: this.mapTypeId,
27130 mapTypeControl: this.mapTypeControl,
27131 disableDoubleClickZoom: this.disableDoubleClickZoom,
27132 scrollwheel: this.scrollwheel,
27133 streetViewControl: this.streetViewControl,
27134 locationName: this.locationName,
27135 draggable: this.draggable,
27136 enableAutocomplete: this.enableAutocomplete,
27137 enableReverseGeocode: this.enableReverseGeocode
27140 var _marker = new google.maps.Marker({
27141 position: position,
27143 title: this.markerTitle,
27144 draggable: this.draggable
27151 location: position,
27152 radius: this.radius,
27153 locationName: this.locationName,
27154 addressComponents: {
27155 formatted_address: null,
27156 addressLine1: null,
27157 addressLine2: null,
27159 streetNumber: null,
27163 stateOrProvince: null
27166 domContainer: this.el.dom,
27167 geodecoder: new google.maps.Geocoder()
27171 drawCircle: function(center, radius, options)
27173 if (this.gMapContext.circle != null) {
27174 this.gMapContext.circle.setMap(null);
27178 options = Roo.apply({}, options, {
27179 strokeColor: "#0000FF",
27180 strokeOpacity: .35,
27182 fillColor: "#0000FF",
27186 options.map = this.gMapContext.map;
27187 options.radius = radius;
27188 options.center = center;
27189 this.gMapContext.circle = new google.maps.Circle(options);
27190 return this.gMapContext.circle;
27196 setPosition: function(location)
27198 this.gMapContext.location = location;
27199 this.gMapContext.marker.setPosition(location);
27200 this.gMapContext.map.panTo(location);
27201 this.drawCircle(location, this.gMapContext.radius, {});
27205 if (this.gMapContext.settings.enableReverseGeocode) {
27206 this.gMapContext.geodecoder.geocode({
27207 latLng: this.gMapContext.location
27208 }, function(results, status) {
27210 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27211 _this.gMapContext.locationName = results[0].formatted_address;
27212 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27214 _this.fireEvent('positionchanged', this, location);
27221 this.fireEvent('positionchanged', this, location);
27226 google.maps.event.trigger(this.gMapContext.map, "resize");
27228 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27230 this.fireEvent('resize', this);
27233 setPositionByLatLng: function(latitude, longitude)
27235 this.setPosition(new google.maps.LatLng(latitude, longitude));
27238 getCurrentPosition: function()
27241 latitude: this.gMapContext.location.lat(),
27242 longitude: this.gMapContext.location.lng()
27246 getAddressName: function()
27248 return this.gMapContext.locationName;
27251 getAddressComponents: function()
27253 return this.gMapContext.addressComponents;
27256 address_component_from_google_geocode: function(address_components)
27260 for (var i = 0; i < address_components.length; i++) {
27261 var component = address_components[i];
27262 if (component.types.indexOf("postal_code") >= 0) {
27263 result.postalCode = component.short_name;
27264 } else if (component.types.indexOf("street_number") >= 0) {
27265 result.streetNumber = component.short_name;
27266 } else if (component.types.indexOf("route") >= 0) {
27267 result.streetName = component.short_name;
27268 } else if (component.types.indexOf("neighborhood") >= 0) {
27269 result.city = component.short_name;
27270 } else if (component.types.indexOf("locality") >= 0) {
27271 result.city = component.short_name;
27272 } else if (component.types.indexOf("sublocality") >= 0) {
27273 result.district = component.short_name;
27274 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27275 result.stateOrProvince = component.short_name;
27276 } else if (component.types.indexOf("country") >= 0) {
27277 result.country = component.short_name;
27281 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27282 result.addressLine2 = "";
27286 setZoomLevel: function(zoom)
27288 this.gMapContext.map.setZoom(zoom);
27301 this.fireEvent('show', this);
27312 this.fireEvent('hide', this);
27317 Roo.apply(Roo.bootstrap.LocationPicker, {
27319 OverlayView : function(map, options)
27321 options = options || {};
27328 * @class Roo.bootstrap.Alert
27329 * @extends Roo.bootstrap.Component
27330 * Bootstrap Alert class - shows an alert area box
27332 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27333 Enter a valid email address
27336 * @cfg {String} title The title of alert
27337 * @cfg {String} html The content of alert
27338 * @cfg {String} weight ( success | info | warning | danger )
27339 * @cfg {String} faicon font-awesomeicon
27342 * Create a new alert
27343 * @param {Object} config The config object
27347 Roo.bootstrap.Alert = function(config){
27348 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27352 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
27359 getAutoCreate : function()
27368 cls : 'roo-alert-icon'
27373 cls : 'roo-alert-title',
27378 cls : 'roo-alert-text',
27385 cfg.cn[0].cls += ' fa ' + this.faicon;
27389 cfg.cls += ' alert-' + this.weight;
27395 initEvents: function()
27397 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27400 setTitle : function(str)
27402 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27405 setText : function(str)
27407 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27410 setWeight : function(weight)
27413 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27416 this.weight = weight;
27418 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27421 setIcon : function(icon)
27424 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27427 this.faicon = icon;
27429 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27450 * @class Roo.bootstrap.UploadCropbox
27451 * @extends Roo.bootstrap.Component
27452 * Bootstrap UploadCropbox class
27453 * @cfg {String} emptyText show when image has been loaded
27454 * @cfg {String} rotateNotify show when image too small to rotate
27455 * @cfg {Number} errorTimeout default 3000
27456 * @cfg {Number} minWidth default 300
27457 * @cfg {Number} minHeight default 300
27458 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27459 * @cfg {Boolean} isDocument (true|false) default false
27460 * @cfg {String} url action url
27461 * @cfg {String} paramName default 'imageUpload'
27462 * @cfg {String} method default POST
27463 * @cfg {Boolean} loadMask (true|false) default true
27464 * @cfg {Boolean} loadingText default 'Loading...'
27467 * Create a new UploadCropbox
27468 * @param {Object} config The config object
27471 Roo.bootstrap.UploadCropbox = function(config){
27472 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27476 * @event beforeselectfile
27477 * Fire before select file
27478 * @param {Roo.bootstrap.UploadCropbox} this
27480 "beforeselectfile" : true,
27483 * Fire after initEvent
27484 * @param {Roo.bootstrap.UploadCropbox} this
27489 * Fire after initEvent
27490 * @param {Roo.bootstrap.UploadCropbox} this
27491 * @param {String} data
27496 * Fire when preparing the file data
27497 * @param {Roo.bootstrap.UploadCropbox} this
27498 * @param {Object} file
27503 * Fire when get exception
27504 * @param {Roo.bootstrap.UploadCropbox} this
27505 * @param {XMLHttpRequest} xhr
27507 "exception" : true,
27509 * @event beforeloadcanvas
27510 * Fire before load the canvas
27511 * @param {Roo.bootstrap.UploadCropbox} this
27512 * @param {String} src
27514 "beforeloadcanvas" : true,
27517 * Fire when trash image
27518 * @param {Roo.bootstrap.UploadCropbox} this
27523 * Fire when download the image
27524 * @param {Roo.bootstrap.UploadCropbox} this
27528 * @event footerbuttonclick
27529 * Fire when footerbuttonclick
27530 * @param {Roo.bootstrap.UploadCropbox} this
27531 * @param {String} type
27533 "footerbuttonclick" : true,
27537 * @param {Roo.bootstrap.UploadCropbox} this
27542 * Fire when rotate the image
27543 * @param {Roo.bootstrap.UploadCropbox} this
27544 * @param {String} pos
27549 * Fire when inspect the file
27550 * @param {Roo.bootstrap.UploadCropbox} this
27551 * @param {Object} file
27556 * Fire when xhr upload the file
27557 * @param {Roo.bootstrap.UploadCropbox} this
27558 * @param {Object} data
27563 * Fire when arrange the file data
27564 * @param {Roo.bootstrap.UploadCropbox} this
27565 * @param {Object} formData
27570 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27573 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27575 emptyText : 'Click to upload image',
27576 rotateNotify : 'Image is too small to rotate',
27577 errorTimeout : 3000,
27591 cropType : 'image/jpeg',
27593 canvasLoaded : false,
27594 isDocument : false,
27596 paramName : 'imageUpload',
27598 loadingText : 'Loading...',
27601 getAutoCreate : function()
27605 cls : 'roo-upload-cropbox',
27609 cls : 'roo-upload-cropbox-selector',
27614 cls : 'roo-upload-cropbox-body',
27615 style : 'cursor:pointer',
27619 cls : 'roo-upload-cropbox-preview'
27623 cls : 'roo-upload-cropbox-thumb'
27627 cls : 'roo-upload-cropbox-empty-notify',
27628 html : this.emptyText
27632 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27633 html : this.rotateNotify
27639 cls : 'roo-upload-cropbox-footer',
27642 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27652 onRender : function(ct, position)
27654 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27656 if (this.buttons.length) {
27658 Roo.each(this.buttons, function(bb) {
27660 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27662 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27668 this.maskEl = this.el;
27672 initEvents : function()
27674 this.urlAPI = (window.createObjectURL && window) ||
27675 (window.URL && URL.revokeObjectURL && URL) ||
27676 (window.webkitURL && webkitURL);
27678 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27679 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27681 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27682 this.selectorEl.hide();
27684 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27685 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27687 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27688 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27689 this.thumbEl.hide();
27691 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27692 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27694 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27695 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27696 this.errorEl.hide();
27698 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27699 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27700 this.footerEl.hide();
27702 this.setThumbBoxSize();
27708 this.fireEvent('initial', this);
27715 window.addEventListener("resize", function() { _this.resize(); } );
27717 this.bodyEl.on('click', this.beforeSelectFile, this);
27720 this.bodyEl.on('touchstart', this.onTouchStart, this);
27721 this.bodyEl.on('touchmove', this.onTouchMove, this);
27722 this.bodyEl.on('touchend', this.onTouchEnd, this);
27726 this.bodyEl.on('mousedown', this.onMouseDown, this);
27727 this.bodyEl.on('mousemove', this.onMouseMove, this);
27728 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27729 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27730 Roo.get(document).on('mouseup', this.onMouseUp, this);
27733 this.selectorEl.on('change', this.onFileSelected, this);
27739 this.baseScale = 1;
27741 this.baseRotate = 1;
27742 this.dragable = false;
27743 this.pinching = false;
27746 this.cropData = false;
27747 this.notifyEl.dom.innerHTML = this.emptyText;
27749 this.selectorEl.dom.value = '';
27753 resize : function()
27755 if(this.fireEvent('resize', this) != false){
27756 this.setThumbBoxPosition();
27757 this.setCanvasPosition();
27761 onFooterButtonClick : function(e, el, o, type)
27764 case 'rotate-left' :
27765 this.onRotateLeft(e);
27767 case 'rotate-right' :
27768 this.onRotateRight(e);
27771 this.beforeSelectFile(e);
27786 this.fireEvent('footerbuttonclick', this, type);
27789 beforeSelectFile : function(e)
27791 e.preventDefault();
27793 if(this.fireEvent('beforeselectfile', this) != false){
27794 this.selectorEl.dom.click();
27798 onFileSelected : function(e)
27800 e.preventDefault();
27802 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27806 var file = this.selectorEl.dom.files[0];
27808 if(this.fireEvent('inspect', this, file) != false){
27809 this.prepare(file);
27814 trash : function(e)
27816 this.fireEvent('trash', this);
27819 download : function(e)
27821 this.fireEvent('download', this);
27824 loadCanvas : function(src)
27826 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27830 this.imageEl = document.createElement('img');
27834 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27836 this.imageEl.src = src;
27840 onLoadCanvas : function()
27842 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27843 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27845 this.bodyEl.un('click', this.beforeSelectFile, this);
27847 this.notifyEl.hide();
27848 this.thumbEl.show();
27849 this.footerEl.show();
27851 this.baseRotateLevel();
27853 if(this.isDocument){
27854 this.setThumbBoxSize();
27857 this.setThumbBoxPosition();
27859 this.baseScaleLevel();
27865 this.canvasLoaded = true;
27868 this.maskEl.unmask();
27873 setCanvasPosition : function()
27875 if(!this.canvasEl){
27879 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27880 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27882 this.previewEl.setLeft(pw);
27883 this.previewEl.setTop(ph);
27887 onMouseDown : function(e)
27891 this.dragable = true;
27892 this.pinching = false;
27894 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27895 this.dragable = false;
27899 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27900 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27904 onMouseMove : function(e)
27908 if(!this.canvasLoaded){
27912 if (!this.dragable){
27916 var minX = Math.ceil(this.thumbEl.getLeft(true));
27917 var minY = Math.ceil(this.thumbEl.getTop(true));
27919 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27920 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27922 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27923 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27925 x = x - this.mouseX;
27926 y = y - this.mouseY;
27928 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27929 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27931 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27932 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27934 this.previewEl.setLeft(bgX);
27935 this.previewEl.setTop(bgY);
27937 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27938 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27941 onMouseUp : function(e)
27945 this.dragable = false;
27948 onMouseWheel : function(e)
27952 this.startScale = this.scale;
27954 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27956 if(!this.zoomable()){
27957 this.scale = this.startScale;
27966 zoomable : function()
27968 var minScale = this.thumbEl.getWidth() / this.minWidth;
27970 if(this.minWidth < this.minHeight){
27971 minScale = this.thumbEl.getHeight() / this.minHeight;
27974 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27975 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27979 (this.rotate == 0 || this.rotate == 180) &&
27981 width > this.imageEl.OriginWidth ||
27982 height > this.imageEl.OriginHeight ||
27983 (width < this.minWidth && height < this.minHeight)
27991 (this.rotate == 90 || this.rotate == 270) &&
27993 width > this.imageEl.OriginWidth ||
27994 height > this.imageEl.OriginHeight ||
27995 (width < this.minHeight && height < this.minWidth)
28002 !this.isDocument &&
28003 (this.rotate == 0 || this.rotate == 180) &&
28005 width < this.minWidth ||
28006 width > this.imageEl.OriginWidth ||
28007 height < this.minHeight ||
28008 height > this.imageEl.OriginHeight
28015 !this.isDocument &&
28016 (this.rotate == 90 || this.rotate == 270) &&
28018 width < this.minHeight ||
28019 width > this.imageEl.OriginWidth ||
28020 height < this.minWidth ||
28021 height > this.imageEl.OriginHeight
28031 onRotateLeft : function(e)
28033 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28035 var minScale = this.thumbEl.getWidth() / this.minWidth;
28037 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28038 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28040 this.startScale = this.scale;
28042 while (this.getScaleLevel() < minScale){
28044 this.scale = this.scale + 1;
28046 if(!this.zoomable()){
28051 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28052 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28057 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28064 this.scale = this.startScale;
28066 this.onRotateFail();
28071 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28073 if(this.isDocument){
28074 this.setThumbBoxSize();
28075 this.setThumbBoxPosition();
28076 this.setCanvasPosition();
28081 this.fireEvent('rotate', this, 'left');
28085 onRotateRight : function(e)
28087 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28089 var minScale = this.thumbEl.getWidth() / this.minWidth;
28091 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28092 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28094 this.startScale = this.scale;
28096 while (this.getScaleLevel() < minScale){
28098 this.scale = this.scale + 1;
28100 if(!this.zoomable()){
28105 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28106 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28111 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28118 this.scale = this.startScale;
28120 this.onRotateFail();
28125 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28127 if(this.isDocument){
28128 this.setThumbBoxSize();
28129 this.setThumbBoxPosition();
28130 this.setCanvasPosition();
28135 this.fireEvent('rotate', this, 'right');
28138 onRotateFail : function()
28140 this.errorEl.show(true);
28144 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28149 this.previewEl.dom.innerHTML = '';
28151 var canvasEl = document.createElement("canvas");
28153 var contextEl = canvasEl.getContext("2d");
28155 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28156 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28157 var center = this.imageEl.OriginWidth / 2;
28159 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28160 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28161 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28162 center = this.imageEl.OriginHeight / 2;
28165 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28167 contextEl.translate(center, center);
28168 contextEl.rotate(this.rotate * Math.PI / 180);
28170 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28172 this.canvasEl = document.createElement("canvas");
28174 this.contextEl = this.canvasEl.getContext("2d");
28176 switch (this.rotate) {
28179 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28180 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28182 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28187 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28188 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28190 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28191 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);
28195 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28200 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28201 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28203 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28204 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);
28208 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);
28213 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28214 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28216 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28217 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28221 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);
28228 this.previewEl.appendChild(this.canvasEl);
28230 this.setCanvasPosition();
28235 if(!this.canvasLoaded){
28239 var imageCanvas = document.createElement("canvas");
28241 var imageContext = imageCanvas.getContext("2d");
28243 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28244 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28246 var center = imageCanvas.width / 2;
28248 imageContext.translate(center, center);
28250 imageContext.rotate(this.rotate * Math.PI / 180);
28252 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28254 var canvas = document.createElement("canvas");
28256 var context = canvas.getContext("2d");
28258 canvas.width = this.minWidth;
28259 canvas.height = this.minHeight;
28261 switch (this.rotate) {
28264 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28265 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28267 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28268 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28270 var targetWidth = this.minWidth - 2 * x;
28271 var targetHeight = this.minHeight - 2 * y;
28275 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28276 scale = targetWidth / width;
28279 if(x > 0 && y == 0){
28280 scale = targetHeight / height;
28283 if(x > 0 && y > 0){
28284 scale = targetWidth / width;
28286 if(width < height){
28287 scale = targetHeight / height;
28291 context.scale(scale, scale);
28293 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28294 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28296 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28297 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28299 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28304 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28305 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28307 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28308 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28310 var targetWidth = this.minWidth - 2 * x;
28311 var targetHeight = this.minHeight - 2 * y;
28315 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28316 scale = targetWidth / width;
28319 if(x > 0 && y == 0){
28320 scale = targetHeight / height;
28323 if(x > 0 && y > 0){
28324 scale = targetWidth / width;
28326 if(width < height){
28327 scale = targetHeight / height;
28331 context.scale(scale, scale);
28333 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28334 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28336 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28337 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28339 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28341 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28346 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28347 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28349 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28350 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28352 var targetWidth = this.minWidth - 2 * x;
28353 var targetHeight = this.minHeight - 2 * y;
28357 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28358 scale = targetWidth / width;
28361 if(x > 0 && y == 0){
28362 scale = targetHeight / height;
28365 if(x > 0 && y > 0){
28366 scale = targetWidth / width;
28368 if(width < height){
28369 scale = targetHeight / height;
28373 context.scale(scale, scale);
28375 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28376 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28378 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28379 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28381 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28382 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28384 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28389 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28390 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28392 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28393 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28395 var targetWidth = this.minWidth - 2 * x;
28396 var targetHeight = this.minHeight - 2 * y;
28400 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28401 scale = targetWidth / width;
28404 if(x > 0 && y == 0){
28405 scale = targetHeight / height;
28408 if(x > 0 && y > 0){
28409 scale = targetWidth / width;
28411 if(width < height){
28412 scale = targetHeight / height;
28416 context.scale(scale, scale);
28418 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28419 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28421 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28422 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28424 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28426 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28433 this.cropData = canvas.toDataURL(this.cropType);
28435 if(this.fireEvent('crop', this, this.cropData) !== false){
28436 this.process(this.file, this.cropData);
28443 setThumbBoxSize : function()
28447 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28448 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28449 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28451 this.minWidth = width;
28452 this.minHeight = height;
28454 if(this.rotate == 90 || this.rotate == 270){
28455 this.minWidth = height;
28456 this.minHeight = width;
28461 width = Math.ceil(this.minWidth * height / this.minHeight);
28463 if(this.minWidth > this.minHeight){
28465 height = Math.ceil(this.minHeight * width / this.minWidth);
28468 this.thumbEl.setStyle({
28469 width : width + 'px',
28470 height : height + 'px'
28477 setThumbBoxPosition : function()
28479 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28480 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28482 this.thumbEl.setLeft(x);
28483 this.thumbEl.setTop(y);
28487 baseRotateLevel : function()
28489 this.baseRotate = 1;
28492 typeof(this.exif) != 'undefined' &&
28493 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28494 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28496 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28499 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28503 baseScaleLevel : function()
28507 if(this.isDocument){
28509 if(this.baseRotate == 6 || this.baseRotate == 8){
28511 height = this.thumbEl.getHeight();
28512 this.baseScale = height / this.imageEl.OriginWidth;
28514 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28515 width = this.thumbEl.getWidth();
28516 this.baseScale = width / this.imageEl.OriginHeight;
28522 height = this.thumbEl.getHeight();
28523 this.baseScale = height / this.imageEl.OriginHeight;
28525 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28526 width = this.thumbEl.getWidth();
28527 this.baseScale = width / this.imageEl.OriginWidth;
28533 if(this.baseRotate == 6 || this.baseRotate == 8){
28535 width = this.thumbEl.getHeight();
28536 this.baseScale = width / this.imageEl.OriginHeight;
28538 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28539 height = this.thumbEl.getWidth();
28540 this.baseScale = height / this.imageEl.OriginHeight;
28543 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28544 height = this.thumbEl.getWidth();
28545 this.baseScale = height / this.imageEl.OriginHeight;
28547 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28548 width = this.thumbEl.getHeight();
28549 this.baseScale = width / this.imageEl.OriginWidth;
28556 width = this.thumbEl.getWidth();
28557 this.baseScale = width / this.imageEl.OriginWidth;
28559 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28560 height = this.thumbEl.getHeight();
28561 this.baseScale = height / this.imageEl.OriginHeight;
28564 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28566 height = this.thumbEl.getHeight();
28567 this.baseScale = height / this.imageEl.OriginHeight;
28569 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28570 width = this.thumbEl.getWidth();
28571 this.baseScale = width / this.imageEl.OriginWidth;
28579 getScaleLevel : function()
28581 return this.baseScale * Math.pow(1.1, this.scale);
28584 onTouchStart : function(e)
28586 if(!this.canvasLoaded){
28587 this.beforeSelectFile(e);
28591 var touches = e.browserEvent.touches;
28597 if(touches.length == 1){
28598 this.onMouseDown(e);
28602 if(touches.length != 2){
28608 for(var i = 0, finger; finger = touches[i]; i++){
28609 coords.push(finger.pageX, finger.pageY);
28612 var x = Math.pow(coords[0] - coords[2], 2);
28613 var y = Math.pow(coords[1] - coords[3], 2);
28615 this.startDistance = Math.sqrt(x + y);
28617 this.startScale = this.scale;
28619 this.pinching = true;
28620 this.dragable = false;
28624 onTouchMove : function(e)
28626 if(!this.pinching && !this.dragable){
28630 var touches = e.browserEvent.touches;
28637 this.onMouseMove(e);
28643 for(var i = 0, finger; finger = touches[i]; i++){
28644 coords.push(finger.pageX, finger.pageY);
28647 var x = Math.pow(coords[0] - coords[2], 2);
28648 var y = Math.pow(coords[1] - coords[3], 2);
28650 this.endDistance = Math.sqrt(x + y);
28652 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28654 if(!this.zoomable()){
28655 this.scale = this.startScale;
28663 onTouchEnd : function(e)
28665 this.pinching = false;
28666 this.dragable = false;
28670 process : function(file, crop)
28673 this.maskEl.mask(this.loadingText);
28676 this.xhr = new XMLHttpRequest();
28678 file.xhr = this.xhr;
28680 this.xhr.open(this.method, this.url, true);
28683 "Accept": "application/json",
28684 "Cache-Control": "no-cache",
28685 "X-Requested-With": "XMLHttpRequest"
28688 for (var headerName in headers) {
28689 var headerValue = headers[headerName];
28691 this.xhr.setRequestHeader(headerName, headerValue);
28697 this.xhr.onload = function()
28699 _this.xhrOnLoad(_this.xhr);
28702 this.xhr.onerror = function()
28704 _this.xhrOnError(_this.xhr);
28707 var formData = new FormData();
28709 formData.append('returnHTML', 'NO');
28712 formData.append('crop', crop);
28715 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28716 formData.append(this.paramName, file, file.name);
28719 if(typeof(file.filename) != 'undefined'){
28720 formData.append('filename', file.filename);
28723 if(typeof(file.mimetype) != 'undefined'){
28724 formData.append('mimetype', file.mimetype);
28727 if(this.fireEvent('arrange', this, formData) != false){
28728 this.xhr.send(formData);
28732 xhrOnLoad : function(xhr)
28735 this.maskEl.unmask();
28738 if (xhr.readyState !== 4) {
28739 this.fireEvent('exception', this, xhr);
28743 var response = Roo.decode(xhr.responseText);
28745 if(!response.success){
28746 this.fireEvent('exception', this, xhr);
28750 var response = Roo.decode(xhr.responseText);
28752 this.fireEvent('upload', this, response);
28756 xhrOnError : function()
28759 this.maskEl.unmask();
28762 Roo.log('xhr on error');
28764 var response = Roo.decode(xhr.responseText);
28770 prepare : function(file)
28773 this.maskEl.mask(this.loadingText);
28779 if(typeof(file) === 'string'){
28780 this.loadCanvas(file);
28784 if(!file || !this.urlAPI){
28789 this.cropType = file.type;
28793 if(this.fireEvent('prepare', this, this.file) != false){
28795 var reader = new FileReader();
28797 reader.onload = function (e) {
28798 if (e.target.error) {
28799 Roo.log(e.target.error);
28803 var buffer = e.target.result,
28804 dataView = new DataView(buffer),
28806 maxOffset = dataView.byteLength - 4,
28810 if (dataView.getUint16(0) === 0xffd8) {
28811 while (offset < maxOffset) {
28812 markerBytes = dataView.getUint16(offset);
28814 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28815 markerLength = dataView.getUint16(offset + 2) + 2;
28816 if (offset + markerLength > dataView.byteLength) {
28817 Roo.log('Invalid meta data: Invalid segment size.');
28821 if(markerBytes == 0xffe1){
28822 _this.parseExifData(
28829 offset += markerLength;
28839 var url = _this.urlAPI.createObjectURL(_this.file);
28841 _this.loadCanvas(url);
28846 reader.readAsArrayBuffer(this.file);
28852 parseExifData : function(dataView, offset, length)
28854 var tiffOffset = offset + 10,
28858 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28859 // No Exif data, might be XMP data instead
28863 // Check for the ASCII code for "Exif" (0x45786966):
28864 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28865 // No Exif data, might be XMP data instead
28868 if (tiffOffset + 8 > dataView.byteLength) {
28869 Roo.log('Invalid Exif data: Invalid segment size.');
28872 // Check for the two null bytes:
28873 if (dataView.getUint16(offset + 8) !== 0x0000) {
28874 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28877 // Check the byte alignment:
28878 switch (dataView.getUint16(tiffOffset)) {
28880 littleEndian = true;
28883 littleEndian = false;
28886 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28889 // Check for the TIFF tag marker (0x002A):
28890 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28891 Roo.log('Invalid Exif data: Missing TIFF marker.');
28894 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28895 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28897 this.parseExifTags(
28900 tiffOffset + dirOffset,
28905 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28910 if (dirOffset + 6 > dataView.byteLength) {
28911 Roo.log('Invalid Exif data: Invalid directory offset.');
28914 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28915 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28916 if (dirEndOffset + 4 > dataView.byteLength) {
28917 Roo.log('Invalid Exif data: Invalid directory size.');
28920 for (i = 0; i < tagsNumber; i += 1) {
28924 dirOffset + 2 + 12 * i, // tag offset
28928 // Return the offset to the next directory:
28929 return dataView.getUint32(dirEndOffset, littleEndian);
28932 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28934 var tag = dataView.getUint16(offset, littleEndian);
28936 this.exif[tag] = this.getExifValue(
28940 dataView.getUint16(offset + 2, littleEndian), // tag type
28941 dataView.getUint32(offset + 4, littleEndian), // tag length
28946 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28948 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28957 Roo.log('Invalid Exif data: Invalid tag type.');
28961 tagSize = tagType.size * length;
28962 // Determine if the value is contained in the dataOffset bytes,
28963 // or if the value at the dataOffset is a pointer to the actual data:
28964 dataOffset = tagSize > 4 ?
28965 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28966 if (dataOffset + tagSize > dataView.byteLength) {
28967 Roo.log('Invalid Exif data: Invalid data offset.');
28970 if (length === 1) {
28971 return tagType.getValue(dataView, dataOffset, littleEndian);
28974 for (i = 0; i < length; i += 1) {
28975 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28978 if (tagType.ascii) {
28980 // Concatenate the chars:
28981 for (i = 0; i < values.length; i += 1) {
28983 // Ignore the terminating NULL byte(s):
28984 if (c === '\u0000') {
28996 Roo.apply(Roo.bootstrap.UploadCropbox, {
28998 'Orientation': 0x0112
29002 1: 0, //'top-left',
29004 3: 180, //'bottom-right',
29005 // 4: 'bottom-left',
29007 6: 90, //'right-top',
29008 // 7: 'right-bottom',
29009 8: 270 //'left-bottom'
29013 // byte, 8-bit unsigned int:
29015 getValue: function (dataView, dataOffset) {
29016 return dataView.getUint8(dataOffset);
29020 // ascii, 8-bit byte:
29022 getValue: function (dataView, dataOffset) {
29023 return String.fromCharCode(dataView.getUint8(dataOffset));
29028 // short, 16 bit int:
29030 getValue: function (dataView, dataOffset, littleEndian) {
29031 return dataView.getUint16(dataOffset, littleEndian);
29035 // long, 32 bit int:
29037 getValue: function (dataView, dataOffset, littleEndian) {
29038 return dataView.getUint32(dataOffset, littleEndian);
29042 // rational = two long values, first is numerator, second is denominator:
29044 getValue: function (dataView, dataOffset, littleEndian) {
29045 return dataView.getUint32(dataOffset, littleEndian) /
29046 dataView.getUint32(dataOffset + 4, littleEndian);
29050 // slong, 32 bit signed int:
29052 getValue: function (dataView, dataOffset, littleEndian) {
29053 return dataView.getInt32(dataOffset, littleEndian);
29057 // srational, two slongs, first is numerator, second is denominator:
29059 getValue: function (dataView, dataOffset, littleEndian) {
29060 return dataView.getInt32(dataOffset, littleEndian) /
29061 dataView.getInt32(dataOffset + 4, littleEndian);
29071 cls : 'btn-group roo-upload-cropbox-rotate-left',
29072 action : 'rotate-left',
29076 cls : 'btn btn-default',
29077 html : '<i class="fa fa-undo"></i>'
29083 cls : 'btn-group roo-upload-cropbox-picture',
29084 action : 'picture',
29088 cls : 'btn btn-default',
29089 html : '<i class="fa fa-picture-o"></i>'
29095 cls : 'btn-group roo-upload-cropbox-rotate-right',
29096 action : 'rotate-right',
29100 cls : 'btn btn-default',
29101 html : '<i class="fa fa-repeat"></i>'
29109 cls : 'btn-group roo-upload-cropbox-rotate-left',
29110 action : 'rotate-left',
29114 cls : 'btn btn-default',
29115 html : '<i class="fa fa-undo"></i>'
29121 cls : 'btn-group roo-upload-cropbox-download',
29122 action : 'download',
29126 cls : 'btn btn-default',
29127 html : '<i class="fa fa-download"></i>'
29133 cls : 'btn-group roo-upload-cropbox-crop',
29138 cls : 'btn btn-default',
29139 html : '<i class="fa fa-crop"></i>'
29145 cls : 'btn-group roo-upload-cropbox-trash',
29150 cls : 'btn btn-default',
29151 html : '<i class="fa fa-trash"></i>'
29157 cls : 'btn-group roo-upload-cropbox-rotate-right',
29158 action : 'rotate-right',
29162 cls : 'btn btn-default',
29163 html : '<i class="fa fa-repeat"></i>'
29171 cls : 'btn-group roo-upload-cropbox-rotate-left',
29172 action : 'rotate-left',
29176 cls : 'btn btn-default',
29177 html : '<i class="fa fa-undo"></i>'
29183 cls : 'btn-group roo-upload-cropbox-rotate-right',
29184 action : 'rotate-right',
29188 cls : 'btn btn-default',
29189 html : '<i class="fa fa-repeat"></i>'
29202 * @class Roo.bootstrap.DocumentManager
29203 * @extends Roo.bootstrap.Component
29204 * Bootstrap DocumentManager class
29205 * @cfg {String} paramName default 'imageUpload'
29206 * @cfg {String} toolTipName default 'filename'
29207 * @cfg {String} method default POST
29208 * @cfg {String} url action url
29209 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29210 * @cfg {Boolean} multiple multiple upload default true
29211 * @cfg {Number} thumbSize default 300
29212 * @cfg {String} fieldLabel
29213 * @cfg {Number} labelWidth default 4
29214 * @cfg {String} labelAlign (left|top) default left
29215 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29216 * @cfg {Number} labellg set the width of label (1-12)
29217 * @cfg {Number} labelmd set the width of label (1-12)
29218 * @cfg {Number} labelsm set the width of label (1-12)
29219 * @cfg {Number} labelxs set the width of label (1-12)
29222 * Create a new DocumentManager
29223 * @param {Object} config The config object
29226 Roo.bootstrap.DocumentManager = function(config){
29227 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29230 this.delegates = [];
29235 * Fire when initial the DocumentManager
29236 * @param {Roo.bootstrap.DocumentManager} this
29241 * inspect selected file
29242 * @param {Roo.bootstrap.DocumentManager} this
29243 * @param {File} file
29248 * Fire when xhr load exception
29249 * @param {Roo.bootstrap.DocumentManager} this
29250 * @param {XMLHttpRequest} xhr
29252 "exception" : true,
29254 * @event afterupload
29255 * Fire when xhr load exception
29256 * @param {Roo.bootstrap.DocumentManager} this
29257 * @param {XMLHttpRequest} xhr
29259 "afterupload" : true,
29262 * prepare the form data
29263 * @param {Roo.bootstrap.DocumentManager} this
29264 * @param {Object} formData
29269 * Fire when remove the file
29270 * @param {Roo.bootstrap.DocumentManager} this
29271 * @param {Object} file
29276 * Fire after refresh the file
29277 * @param {Roo.bootstrap.DocumentManager} this
29282 * Fire after click the image
29283 * @param {Roo.bootstrap.DocumentManager} this
29284 * @param {Object} file
29289 * Fire when upload a image and editable set to true
29290 * @param {Roo.bootstrap.DocumentManager} this
29291 * @param {Object} file
29295 * @event beforeselectfile
29296 * Fire before select file
29297 * @param {Roo.bootstrap.DocumentManager} this
29299 "beforeselectfile" : true,
29302 * Fire before process file
29303 * @param {Roo.bootstrap.DocumentManager} this
29304 * @param {Object} file
29308 * @event previewrendered
29309 * Fire when preview rendered
29310 * @param {Roo.bootstrap.DocumentManager} this
29311 * @param {Object} file
29313 "previewrendered" : true,
29316 "previewResize" : true
29321 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
29330 paramName : 'imageUpload',
29331 toolTipName : 'filename',
29334 labelAlign : 'left',
29344 getAutoCreate : function()
29346 var managerWidget = {
29348 cls : 'roo-document-manager',
29352 cls : 'roo-document-manager-selector',
29357 cls : 'roo-document-manager-uploader',
29361 cls : 'roo-document-manager-upload-btn',
29362 html : '<i class="fa fa-plus"></i>'
29373 cls : 'column col-md-12',
29378 if(this.fieldLabel.length){
29383 cls : 'column col-md-12',
29384 html : this.fieldLabel
29388 cls : 'column col-md-12',
29393 if(this.labelAlign == 'left'){
29398 html : this.fieldLabel
29407 if(this.labelWidth > 12){
29408 content[0].style = "width: " + this.labelWidth + 'px';
29411 if(this.labelWidth < 13 && this.labelmd == 0){
29412 this.labelmd = this.labelWidth;
29415 if(this.labellg > 0){
29416 content[0].cls += ' col-lg-' + this.labellg;
29417 content[1].cls += ' col-lg-' + (12 - this.labellg);
29420 if(this.labelmd > 0){
29421 content[0].cls += ' col-md-' + this.labelmd;
29422 content[1].cls += ' col-md-' + (12 - this.labelmd);
29425 if(this.labelsm > 0){
29426 content[0].cls += ' col-sm-' + this.labelsm;
29427 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29430 if(this.labelxs > 0){
29431 content[0].cls += ' col-xs-' + this.labelxs;
29432 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29440 cls : 'row clearfix',
29448 initEvents : function()
29450 this.managerEl = this.el.select('.roo-document-manager', true).first();
29451 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29453 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29454 this.selectorEl.hide();
29457 this.selectorEl.attr('multiple', 'multiple');
29460 this.selectorEl.on('change', this.onFileSelected, this);
29462 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29463 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29465 this.uploader.on('click', this.onUploaderClick, this);
29467 this.renderProgressDialog();
29471 window.addEventListener("resize", function() { _this.refresh(); } );
29473 this.fireEvent('initial', this);
29476 renderProgressDialog : function()
29480 this.progressDialog = new Roo.bootstrap.Modal({
29481 cls : 'roo-document-manager-progress-dialog',
29482 allow_close : false,
29493 btnclick : function() {
29494 _this.uploadCancel();
29500 this.progressDialog.render(Roo.get(document.body));
29502 this.progress = new Roo.bootstrap.Progress({
29503 cls : 'roo-document-manager-progress',
29508 this.progress.render(this.progressDialog.getChildContainer());
29510 this.progressBar = new Roo.bootstrap.ProgressBar({
29511 cls : 'roo-document-manager-progress-bar',
29514 aria_valuemax : 12,
29518 this.progressBar.render(this.progress.getChildContainer());
29521 onUploaderClick : function(e)
29523 e.preventDefault();
29525 if(this.fireEvent('beforeselectfile', this) != false){
29526 this.selectorEl.dom.click();
29531 onFileSelected : function(e)
29533 e.preventDefault();
29535 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29539 Roo.each(this.selectorEl.dom.files, function(file){
29540 if(this.fireEvent('inspect', this, file) != false){
29541 this.files.push(file);
29551 this.selectorEl.dom.value = '';
29553 if(!this.files || !this.files.length){
29557 if(this.boxes > 0 && this.files.length > this.boxes){
29558 this.files = this.files.slice(0, this.boxes);
29561 this.uploader.show();
29563 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29564 this.uploader.hide();
29573 Roo.each(this.files, function(file){
29575 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29576 var f = this.renderPreview(file);
29581 if(file.type.indexOf('image') != -1){
29582 this.delegates.push(
29584 _this.process(file);
29585 }).createDelegate(this)
29593 _this.process(file);
29594 }).createDelegate(this)
29599 this.files = files;
29601 this.delegates = this.delegates.concat(docs);
29603 if(!this.delegates.length){
29608 this.progressBar.aria_valuemax = this.delegates.length;
29615 arrange : function()
29617 if(!this.delegates.length){
29618 this.progressDialog.hide();
29623 var delegate = this.delegates.shift();
29625 this.progressDialog.show();
29627 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29629 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29634 refresh : function()
29636 this.uploader.show();
29638 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29639 this.uploader.hide();
29642 Roo.isTouch ? this.closable(false) : this.closable(true);
29644 this.fireEvent('refresh', this);
29647 onRemove : function(e, el, o)
29649 e.preventDefault();
29651 this.fireEvent('remove', this, o);
29655 remove : function(o)
29659 Roo.each(this.files, function(file){
29660 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29669 this.files = files;
29676 Roo.each(this.files, function(file){
29681 file.target.remove();
29690 onClick : function(e, el, o)
29692 e.preventDefault();
29694 this.fireEvent('click', this, o);
29698 closable : function(closable)
29700 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29702 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29714 xhrOnLoad : function(xhr)
29716 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29720 if (xhr.readyState !== 4) {
29722 this.fireEvent('exception', this, xhr);
29726 var response = Roo.decode(xhr.responseText);
29728 if(!response.success){
29730 this.fireEvent('exception', this, xhr);
29734 var file = this.renderPreview(response.data);
29736 this.files.push(file);
29740 this.fireEvent('afterupload', this, xhr);
29744 xhrOnError : function(xhr)
29746 Roo.log('xhr on error');
29748 var response = Roo.decode(xhr.responseText);
29755 process : function(file)
29757 if(this.fireEvent('process', this, file) !== false){
29758 if(this.editable && file.type.indexOf('image') != -1){
29759 this.fireEvent('edit', this, file);
29763 this.uploadStart(file, false);
29770 uploadStart : function(file, crop)
29772 this.xhr = new XMLHttpRequest();
29774 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29779 file.xhr = this.xhr;
29781 this.managerEl.createChild({
29783 cls : 'roo-document-manager-loading',
29787 tooltip : file.name,
29788 cls : 'roo-document-manager-thumb',
29789 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29795 this.xhr.open(this.method, this.url, true);
29798 "Accept": "application/json",
29799 "Cache-Control": "no-cache",
29800 "X-Requested-With": "XMLHttpRequest"
29803 for (var headerName in headers) {
29804 var headerValue = headers[headerName];
29806 this.xhr.setRequestHeader(headerName, headerValue);
29812 this.xhr.onload = function()
29814 _this.xhrOnLoad(_this.xhr);
29817 this.xhr.onerror = function()
29819 _this.xhrOnError(_this.xhr);
29822 var formData = new FormData();
29824 formData.append('returnHTML', 'NO');
29827 formData.append('crop', crop);
29830 formData.append(this.paramName, file, file.name);
29837 if(this.fireEvent('prepare', this, formData, options) != false){
29839 if(options.manually){
29843 this.xhr.send(formData);
29847 this.uploadCancel();
29850 uploadCancel : function()
29856 this.delegates = [];
29858 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29865 renderPreview : function(file)
29867 if(typeof(file.target) != 'undefined' && file.target){
29871 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29873 var previewEl = this.managerEl.createChild({
29875 cls : 'roo-document-manager-preview',
29879 tooltip : file[this.toolTipName],
29880 cls : 'roo-document-manager-thumb',
29881 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29886 html : '<i class="fa fa-times-circle"></i>'
29891 var close = previewEl.select('button.close', true).first();
29893 close.on('click', this.onRemove, this, file);
29895 file.target = previewEl;
29897 var image = previewEl.select('img', true).first();
29901 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29903 image.on('click', this.onClick, this, file);
29905 this.fireEvent('previewrendered', this, file);
29911 onPreviewLoad : function(file, image)
29913 if(typeof(file.target) == 'undefined' || !file.target){
29917 var width = image.dom.naturalWidth || image.dom.width;
29918 var height = image.dom.naturalHeight || image.dom.height;
29920 if(!this.previewResize) {
29924 if(width > height){
29925 file.target.addClass('wide');
29929 file.target.addClass('tall');
29934 uploadFromSource : function(file, crop)
29936 this.xhr = new XMLHttpRequest();
29938 this.managerEl.createChild({
29940 cls : 'roo-document-manager-loading',
29944 tooltip : file.name,
29945 cls : 'roo-document-manager-thumb',
29946 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29952 this.xhr.open(this.method, this.url, true);
29955 "Accept": "application/json",
29956 "Cache-Control": "no-cache",
29957 "X-Requested-With": "XMLHttpRequest"
29960 for (var headerName in headers) {
29961 var headerValue = headers[headerName];
29963 this.xhr.setRequestHeader(headerName, headerValue);
29969 this.xhr.onload = function()
29971 _this.xhrOnLoad(_this.xhr);
29974 this.xhr.onerror = function()
29976 _this.xhrOnError(_this.xhr);
29979 var formData = new FormData();
29981 formData.append('returnHTML', 'NO');
29983 formData.append('crop', crop);
29985 if(typeof(file.filename) != 'undefined'){
29986 formData.append('filename', file.filename);
29989 if(typeof(file.mimetype) != 'undefined'){
29990 formData.append('mimetype', file.mimetype);
29995 if(this.fireEvent('prepare', this, formData) != false){
29996 this.xhr.send(formData);
30006 * @class Roo.bootstrap.DocumentViewer
30007 * @extends Roo.bootstrap.Component
30008 * Bootstrap DocumentViewer class
30009 * @cfg {Boolean} showDownload (true|false) show download button (default true)
30010 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30013 * Create a new DocumentViewer
30014 * @param {Object} config The config object
30017 Roo.bootstrap.DocumentViewer = function(config){
30018 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30023 * Fire after initEvent
30024 * @param {Roo.bootstrap.DocumentViewer} this
30030 * @param {Roo.bootstrap.DocumentViewer} this
30035 * Fire after download button
30036 * @param {Roo.bootstrap.DocumentViewer} this
30041 * Fire after trash button
30042 * @param {Roo.bootstrap.DocumentViewer} this
30049 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
30051 showDownload : true,
30055 getAutoCreate : function()
30059 cls : 'roo-document-viewer',
30063 cls : 'roo-document-viewer-body',
30067 cls : 'roo-document-viewer-thumb',
30071 cls : 'roo-document-viewer-image'
30079 cls : 'roo-document-viewer-footer',
30082 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
30086 cls : 'btn-group roo-document-viewer-download',
30090 cls : 'btn btn-default',
30091 html : '<i class="fa fa-download"></i>'
30097 cls : 'btn-group roo-document-viewer-trash',
30101 cls : 'btn btn-default',
30102 html : '<i class="fa fa-trash"></i>'
30115 initEvents : function()
30117 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30118 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30120 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30121 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30123 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30124 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30126 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30127 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30129 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30130 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30132 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30133 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30135 this.bodyEl.on('click', this.onClick, this);
30136 this.downloadBtn.on('click', this.onDownload, this);
30137 this.trashBtn.on('click', this.onTrash, this);
30139 this.downloadBtn.hide();
30140 this.trashBtn.hide();
30142 if(this.showDownload){
30143 this.downloadBtn.show();
30146 if(this.showTrash){
30147 this.trashBtn.show();
30150 if(!this.showDownload && !this.showTrash) {
30151 this.footerEl.hide();
30156 initial : function()
30158 this.fireEvent('initial', this);
30162 onClick : function(e)
30164 e.preventDefault();
30166 this.fireEvent('click', this);
30169 onDownload : function(e)
30171 e.preventDefault();
30173 this.fireEvent('download', this);
30176 onTrash : function(e)
30178 e.preventDefault();
30180 this.fireEvent('trash', this);
30192 * @class Roo.bootstrap.NavProgressBar
30193 * @extends Roo.bootstrap.Component
30194 * Bootstrap NavProgressBar class
30197 * Create a new nav progress bar
30198 * @param {Object} config The config object
30201 Roo.bootstrap.NavProgressBar = function(config){
30202 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30204 this.bullets = this.bullets || [];
30206 // Roo.bootstrap.NavProgressBar.register(this);
30210 * Fires when the active item changes
30211 * @param {Roo.bootstrap.NavProgressBar} this
30212 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30213 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
30220 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
30225 getAutoCreate : function()
30227 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30231 cls : 'roo-navigation-bar-group',
30235 cls : 'roo-navigation-top-bar'
30239 cls : 'roo-navigation-bullets-bar',
30243 cls : 'roo-navigation-bar'
30250 cls : 'roo-navigation-bottom-bar'
30260 initEvents: function()
30265 onRender : function(ct, position)
30267 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30269 if(this.bullets.length){
30270 Roo.each(this.bullets, function(b){
30279 addItem : function(cfg)
30281 var item = new Roo.bootstrap.NavProgressItem(cfg);
30283 item.parentId = this.id;
30284 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30287 var top = new Roo.bootstrap.Element({
30289 cls : 'roo-navigation-bar-text'
30292 var bottom = new Roo.bootstrap.Element({
30294 cls : 'roo-navigation-bar-text'
30297 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30298 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30300 var topText = new Roo.bootstrap.Element({
30302 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30305 var bottomText = new Roo.bootstrap.Element({
30307 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30310 topText.onRender(top.el, null);
30311 bottomText.onRender(bottom.el, null);
30314 item.bottomEl = bottom;
30317 this.barItems.push(item);
30322 getActive : function()
30324 var active = false;
30326 Roo.each(this.barItems, function(v){
30328 if (!v.isActive()) {
30340 setActiveItem : function(item)
30344 Roo.each(this.barItems, function(v){
30345 if (v.rid == item.rid) {
30349 if (v.isActive()) {
30350 v.setActive(false);
30355 item.setActive(true);
30357 this.fireEvent('changed', this, item, prev);
30360 getBarItem: function(rid)
30364 Roo.each(this.barItems, function(e) {
30365 if (e.rid != rid) {
30376 indexOfItem : function(item)
30380 Roo.each(this.barItems, function(v, i){
30382 if (v.rid != item.rid) {
30393 setActiveNext : function()
30395 var i = this.indexOfItem(this.getActive());
30397 if (i > this.barItems.length) {
30401 this.setActiveItem(this.barItems[i+1]);
30404 setActivePrev : function()
30406 var i = this.indexOfItem(this.getActive());
30412 this.setActiveItem(this.barItems[i-1]);
30415 format : function()
30417 if(!this.barItems.length){
30421 var width = 100 / this.barItems.length;
30423 Roo.each(this.barItems, function(i){
30424 i.el.setStyle('width', width + '%');
30425 i.topEl.el.setStyle('width', width + '%');
30426 i.bottomEl.el.setStyle('width', width + '%');
30435 * Nav Progress Item
30440 * @class Roo.bootstrap.NavProgressItem
30441 * @extends Roo.bootstrap.Component
30442 * Bootstrap NavProgressItem class
30443 * @cfg {String} rid the reference id
30444 * @cfg {Boolean} active (true|false) Is item active default false
30445 * @cfg {Boolean} disabled (true|false) Is item active default false
30446 * @cfg {String} html
30447 * @cfg {String} position (top|bottom) text position default bottom
30448 * @cfg {String} icon show icon instead of number
30451 * Create a new NavProgressItem
30452 * @param {Object} config The config object
30454 Roo.bootstrap.NavProgressItem = function(config){
30455 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30460 * The raw click event for the entire grid.
30461 * @param {Roo.bootstrap.NavProgressItem} this
30462 * @param {Roo.EventObject} e
30469 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30475 position : 'bottom',
30478 getAutoCreate : function()
30480 var iconCls = 'roo-navigation-bar-item-icon';
30482 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30486 cls: 'roo-navigation-bar-item',
30496 cfg.cls += ' active';
30499 cfg.cls += ' disabled';
30505 disable : function()
30507 this.setDisabled(true);
30510 enable : function()
30512 this.setDisabled(false);
30515 initEvents: function()
30517 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30519 this.iconEl.on('click', this.onClick, this);
30522 onClick : function(e)
30524 e.preventDefault();
30530 if(this.fireEvent('click', this, e) === false){
30534 this.parent().setActiveItem(this);
30537 isActive: function ()
30539 return this.active;
30542 setActive : function(state)
30544 if(this.active == state){
30548 this.active = state;
30551 this.el.addClass('active');
30555 this.el.removeClass('active');
30560 setDisabled : function(state)
30562 if(this.disabled == state){
30566 this.disabled = state;
30569 this.el.addClass('disabled');
30573 this.el.removeClass('disabled');
30576 tooltipEl : function()
30578 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30591 * @class Roo.bootstrap.FieldLabel
30592 * @extends Roo.bootstrap.Component
30593 * Bootstrap FieldLabel class
30594 * @cfg {String} html contents of the element
30595 * @cfg {String} tag tag of the element default label
30596 * @cfg {String} cls class of the element
30597 * @cfg {String} target label target
30598 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30599 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30600 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30601 * @cfg {String} iconTooltip default "This field is required"
30602 * @cfg {String} indicatorpos (left|right) default left
30605 * Create a new FieldLabel
30606 * @param {Object} config The config object
30609 Roo.bootstrap.FieldLabel = function(config){
30610 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30615 * Fires after the field has been marked as invalid.
30616 * @param {Roo.form.FieldLabel} this
30617 * @param {String} msg The validation message
30622 * Fires after the field has been validated with no errors.
30623 * @param {Roo.form.FieldLabel} this
30629 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30636 invalidClass : 'has-warning',
30637 validClass : 'has-success',
30638 iconTooltip : 'This field is required',
30639 indicatorpos : 'left',
30641 getAutoCreate : function(){
30644 if (!this.allowBlank) {
30650 cls : 'roo-bootstrap-field-label ' + this.cls,
30655 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30656 tooltip : this.iconTooltip
30665 if(this.indicatorpos == 'right'){
30668 cls : 'roo-bootstrap-field-label ' + this.cls,
30677 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30678 tooltip : this.iconTooltip
30687 initEvents: function()
30689 Roo.bootstrap.Element.superclass.initEvents.call(this);
30691 this.indicator = this.indicatorEl();
30693 if(this.indicator){
30694 this.indicator.removeClass('visible');
30695 this.indicator.addClass('invisible');
30698 Roo.bootstrap.FieldLabel.register(this);
30701 indicatorEl : function()
30703 var indicator = this.el.select('i.roo-required-indicator',true).first();
30714 * Mark this field as valid
30716 markValid : function()
30718 if(this.indicator){
30719 this.indicator.removeClass('visible');
30720 this.indicator.addClass('invisible');
30722 if (Roo.bootstrap.version == 3) {
30723 this.el.removeClass(this.invalidClass);
30724 this.el.addClass(this.validClass);
30726 this.el.removeClass('is-invalid');
30727 this.el.addClass('is-valid');
30731 this.fireEvent('valid', this);
30735 * Mark this field as invalid
30736 * @param {String} msg The validation message
30738 markInvalid : function(msg)
30740 if(this.indicator){
30741 this.indicator.removeClass('invisible');
30742 this.indicator.addClass('visible');
30744 if (Roo.bootstrap.version == 3) {
30745 this.el.removeClass(this.validClass);
30746 this.el.addClass(this.invalidClass);
30748 this.el.removeClass('is-valid');
30749 this.el.addClass('is-invalid');
30753 this.fireEvent('invalid', this, msg);
30759 Roo.apply(Roo.bootstrap.FieldLabel, {
30764 * register a FieldLabel Group
30765 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30767 register : function(label)
30769 if(this.groups.hasOwnProperty(label.target)){
30773 this.groups[label.target] = label;
30777 * fetch a FieldLabel Group based on the target
30778 * @param {string} target
30779 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30781 get: function(target) {
30782 if (typeof(this.groups[target]) == 'undefined') {
30786 return this.groups[target] ;
30795 * page DateSplitField.
30801 * @class Roo.bootstrap.DateSplitField
30802 * @extends Roo.bootstrap.Component
30803 * Bootstrap DateSplitField class
30804 * @cfg {string} fieldLabel - the label associated
30805 * @cfg {Number} labelWidth set the width of label (0-12)
30806 * @cfg {String} labelAlign (top|left)
30807 * @cfg {Boolean} dayAllowBlank (true|false) default false
30808 * @cfg {Boolean} monthAllowBlank (true|false) default false
30809 * @cfg {Boolean} yearAllowBlank (true|false) default false
30810 * @cfg {string} dayPlaceholder
30811 * @cfg {string} monthPlaceholder
30812 * @cfg {string} yearPlaceholder
30813 * @cfg {string} dayFormat default 'd'
30814 * @cfg {string} monthFormat default 'm'
30815 * @cfg {string} yearFormat default 'Y'
30816 * @cfg {Number} labellg set the width of label (1-12)
30817 * @cfg {Number} labelmd set the width of label (1-12)
30818 * @cfg {Number} labelsm set the width of label (1-12)
30819 * @cfg {Number} labelxs set the width of label (1-12)
30823 * Create a new DateSplitField
30824 * @param {Object} config The config object
30827 Roo.bootstrap.DateSplitField = function(config){
30828 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30834 * getting the data of years
30835 * @param {Roo.bootstrap.DateSplitField} this
30836 * @param {Object} years
30841 * getting the data of days
30842 * @param {Roo.bootstrap.DateSplitField} this
30843 * @param {Object} days
30848 * Fires after the field has been marked as invalid.
30849 * @param {Roo.form.Field} this
30850 * @param {String} msg The validation message
30855 * Fires after the field has been validated with no errors.
30856 * @param {Roo.form.Field} this
30862 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30865 labelAlign : 'top',
30867 dayAllowBlank : false,
30868 monthAllowBlank : false,
30869 yearAllowBlank : false,
30870 dayPlaceholder : '',
30871 monthPlaceholder : '',
30872 yearPlaceholder : '',
30876 isFormField : true,
30882 getAutoCreate : function()
30886 cls : 'row roo-date-split-field-group',
30891 cls : 'form-hidden-field roo-date-split-field-group-value',
30897 var labelCls = 'col-md-12';
30898 var contentCls = 'col-md-4';
30900 if(this.fieldLabel){
30904 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30908 html : this.fieldLabel
30913 if(this.labelAlign == 'left'){
30915 if(this.labelWidth > 12){
30916 label.style = "width: " + this.labelWidth + 'px';
30919 if(this.labelWidth < 13 && this.labelmd == 0){
30920 this.labelmd = this.labelWidth;
30923 if(this.labellg > 0){
30924 labelCls = ' col-lg-' + this.labellg;
30925 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30928 if(this.labelmd > 0){
30929 labelCls = ' col-md-' + this.labelmd;
30930 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30933 if(this.labelsm > 0){
30934 labelCls = ' col-sm-' + this.labelsm;
30935 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30938 if(this.labelxs > 0){
30939 labelCls = ' col-xs-' + this.labelxs;
30940 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30944 label.cls += ' ' + labelCls;
30946 cfg.cn.push(label);
30949 Roo.each(['day', 'month', 'year'], function(t){
30952 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30959 inputEl: function ()
30961 return this.el.select('.roo-date-split-field-group-value', true).first();
30964 onRender : function(ct, position)
30968 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30970 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30972 this.dayField = new Roo.bootstrap.ComboBox({
30973 allowBlank : this.dayAllowBlank,
30974 alwaysQuery : true,
30975 displayField : 'value',
30978 forceSelection : true,
30980 placeholder : this.dayPlaceholder,
30981 selectOnFocus : true,
30982 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30983 triggerAction : 'all',
30985 valueField : 'value',
30986 store : new Roo.data.SimpleStore({
30987 data : (function() {
30989 _this.fireEvent('days', _this, days);
30992 fields : [ 'value' ]
30995 select : function (_self, record, index)
30997 _this.setValue(_this.getValue());
31002 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
31004 this.monthField = new Roo.bootstrap.MonthField({
31005 after : '<i class=\"fa fa-calendar\"></i>',
31006 allowBlank : this.monthAllowBlank,
31007 placeholder : this.monthPlaceholder,
31010 render : function (_self)
31012 this.el.select('span.input-group-addon', true).first().on('click', function(e){
31013 e.preventDefault();
31017 select : function (_self, oldvalue, newvalue)
31019 _this.setValue(_this.getValue());
31024 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31026 this.yearField = new Roo.bootstrap.ComboBox({
31027 allowBlank : this.yearAllowBlank,
31028 alwaysQuery : true,
31029 displayField : 'value',
31032 forceSelection : true,
31034 placeholder : this.yearPlaceholder,
31035 selectOnFocus : true,
31036 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31037 triggerAction : 'all',
31039 valueField : 'value',
31040 store : new Roo.data.SimpleStore({
31041 data : (function() {
31043 _this.fireEvent('years', _this, years);
31046 fields : [ 'value' ]
31049 select : function (_self, record, index)
31051 _this.setValue(_this.getValue());
31056 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
31059 setValue : function(v, format)
31061 this.inputEl.dom.value = v;
31063 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
31065 var d = Date.parseDate(v, f);
31072 this.setDay(d.format(this.dayFormat));
31073 this.setMonth(d.format(this.monthFormat));
31074 this.setYear(d.format(this.yearFormat));
31081 setDay : function(v)
31083 this.dayField.setValue(v);
31084 this.inputEl.dom.value = this.getValue();
31089 setMonth : function(v)
31091 this.monthField.setValue(v, true);
31092 this.inputEl.dom.value = this.getValue();
31097 setYear : function(v)
31099 this.yearField.setValue(v);
31100 this.inputEl.dom.value = this.getValue();
31105 getDay : function()
31107 return this.dayField.getValue();
31110 getMonth : function()
31112 return this.monthField.getValue();
31115 getYear : function()
31117 return this.yearField.getValue();
31120 getValue : function()
31122 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31124 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31134 this.inputEl.dom.value = '';
31139 validate : function()
31141 var d = this.dayField.validate();
31142 var m = this.monthField.validate();
31143 var y = this.yearField.validate();
31148 (!this.dayAllowBlank && !d) ||
31149 (!this.monthAllowBlank && !m) ||
31150 (!this.yearAllowBlank && !y)
31155 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31164 this.markInvalid();
31169 markValid : function()
31172 var label = this.el.select('label', true).first();
31173 var icon = this.el.select('i.fa-star', true).first();
31179 this.fireEvent('valid', this);
31183 * Mark this field as invalid
31184 * @param {String} msg The validation message
31186 markInvalid : function(msg)
31189 var label = this.el.select('label', true).first();
31190 var icon = this.el.select('i.fa-star', true).first();
31192 if(label && !icon){
31193 this.el.select('.roo-date-split-field-label', true).createChild({
31195 cls : 'text-danger fa fa-lg fa-star',
31196 tooltip : 'This field is required',
31197 style : 'margin-right:5px;'
31201 this.fireEvent('invalid', this, msg);
31204 clearInvalid : function()
31206 var label = this.el.select('label', true).first();
31207 var icon = this.el.select('i.fa-star', true).first();
31213 this.fireEvent('valid', this);
31216 getName: function()
31226 * http://masonry.desandro.com
31228 * The idea is to render all the bricks based on vertical width...
31230 * The original code extends 'outlayer' - we might need to use that....
31236 * @class Roo.bootstrap.LayoutMasonry
31237 * @extends Roo.bootstrap.Component
31238 * Bootstrap Layout Masonry class
31241 * Create a new Element
31242 * @param {Object} config The config object
31245 Roo.bootstrap.LayoutMasonry = function(config){
31247 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31251 Roo.bootstrap.LayoutMasonry.register(this);
31257 * Fire after layout the items
31258 * @param {Roo.bootstrap.LayoutMasonry} this
31259 * @param {Roo.EventObject} e
31266 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
31269 * @cfg {Boolean} isLayoutInstant = no animation?
31271 isLayoutInstant : false, // needed?
31274 * @cfg {Number} boxWidth width of the columns
31279 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
31284 * @cfg {Number} padWidth padding below box..
31289 * @cfg {Number} gutter gutter width..
31294 * @cfg {Number} maxCols maximum number of columns
31300 * @cfg {Boolean} isAutoInitial defalut true
31302 isAutoInitial : true,
31307 * @cfg {Boolean} isHorizontal defalut false
31309 isHorizontal : false,
31311 currentSize : null,
31317 bricks: null, //CompositeElement
31321 _isLayoutInited : false,
31323 // isAlternative : false, // only use for vertical layout...
31326 * @cfg {Number} alternativePadWidth padding below box..
31328 alternativePadWidth : 50,
31330 selectedBrick : [],
31332 getAutoCreate : function(){
31334 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31338 cls: 'blog-masonary-wrapper ' + this.cls,
31340 cls : 'mas-boxes masonary'
31347 getChildContainer: function( )
31349 if (this.boxesEl) {
31350 return this.boxesEl;
31353 this.boxesEl = this.el.select('.mas-boxes').first();
31355 return this.boxesEl;
31359 initEvents : function()
31363 if(this.isAutoInitial){
31364 Roo.log('hook children rendered');
31365 this.on('childrenrendered', function() {
31366 Roo.log('children rendered');
31372 initial : function()
31374 this.selectedBrick = [];
31376 this.currentSize = this.el.getBox(true);
31378 Roo.EventManager.onWindowResize(this.resize, this);
31380 if(!this.isAutoInitial){
31388 //this.layout.defer(500,this);
31392 resize : function()
31394 var cs = this.el.getBox(true);
31397 this.currentSize.width == cs.width &&
31398 this.currentSize.x == cs.x &&
31399 this.currentSize.height == cs.height &&
31400 this.currentSize.y == cs.y
31402 Roo.log("no change in with or X or Y");
31406 this.currentSize = cs;
31412 layout : function()
31414 this._resetLayout();
31416 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31418 this.layoutItems( isInstant );
31420 this._isLayoutInited = true;
31422 this.fireEvent('layout', this);
31426 _resetLayout : function()
31428 if(this.isHorizontal){
31429 this.horizontalMeasureColumns();
31433 this.verticalMeasureColumns();
31437 verticalMeasureColumns : function()
31439 this.getContainerWidth();
31441 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31442 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31446 var boxWidth = this.boxWidth + this.padWidth;
31448 if(this.containerWidth < this.boxWidth){
31449 boxWidth = this.containerWidth
31452 var containerWidth = this.containerWidth;
31454 var cols = Math.floor(containerWidth / boxWidth);
31456 this.cols = Math.max( cols, 1 );
31458 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31460 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31462 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31464 this.colWidth = boxWidth + avail - this.padWidth;
31466 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31467 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31470 horizontalMeasureColumns : function()
31472 this.getContainerWidth();
31474 var boxWidth = this.boxWidth;
31476 if(this.containerWidth < boxWidth){
31477 boxWidth = this.containerWidth;
31480 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31482 this.el.setHeight(boxWidth);
31486 getContainerWidth : function()
31488 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31491 layoutItems : function( isInstant )
31493 Roo.log(this.bricks);
31495 var items = Roo.apply([], this.bricks);
31497 if(this.isHorizontal){
31498 this._horizontalLayoutItems( items , isInstant );
31502 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31503 // this._verticalAlternativeLayoutItems( items , isInstant );
31507 this._verticalLayoutItems( items , isInstant );
31511 _verticalLayoutItems : function ( items , isInstant)
31513 if ( !items || !items.length ) {
31518 ['xs', 'xs', 'xs', 'tall'],
31519 ['xs', 'xs', 'tall'],
31520 ['xs', 'xs', 'sm'],
31521 ['xs', 'xs', 'xs'],
31527 ['sm', 'xs', 'xs'],
31531 ['tall', 'xs', 'xs', 'xs'],
31532 ['tall', 'xs', 'xs'],
31544 Roo.each(items, function(item, k){
31546 switch (item.size) {
31547 // these layouts take up a full box,
31558 boxes.push([item]);
31581 var filterPattern = function(box, length)
31589 var pattern = box.slice(0, length);
31593 Roo.each(pattern, function(i){
31594 format.push(i.size);
31597 Roo.each(standard, function(s){
31599 if(String(s) != String(format)){
31608 if(!match && length == 1){
31613 filterPattern(box, length - 1);
31617 queue.push(pattern);
31619 box = box.slice(length, box.length);
31621 filterPattern(box, 4);
31627 Roo.each(boxes, function(box, k){
31633 if(box.length == 1){
31638 filterPattern(box, 4);
31642 this._processVerticalLayoutQueue( queue, isInstant );
31646 // _verticalAlternativeLayoutItems : function( items , isInstant )
31648 // if ( !items || !items.length ) {
31652 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31656 _horizontalLayoutItems : function ( items , isInstant)
31658 if ( !items || !items.length || items.length < 3) {
31664 var eItems = items.slice(0, 3);
31666 items = items.slice(3, items.length);
31669 ['xs', 'xs', 'xs', 'wide'],
31670 ['xs', 'xs', 'wide'],
31671 ['xs', 'xs', 'sm'],
31672 ['xs', 'xs', 'xs'],
31678 ['sm', 'xs', 'xs'],
31682 ['wide', 'xs', 'xs', 'xs'],
31683 ['wide', 'xs', 'xs'],
31696 Roo.each(items, function(item, k){
31698 switch (item.size) {
31709 boxes.push([item]);
31733 var filterPattern = function(box, length)
31741 var pattern = box.slice(0, length);
31745 Roo.each(pattern, function(i){
31746 format.push(i.size);
31749 Roo.each(standard, function(s){
31751 if(String(s) != String(format)){
31760 if(!match && length == 1){
31765 filterPattern(box, length - 1);
31769 queue.push(pattern);
31771 box = box.slice(length, box.length);
31773 filterPattern(box, 4);
31779 Roo.each(boxes, function(box, k){
31785 if(box.length == 1){
31790 filterPattern(box, 4);
31797 var pos = this.el.getBox(true);
31801 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31803 var hit_end = false;
31805 Roo.each(queue, function(box){
31809 Roo.each(box, function(b){
31811 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31821 Roo.each(box, function(b){
31823 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31826 mx = Math.max(mx, b.x);
31830 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31834 Roo.each(box, function(b){
31836 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31850 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31853 /** Sets position of item in DOM
31854 * @param {Element} item
31855 * @param {Number} x - horizontal position
31856 * @param {Number} y - vertical position
31857 * @param {Boolean} isInstant - disables transitions
31859 _processVerticalLayoutQueue : function( queue, isInstant )
31861 var pos = this.el.getBox(true);
31866 for (var i = 0; i < this.cols; i++){
31870 Roo.each(queue, function(box, k){
31872 var col = k % this.cols;
31874 Roo.each(box, function(b,kk){
31876 b.el.position('absolute');
31878 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31879 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31881 if(b.size == 'md-left' || b.size == 'md-right'){
31882 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31883 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31886 b.el.setWidth(width);
31887 b.el.setHeight(height);
31889 b.el.select('iframe',true).setSize(width,height);
31893 for (var i = 0; i < this.cols; i++){
31895 if(maxY[i] < maxY[col]){
31900 col = Math.min(col, i);
31904 x = pos.x + col * (this.colWidth + this.padWidth);
31908 var positions = [];
31910 switch (box.length){
31912 positions = this.getVerticalOneBoxColPositions(x, y, box);
31915 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31918 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31921 positions = this.getVerticalFourBoxColPositions(x, y, box);
31927 Roo.each(box, function(b,kk){
31929 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31931 var sz = b.el.getSize();
31933 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31941 for (var i = 0; i < this.cols; i++){
31942 mY = Math.max(mY, maxY[i]);
31945 this.el.setHeight(mY - pos.y);
31949 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31951 // var pos = this.el.getBox(true);
31954 // var maxX = pos.right;
31956 // var maxHeight = 0;
31958 // Roo.each(items, function(item, k){
31962 // item.el.position('absolute');
31964 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31966 // item.el.setWidth(width);
31968 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31970 // item.el.setHeight(height);
31973 // item.el.setXY([x, y], isInstant ? false : true);
31975 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31978 // y = y + height + this.alternativePadWidth;
31980 // maxHeight = maxHeight + height + this.alternativePadWidth;
31984 // this.el.setHeight(maxHeight);
31988 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31990 var pos = this.el.getBox(true);
31995 var maxX = pos.right;
31997 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31999 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32001 Roo.each(queue, function(box, k){
32003 Roo.each(box, function(b, kk){
32005 b.el.position('absolute');
32007 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32008 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32010 if(b.size == 'md-left' || b.size == 'md-right'){
32011 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32012 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32015 b.el.setWidth(width);
32016 b.el.setHeight(height);
32024 var positions = [];
32026 switch (box.length){
32028 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32031 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
32034 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
32037 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
32043 Roo.each(box, function(b,kk){
32045 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32047 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
32055 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
32057 Roo.each(eItems, function(b,k){
32059 b.size = (k == 0) ? 'sm' : 'xs';
32060 b.x = (k == 0) ? 2 : 1;
32061 b.y = (k == 0) ? 2 : 1;
32063 b.el.position('absolute');
32065 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32067 b.el.setWidth(width);
32069 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32071 b.el.setHeight(height);
32075 var positions = [];
32078 x : maxX - this.unitWidth * 2 - this.gutter,
32083 x : maxX - this.unitWidth,
32084 y : minY + (this.unitWidth + this.gutter) * 2
32088 x : maxX - this.unitWidth * 3 - this.gutter * 2,
32092 Roo.each(eItems, function(b,k){
32094 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32100 getVerticalOneBoxColPositions : function(x, y, box)
32104 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32106 if(box[0].size == 'md-left'){
32110 if(box[0].size == 'md-right'){
32115 x : x + (this.unitWidth + this.gutter) * rand,
32122 getVerticalTwoBoxColPositions : function(x, y, box)
32126 if(box[0].size == 'xs'){
32130 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32134 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32148 x : x + (this.unitWidth + this.gutter) * 2,
32149 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32156 getVerticalThreeBoxColPositions : function(x, y, box)
32160 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32168 x : x + (this.unitWidth + this.gutter) * 1,
32173 x : x + (this.unitWidth + this.gutter) * 2,
32181 if(box[0].size == 'xs' && box[1].size == 'xs'){
32190 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32194 x : x + (this.unitWidth + this.gutter) * 1,
32208 x : x + (this.unitWidth + this.gutter) * 2,
32213 x : x + (this.unitWidth + this.gutter) * 2,
32214 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32221 getVerticalFourBoxColPositions : function(x, y, box)
32225 if(box[0].size == 'xs'){
32234 y : y + (this.unitHeight + this.gutter) * 1
32239 y : y + (this.unitHeight + this.gutter) * 2
32243 x : x + (this.unitWidth + this.gutter) * 1,
32257 x : x + (this.unitWidth + this.gutter) * 2,
32262 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32263 y : y + (this.unitHeight + this.gutter) * 1
32267 x : x + (this.unitWidth + this.gutter) * 2,
32268 y : y + (this.unitWidth + this.gutter) * 2
32275 getHorizontalOneBoxColPositions : function(maxX, minY, box)
32279 if(box[0].size == 'md-left'){
32281 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32288 if(box[0].size == 'md-right'){
32290 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32291 y : minY + (this.unitWidth + this.gutter) * 1
32297 var rand = Math.floor(Math.random() * (4 - box[0].y));
32300 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32301 y : minY + (this.unitWidth + this.gutter) * rand
32308 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32312 if(box[0].size == 'xs'){
32315 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32320 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32321 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32329 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32334 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32335 y : minY + (this.unitWidth + this.gutter) * 2
32342 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32346 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32349 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32354 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32355 y : minY + (this.unitWidth + this.gutter) * 1
32359 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32360 y : minY + (this.unitWidth + this.gutter) * 2
32367 if(box[0].size == 'xs' && box[1].size == 'xs'){
32370 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32375 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32380 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32381 y : minY + (this.unitWidth + this.gutter) * 1
32389 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32394 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32395 y : minY + (this.unitWidth + this.gutter) * 2
32399 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32400 y : minY + (this.unitWidth + this.gutter) * 2
32407 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32411 if(box[0].size == 'xs'){
32414 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32419 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32424 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),
32429 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32430 y : minY + (this.unitWidth + this.gutter) * 1
32438 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32443 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32444 y : minY + (this.unitWidth + this.gutter) * 2
32448 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32449 y : minY + (this.unitWidth + this.gutter) * 2
32453 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),
32454 y : minY + (this.unitWidth + this.gutter) * 2
32462 * remove a Masonry Brick
32463 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32465 removeBrick : function(brick_id)
32471 for (var i = 0; i<this.bricks.length; i++) {
32472 if (this.bricks[i].id == brick_id) {
32473 this.bricks.splice(i,1);
32474 this.el.dom.removeChild(Roo.get(brick_id).dom);
32481 * adds a Masonry Brick
32482 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32484 addBrick : function(cfg)
32486 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32487 //this.register(cn);
32488 cn.parentId = this.id;
32489 cn.render(this.el);
32494 * register a Masonry Brick
32495 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32498 register : function(brick)
32500 this.bricks.push(brick);
32501 brick.masonryId = this.id;
32505 * clear all the Masonry Brick
32507 clearAll : function()
32510 //this.getChildContainer().dom.innerHTML = "";
32511 this.el.dom.innerHTML = '';
32514 getSelected : function()
32516 if (!this.selectedBrick) {
32520 return this.selectedBrick;
32524 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32528 * register a Masonry Layout
32529 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32532 register : function(layout)
32534 this.groups[layout.id] = layout;
32537 * fetch a Masonry Layout based on the masonry layout ID
32538 * @param {string} the masonry layout to add
32539 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32542 get: function(layout_id) {
32543 if (typeof(this.groups[layout_id]) == 'undefined') {
32546 return this.groups[layout_id] ;
32558 * http://masonry.desandro.com
32560 * The idea is to render all the bricks based on vertical width...
32562 * The original code extends 'outlayer' - we might need to use that....
32568 * @class Roo.bootstrap.LayoutMasonryAuto
32569 * @extends Roo.bootstrap.Component
32570 * Bootstrap Layout Masonry class
32573 * Create a new Element
32574 * @param {Object} config The config object
32577 Roo.bootstrap.LayoutMasonryAuto = function(config){
32578 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32581 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32584 * @cfg {Boolean} isFitWidth - resize the width..
32586 isFitWidth : false, // options..
32588 * @cfg {Boolean} isOriginLeft = left align?
32590 isOriginLeft : true,
32592 * @cfg {Boolean} isOriginTop = top align?
32594 isOriginTop : false,
32596 * @cfg {Boolean} isLayoutInstant = no animation?
32598 isLayoutInstant : false, // needed?
32600 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32602 isResizingContainer : true,
32604 * @cfg {Number} columnWidth width of the columns
32610 * @cfg {Number} maxCols maximum number of columns
32615 * @cfg {Number} padHeight padding below box..
32621 * @cfg {Boolean} isAutoInitial defalut true
32624 isAutoInitial : true,
32630 initialColumnWidth : 0,
32631 currentSize : null,
32633 colYs : null, // array.
32640 bricks: null, //CompositeElement
32641 cols : 0, // array?
32642 // element : null, // wrapped now this.el
32643 _isLayoutInited : null,
32646 getAutoCreate : function(){
32650 cls: 'blog-masonary-wrapper ' + this.cls,
32652 cls : 'mas-boxes masonary'
32659 getChildContainer: function( )
32661 if (this.boxesEl) {
32662 return this.boxesEl;
32665 this.boxesEl = this.el.select('.mas-boxes').first();
32667 return this.boxesEl;
32671 initEvents : function()
32675 if(this.isAutoInitial){
32676 Roo.log('hook children rendered');
32677 this.on('childrenrendered', function() {
32678 Roo.log('children rendered');
32685 initial : function()
32687 this.reloadItems();
32689 this.currentSize = this.el.getBox(true);
32691 /// was window resize... - let's see if this works..
32692 Roo.EventManager.onWindowResize(this.resize, this);
32694 if(!this.isAutoInitial){
32699 this.layout.defer(500,this);
32702 reloadItems: function()
32704 this.bricks = this.el.select('.masonry-brick', true);
32706 this.bricks.each(function(b) {
32707 //Roo.log(b.getSize());
32708 if (!b.attr('originalwidth')) {
32709 b.attr('originalwidth', b.getSize().width);
32714 Roo.log(this.bricks.elements.length);
32717 resize : function()
32720 var cs = this.el.getBox(true);
32722 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32723 Roo.log("no change in with or X");
32726 this.currentSize = cs;
32730 layout : function()
32733 this._resetLayout();
32734 //this._manageStamps();
32736 // don't animate first layout
32737 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32738 this.layoutItems( isInstant );
32740 // flag for initalized
32741 this._isLayoutInited = true;
32744 layoutItems : function( isInstant )
32746 //var items = this._getItemsForLayout( this.items );
32747 // original code supports filtering layout items.. we just ignore it..
32749 this._layoutItems( this.bricks , isInstant );
32751 this._postLayout();
32753 _layoutItems : function ( items , isInstant)
32755 //this.fireEvent( 'layout', this, items );
32758 if ( !items || !items.elements.length ) {
32759 // no items, emit event with empty array
32764 items.each(function(item) {
32765 Roo.log("layout item");
32767 // get x/y object from method
32768 var position = this._getItemLayoutPosition( item );
32770 position.item = item;
32771 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32772 queue.push( position );
32775 this._processLayoutQueue( queue );
32777 /** Sets position of item in DOM
32778 * @param {Element} item
32779 * @param {Number} x - horizontal position
32780 * @param {Number} y - vertical position
32781 * @param {Boolean} isInstant - disables transitions
32783 _processLayoutQueue : function( queue )
32785 for ( var i=0, len = queue.length; i < len; i++ ) {
32786 var obj = queue[i];
32787 obj.item.position('absolute');
32788 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32794 * Any logic you want to do after each layout,
32795 * i.e. size the container
32797 _postLayout : function()
32799 this.resizeContainer();
32802 resizeContainer : function()
32804 if ( !this.isResizingContainer ) {
32807 var size = this._getContainerSize();
32809 this.el.setSize(size.width,size.height);
32810 this.boxesEl.setSize(size.width,size.height);
32816 _resetLayout : function()
32818 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32819 this.colWidth = this.el.getWidth();
32820 //this.gutter = this.el.getWidth();
32822 this.measureColumns();
32828 this.colYs.push( 0 );
32834 measureColumns : function()
32836 this.getContainerWidth();
32837 // if columnWidth is 0, default to outerWidth of first item
32838 if ( !this.columnWidth ) {
32839 var firstItem = this.bricks.first();
32840 Roo.log(firstItem);
32841 this.columnWidth = this.containerWidth;
32842 if (firstItem && firstItem.attr('originalwidth') ) {
32843 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32845 // columnWidth fall back to item of first element
32846 Roo.log("set column width?");
32847 this.initialColumnWidth = this.columnWidth ;
32849 // if first elem has no width, default to size of container
32854 if (this.initialColumnWidth) {
32855 this.columnWidth = this.initialColumnWidth;
32860 // column width is fixed at the top - however if container width get's smaller we should
32863 // this bit calcs how man columns..
32865 var columnWidth = this.columnWidth += this.gutter;
32867 // calculate columns
32868 var containerWidth = this.containerWidth + this.gutter;
32870 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32871 // fix rounding errors, typically with gutters
32872 var excess = columnWidth - containerWidth % columnWidth;
32875 // if overshoot is less than a pixel, round up, otherwise floor it
32876 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32877 cols = Math[ mathMethod ]( cols );
32878 this.cols = Math.max( cols, 1 );
32879 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32881 // padding positioning..
32882 var totalColWidth = this.cols * this.columnWidth;
32883 var padavail = this.containerWidth - totalColWidth;
32884 // so for 2 columns - we need 3 'pads'
32886 var padNeeded = (1+this.cols) * this.padWidth;
32888 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32890 this.columnWidth += padExtra
32891 //this.padWidth = Math.floor(padavail / ( this.cols));
32893 // adjust colum width so that padding is fixed??
32895 // we have 3 columns ... total = width * 3
32896 // we have X left over... that should be used by
32898 //if (this.expandC) {
32906 getContainerWidth : function()
32908 /* // container is parent if fit width
32909 var container = this.isFitWidth ? this.element.parentNode : this.element;
32910 // check that this.size and size are there
32911 // IE8 triggers resize on body size change, so they might not be
32913 var size = getSize( container ); //FIXME
32914 this.containerWidth = size && size.innerWidth; //FIXME
32917 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32921 _getItemLayoutPosition : function( item ) // what is item?
32923 // we resize the item to our columnWidth..
32925 item.setWidth(this.columnWidth);
32926 item.autoBoxAdjust = false;
32928 var sz = item.getSize();
32930 // how many columns does this brick span
32931 var remainder = this.containerWidth % this.columnWidth;
32933 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32934 // round if off by 1 pixel, otherwise use ceil
32935 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32936 colSpan = Math.min( colSpan, this.cols );
32938 // normally this should be '1' as we dont' currently allow multi width columns..
32940 var colGroup = this._getColGroup( colSpan );
32941 // get the minimum Y value from the columns
32942 var minimumY = Math.min.apply( Math, colGroup );
32943 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32945 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32947 // position the brick
32949 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32950 y: this.currentSize.y + minimumY + this.padHeight
32954 // apply setHeight to necessary columns
32955 var setHeight = minimumY + sz.height + this.padHeight;
32956 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32958 var setSpan = this.cols + 1 - colGroup.length;
32959 for ( var i = 0; i < setSpan; i++ ) {
32960 this.colYs[ shortColIndex + i ] = setHeight ;
32967 * @param {Number} colSpan - number of columns the element spans
32968 * @returns {Array} colGroup
32970 _getColGroup : function( colSpan )
32972 if ( colSpan < 2 ) {
32973 // if brick spans only one column, use all the column Ys
32978 // how many different places could this brick fit horizontally
32979 var groupCount = this.cols + 1 - colSpan;
32980 // for each group potential horizontal position
32981 for ( var i = 0; i < groupCount; i++ ) {
32982 // make an array of colY values for that one group
32983 var groupColYs = this.colYs.slice( i, i + colSpan );
32984 // and get the max value of the array
32985 colGroup[i] = Math.max.apply( Math, groupColYs );
32990 _manageStamp : function( stamp )
32992 var stampSize = stamp.getSize();
32993 var offset = stamp.getBox();
32994 // get the columns that this stamp affects
32995 var firstX = this.isOriginLeft ? offset.x : offset.right;
32996 var lastX = firstX + stampSize.width;
32997 var firstCol = Math.floor( firstX / this.columnWidth );
32998 firstCol = Math.max( 0, firstCol );
33000 var lastCol = Math.floor( lastX / this.columnWidth );
33001 // lastCol should not go over if multiple of columnWidth #425
33002 lastCol -= lastX % this.columnWidth ? 0 : 1;
33003 lastCol = Math.min( this.cols - 1, lastCol );
33005 // set colYs to bottom of the stamp
33006 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
33009 for ( var i = firstCol; i <= lastCol; i++ ) {
33010 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33015 _getContainerSize : function()
33017 this.maxY = Math.max.apply( Math, this.colYs );
33022 if ( this.isFitWidth ) {
33023 size.width = this._getContainerFitWidth();
33029 _getContainerFitWidth : function()
33031 var unusedCols = 0;
33032 // count unused columns
33035 if ( this.colYs[i] !== 0 ) {
33040 // fit container to columns that have been used
33041 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
33044 needsResizeLayout : function()
33046 var previousWidth = this.containerWidth;
33047 this.getContainerWidth();
33048 return previousWidth !== this.containerWidth;
33063 * @class Roo.bootstrap.MasonryBrick
33064 * @extends Roo.bootstrap.Component
33065 * Bootstrap MasonryBrick class
33068 * Create a new MasonryBrick
33069 * @param {Object} config The config object
33072 Roo.bootstrap.MasonryBrick = function(config){
33074 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
33076 Roo.bootstrap.MasonryBrick.register(this);
33082 * When a MasonryBrick is clcik
33083 * @param {Roo.bootstrap.MasonryBrick} this
33084 * @param {Roo.EventObject} e
33090 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
33093 * @cfg {String} title
33097 * @cfg {String} html
33101 * @cfg {String} bgimage
33105 * @cfg {String} videourl
33109 * @cfg {String} cls
33113 * @cfg {String} href
33117 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33122 * @cfg {String} placetitle (center|bottom)
33127 * @cfg {Boolean} isFitContainer defalut true
33129 isFitContainer : true,
33132 * @cfg {Boolean} preventDefault defalut false
33134 preventDefault : false,
33137 * @cfg {Boolean} inverse defalut false
33139 maskInverse : false,
33141 getAutoCreate : function()
33143 if(!this.isFitContainer){
33144 return this.getSplitAutoCreate();
33147 var cls = 'masonry-brick masonry-brick-full';
33149 if(this.href.length){
33150 cls += ' masonry-brick-link';
33153 if(this.bgimage.length){
33154 cls += ' masonry-brick-image';
33157 if(this.maskInverse){
33158 cls += ' mask-inverse';
33161 if(!this.html.length && !this.maskInverse && !this.videourl.length){
33162 cls += ' enable-mask';
33166 cls += ' masonry-' + this.size + '-brick';
33169 if(this.placetitle.length){
33171 switch (this.placetitle) {
33173 cls += ' masonry-center-title';
33176 cls += ' masonry-bottom-title';
33183 if(!this.html.length && !this.bgimage.length){
33184 cls += ' masonry-center-title';
33187 if(!this.html.length && this.bgimage.length){
33188 cls += ' masonry-bottom-title';
33193 cls += ' ' + this.cls;
33197 tag: (this.href.length) ? 'a' : 'div',
33202 cls: 'masonry-brick-mask'
33206 cls: 'masonry-brick-paragraph',
33212 if(this.href.length){
33213 cfg.href = this.href;
33216 var cn = cfg.cn[1].cn;
33218 if(this.title.length){
33221 cls: 'masonry-brick-title',
33226 if(this.html.length){
33229 cls: 'masonry-brick-text',
33234 if (!this.title.length && !this.html.length) {
33235 cfg.cn[1].cls += ' hide';
33238 if(this.bgimage.length){
33241 cls: 'masonry-brick-image-view',
33246 if(this.videourl.length){
33247 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33248 // youtube support only?
33251 cls: 'masonry-brick-image-view',
33254 allowfullscreen : true
33262 getSplitAutoCreate : function()
33264 var cls = 'masonry-brick masonry-brick-split';
33266 if(this.href.length){
33267 cls += ' masonry-brick-link';
33270 if(this.bgimage.length){
33271 cls += ' masonry-brick-image';
33275 cls += ' masonry-' + this.size + '-brick';
33278 switch (this.placetitle) {
33280 cls += ' masonry-center-title';
33283 cls += ' masonry-bottom-title';
33286 if(!this.bgimage.length){
33287 cls += ' masonry-center-title';
33290 if(this.bgimage.length){
33291 cls += ' masonry-bottom-title';
33297 cls += ' ' + this.cls;
33301 tag: (this.href.length) ? 'a' : 'div',
33306 cls: 'masonry-brick-split-head',
33310 cls: 'masonry-brick-paragraph',
33317 cls: 'masonry-brick-split-body',
33323 if(this.href.length){
33324 cfg.href = this.href;
33327 if(this.title.length){
33328 cfg.cn[0].cn[0].cn.push({
33330 cls: 'masonry-brick-title',
33335 if(this.html.length){
33336 cfg.cn[1].cn.push({
33338 cls: 'masonry-brick-text',
33343 if(this.bgimage.length){
33344 cfg.cn[0].cn.push({
33346 cls: 'masonry-brick-image-view',
33351 if(this.videourl.length){
33352 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33353 // youtube support only?
33354 cfg.cn[0].cn.cn.push({
33356 cls: 'masonry-brick-image-view',
33359 allowfullscreen : true
33366 initEvents: function()
33368 switch (this.size) {
33401 this.el.on('touchstart', this.onTouchStart, this);
33402 this.el.on('touchmove', this.onTouchMove, this);
33403 this.el.on('touchend', this.onTouchEnd, this);
33404 this.el.on('contextmenu', this.onContextMenu, this);
33406 this.el.on('mouseenter' ,this.enter, this);
33407 this.el.on('mouseleave', this.leave, this);
33408 this.el.on('click', this.onClick, this);
33411 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33412 this.parent().bricks.push(this);
33417 onClick: function(e, el)
33419 var time = this.endTimer - this.startTimer;
33420 // Roo.log(e.preventDefault());
33423 e.preventDefault();
33428 if(!this.preventDefault){
33432 e.preventDefault();
33434 if (this.activeClass != '') {
33435 this.selectBrick();
33438 this.fireEvent('click', this, e);
33441 enter: function(e, el)
33443 e.preventDefault();
33445 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33449 if(this.bgimage.length && this.html.length){
33450 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33454 leave: function(e, el)
33456 e.preventDefault();
33458 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33462 if(this.bgimage.length && this.html.length){
33463 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33467 onTouchStart: function(e, el)
33469 // e.preventDefault();
33471 this.touchmoved = false;
33473 if(!this.isFitContainer){
33477 if(!this.bgimage.length || !this.html.length){
33481 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33483 this.timer = new Date().getTime();
33487 onTouchMove: function(e, el)
33489 this.touchmoved = true;
33492 onContextMenu : function(e,el)
33494 e.preventDefault();
33495 e.stopPropagation();
33499 onTouchEnd: function(e, el)
33501 // e.preventDefault();
33503 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33510 if(!this.bgimage.length || !this.html.length){
33512 if(this.href.length){
33513 window.location.href = this.href;
33519 if(!this.isFitContainer){
33523 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33525 window.location.href = this.href;
33528 //selection on single brick only
33529 selectBrick : function() {
33531 if (!this.parentId) {
33535 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33536 var index = m.selectedBrick.indexOf(this.id);
33539 m.selectedBrick.splice(index,1);
33540 this.el.removeClass(this.activeClass);
33544 for(var i = 0; i < m.selectedBrick.length; i++) {
33545 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33546 b.el.removeClass(b.activeClass);
33549 m.selectedBrick = [];
33551 m.selectedBrick.push(this.id);
33552 this.el.addClass(this.activeClass);
33556 isSelected : function(){
33557 return this.el.hasClass(this.activeClass);
33562 Roo.apply(Roo.bootstrap.MasonryBrick, {
33565 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33567 * register a Masonry Brick
33568 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33571 register : function(brick)
33573 //this.groups[brick.id] = brick;
33574 this.groups.add(brick.id, brick);
33577 * fetch a masonry brick based on the masonry brick ID
33578 * @param {string} the masonry brick to add
33579 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33582 get: function(brick_id)
33584 // if (typeof(this.groups[brick_id]) == 'undefined') {
33587 // return this.groups[brick_id] ;
33589 if(this.groups.key(brick_id)) {
33590 return this.groups.key(brick_id);
33608 * @class Roo.bootstrap.Brick
33609 * @extends Roo.bootstrap.Component
33610 * Bootstrap Brick class
33613 * Create a new Brick
33614 * @param {Object} config The config object
33617 Roo.bootstrap.Brick = function(config){
33618 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33624 * When a Brick is click
33625 * @param {Roo.bootstrap.Brick} this
33626 * @param {Roo.EventObject} e
33632 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33635 * @cfg {String} title
33639 * @cfg {String} html
33643 * @cfg {String} bgimage
33647 * @cfg {String} cls
33651 * @cfg {String} href
33655 * @cfg {String} video
33659 * @cfg {Boolean} square
33663 getAutoCreate : function()
33665 var cls = 'roo-brick';
33667 if(this.href.length){
33668 cls += ' roo-brick-link';
33671 if(this.bgimage.length){
33672 cls += ' roo-brick-image';
33675 if(!this.html.length && !this.bgimage.length){
33676 cls += ' roo-brick-center-title';
33679 if(!this.html.length && this.bgimage.length){
33680 cls += ' roo-brick-bottom-title';
33684 cls += ' ' + this.cls;
33688 tag: (this.href.length) ? 'a' : 'div',
33693 cls: 'roo-brick-paragraph',
33699 if(this.href.length){
33700 cfg.href = this.href;
33703 var cn = cfg.cn[0].cn;
33705 if(this.title.length){
33708 cls: 'roo-brick-title',
33713 if(this.html.length){
33716 cls: 'roo-brick-text',
33723 if(this.bgimage.length){
33726 cls: 'roo-brick-image-view',
33734 initEvents: function()
33736 if(this.title.length || this.html.length){
33737 this.el.on('mouseenter' ,this.enter, this);
33738 this.el.on('mouseleave', this.leave, this);
33741 Roo.EventManager.onWindowResize(this.resize, this);
33743 if(this.bgimage.length){
33744 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33745 this.imageEl.on('load', this.onImageLoad, this);
33752 onImageLoad : function()
33757 resize : function()
33759 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33761 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33763 if(this.bgimage.length){
33764 var image = this.el.select('.roo-brick-image-view', true).first();
33766 image.setWidth(paragraph.getWidth());
33769 image.setHeight(paragraph.getWidth());
33772 this.el.setHeight(image.getHeight());
33773 paragraph.setHeight(image.getHeight());
33779 enter: function(e, el)
33781 e.preventDefault();
33783 if(this.bgimage.length){
33784 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33785 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33789 leave: function(e, el)
33791 e.preventDefault();
33793 if(this.bgimage.length){
33794 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33795 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33810 * @class Roo.bootstrap.NumberField
33811 * @extends Roo.bootstrap.Input
33812 * Bootstrap NumberField class
33818 * Create a new NumberField
33819 * @param {Object} config The config object
33822 Roo.bootstrap.NumberField = function(config){
33823 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33826 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33829 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33831 allowDecimals : true,
33833 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33835 decimalSeparator : ".",
33837 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33839 decimalPrecision : 2,
33841 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33843 allowNegative : true,
33846 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33850 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33852 minValue : Number.NEGATIVE_INFINITY,
33854 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33856 maxValue : Number.MAX_VALUE,
33858 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33860 minText : "The minimum value for this field is {0}",
33862 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33864 maxText : "The maximum value for this field is {0}",
33866 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33867 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33869 nanText : "{0} is not a valid number",
33871 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33873 thousandsDelimiter : false,
33875 * @cfg {String} valueAlign alignment of value
33877 valueAlign : "left",
33879 getAutoCreate : function()
33881 var hiddenInput = {
33885 cls: 'hidden-number-input'
33889 hiddenInput.name = this.name;
33894 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33896 this.name = hiddenInput.name;
33898 if(cfg.cn.length > 0) {
33899 cfg.cn.push(hiddenInput);
33906 initEvents : function()
33908 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33910 var allowed = "0123456789";
33912 if(this.allowDecimals){
33913 allowed += this.decimalSeparator;
33916 if(this.allowNegative){
33920 if(this.thousandsDelimiter) {
33924 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33926 var keyPress = function(e){
33928 var k = e.getKey();
33930 var c = e.getCharCode();
33933 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33934 allowed.indexOf(String.fromCharCode(c)) === -1
33940 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33944 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33949 this.el.on("keypress", keyPress, this);
33952 validateValue : function(value)
33955 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33959 var num = this.parseValue(value);
33962 this.markInvalid(String.format(this.nanText, value));
33966 if(num < this.minValue){
33967 this.markInvalid(String.format(this.minText, this.minValue));
33971 if(num > this.maxValue){
33972 this.markInvalid(String.format(this.maxText, this.maxValue));
33979 getValue : function()
33981 var v = this.hiddenEl().getValue();
33983 return this.fixPrecision(this.parseValue(v));
33986 parseValue : function(value)
33988 if(this.thousandsDelimiter) {
33990 r = new RegExp(",", "g");
33991 value = value.replace(r, "");
33994 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33995 return isNaN(value) ? '' : value;
33998 fixPrecision : function(value)
34000 if(this.thousandsDelimiter) {
34002 r = new RegExp(",", "g");
34003 value = value.replace(r, "");
34006 var nan = isNaN(value);
34008 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
34009 return nan ? '' : value;
34011 return parseFloat(value).toFixed(this.decimalPrecision);
34014 setValue : function(v)
34016 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34022 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34024 this.inputEl().dom.value = (v == '') ? '' :
34025 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34027 if(!this.allowZero && v === '0') {
34028 this.hiddenEl().dom.value = '';
34029 this.inputEl().dom.value = '';
34036 decimalPrecisionFcn : function(v)
34038 return Math.floor(v);
34041 beforeBlur : function()
34043 var v = this.parseValue(this.getRawValue());
34045 if(v || v === 0 || v === ''){
34050 hiddenEl : function()
34052 return this.el.select('input.hidden-number-input',true).first();
34064 * @class Roo.bootstrap.DocumentSlider
34065 * @extends Roo.bootstrap.Component
34066 * Bootstrap DocumentSlider class
34069 * Create a new DocumentViewer
34070 * @param {Object} config The config object
34073 Roo.bootstrap.DocumentSlider = function(config){
34074 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
34081 * Fire after initEvent
34082 * @param {Roo.bootstrap.DocumentSlider} this
34087 * Fire after update
34088 * @param {Roo.bootstrap.DocumentSlider} this
34094 * @param {Roo.bootstrap.DocumentSlider} this
34100 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
34106 getAutoCreate : function()
34110 cls : 'roo-document-slider',
34114 cls : 'roo-document-slider-header',
34118 cls : 'roo-document-slider-header-title'
34124 cls : 'roo-document-slider-body',
34128 cls : 'roo-document-slider-prev',
34132 cls : 'fa fa-chevron-left'
34138 cls : 'roo-document-slider-thumb',
34142 cls : 'roo-document-slider-image'
34148 cls : 'roo-document-slider-next',
34152 cls : 'fa fa-chevron-right'
34164 initEvents : function()
34166 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34167 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34169 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34170 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34172 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34173 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34175 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34176 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34178 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34179 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34181 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34182 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34184 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34185 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34187 this.thumbEl.on('click', this.onClick, this);
34189 this.prevIndicator.on('click', this.prev, this);
34191 this.nextIndicator.on('click', this.next, this);
34195 initial : function()
34197 if(this.files.length){
34198 this.indicator = 1;
34202 this.fireEvent('initial', this);
34205 update : function()
34207 this.imageEl.attr('src', this.files[this.indicator - 1]);
34209 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34211 this.prevIndicator.show();
34213 if(this.indicator == 1){
34214 this.prevIndicator.hide();
34217 this.nextIndicator.show();
34219 if(this.indicator == this.files.length){
34220 this.nextIndicator.hide();
34223 this.thumbEl.scrollTo('top');
34225 this.fireEvent('update', this);
34228 onClick : function(e)
34230 e.preventDefault();
34232 this.fireEvent('click', this);
34237 e.preventDefault();
34239 this.indicator = Math.max(1, this.indicator - 1);
34246 e.preventDefault();
34248 this.indicator = Math.min(this.files.length, this.indicator + 1);
34262 * @class Roo.bootstrap.RadioSet
34263 * @extends Roo.bootstrap.Input
34264 * Bootstrap RadioSet class
34265 * @cfg {String} indicatorpos (left|right) default left
34266 * @cfg {Boolean} inline (true|false) inline the element (default true)
34267 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34269 * Create a new RadioSet
34270 * @param {Object} config The config object
34273 Roo.bootstrap.RadioSet = function(config){
34275 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34279 Roo.bootstrap.RadioSet.register(this);
34284 * Fires when the element is checked or unchecked.
34285 * @param {Roo.bootstrap.RadioSet} this This radio
34286 * @param {Roo.bootstrap.Radio} item The checked item
34291 * Fires when the element is click.
34292 * @param {Roo.bootstrap.RadioSet} this This radio set
34293 * @param {Roo.bootstrap.Radio} item The checked item
34294 * @param {Roo.EventObject} e The event object
34301 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
34309 indicatorpos : 'left',
34311 getAutoCreate : function()
34315 cls : 'roo-radio-set-label',
34319 html : this.fieldLabel
34323 if (Roo.bootstrap.version == 3) {
34326 if(this.indicatorpos == 'left'){
34329 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34330 tooltip : 'This field is required'
34335 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34336 tooltip : 'This field is required'
34342 cls : 'roo-radio-set-items'
34345 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34347 if (align === 'left' && this.fieldLabel.length) {
34350 cls : "roo-radio-set-right",
34356 if(this.labelWidth > 12){
34357 label.style = "width: " + this.labelWidth + 'px';
34360 if(this.labelWidth < 13 && this.labelmd == 0){
34361 this.labelmd = this.labelWidth;
34364 if(this.labellg > 0){
34365 label.cls += ' col-lg-' + this.labellg;
34366 items.cls += ' col-lg-' + (12 - this.labellg);
34369 if(this.labelmd > 0){
34370 label.cls += ' col-md-' + this.labelmd;
34371 items.cls += ' col-md-' + (12 - this.labelmd);
34374 if(this.labelsm > 0){
34375 label.cls += ' col-sm-' + this.labelsm;
34376 items.cls += ' col-sm-' + (12 - this.labelsm);
34379 if(this.labelxs > 0){
34380 label.cls += ' col-xs-' + this.labelxs;
34381 items.cls += ' col-xs-' + (12 - this.labelxs);
34387 cls : 'roo-radio-set',
34391 cls : 'roo-radio-set-input',
34394 value : this.value ? this.value : ''
34401 if(this.weight.length){
34402 cfg.cls += ' roo-radio-' + this.weight;
34406 cfg.cls += ' roo-radio-set-inline';
34410 ['xs','sm','md','lg'].map(function(size){
34411 if (settings[size]) {
34412 cfg.cls += ' col-' + size + '-' + settings[size];
34420 initEvents : function()
34422 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34423 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34425 if(!this.fieldLabel.length){
34426 this.labelEl.hide();
34429 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34430 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34432 this.indicator = this.indicatorEl();
34434 if(this.indicator){
34435 this.indicator.addClass('invisible');
34438 this.originalValue = this.getValue();
34442 inputEl: function ()
34444 return this.el.select('.roo-radio-set-input', true).first();
34447 getChildContainer : function()
34449 return this.itemsEl;
34452 register : function(item)
34454 this.radioes.push(item);
34458 validate : function()
34460 if(this.getVisibilityEl().hasClass('hidden')){
34466 Roo.each(this.radioes, function(i){
34475 if(this.allowBlank) {
34479 if(this.disabled || valid){
34484 this.markInvalid();
34489 markValid : function()
34491 if(this.labelEl.isVisible(true) && this.indicatorEl()){
34492 this.indicatorEl().removeClass('visible');
34493 this.indicatorEl().addClass('invisible');
34497 if (Roo.bootstrap.version == 3) {
34498 this.el.removeClass([this.invalidClass, this.validClass]);
34499 this.el.addClass(this.validClass);
34501 this.el.removeClass(['is-invalid','is-valid']);
34502 this.el.addClass(['is-valid']);
34504 this.fireEvent('valid', this);
34507 markInvalid : function(msg)
34509 if(this.allowBlank || this.disabled){
34513 if(this.labelEl.isVisible(true) && this.indicatorEl()){
34514 this.indicatorEl().removeClass('invisible');
34515 this.indicatorEl().addClass('visible');
34517 if (Roo.bootstrap.version == 3) {
34518 this.el.removeClass([this.invalidClass, this.validClass]);
34519 this.el.addClass(this.invalidClass);
34521 this.el.removeClass(['is-invalid','is-valid']);
34522 this.el.addClass(['is-invalid']);
34525 this.fireEvent('invalid', this, msg);
34529 setValue : function(v, suppressEvent)
34531 if(this.value === v){
34538 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34541 Roo.each(this.radioes, function(i){
34543 i.el.removeClass('checked');
34546 Roo.each(this.radioes, function(i){
34548 if(i.value === v || i.value.toString() === v.toString()){
34550 i.el.addClass('checked');
34552 if(suppressEvent !== true){
34553 this.fireEvent('check', this, i);
34564 clearInvalid : function(){
34566 if(!this.el || this.preventMark){
34570 this.el.removeClass([this.invalidClass]);
34572 this.fireEvent('valid', this);
34577 Roo.apply(Roo.bootstrap.RadioSet, {
34581 register : function(set)
34583 this.groups[set.name] = set;
34586 get: function(name)
34588 if (typeof(this.groups[name]) == 'undefined') {
34592 return this.groups[name] ;
34598 * Ext JS Library 1.1.1
34599 * Copyright(c) 2006-2007, Ext JS, LLC.
34601 * Originally Released Under LGPL - original licence link has changed is not relivant.
34604 * <script type="text/javascript">
34609 * @class Roo.bootstrap.SplitBar
34610 * @extends Roo.util.Observable
34611 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34615 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34616 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34617 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34618 split.minSize = 100;
34619 split.maxSize = 600;
34620 split.animate = true;
34621 split.on('moved', splitterMoved);
34624 * Create a new SplitBar
34625 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34626 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34627 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34628 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34629 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34630 position of the SplitBar).
34632 Roo.bootstrap.SplitBar = function(cfg){
34637 // dragElement : elm
34638 // resizingElement: el,
34640 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34641 // placement : Roo.bootstrap.SplitBar.LEFT ,
34642 // existingProxy ???
34645 this.el = Roo.get(cfg.dragElement, true);
34646 this.el.dom.unselectable = "on";
34648 this.resizingEl = Roo.get(cfg.resizingElement, true);
34652 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34653 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34656 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34659 * The minimum size of the resizing element. (Defaults to 0)
34665 * The maximum size of the resizing element. (Defaults to 2000)
34668 this.maxSize = 2000;
34671 * Whether to animate the transition to the new size
34674 this.animate = false;
34677 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34680 this.useShim = false;
34685 if(!cfg.existingProxy){
34687 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34689 this.proxy = Roo.get(cfg.existingProxy).dom;
34692 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34695 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34698 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34701 this.dragSpecs = {};
34704 * @private The adapter to use to positon and resize elements
34706 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34707 this.adapter.init(this);
34709 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34711 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34712 this.el.addClass("roo-splitbar-h");
34715 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34716 this.el.addClass("roo-splitbar-v");
34722 * Fires when the splitter is moved (alias for {@link #event-moved})
34723 * @param {Roo.bootstrap.SplitBar} this
34724 * @param {Number} newSize the new width or height
34729 * Fires when the splitter is moved
34730 * @param {Roo.bootstrap.SplitBar} this
34731 * @param {Number} newSize the new width or height
34735 * @event beforeresize
34736 * Fires before the splitter is dragged
34737 * @param {Roo.bootstrap.SplitBar} this
34739 "beforeresize" : true,
34741 "beforeapply" : true
34744 Roo.util.Observable.call(this);
34747 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34748 onStartProxyDrag : function(x, y){
34749 this.fireEvent("beforeresize", this);
34751 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34753 o.enableDisplayMode("block");
34754 // all splitbars share the same overlay
34755 Roo.bootstrap.SplitBar.prototype.overlay = o;
34757 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34758 this.overlay.show();
34759 Roo.get(this.proxy).setDisplayed("block");
34760 var size = this.adapter.getElementSize(this);
34761 this.activeMinSize = this.getMinimumSize();;
34762 this.activeMaxSize = this.getMaximumSize();;
34763 var c1 = size - this.activeMinSize;
34764 var c2 = Math.max(this.activeMaxSize - size, 0);
34765 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34766 this.dd.resetConstraints();
34767 this.dd.setXConstraint(
34768 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34769 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34771 this.dd.setYConstraint(0, 0);
34773 this.dd.resetConstraints();
34774 this.dd.setXConstraint(0, 0);
34775 this.dd.setYConstraint(
34776 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34777 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34780 this.dragSpecs.startSize = size;
34781 this.dragSpecs.startPoint = [x, y];
34782 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34786 * @private Called after the drag operation by the DDProxy
34788 onEndProxyDrag : function(e){
34789 Roo.get(this.proxy).setDisplayed(false);
34790 var endPoint = Roo.lib.Event.getXY(e);
34792 this.overlay.hide();
34795 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34796 newSize = this.dragSpecs.startSize +
34797 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34798 endPoint[0] - this.dragSpecs.startPoint[0] :
34799 this.dragSpecs.startPoint[0] - endPoint[0]
34802 newSize = this.dragSpecs.startSize +
34803 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34804 endPoint[1] - this.dragSpecs.startPoint[1] :
34805 this.dragSpecs.startPoint[1] - endPoint[1]
34808 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34809 if(newSize != this.dragSpecs.startSize){
34810 if(this.fireEvent('beforeapply', this, newSize) !== false){
34811 this.adapter.setElementSize(this, newSize);
34812 this.fireEvent("moved", this, newSize);
34813 this.fireEvent("resize", this, newSize);
34819 * Get the adapter this SplitBar uses
34820 * @return The adapter object
34822 getAdapter : function(){
34823 return this.adapter;
34827 * Set the adapter this SplitBar uses
34828 * @param {Object} adapter A SplitBar adapter object
34830 setAdapter : function(adapter){
34831 this.adapter = adapter;
34832 this.adapter.init(this);
34836 * Gets the minimum size for the resizing element
34837 * @return {Number} The minimum size
34839 getMinimumSize : function(){
34840 return this.minSize;
34844 * Sets the minimum size for the resizing element
34845 * @param {Number} minSize The minimum size
34847 setMinimumSize : function(minSize){
34848 this.minSize = minSize;
34852 * Gets the maximum size for the resizing element
34853 * @return {Number} The maximum size
34855 getMaximumSize : function(){
34856 return this.maxSize;
34860 * Sets the maximum size for the resizing element
34861 * @param {Number} maxSize The maximum size
34863 setMaximumSize : function(maxSize){
34864 this.maxSize = maxSize;
34868 * Sets the initialize size for the resizing element
34869 * @param {Number} size The initial size
34871 setCurrentSize : function(size){
34872 var oldAnimate = this.animate;
34873 this.animate = false;
34874 this.adapter.setElementSize(this, size);
34875 this.animate = oldAnimate;
34879 * Destroy this splitbar.
34880 * @param {Boolean} removeEl True to remove the element
34882 destroy : function(removeEl){
34884 this.shim.remove();
34887 this.proxy.parentNode.removeChild(this.proxy);
34895 * @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.
34897 Roo.bootstrap.SplitBar.createProxy = function(dir){
34898 var proxy = new Roo.Element(document.createElement("div"));
34899 proxy.unselectable();
34900 var cls = 'roo-splitbar-proxy';
34901 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34902 document.body.appendChild(proxy.dom);
34907 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34908 * Default Adapter. It assumes the splitter and resizing element are not positioned
34909 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34911 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34914 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34915 // do nothing for now
34916 init : function(s){
34920 * Called before drag operations to get the current size of the resizing element.
34921 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34923 getElementSize : function(s){
34924 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34925 return s.resizingEl.getWidth();
34927 return s.resizingEl.getHeight();
34932 * Called after drag operations to set the size of the resizing element.
34933 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34934 * @param {Number} newSize The new size to set
34935 * @param {Function} onComplete A function to be invoked when resizing is complete
34937 setElementSize : function(s, newSize, onComplete){
34938 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34940 s.resizingEl.setWidth(newSize);
34942 onComplete(s, newSize);
34945 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34950 s.resizingEl.setHeight(newSize);
34952 onComplete(s, newSize);
34955 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34962 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34963 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34964 * Adapter that moves the splitter element to align with the resized sizing element.
34965 * Used with an absolute positioned SplitBar.
34966 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34967 * document.body, make sure you assign an id to the body element.
34969 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34970 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34971 this.container = Roo.get(container);
34974 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34975 init : function(s){
34976 this.basic.init(s);
34979 getElementSize : function(s){
34980 return this.basic.getElementSize(s);
34983 setElementSize : function(s, newSize, onComplete){
34984 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34987 moveSplitter : function(s){
34988 var yes = Roo.bootstrap.SplitBar;
34989 switch(s.placement){
34991 s.el.setX(s.resizingEl.getRight());
34994 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34997 s.el.setY(s.resizingEl.getBottom());
35000 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
35007 * Orientation constant - Create a vertical SplitBar
35011 Roo.bootstrap.SplitBar.VERTICAL = 1;
35014 * Orientation constant - Create a horizontal SplitBar
35018 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35021 * Placement constant - The resizing element is to the left of the splitter element
35025 Roo.bootstrap.SplitBar.LEFT = 1;
35028 * Placement constant - The resizing element is to the right of the splitter element
35032 Roo.bootstrap.SplitBar.RIGHT = 2;
35035 * Placement constant - The resizing element is positioned above the splitter element
35039 Roo.bootstrap.SplitBar.TOP = 3;
35042 * Placement constant - The resizing element is positioned under splitter element
35046 Roo.bootstrap.SplitBar.BOTTOM = 4;
35047 Roo.namespace("Roo.bootstrap.layout");/*
35049 * Ext JS Library 1.1.1
35050 * Copyright(c) 2006-2007, Ext JS, LLC.
35052 * Originally Released Under LGPL - original licence link has changed is not relivant.
35055 * <script type="text/javascript">
35059 * @class Roo.bootstrap.layout.Manager
35060 * @extends Roo.bootstrap.Component
35061 * Base class for layout managers.
35063 Roo.bootstrap.layout.Manager = function(config)
35065 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
35071 /** false to disable window resize monitoring @type Boolean */
35072 this.monitorWindowResize = true;
35077 * Fires when a layout is performed.
35078 * @param {Roo.LayoutManager} this
35082 * @event regionresized
35083 * Fires when the user resizes a region.
35084 * @param {Roo.LayoutRegion} region The resized region
35085 * @param {Number} newSize The new size (width for east/west, height for north/south)
35087 "regionresized" : true,
35089 * @event regioncollapsed
35090 * Fires when a region is collapsed.
35091 * @param {Roo.LayoutRegion} region The collapsed region
35093 "regioncollapsed" : true,
35095 * @event regionexpanded
35096 * Fires when a region is expanded.
35097 * @param {Roo.LayoutRegion} region The expanded region
35099 "regionexpanded" : true
35101 this.updating = false;
35104 this.el = Roo.get(config.el);
35110 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35115 monitorWindowResize : true,
35121 onRender : function(ct, position)
35124 this.el = Roo.get(ct);
35127 //this.fireEvent('render',this);
35131 initEvents: function()
35135 // ie scrollbar fix
35136 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35137 document.body.scroll = "no";
35138 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35139 this.el.position('relative');
35141 this.id = this.el.id;
35142 this.el.addClass("roo-layout-container");
35143 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35144 if(this.el.dom != document.body ) {
35145 this.el.on('resize', this.layout,this);
35146 this.el.on('show', this.layout,this);
35152 * Returns true if this layout is currently being updated
35153 * @return {Boolean}
35155 isUpdating : function(){
35156 return this.updating;
35160 * Suspend the LayoutManager from doing auto-layouts while
35161 * making multiple add or remove calls
35163 beginUpdate : function(){
35164 this.updating = true;
35168 * Restore auto-layouts and optionally disable the manager from performing a layout
35169 * @param {Boolean} noLayout true to disable a layout update
35171 endUpdate : function(noLayout){
35172 this.updating = false;
35178 layout: function(){
35182 onRegionResized : function(region, newSize){
35183 this.fireEvent("regionresized", region, newSize);
35187 onRegionCollapsed : function(region){
35188 this.fireEvent("regioncollapsed", region);
35191 onRegionExpanded : function(region){
35192 this.fireEvent("regionexpanded", region);
35196 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35197 * performs box-model adjustments.
35198 * @return {Object} The size as an object {width: (the width), height: (the height)}
35200 getViewSize : function()
35203 if(this.el.dom != document.body){
35204 size = this.el.getSize();
35206 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35208 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35209 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35214 * Returns the Element this layout is bound to.
35215 * @return {Roo.Element}
35217 getEl : function(){
35222 * Returns the specified region.
35223 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35224 * @return {Roo.LayoutRegion}
35226 getRegion : function(target){
35227 return this.regions[target.toLowerCase()];
35230 onWindowResize : function(){
35231 if(this.monitorWindowResize){
35238 * Ext JS Library 1.1.1
35239 * Copyright(c) 2006-2007, Ext JS, LLC.
35241 * Originally Released Under LGPL - original licence link has changed is not relivant.
35244 * <script type="text/javascript">
35247 * @class Roo.bootstrap.layout.Border
35248 * @extends Roo.bootstrap.layout.Manager
35249 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35250 * please see: examples/bootstrap/nested.html<br><br>
35252 <b>The container the layout is rendered into can be either the body element or any other element.
35253 If it is not the body element, the container needs to either be an absolute positioned element,
35254 or you will need to add "position:relative" to the css of the container. You will also need to specify
35255 the container size if it is not the body element.</b>
35258 * Create a new Border
35259 * @param {Object} config Configuration options
35261 Roo.bootstrap.layout.Border = function(config){
35262 config = config || {};
35263 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35267 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35268 if(config[region]){
35269 config[region].region = region;
35270 this.addRegion(config[region]);
35276 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
35278 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35280 parent : false, // this might point to a 'nest' or a ???
35283 * Creates and adds a new region if it doesn't already exist.
35284 * @param {String} target The target region key (north, south, east, west or center).
35285 * @param {Object} config The regions config object
35286 * @return {BorderLayoutRegion} The new region
35288 addRegion : function(config)
35290 if(!this.regions[config.region]){
35291 var r = this.factory(config);
35292 this.bindRegion(r);
35294 return this.regions[config.region];
35298 bindRegion : function(r){
35299 this.regions[r.config.region] = r;
35301 r.on("visibilitychange", this.layout, this);
35302 r.on("paneladded", this.layout, this);
35303 r.on("panelremoved", this.layout, this);
35304 r.on("invalidated", this.layout, this);
35305 r.on("resized", this.onRegionResized, this);
35306 r.on("collapsed", this.onRegionCollapsed, this);
35307 r.on("expanded", this.onRegionExpanded, this);
35311 * Performs a layout update.
35313 layout : function()
35315 if(this.updating) {
35319 // render all the rebions if they have not been done alreayd?
35320 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35321 if(this.regions[region] && !this.regions[region].bodyEl){
35322 this.regions[region].onRender(this.el)
35326 var size = this.getViewSize();
35327 var w = size.width;
35328 var h = size.height;
35333 //var x = 0, y = 0;
35335 var rs = this.regions;
35336 var north = rs["north"];
35337 var south = rs["south"];
35338 var west = rs["west"];
35339 var east = rs["east"];
35340 var center = rs["center"];
35341 //if(this.hideOnLayout){ // not supported anymore
35342 //c.el.setStyle("display", "none");
35344 if(north && north.isVisible()){
35345 var b = north.getBox();
35346 var m = north.getMargins();
35347 b.width = w - (m.left+m.right);
35350 centerY = b.height + b.y + m.bottom;
35351 centerH -= centerY;
35352 north.updateBox(this.safeBox(b));
35354 if(south && south.isVisible()){
35355 var b = south.getBox();
35356 var m = south.getMargins();
35357 b.width = w - (m.left+m.right);
35359 var totalHeight = (b.height + m.top + m.bottom);
35360 b.y = h - totalHeight + m.top;
35361 centerH -= totalHeight;
35362 south.updateBox(this.safeBox(b));
35364 if(west && west.isVisible()){
35365 var b = west.getBox();
35366 var m = west.getMargins();
35367 b.height = centerH - (m.top+m.bottom);
35369 b.y = centerY + m.top;
35370 var totalWidth = (b.width + m.left + m.right);
35371 centerX += totalWidth;
35372 centerW -= totalWidth;
35373 west.updateBox(this.safeBox(b));
35375 if(east && east.isVisible()){
35376 var b = east.getBox();
35377 var m = east.getMargins();
35378 b.height = centerH - (m.top+m.bottom);
35379 var totalWidth = (b.width + m.left + m.right);
35380 b.x = w - totalWidth + m.left;
35381 b.y = centerY + m.top;
35382 centerW -= totalWidth;
35383 east.updateBox(this.safeBox(b));
35386 var m = center.getMargins();
35388 x: centerX + m.left,
35389 y: centerY + m.top,
35390 width: centerW - (m.left+m.right),
35391 height: centerH - (m.top+m.bottom)
35393 //if(this.hideOnLayout){
35394 //center.el.setStyle("display", "block");
35396 center.updateBox(this.safeBox(centerBox));
35399 this.fireEvent("layout", this);
35403 safeBox : function(box){
35404 box.width = Math.max(0, box.width);
35405 box.height = Math.max(0, box.height);
35410 * Adds a ContentPanel (or subclass) to this layout.
35411 * @param {String} target The target region key (north, south, east, west or center).
35412 * @param {Roo.ContentPanel} panel The panel to add
35413 * @return {Roo.ContentPanel} The added panel
35415 add : function(target, panel){
35417 target = target.toLowerCase();
35418 return this.regions[target].add(panel);
35422 * Remove a ContentPanel (or subclass) to this layout.
35423 * @param {String} target The target region key (north, south, east, west or center).
35424 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35425 * @return {Roo.ContentPanel} The removed panel
35427 remove : function(target, panel){
35428 target = target.toLowerCase();
35429 return this.regions[target].remove(panel);
35433 * Searches all regions for a panel with the specified id
35434 * @param {String} panelId
35435 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35437 findPanel : function(panelId){
35438 var rs = this.regions;
35439 for(var target in rs){
35440 if(typeof rs[target] != "function"){
35441 var p = rs[target].getPanel(panelId);
35451 * Searches all regions for a panel with the specified id and activates (shows) it.
35452 * @param {String/ContentPanel} panelId The panels id or the panel itself
35453 * @return {Roo.ContentPanel} The shown panel or null
35455 showPanel : function(panelId) {
35456 var rs = this.regions;
35457 for(var target in rs){
35458 var r = rs[target];
35459 if(typeof r != "function"){
35460 if(r.hasPanel(panelId)){
35461 return r.showPanel(panelId);
35469 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35470 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35473 restoreState : function(provider){
35475 provider = Roo.state.Manager;
35477 var sm = new Roo.LayoutStateManager();
35478 sm.init(this, provider);
35484 * Adds a xtype elements to the layout.
35488 xtype : 'ContentPanel',
35495 xtype : 'NestedLayoutPanel',
35501 items : [ ... list of content panels or nested layout panels.. ]
35505 * @param {Object} cfg Xtype definition of item to add.
35507 addxtype : function(cfg)
35509 // basically accepts a pannel...
35510 // can accept a layout region..!?!?
35511 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35514 // theory? children can only be panels??
35516 //if (!cfg.xtype.match(/Panel$/)) {
35521 if (typeof(cfg.region) == 'undefined') {
35522 Roo.log("Failed to add Panel, region was not set");
35526 var region = cfg.region;
35532 xitems = cfg.items;
35537 if ( region == 'center') {
35538 Roo.log("Center: " + cfg.title);
35544 case 'Content': // ContentPanel (el, cfg)
35545 case 'Scroll': // ContentPanel (el, cfg)
35547 cfg.autoCreate = cfg.autoCreate || true;
35548 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35550 // var el = this.el.createChild();
35551 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35554 this.add(region, ret);
35558 case 'TreePanel': // our new panel!
35559 cfg.el = this.el.createChild();
35560 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35561 this.add(region, ret);
35566 // create a new Layout (which is a Border Layout...
35568 var clayout = cfg.layout;
35569 clayout.el = this.el.createChild();
35570 clayout.items = clayout.items || [];
35574 // replace this exitems with the clayout ones..
35575 xitems = clayout.items;
35577 // force background off if it's in center...
35578 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35579 cfg.background = false;
35581 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35584 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35585 //console.log('adding nested layout panel ' + cfg.toSource());
35586 this.add(region, ret);
35587 nb = {}; /// find first...
35592 // needs grid and region
35594 //var el = this.getRegion(region).el.createChild();
35596 *var el = this.el.createChild();
35597 // create the grid first...
35598 cfg.grid.container = el;
35599 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35602 if (region == 'center' && this.active ) {
35603 cfg.background = false;
35606 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35608 this.add(region, ret);
35610 if (cfg.background) {
35611 // render grid on panel activation (if panel background)
35612 ret.on('activate', function(gp) {
35613 if (!gp.grid.rendered) {
35614 // gp.grid.render(el);
35618 // cfg.grid.render(el);
35624 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35625 // it was the old xcomponent building that caused this before.
35626 // espeically if border is the top element in the tree.
35636 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35638 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35639 this.add(region, ret);
35643 throw "Can not add '" + cfg.xtype + "' to Border";
35649 this.beginUpdate();
35653 Roo.each(xitems, function(i) {
35654 region = nb && i.region ? i.region : false;
35656 var add = ret.addxtype(i);
35659 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35660 if (!i.background) {
35661 abn[region] = nb[region] ;
35668 // make the last non-background panel active..
35669 //if (nb) { Roo.log(abn); }
35672 for(var r in abn) {
35673 region = this.getRegion(r);
35675 // tried using nb[r], but it does not work..
35677 region.showPanel(abn[r]);
35688 factory : function(cfg)
35691 var validRegions = Roo.bootstrap.layout.Border.regions;
35693 var target = cfg.region;
35696 var r = Roo.bootstrap.layout;
35700 return new r.North(cfg);
35702 return new r.South(cfg);
35704 return new r.East(cfg);
35706 return new r.West(cfg);
35708 return new r.Center(cfg);
35710 throw 'Layout region "'+target+'" not supported.';
35717 * Ext JS Library 1.1.1
35718 * Copyright(c) 2006-2007, Ext JS, LLC.
35720 * Originally Released Under LGPL - original licence link has changed is not relivant.
35723 * <script type="text/javascript">
35727 * @class Roo.bootstrap.layout.Basic
35728 * @extends Roo.util.Observable
35729 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35730 * and does not have a titlebar, tabs or any other features. All it does is size and position
35731 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35732 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35733 * @cfg {string} region the region that it inhabits..
35734 * @cfg {bool} skipConfig skip config?
35738 Roo.bootstrap.layout.Basic = function(config){
35740 this.mgr = config.mgr;
35742 this.position = config.region;
35744 var skipConfig = config.skipConfig;
35748 * @scope Roo.BasicLayoutRegion
35752 * @event beforeremove
35753 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35754 * @param {Roo.LayoutRegion} this
35755 * @param {Roo.ContentPanel} panel The panel
35756 * @param {Object} e The cancel event object
35758 "beforeremove" : true,
35760 * @event invalidated
35761 * Fires when the layout for this region is changed.
35762 * @param {Roo.LayoutRegion} this
35764 "invalidated" : true,
35766 * @event visibilitychange
35767 * Fires when this region is shown or hidden
35768 * @param {Roo.LayoutRegion} this
35769 * @param {Boolean} visibility true or false
35771 "visibilitychange" : true,
35773 * @event paneladded
35774 * Fires when a panel is added.
35775 * @param {Roo.LayoutRegion} this
35776 * @param {Roo.ContentPanel} panel The panel
35778 "paneladded" : true,
35780 * @event panelremoved
35781 * Fires when a panel is removed.
35782 * @param {Roo.LayoutRegion} this
35783 * @param {Roo.ContentPanel} panel The panel
35785 "panelremoved" : true,
35787 * @event beforecollapse
35788 * Fires when this region before collapse.
35789 * @param {Roo.LayoutRegion} this
35791 "beforecollapse" : true,
35794 * Fires when this region is collapsed.
35795 * @param {Roo.LayoutRegion} this
35797 "collapsed" : true,
35800 * Fires when this region is expanded.
35801 * @param {Roo.LayoutRegion} this
35806 * Fires when this region is slid into view.
35807 * @param {Roo.LayoutRegion} this
35809 "slideshow" : true,
35812 * Fires when this region slides out of view.
35813 * @param {Roo.LayoutRegion} this
35815 "slidehide" : true,
35817 * @event panelactivated
35818 * Fires when a panel is activated.
35819 * @param {Roo.LayoutRegion} this
35820 * @param {Roo.ContentPanel} panel The activated panel
35822 "panelactivated" : true,
35825 * Fires when the user resizes this region.
35826 * @param {Roo.LayoutRegion} this
35827 * @param {Number} newSize The new size (width for east/west, height for north/south)
35831 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35832 this.panels = new Roo.util.MixedCollection();
35833 this.panels.getKey = this.getPanelId.createDelegate(this);
35835 this.activePanel = null;
35836 // ensure listeners are added...
35838 if (config.listeners || config.events) {
35839 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35840 listeners : config.listeners || {},
35841 events : config.events || {}
35845 if(skipConfig !== true){
35846 this.applyConfig(config);
35850 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35852 getPanelId : function(p){
35856 applyConfig : function(config){
35857 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35858 this.config = config;
35863 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35864 * the width, for horizontal (north, south) the height.
35865 * @param {Number} newSize The new width or height
35867 resizeTo : function(newSize){
35868 var el = this.el ? this.el :
35869 (this.activePanel ? this.activePanel.getEl() : null);
35871 switch(this.position){
35874 el.setWidth(newSize);
35875 this.fireEvent("resized", this, newSize);
35879 el.setHeight(newSize);
35880 this.fireEvent("resized", this, newSize);
35886 getBox : function(){
35887 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35890 getMargins : function(){
35891 return this.margins;
35894 updateBox : function(box){
35896 var el = this.activePanel.getEl();
35897 el.dom.style.left = box.x + "px";
35898 el.dom.style.top = box.y + "px";
35899 this.activePanel.setSize(box.width, box.height);
35903 * Returns the container element for this region.
35904 * @return {Roo.Element}
35906 getEl : function(){
35907 return this.activePanel;
35911 * Returns true if this region is currently visible.
35912 * @return {Boolean}
35914 isVisible : function(){
35915 return this.activePanel ? true : false;
35918 setActivePanel : function(panel){
35919 panel = this.getPanel(panel);
35920 if(this.activePanel && this.activePanel != panel){
35921 this.activePanel.setActiveState(false);
35922 this.activePanel.getEl().setLeftTop(-10000,-10000);
35924 this.activePanel = panel;
35925 panel.setActiveState(true);
35927 panel.setSize(this.box.width, this.box.height);
35929 this.fireEvent("panelactivated", this, panel);
35930 this.fireEvent("invalidated");
35934 * Show the specified panel.
35935 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35936 * @return {Roo.ContentPanel} The shown panel or null
35938 showPanel : function(panel){
35939 panel = this.getPanel(panel);
35941 this.setActivePanel(panel);
35947 * Get the active panel for this region.
35948 * @return {Roo.ContentPanel} The active panel or null
35950 getActivePanel : function(){
35951 return this.activePanel;
35955 * Add the passed ContentPanel(s)
35956 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35957 * @return {Roo.ContentPanel} The panel added (if only one was added)
35959 add : function(panel){
35960 if(arguments.length > 1){
35961 for(var i = 0, len = arguments.length; i < len; i++) {
35962 this.add(arguments[i]);
35966 if(this.hasPanel(panel)){
35967 this.showPanel(panel);
35970 var el = panel.getEl();
35971 if(el.dom.parentNode != this.mgr.el.dom){
35972 this.mgr.el.dom.appendChild(el.dom);
35974 if(panel.setRegion){
35975 panel.setRegion(this);
35977 this.panels.add(panel);
35978 el.setStyle("position", "absolute");
35979 if(!panel.background){
35980 this.setActivePanel(panel);
35981 if(this.config.initialSize && this.panels.getCount()==1){
35982 this.resizeTo(this.config.initialSize);
35985 this.fireEvent("paneladded", this, panel);
35990 * Returns true if the panel is in this region.
35991 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35992 * @return {Boolean}
35994 hasPanel : function(panel){
35995 if(typeof panel == "object"){ // must be panel obj
35996 panel = panel.getId();
35998 return this.getPanel(panel) ? true : false;
36002 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36003 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36004 * @param {Boolean} preservePanel Overrides the config preservePanel option
36005 * @return {Roo.ContentPanel} The panel that was removed
36007 remove : function(panel, preservePanel){
36008 panel = this.getPanel(panel);
36013 this.fireEvent("beforeremove", this, panel, e);
36014 if(e.cancel === true){
36017 var panelId = panel.getId();
36018 this.panels.removeKey(panelId);
36023 * Returns the panel specified or null if it's not in this region.
36024 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36025 * @return {Roo.ContentPanel}
36027 getPanel : function(id){
36028 if(typeof id == "object"){ // must be panel obj
36031 return this.panels.get(id);
36035 * Returns this regions position (north/south/east/west/center).
36038 getPosition: function(){
36039 return this.position;
36043 * Ext JS Library 1.1.1
36044 * Copyright(c) 2006-2007, Ext JS, LLC.
36046 * Originally Released Under LGPL - original licence link has changed is not relivant.
36049 * <script type="text/javascript">
36053 * @class Roo.bootstrap.layout.Region
36054 * @extends Roo.bootstrap.layout.Basic
36055 * This class represents a region in a layout manager.
36057 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
36058 * @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})
36059 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
36060 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
36061 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
36062 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
36063 * @cfg {String} title The title for the region (overrides panel titles)
36064 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
36065 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36066 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
36067 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
36068 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
36069 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
36070 * the space available, similar to FireFox 1.5 tabs (defaults to false)
36071 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
36072 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
36073 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
36075 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
36076 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
36077 * @cfg {Boolean} disableTabTips True to disable tab tooltips
36078 * @cfg {Number} width For East/West panels
36079 * @cfg {Number} height For North/South panels
36080 * @cfg {Boolean} split To show the splitter
36081 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
36083 * @cfg {string} cls Extra CSS classes to add to region
36085 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
36086 * @cfg {string} region the region that it inhabits..
36089 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
36090 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
36092 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
36093 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
36094 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
36096 Roo.bootstrap.layout.Region = function(config)
36098 this.applyConfig(config);
36100 var mgr = config.mgr;
36101 var pos = config.region;
36102 config.skipConfig = true;
36103 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36106 this.onRender(mgr.el);
36109 this.visible = true;
36110 this.collapsed = false;
36111 this.unrendered_panels = [];
36114 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36116 position: '', // set by wrapper (eg. north/south etc..)
36117 unrendered_panels : null, // unrendered panels.
36119 tabPosition : false,
36121 mgr: false, // points to 'Border'
36124 createBody : function(){
36125 /** This region's body element
36126 * @type Roo.Element */
36127 this.bodyEl = this.el.createChild({
36129 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36133 onRender: function(ctr, pos)
36135 var dh = Roo.DomHelper;
36136 /** This region's container element
36137 * @type Roo.Element */
36138 this.el = dh.append(ctr.dom, {
36140 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36142 /** This region's title element
36143 * @type Roo.Element */
36145 this.titleEl = dh.append(this.el.dom, {
36147 unselectable: "on",
36148 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36150 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
36151 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36155 this.titleEl.enableDisplayMode();
36156 /** This region's title text element
36157 * @type HTMLElement */
36158 this.titleTextEl = this.titleEl.dom.firstChild;
36159 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36161 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36162 this.closeBtn.enableDisplayMode();
36163 this.closeBtn.on("click", this.closeClicked, this);
36164 this.closeBtn.hide();
36166 this.createBody(this.config);
36167 if(this.config.hideWhenEmpty){
36169 this.on("paneladded", this.validateVisibility, this);
36170 this.on("panelremoved", this.validateVisibility, this);
36172 if(this.autoScroll){
36173 this.bodyEl.setStyle("overflow", "auto");
36175 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36177 //if(c.titlebar !== false){
36178 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36179 this.titleEl.hide();
36181 this.titleEl.show();
36182 if(this.config.title){
36183 this.titleTextEl.innerHTML = this.config.title;
36187 if(this.config.collapsed){
36188 this.collapse(true);
36190 if(this.config.hidden){
36194 if (this.unrendered_panels && this.unrendered_panels.length) {
36195 for (var i =0;i< this.unrendered_panels.length; i++) {
36196 this.add(this.unrendered_panels[i]);
36198 this.unrendered_panels = null;
36204 applyConfig : function(c)
36207 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36208 var dh = Roo.DomHelper;
36209 if(c.titlebar !== false){
36210 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36211 this.collapseBtn.on("click", this.collapse, this);
36212 this.collapseBtn.enableDisplayMode();
36214 if(c.showPin === true || this.showPin){
36215 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36216 this.stickBtn.enableDisplayMode();
36217 this.stickBtn.on("click", this.expand, this);
36218 this.stickBtn.hide();
36223 /** This region's collapsed element
36224 * @type Roo.Element */
36227 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36228 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36231 if(c.floatable !== false){
36232 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36233 this.collapsedEl.on("click", this.collapseClick, this);
36236 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36237 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36238 id: "message", unselectable: "on", style:{"float":"left"}});
36239 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36241 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36242 this.expandBtn.on("click", this.expand, this);
36246 if(this.collapseBtn){
36247 this.collapseBtn.setVisible(c.collapsible == true);
36250 this.cmargins = c.cmargins || this.cmargins ||
36251 (this.position == "west" || this.position == "east" ?
36252 {top: 0, left: 2, right:2, bottom: 0} :
36253 {top: 2, left: 0, right:0, bottom: 2});
36255 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36258 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36260 this.autoScroll = c.autoScroll || false;
36265 this.duration = c.duration || .30;
36266 this.slideDuration = c.slideDuration || .45;
36271 * Returns true if this region is currently visible.
36272 * @return {Boolean}
36274 isVisible : function(){
36275 return this.visible;
36279 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36280 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
36282 //setCollapsedTitle : function(title){
36283 // title = title || " ";
36284 // if(this.collapsedTitleTextEl){
36285 // this.collapsedTitleTextEl.innerHTML = title;
36289 getBox : function(){
36291 // if(!this.collapsed){
36292 b = this.el.getBox(false, true);
36294 // b = this.collapsedEl.getBox(false, true);
36299 getMargins : function(){
36300 return this.margins;
36301 //return this.collapsed ? this.cmargins : this.margins;
36304 highlight : function(){
36305 this.el.addClass("x-layout-panel-dragover");
36308 unhighlight : function(){
36309 this.el.removeClass("x-layout-panel-dragover");
36312 updateBox : function(box)
36314 if (!this.bodyEl) {
36315 return; // not rendered yet..
36319 if(!this.collapsed){
36320 this.el.dom.style.left = box.x + "px";
36321 this.el.dom.style.top = box.y + "px";
36322 this.updateBody(box.width, box.height);
36324 this.collapsedEl.dom.style.left = box.x + "px";
36325 this.collapsedEl.dom.style.top = box.y + "px";
36326 this.collapsedEl.setSize(box.width, box.height);
36329 this.tabs.autoSizeTabs();
36333 updateBody : function(w, h)
36336 this.el.setWidth(w);
36337 w -= this.el.getBorderWidth("rl");
36338 if(this.config.adjustments){
36339 w += this.config.adjustments[0];
36342 if(h !== null && h > 0){
36343 this.el.setHeight(h);
36344 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36345 h -= this.el.getBorderWidth("tb");
36346 if(this.config.adjustments){
36347 h += this.config.adjustments[1];
36349 this.bodyEl.setHeight(h);
36351 h = this.tabs.syncHeight(h);
36354 if(this.panelSize){
36355 w = w !== null ? w : this.panelSize.width;
36356 h = h !== null ? h : this.panelSize.height;
36358 if(this.activePanel){
36359 var el = this.activePanel.getEl();
36360 w = w !== null ? w : el.getWidth();
36361 h = h !== null ? h : el.getHeight();
36362 this.panelSize = {width: w, height: h};
36363 this.activePanel.setSize(w, h);
36365 if(Roo.isIE && this.tabs){
36366 this.tabs.el.repaint();
36371 * Returns the container element for this region.
36372 * @return {Roo.Element}
36374 getEl : function(){
36379 * Hides this region.
36382 //if(!this.collapsed){
36383 this.el.dom.style.left = "-2000px";
36386 // this.collapsedEl.dom.style.left = "-2000px";
36387 // this.collapsedEl.hide();
36389 this.visible = false;
36390 this.fireEvent("visibilitychange", this, false);
36394 * Shows this region if it was previously hidden.
36397 //if(!this.collapsed){
36400 // this.collapsedEl.show();
36402 this.visible = true;
36403 this.fireEvent("visibilitychange", this, true);
36406 closeClicked : function(){
36407 if(this.activePanel){
36408 this.remove(this.activePanel);
36412 collapseClick : function(e){
36414 e.stopPropagation();
36417 e.stopPropagation();
36423 * Collapses this region.
36424 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36427 collapse : function(skipAnim, skipCheck = false){
36428 if(this.collapsed) {
36432 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36434 this.collapsed = true;
36436 this.split.el.hide();
36438 if(this.config.animate && skipAnim !== true){
36439 this.fireEvent("invalidated", this);
36440 this.animateCollapse();
36442 this.el.setLocation(-20000,-20000);
36444 this.collapsedEl.show();
36445 this.fireEvent("collapsed", this);
36446 this.fireEvent("invalidated", this);
36452 animateCollapse : function(){
36457 * Expands this region if it was previously collapsed.
36458 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36459 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36462 expand : function(e, skipAnim){
36464 e.stopPropagation();
36466 if(!this.collapsed || this.el.hasActiveFx()) {
36470 this.afterSlideIn();
36473 this.collapsed = false;
36474 if(this.config.animate && skipAnim !== true){
36475 this.animateExpand();
36479 this.split.el.show();
36481 this.collapsedEl.setLocation(-2000,-2000);
36482 this.collapsedEl.hide();
36483 this.fireEvent("invalidated", this);
36484 this.fireEvent("expanded", this);
36488 animateExpand : function(){
36492 initTabs : function()
36494 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36496 var ts = new Roo.bootstrap.panel.Tabs({
36497 el: this.bodyEl.dom,
36499 tabPosition: this.tabPosition ? this.tabPosition : 'top',
36500 disableTooltips: this.config.disableTabTips,
36501 toolbar : this.config.toolbar
36504 if(this.config.hideTabs){
36505 ts.stripWrap.setDisplayed(false);
36508 ts.resizeTabs = this.config.resizeTabs === true;
36509 ts.minTabWidth = this.config.minTabWidth || 40;
36510 ts.maxTabWidth = this.config.maxTabWidth || 250;
36511 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36512 ts.monitorResize = false;
36513 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36514 ts.bodyEl.addClass('roo-layout-tabs-body');
36515 this.panels.each(this.initPanelAsTab, this);
36518 initPanelAsTab : function(panel){
36519 var ti = this.tabs.addTab(
36523 this.config.closeOnTab && panel.isClosable(),
36526 if(panel.tabTip !== undefined){
36527 ti.setTooltip(panel.tabTip);
36529 ti.on("activate", function(){
36530 this.setActivePanel(panel);
36533 if(this.config.closeOnTab){
36534 ti.on("beforeclose", function(t, e){
36536 this.remove(panel);
36540 panel.tabItem = ti;
36545 updatePanelTitle : function(panel, title)
36547 if(this.activePanel == panel){
36548 this.updateTitle(title);
36551 var ti = this.tabs.getTab(panel.getEl().id);
36553 if(panel.tabTip !== undefined){
36554 ti.setTooltip(panel.tabTip);
36559 updateTitle : function(title){
36560 if(this.titleTextEl && !this.config.title){
36561 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36565 setActivePanel : function(panel)
36567 panel = this.getPanel(panel);
36568 if(this.activePanel && this.activePanel != panel){
36569 if(this.activePanel.setActiveState(false) === false){
36573 this.activePanel = panel;
36574 panel.setActiveState(true);
36575 if(this.panelSize){
36576 panel.setSize(this.panelSize.width, this.panelSize.height);
36579 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36581 this.updateTitle(panel.getTitle());
36583 this.fireEvent("invalidated", this);
36585 this.fireEvent("panelactivated", this, panel);
36589 * Shows the specified panel.
36590 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36591 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36593 showPanel : function(panel)
36595 panel = this.getPanel(panel);
36598 var tab = this.tabs.getTab(panel.getEl().id);
36599 if(tab.isHidden()){
36600 this.tabs.unhideTab(tab.id);
36604 this.setActivePanel(panel);
36611 * Get the active panel for this region.
36612 * @return {Roo.ContentPanel} The active panel or null
36614 getActivePanel : function(){
36615 return this.activePanel;
36618 validateVisibility : function(){
36619 if(this.panels.getCount() < 1){
36620 this.updateTitle(" ");
36621 this.closeBtn.hide();
36624 if(!this.isVisible()){
36631 * Adds the passed ContentPanel(s) to this region.
36632 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36633 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36635 add : function(panel)
36637 if(arguments.length > 1){
36638 for(var i = 0, len = arguments.length; i < len; i++) {
36639 this.add(arguments[i]);
36644 // if we have not been rendered yet, then we can not really do much of this..
36645 if (!this.bodyEl) {
36646 this.unrendered_panels.push(panel);
36653 if(this.hasPanel(panel)){
36654 this.showPanel(panel);
36657 panel.setRegion(this);
36658 this.panels.add(panel);
36659 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36660 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36661 // and hide them... ???
36662 this.bodyEl.dom.appendChild(panel.getEl().dom);
36663 if(panel.background !== true){
36664 this.setActivePanel(panel);
36666 this.fireEvent("paneladded", this, panel);
36673 this.initPanelAsTab(panel);
36677 if(panel.background !== true){
36678 this.tabs.activate(panel.getEl().id);
36680 this.fireEvent("paneladded", this, panel);
36685 * Hides the tab for the specified panel.
36686 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36688 hidePanel : function(panel){
36689 if(this.tabs && (panel = this.getPanel(panel))){
36690 this.tabs.hideTab(panel.getEl().id);
36695 * Unhides the tab for a previously hidden panel.
36696 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36698 unhidePanel : function(panel){
36699 if(this.tabs && (panel = this.getPanel(panel))){
36700 this.tabs.unhideTab(panel.getEl().id);
36704 clearPanels : function(){
36705 while(this.panels.getCount() > 0){
36706 this.remove(this.panels.first());
36711 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36712 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36713 * @param {Boolean} preservePanel Overrides the config preservePanel option
36714 * @return {Roo.ContentPanel} The panel that was removed
36716 remove : function(panel, preservePanel)
36718 panel = this.getPanel(panel);
36723 this.fireEvent("beforeremove", this, panel, e);
36724 if(e.cancel === true){
36727 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36728 var panelId = panel.getId();
36729 this.panels.removeKey(panelId);
36731 document.body.appendChild(panel.getEl().dom);
36734 this.tabs.removeTab(panel.getEl().id);
36735 }else if (!preservePanel){
36736 this.bodyEl.dom.removeChild(panel.getEl().dom);
36738 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36739 var p = this.panels.first();
36740 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36741 tempEl.appendChild(p.getEl().dom);
36742 this.bodyEl.update("");
36743 this.bodyEl.dom.appendChild(p.getEl().dom);
36745 this.updateTitle(p.getTitle());
36747 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36748 this.setActivePanel(p);
36750 panel.setRegion(null);
36751 if(this.activePanel == panel){
36752 this.activePanel = null;
36754 if(this.config.autoDestroy !== false && preservePanel !== true){
36755 try{panel.destroy();}catch(e){}
36757 this.fireEvent("panelremoved", this, panel);
36762 * Returns the TabPanel component used by this region
36763 * @return {Roo.TabPanel}
36765 getTabs : function(){
36769 createTool : function(parentEl, className){
36770 var btn = Roo.DomHelper.append(parentEl, {
36772 cls: "x-layout-tools-button",
36775 cls: "roo-layout-tools-button-inner " + className,
36779 btn.addClassOnOver("roo-layout-tools-button-over");
36784 * Ext JS Library 1.1.1
36785 * Copyright(c) 2006-2007, Ext JS, LLC.
36787 * Originally Released Under LGPL - original licence link has changed is not relivant.
36790 * <script type="text/javascript">
36796 * @class Roo.SplitLayoutRegion
36797 * @extends Roo.LayoutRegion
36798 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36800 Roo.bootstrap.layout.Split = function(config){
36801 this.cursor = config.cursor;
36802 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36805 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36807 splitTip : "Drag to resize.",
36808 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36809 useSplitTips : false,
36811 applyConfig : function(config){
36812 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36815 onRender : function(ctr,pos) {
36817 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36818 if(!this.config.split){
36823 var splitEl = Roo.DomHelper.append(ctr.dom, {
36825 id: this.el.id + "-split",
36826 cls: "roo-layout-split roo-layout-split-"+this.position,
36829 /** The SplitBar for this region
36830 * @type Roo.SplitBar */
36831 // does not exist yet...
36832 Roo.log([this.position, this.orientation]);
36834 this.split = new Roo.bootstrap.SplitBar({
36835 dragElement : splitEl,
36836 resizingElement: this.el,
36837 orientation : this.orientation
36840 this.split.on("moved", this.onSplitMove, this);
36841 this.split.useShim = this.config.useShim === true;
36842 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36843 if(this.useSplitTips){
36844 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36846 //if(config.collapsible){
36847 // this.split.el.on("dblclick", this.collapse, this);
36850 if(typeof this.config.minSize != "undefined"){
36851 this.split.minSize = this.config.minSize;
36853 if(typeof this.config.maxSize != "undefined"){
36854 this.split.maxSize = this.config.maxSize;
36856 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36857 this.hideSplitter();
36862 getHMaxSize : function(){
36863 var cmax = this.config.maxSize || 10000;
36864 var center = this.mgr.getRegion("center");
36865 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36868 getVMaxSize : function(){
36869 var cmax = this.config.maxSize || 10000;
36870 var center = this.mgr.getRegion("center");
36871 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36874 onSplitMove : function(split, newSize){
36875 this.fireEvent("resized", this, newSize);
36879 * Returns the {@link Roo.SplitBar} for this region.
36880 * @return {Roo.SplitBar}
36882 getSplitBar : function(){
36887 this.hideSplitter();
36888 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36891 hideSplitter : function(){
36893 this.split.el.setLocation(-2000,-2000);
36894 this.split.el.hide();
36900 this.split.el.show();
36902 Roo.bootstrap.layout.Split.superclass.show.call(this);
36905 beforeSlide: function(){
36906 if(Roo.isGecko){// firefox overflow auto bug workaround
36907 this.bodyEl.clip();
36909 this.tabs.bodyEl.clip();
36911 if(this.activePanel){
36912 this.activePanel.getEl().clip();
36914 if(this.activePanel.beforeSlide){
36915 this.activePanel.beforeSlide();
36921 afterSlide : function(){
36922 if(Roo.isGecko){// firefox overflow auto bug workaround
36923 this.bodyEl.unclip();
36925 this.tabs.bodyEl.unclip();
36927 if(this.activePanel){
36928 this.activePanel.getEl().unclip();
36929 if(this.activePanel.afterSlide){
36930 this.activePanel.afterSlide();
36936 initAutoHide : function(){
36937 if(this.autoHide !== false){
36938 if(!this.autoHideHd){
36939 var st = new Roo.util.DelayedTask(this.slideIn, this);
36940 this.autoHideHd = {
36941 "mouseout": function(e){
36942 if(!e.within(this.el, true)){
36946 "mouseover" : function(e){
36952 this.el.on(this.autoHideHd);
36956 clearAutoHide : function(){
36957 if(this.autoHide !== false){
36958 this.el.un("mouseout", this.autoHideHd.mouseout);
36959 this.el.un("mouseover", this.autoHideHd.mouseover);
36963 clearMonitor : function(){
36964 Roo.get(document).un("click", this.slideInIf, this);
36967 // these names are backwards but not changed for compat
36968 slideOut : function(){
36969 if(this.isSlid || this.el.hasActiveFx()){
36972 this.isSlid = true;
36973 if(this.collapseBtn){
36974 this.collapseBtn.hide();
36976 this.closeBtnState = this.closeBtn.getStyle('display');
36977 this.closeBtn.hide();
36979 this.stickBtn.show();
36982 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36983 this.beforeSlide();
36984 this.el.setStyle("z-index", 10001);
36985 this.el.slideIn(this.getSlideAnchor(), {
36986 callback: function(){
36988 this.initAutoHide();
36989 Roo.get(document).on("click", this.slideInIf, this);
36990 this.fireEvent("slideshow", this);
36997 afterSlideIn : function(){
36998 this.clearAutoHide();
36999 this.isSlid = false;
37000 this.clearMonitor();
37001 this.el.setStyle("z-index", "");
37002 if(this.collapseBtn){
37003 this.collapseBtn.show();
37005 this.closeBtn.setStyle('display', this.closeBtnState);
37007 this.stickBtn.hide();
37009 this.fireEvent("slidehide", this);
37012 slideIn : function(cb){
37013 if(!this.isSlid || this.el.hasActiveFx()){
37017 this.isSlid = false;
37018 this.beforeSlide();
37019 this.el.slideOut(this.getSlideAnchor(), {
37020 callback: function(){
37021 this.el.setLeftTop(-10000, -10000);
37023 this.afterSlideIn();
37031 slideInIf : function(e){
37032 if(!e.within(this.el)){
37037 animateCollapse : function(){
37038 this.beforeSlide();
37039 this.el.setStyle("z-index", 20000);
37040 var anchor = this.getSlideAnchor();
37041 this.el.slideOut(anchor, {
37042 callback : function(){
37043 this.el.setStyle("z-index", "");
37044 this.collapsedEl.slideIn(anchor, {duration:.3});
37046 this.el.setLocation(-10000,-10000);
37048 this.fireEvent("collapsed", this);
37055 animateExpand : function(){
37056 this.beforeSlide();
37057 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
37058 this.el.setStyle("z-index", 20000);
37059 this.collapsedEl.hide({
37062 this.el.slideIn(this.getSlideAnchor(), {
37063 callback : function(){
37064 this.el.setStyle("z-index", "");
37067 this.split.el.show();
37069 this.fireEvent("invalidated", this);
37070 this.fireEvent("expanded", this);
37098 getAnchor : function(){
37099 return this.anchors[this.position];
37102 getCollapseAnchor : function(){
37103 return this.canchors[this.position];
37106 getSlideAnchor : function(){
37107 return this.sanchors[this.position];
37110 getAlignAdj : function(){
37111 var cm = this.cmargins;
37112 switch(this.position){
37128 getExpandAdj : function(){
37129 var c = this.collapsedEl, cm = this.cmargins;
37130 switch(this.position){
37132 return [-(cm.right+c.getWidth()+cm.left), 0];
37135 return [cm.right+c.getWidth()+cm.left, 0];
37138 return [0, -(cm.top+cm.bottom+c.getHeight())];
37141 return [0, cm.top+cm.bottom+c.getHeight()];
37147 * Ext JS Library 1.1.1
37148 * Copyright(c) 2006-2007, Ext JS, LLC.
37150 * Originally Released Under LGPL - original licence link has changed is not relivant.
37153 * <script type="text/javascript">
37156 * These classes are private internal classes
37158 Roo.bootstrap.layout.Center = function(config){
37159 config.region = "center";
37160 Roo.bootstrap.layout.Region.call(this, config);
37161 this.visible = true;
37162 this.minWidth = config.minWidth || 20;
37163 this.minHeight = config.minHeight || 20;
37166 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37168 // center panel can't be hidden
37172 // center panel can't be hidden
37175 getMinWidth: function(){
37176 return this.minWidth;
37179 getMinHeight: function(){
37180 return this.minHeight;
37194 Roo.bootstrap.layout.North = function(config)
37196 config.region = 'north';
37197 config.cursor = 'n-resize';
37199 Roo.bootstrap.layout.Split.call(this, config);
37203 this.split.placement = Roo.bootstrap.SplitBar.TOP;
37204 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37205 this.split.el.addClass("roo-layout-split-v");
37207 var size = config.initialSize || config.height;
37208 if(typeof size != "undefined"){
37209 this.el.setHeight(size);
37212 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37214 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37218 getBox : function(){
37219 if(this.collapsed){
37220 return this.collapsedEl.getBox();
37222 var box = this.el.getBox();
37224 box.height += this.split.el.getHeight();
37229 updateBox : function(box){
37230 if(this.split && !this.collapsed){
37231 box.height -= this.split.el.getHeight();
37232 this.split.el.setLeft(box.x);
37233 this.split.el.setTop(box.y+box.height);
37234 this.split.el.setWidth(box.width);
37236 if(this.collapsed){
37237 this.updateBody(box.width, null);
37239 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37247 Roo.bootstrap.layout.South = function(config){
37248 config.region = 'south';
37249 config.cursor = 's-resize';
37250 Roo.bootstrap.layout.Split.call(this, config);
37252 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37253 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37254 this.split.el.addClass("roo-layout-split-v");
37256 var size = config.initialSize || config.height;
37257 if(typeof size != "undefined"){
37258 this.el.setHeight(size);
37262 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37263 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37264 getBox : function(){
37265 if(this.collapsed){
37266 return this.collapsedEl.getBox();
37268 var box = this.el.getBox();
37270 var sh = this.split.el.getHeight();
37277 updateBox : function(box){
37278 if(this.split && !this.collapsed){
37279 var sh = this.split.el.getHeight();
37282 this.split.el.setLeft(box.x);
37283 this.split.el.setTop(box.y-sh);
37284 this.split.el.setWidth(box.width);
37286 if(this.collapsed){
37287 this.updateBody(box.width, null);
37289 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37293 Roo.bootstrap.layout.East = function(config){
37294 config.region = "east";
37295 config.cursor = "e-resize";
37296 Roo.bootstrap.layout.Split.call(this, config);
37298 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37299 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37300 this.split.el.addClass("roo-layout-split-h");
37302 var size = config.initialSize || config.width;
37303 if(typeof size != "undefined"){
37304 this.el.setWidth(size);
37307 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37308 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37309 getBox : function(){
37310 if(this.collapsed){
37311 return this.collapsedEl.getBox();
37313 var box = this.el.getBox();
37315 var sw = this.split.el.getWidth();
37322 updateBox : function(box){
37323 if(this.split && !this.collapsed){
37324 var sw = this.split.el.getWidth();
37326 this.split.el.setLeft(box.x);
37327 this.split.el.setTop(box.y);
37328 this.split.el.setHeight(box.height);
37331 if(this.collapsed){
37332 this.updateBody(null, box.height);
37334 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37338 Roo.bootstrap.layout.West = function(config){
37339 config.region = "west";
37340 config.cursor = "w-resize";
37342 Roo.bootstrap.layout.Split.call(this, config);
37344 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37345 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37346 this.split.el.addClass("roo-layout-split-h");
37350 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37351 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37353 onRender: function(ctr, pos)
37355 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37356 var size = this.config.initialSize || this.config.width;
37357 if(typeof size != "undefined"){
37358 this.el.setWidth(size);
37362 getBox : function(){
37363 if(this.collapsed){
37364 return this.collapsedEl.getBox();
37366 var box = this.el.getBox();
37368 box.width += this.split.el.getWidth();
37373 updateBox : function(box){
37374 if(this.split && !this.collapsed){
37375 var sw = this.split.el.getWidth();
37377 this.split.el.setLeft(box.x+box.width);
37378 this.split.el.setTop(box.y);
37379 this.split.el.setHeight(box.height);
37381 if(this.collapsed){
37382 this.updateBody(null, box.height);
37384 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37386 });Roo.namespace("Roo.bootstrap.panel");/*
37388 * Ext JS Library 1.1.1
37389 * Copyright(c) 2006-2007, Ext JS, LLC.
37391 * Originally Released Under LGPL - original licence link has changed is not relivant.
37394 * <script type="text/javascript">
37397 * @class Roo.ContentPanel
37398 * @extends Roo.util.Observable
37399 * A basic ContentPanel element.
37400 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
37401 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
37402 * @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
37403 * @cfg {Boolean} closable True if the panel can be closed/removed
37404 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
37405 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37406 * @cfg {Toolbar} toolbar A toolbar for this panel
37407 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37408 * @cfg {String} title The title for this panel
37409 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37410 * @cfg {String} url Calls {@link #setUrl} with this value
37411 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37412 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37413 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37414 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37415 * @cfg {Boolean} badges render the badges
37418 * Create a new ContentPanel.
37419 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37420 * @param {String/Object} config A string to set only the title or a config object
37421 * @param {String} content (optional) Set the HTML content for this panel
37422 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37424 Roo.bootstrap.panel.Content = function( config){
37426 this.tpl = config.tpl || false;
37428 var el = config.el;
37429 var content = config.content;
37431 if(config.autoCreate){ // xtype is available if this is called from factory
37434 this.el = Roo.get(el);
37435 if(!this.el && config && config.autoCreate){
37436 if(typeof config.autoCreate == "object"){
37437 if(!config.autoCreate.id){
37438 config.autoCreate.id = config.id||el;
37440 this.el = Roo.DomHelper.append(document.body,
37441 config.autoCreate, true);
37443 var elcfg = { tag: "div",
37444 cls: "roo-layout-inactive-content",
37448 elcfg.html = config.html;
37452 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37455 this.closable = false;
37456 this.loaded = false;
37457 this.active = false;
37460 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37462 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37464 this.wrapEl = this.el; //this.el.wrap();
37466 if (config.toolbar.items) {
37467 ti = config.toolbar.items ;
37468 delete config.toolbar.items ;
37472 this.toolbar.render(this.wrapEl, 'before');
37473 for(var i =0;i < ti.length;i++) {
37474 // Roo.log(['add child', items[i]]);
37475 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37477 this.toolbar.items = nitems;
37478 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37479 delete config.toolbar;
37483 // xtype created footer. - not sure if will work as we normally have to render first..
37484 if (this.footer && !this.footer.el && this.footer.xtype) {
37485 if (!this.wrapEl) {
37486 this.wrapEl = this.el.wrap();
37489 this.footer.container = this.wrapEl.createChild();
37491 this.footer = Roo.factory(this.footer, Roo);
37496 if(typeof config == "string"){
37497 this.title = config;
37499 Roo.apply(this, config);
37503 this.resizeEl = Roo.get(this.resizeEl, true);
37505 this.resizeEl = this.el;
37507 // handle view.xtype
37515 * Fires when this panel is activated.
37516 * @param {Roo.ContentPanel} this
37520 * @event deactivate
37521 * Fires when this panel is activated.
37522 * @param {Roo.ContentPanel} this
37524 "deactivate" : true,
37528 * Fires when this panel is resized if fitToFrame is true.
37529 * @param {Roo.ContentPanel} this
37530 * @param {Number} width The width after any component adjustments
37531 * @param {Number} height The height after any component adjustments
37537 * Fires when this tab is created
37538 * @param {Roo.ContentPanel} this
37549 if(this.autoScroll){
37550 this.resizeEl.setStyle("overflow", "auto");
37552 // fix randome scrolling
37553 //this.el.on('scroll', function() {
37554 // Roo.log('fix random scolling');
37555 // this.scrollTo('top',0);
37558 content = content || this.content;
37560 this.setContent(content);
37562 if(config && config.url){
37563 this.setUrl(this.url, this.params, this.loadOnce);
37568 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37570 if (this.view && typeof(this.view.xtype) != 'undefined') {
37571 this.view.el = this.el.appendChild(document.createElement("div"));
37572 this.view = Roo.factory(this.view);
37573 this.view.render && this.view.render(false, '');
37577 this.fireEvent('render', this);
37580 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37584 setRegion : function(region){
37585 this.region = region;
37586 this.setActiveClass(region && !this.background);
37590 setActiveClass: function(state)
37593 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37594 this.el.setStyle('position','relative');
37596 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37597 this.el.setStyle('position', 'absolute');
37602 * Returns the toolbar for this Panel if one was configured.
37603 * @return {Roo.Toolbar}
37605 getToolbar : function(){
37606 return this.toolbar;
37609 setActiveState : function(active)
37611 this.active = active;
37612 this.setActiveClass(active);
37614 if(this.fireEvent("deactivate", this) === false){
37619 this.fireEvent("activate", this);
37623 * Updates this panel's element
37624 * @param {String} content The new content
37625 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37627 setContent : function(content, loadScripts){
37628 this.el.update(content, loadScripts);
37631 ignoreResize : function(w, h){
37632 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37635 this.lastSize = {width: w, height: h};
37640 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37641 * @return {Roo.UpdateManager} The UpdateManager
37643 getUpdateManager : function(){
37644 return this.el.getUpdateManager();
37647 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37648 * @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:
37651 url: "your-url.php",
37652 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37653 callback: yourFunction,
37654 scope: yourObject, //(optional scope)
37657 text: "Loading...",
37662 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37663 * 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.
37664 * @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}
37665 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37666 * @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.
37667 * @return {Roo.ContentPanel} this
37670 var um = this.el.getUpdateManager();
37671 um.update.apply(um, arguments);
37677 * 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.
37678 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37679 * @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)
37680 * @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)
37681 * @return {Roo.UpdateManager} The UpdateManager
37683 setUrl : function(url, params, loadOnce){
37684 if(this.refreshDelegate){
37685 this.removeListener("activate", this.refreshDelegate);
37687 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37688 this.on("activate", this.refreshDelegate);
37689 return this.el.getUpdateManager();
37692 _handleRefresh : function(url, params, loadOnce){
37693 if(!loadOnce || !this.loaded){
37694 var updater = this.el.getUpdateManager();
37695 updater.update(url, params, this._setLoaded.createDelegate(this));
37699 _setLoaded : function(){
37700 this.loaded = true;
37704 * Returns this panel's id
37707 getId : function(){
37712 * Returns this panel's element - used by regiosn to add.
37713 * @return {Roo.Element}
37715 getEl : function(){
37716 return this.wrapEl || this.el;
37721 adjustForComponents : function(width, height)
37723 //Roo.log('adjustForComponents ');
37724 if(this.resizeEl != this.el){
37725 width -= this.el.getFrameWidth('lr');
37726 height -= this.el.getFrameWidth('tb');
37729 var te = this.toolbar.getEl();
37730 te.setWidth(width);
37731 height -= te.getHeight();
37734 var te = this.footer.getEl();
37735 te.setWidth(width);
37736 height -= te.getHeight();
37740 if(this.adjustments){
37741 width += this.adjustments[0];
37742 height += this.adjustments[1];
37744 return {"width": width, "height": height};
37747 setSize : function(width, height){
37748 if(this.fitToFrame && !this.ignoreResize(width, height)){
37749 if(this.fitContainer && this.resizeEl != this.el){
37750 this.el.setSize(width, height);
37752 var size = this.adjustForComponents(width, height);
37753 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37754 this.fireEvent('resize', this, size.width, size.height);
37759 * Returns this panel's title
37762 getTitle : function(){
37764 if (typeof(this.title) != 'object') {
37769 for (var k in this.title) {
37770 if (!this.title.hasOwnProperty(k)) {
37774 if (k.indexOf('-') >= 0) {
37775 var s = k.split('-');
37776 for (var i = 0; i<s.length; i++) {
37777 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37780 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37787 * Set this panel's title
37788 * @param {String} title
37790 setTitle : function(title){
37791 this.title = title;
37793 this.region.updatePanelTitle(this, title);
37798 * Returns true is this panel was configured to be closable
37799 * @return {Boolean}
37801 isClosable : function(){
37802 return this.closable;
37805 beforeSlide : function(){
37807 this.resizeEl.clip();
37810 afterSlide : function(){
37812 this.resizeEl.unclip();
37816 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37817 * Will fail silently if the {@link #setUrl} method has not been called.
37818 * This does not activate the panel, just updates its content.
37820 refresh : function(){
37821 if(this.refreshDelegate){
37822 this.loaded = false;
37823 this.refreshDelegate();
37828 * Destroys this panel
37830 destroy : function(){
37831 this.el.removeAllListeners();
37832 var tempEl = document.createElement("span");
37833 tempEl.appendChild(this.el.dom);
37834 tempEl.innerHTML = "";
37840 * form - if the content panel contains a form - this is a reference to it.
37841 * @type {Roo.form.Form}
37845 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37846 * This contains a reference to it.
37852 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37862 * @param {Object} cfg Xtype definition of item to add.
37866 getChildContainer: function () {
37867 return this.getEl();
37872 var ret = new Roo.factory(cfg);
37877 if (cfg.xtype.match(/^Form$/)) {
37880 //if (this.footer) {
37881 // el = this.footer.container.insertSibling(false, 'before');
37883 el = this.el.createChild();
37886 this.form = new Roo.form.Form(cfg);
37889 if ( this.form.allItems.length) {
37890 this.form.render(el.dom);
37894 // should only have one of theses..
37895 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37896 // views.. should not be just added - used named prop 'view''
37898 cfg.el = this.el.appendChild(document.createElement("div"));
37901 var ret = new Roo.factory(cfg);
37903 ret.render && ret.render(false, ''); // render blank..
37913 * @class Roo.bootstrap.panel.Grid
37914 * @extends Roo.bootstrap.panel.Content
37916 * Create a new GridPanel.
37917 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37918 * @param {Object} config A the config object
37924 Roo.bootstrap.panel.Grid = function(config)
37928 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37929 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37931 config.el = this.wrapper;
37932 //this.el = this.wrapper;
37934 if (config.container) {
37935 // ctor'ed from a Border/panel.grid
37938 this.wrapper.setStyle("overflow", "hidden");
37939 this.wrapper.addClass('roo-grid-container');
37944 if(config.toolbar){
37945 var tool_el = this.wrapper.createChild();
37946 this.toolbar = Roo.factory(config.toolbar);
37948 if (config.toolbar.items) {
37949 ti = config.toolbar.items ;
37950 delete config.toolbar.items ;
37954 this.toolbar.render(tool_el);
37955 for(var i =0;i < ti.length;i++) {
37956 // Roo.log(['add child', items[i]]);
37957 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37959 this.toolbar.items = nitems;
37961 delete config.toolbar;
37964 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37965 config.grid.scrollBody = true;;
37966 config.grid.monitorWindowResize = false; // turn off autosizing
37967 config.grid.autoHeight = false;
37968 config.grid.autoWidth = false;
37970 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37972 if (config.background) {
37973 // render grid on panel activation (if panel background)
37974 this.on('activate', function(gp) {
37975 if (!gp.grid.rendered) {
37976 gp.grid.render(this.wrapper);
37977 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37982 this.grid.render(this.wrapper);
37983 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37986 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37987 // ??? needed ??? config.el = this.wrapper;
37992 // xtype created footer. - not sure if will work as we normally have to render first..
37993 if (this.footer && !this.footer.el && this.footer.xtype) {
37995 var ctr = this.grid.getView().getFooterPanel(true);
37996 this.footer.dataSource = this.grid.dataSource;
37997 this.footer = Roo.factory(this.footer, Roo);
37998 this.footer.render(ctr);
38008 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
38009 getId : function(){
38010 return this.grid.id;
38014 * Returns the grid for this panel
38015 * @return {Roo.bootstrap.Table}
38017 getGrid : function(){
38021 setSize : function(width, height){
38022 if(!this.ignoreResize(width, height)){
38023 var grid = this.grid;
38024 var size = this.adjustForComponents(width, height);
38025 var gridel = grid.getGridEl();
38026 gridel.setSize(size.width, size.height);
38028 var thd = grid.getGridEl().select('thead',true).first();
38029 var tbd = grid.getGridEl().select('tbody', true).first();
38031 tbd.setSize(width, height - thd.getHeight());
38040 beforeSlide : function(){
38041 this.grid.getView().scroller.clip();
38044 afterSlide : function(){
38045 this.grid.getView().scroller.unclip();
38048 destroy : function(){
38049 this.grid.destroy();
38051 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
38056 * @class Roo.bootstrap.panel.Nest
38057 * @extends Roo.bootstrap.panel.Content
38059 * Create a new Panel, that can contain a layout.Border.
38062 * @param {Roo.BorderLayout} layout The layout for this panel
38063 * @param {String/Object} config A string to set only the title or a config object
38065 Roo.bootstrap.panel.Nest = function(config)
38067 // construct with only one argument..
38068 /* FIXME - implement nicer consturctors
38069 if (layout.layout) {
38071 layout = config.layout;
38072 delete config.layout;
38074 if (layout.xtype && !layout.getEl) {
38075 // then layout needs constructing..
38076 layout = Roo.factory(layout, Roo);
38080 config.el = config.layout.getEl();
38082 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
38084 config.layout.monitorWindowResize = false; // turn off autosizing
38085 this.layout = config.layout;
38086 this.layout.getEl().addClass("roo-layout-nested-layout");
38087 this.layout.parent = this;
38094 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38096 setSize : function(width, height){
38097 if(!this.ignoreResize(width, height)){
38098 var size = this.adjustForComponents(width, height);
38099 var el = this.layout.getEl();
38100 if (size.height < 1) {
38101 el.setWidth(size.width);
38103 el.setSize(size.width, size.height);
38105 var touch = el.dom.offsetWidth;
38106 this.layout.layout();
38107 // ie requires a double layout on the first pass
38108 if(Roo.isIE && !this.initialized){
38109 this.initialized = true;
38110 this.layout.layout();
38115 // activate all subpanels if not currently active..
38117 setActiveState : function(active){
38118 this.active = active;
38119 this.setActiveClass(active);
38122 this.fireEvent("deactivate", this);
38126 this.fireEvent("activate", this);
38127 // not sure if this should happen before or after..
38128 if (!this.layout) {
38129 return; // should not happen..
38132 for (var r in this.layout.regions) {
38133 reg = this.layout.getRegion(r);
38134 if (reg.getActivePanel()) {
38135 //reg.showPanel(reg.getActivePanel()); // force it to activate..
38136 reg.setActivePanel(reg.getActivePanel());
38139 if (!reg.panels.length) {
38142 reg.showPanel(reg.getPanel(0));
38151 * Returns the nested BorderLayout for this panel
38152 * @return {Roo.BorderLayout}
38154 getLayout : function(){
38155 return this.layout;
38159 * Adds a xtype elements to the layout of the nested panel
38163 xtype : 'ContentPanel',
38170 xtype : 'NestedLayoutPanel',
38176 items : [ ... list of content panels or nested layout panels.. ]
38180 * @param {Object} cfg Xtype definition of item to add.
38182 addxtype : function(cfg) {
38183 return this.layout.addxtype(cfg);
38188 * Ext JS Library 1.1.1
38189 * Copyright(c) 2006-2007, Ext JS, LLC.
38191 * Originally Released Under LGPL - original licence link has changed is not relivant.
38194 * <script type="text/javascript">
38197 * @class Roo.TabPanel
38198 * @extends Roo.util.Observable
38199 * A lightweight tab container.
38203 // basic tabs 1, built from existing content
38204 var tabs = new Roo.TabPanel("tabs1");
38205 tabs.addTab("script", "View Script");
38206 tabs.addTab("markup", "View Markup");
38207 tabs.activate("script");
38209 // more advanced tabs, built from javascript
38210 var jtabs = new Roo.TabPanel("jtabs");
38211 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38213 // set up the UpdateManager
38214 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38215 var updater = tab2.getUpdateManager();
38216 updater.setDefaultUrl("ajax1.htm");
38217 tab2.on('activate', updater.refresh, updater, true);
38219 // Use setUrl for Ajax loading
38220 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38221 tab3.setUrl("ajax2.htm", null, true);
38224 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38227 jtabs.activate("jtabs-1");
38230 * Create a new TabPanel.
38231 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38232 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38234 Roo.bootstrap.panel.Tabs = function(config){
38236 * The container element for this TabPanel.
38237 * @type Roo.Element
38239 this.el = Roo.get(config.el);
38242 if(typeof config == "boolean"){
38243 this.tabPosition = config ? "bottom" : "top";
38245 Roo.apply(this, config);
38249 if(this.tabPosition == "bottom"){
38250 // if tabs are at the bottom = create the body first.
38251 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38252 this.el.addClass("roo-tabs-bottom");
38254 // next create the tabs holders
38256 if (this.tabPosition == "west"){
38258 var reg = this.region; // fake it..
38260 if (!reg.mgr.parent) {
38263 reg = reg.mgr.parent.region;
38265 Roo.log("got nest?");
38267 if (reg.mgr.getRegion('west')) {
38268 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38269 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38270 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38271 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38272 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38280 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38281 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38282 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38283 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38288 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38291 // finally - if tabs are at the top, then create the body last..
38292 if(this.tabPosition != "bottom"){
38293 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38294 * @type Roo.Element
38296 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38297 this.el.addClass("roo-tabs-top");
38301 this.bodyEl.setStyle("position", "relative");
38303 this.active = null;
38304 this.activateDelegate = this.activate.createDelegate(this);
38309 * Fires when the active tab changes
38310 * @param {Roo.TabPanel} this
38311 * @param {Roo.TabPanelItem} activePanel The new active tab
38315 * @event beforetabchange
38316 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38317 * @param {Roo.TabPanel} this
38318 * @param {Object} e Set cancel to true on this object to cancel the tab change
38319 * @param {Roo.TabPanelItem} tab The tab being changed to
38321 "beforetabchange" : true
38324 Roo.EventManager.onWindowResize(this.onResize, this);
38325 this.cpad = this.el.getPadding("lr");
38326 this.hiddenCount = 0;
38329 // toolbar on the tabbar support...
38330 if (this.toolbar) {
38331 alert("no toolbar support yet");
38332 this.toolbar = false;
38334 var tcfg = this.toolbar;
38335 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
38336 this.toolbar = new Roo.Toolbar(tcfg);
38337 if (Roo.isSafari) {
38338 var tbl = tcfg.container.child('table', true);
38339 tbl.setAttribute('width', '100%');
38347 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38350 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38352 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38354 tabPosition : "top",
38356 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38358 currentTabWidth : 0,
38360 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38364 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38368 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38370 preferredTabWidth : 175,
38372 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38374 resizeTabs : false,
38376 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38378 monitorResize : true,
38380 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
38382 toolbar : false, // set by caller..
38384 region : false, /// set by caller
38386 disableTooltips : true, // not used yet...
38389 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38390 * @param {String} id The id of the div to use <b>or create</b>
38391 * @param {String} text The text for the tab
38392 * @param {String} content (optional) Content to put in the TabPanelItem body
38393 * @param {Boolean} closable (optional) True to create a close icon on the tab
38394 * @return {Roo.TabPanelItem} The created TabPanelItem
38396 addTab : function(id, text, content, closable, tpl)
38398 var item = new Roo.bootstrap.panel.TabItem({
38402 closable : closable,
38405 this.addTabItem(item);
38407 item.setContent(content);
38413 * Returns the {@link Roo.TabPanelItem} with the specified id/index
38414 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38415 * @return {Roo.TabPanelItem}
38417 getTab : function(id){
38418 return this.items[id];
38422 * Hides the {@link Roo.TabPanelItem} with the specified id/index
38423 * @param {String/Number} id The id or index of the TabPanelItem to hide.
38425 hideTab : function(id){
38426 var t = this.items[id];
38429 this.hiddenCount++;
38430 this.autoSizeTabs();
38435 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38436 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38438 unhideTab : function(id){
38439 var t = this.items[id];
38441 t.setHidden(false);
38442 this.hiddenCount--;
38443 this.autoSizeTabs();
38448 * Adds an existing {@link Roo.TabPanelItem}.
38449 * @param {Roo.TabPanelItem} item The TabPanelItem to add
38451 addTabItem : function(item)
38453 this.items[item.id] = item;
38454 this.items.push(item);
38455 this.autoSizeTabs();
38456 // if(this.resizeTabs){
38457 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38458 // this.autoSizeTabs();
38460 // item.autoSize();
38465 * Removes a {@link Roo.TabPanelItem}.
38466 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38468 removeTab : function(id){
38469 var items = this.items;
38470 var tab = items[id];
38471 if(!tab) { return; }
38472 var index = items.indexOf(tab);
38473 if(this.active == tab && items.length > 1){
38474 var newTab = this.getNextAvailable(index);
38479 this.stripEl.dom.removeChild(tab.pnode.dom);
38480 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38481 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38483 items.splice(index, 1);
38484 delete this.items[tab.id];
38485 tab.fireEvent("close", tab);
38486 tab.purgeListeners();
38487 this.autoSizeTabs();
38490 getNextAvailable : function(start){
38491 var items = this.items;
38493 // look for a next tab that will slide over to
38494 // replace the one being removed
38495 while(index < items.length){
38496 var item = items[++index];
38497 if(item && !item.isHidden()){
38501 // if one isn't found select the previous tab (on the left)
38504 var item = items[--index];
38505 if(item && !item.isHidden()){
38513 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38514 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38516 disableTab : function(id){
38517 var tab = this.items[id];
38518 if(tab && this.active != tab){
38524 * Enables a {@link Roo.TabPanelItem} that is disabled.
38525 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38527 enableTab : function(id){
38528 var tab = this.items[id];
38533 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38534 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38535 * @return {Roo.TabPanelItem} The TabPanelItem.
38537 activate : function(id)
38539 //Roo.log('activite:' + id);
38541 var tab = this.items[id];
38545 if(tab == this.active || tab.disabled){
38549 this.fireEvent("beforetabchange", this, e, tab);
38550 if(e.cancel !== true && !tab.disabled){
38552 this.active.hide();
38554 this.active = this.items[id];
38555 this.active.show();
38556 this.fireEvent("tabchange", this, this.active);
38562 * Gets the active {@link Roo.TabPanelItem}.
38563 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38565 getActiveTab : function(){
38566 return this.active;
38570 * Updates the tab body element to fit the height of the container element
38571 * for overflow scrolling
38572 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38574 syncHeight : function(targetHeight){
38575 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38576 var bm = this.bodyEl.getMargins();
38577 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38578 this.bodyEl.setHeight(newHeight);
38582 onResize : function(){
38583 if(this.monitorResize){
38584 this.autoSizeTabs();
38589 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38591 beginUpdate : function(){
38592 this.updating = true;
38596 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38598 endUpdate : function(){
38599 this.updating = false;
38600 this.autoSizeTabs();
38604 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38606 autoSizeTabs : function()
38608 var count = this.items.length;
38609 var vcount = count - this.hiddenCount;
38612 this.stripEl.hide();
38614 this.stripEl.show();
38617 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38622 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38623 var availWidth = Math.floor(w / vcount);
38624 var b = this.stripBody;
38625 if(b.getWidth() > w){
38626 var tabs = this.items;
38627 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38628 if(availWidth < this.minTabWidth){
38629 /*if(!this.sleft){ // incomplete scrolling code
38630 this.createScrollButtons();
38633 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38636 if(this.currentTabWidth < this.preferredTabWidth){
38637 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38643 * Returns the number of tabs in this TabPanel.
38646 getCount : function(){
38647 return this.items.length;
38651 * Resizes all the tabs to the passed width
38652 * @param {Number} The new width
38654 setTabWidth : function(width){
38655 this.currentTabWidth = width;
38656 for(var i = 0, len = this.items.length; i < len; i++) {
38657 if(!this.items[i].isHidden()) {
38658 this.items[i].setWidth(width);
38664 * Destroys this TabPanel
38665 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38667 destroy : function(removeEl){
38668 Roo.EventManager.removeResizeListener(this.onResize, this);
38669 for(var i = 0, len = this.items.length; i < len; i++){
38670 this.items[i].purgeListeners();
38672 if(removeEl === true){
38673 this.el.update("");
38678 createStrip : function(container)
38680 var strip = document.createElement("nav");
38681 strip.className = Roo.bootstrap.version == 4 ?
38682 "navbar-light bg-light" :
38683 "navbar navbar-default"; //"x-tabs-wrap";
38684 container.appendChild(strip);
38688 createStripList : function(strip)
38690 // div wrapper for retard IE
38691 // returns the "tr" element.
38692 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38693 //'<div class="x-tabs-strip-wrap">'+
38694 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38695 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38696 return strip.firstChild; //.firstChild.firstChild.firstChild;
38698 createBody : function(container)
38700 var body = document.createElement("div");
38701 Roo.id(body, "tab-body");
38702 //Roo.fly(body).addClass("x-tabs-body");
38703 Roo.fly(body).addClass("tab-content");
38704 container.appendChild(body);
38707 createItemBody :function(bodyEl, id){
38708 var body = Roo.getDom(id);
38710 body = document.createElement("div");
38713 //Roo.fly(body).addClass("x-tabs-item-body");
38714 Roo.fly(body).addClass("tab-pane");
38715 bodyEl.insertBefore(body, bodyEl.firstChild);
38719 createStripElements : function(stripEl, text, closable, tpl)
38721 var td = document.createElement("li"); // was td..
38722 td.className = 'nav-item';
38724 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38727 stripEl.appendChild(td);
38729 td.className = "x-tabs-closable";
38730 if(!this.closeTpl){
38731 this.closeTpl = new Roo.Template(
38732 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38733 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38734 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38737 var el = this.closeTpl.overwrite(td, {"text": text});
38738 var close = el.getElementsByTagName("div")[0];
38739 var inner = el.getElementsByTagName("em")[0];
38740 return {"el": el, "close": close, "inner": inner};
38743 // not sure what this is..
38744 // if(!this.tabTpl){
38745 //this.tabTpl = new Roo.Template(
38746 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38747 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38749 // this.tabTpl = new Roo.Template(
38750 // '<a href="#">' +
38751 // '<span unselectable="on"' +
38752 // (this.disableTooltips ? '' : ' title="{text}"') +
38753 // ' >{text}</span></a>'
38759 var template = tpl || this.tabTpl || false;
38762 template = new Roo.Template(
38763 Roo.bootstrap.version == 4 ?
38765 '<a class="nav-link" href="#" unselectable="on"' +
38766 (this.disableTooltips ? '' : ' title="{text}"') +
38769 '<a class="nav-link" href="#">' +
38770 '<span unselectable="on"' +
38771 (this.disableTooltips ? '' : ' title="{text}"') +
38772 ' >{text}</span></a>'
38777 switch (typeof(template)) {
38781 template = new Roo.Template(template);
38787 var el = template.overwrite(td, {"text": text});
38789 var inner = el.getElementsByTagName("span")[0];
38791 return {"el": el, "inner": inner};
38799 * @class Roo.TabPanelItem
38800 * @extends Roo.util.Observable
38801 * Represents an individual item (tab plus body) in a TabPanel.
38802 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38803 * @param {String} id The id of this TabPanelItem
38804 * @param {String} text The text for the tab of this TabPanelItem
38805 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38807 Roo.bootstrap.panel.TabItem = function(config){
38809 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38810 * @type Roo.TabPanel
38812 this.tabPanel = config.panel;
38814 * The id for this TabPanelItem
38817 this.id = config.id;
38819 this.disabled = false;
38821 this.text = config.text;
38823 this.loaded = false;
38824 this.closable = config.closable;
38827 * The body element for this TabPanelItem.
38828 * @type Roo.Element
38830 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38831 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38832 this.bodyEl.setStyle("display", "block");
38833 this.bodyEl.setStyle("zoom", "1");
38834 //this.hideAction();
38836 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38838 this.el = Roo.get(els.el);
38839 this.inner = Roo.get(els.inner, true);
38840 this.textEl = Roo.bootstrap.version == 4 ?
38841 this.el : Roo.get(this.el.dom.firstChild, true);
38843 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38844 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38847 // this.el.on("mousedown", this.onTabMouseDown, this);
38848 this.el.on("click", this.onTabClick, this);
38850 if(config.closable){
38851 var c = Roo.get(els.close, true);
38852 c.dom.title = this.closeText;
38853 c.addClassOnOver("close-over");
38854 c.on("click", this.closeClick, this);
38860 * Fires when this tab becomes the active tab.
38861 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38862 * @param {Roo.TabPanelItem} this
38866 * @event beforeclose
38867 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38868 * @param {Roo.TabPanelItem} this
38869 * @param {Object} e Set cancel to true on this object to cancel the close.
38871 "beforeclose": true,
38874 * Fires when this tab is closed.
38875 * @param {Roo.TabPanelItem} this
38879 * @event deactivate
38880 * Fires when this tab is no longer the active tab.
38881 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38882 * @param {Roo.TabPanelItem} this
38884 "deactivate" : true
38886 this.hidden = false;
38888 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38891 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38893 purgeListeners : function(){
38894 Roo.util.Observable.prototype.purgeListeners.call(this);
38895 this.el.removeAllListeners();
38898 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38901 this.status_node.addClass("active");
38904 this.tabPanel.stripWrap.repaint();
38906 this.fireEvent("activate", this.tabPanel, this);
38910 * Returns true if this tab is the active tab.
38911 * @return {Boolean}
38913 isActive : function(){
38914 return this.tabPanel.getActiveTab() == this;
38918 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38921 this.status_node.removeClass("active");
38923 this.fireEvent("deactivate", this.tabPanel, this);
38926 hideAction : function(){
38927 this.bodyEl.hide();
38928 this.bodyEl.setStyle("position", "absolute");
38929 this.bodyEl.setLeft("-20000px");
38930 this.bodyEl.setTop("-20000px");
38933 showAction : function(){
38934 this.bodyEl.setStyle("position", "relative");
38935 this.bodyEl.setTop("");
38936 this.bodyEl.setLeft("");
38937 this.bodyEl.show();
38941 * Set the tooltip for the tab.
38942 * @param {String} tooltip The tab's tooltip
38944 setTooltip : function(text){
38945 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38946 this.textEl.dom.qtip = text;
38947 this.textEl.dom.removeAttribute('title');
38949 this.textEl.dom.title = text;
38953 onTabClick : function(e){
38954 e.preventDefault();
38955 this.tabPanel.activate(this.id);
38958 onTabMouseDown : function(e){
38959 e.preventDefault();
38960 this.tabPanel.activate(this.id);
38963 getWidth : function(){
38964 return this.inner.getWidth();
38967 setWidth : function(width){
38968 var iwidth = width - this.linode.getPadding("lr");
38969 this.inner.setWidth(iwidth);
38970 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38971 this.linode.setWidth(width);
38975 * Show or hide the tab
38976 * @param {Boolean} hidden True to hide or false to show.
38978 setHidden : function(hidden){
38979 this.hidden = hidden;
38980 this.linode.setStyle("display", hidden ? "none" : "");
38984 * Returns true if this tab is "hidden"
38985 * @return {Boolean}
38987 isHidden : function(){
38988 return this.hidden;
38992 * Returns the text for this tab
38995 getText : function(){
38999 autoSize : function(){
39000 //this.el.beginMeasure();
39001 this.textEl.setWidth(1);
39003 * #2804 [new] Tabs in Roojs
39004 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
39006 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
39007 //this.el.endMeasure();
39011 * Sets the text for the tab (Note: this also sets the tooltip text)
39012 * @param {String} text The tab's text and tooltip
39014 setText : function(text){
39016 this.textEl.update(text);
39017 this.setTooltip(text);
39018 //if(!this.tabPanel.resizeTabs){
39019 // this.autoSize();
39023 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39025 activate : function(){
39026 this.tabPanel.activate(this.id);
39030 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
39032 disable : function(){
39033 if(this.tabPanel.active != this){
39034 this.disabled = true;
39035 this.status_node.addClass("disabled");
39040 * Enables this TabPanelItem if it was previously disabled.
39042 enable : function(){
39043 this.disabled = false;
39044 this.status_node.removeClass("disabled");
39048 * Sets the content for this TabPanelItem.
39049 * @param {String} content The content
39050 * @param {Boolean} loadScripts true to look for and load scripts
39052 setContent : function(content, loadScripts){
39053 this.bodyEl.update(content, loadScripts);
39057 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
39058 * @return {Roo.UpdateManager} The UpdateManager
39060 getUpdateManager : function(){
39061 return this.bodyEl.getUpdateManager();
39065 * Set a URL to be used to load the content for this TabPanelItem.
39066 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
39067 * @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)
39068 * @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)
39069 * @return {Roo.UpdateManager} The UpdateManager
39071 setUrl : function(url, params, loadOnce){
39072 if(this.refreshDelegate){
39073 this.un('activate', this.refreshDelegate);
39075 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39076 this.on("activate", this.refreshDelegate);
39077 return this.bodyEl.getUpdateManager();
39081 _handleRefresh : function(url, params, loadOnce){
39082 if(!loadOnce || !this.loaded){
39083 var updater = this.bodyEl.getUpdateManager();
39084 updater.update(url, params, this._setLoaded.createDelegate(this));
39089 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
39090 * Will fail silently if the setUrl method has not been called.
39091 * This does not activate the panel, just updates its content.
39093 refresh : function(){
39094 if(this.refreshDelegate){
39095 this.loaded = false;
39096 this.refreshDelegate();
39101 _setLoaded : function(){
39102 this.loaded = true;
39106 closeClick : function(e){
39109 this.fireEvent("beforeclose", this, o);
39110 if(o.cancel !== true){
39111 this.tabPanel.removeTab(this.id);
39115 * The text displayed in the tooltip for the close icon.
39118 closeText : "Close this tab"
39121 * This script refer to:
39122 * Title: International Telephone Input
39123 * Author: Jack O'Connor
39124 * Code version: v12.1.12
39125 * Availability: https://github.com/jackocnr/intl-tel-input.git
39128 Roo.bootstrap.PhoneInputData = function() {
39131 "Afghanistan (افغانستان)",
39136 "Albania (Shqipëri)",
39141 "Algeria (الجزائر)",
39166 "Antigua and Barbuda",
39176 "Armenia (Հայաստան)",
39192 "Austria (Österreich)",
39197 "Azerbaijan (Azərbaycan)",
39207 "Bahrain (البحرين)",
39212 "Bangladesh (বাংলাদেশ)",
39222 "Belarus (Беларусь)",
39227 "Belgium (België)",
39257 "Bosnia and Herzegovina (Босна и Херцеговина)",
39272 "British Indian Ocean Territory",
39277 "British Virgin Islands",
39287 "Bulgaria (България)",
39297 "Burundi (Uburundi)",
39302 "Cambodia (កម្ពុជា)",
39307 "Cameroon (Cameroun)",
39316 ["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"]
39319 "Cape Verde (Kabu Verdi)",
39324 "Caribbean Netherlands",
39335 "Central African Republic (République centrafricaine)",
39355 "Christmas Island",
39361 "Cocos (Keeling) Islands",
39372 "Comoros (جزر القمر)",
39377 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39382 "Congo (Republic) (Congo-Brazzaville)",
39402 "Croatia (Hrvatska)",
39423 "Czech Republic (Česká republika)",
39428 "Denmark (Danmark)",
39443 "Dominican Republic (República Dominicana)",
39447 ["809", "829", "849"]
39465 "Equatorial Guinea (Guinea Ecuatorial)",
39485 "Falkland Islands (Islas Malvinas)",
39490 "Faroe Islands (Føroyar)",
39511 "French Guiana (Guyane française)",
39516 "French Polynesia (Polynésie française)",
39531 "Georgia (საქართველო)",
39536 "Germany (Deutschland)",
39556 "Greenland (Kalaallit Nunaat)",
39593 "Guinea-Bissau (Guiné Bissau)",
39618 "Hungary (Magyarország)",
39623 "Iceland (Ísland)",
39643 "Iraq (العراق)",
39659 "Israel (ישראל)",
39686 "Jordan (الأردن)",
39691 "Kazakhstan (Казахстан)",
39712 "Kuwait (الكويت)",
39717 "Kyrgyzstan (Кыргызстан)",
39727 "Latvia (Latvija)",
39732 "Lebanon (لبنان)",
39747 "Libya (ليبيا)",
39757 "Lithuania (Lietuva)",
39772 "Macedonia (FYROM) (Македонија)",
39777 "Madagascar (Madagasikara)",
39807 "Marshall Islands",
39817 "Mauritania (موريتانيا)",
39822 "Mauritius (Moris)",
39843 "Moldova (Republica Moldova)",
39853 "Mongolia (Монгол)",
39858 "Montenegro (Crna Gora)",
39868 "Morocco (المغرب)",
39874 "Mozambique (Moçambique)",
39879 "Myanmar (Burma) (မြန်မာ)",
39884 "Namibia (Namibië)",
39899 "Netherlands (Nederland)",
39904 "New Caledonia (Nouvelle-Calédonie)",
39939 "North Korea (조선 민주주의 인민 공화국)",
39944 "Northern Mariana Islands",
39960 "Pakistan (پاکستان)",
39970 "Palestine (فلسطين)",
39980 "Papua New Guinea",
40022 "Réunion (La Réunion)",
40028 "Romania (România)",
40044 "Saint Barthélemy",
40055 "Saint Kitts and Nevis",
40065 "Saint Martin (Saint-Martin (partie française))",
40071 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
40076 "Saint Vincent and the Grenadines",
40091 "São Tomé and Príncipe (São Tomé e Príncipe)",
40096 "Saudi Arabia (المملكة العربية السعودية)",
40101 "Senegal (Sénégal)",
40131 "Slovakia (Slovensko)",
40136 "Slovenia (Slovenija)",
40146 "Somalia (Soomaaliya)",
40156 "South Korea (대한민국)",
40161 "South Sudan (جنوب السودان)",
40171 "Sri Lanka (ශ්රී ලංකාව)",
40176 "Sudan (السودان)",
40186 "Svalbard and Jan Mayen",
40197 "Sweden (Sverige)",
40202 "Switzerland (Schweiz)",
40207 "Syria (سوريا)",
40252 "Trinidad and Tobago",
40257 "Tunisia (تونس)",
40262 "Turkey (Türkiye)",
40272 "Turks and Caicos Islands",
40282 "U.S. Virgin Islands",
40292 "Ukraine (Україна)",
40297 "United Arab Emirates (الإمارات العربية المتحدة)",
40319 "Uzbekistan (Oʻzbekiston)",
40329 "Vatican City (Città del Vaticano)",
40340 "Vietnam (Việt Nam)",
40345 "Wallis and Futuna (Wallis-et-Futuna)",
40350 "Western Sahara (الصحراء الغربية)",
40356 "Yemen (اليمن)",
40380 * This script refer to:
40381 * Title: International Telephone Input
40382 * Author: Jack O'Connor
40383 * Code version: v12.1.12
40384 * Availability: https://github.com/jackocnr/intl-tel-input.git
40388 * @class Roo.bootstrap.PhoneInput
40389 * @extends Roo.bootstrap.TriggerField
40390 * An input with International dial-code selection
40392 * @cfg {String} defaultDialCode default '+852'
40393 * @cfg {Array} preferedCountries default []
40396 * Create a new PhoneInput.
40397 * @param {Object} config Configuration options
40400 Roo.bootstrap.PhoneInput = function(config) {
40401 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40404 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40406 listWidth: undefined,
40408 selectedClass: 'active',
40410 invalidClass : "has-warning",
40412 validClass: 'has-success',
40414 allowed: '0123456789',
40419 * @cfg {String} defaultDialCode The default dial code when initializing the input
40421 defaultDialCode: '+852',
40424 * @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
40426 preferedCountries: false,
40428 getAutoCreate : function()
40430 var data = Roo.bootstrap.PhoneInputData();
40431 var align = this.labelAlign || this.parentLabelAlign();
40434 this.allCountries = [];
40435 this.dialCodeMapping = [];
40437 for (var i = 0; i < data.length; i++) {
40439 this.allCountries[i] = {
40443 priority: c[3] || 0,
40444 areaCodes: c[4] || null
40446 this.dialCodeMapping[c[2]] = {
40449 priority: c[3] || 0,
40450 areaCodes: c[4] || null
40462 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40463 maxlength: this.max_length,
40464 cls : 'form-control tel-input',
40465 autocomplete: 'new-password'
40468 var hiddenInput = {
40471 cls: 'hidden-tel-input'
40475 hiddenInput.name = this.name;
40478 if (this.disabled) {
40479 input.disabled = true;
40482 var flag_container = {
40499 cls: this.hasFeedback ? 'has-feedback' : '',
40505 cls: 'dial-code-holder',
40512 cls: 'roo-select2-container input-group',
40519 if (this.fieldLabel.length) {
40522 tooltip: 'This field is required'
40528 cls: 'control-label',
40534 html: this.fieldLabel
40537 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40543 if(this.indicatorpos == 'right') {
40544 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40551 if(align == 'left') {
40559 if(this.labelWidth > 12){
40560 label.style = "width: " + this.labelWidth + 'px';
40562 if(this.labelWidth < 13 && this.labelmd == 0){
40563 this.labelmd = this.labelWidth;
40565 if(this.labellg > 0){
40566 label.cls += ' col-lg-' + this.labellg;
40567 input.cls += ' col-lg-' + (12 - this.labellg);
40569 if(this.labelmd > 0){
40570 label.cls += ' col-md-' + this.labelmd;
40571 container.cls += ' col-md-' + (12 - this.labelmd);
40573 if(this.labelsm > 0){
40574 label.cls += ' col-sm-' + this.labelsm;
40575 container.cls += ' col-sm-' + (12 - this.labelsm);
40577 if(this.labelxs > 0){
40578 label.cls += ' col-xs-' + this.labelxs;
40579 container.cls += ' col-xs-' + (12 - this.labelxs);
40589 var settings = this;
40591 ['xs','sm','md','lg'].map(function(size){
40592 if (settings[size]) {
40593 cfg.cls += ' col-' + size + '-' + settings[size];
40597 this.store = new Roo.data.Store({
40598 proxy : new Roo.data.MemoryProxy({}),
40599 reader : new Roo.data.JsonReader({
40610 'name' : 'dialCode',
40614 'name' : 'priority',
40618 'name' : 'areaCodes',
40625 if(!this.preferedCountries) {
40626 this.preferedCountries = [
40633 var p = this.preferedCountries.reverse();
40636 for (var i = 0; i < p.length; i++) {
40637 for (var j = 0; j < this.allCountries.length; j++) {
40638 if(this.allCountries[j].iso2 == p[i]) {
40639 var t = this.allCountries[j];
40640 this.allCountries.splice(j,1);
40641 this.allCountries.unshift(t);
40647 this.store.proxy.data = {
40649 data: this.allCountries
40655 initEvents : function()
40658 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40660 this.indicator = this.indicatorEl();
40661 this.flag = this.flagEl();
40662 this.dialCodeHolder = this.dialCodeHolderEl();
40664 this.trigger = this.el.select('div.flag-box',true).first();
40665 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40670 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40671 _this.list.setWidth(lw);
40674 this.list.on('mouseover', this.onViewOver, this);
40675 this.list.on('mousemove', this.onViewMove, this);
40676 this.inputEl().on("keyup", this.onKeyUp, this);
40677 this.inputEl().on("keypress", this.onKeyPress, this);
40679 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40681 this.view = new Roo.View(this.list, this.tpl, {
40682 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40685 this.view.on('click', this.onViewClick, this);
40686 this.setValue(this.defaultDialCode);
40689 onTriggerClick : function(e)
40691 Roo.log('trigger click');
40696 if(this.isExpanded()){
40698 this.hasFocus = false;
40700 this.store.load({});
40701 this.hasFocus = true;
40706 isExpanded : function()
40708 return this.list.isVisible();
40711 collapse : function()
40713 if(!this.isExpanded()){
40717 Roo.get(document).un('mousedown', this.collapseIf, this);
40718 Roo.get(document).un('mousewheel', this.collapseIf, this);
40719 this.fireEvent('collapse', this);
40723 expand : function()
40727 if(this.isExpanded() || !this.hasFocus){
40731 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40732 this.list.setWidth(lw);
40735 this.restrictHeight();
40737 Roo.get(document).on('mousedown', this.collapseIf, this);
40738 Roo.get(document).on('mousewheel', this.collapseIf, this);
40740 this.fireEvent('expand', this);
40743 restrictHeight : function()
40745 this.list.alignTo(this.inputEl(), this.listAlign);
40746 this.list.alignTo(this.inputEl(), this.listAlign);
40749 onViewOver : function(e, t)
40751 if(this.inKeyMode){
40754 var item = this.view.findItemFromChild(t);
40757 var index = this.view.indexOf(item);
40758 this.select(index, false);
40763 onViewClick : function(view, doFocus, el, e)
40765 var index = this.view.getSelectedIndexes()[0];
40767 var r = this.store.getAt(index);
40770 this.onSelect(r, index);
40772 if(doFocus !== false && !this.blockFocus){
40773 this.inputEl().focus();
40777 onViewMove : function(e, t)
40779 this.inKeyMode = false;
40782 select : function(index, scrollIntoView)
40784 this.selectedIndex = index;
40785 this.view.select(index);
40786 if(scrollIntoView !== false){
40787 var el = this.view.getNode(index);
40789 this.list.scrollChildIntoView(el, false);
40794 createList : function()
40796 this.list = Roo.get(document.body).createChild({
40798 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40799 style: 'display:none'
40802 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40805 collapseIf : function(e)
40807 var in_combo = e.within(this.el);
40808 var in_list = e.within(this.list);
40809 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40811 if (in_combo || in_list || is_list) {
40817 onSelect : function(record, index)
40819 if(this.fireEvent('beforeselect', this, record, index) !== false){
40821 this.setFlagClass(record.data.iso2);
40822 this.setDialCode(record.data.dialCode);
40823 this.hasFocus = false;
40825 this.fireEvent('select', this, record, index);
40829 flagEl : function()
40831 var flag = this.el.select('div.flag',true).first();
40838 dialCodeHolderEl : function()
40840 var d = this.el.select('input.dial-code-holder',true).first();
40847 setDialCode : function(v)
40849 this.dialCodeHolder.dom.value = '+'+v;
40852 setFlagClass : function(n)
40854 this.flag.dom.className = 'flag '+n;
40857 getValue : function()
40859 var v = this.inputEl().getValue();
40860 if(this.dialCodeHolder) {
40861 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40866 setValue : function(v)
40868 var d = this.getDialCode(v);
40870 //invalid dial code
40871 if(v.length == 0 || !d || d.length == 0) {
40873 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40874 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40880 this.setFlagClass(this.dialCodeMapping[d].iso2);
40881 this.setDialCode(d);
40882 this.inputEl().dom.value = v.replace('+'+d,'');
40883 this.hiddenEl().dom.value = this.getValue();
40888 getDialCode : function(v)
40892 if (v.length == 0) {
40893 return this.dialCodeHolder.dom.value;
40897 if (v.charAt(0) != "+") {
40900 var numericChars = "";
40901 for (var i = 1; i < v.length; i++) {
40902 var c = v.charAt(i);
40905 if (this.dialCodeMapping[numericChars]) {
40906 dialCode = v.substr(1, i);
40908 if (numericChars.length == 4) {
40918 this.setValue(this.defaultDialCode);
40922 hiddenEl : function()
40924 return this.el.select('input.hidden-tel-input',true).first();
40927 // after setting val
40928 onKeyUp : function(e){
40929 this.setValue(this.getValue());
40932 onKeyPress : function(e){
40933 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40940 * @class Roo.bootstrap.MoneyField
40941 * @extends Roo.bootstrap.ComboBox
40942 * Bootstrap MoneyField class
40945 * Create a new MoneyField.
40946 * @param {Object} config Configuration options
40949 Roo.bootstrap.MoneyField = function(config) {
40951 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40955 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40958 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40960 allowDecimals : true,
40962 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40964 decimalSeparator : ".",
40966 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40968 decimalPrecision : 0,
40970 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40972 allowNegative : true,
40974 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40978 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40980 minValue : Number.NEGATIVE_INFINITY,
40982 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40984 maxValue : Number.MAX_VALUE,
40986 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40988 minText : "The minimum value for this field is {0}",
40990 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40992 maxText : "The maximum value for this field is {0}",
40994 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40995 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40997 nanText : "{0} is not a valid number",
40999 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
41003 * @cfg {String} defaults currency of the MoneyField
41004 * value should be in lkey
41006 defaultCurrency : false,
41008 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41010 thousandsDelimiter : false,
41012 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41023 getAutoCreate : function()
41025 var align = this.labelAlign || this.parentLabelAlign();
41037 cls : 'form-control roo-money-amount-input',
41038 autocomplete: 'new-password'
41041 var hiddenInput = {
41045 cls: 'hidden-number-input'
41048 if(this.max_length) {
41049 input.maxlength = this.max_length;
41053 hiddenInput.name = this.name;
41056 if (this.disabled) {
41057 input.disabled = true;
41060 var clg = 12 - this.inputlg;
41061 var cmd = 12 - this.inputmd;
41062 var csm = 12 - this.inputsm;
41063 var cxs = 12 - this.inputxs;
41067 cls : 'row roo-money-field',
41071 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
41075 cls: 'roo-select2-container input-group',
41079 cls : 'form-control roo-money-currency-input',
41080 autocomplete: 'new-password',
41082 name : this.currencyName
41086 cls : 'input-group-addon',
41100 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41104 cls: this.hasFeedback ? 'has-feedback' : '',
41115 if (this.fieldLabel.length) {
41118 tooltip: 'This field is required'
41124 cls: 'control-label',
41130 html: this.fieldLabel
41133 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41139 if(this.indicatorpos == 'right') {
41140 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41147 if(align == 'left') {
41155 if(this.labelWidth > 12){
41156 label.style = "width: " + this.labelWidth + 'px';
41158 if(this.labelWidth < 13 && this.labelmd == 0){
41159 this.labelmd = this.labelWidth;
41161 if(this.labellg > 0){
41162 label.cls += ' col-lg-' + this.labellg;
41163 input.cls += ' col-lg-' + (12 - this.labellg);
41165 if(this.labelmd > 0){
41166 label.cls += ' col-md-' + this.labelmd;
41167 container.cls += ' col-md-' + (12 - this.labelmd);
41169 if(this.labelsm > 0){
41170 label.cls += ' col-sm-' + this.labelsm;
41171 container.cls += ' col-sm-' + (12 - this.labelsm);
41173 if(this.labelxs > 0){
41174 label.cls += ' col-xs-' + this.labelxs;
41175 container.cls += ' col-xs-' + (12 - this.labelxs);
41186 var settings = this;
41188 ['xs','sm','md','lg'].map(function(size){
41189 if (settings[size]) {
41190 cfg.cls += ' col-' + size + '-' + settings[size];
41197 initEvents : function()
41199 this.indicator = this.indicatorEl();
41201 this.initCurrencyEvent();
41203 this.initNumberEvent();
41206 initCurrencyEvent : function()
41209 throw "can not find store for combo";
41212 this.store = Roo.factory(this.store, Roo.data);
41213 this.store.parent = this;
41217 this.triggerEl = this.el.select('.input-group-addon', true).first();
41219 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41224 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41225 _this.list.setWidth(lw);
41228 this.list.on('mouseover', this.onViewOver, this);
41229 this.list.on('mousemove', this.onViewMove, this);
41230 this.list.on('scroll', this.onViewScroll, this);
41233 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41236 this.view = new Roo.View(this.list, this.tpl, {
41237 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41240 this.view.on('click', this.onViewClick, this);
41242 this.store.on('beforeload', this.onBeforeLoad, this);
41243 this.store.on('load', this.onLoad, this);
41244 this.store.on('loadexception', this.onLoadException, this);
41246 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41247 "up" : function(e){
41248 this.inKeyMode = true;
41252 "down" : function(e){
41253 if(!this.isExpanded()){
41254 this.onTriggerClick();
41256 this.inKeyMode = true;
41261 "enter" : function(e){
41264 if(this.fireEvent("specialkey", this, e)){
41265 this.onViewClick(false);
41271 "esc" : function(e){
41275 "tab" : function(e){
41278 if(this.fireEvent("specialkey", this, e)){
41279 this.onViewClick(false);
41287 doRelay : function(foo, bar, hname){
41288 if(hname == 'down' || this.scope.isExpanded()){
41289 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41297 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41301 initNumberEvent : function(e)
41303 this.inputEl().on("keydown" , this.fireKey, this);
41304 this.inputEl().on("focus", this.onFocus, this);
41305 this.inputEl().on("blur", this.onBlur, this);
41307 this.inputEl().relayEvent('keyup', this);
41309 if(this.indicator){
41310 this.indicator.addClass('invisible');
41313 this.originalValue = this.getValue();
41315 if(this.validationEvent == 'keyup'){
41316 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41317 this.inputEl().on('keyup', this.filterValidation, this);
41319 else if(this.validationEvent !== false){
41320 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41323 if(this.selectOnFocus){
41324 this.on("focus", this.preFocus, this);
41327 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41328 this.inputEl().on("keypress", this.filterKeys, this);
41330 this.inputEl().relayEvent('keypress', this);
41333 var allowed = "0123456789";
41335 if(this.allowDecimals){
41336 allowed += this.decimalSeparator;
41339 if(this.allowNegative){
41343 if(this.thousandsDelimiter) {
41347 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41349 var keyPress = function(e){
41351 var k = e.getKey();
41353 var c = e.getCharCode();
41356 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41357 allowed.indexOf(String.fromCharCode(c)) === -1
41363 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41367 if(allowed.indexOf(String.fromCharCode(c)) === -1){
41372 this.inputEl().on("keypress", keyPress, this);
41376 onTriggerClick : function(e)
41383 this.loadNext = false;
41385 if(this.isExpanded()){
41390 this.hasFocus = true;
41392 if(this.triggerAction == 'all') {
41393 this.doQuery(this.allQuery, true);
41397 this.doQuery(this.getRawValue());
41400 getCurrency : function()
41402 var v = this.currencyEl().getValue();
41407 restrictHeight : function()
41409 this.list.alignTo(this.currencyEl(), this.listAlign);
41410 this.list.alignTo(this.currencyEl(), this.listAlign);
41413 onViewClick : function(view, doFocus, el, e)
41415 var index = this.view.getSelectedIndexes()[0];
41417 var r = this.store.getAt(index);
41420 this.onSelect(r, index);
41424 onSelect : function(record, index){
41426 if(this.fireEvent('beforeselect', this, record, index) !== false){
41428 this.setFromCurrencyData(index > -1 ? record.data : false);
41432 this.fireEvent('select', this, record, index);
41436 setFromCurrencyData : function(o)
41440 this.lastCurrency = o;
41442 if (this.currencyField) {
41443 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41445 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
41448 this.lastSelectionText = currency;
41450 //setting default currency
41451 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41452 this.setCurrency(this.defaultCurrency);
41456 this.setCurrency(currency);
41459 setFromData : function(o)
41463 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41465 this.setFromCurrencyData(c);
41470 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41472 Roo.log('no value set for '+ (this.name ? this.name : this.id));
41475 this.setValue(value);
41479 setCurrency : function(v)
41481 this.currencyValue = v;
41484 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41489 setValue : function(v)
41491 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41497 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41499 this.inputEl().dom.value = (v == '') ? '' :
41500 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41502 if(!this.allowZero && v === '0') {
41503 this.hiddenEl().dom.value = '';
41504 this.inputEl().dom.value = '';
41511 getRawValue : function()
41513 var v = this.inputEl().getValue();
41518 getValue : function()
41520 return this.fixPrecision(this.parseValue(this.getRawValue()));
41523 parseValue : function(value)
41525 if(this.thousandsDelimiter) {
41527 r = new RegExp(",", "g");
41528 value = value.replace(r, "");
41531 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41532 return isNaN(value) ? '' : value;
41536 fixPrecision : function(value)
41538 if(this.thousandsDelimiter) {
41540 r = new RegExp(",", "g");
41541 value = value.replace(r, "");
41544 var nan = isNaN(value);
41546 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41547 return nan ? '' : value;
41549 return parseFloat(value).toFixed(this.decimalPrecision);
41552 decimalPrecisionFcn : function(v)
41554 return Math.floor(v);
41557 validateValue : function(value)
41559 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41563 var num = this.parseValue(value);
41566 this.markInvalid(String.format(this.nanText, value));
41570 if(num < this.minValue){
41571 this.markInvalid(String.format(this.minText, this.minValue));
41575 if(num > this.maxValue){
41576 this.markInvalid(String.format(this.maxText, this.maxValue));
41583 validate : function()
41585 if(this.disabled || this.allowBlank){
41590 var currency = this.getCurrency();
41592 if(this.validateValue(this.getRawValue()) && currency.length){
41597 this.markInvalid();
41601 getName: function()
41606 beforeBlur : function()
41612 var v = this.parseValue(this.getRawValue());
41619 onBlur : function()
41623 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41624 //this.el.removeClass(this.focusClass);
41627 this.hasFocus = false;
41629 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41633 var v = this.getValue();
41635 if(String(v) !== String(this.startValue)){
41636 this.fireEvent('change', this, v, this.startValue);
41639 this.fireEvent("blur", this);
41642 inputEl : function()
41644 return this.el.select('.roo-money-amount-input', true).first();
41647 currencyEl : function()
41649 return this.el.select('.roo-money-currency-input', true).first();
41652 hiddenEl : function()
41654 return this.el.select('input.hidden-number-input',true).first();
41658 * @class Roo.bootstrap.BezierSignature
41659 * @extends Roo.bootstrap.Component
41660 * Bootstrap BezierSignature class
41661 * This script refer to:
41662 * Title: Signature Pad
41664 * Availability: https://github.com/szimek/signature_pad
41667 * Create a new BezierSignature
41668 * @param {Object} config The config object
41671 Roo.bootstrap.BezierSignature = function(config){
41672 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41678 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
41685 mouse_btn_down: true,
41688 * @cfg {int} canvas height
41690 canvas_height: '200px',
41693 * @cfg {float|function} Radius of a single dot.
41698 * @cfg {float} Minimum width of a line. Defaults to 0.5.
41703 * @cfg {float} Maximum width of a line. Defaults to 2.5.
41708 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41713 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41718 * @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.
41720 bg_color: 'rgba(0, 0, 0, 0)',
41723 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41725 dot_color: 'black',
41728 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41730 velocity_filter_weight: 0.7,
41733 * @cfg {function} Callback when stroke begin.
41738 * @cfg {function} Callback when stroke end.
41742 getAutoCreate : function()
41744 var cls = 'roo-signature column';
41747 cls += ' ' + this.cls;
41757 for(var i = 0; i < col_sizes.length; i++) {
41758 if(this[col_sizes[i]]) {
41759 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
41769 cls: 'roo-signature-body',
41773 cls: 'roo-signature-body-canvas',
41774 height: this.canvas_height,
41775 width: this.canvas_width
41782 style: 'display: none'
41790 initEvents: function()
41792 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
41794 var canvas = this.canvasEl();
41796 // mouse && touch event swapping...
41797 canvas.dom.style.touchAction = 'none';
41798 canvas.dom.style.msTouchAction = 'none';
41800 this.mouse_btn_down = false;
41801 canvas.on('mousedown', this._handleMouseDown, this);
41802 canvas.on('mousemove', this._handleMouseMove, this);
41803 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
41805 if (window.PointerEvent) {
41806 canvas.on('pointerdown', this._handleMouseDown, this);
41807 canvas.on('pointermove', this._handleMouseMove, this);
41808 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
41811 if ('ontouchstart' in window) {
41812 canvas.on('touchstart', this._handleTouchStart, this);
41813 canvas.on('touchmove', this._handleTouchMove, this);
41814 canvas.on('touchend', this._handleTouchEnd, this);
41817 Roo.EventManager.onWindowResize(this.resize, this, true);
41819 // file input event
41820 this.fileEl().on('change', this.uploadImage, this);
41827 resize: function(){
41829 var canvas = this.canvasEl().dom;
41830 var ctx = this.canvasElCtx();
41831 var img_data = false;
41833 if(canvas.width > 0) {
41834 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
41836 // setting canvas width will clean img data
41839 var style = window.getComputedStyle ?
41840 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
41842 var padding_left = parseInt(style.paddingLeft) || 0;
41843 var padding_right = parseInt(style.paddingRight) || 0;
41845 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
41848 ctx.putImageData(img_data, 0, 0);
41852 _handleMouseDown: function(e)
41854 if (e.browserEvent.which === 1) {
41855 this.mouse_btn_down = true;
41856 this.strokeBegin(e);
41860 _handleMouseMove: function (e)
41862 if (this.mouse_btn_down) {
41863 this.strokeMoveUpdate(e);
41867 _handleMouseUp: function (e)
41869 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
41870 this.mouse_btn_down = false;
41875 _handleTouchStart: function (e) {
41877 e.preventDefault();
41878 if (e.browserEvent.targetTouches.length === 1) {
41879 // var touch = e.browserEvent.changedTouches[0];
41880 // this.strokeBegin(touch);
41882 this.strokeBegin(e); // assume e catching the correct xy...
41886 _handleTouchMove: function (e) {
41887 e.preventDefault();
41888 // var touch = event.targetTouches[0];
41889 // _this._strokeMoveUpdate(touch);
41890 this.strokeMoveUpdate(e);
41893 _handleTouchEnd: function (e) {
41894 var wasCanvasTouched = e.target === this.canvasEl().dom;
41895 if (wasCanvasTouched) {
41896 e.preventDefault();
41897 // var touch = event.changedTouches[0];
41898 // _this._strokeEnd(touch);
41903 reset: function () {
41904 this._lastPoints = [];
41905 this._lastVelocity = 0;
41906 this._lastWidth = (this.min_width + this.max_width) / 2;
41907 this.canvasElCtx().fillStyle = this.dot_color;
41910 strokeMoveUpdate: function(e)
41912 this.strokeUpdate(e);
41914 if (this.throttle) {
41915 this.throttleStroke(this.strokeUpdate, this.throttle);
41918 this.strokeUpdate(e);
41922 strokeBegin: function(e)
41924 var newPointGroup = {
41925 color: this.dot_color,
41929 if (typeof this.onBegin === 'function') {
41933 this.curve_data.push(newPointGroup);
41935 this.strokeUpdate(e);
41938 strokeUpdate: function(e)
41940 var rect = this.canvasEl().dom.getBoundingClientRect();
41941 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
41942 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
41943 var lastPoints = lastPointGroup.points;
41944 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
41945 var isLastPointTooClose = lastPoint
41946 ? point.distanceTo(lastPoint) <= this.min_distance
41948 var color = lastPointGroup.color;
41949 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
41950 var curve = this.addPoint(point);
41952 this.drawDot({color: color, point: point});
41955 this.drawCurve({color: color, curve: curve});
41965 strokeEnd: function(e)
41967 this.strokeUpdate(e);
41968 if (typeof this.onEnd === 'function') {
41973 addPoint: function (point) {
41974 var _lastPoints = this._lastPoints;
41975 _lastPoints.push(point);
41976 if (_lastPoints.length > 2) {
41977 if (_lastPoints.length === 3) {
41978 _lastPoints.unshift(_lastPoints[0]);
41980 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
41981 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
41982 _lastPoints.shift();
41988 calculateCurveWidths: function (startPoint, endPoint) {
41989 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
41990 (1 - this.velocity_filter_weight) * this._lastVelocity;
41992 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
41995 start: this._lastWidth
41998 this._lastVelocity = velocity;
41999 this._lastWidth = newWidth;
42003 drawDot: function (_a) {
42004 var color = _a.color, point = _a.point;
42005 var ctx = this.canvasElCtx();
42006 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
42008 this.drawCurveSegment(point.x, point.y, width);
42010 ctx.fillStyle = color;
42014 drawCurve: function (_a) {
42015 var color = _a.color, curve = _a.curve;
42016 var ctx = this.canvasElCtx();
42017 var widthDelta = curve.endWidth - curve.startWidth;
42018 var drawSteps = Math.floor(curve.length()) * 2;
42020 ctx.fillStyle = color;
42021 for (var i = 0; i < drawSteps; i += 1) {
42022 var t = i / drawSteps;
42028 var x = uuu * curve.startPoint.x;
42029 x += 3 * uu * t * curve.control1.x;
42030 x += 3 * u * tt * curve.control2.x;
42031 x += ttt * curve.endPoint.x;
42032 var y = uuu * curve.startPoint.y;
42033 y += 3 * uu * t * curve.control1.y;
42034 y += 3 * u * tt * curve.control2.y;
42035 y += ttt * curve.endPoint.y;
42036 var width = curve.startWidth + ttt * widthDelta;
42037 this.drawCurveSegment(x, y, width);
42043 drawCurveSegment: function (x, y, width) {
42044 var ctx = this.canvasElCtx();
42046 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
42047 this.is_empty = false;
42052 var ctx = this.canvasElCtx();
42053 var canvas = this.canvasEl().dom;
42054 ctx.fillStyle = this.bg_color;
42055 ctx.clearRect(0, 0, canvas.width, canvas.height);
42056 ctx.fillRect(0, 0, canvas.width, canvas.height);
42057 this.curve_data = [];
42059 this.is_empty = true;
42064 return this.el.select('input',true).first();
42067 canvasEl: function()
42069 return this.el.select('canvas',true).first();
42072 canvasElCtx: function()
42074 return this.el.select('canvas',true).first().dom.getContext('2d');
42077 getImage: function(type)
42079 if(this.is_empty) {
42084 return this.canvasEl().dom.toDataURL('image/'+type, 1);
42087 drawFromImage: function(img_src)
42089 var img = new Image();
42091 img.onload = function(){
42092 this.canvasElCtx().drawImage(img, 0, 0);
42097 this.is_empty = false;
42100 selectImage: function()
42102 this.fileEl().dom.click();
42105 uploadImage: function(e)
42107 var reader = new FileReader();
42109 reader.onload = function(e){
42110 var img = new Image();
42111 img.onload = function(){
42113 this.canvasElCtx().drawImage(img, 0, 0);
42115 img.src = e.target.result;
42118 reader.readAsDataURL(e.target.files[0]);
42121 // Bezier Point Constructor
42122 Point: (function () {
42123 function Point(x, y, time) {
42126 this.time = time || Date.now();
42128 Point.prototype.distanceTo = function (start) {
42129 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42131 Point.prototype.equals = function (other) {
42132 return this.x === other.x && this.y === other.y && this.time === other.time;
42134 Point.prototype.velocityFrom = function (start) {
42135 return this.time !== start.time
42136 ? this.distanceTo(start) / (this.time - start.time)
42143 // Bezier Constructor
42144 Bezier: (function () {
42145 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42146 this.startPoint = startPoint;
42147 this.control2 = control2;
42148 this.control1 = control1;
42149 this.endPoint = endPoint;
42150 this.startWidth = startWidth;
42151 this.endWidth = endWidth;
42153 Bezier.fromPoints = function (points, widths, scope) {
42154 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42155 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42156 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42158 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42159 var dx1 = s1.x - s2.x;
42160 var dy1 = s1.y - s2.y;
42161 var dx2 = s2.x - s3.x;
42162 var dy2 = s2.y - s3.y;
42163 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42164 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42165 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42166 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42167 var dxm = m1.x - m2.x;
42168 var dym = m1.y - m2.y;
42169 var k = l2 / (l1 + l2);
42170 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42171 var tx = s2.x - cm.x;
42172 var ty = s2.y - cm.y;
42174 c1: new scope.Point(m1.x + tx, m1.y + ty),
42175 c2: new scope.Point(m2.x + tx, m2.y + ty)
42178 Bezier.prototype.length = function () {
42183 for (var i = 0; i <= steps; i += 1) {
42185 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42186 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42188 var xdiff = cx - px;
42189 var ydiff = cy - py;
42190 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42197 Bezier.prototype.point = function (t, start, c1, c2, end) {
42198 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42199 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42200 + (3.0 * c2 * (1.0 - t) * t * t)
42201 + (end * t * t * t);
42206 throttleStroke: function(fn, wait) {
42207 if (wait === void 0) { wait = 250; }
42209 var timeout = null;
42213 var later = function () {
42214 previous = Date.now();
42216 result = fn.apply(storedContext, storedArgs);
42218 storedContext = null;
42222 return function wrapper() {
42224 for (var _i = 0; _i < arguments.length; _i++) {
42225 args[_i] = arguments[_i];
42227 var now = Date.now();
42228 var remaining = wait - (now - previous);
42229 storedContext = this;
42231 if (remaining <= 0 || remaining > wait) {
42233 clearTimeout(timeout);
42237 result = fn.apply(storedContext, storedArgs);
42239 storedContext = null;
42243 else if (!timeout) {
42244 timeout = window.setTimeout(later, remaining);