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.setWidth(ctr.getWidth());
7324 // if the body has a max height - and then scrolls - we should perhaps set up the height here
7325 // this needs fixing for various usage - currently only hydra job advers I think..
7327 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7329 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7332 cw = Math.max(cw, this.totalWidth);
7333 this.getGridEl().select('tr',true).setWidth(cw);
7334 // resize 'expandable coloumn?
7336 return; // we doe not have a view in this design..
7339 onBodyScroll: function()
7341 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7343 this.mainHead.setStyle({
7344 'position' : 'relative',
7345 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7351 var scrollHeight = this.mainBody.dom.scrollHeight;
7353 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7355 var height = this.mainBody.getHeight();
7357 if(scrollHeight - height == scrollTop) {
7359 var total = this.ds.getTotalCount();
7361 if(this.footer.cursor + this.footer.pageSize < total){
7363 this.footer.ds.load({
7365 start : this.footer.cursor + this.footer.pageSize,
7366 limit : this.footer.pageSize
7376 onHeaderChange : function()
7378 var header = this.renderHeader();
7379 var table = this.el.select('table', true).first();
7381 this.mainHead.remove();
7382 this.mainHead = table.createChild(header, this.mainBody, false);
7385 onHiddenChange : function(colModel, colIndex, hidden)
7387 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7388 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7390 this.CSS.updateRule(thSelector, "display", "");
7391 this.CSS.updateRule(tdSelector, "display", "");
7394 this.CSS.updateRule(thSelector, "display", "none");
7395 this.CSS.updateRule(tdSelector, "display", "none");
7398 this.onHeaderChange();
7402 setColumnWidth: function(col_index, width)
7404 // width = "md-2 xs-2..."
7405 if(!this.colModel.config[col_index]) {
7409 var w = width.split(" ");
7411 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7413 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7416 for(var j = 0; j < w.length; j++) {
7422 var size_cls = w[j].split("-");
7424 if(!Number.isInteger(size_cls[1] * 1)) {
7428 if(!this.colModel.config[col_index][size_cls[0]]) {
7432 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7436 h_row[0].classList.replace(
7437 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7438 "col-"+size_cls[0]+"-"+size_cls[1]
7441 for(var i = 0; i < rows.length; i++) {
7443 var size_cls = w[j].split("-");
7445 if(!Number.isInteger(size_cls[1] * 1)) {
7449 if(!this.colModel.config[col_index][size_cls[0]]) {
7453 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7457 rows[i].classList.replace(
7458 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7459 "col-"+size_cls[0]+"-"+size_cls[1]
7463 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7478 * @class Roo.bootstrap.TableCell
7479 * @extends Roo.bootstrap.Component
7480 * Bootstrap TableCell class
7481 * @cfg {String} html cell contain text
7482 * @cfg {String} cls cell class
7483 * @cfg {String} tag cell tag (td|th) default td
7484 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7485 * @cfg {String} align Aligns the content in a cell
7486 * @cfg {String} axis Categorizes cells
7487 * @cfg {String} bgcolor Specifies the background color of a cell
7488 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7489 * @cfg {Number} colspan Specifies the number of columns a cell should span
7490 * @cfg {String} headers Specifies one or more header cells a cell is related to
7491 * @cfg {Number} height Sets the height of a cell
7492 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7493 * @cfg {Number} rowspan Sets the number of rows a cell should span
7494 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7495 * @cfg {String} valign Vertical aligns the content in a cell
7496 * @cfg {Number} width Specifies the width of a cell
7499 * Create a new TableCell
7500 * @param {Object} config The config object
7503 Roo.bootstrap.TableCell = function(config){
7504 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7507 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7527 getAutoCreate : function(){
7528 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7548 cfg.align=this.align
7554 cfg.bgcolor=this.bgcolor
7557 cfg.charoff=this.charoff
7560 cfg.colspan=this.colspan
7563 cfg.headers=this.headers
7566 cfg.height=this.height
7569 cfg.nowrap=this.nowrap
7572 cfg.rowspan=this.rowspan
7575 cfg.scope=this.scope
7578 cfg.valign=this.valign
7581 cfg.width=this.width
7600 * @class Roo.bootstrap.TableRow
7601 * @extends Roo.bootstrap.Component
7602 * Bootstrap TableRow class
7603 * @cfg {String} cls row class
7604 * @cfg {String} align Aligns the content in a table row
7605 * @cfg {String} bgcolor Specifies a background color for a table row
7606 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7607 * @cfg {String} valign Vertical aligns the content in a table row
7610 * Create a new TableRow
7611 * @param {Object} config The config object
7614 Roo.bootstrap.TableRow = function(config){
7615 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7618 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7626 getAutoCreate : function(){
7627 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7637 cfg.align = this.align;
7640 cfg.bgcolor = this.bgcolor;
7643 cfg.charoff = this.charoff;
7646 cfg.valign = this.valign;
7664 * @class Roo.bootstrap.TableBody
7665 * @extends Roo.bootstrap.Component
7666 * Bootstrap TableBody class
7667 * @cfg {String} cls element class
7668 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7669 * @cfg {String} align Aligns the content inside the element
7670 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7671 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7674 * Create a new TableBody
7675 * @param {Object} config The config object
7678 Roo.bootstrap.TableBody = function(config){
7679 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7682 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7690 getAutoCreate : function(){
7691 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7705 cfg.align = this.align;
7708 cfg.charoff = this.charoff;
7711 cfg.valign = this.valign;
7718 // initEvents : function()
7725 // this.store = Roo.factory(this.store, Roo.data);
7726 // this.store.on('load', this.onLoad, this);
7728 // this.store.load();
7732 // onLoad: function ()
7734 // this.fireEvent('load', this);
7744 * Ext JS Library 1.1.1
7745 * Copyright(c) 2006-2007, Ext JS, LLC.
7747 * Originally Released Under LGPL - original licence link has changed is not relivant.
7750 * <script type="text/javascript">
7753 // as we use this in bootstrap.
7754 Roo.namespace('Roo.form');
7756 * @class Roo.form.Action
7757 * Internal Class used to handle form actions
7759 * @param {Roo.form.BasicForm} el The form element or its id
7760 * @param {Object} config Configuration options
7765 // define the action interface
7766 Roo.form.Action = function(form, options){
7768 this.options = options || {};
7771 * Client Validation Failed
7774 Roo.form.Action.CLIENT_INVALID = 'client';
7776 * Server Validation Failed
7779 Roo.form.Action.SERVER_INVALID = 'server';
7781 * Connect to Server Failed
7784 Roo.form.Action.CONNECT_FAILURE = 'connect';
7786 * Reading Data from Server Failed
7789 Roo.form.Action.LOAD_FAILURE = 'load';
7791 Roo.form.Action.prototype = {
7793 failureType : undefined,
7794 response : undefined,
7798 run : function(options){
7803 success : function(response){
7808 handleResponse : function(response){
7812 // default connection failure
7813 failure : function(response){
7815 this.response = response;
7816 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7817 this.form.afterAction(this, false);
7820 processResponse : function(response){
7821 this.response = response;
7822 if(!response.responseText){
7825 this.result = this.handleResponse(response);
7829 // utility functions used internally
7830 getUrl : function(appendParams){
7831 var url = this.options.url || this.form.url || this.form.el.dom.action;
7833 var p = this.getParams();
7835 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7841 getMethod : function(){
7842 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7845 getParams : function(){
7846 var bp = this.form.baseParams;
7847 var p = this.options.params;
7849 if(typeof p == "object"){
7850 p = Roo.urlEncode(Roo.applyIf(p, bp));
7851 }else if(typeof p == 'string' && bp){
7852 p += '&' + Roo.urlEncode(bp);
7855 p = Roo.urlEncode(bp);
7860 createCallback : function(){
7862 success: this.success,
7863 failure: this.failure,
7865 timeout: (this.form.timeout*1000),
7866 upload: this.form.fileUpload ? this.success : undefined
7871 Roo.form.Action.Submit = function(form, options){
7872 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7875 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7878 haveProgress : false,
7879 uploadComplete : false,
7881 // uploadProgress indicator.
7882 uploadProgress : function()
7884 if (!this.form.progressUrl) {
7888 if (!this.haveProgress) {
7889 Roo.MessageBox.progress("Uploading", "Uploading");
7891 if (this.uploadComplete) {
7892 Roo.MessageBox.hide();
7896 this.haveProgress = true;
7898 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7900 var c = new Roo.data.Connection();
7902 url : this.form.progressUrl,
7907 success : function(req){
7908 //console.log(data);
7912 rdata = Roo.decode(req.responseText)
7914 Roo.log("Invalid data from server..");
7918 if (!rdata || !rdata.success) {
7920 Roo.MessageBox.alert(Roo.encode(rdata));
7923 var data = rdata.data;
7925 if (this.uploadComplete) {
7926 Roo.MessageBox.hide();
7931 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7932 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7935 this.uploadProgress.defer(2000,this);
7938 failure: function(data) {
7939 Roo.log('progress url failed ');
7950 // run get Values on the form, so it syncs any secondary forms.
7951 this.form.getValues();
7953 var o = this.options;
7954 var method = this.getMethod();
7955 var isPost = method == 'POST';
7956 if(o.clientValidation === false || this.form.isValid()){
7958 if (this.form.progressUrl) {
7959 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7960 (new Date() * 1) + '' + Math.random());
7965 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7966 form:this.form.el.dom,
7967 url:this.getUrl(!isPost),
7969 params:isPost ? this.getParams() : null,
7970 isUpload: this.form.fileUpload,
7971 formData : this.form.formData
7974 this.uploadProgress();
7976 }else if (o.clientValidation !== false){ // client validation failed
7977 this.failureType = Roo.form.Action.CLIENT_INVALID;
7978 this.form.afterAction(this, false);
7982 success : function(response)
7984 this.uploadComplete= true;
7985 if (this.haveProgress) {
7986 Roo.MessageBox.hide();
7990 var result = this.processResponse(response);
7991 if(result === true || result.success){
7992 this.form.afterAction(this, true);
7996 this.form.markInvalid(result.errors);
7997 this.failureType = Roo.form.Action.SERVER_INVALID;
7999 this.form.afterAction(this, false);
8001 failure : function(response)
8003 this.uploadComplete= true;
8004 if (this.haveProgress) {
8005 Roo.MessageBox.hide();
8008 this.response = response;
8009 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8010 this.form.afterAction(this, false);
8013 handleResponse : function(response){
8014 if(this.form.errorReader){
8015 var rs = this.form.errorReader.read(response);
8018 for(var i = 0, len = rs.records.length; i < len; i++) {
8019 var r = rs.records[i];
8023 if(errors.length < 1){
8027 success : rs.success,
8033 ret = Roo.decode(response.responseText);
8037 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8047 Roo.form.Action.Load = function(form, options){
8048 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8049 this.reader = this.form.reader;
8052 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8057 Roo.Ajax.request(Roo.apply(
8058 this.createCallback(), {
8059 method:this.getMethod(),
8060 url:this.getUrl(false),
8061 params:this.getParams()
8065 success : function(response){
8067 var result = this.processResponse(response);
8068 if(result === true || !result.success || !result.data){
8069 this.failureType = Roo.form.Action.LOAD_FAILURE;
8070 this.form.afterAction(this, false);
8073 this.form.clearInvalid();
8074 this.form.setValues(result.data);
8075 this.form.afterAction(this, true);
8078 handleResponse : function(response){
8079 if(this.form.reader){
8080 var rs = this.form.reader.read(response);
8081 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8083 success : rs.success,
8087 return Roo.decode(response.responseText);
8091 Roo.form.Action.ACTION_TYPES = {
8092 'load' : Roo.form.Action.Load,
8093 'submit' : Roo.form.Action.Submit
8102 * @class Roo.bootstrap.Form
8103 * @extends Roo.bootstrap.Component
8104 * Bootstrap Form class
8105 * @cfg {String} method GET | POST (default POST)
8106 * @cfg {String} labelAlign top | left (default top)
8107 * @cfg {String} align left | right - for navbars
8108 * @cfg {Boolean} loadMask load mask when submit (default true)
8113 * @param {Object} config The config object
8117 Roo.bootstrap.Form = function(config){
8119 Roo.bootstrap.Form.superclass.constructor.call(this, config);
8121 Roo.bootstrap.Form.popover.apply();
8125 * @event clientvalidation
8126 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8127 * @param {Form} this
8128 * @param {Boolean} valid true if the form has passed client-side validation
8130 clientvalidation: true,
8132 * @event beforeaction
8133 * Fires before any action is performed. Return false to cancel the action.
8134 * @param {Form} this
8135 * @param {Action} action The action to be performed
8139 * @event actionfailed
8140 * Fires when an action fails.
8141 * @param {Form} this
8142 * @param {Action} action The action that failed
8144 actionfailed : true,
8146 * @event actioncomplete
8147 * Fires when an action is completed.
8148 * @param {Form} this
8149 * @param {Action} action The action that completed
8151 actioncomplete : true
8155 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
8158 * @cfg {String} method
8159 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8164 * The URL to use for form actions if one isn't supplied in the action options.
8167 * @cfg {Boolean} fileUpload
8168 * Set to true if this form is a file upload.
8172 * @cfg {Object} baseParams
8173 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8177 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8181 * @cfg {Sting} align (left|right) for navbar forms
8186 activeAction : null,
8189 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8190 * element by passing it or its id or mask the form itself by passing in true.
8193 waitMsgTarget : false,
8198 * @cfg {Boolean} errorMask (true|false) default false
8203 * @cfg {Number} maskOffset Default 100
8208 * @cfg {Boolean} maskBody
8212 getAutoCreate : function(){
8216 method : this.method || 'POST',
8217 id : this.id || Roo.id(),
8220 if (this.parent().xtype.match(/^Nav/)) {
8221 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8225 if (this.labelAlign == 'left' ) {
8226 cfg.cls += ' form-horizontal';
8232 initEvents : function()
8234 this.el.on('submit', this.onSubmit, this);
8235 // this was added as random key presses on the form where triggering form submit.
8236 this.el.on('keypress', function(e) {
8237 if (e.getCharCode() != 13) {
8240 // we might need to allow it for textareas.. and some other items.
8241 // check e.getTarget().
8243 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8247 Roo.log("keypress blocked");
8255 onSubmit : function(e){
8260 * Returns true if client-side validation on the form is successful.
8263 isValid : function(){
8264 var items = this.getItems();
8268 items.each(function(f){
8274 Roo.log('invalid field: ' + f.name);
8278 if(!target && f.el.isVisible(true)){
8284 if(this.errorMask && !valid){
8285 Roo.bootstrap.Form.popover.mask(this, target);
8292 * Returns true if any fields in this form have changed since their original load.
8295 isDirty : function(){
8297 var items = this.getItems();
8298 items.each(function(f){
8308 * Performs a predefined action (submit or load) or custom actions you define on this form.
8309 * @param {String} actionName The name of the action type
8310 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8311 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8312 * accept other config options):
8314 Property Type Description
8315 ---------------- --------------- ----------------------------------------------------------------------------------
8316 url String The url for the action (defaults to the form's url)
8317 method String The form method to use (defaults to the form's method, or POST if not defined)
8318 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8319 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8320 validate the form on the client (defaults to false)
8322 * @return {BasicForm} this
8324 doAction : function(action, options){
8325 if(typeof action == 'string'){
8326 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8328 if(this.fireEvent('beforeaction', this, action) !== false){
8329 this.beforeAction(action);
8330 action.run.defer(100, action);
8336 beforeAction : function(action){
8337 var o = action.options;
8342 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8344 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8347 // not really supported yet.. ??
8349 //if(this.waitMsgTarget === true){
8350 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8351 //}else if(this.waitMsgTarget){
8352 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8353 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8355 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8361 afterAction : function(action, success){
8362 this.activeAction = null;
8363 var o = action.options;
8368 Roo.get(document.body).unmask();
8374 //if(this.waitMsgTarget === true){
8375 // this.el.unmask();
8376 //}else if(this.waitMsgTarget){
8377 // this.waitMsgTarget.unmask();
8379 // Roo.MessageBox.updateProgress(1);
8380 // Roo.MessageBox.hide();
8387 Roo.callback(o.success, o.scope, [this, action]);
8388 this.fireEvent('actioncomplete', this, action);
8392 // failure condition..
8393 // we have a scenario where updates need confirming.
8394 // eg. if a locking scenario exists..
8395 // we look for { errors : { needs_confirm : true }} in the response.
8397 (typeof(action.result) != 'undefined') &&
8398 (typeof(action.result.errors) != 'undefined') &&
8399 (typeof(action.result.errors.needs_confirm) != 'undefined')
8402 Roo.log("not supported yet");
8405 Roo.MessageBox.confirm(
8406 "Change requires confirmation",
8407 action.result.errorMsg,
8412 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8422 Roo.callback(o.failure, o.scope, [this, action]);
8423 // show an error message if no failed handler is set..
8424 if (!this.hasListener('actionfailed')) {
8425 Roo.log("need to add dialog support");
8427 Roo.MessageBox.alert("Error",
8428 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8429 action.result.errorMsg :
8430 "Saving Failed, please check your entries or try again"
8435 this.fireEvent('actionfailed', this, action);
8440 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8441 * @param {String} id The value to search for
8444 findField : function(id){
8445 var items = this.getItems();
8446 var field = items.get(id);
8448 items.each(function(f){
8449 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8456 return field || null;
8459 * Mark fields in this form invalid in bulk.
8460 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8461 * @return {BasicForm} this
8463 markInvalid : function(errors){
8464 if(errors instanceof Array){
8465 for(var i = 0, len = errors.length; i < len; i++){
8466 var fieldError = errors[i];
8467 var f = this.findField(fieldError.id);
8469 f.markInvalid(fieldError.msg);
8475 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8476 field.markInvalid(errors[id]);
8480 //Roo.each(this.childForms || [], function (f) {
8481 // f.markInvalid(errors);
8488 * Set values for fields in this form in bulk.
8489 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8490 * @return {BasicForm} this
8492 setValues : function(values){
8493 if(values instanceof Array){ // array of objects
8494 for(var i = 0, len = values.length; i < len; i++){
8496 var f = this.findField(v.id);
8498 f.setValue(v.value);
8499 if(this.trackResetOnLoad){
8500 f.originalValue = f.getValue();
8504 }else{ // object hash
8507 if(typeof values[id] != 'function' && (field = this.findField(id))){
8509 if (field.setFromData &&
8511 field.displayField &&
8512 // combos' with local stores can
8513 // be queried via setValue()
8514 // to set their value..
8515 (field.store && !field.store.isLocal)
8519 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8520 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8521 field.setFromData(sd);
8523 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8525 field.setFromData(values);
8528 field.setValue(values[id]);
8532 if(this.trackResetOnLoad){
8533 field.originalValue = field.getValue();
8539 //Roo.each(this.childForms || [], function (f) {
8540 // f.setValues(values);
8547 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8548 * they are returned as an array.
8549 * @param {Boolean} asString
8552 getValues : function(asString){
8553 //if (this.childForms) {
8554 // copy values from the child forms
8555 // Roo.each(this.childForms, function (f) {
8556 // this.setValues(f.getValues());
8562 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8563 if(asString === true){
8566 return Roo.urlDecode(fs);
8570 * Returns the fields in this form as an object with key/value pairs.
8571 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8574 getFieldValues : function(with_hidden)
8576 var items = this.getItems();
8578 items.each(function(f){
8584 var v = f.getValue();
8586 if (f.inputType =='radio') {
8587 if (typeof(ret[f.getName()]) == 'undefined') {
8588 ret[f.getName()] = ''; // empty..
8591 if (!f.el.dom.checked) {
8599 if(f.xtype == 'MoneyField'){
8600 ret[f.currencyName] = f.getCurrency();
8603 // not sure if this supported any more..
8604 if ((typeof(v) == 'object') && f.getRawValue) {
8605 v = f.getRawValue() ; // dates..
8607 // combo boxes where name != hiddenName...
8608 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8609 ret[f.name] = f.getRawValue();
8611 ret[f.getName()] = v;
8618 * Clears all invalid messages in this form.
8619 * @return {BasicForm} this
8621 clearInvalid : function(){
8622 var items = this.getItems();
8624 items.each(function(f){
8633 * @return {BasicForm} this
8636 var items = this.getItems();
8637 items.each(function(f){
8641 Roo.each(this.childForms || [], function (f) {
8649 getItems : function()
8651 var r=new Roo.util.MixedCollection(false, function(o){
8652 return o.id || (o.id = Roo.id());
8654 var iter = function(el) {
8661 Roo.each(el.items,function(e) {
8670 hideFields : function(items)
8672 Roo.each(items, function(i){
8674 var f = this.findField(i);
8685 showFields : function(items)
8687 Roo.each(items, function(i){
8689 var f = this.findField(i);
8702 Roo.apply(Roo.bootstrap.Form, {
8729 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8730 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8731 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8732 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8735 this.maskEl.top.enableDisplayMode("block");
8736 this.maskEl.left.enableDisplayMode("block");
8737 this.maskEl.bottom.enableDisplayMode("block");
8738 this.maskEl.right.enableDisplayMode("block");
8740 this.toolTip = new Roo.bootstrap.Tooltip({
8741 cls : 'roo-form-error-popover',
8743 'left' : ['r-l', [-2,0], 'right'],
8744 'right' : ['l-r', [2,0], 'left'],
8745 'bottom' : ['tl-bl', [0,2], 'top'],
8746 'top' : [ 'bl-tl', [0,-2], 'bottom']
8750 this.toolTip.render(Roo.get(document.body));
8752 this.toolTip.el.enableDisplayMode("block");
8754 Roo.get(document.body).on('click', function(){
8758 Roo.get(document.body).on('touchstart', function(){
8762 this.isApplied = true
8765 mask : function(form, target)
8769 this.target = target;
8771 if(!this.form.errorMask || !target.el){
8775 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8777 Roo.log(scrollable);
8779 var ot = this.target.el.calcOffsetsTo(scrollable);
8781 var scrollTo = ot[1] - this.form.maskOffset;
8783 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8785 scrollable.scrollTo('top', scrollTo);
8787 var box = this.target.el.getBox();
8789 var zIndex = Roo.bootstrap.Modal.zIndex++;
8792 this.maskEl.top.setStyle('position', 'absolute');
8793 this.maskEl.top.setStyle('z-index', zIndex);
8794 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8795 this.maskEl.top.setLeft(0);
8796 this.maskEl.top.setTop(0);
8797 this.maskEl.top.show();
8799 this.maskEl.left.setStyle('position', 'absolute');
8800 this.maskEl.left.setStyle('z-index', zIndex);
8801 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8802 this.maskEl.left.setLeft(0);
8803 this.maskEl.left.setTop(box.y - this.padding);
8804 this.maskEl.left.show();
8806 this.maskEl.bottom.setStyle('position', 'absolute');
8807 this.maskEl.bottom.setStyle('z-index', zIndex);
8808 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8809 this.maskEl.bottom.setLeft(0);
8810 this.maskEl.bottom.setTop(box.bottom + this.padding);
8811 this.maskEl.bottom.show();
8813 this.maskEl.right.setStyle('position', 'absolute');
8814 this.maskEl.right.setStyle('z-index', zIndex);
8815 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8816 this.maskEl.right.setLeft(box.right + this.padding);
8817 this.maskEl.right.setTop(box.y - this.padding);
8818 this.maskEl.right.show();
8820 this.toolTip.bindEl = this.target.el;
8822 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8824 var tip = this.target.blankText;
8826 if(this.target.getValue() !== '' ) {
8828 if (this.target.invalidText.length) {
8829 tip = this.target.invalidText;
8830 } else if (this.target.regexText.length){
8831 tip = this.target.regexText;
8835 this.toolTip.show(tip);
8837 this.intervalID = window.setInterval(function() {
8838 Roo.bootstrap.Form.popover.unmask();
8841 window.onwheel = function(){ return false;};
8843 (function(){ this.isMasked = true; }).defer(500, this);
8849 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8853 this.maskEl.top.setStyle('position', 'absolute');
8854 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8855 this.maskEl.top.hide();
8857 this.maskEl.left.setStyle('position', 'absolute');
8858 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8859 this.maskEl.left.hide();
8861 this.maskEl.bottom.setStyle('position', 'absolute');
8862 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8863 this.maskEl.bottom.hide();
8865 this.maskEl.right.setStyle('position', 'absolute');
8866 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8867 this.maskEl.right.hide();
8869 this.toolTip.hide();
8871 this.toolTip.el.hide();
8873 window.onwheel = function(){ return true;};
8875 if(this.intervalID){
8876 window.clearInterval(this.intervalID);
8877 this.intervalID = false;
8880 this.isMasked = false;
8890 * Ext JS Library 1.1.1
8891 * Copyright(c) 2006-2007, Ext JS, LLC.
8893 * Originally Released Under LGPL - original licence link has changed is not relivant.
8896 * <script type="text/javascript">
8899 * @class Roo.form.VTypes
8900 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8903 Roo.form.VTypes = function(){
8904 // closure these in so they are only created once.
8905 var alpha = /^[a-zA-Z_]+$/;
8906 var alphanum = /^[a-zA-Z0-9_]+$/;
8907 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8908 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8910 // All these messages and functions are configurable
8913 * The function used to validate email addresses
8914 * @param {String} value The email address
8916 'email' : function(v){
8917 return email.test(v);
8920 * The error text to display when the email validation function returns false
8923 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8925 * The keystroke filter mask to be applied on email input
8928 'emailMask' : /[a-z0-9_\.\-@]/i,
8931 * The function used to validate URLs
8932 * @param {String} value The URL
8934 'url' : function(v){
8938 * The error text to display when the url validation function returns false
8941 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8944 * The function used to validate alpha values
8945 * @param {String} value The value
8947 'alpha' : function(v){
8948 return alpha.test(v);
8951 * The error text to display when the alpha validation function returns false
8954 'alphaText' : 'This field should only contain letters and _',
8956 * The keystroke filter mask to be applied on alpha input
8959 'alphaMask' : /[a-z_]/i,
8962 * The function used to validate alphanumeric values
8963 * @param {String} value The value
8965 'alphanum' : function(v){
8966 return alphanum.test(v);
8969 * The error text to display when the alphanumeric validation function returns false
8972 'alphanumText' : 'This field should only contain letters, numbers and _',
8974 * The keystroke filter mask to be applied on alphanumeric input
8977 'alphanumMask' : /[a-z0-9_]/i
8987 * @class Roo.bootstrap.Input
8988 * @extends Roo.bootstrap.Component
8989 * Bootstrap Input class
8990 * @cfg {Boolean} disabled is it disabled
8991 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8992 * @cfg {String} name name of the input
8993 * @cfg {string} fieldLabel - the label associated
8994 * @cfg {string} placeholder - placeholder to put in text.
8995 * @cfg {string} before - input group add on before
8996 * @cfg {string} after - input group add on after
8997 * @cfg {string} size - (lg|sm) or leave empty..
8998 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8999 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
9000 * @cfg {Number} md colspan out of 12 for computer-sized screens
9001 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
9002 * @cfg {string} value default value of the input
9003 * @cfg {Number} labelWidth set the width of label
9004 * @cfg {Number} labellg set the width of label (1-12)
9005 * @cfg {Number} labelmd set the width of label (1-12)
9006 * @cfg {Number} labelsm set the width of label (1-12)
9007 * @cfg {Number} labelxs set the width of label (1-12)
9008 * @cfg {String} labelAlign (top|left)
9009 * @cfg {Boolean} readOnly Specifies that the field should be read-only
9010 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9011 * @cfg {String} indicatorpos (left|right) default left
9012 * @cfg {String} capture (user|camera) use for file input only. (default empty)
9013 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9015 * @cfg {String} align (left|center|right) Default left
9016 * @cfg {Boolean} forceFeedback (true|false) Default false
9019 * Create a new Input
9020 * @param {Object} config The config object
9023 Roo.bootstrap.Input = function(config){
9025 Roo.bootstrap.Input.superclass.constructor.call(this, config);
9030 * Fires when this field receives input focus.
9031 * @param {Roo.form.Field} this
9036 * Fires when this field loses input focus.
9037 * @param {Roo.form.Field} this
9042 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
9043 * {@link Roo.EventObject#getKey} to determine which key was pressed.
9044 * @param {Roo.form.Field} this
9045 * @param {Roo.EventObject} e The event object
9050 * Fires just before the field blurs if the field value has changed.
9051 * @param {Roo.form.Field} this
9052 * @param {Mixed} newValue The new value
9053 * @param {Mixed} oldValue The original value
9058 * Fires after the field has been marked as invalid.
9059 * @param {Roo.form.Field} this
9060 * @param {String} msg The validation message
9065 * Fires after the field has been validated with no errors.
9066 * @param {Roo.form.Field} this
9071 * Fires after the key up
9072 * @param {Roo.form.Field} this
9073 * @param {Roo.EventObject} e The event Object
9079 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
9081 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9082 automatic validation (defaults to "keyup").
9084 validationEvent : "keyup",
9086 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9088 validateOnBlur : true,
9090 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9092 validationDelay : 250,
9094 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9096 focusClass : "x-form-focus", // not needed???
9100 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9102 invalidClass : "has-warning",
9105 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9107 validClass : "has-success",
9110 * @cfg {Boolean} hasFeedback (true|false) default true
9115 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9117 invalidFeedbackClass : "glyphicon-warning-sign",
9120 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9122 validFeedbackClass : "glyphicon-ok",
9125 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9127 selectOnFocus : false,
9130 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9134 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9139 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9141 disableKeyFilter : false,
9144 * @cfg {Boolean} disabled True to disable the field (defaults to false).
9148 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9152 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9154 blankText : "Please complete this mandatory field",
9157 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9161 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9163 maxLength : Number.MAX_VALUE,
9165 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9167 minLengthText : "The minimum length for this field is {0}",
9169 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9171 maxLengthText : "The maximum length for this field is {0}",
9175 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9176 * If available, this function will be called only after the basic validators all return true, and will be passed the
9177 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9181 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9182 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9183 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
9187 * @cfg {String} regexText -- Depricated - use Invalid Text
9192 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9198 autocomplete: false,
9217 formatedValue : false,
9218 forceFeedback : false,
9220 indicatorpos : 'left',
9230 parentLabelAlign : function()
9233 while (parent.parent()) {
9234 parent = parent.parent();
9235 if (typeof(parent.labelAlign) !='undefined') {
9236 return parent.labelAlign;
9243 getAutoCreate : function()
9245 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9251 if(this.inputType != 'hidden'){
9252 cfg.cls = 'form-group' //input-group
9258 type : this.inputType,
9260 cls : 'form-control',
9261 placeholder : this.placeholder || '',
9262 autocomplete : this.autocomplete || 'new-password'
9265 if(this.capture.length){
9266 input.capture = this.capture;
9269 if(this.accept.length){
9270 input.accept = this.accept + "/*";
9274 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9277 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9278 input.maxLength = this.maxLength;
9281 if (this.disabled) {
9282 input.disabled=true;
9285 if (this.readOnly) {
9286 input.readonly=true;
9290 input.name = this.name;
9294 input.cls += ' input-' + this.size;
9298 ['xs','sm','md','lg'].map(function(size){
9299 if (settings[size]) {
9300 cfg.cls += ' col-' + size + '-' + settings[size];
9304 var inputblock = input;
9308 cls: 'glyphicon form-control-feedback'
9311 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9314 cls : 'has-feedback',
9322 if (this.before || this.after) {
9325 cls : 'input-group',
9329 if (this.before && typeof(this.before) == 'string') {
9331 inputblock.cn.push({
9333 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9337 if (this.before && typeof(this.before) == 'object') {
9338 this.before = Roo.factory(this.before);
9340 inputblock.cn.push({
9342 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9343 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9347 inputblock.cn.push(input);
9349 if (this.after && typeof(this.after) == 'string') {
9350 inputblock.cn.push({
9352 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9356 if (this.after && typeof(this.after) == 'object') {
9357 this.after = Roo.factory(this.after);
9359 inputblock.cn.push({
9361 cls : 'roo-input-after input-group-append input-group-text input-group-' +
9362 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9366 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9367 inputblock.cls += ' has-feedback';
9368 inputblock.cn.push(feedback);
9373 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9374 tooltip : 'This field is required'
9376 if (Roo.bootstrap.version == 4) {
9379 style : 'display-none'
9382 if (align ==='left' && this.fieldLabel.length) {
9384 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
9391 cls : 'control-label col-form-label',
9392 html : this.fieldLabel
9403 var labelCfg = cfg.cn[1];
9404 var contentCfg = cfg.cn[2];
9406 if(this.indicatorpos == 'right'){
9411 cls : 'control-label col-form-label',
9415 html : this.fieldLabel
9429 labelCfg = cfg.cn[0];
9430 contentCfg = cfg.cn[1];
9434 if(this.labelWidth > 12){
9435 labelCfg.style = "width: " + this.labelWidth + 'px';
9438 if(this.labelWidth < 13 && this.labelmd == 0){
9439 this.labelmd = this.labelWidth;
9442 if(this.labellg > 0){
9443 labelCfg.cls += ' col-lg-' + this.labellg;
9444 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9447 if(this.labelmd > 0){
9448 labelCfg.cls += ' col-md-' + this.labelmd;
9449 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9452 if(this.labelsm > 0){
9453 labelCfg.cls += ' col-sm-' + this.labelsm;
9454 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9457 if(this.labelxs > 0){
9458 labelCfg.cls += ' col-xs-' + this.labelxs;
9459 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9463 } else if ( this.fieldLabel.length) {
9468 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9469 tooltip : 'This field is required'
9473 //cls : 'input-group-addon',
9474 html : this.fieldLabel
9482 if(this.indicatorpos == 'right'){
9487 //cls : 'input-group-addon',
9488 html : this.fieldLabel
9493 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9494 tooltip : 'This field is required'
9514 if (this.parentType === 'Navbar' && this.parent().bar) {
9515 cfg.cls += ' navbar-form';
9518 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9519 // on BS4 we do this only if not form
9520 cfg.cls += ' navbar-form';
9528 * return the real input element.
9530 inputEl: function ()
9532 return this.el.select('input.form-control',true).first();
9535 tooltipEl : function()
9537 return this.inputEl();
9540 indicatorEl : function()
9542 if (Roo.bootstrap.version == 4) {
9543 return false; // not enabled in v4 yet.
9546 var indicator = this.el.select('i.roo-required-indicator',true).first();
9556 setDisabled : function(v)
9558 var i = this.inputEl().dom;
9560 i.removeAttribute('disabled');
9564 i.setAttribute('disabled','true');
9566 initEvents : function()
9569 this.inputEl().on("keydown" , this.fireKey, this);
9570 this.inputEl().on("focus", this.onFocus, this);
9571 this.inputEl().on("blur", this.onBlur, this);
9573 this.inputEl().relayEvent('keyup', this);
9575 this.indicator = this.indicatorEl();
9578 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9581 // reference to original value for reset
9582 this.originalValue = this.getValue();
9583 //Roo.form.TextField.superclass.initEvents.call(this);
9584 if(this.validationEvent == 'keyup'){
9585 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9586 this.inputEl().on('keyup', this.filterValidation, this);
9588 else if(this.validationEvent !== false){
9589 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9592 if(this.selectOnFocus){
9593 this.on("focus", this.preFocus, this);
9596 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9597 this.inputEl().on("keypress", this.filterKeys, this);
9599 this.inputEl().relayEvent('keypress', this);
9602 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9603 this.el.on("click", this.autoSize, this);
9606 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9607 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9610 if (typeof(this.before) == 'object') {
9611 this.before.render(this.el.select('.roo-input-before',true).first());
9613 if (typeof(this.after) == 'object') {
9614 this.after.render(this.el.select('.roo-input-after',true).first());
9617 this.inputEl().on('change', this.onChange, this);
9620 filterValidation : function(e){
9621 if(!e.isNavKeyPress()){
9622 this.validationTask.delay(this.validationDelay);
9626 * Validates the field value
9627 * @return {Boolean} True if the value is valid, else false
9629 validate : function(){
9630 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9631 if(this.disabled || this.validateValue(this.getRawValue())){
9642 * Validates a value according to the field's validation rules and marks the field as invalid
9643 * if the validation fails
9644 * @param {Mixed} value The value to validate
9645 * @return {Boolean} True if the value is valid, else false
9647 validateValue : function(value)
9649 if(this.getVisibilityEl().hasClass('hidden')){
9653 if(value.length < 1) { // if it's blank
9654 if(this.allowBlank){
9660 if(value.length < this.minLength){
9663 if(value.length > this.maxLength){
9667 var vt = Roo.form.VTypes;
9668 if(!vt[this.vtype](value, this)){
9672 if(typeof this.validator == "function"){
9673 var msg = this.validator(value);
9677 if (typeof(msg) == 'string') {
9678 this.invalidText = msg;
9682 if(this.regex && !this.regex.test(value)){
9690 fireKey : function(e){
9691 //Roo.log('field ' + e.getKey());
9692 if(e.isNavKeyPress()){
9693 this.fireEvent("specialkey", this, e);
9696 focus : function (selectText){
9698 this.inputEl().focus();
9699 if(selectText === true){
9700 this.inputEl().dom.select();
9706 onFocus : function(){
9707 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9708 // this.el.addClass(this.focusClass);
9711 this.hasFocus = true;
9712 this.startValue = this.getValue();
9713 this.fireEvent("focus", this);
9717 beforeBlur : Roo.emptyFn,
9721 onBlur : function(){
9723 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9724 //this.el.removeClass(this.focusClass);
9726 this.hasFocus = false;
9727 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9730 var v = this.getValue();
9731 if(String(v) !== String(this.startValue)){
9732 this.fireEvent('change', this, v, this.startValue);
9734 this.fireEvent("blur", this);
9737 onChange : function(e)
9739 var v = this.getValue();
9740 if(String(v) !== String(this.startValue)){
9741 this.fireEvent('change', this, v, this.startValue);
9747 * Resets the current field value to the originally loaded value and clears any validation messages
9750 this.setValue(this.originalValue);
9754 * Returns the name of the field
9755 * @return {Mixed} name The name field
9757 getName: function(){
9761 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9762 * @return {Mixed} value The field value
9764 getValue : function(){
9766 var v = this.inputEl().getValue();
9771 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9772 * @return {Mixed} value The field value
9774 getRawValue : function(){
9775 var v = this.inputEl().getValue();
9781 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9782 * @param {Mixed} value The value to set
9784 setRawValue : function(v){
9785 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9788 selectText : function(start, end){
9789 var v = this.getRawValue();
9791 start = start === undefined ? 0 : start;
9792 end = end === undefined ? v.length : end;
9793 var d = this.inputEl().dom;
9794 if(d.setSelectionRange){
9795 d.setSelectionRange(start, end);
9796 }else if(d.createTextRange){
9797 var range = d.createTextRange();
9798 range.moveStart("character", start);
9799 range.moveEnd("character", v.length-end);
9806 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9807 * @param {Mixed} value The value to set
9809 setValue : function(v){
9812 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9818 processValue : function(value){
9819 if(this.stripCharsRe){
9820 var newValue = value.replace(this.stripCharsRe, '');
9821 if(newValue !== value){
9822 this.setRawValue(newValue);
9829 preFocus : function(){
9831 if(this.selectOnFocus){
9832 this.inputEl().dom.select();
9835 filterKeys : function(e){
9837 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9840 var c = e.getCharCode(), cc = String.fromCharCode(c);
9841 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9844 if(!this.maskRe.test(cc)){
9849 * Clear any invalid styles/messages for this field
9851 clearInvalid : function(){
9853 if(!this.el || this.preventMark){ // not rendered
9858 this.el.removeClass([this.invalidClass, 'is-invalid']);
9860 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9862 var feedback = this.el.select('.form-control-feedback', true).first();
9865 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9871 this.indicator.removeClass('visible');
9872 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9875 this.fireEvent('valid', this);
9879 * Mark this field as valid
9881 markValid : function()
9883 if(!this.el || this.preventMark){ // not rendered...
9887 this.el.removeClass([this.invalidClass, this.validClass]);
9888 this.inputEl().removeClass(['is-valid', 'is-invalid']);
9890 var feedback = this.el.select('.form-control-feedback', true).first();
9893 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9897 this.indicator.removeClass('visible');
9898 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9905 if(this.allowBlank && !this.getRawValue().length){
9908 if (Roo.bootstrap.version == 3) {
9909 this.el.addClass(this.validClass);
9911 this.inputEl().addClass('is-valid');
9914 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9916 var feedback = this.el.select('.form-control-feedback', true).first();
9919 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9920 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9925 this.fireEvent('valid', this);
9929 * Mark this field as invalid
9930 * @param {String} msg The validation message
9932 markInvalid : function(msg)
9934 if(!this.el || this.preventMark){ // not rendered
9938 this.el.removeClass([this.invalidClass, this.validClass]);
9939 this.inputEl().removeClass(['is-valid', 'is-invalid']);
9941 var feedback = this.el.select('.form-control-feedback', true).first();
9944 this.el.select('.form-control-feedback', true).first().removeClass(
9945 [this.invalidFeedbackClass, this.validFeedbackClass]);
9952 if(this.allowBlank && !this.getRawValue().length){
9957 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9958 this.indicator.addClass('visible');
9960 if (Roo.bootstrap.version == 3) {
9961 this.el.addClass(this.invalidClass);
9963 this.inputEl().addClass('is-invalid');
9968 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9970 var feedback = this.el.select('.form-control-feedback', true).first();
9973 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9975 if(this.getValue().length || this.forceFeedback){
9976 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9983 this.fireEvent('invalid', this, msg);
9986 SafariOnKeyDown : function(event)
9988 // this is a workaround for a password hang bug on chrome/ webkit.
9989 if (this.inputEl().dom.type != 'password') {
9993 var isSelectAll = false;
9995 if(this.inputEl().dom.selectionEnd > 0){
9996 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9998 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9999 event.preventDefault();
10004 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10006 event.preventDefault();
10007 // this is very hacky as keydown always get's upper case.
10009 var cc = String.fromCharCode(event.getCharCode());
10010 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
10014 adjustWidth : function(tag, w){
10015 tag = tag.toLowerCase();
10016 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10017 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10018 if(tag == 'input'){
10021 if(tag == 'textarea'){
10024 }else if(Roo.isOpera){
10025 if(tag == 'input'){
10028 if(tag == 'textarea'){
10036 setFieldLabel : function(v)
10038 if(!this.rendered){
10042 if(this.indicatorEl()){
10043 var ar = this.el.select('label > span',true);
10045 if (ar.elements.length) {
10046 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10047 this.fieldLabel = v;
10051 var br = this.el.select('label',true);
10053 if(br.elements.length) {
10054 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10055 this.fieldLabel = v;
10059 Roo.log('Cannot Found any of label > span || label in input');
10063 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10064 this.fieldLabel = v;
10079 * @class Roo.bootstrap.TextArea
10080 * @extends Roo.bootstrap.Input
10081 * Bootstrap TextArea class
10082 * @cfg {Number} cols Specifies the visible width of a text area
10083 * @cfg {Number} rows Specifies the visible number of lines in a text area
10084 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10085 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10086 * @cfg {string} html text
10089 * Create a new TextArea
10090 * @param {Object} config The config object
10093 Roo.bootstrap.TextArea = function(config){
10094 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10098 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
10108 getAutoCreate : function(){
10110 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10116 if(this.inputType != 'hidden'){
10117 cfg.cls = 'form-group' //input-group
10125 value : this.value || '',
10126 html: this.html || '',
10127 cls : 'form-control',
10128 placeholder : this.placeholder || ''
10132 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10133 input.maxLength = this.maxLength;
10137 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10141 input.cols = this.cols;
10144 if (this.readOnly) {
10145 input.readonly = true;
10149 input.name = this.name;
10153 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10157 ['xs','sm','md','lg'].map(function(size){
10158 if (settings[size]) {
10159 cfg.cls += ' col-' + size + '-' + settings[size];
10163 var inputblock = input;
10165 if(this.hasFeedback && !this.allowBlank){
10169 cls: 'glyphicon form-control-feedback'
10173 cls : 'has-feedback',
10182 if (this.before || this.after) {
10185 cls : 'input-group',
10189 inputblock.cn.push({
10191 cls : 'input-group-addon',
10196 inputblock.cn.push(input);
10198 if(this.hasFeedback && !this.allowBlank){
10199 inputblock.cls += ' has-feedback';
10200 inputblock.cn.push(feedback);
10204 inputblock.cn.push({
10206 cls : 'input-group-addon',
10213 if (align ==='left' && this.fieldLabel.length) {
10218 cls : 'control-label',
10219 html : this.fieldLabel
10230 if(this.labelWidth > 12){
10231 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10234 if(this.labelWidth < 13 && this.labelmd == 0){
10235 this.labelmd = this.labelWidth;
10238 if(this.labellg > 0){
10239 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10240 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10243 if(this.labelmd > 0){
10244 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10245 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10248 if(this.labelsm > 0){
10249 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10250 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10253 if(this.labelxs > 0){
10254 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10255 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10258 } else if ( this.fieldLabel.length) {
10263 //cls : 'input-group-addon',
10264 html : this.fieldLabel
10282 if (this.disabled) {
10283 input.disabled=true;
10290 * return the real textarea element.
10292 inputEl: function ()
10294 return this.el.select('textarea.form-control',true).first();
10298 * Clear any invalid styles/messages for this field
10300 clearInvalid : function()
10303 if(!this.el || this.preventMark){ // not rendered
10307 var label = this.el.select('label', true).first();
10308 var icon = this.el.select('i.fa-star', true).first();
10313 this.el.removeClass( this.validClass);
10314 this.inputEl().removeClass('is-invalid');
10316 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10318 var feedback = this.el.select('.form-control-feedback', true).first();
10321 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10326 this.fireEvent('valid', this);
10330 * Mark this field as valid
10332 markValid : function()
10334 if(!this.el || this.preventMark){ // not rendered
10338 this.el.removeClass([this.invalidClass, this.validClass]);
10339 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10341 var feedback = this.el.select('.form-control-feedback', true).first();
10344 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10347 if(this.disabled || this.allowBlank){
10351 var label = this.el.select('label', true).first();
10352 var icon = this.el.select('i.fa-star', true).first();
10357 if (Roo.bootstrap.version == 3) {
10358 this.el.addClass(this.validClass);
10360 this.inputEl().addClass('is-valid');
10364 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10366 var feedback = this.el.select('.form-control-feedback', true).first();
10369 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10370 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10375 this.fireEvent('valid', this);
10379 * Mark this field as invalid
10380 * @param {String} msg The validation message
10382 markInvalid : function(msg)
10384 if(!this.el || this.preventMark){ // not rendered
10388 this.el.removeClass([this.invalidClass, this.validClass]);
10389 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10391 var feedback = this.el.select('.form-control-feedback', true).first();
10394 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10397 if(this.disabled || this.allowBlank){
10401 var label = this.el.select('label', true).first();
10402 var icon = this.el.select('i.fa-star', true).first();
10404 if(!this.getValue().length && label && !icon){
10405 this.el.createChild({
10407 cls : 'text-danger fa fa-lg fa-star',
10408 tooltip : 'This field is required',
10409 style : 'margin-right:5px;'
10413 if (Roo.bootstrap.version == 3) {
10414 this.el.addClass(this.invalidClass);
10416 this.inputEl().addClass('is-invalid');
10419 // fixme ... this may be depricated need to test..
10420 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10422 var feedback = this.el.select('.form-control-feedback', true).first();
10425 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10427 if(this.getValue().length || this.forceFeedback){
10428 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10435 this.fireEvent('invalid', this, msg);
10443 * trigger field - base class for combo..
10448 * @class Roo.bootstrap.TriggerField
10449 * @extends Roo.bootstrap.Input
10450 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10451 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10452 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10453 * for which you can provide a custom implementation. For example:
10455 var trigger = new Roo.bootstrap.TriggerField();
10456 trigger.onTriggerClick = myTriggerFn;
10457 trigger.applyTo('my-field');
10460 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10461 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10462 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10463 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10464 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
10467 * Create a new TriggerField.
10468 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10469 * to the base TextField)
10471 Roo.bootstrap.TriggerField = function(config){
10472 this.mimicing = false;
10473 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10476 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10478 * @cfg {String} triggerClass A CSS class to apply to the trigger
10481 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10486 * @cfg {Boolean} removable (true|false) special filter default false
10490 /** @cfg {Boolean} grow @hide */
10491 /** @cfg {Number} growMin @hide */
10492 /** @cfg {Number} growMax @hide */
10498 autoSize: Roo.emptyFn,
10502 deferHeight : true,
10505 actionMode : 'wrap',
10510 getAutoCreate : function(){
10512 var align = this.labelAlign || this.parentLabelAlign();
10517 cls: 'form-group' //input-group
10524 type : this.inputType,
10525 cls : 'form-control',
10526 autocomplete: 'new-password',
10527 placeholder : this.placeholder || ''
10531 input.name = this.name;
10534 input.cls += ' input-' + this.size;
10537 if (this.disabled) {
10538 input.disabled=true;
10541 var inputblock = input;
10543 if(this.hasFeedback && !this.allowBlank){
10547 cls: 'glyphicon form-control-feedback'
10550 if(this.removable && !this.editable && !this.tickable){
10552 cls : 'has-feedback',
10558 cls : 'roo-combo-removable-btn close'
10565 cls : 'has-feedback',
10574 if(this.removable && !this.editable && !this.tickable){
10576 cls : 'roo-removable',
10582 cls : 'roo-combo-removable-btn close'
10589 if (this.before || this.after) {
10592 cls : 'input-group',
10596 inputblock.cn.push({
10598 cls : 'input-group-addon input-group-prepend input-group-text',
10603 inputblock.cn.push(input);
10605 if(this.hasFeedback && !this.allowBlank){
10606 inputblock.cls += ' has-feedback';
10607 inputblock.cn.push(feedback);
10611 inputblock.cn.push({
10613 cls : 'input-group-addon input-group-append input-group-text',
10622 var ibwrap = inputblock;
10627 cls: 'roo-select2-choices',
10631 cls: 'roo-select2-search-field',
10643 cls: 'roo-select2-container input-group',
10648 cls: 'form-hidden-field'
10654 if(!this.multiple && this.showToggleBtn){
10660 if (this.caret != false) {
10663 cls: 'fa fa-' + this.caret
10670 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10672 Roo.bootstrap.version == 3 ? caret : '',
10675 cls: 'combobox-clear',
10689 combobox.cls += ' roo-select2-container-multi';
10693 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10694 tooltip : 'This field is required'
10696 if (Roo.bootstrap.version == 4) {
10699 style : 'display:none'
10704 if (align ==='left' && this.fieldLabel.length) {
10706 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10713 cls : 'control-label',
10714 html : this.fieldLabel
10726 var labelCfg = cfg.cn[1];
10727 var contentCfg = cfg.cn[2];
10729 if(this.indicatorpos == 'right'){
10734 cls : 'control-label',
10738 html : this.fieldLabel
10752 labelCfg = cfg.cn[0];
10753 contentCfg = cfg.cn[1];
10756 if(this.labelWidth > 12){
10757 labelCfg.style = "width: " + this.labelWidth + 'px';
10760 if(this.labelWidth < 13 && this.labelmd == 0){
10761 this.labelmd = this.labelWidth;
10764 if(this.labellg > 0){
10765 labelCfg.cls += ' col-lg-' + this.labellg;
10766 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10769 if(this.labelmd > 0){
10770 labelCfg.cls += ' col-md-' + this.labelmd;
10771 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10774 if(this.labelsm > 0){
10775 labelCfg.cls += ' col-sm-' + this.labelsm;
10776 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10779 if(this.labelxs > 0){
10780 labelCfg.cls += ' col-xs-' + this.labelxs;
10781 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10784 } else if ( this.fieldLabel.length) {
10785 // Roo.log(" label");
10790 //cls : 'input-group-addon',
10791 html : this.fieldLabel
10799 if(this.indicatorpos == 'right'){
10807 html : this.fieldLabel
10821 // Roo.log(" no label && no align");
10828 ['xs','sm','md','lg'].map(function(size){
10829 if (settings[size]) {
10830 cfg.cls += ' col-' + size + '-' + settings[size];
10841 onResize : function(w, h){
10842 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10843 // if(typeof w == 'number'){
10844 // var x = w - this.trigger.getWidth();
10845 // this.inputEl().setWidth(this.adjustWidth('input', x));
10846 // this.trigger.setStyle('left', x+'px');
10851 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10854 getResizeEl : function(){
10855 return this.inputEl();
10859 getPositionEl : function(){
10860 return this.inputEl();
10864 alignErrorIcon : function(){
10865 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10869 initEvents : function(){
10873 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10874 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10875 if(!this.multiple && this.showToggleBtn){
10876 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10877 if(this.hideTrigger){
10878 this.trigger.setDisplayed(false);
10880 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10884 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10887 if(this.removable && !this.editable && !this.tickable){
10888 var close = this.closeTriggerEl();
10891 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10892 close.on('click', this.removeBtnClick, this, close);
10896 //this.trigger.addClassOnOver('x-form-trigger-over');
10897 //this.trigger.addClassOnClick('x-form-trigger-click');
10900 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10904 closeTriggerEl : function()
10906 var close = this.el.select('.roo-combo-removable-btn', true).first();
10907 return close ? close : false;
10910 removeBtnClick : function(e, h, el)
10912 e.preventDefault();
10914 if(this.fireEvent("remove", this) !== false){
10916 this.fireEvent("afterremove", this)
10920 createList : function()
10922 this.list = Roo.get(document.body).createChild({
10923 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10924 cls: 'typeahead typeahead-long dropdown-menu',
10925 style: 'display:none'
10928 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10933 initTrigger : function(){
10938 onDestroy : function(){
10940 this.trigger.removeAllListeners();
10941 // this.trigger.remove();
10944 // this.wrap.remove();
10946 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10950 onFocus : function(){
10951 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10953 if(!this.mimicing){
10954 this.wrap.addClass('x-trigger-wrap-focus');
10955 this.mimicing = true;
10956 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10957 if(this.monitorTab){
10958 this.el.on("keydown", this.checkTab, this);
10965 checkTab : function(e){
10966 if(e.getKey() == e.TAB){
10967 this.triggerBlur();
10972 onBlur : function(){
10977 mimicBlur : function(e, t){
10979 if(!this.wrap.contains(t) && this.validateBlur()){
10980 this.triggerBlur();
10986 triggerBlur : function(){
10987 this.mimicing = false;
10988 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10989 if(this.monitorTab){
10990 this.el.un("keydown", this.checkTab, this);
10992 //this.wrap.removeClass('x-trigger-wrap-focus');
10993 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10997 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10998 validateBlur : function(e, t){
11003 onDisable : function(){
11004 this.inputEl().dom.disabled = true;
11005 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11007 // this.wrap.addClass('x-item-disabled');
11012 onEnable : function(){
11013 this.inputEl().dom.disabled = false;
11014 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11016 // this.el.removeClass('x-item-disabled');
11021 onShow : function(){
11022 var ae = this.getActionEl();
11025 ae.dom.style.display = '';
11026 ae.dom.style.visibility = 'visible';
11032 onHide : function(){
11033 var ae = this.getActionEl();
11034 ae.dom.style.display = 'none';
11038 * The function that should handle the trigger's click event. This method does nothing by default until overridden
11039 * by an implementing function.
11041 * @param {EventObject} e
11043 onTriggerClick : Roo.emptyFn
11047 * Ext JS Library 1.1.1
11048 * Copyright(c) 2006-2007, Ext JS, LLC.
11050 * Originally Released Under LGPL - original licence link has changed is not relivant.
11053 * <script type="text/javascript">
11058 * @class Roo.data.SortTypes
11060 * Defines the default sorting (casting?) comparison functions used when sorting data.
11062 Roo.data.SortTypes = {
11064 * Default sort that does nothing
11065 * @param {Mixed} s The value being converted
11066 * @return {Mixed} The comparison value
11068 none : function(s){
11073 * The regular expression used to strip tags
11077 stripTagsRE : /<\/?[^>]+>/gi,
11080 * Strips all HTML tags to sort on text only
11081 * @param {Mixed} s The value being converted
11082 * @return {String} The comparison value
11084 asText : function(s){
11085 return String(s).replace(this.stripTagsRE, "");
11089 * Strips all HTML tags to sort on text only - Case insensitive
11090 * @param {Mixed} s The value being converted
11091 * @return {String} The comparison value
11093 asUCText : function(s){
11094 return String(s).toUpperCase().replace(this.stripTagsRE, "");
11098 * Case insensitive string
11099 * @param {Mixed} s The value being converted
11100 * @return {String} The comparison value
11102 asUCString : function(s) {
11103 return String(s).toUpperCase();
11108 * @param {Mixed} s The value being converted
11109 * @return {Number} The comparison value
11111 asDate : function(s) {
11115 if(s instanceof Date){
11116 return s.getTime();
11118 return Date.parse(String(s));
11123 * @param {Mixed} s The value being converted
11124 * @return {Float} The comparison value
11126 asFloat : function(s) {
11127 var val = parseFloat(String(s).replace(/,/g, ""));
11136 * @param {Mixed} s The value being converted
11137 * @return {Number} The comparison value
11139 asInt : function(s) {
11140 var val = parseInt(String(s).replace(/,/g, ""));
11148 * Ext JS Library 1.1.1
11149 * Copyright(c) 2006-2007, Ext JS, LLC.
11151 * Originally Released Under LGPL - original licence link has changed is not relivant.
11154 * <script type="text/javascript">
11158 * @class Roo.data.Record
11159 * Instances of this class encapsulate both record <em>definition</em> information, and record
11160 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11161 * to access Records cached in an {@link Roo.data.Store} object.<br>
11163 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11164 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11167 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11169 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11170 * {@link #create}. The parameters are the same.
11171 * @param {Array} data An associative Array of data values keyed by the field name.
11172 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11173 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11174 * not specified an integer id is generated.
11176 Roo.data.Record = function(data, id){
11177 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11182 * Generate a constructor for a specific record layout.
11183 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11184 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11185 * Each field definition object may contain the following properties: <ul>
11186 * <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,
11187 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11188 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11189 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11190 * is being used, then this is a string containing the javascript expression to reference the data relative to
11191 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11192 * to the data item relative to the record element. If the mapping expression is the same as the field name,
11193 * this may be omitted.</p></li>
11194 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11195 * <ul><li>auto (Default, implies no conversion)</li>
11200 * <li>date</li></ul></p></li>
11201 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11202 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11203 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11204 * by the Reader into an object that will be stored in the Record. It is passed the
11205 * following parameters:<ul>
11206 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11208 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11210 * <br>usage:<br><pre><code>
11211 var TopicRecord = Roo.data.Record.create(
11212 {name: 'title', mapping: 'topic_title'},
11213 {name: 'author', mapping: 'username'},
11214 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11215 {name: 'lastPost', mapping: 'post_time', type: 'date'},
11216 {name: 'lastPoster', mapping: 'user2'},
11217 {name: 'excerpt', mapping: 'post_text'}
11220 var myNewRecord = new TopicRecord({
11221 title: 'Do my job please',
11224 lastPost: new Date(),
11225 lastPoster: 'Animal',
11226 excerpt: 'No way dude!'
11228 myStore.add(myNewRecord);
11233 Roo.data.Record.create = function(o){
11234 var f = function(){
11235 f.superclass.constructor.apply(this, arguments);
11237 Roo.extend(f, Roo.data.Record);
11238 var p = f.prototype;
11239 p.fields = new Roo.util.MixedCollection(false, function(field){
11242 for(var i = 0, len = o.length; i < len; i++){
11243 p.fields.add(new Roo.data.Field(o[i]));
11245 f.getField = function(name){
11246 return p.fields.get(name);
11251 Roo.data.Record.AUTO_ID = 1000;
11252 Roo.data.Record.EDIT = 'edit';
11253 Roo.data.Record.REJECT = 'reject';
11254 Roo.data.Record.COMMIT = 'commit';
11256 Roo.data.Record.prototype = {
11258 * Readonly flag - true if this record has been modified.
11267 join : function(store){
11268 this.store = store;
11272 * Set the named field to the specified value.
11273 * @param {String} name The name of the field to set.
11274 * @param {Object} value The value to set the field to.
11276 set : function(name, value){
11277 if(this.data[name] == value){
11281 if(!this.modified){
11282 this.modified = {};
11284 if(typeof this.modified[name] == 'undefined'){
11285 this.modified[name] = this.data[name];
11287 this.data[name] = value;
11288 if(!this.editing && this.store){
11289 this.store.afterEdit(this);
11294 * Get the value of the named field.
11295 * @param {String} name The name of the field to get the value of.
11296 * @return {Object} The value of the field.
11298 get : function(name){
11299 return this.data[name];
11303 beginEdit : function(){
11304 this.editing = true;
11305 this.modified = {};
11309 cancelEdit : function(){
11310 this.editing = false;
11311 delete this.modified;
11315 endEdit : function(){
11316 this.editing = false;
11317 if(this.dirty && this.store){
11318 this.store.afterEdit(this);
11323 * Usually called by the {@link Roo.data.Store} which owns the Record.
11324 * Rejects all changes made to the Record since either creation, or the last commit operation.
11325 * Modified fields are reverted to their original values.
11327 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11328 * of reject operations.
11330 reject : function(){
11331 var m = this.modified;
11333 if(typeof m[n] != "function"){
11334 this.data[n] = m[n];
11337 this.dirty = false;
11338 delete this.modified;
11339 this.editing = false;
11341 this.store.afterReject(this);
11346 * Usually called by the {@link Roo.data.Store} which owns the Record.
11347 * Commits all changes made to the Record since either creation, or the last commit operation.
11349 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11350 * of commit operations.
11352 commit : function(){
11353 this.dirty = false;
11354 delete this.modified;
11355 this.editing = false;
11357 this.store.afterCommit(this);
11362 hasError : function(){
11363 return this.error != null;
11367 clearError : function(){
11372 * Creates a copy of this record.
11373 * @param {String} id (optional) A new record id if you don't want to use this record's id
11376 copy : function(newId) {
11377 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11381 * Ext JS Library 1.1.1
11382 * Copyright(c) 2006-2007, Ext JS, LLC.
11384 * Originally Released Under LGPL - original licence link has changed is not relivant.
11387 * <script type="text/javascript">
11393 * @class Roo.data.Store
11394 * @extends Roo.util.Observable
11395 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11396 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11398 * 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
11399 * has no knowledge of the format of the data returned by the Proxy.<br>
11401 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11402 * instances from the data object. These records are cached and made available through accessor functions.
11404 * Creates a new Store.
11405 * @param {Object} config A config object containing the objects needed for the Store to access data,
11406 * and read the data into Records.
11408 Roo.data.Store = function(config){
11409 this.data = new Roo.util.MixedCollection(false);
11410 this.data.getKey = function(o){
11413 this.baseParams = {};
11415 this.paramNames = {
11420 "multisort" : "_multisort"
11423 if(config && config.data){
11424 this.inlineData = config.data;
11425 delete config.data;
11428 Roo.apply(this, config);
11430 if(this.reader){ // reader passed
11431 this.reader = Roo.factory(this.reader, Roo.data);
11432 this.reader.xmodule = this.xmodule || false;
11433 if(!this.recordType){
11434 this.recordType = this.reader.recordType;
11436 if(this.reader.onMetaChange){
11437 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11441 if(this.recordType){
11442 this.fields = this.recordType.prototype.fields;
11444 this.modified = [];
11448 * @event datachanged
11449 * Fires when the data cache has changed, and a widget which is using this Store
11450 * as a Record cache should refresh its view.
11451 * @param {Store} this
11453 datachanged : true,
11455 * @event metachange
11456 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11457 * @param {Store} this
11458 * @param {Object} meta The JSON metadata
11463 * Fires when Records have been added to the Store
11464 * @param {Store} this
11465 * @param {Roo.data.Record[]} records The array of Records added
11466 * @param {Number} index The index at which the record(s) were added
11471 * Fires when a Record has been removed from the Store
11472 * @param {Store} this
11473 * @param {Roo.data.Record} record The Record that was removed
11474 * @param {Number} index The index at which the record was removed
11479 * Fires when a Record has been updated
11480 * @param {Store} this
11481 * @param {Roo.data.Record} record The Record that was updated
11482 * @param {String} operation The update operation being performed. Value may be one of:
11484 Roo.data.Record.EDIT
11485 Roo.data.Record.REJECT
11486 Roo.data.Record.COMMIT
11492 * Fires when the data cache has been cleared.
11493 * @param {Store} this
11497 * @event beforeload
11498 * Fires before a request is made for a new data object. If the beforeload handler returns false
11499 * the load action will be canceled.
11500 * @param {Store} this
11501 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11505 * @event beforeloadadd
11506 * Fires after a new set of Records has been loaded.
11507 * @param {Store} this
11508 * @param {Roo.data.Record[]} records The Records that were loaded
11509 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11511 beforeloadadd : true,
11514 * Fires after a new set of Records has been loaded, before they are added to the store.
11515 * @param {Store} this
11516 * @param {Roo.data.Record[]} records The Records that were loaded
11517 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11518 * @params {Object} return from reader
11522 * @event loadexception
11523 * Fires if an exception occurs in the Proxy during loading.
11524 * Called with the signature of the Proxy's "loadexception" event.
11525 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11528 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11529 * @param {Object} load options
11530 * @param {Object} jsonData from your request (normally this contains the Exception)
11532 loadexception : true
11536 this.proxy = Roo.factory(this.proxy, Roo.data);
11537 this.proxy.xmodule = this.xmodule || false;
11538 this.relayEvents(this.proxy, ["loadexception"]);
11540 this.sortToggle = {};
11541 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11543 Roo.data.Store.superclass.constructor.call(this);
11545 if(this.inlineData){
11546 this.loadData(this.inlineData);
11547 delete this.inlineData;
11551 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11553 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11554 * without a remote query - used by combo/forms at present.
11558 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11561 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11564 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11565 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11568 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11569 * on any HTTP request
11572 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11575 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11579 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11580 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11582 remoteSort : false,
11585 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11586 * loaded or when a record is removed. (defaults to false).
11588 pruneModifiedRecords : false,
11591 lastOptions : null,
11594 * Add Records to the Store and fires the add event.
11595 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11597 add : function(records){
11598 records = [].concat(records);
11599 for(var i = 0, len = records.length; i < len; i++){
11600 records[i].join(this);
11602 var index = this.data.length;
11603 this.data.addAll(records);
11604 this.fireEvent("add", this, records, index);
11608 * Remove a Record from the Store and fires the remove event.
11609 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11611 remove : function(record){
11612 var index = this.data.indexOf(record);
11613 this.data.removeAt(index);
11615 if(this.pruneModifiedRecords){
11616 this.modified.remove(record);
11618 this.fireEvent("remove", this, record, index);
11622 * Remove all Records from the Store and fires the clear event.
11624 removeAll : function(){
11626 if(this.pruneModifiedRecords){
11627 this.modified = [];
11629 this.fireEvent("clear", this);
11633 * Inserts Records to the Store at the given index and fires the add event.
11634 * @param {Number} index The start index at which to insert the passed Records.
11635 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11637 insert : function(index, records){
11638 records = [].concat(records);
11639 for(var i = 0, len = records.length; i < len; i++){
11640 this.data.insert(index, records[i]);
11641 records[i].join(this);
11643 this.fireEvent("add", this, records, index);
11647 * Get the index within the cache of the passed Record.
11648 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11649 * @return {Number} The index of the passed Record. Returns -1 if not found.
11651 indexOf : function(record){
11652 return this.data.indexOf(record);
11656 * Get the index within the cache of the Record with the passed id.
11657 * @param {String} id The id of the Record to find.
11658 * @return {Number} The index of the Record. Returns -1 if not found.
11660 indexOfId : function(id){
11661 return this.data.indexOfKey(id);
11665 * Get the Record with the specified id.
11666 * @param {String} id The id of the Record to find.
11667 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11669 getById : function(id){
11670 return this.data.key(id);
11674 * Get the Record at the specified index.
11675 * @param {Number} index The index of the Record to find.
11676 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11678 getAt : function(index){
11679 return this.data.itemAt(index);
11683 * Returns a range of Records between specified indices.
11684 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11685 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11686 * @return {Roo.data.Record[]} An array of Records
11688 getRange : function(start, end){
11689 return this.data.getRange(start, end);
11693 storeOptions : function(o){
11694 o = Roo.apply({}, o);
11697 this.lastOptions = o;
11701 * Loads the Record cache from the configured Proxy using the configured Reader.
11703 * If using remote paging, then the first load call must specify the <em>start</em>
11704 * and <em>limit</em> properties in the options.params property to establish the initial
11705 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11707 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11708 * and this call will return before the new data has been loaded. Perform any post-processing
11709 * in a callback function, or in a "load" event handler.</strong>
11711 * @param {Object} options An object containing properties which control loading options:<ul>
11712 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11713 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11714 * passed the following arguments:<ul>
11715 * <li>r : Roo.data.Record[]</li>
11716 * <li>options: Options object from the load call</li>
11717 * <li>success: Boolean success indicator</li></ul></li>
11718 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11719 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11722 load : function(options){
11723 options = options || {};
11724 if(this.fireEvent("beforeload", this, options) !== false){
11725 this.storeOptions(options);
11726 var p = Roo.apply(options.params || {}, this.baseParams);
11727 // if meta was not loaded from remote source.. try requesting it.
11728 if (!this.reader.metaFromRemote) {
11729 p._requestMeta = 1;
11731 if(this.sortInfo && this.remoteSort){
11732 var pn = this.paramNames;
11733 p[pn["sort"]] = this.sortInfo.field;
11734 p[pn["dir"]] = this.sortInfo.direction;
11736 if (this.multiSort) {
11737 var pn = this.paramNames;
11738 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11741 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11746 * Reloads the Record cache from the configured Proxy using the configured Reader and
11747 * the options from the last load operation performed.
11748 * @param {Object} options (optional) An object containing properties which may override the options
11749 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11750 * the most recently used options are reused).
11752 reload : function(options){
11753 this.load(Roo.applyIf(options||{}, this.lastOptions));
11757 // Called as a callback by the Reader during a load operation.
11758 loadRecords : function(o, options, success){
11759 if(!o || success === false){
11760 if(success !== false){
11761 this.fireEvent("load", this, [], options, o);
11763 if(options.callback){
11764 options.callback.call(options.scope || this, [], options, false);
11768 // if data returned failure - throw an exception.
11769 if (o.success === false) {
11770 // show a message if no listener is registered.
11771 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11772 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11774 // loadmask wil be hooked into this..
11775 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11778 var r = o.records, t = o.totalRecords || r.length;
11780 this.fireEvent("beforeloadadd", this, r, options, o);
11782 if(!options || options.add !== true){
11783 if(this.pruneModifiedRecords){
11784 this.modified = [];
11786 for(var i = 0, len = r.length; i < len; i++){
11790 this.data = this.snapshot;
11791 delete this.snapshot;
11794 this.data.addAll(r);
11795 this.totalLength = t;
11797 this.fireEvent("datachanged", this);
11799 this.totalLength = Math.max(t, this.data.length+r.length);
11803 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11805 var e = new Roo.data.Record({});
11807 e.set(this.parent.displayField, this.parent.emptyTitle);
11808 e.set(this.parent.valueField, '');
11813 this.fireEvent("load", this, r, options, o);
11814 if(options.callback){
11815 options.callback.call(options.scope || this, r, options, true);
11821 * Loads data from a passed data block. A Reader which understands the format of the data
11822 * must have been configured in the constructor.
11823 * @param {Object} data The data block from which to read the Records. The format of the data expected
11824 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11825 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11827 loadData : function(o, append){
11828 var r = this.reader.readRecords(o);
11829 this.loadRecords(r, {add: append}, true);
11833 * using 'cn' the nested child reader read the child array into it's child stores.
11834 * @param {Object} rec The record with a 'children array
11836 loadDataFromChildren : function(rec)
11838 this.loadData(this.reader.toLoadData(rec));
11843 * Gets the number of cached records.
11845 * <em>If using paging, this may not be the total size of the dataset. If the data object
11846 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11847 * the data set size</em>
11849 getCount : function(){
11850 return this.data.length || 0;
11854 * Gets the total number of records in the dataset as returned by the server.
11856 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11857 * the dataset size</em>
11859 getTotalCount : function(){
11860 return this.totalLength || 0;
11864 * Returns the sort state of the Store as an object with two properties:
11866 field {String} The name of the field by which the Records are sorted
11867 direction {String} The sort order, "ASC" or "DESC"
11870 getSortState : function(){
11871 return this.sortInfo;
11875 applySort : function(){
11876 if(this.sortInfo && !this.remoteSort){
11877 var s = this.sortInfo, f = s.field;
11878 var st = this.fields.get(f).sortType;
11879 var fn = function(r1, r2){
11880 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11881 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11883 this.data.sort(s.direction, fn);
11884 if(this.snapshot && this.snapshot != this.data){
11885 this.snapshot.sort(s.direction, fn);
11891 * Sets the default sort column and order to be used by the next load operation.
11892 * @param {String} fieldName The name of the field to sort by.
11893 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11895 setDefaultSort : function(field, dir){
11896 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11900 * Sort the Records.
11901 * If remote sorting is used, the sort is performed on the server, and the cache is
11902 * reloaded. If local sorting is used, the cache is sorted internally.
11903 * @param {String} fieldName The name of the field to sort by.
11904 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11906 sort : function(fieldName, dir){
11907 var f = this.fields.get(fieldName);
11909 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11911 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11912 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11917 this.sortToggle[f.name] = dir;
11918 this.sortInfo = {field: f.name, direction: dir};
11919 if(!this.remoteSort){
11921 this.fireEvent("datachanged", this);
11923 this.load(this.lastOptions);
11928 * Calls the specified function for each of the Records in the cache.
11929 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11930 * Returning <em>false</em> aborts and exits the iteration.
11931 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11933 each : function(fn, scope){
11934 this.data.each(fn, scope);
11938 * Gets all records modified since the last commit. Modified records are persisted across load operations
11939 * (e.g., during paging).
11940 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11942 getModifiedRecords : function(){
11943 return this.modified;
11947 createFilterFn : function(property, value, anyMatch){
11948 if(!value.exec){ // not a regex
11949 value = String(value);
11950 if(value.length == 0){
11953 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11955 return function(r){
11956 return value.test(r.data[property]);
11961 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11962 * @param {String} property A field on your records
11963 * @param {Number} start The record index to start at (defaults to 0)
11964 * @param {Number} end The last record index to include (defaults to length - 1)
11965 * @return {Number} The sum
11967 sum : function(property, start, end){
11968 var rs = this.data.items, v = 0;
11969 start = start || 0;
11970 end = (end || end === 0) ? end : rs.length-1;
11972 for(var i = start; i <= end; i++){
11973 v += (rs[i].data[property] || 0);
11979 * Filter the records by a specified property.
11980 * @param {String} field A field on your records
11981 * @param {String/RegExp} value Either a string that the field
11982 * should start with or a RegExp to test against the field
11983 * @param {Boolean} anyMatch True to match any part not just the beginning
11985 filter : function(property, value, anyMatch){
11986 var fn = this.createFilterFn(property, value, anyMatch);
11987 return fn ? this.filterBy(fn) : this.clearFilter();
11991 * Filter by a function. The specified function will be called with each
11992 * record in this data source. If the function returns true the record is included,
11993 * otherwise it is filtered.
11994 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11995 * @param {Object} scope (optional) The scope of the function (defaults to this)
11997 filterBy : function(fn, scope){
11998 this.snapshot = this.snapshot || this.data;
11999 this.data = this.queryBy(fn, scope||this);
12000 this.fireEvent("datachanged", this);
12004 * Query the records by a specified property.
12005 * @param {String} field A field on your records
12006 * @param {String/RegExp} value Either a string that the field
12007 * should start with or a RegExp to test against the field
12008 * @param {Boolean} anyMatch True to match any part not just the beginning
12009 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12011 query : function(property, value, anyMatch){
12012 var fn = this.createFilterFn(property, value, anyMatch);
12013 return fn ? this.queryBy(fn) : this.data.clone();
12017 * Query by a function. The specified function will be called with each
12018 * record in this data source. If the function returns true the record is included
12020 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12021 * @param {Object} scope (optional) The scope of the function (defaults to this)
12022 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12024 queryBy : function(fn, scope){
12025 var data = this.snapshot || this.data;
12026 return data.filterBy(fn, scope||this);
12030 * Collects unique values for a particular dataIndex from this store.
12031 * @param {String} dataIndex The property to collect
12032 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12033 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12034 * @return {Array} An array of the unique values
12036 collect : function(dataIndex, allowNull, bypassFilter){
12037 var d = (bypassFilter === true && this.snapshot) ?
12038 this.snapshot.items : this.data.items;
12039 var v, sv, r = [], l = {};
12040 for(var i = 0, len = d.length; i < len; i++){
12041 v = d[i].data[dataIndex];
12043 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12052 * Revert to a view of the Record cache with no filtering applied.
12053 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12055 clearFilter : function(suppressEvent){
12056 if(this.snapshot && this.snapshot != this.data){
12057 this.data = this.snapshot;
12058 delete this.snapshot;
12059 if(suppressEvent !== true){
12060 this.fireEvent("datachanged", this);
12066 afterEdit : function(record){
12067 if(this.modified.indexOf(record) == -1){
12068 this.modified.push(record);
12070 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12074 afterReject : function(record){
12075 this.modified.remove(record);
12076 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12080 afterCommit : function(record){
12081 this.modified.remove(record);
12082 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12086 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12087 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12089 commitChanges : function(){
12090 var m = this.modified.slice(0);
12091 this.modified = [];
12092 for(var i = 0, len = m.length; i < len; i++){
12098 * Cancel outstanding changes on all changed records.
12100 rejectChanges : function(){
12101 var m = this.modified.slice(0);
12102 this.modified = [];
12103 for(var i = 0, len = m.length; i < len; i++){
12108 onMetaChange : function(meta, rtype, o){
12109 this.recordType = rtype;
12110 this.fields = rtype.prototype.fields;
12111 delete this.snapshot;
12112 this.sortInfo = meta.sortInfo || this.sortInfo;
12113 this.modified = [];
12114 this.fireEvent('metachange', this, this.reader.meta);
12117 moveIndex : function(data, type)
12119 var index = this.indexOf(data);
12121 var newIndex = index + type;
12125 this.insert(newIndex, data);
12130 * Ext JS Library 1.1.1
12131 * Copyright(c) 2006-2007, Ext JS, LLC.
12133 * Originally Released Under LGPL - original licence link has changed is not relivant.
12136 * <script type="text/javascript">
12140 * @class Roo.data.SimpleStore
12141 * @extends Roo.data.Store
12142 * Small helper class to make creating Stores from Array data easier.
12143 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12144 * @cfg {Array} fields An array of field definition objects, or field name strings.
12145 * @cfg {Object} an existing reader (eg. copied from another store)
12146 * @cfg {Array} data The multi-dimensional array of data
12148 * @param {Object} config
12150 Roo.data.SimpleStore = function(config)
12152 Roo.data.SimpleStore.superclass.constructor.call(this, {
12154 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
12157 Roo.data.Record.create(config.fields)
12159 proxy : new Roo.data.MemoryProxy(config.data)
12163 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12165 * Ext JS Library 1.1.1
12166 * Copyright(c) 2006-2007, Ext JS, LLC.
12168 * Originally Released Under LGPL - original licence link has changed is not relivant.
12171 * <script type="text/javascript">
12176 * @extends Roo.data.Store
12177 * @class Roo.data.JsonStore
12178 * Small helper class to make creating Stores for JSON data easier. <br/>
12180 var store = new Roo.data.JsonStore({
12181 url: 'get-images.php',
12183 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12186 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12187 * JsonReader and HttpProxy (unless inline data is provided).</b>
12188 * @cfg {Array} fields An array of field definition objects, or field name strings.
12190 * @param {Object} config
12192 Roo.data.JsonStore = function(c){
12193 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12194 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12195 reader: new Roo.data.JsonReader(c, c.fields)
12198 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12200 * Ext JS Library 1.1.1
12201 * Copyright(c) 2006-2007, Ext JS, LLC.
12203 * Originally Released Under LGPL - original licence link has changed is not relivant.
12206 * <script type="text/javascript">
12210 Roo.data.Field = function(config){
12211 if(typeof config == "string"){
12212 config = {name: config};
12214 Roo.apply(this, config);
12217 this.type = "auto";
12220 var st = Roo.data.SortTypes;
12221 // named sortTypes are supported, here we look them up
12222 if(typeof this.sortType == "string"){
12223 this.sortType = st[this.sortType];
12226 // set default sortType for strings and dates
12227 if(!this.sortType){
12230 this.sortType = st.asUCString;
12233 this.sortType = st.asDate;
12236 this.sortType = st.none;
12241 var stripRe = /[\$,%]/g;
12243 // prebuilt conversion function for this field, instead of
12244 // switching every time we're reading a value
12246 var cv, dateFormat = this.dateFormat;
12251 cv = function(v){ return v; };
12254 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12258 return v !== undefined && v !== null && v !== '' ?
12259 parseInt(String(v).replace(stripRe, ""), 10) : '';
12264 return v !== undefined && v !== null && v !== '' ?
12265 parseFloat(String(v).replace(stripRe, ""), 10) : '';
12270 cv = function(v){ return v === true || v === "true" || v == 1; };
12277 if(v instanceof Date){
12281 if(dateFormat == "timestamp"){
12282 return new Date(v*1000);
12284 return Date.parseDate(v, dateFormat);
12286 var parsed = Date.parse(v);
12287 return parsed ? new Date(parsed) : null;
12296 Roo.data.Field.prototype = {
12304 * Ext JS Library 1.1.1
12305 * Copyright(c) 2006-2007, Ext JS, LLC.
12307 * Originally Released Under LGPL - original licence link has changed is not relivant.
12310 * <script type="text/javascript">
12313 // Base class for reading structured data from a data source. This class is intended to be
12314 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12317 * @class Roo.data.DataReader
12318 * Base class for reading structured data from a data source. This class is intended to be
12319 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12322 Roo.data.DataReader = function(meta, recordType){
12326 this.recordType = recordType instanceof Array ?
12327 Roo.data.Record.create(recordType) : recordType;
12330 Roo.data.DataReader.prototype = {
12333 readerType : 'Data',
12335 * Create an empty record
12336 * @param {Object} data (optional) - overlay some values
12337 * @return {Roo.data.Record} record created.
12339 newRow : function(d) {
12341 this.recordType.prototype.fields.each(function(c) {
12343 case 'int' : da[c.name] = 0; break;
12344 case 'date' : da[c.name] = new Date(); break;
12345 case 'float' : da[c.name] = 0.0; break;
12346 case 'boolean' : da[c.name] = false; break;
12347 default : da[c.name] = ""; break;
12351 return new this.recordType(Roo.apply(da, d));
12357 * Ext JS Library 1.1.1
12358 * Copyright(c) 2006-2007, Ext JS, LLC.
12360 * Originally Released Under LGPL - original licence link has changed is not relivant.
12363 * <script type="text/javascript">
12367 * @class Roo.data.DataProxy
12368 * @extends Roo.data.Observable
12369 * This class is an abstract base class for implementations which provide retrieval of
12370 * unformatted data objects.<br>
12372 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12373 * (of the appropriate type which knows how to parse the data object) to provide a block of
12374 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12376 * Custom implementations must implement the load method as described in
12377 * {@link Roo.data.HttpProxy#load}.
12379 Roo.data.DataProxy = function(){
12382 * @event beforeload
12383 * Fires before a network request is made to retrieve a data object.
12384 * @param {Object} This DataProxy object.
12385 * @param {Object} params The params parameter to the load function.
12390 * Fires before the load method's callback is called.
12391 * @param {Object} This DataProxy object.
12392 * @param {Object} o The data object.
12393 * @param {Object} arg The callback argument object passed to the load function.
12397 * @event loadexception
12398 * Fires if an Exception occurs during data retrieval.
12399 * @param {Object} This DataProxy object.
12400 * @param {Object} o The data object.
12401 * @param {Object} arg The callback argument object passed to the load function.
12402 * @param {Object} e The Exception.
12404 loadexception : true
12406 Roo.data.DataProxy.superclass.constructor.call(this);
12409 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12412 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12416 * Ext JS Library 1.1.1
12417 * Copyright(c) 2006-2007, Ext JS, LLC.
12419 * Originally Released Under LGPL - original licence link has changed is not relivant.
12422 * <script type="text/javascript">
12425 * @class Roo.data.MemoryProxy
12426 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12427 * to the Reader when its load method is called.
12429 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12431 Roo.data.MemoryProxy = function(data){
12435 Roo.data.MemoryProxy.superclass.constructor.call(this);
12439 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12442 * Load data from the requested source (in this case an in-memory
12443 * data object passed to the constructor), read the data object into
12444 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12445 * process that block using the passed callback.
12446 * @param {Object} params This parameter is not used by the MemoryProxy class.
12447 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12448 * object into a block of Roo.data.Records.
12449 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12450 * The function must be passed <ul>
12451 * <li>The Record block object</li>
12452 * <li>The "arg" argument from the load function</li>
12453 * <li>A boolean success indicator</li>
12455 * @param {Object} scope The scope in which to call the callback
12456 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12458 load : function(params, reader, callback, scope, arg){
12459 params = params || {};
12462 result = reader.readRecords(params.data ? params.data :this.data);
12464 this.fireEvent("loadexception", this, arg, null, e);
12465 callback.call(scope, null, arg, false);
12468 callback.call(scope, result, arg, true);
12472 update : function(params, records){
12477 * Ext JS Library 1.1.1
12478 * Copyright(c) 2006-2007, Ext JS, LLC.
12480 * Originally Released Under LGPL - original licence link has changed is not relivant.
12483 * <script type="text/javascript">
12486 * @class Roo.data.HttpProxy
12487 * @extends Roo.data.DataProxy
12488 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12489 * configured to reference a certain URL.<br><br>
12491 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12492 * from which the running page was served.<br><br>
12494 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12496 * Be aware that to enable the browser to parse an XML document, the server must set
12497 * the Content-Type header in the HTTP response to "text/xml".
12499 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12500 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12501 * will be used to make the request.
12503 Roo.data.HttpProxy = function(conn){
12504 Roo.data.HttpProxy.superclass.constructor.call(this);
12505 // is conn a conn config or a real conn?
12507 this.useAjax = !conn || !conn.events;
12511 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12512 // thse are take from connection...
12515 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12518 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12519 * extra parameters to each request made by this object. (defaults to undefined)
12522 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12523 * to each request made by this object. (defaults to undefined)
12526 * @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)
12529 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12532 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12538 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12542 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12543 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12544 * a finer-grained basis than the DataProxy events.
12546 getConnection : function(){
12547 return this.useAjax ? Roo.Ajax : this.conn;
12551 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12552 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12553 * process that block using the passed callback.
12554 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12555 * for the request to the remote server.
12556 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12557 * object into a block of Roo.data.Records.
12558 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12559 * The function must be passed <ul>
12560 * <li>The Record block object</li>
12561 * <li>The "arg" argument from the load function</li>
12562 * <li>A boolean success indicator</li>
12564 * @param {Object} scope The scope in which to call the callback
12565 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12567 load : function(params, reader, callback, scope, arg){
12568 if(this.fireEvent("beforeload", this, params) !== false){
12570 params : params || {},
12572 callback : callback,
12577 callback : this.loadResponse,
12581 Roo.applyIf(o, this.conn);
12582 if(this.activeRequest){
12583 Roo.Ajax.abort(this.activeRequest);
12585 this.activeRequest = Roo.Ajax.request(o);
12587 this.conn.request(o);
12590 callback.call(scope||this, null, arg, false);
12595 loadResponse : function(o, success, response){
12596 delete this.activeRequest;
12598 this.fireEvent("loadexception", this, o, response);
12599 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12604 result = o.reader.read(response);
12606 this.fireEvent("loadexception", this, o, response, e);
12607 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12611 this.fireEvent("load", this, o, o.request.arg);
12612 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12616 update : function(dataSet){
12621 updateResponse : function(dataSet){
12626 * Ext JS Library 1.1.1
12627 * Copyright(c) 2006-2007, Ext JS, LLC.
12629 * Originally Released Under LGPL - original licence link has changed is not relivant.
12632 * <script type="text/javascript">
12636 * @class Roo.data.ScriptTagProxy
12637 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12638 * other than the originating domain of the running page.<br><br>
12640 * <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
12641 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12643 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12644 * source code that is used as the source inside a <script> tag.<br><br>
12646 * In order for the browser to process the returned data, the server must wrap the data object
12647 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12648 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12649 * depending on whether the callback name was passed:
12652 boolean scriptTag = false;
12653 String cb = request.getParameter("callback");
12656 response.setContentType("text/javascript");
12658 response.setContentType("application/x-json");
12660 Writer out = response.getWriter();
12662 out.write(cb + "(");
12664 out.print(dataBlock.toJsonString());
12671 * @param {Object} config A configuration object.
12673 Roo.data.ScriptTagProxy = function(config){
12674 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12675 Roo.apply(this, config);
12676 this.head = document.getElementsByTagName("head")[0];
12679 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12681 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12683 * @cfg {String} url The URL from which to request the data object.
12686 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12690 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12691 * the server the name of the callback function set up by the load call to process the returned data object.
12692 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12693 * javascript output which calls this named function passing the data object as its only parameter.
12695 callbackParam : "callback",
12697 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12698 * name to the request.
12703 * Load data from the configured URL, read the data object into
12704 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12705 * process that block using the passed callback.
12706 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12707 * for the request to the remote server.
12708 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12709 * object into a block of Roo.data.Records.
12710 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12711 * The function must be passed <ul>
12712 * <li>The Record block object</li>
12713 * <li>The "arg" argument from the load function</li>
12714 * <li>A boolean success indicator</li>
12716 * @param {Object} scope The scope in which to call the callback
12717 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12719 load : function(params, reader, callback, scope, arg){
12720 if(this.fireEvent("beforeload", this, params) !== false){
12722 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12724 var url = this.url;
12725 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12727 url += "&_dc=" + (new Date().getTime());
12729 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12732 cb : "stcCallback"+transId,
12733 scriptId : "stcScript"+transId,
12737 callback : callback,
12743 window[trans.cb] = function(o){
12744 conn.handleResponse(o, trans);
12747 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12749 if(this.autoAbort !== false){
12753 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12755 var script = document.createElement("script");
12756 script.setAttribute("src", url);
12757 script.setAttribute("type", "text/javascript");
12758 script.setAttribute("id", trans.scriptId);
12759 this.head.appendChild(script);
12761 this.trans = trans;
12763 callback.call(scope||this, null, arg, false);
12768 isLoading : function(){
12769 return this.trans ? true : false;
12773 * Abort the current server request.
12775 abort : function(){
12776 if(this.isLoading()){
12777 this.destroyTrans(this.trans);
12782 destroyTrans : function(trans, isLoaded){
12783 this.head.removeChild(document.getElementById(trans.scriptId));
12784 clearTimeout(trans.timeoutId);
12786 window[trans.cb] = undefined;
12788 delete window[trans.cb];
12791 // if hasn't been loaded, wait for load to remove it to prevent script error
12792 window[trans.cb] = function(){
12793 window[trans.cb] = undefined;
12795 delete window[trans.cb];
12802 handleResponse : function(o, trans){
12803 this.trans = false;
12804 this.destroyTrans(trans, true);
12807 result = trans.reader.readRecords(o);
12809 this.fireEvent("loadexception", this, o, trans.arg, e);
12810 trans.callback.call(trans.scope||window, null, trans.arg, false);
12813 this.fireEvent("load", this, o, trans.arg);
12814 trans.callback.call(trans.scope||window, result, trans.arg, true);
12818 handleFailure : function(trans){
12819 this.trans = false;
12820 this.destroyTrans(trans, false);
12821 this.fireEvent("loadexception", this, null, trans.arg);
12822 trans.callback.call(trans.scope||window, null, trans.arg, false);
12826 * Ext JS Library 1.1.1
12827 * Copyright(c) 2006-2007, Ext JS, LLC.
12829 * Originally Released Under LGPL - original licence link has changed is not relivant.
12832 * <script type="text/javascript">
12836 * @class Roo.data.JsonReader
12837 * @extends Roo.data.DataReader
12838 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12839 * based on mappings in a provided Roo.data.Record constructor.
12841 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12842 * in the reply previously.
12847 var RecordDef = Roo.data.Record.create([
12848 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12849 {name: 'occupation'} // This field will use "occupation" as the mapping.
12851 var myReader = new Roo.data.JsonReader({
12852 totalProperty: "results", // The property which contains the total dataset size (optional)
12853 root: "rows", // The property which contains an Array of row objects
12854 id: "id" // The property within each row object that provides an ID for the record (optional)
12858 * This would consume a JSON file like this:
12860 { 'results': 2, 'rows': [
12861 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12862 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12865 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12866 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12867 * paged from the remote server.
12868 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12869 * @cfg {String} root name of the property which contains the Array of row objects.
12870 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12871 * @cfg {Array} fields Array of field definition objects
12873 * Create a new JsonReader
12874 * @param {Object} meta Metadata configuration options
12875 * @param {Object} recordType Either an Array of field definition objects,
12876 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12878 Roo.data.JsonReader = function(meta, recordType){
12881 // set some defaults:
12882 Roo.applyIf(meta, {
12883 totalProperty: 'total',
12884 successProperty : 'success',
12889 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12891 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12893 readerType : 'Json',
12896 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12897 * Used by Store query builder to append _requestMeta to params.
12900 metaFromRemote : false,
12902 * This method is only used by a DataProxy which has retrieved data from a remote server.
12903 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12904 * @return {Object} data A data block which is used by an Roo.data.Store object as
12905 * a cache of Roo.data.Records.
12907 read : function(response){
12908 var json = response.responseText;
12910 var o = /* eval:var:o */ eval("("+json+")");
12912 throw {message: "JsonReader.read: Json object not found"};
12918 this.metaFromRemote = true;
12919 this.meta = o.metaData;
12920 this.recordType = Roo.data.Record.create(o.metaData.fields);
12921 this.onMetaChange(this.meta, this.recordType, o);
12923 return this.readRecords(o);
12926 // private function a store will implement
12927 onMetaChange : function(meta, recordType, o){
12934 simpleAccess: function(obj, subsc) {
12941 getJsonAccessor: function(){
12943 return function(expr) {
12945 return(re.test(expr))
12946 ? new Function("obj", "return obj." + expr)
12951 return Roo.emptyFn;
12956 * Create a data block containing Roo.data.Records from an XML document.
12957 * @param {Object} o An object which contains an Array of row objects in the property specified
12958 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12959 * which contains the total size of the dataset.
12960 * @return {Object} data A data block which is used by an Roo.data.Store object as
12961 * a cache of Roo.data.Records.
12963 readRecords : function(o){
12965 * After any data loads, the raw JSON data is available for further custom processing.
12969 var s = this.meta, Record = this.recordType,
12970 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12972 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12974 if(s.totalProperty) {
12975 this.getTotal = this.getJsonAccessor(s.totalProperty);
12977 if(s.successProperty) {
12978 this.getSuccess = this.getJsonAccessor(s.successProperty);
12980 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12982 var g = this.getJsonAccessor(s.id);
12983 this.getId = function(rec) {
12985 return (r === undefined || r === "") ? null : r;
12988 this.getId = function(){return null;};
12991 for(var jj = 0; jj < fl; jj++){
12993 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12994 this.ef[jj] = this.getJsonAccessor(map);
12998 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12999 if(s.totalProperty){
13000 var vt = parseInt(this.getTotal(o), 10);
13005 if(s.successProperty){
13006 var vs = this.getSuccess(o);
13007 if(vs === false || vs === 'false'){
13012 for(var i = 0; i < c; i++){
13015 var id = this.getId(n);
13016 for(var j = 0; j < fl; j++){
13018 var v = this.ef[j](n);
13020 Roo.log('missing convert for ' + f.name);
13024 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13026 var record = new Record(values, id);
13028 records[i] = record;
13034 totalRecords : totalRecords
13037 // used when loading children.. @see loadDataFromChildren
13038 toLoadData: function(rec)
13040 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13041 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13042 return { data : data, total : data.length };
13047 * Ext JS Library 1.1.1
13048 * Copyright(c) 2006-2007, Ext JS, LLC.
13050 * Originally Released Under LGPL - original licence link has changed is not relivant.
13053 * <script type="text/javascript">
13057 * @class Roo.data.ArrayReader
13058 * @extends Roo.data.DataReader
13059 * Data reader class to create an Array of Roo.data.Record objects from an Array.
13060 * Each element of that Array represents a row of data fields. The
13061 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13062 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13066 var RecordDef = Roo.data.Record.create([
13067 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
13068 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
13070 var myReader = new Roo.data.ArrayReader({
13071 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
13075 * This would consume an Array like this:
13077 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13081 * Create a new JsonReader
13082 * @param {Object} meta Metadata configuration options.
13083 * @param {Object|Array} recordType Either an Array of field definition objects
13085 * @cfg {Array} fields Array of field definition objects
13086 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13087 * as specified to {@link Roo.data.Record#create},
13088 * or an {@link Roo.data.Record} object
13091 * created using {@link Roo.data.Record#create}.
13093 Roo.data.ArrayReader = function(meta, recordType)
13095 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13098 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13101 * Create a data block containing Roo.data.Records from an XML document.
13102 * @param {Object} o An Array of row objects which represents the dataset.
13103 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13104 * a cache of Roo.data.Records.
13106 readRecords : function(o)
13108 var sid = this.meta ? this.meta.id : null;
13109 var recordType = this.recordType, fields = recordType.prototype.fields;
13112 for(var i = 0; i < root.length; i++){
13115 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13116 for(var j = 0, jlen = fields.length; j < jlen; j++){
13117 var f = fields.items[j];
13118 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13119 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13121 values[f.name] = v;
13123 var record = new recordType(values, id);
13125 records[records.length] = record;
13129 totalRecords : records.length
13132 // used when loading children.. @see loadDataFromChildren
13133 toLoadData: function(rec)
13135 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13136 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13147 * @class Roo.bootstrap.ComboBox
13148 * @extends Roo.bootstrap.TriggerField
13149 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13150 * @cfg {Boolean} append (true|false) default false
13151 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13152 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13153 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13154 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13155 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13156 * @cfg {Boolean} animate default true
13157 * @cfg {Boolean} emptyResultText only for touch device
13158 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13159 * @cfg {String} emptyTitle default ''
13161 * Create a new ComboBox.
13162 * @param {Object} config Configuration options
13164 Roo.bootstrap.ComboBox = function(config){
13165 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13169 * Fires when the dropdown list is expanded
13170 * @param {Roo.bootstrap.ComboBox} combo This combo box
13175 * Fires when the dropdown list is collapsed
13176 * @param {Roo.bootstrap.ComboBox} combo This combo box
13180 * @event beforeselect
13181 * Fires before a list item is selected. Return false to cancel the selection.
13182 * @param {Roo.bootstrap.ComboBox} combo This combo box
13183 * @param {Roo.data.Record} record The data record returned from the underlying store
13184 * @param {Number} index The index of the selected item in the dropdown list
13186 'beforeselect' : true,
13189 * Fires when a list item is selected
13190 * @param {Roo.bootstrap.ComboBox} combo This combo box
13191 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13192 * @param {Number} index The index of the selected item in the dropdown list
13196 * @event beforequery
13197 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13198 * The event object passed has these properties:
13199 * @param {Roo.bootstrap.ComboBox} combo This combo box
13200 * @param {String} query The query
13201 * @param {Boolean} forceAll true to force "all" query
13202 * @param {Boolean} cancel true to cancel the query
13203 * @param {Object} e The query event object
13205 'beforequery': true,
13208 * Fires when the 'add' icon is pressed (add a listener to enable add button)
13209 * @param {Roo.bootstrap.ComboBox} combo This combo box
13214 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13215 * @param {Roo.bootstrap.ComboBox} combo This combo box
13216 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13221 * Fires when the remove value from the combobox array
13222 * @param {Roo.bootstrap.ComboBox} combo This combo box
13226 * @event afterremove
13227 * Fires when the remove value from the combobox array
13228 * @param {Roo.bootstrap.ComboBox} combo This combo box
13230 'afterremove' : true,
13232 * @event specialfilter
13233 * Fires when specialfilter
13234 * @param {Roo.bootstrap.ComboBox} combo This combo box
13236 'specialfilter' : true,
13239 * Fires when tick the element
13240 * @param {Roo.bootstrap.ComboBox} combo This combo box
13244 * @event touchviewdisplay
13245 * Fires when touch view require special display (default is using displayField)
13246 * @param {Roo.bootstrap.ComboBox} combo This combo box
13247 * @param {Object} cfg set html .
13249 'touchviewdisplay' : true
13254 this.tickItems = [];
13256 this.selectedIndex = -1;
13257 if(this.mode == 'local'){
13258 if(config.queryDelay === undefined){
13259 this.queryDelay = 10;
13261 if(config.minChars === undefined){
13267 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13270 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13271 * rendering into an Roo.Editor, defaults to false)
13274 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13275 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13278 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13281 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13282 * the dropdown list (defaults to undefined, with no header element)
13286 * @cfg {String/Roo.Template} tpl The template to use to render the output
13290 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13292 listWidth: undefined,
13294 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13295 * mode = 'remote' or 'text' if mode = 'local')
13297 displayField: undefined,
13300 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13301 * mode = 'remote' or 'value' if mode = 'local').
13302 * Note: use of a valueField requires the user make a selection
13303 * in order for a value to be mapped.
13305 valueField: undefined,
13307 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13312 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13313 * field's data value (defaults to the underlying DOM element's name)
13315 hiddenName: undefined,
13317 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13321 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13323 selectedClass: 'active',
13326 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13330 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13331 * anchor positions (defaults to 'tl-bl')
13333 listAlign: 'tl-bl?',
13335 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13339 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13340 * query specified by the allQuery config option (defaults to 'query')
13342 triggerAction: 'query',
13344 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13345 * (defaults to 4, does not apply if editable = false)
13349 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13350 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13354 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13355 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13359 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13360 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13364 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13365 * when editable = true (defaults to false)
13367 selectOnFocus:false,
13369 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13371 queryParam: 'query',
13373 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13374 * when mode = 'remote' (defaults to 'Loading...')
13376 loadingText: 'Loading...',
13378 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13382 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13386 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13387 * traditional select (defaults to true)
13391 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13395 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13399 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13400 * listWidth has a higher value)
13404 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13405 * allow the user to set arbitrary text into the field (defaults to false)
13407 forceSelection:false,
13409 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13410 * if typeAhead = true (defaults to 250)
13412 typeAheadDelay : 250,
13414 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13415 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13417 valueNotFoundText : undefined,
13419 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13421 blockFocus : false,
13424 * @cfg {Boolean} disableClear Disable showing of clear button.
13426 disableClear : false,
13428 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13430 alwaysQuery : false,
13433 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13438 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13440 invalidClass : "has-warning",
13443 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13445 validClass : "has-success",
13448 * @cfg {Boolean} specialFilter (true|false) special filter default false
13450 specialFilter : false,
13453 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13455 mobileTouchView : true,
13458 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13460 useNativeIOS : false,
13463 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13465 mobile_restrict_height : false,
13467 ios_options : false,
13479 btnPosition : 'right',
13480 triggerList : true,
13481 showToggleBtn : true,
13483 emptyResultText: 'Empty',
13484 triggerText : 'Select',
13487 // element that contains real text value.. (when hidden is used..)
13489 getAutoCreate : function()
13494 * Render classic select for iso
13497 if(Roo.isIOS && this.useNativeIOS){
13498 cfg = this.getAutoCreateNativeIOS();
13506 if(Roo.isTouch && this.mobileTouchView){
13507 cfg = this.getAutoCreateTouchView();
13514 if(!this.tickable){
13515 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13520 * ComboBox with tickable selections
13523 var align = this.labelAlign || this.parentLabelAlign();
13526 cls : 'form-group roo-combobox-tickable' //input-group
13529 var btn_text_select = '';
13530 var btn_text_done = '';
13531 var btn_text_cancel = '';
13533 if (this.btn_text_show) {
13534 btn_text_select = 'Select';
13535 btn_text_done = 'Done';
13536 btn_text_cancel = 'Cancel';
13541 cls : 'tickable-buttons',
13546 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13547 //html : this.triggerText
13548 html: btn_text_select
13554 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13556 html: btn_text_done
13562 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13564 html: btn_text_cancel
13570 buttons.cn.unshift({
13572 cls: 'roo-select2-search-field-input'
13578 Roo.each(buttons.cn, function(c){
13580 c.cls += ' btn-' + _this.size;
13583 if (_this.disabled) {
13590 style : 'display: contents',
13595 cls: 'form-hidden-field'
13599 cls: 'roo-select2-choices',
13603 cls: 'roo-select2-search-field',
13614 cls: 'roo-select2-container input-group roo-select2-container-multi',
13620 // cls: 'typeahead typeahead-long dropdown-menu',
13621 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13626 if(this.hasFeedback && !this.allowBlank){
13630 cls: 'glyphicon form-control-feedback'
13633 combobox.cn.push(feedback);
13638 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13639 tooltip : 'This field is required'
13641 if (Roo.bootstrap.version == 4) {
13644 style : 'display:none'
13647 if (align ==='left' && this.fieldLabel.length) {
13649 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13656 cls : 'control-label col-form-label',
13657 html : this.fieldLabel
13669 var labelCfg = cfg.cn[1];
13670 var contentCfg = cfg.cn[2];
13673 if(this.indicatorpos == 'right'){
13679 cls : 'control-label col-form-label',
13683 html : this.fieldLabel
13699 labelCfg = cfg.cn[0];
13700 contentCfg = cfg.cn[1];
13704 if(this.labelWidth > 12){
13705 labelCfg.style = "width: " + this.labelWidth + 'px';
13708 if(this.labelWidth < 13 && this.labelmd == 0){
13709 this.labelmd = this.labelWidth;
13712 if(this.labellg > 0){
13713 labelCfg.cls += ' col-lg-' + this.labellg;
13714 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13717 if(this.labelmd > 0){
13718 labelCfg.cls += ' col-md-' + this.labelmd;
13719 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13722 if(this.labelsm > 0){
13723 labelCfg.cls += ' col-sm-' + this.labelsm;
13724 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13727 if(this.labelxs > 0){
13728 labelCfg.cls += ' col-xs-' + this.labelxs;
13729 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13733 } else if ( this.fieldLabel.length) {
13734 // Roo.log(" label");
13739 //cls : 'input-group-addon',
13740 html : this.fieldLabel
13745 if(this.indicatorpos == 'right'){
13749 //cls : 'input-group-addon',
13750 html : this.fieldLabel
13760 // Roo.log(" no label && no align");
13767 ['xs','sm','md','lg'].map(function(size){
13768 if (settings[size]) {
13769 cfg.cls += ' col-' + size + '-' + settings[size];
13777 _initEventsCalled : false,
13780 initEvents: function()
13782 if (this._initEventsCalled) { // as we call render... prevent looping...
13785 this._initEventsCalled = true;
13788 throw "can not find store for combo";
13791 this.indicator = this.indicatorEl();
13793 this.store = Roo.factory(this.store, Roo.data);
13794 this.store.parent = this;
13796 // if we are building from html. then this element is so complex, that we can not really
13797 // use the rendered HTML.
13798 // so we have to trash and replace the previous code.
13799 if (Roo.XComponent.build_from_html) {
13800 // remove this element....
13801 var e = this.el.dom, k=0;
13802 while (e ) { e = e.previousSibling; ++k;}
13807 this.rendered = false;
13809 this.render(this.parent().getChildContainer(true), k);
13812 if(Roo.isIOS && this.useNativeIOS){
13813 this.initIOSView();
13821 if(Roo.isTouch && this.mobileTouchView){
13822 this.initTouchView();
13827 this.initTickableEvents();
13831 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13833 if(this.hiddenName){
13835 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13837 this.hiddenField.dom.value =
13838 this.hiddenValue !== undefined ? this.hiddenValue :
13839 this.value !== undefined ? this.value : '';
13841 // prevent input submission
13842 this.el.dom.removeAttribute('name');
13843 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13848 // this.el.dom.setAttribute('autocomplete', 'off');
13851 var cls = 'x-combo-list';
13853 //this.list = new Roo.Layer({
13854 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13860 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13861 _this.list.setWidth(lw);
13864 this.list.on('mouseover', this.onViewOver, this);
13865 this.list.on('mousemove', this.onViewMove, this);
13866 this.list.on('scroll', this.onViewScroll, this);
13869 this.list.swallowEvent('mousewheel');
13870 this.assetHeight = 0;
13873 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13874 this.assetHeight += this.header.getHeight();
13877 this.innerList = this.list.createChild({cls:cls+'-inner'});
13878 this.innerList.on('mouseover', this.onViewOver, this);
13879 this.innerList.on('mousemove', this.onViewMove, this);
13880 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13882 if(this.allowBlank && !this.pageSize && !this.disableClear){
13883 this.footer = this.list.createChild({cls:cls+'-ft'});
13884 this.pageTb = new Roo.Toolbar(this.footer);
13888 this.footer = this.list.createChild({cls:cls+'-ft'});
13889 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13890 {pageSize: this.pageSize});
13894 if (this.pageTb && this.allowBlank && !this.disableClear) {
13896 this.pageTb.add(new Roo.Toolbar.Fill(), {
13897 cls: 'x-btn-icon x-btn-clear',
13899 handler: function()
13902 _this.clearValue();
13903 _this.onSelect(false, -1);
13908 this.assetHeight += this.footer.getHeight();
13913 this.tpl = Roo.bootstrap.version == 4 ?
13914 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
13915 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13918 this.view = new Roo.View(this.list, this.tpl, {
13919 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13921 //this.view.wrapEl.setDisplayed(false);
13922 this.view.on('click', this.onViewClick, this);
13925 this.store.on('beforeload', this.onBeforeLoad, this);
13926 this.store.on('load', this.onLoad, this);
13927 this.store.on('loadexception', this.onLoadException, this);
13929 if(this.resizable){
13930 this.resizer = new Roo.Resizable(this.list, {
13931 pinned:true, handles:'se'
13933 this.resizer.on('resize', function(r, w, h){
13934 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13935 this.listWidth = w;
13936 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13937 this.restrictHeight();
13939 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13942 if(!this.editable){
13943 this.editable = true;
13944 this.setEditable(false);
13949 if (typeof(this.events.add.listeners) != 'undefined') {
13951 this.addicon = this.wrap.createChild(
13952 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13954 this.addicon.on('click', function(e) {
13955 this.fireEvent('add', this);
13958 if (typeof(this.events.edit.listeners) != 'undefined') {
13960 this.editicon = this.wrap.createChild(
13961 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13962 if (this.addicon) {
13963 this.editicon.setStyle('margin-left', '40px');
13965 this.editicon.on('click', function(e) {
13967 // we fire even if inothing is selected..
13968 this.fireEvent('edit', this, this.lastData );
13974 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13975 "up" : function(e){
13976 this.inKeyMode = true;
13980 "down" : function(e){
13981 if(!this.isExpanded()){
13982 this.onTriggerClick();
13984 this.inKeyMode = true;
13989 "enter" : function(e){
13990 // this.onViewClick();
13994 if(this.fireEvent("specialkey", this, e)){
13995 this.onViewClick(false);
14001 "esc" : function(e){
14005 "tab" : function(e){
14008 if(this.fireEvent("specialkey", this, e)){
14009 this.onViewClick(false);
14017 doRelay : function(foo, bar, hname){
14018 if(hname == 'down' || this.scope.isExpanded()){
14019 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14028 this.queryDelay = Math.max(this.queryDelay || 10,
14029 this.mode == 'local' ? 10 : 250);
14032 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14034 if(this.typeAhead){
14035 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14037 if(this.editable !== false){
14038 this.inputEl().on("keyup", this.onKeyUp, this);
14040 if(this.forceSelection){
14041 this.inputEl().on('blur', this.doForce, this);
14045 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14046 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14050 initTickableEvents: function()
14054 if(this.hiddenName){
14056 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14058 this.hiddenField.dom.value =
14059 this.hiddenValue !== undefined ? this.hiddenValue :
14060 this.value !== undefined ? this.value : '';
14062 // prevent input submission
14063 this.el.dom.removeAttribute('name');
14064 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14069 // this.list = this.el.select('ul.dropdown-menu',true).first();
14071 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14072 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14073 if(this.triggerList){
14074 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14077 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14078 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14080 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14081 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14083 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14084 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14086 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14087 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14088 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14091 this.cancelBtn.hide();
14096 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14097 _this.list.setWidth(lw);
14100 this.list.on('mouseover', this.onViewOver, this);
14101 this.list.on('mousemove', this.onViewMove, this);
14103 this.list.on('scroll', this.onViewScroll, this);
14106 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
14107 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14110 this.view = new Roo.View(this.list, this.tpl, {
14115 selectedClass: this.selectedClass
14118 //this.view.wrapEl.setDisplayed(false);
14119 this.view.on('click', this.onViewClick, this);
14123 this.store.on('beforeload', this.onBeforeLoad, this);
14124 this.store.on('load', this.onLoad, this);
14125 this.store.on('loadexception', this.onLoadException, this);
14128 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14129 "up" : function(e){
14130 this.inKeyMode = true;
14134 "down" : function(e){
14135 this.inKeyMode = true;
14139 "enter" : function(e){
14140 if(this.fireEvent("specialkey", this, e)){
14141 this.onViewClick(false);
14147 "esc" : function(e){
14148 this.onTickableFooterButtonClick(e, false, false);
14151 "tab" : function(e){
14152 this.fireEvent("specialkey", this, e);
14154 this.onTickableFooterButtonClick(e, false, false);
14161 doRelay : function(e, fn, key){
14162 if(this.scope.isExpanded()){
14163 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14172 this.queryDelay = Math.max(this.queryDelay || 10,
14173 this.mode == 'local' ? 10 : 250);
14176 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14178 if(this.typeAhead){
14179 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14182 if(this.editable !== false){
14183 this.tickableInputEl().on("keyup", this.onKeyUp, this);
14186 this.indicator = this.indicatorEl();
14188 if(this.indicator){
14189 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14190 this.indicator.hide();
14195 onDestroy : function(){
14197 this.view.setStore(null);
14198 this.view.el.removeAllListeners();
14199 this.view.el.remove();
14200 this.view.purgeListeners();
14203 this.list.dom.innerHTML = '';
14207 this.store.un('beforeload', this.onBeforeLoad, this);
14208 this.store.un('load', this.onLoad, this);
14209 this.store.un('loadexception', this.onLoadException, this);
14211 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14215 fireKey : function(e){
14216 if(e.isNavKeyPress() && !this.list.isVisible()){
14217 this.fireEvent("specialkey", this, e);
14222 onResize: function(w, h){
14223 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14225 // if(typeof w != 'number'){
14226 // // we do not handle it!?!?
14229 // var tw = this.trigger.getWidth();
14230 // // tw += this.addicon ? this.addicon.getWidth() : 0;
14231 // // tw += this.editicon ? this.editicon.getWidth() : 0;
14233 // this.inputEl().setWidth( this.adjustWidth('input', x));
14235 // //this.trigger.setStyle('left', x+'px');
14237 // if(this.list && this.listWidth === undefined){
14238 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14239 // this.list.setWidth(lw);
14240 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14248 * Allow or prevent the user from directly editing the field text. If false is passed,
14249 * the user will only be able to select from the items defined in the dropdown list. This method
14250 * is the runtime equivalent of setting the 'editable' config option at config time.
14251 * @param {Boolean} value True to allow the user to directly edit the field text
14253 setEditable : function(value){
14254 if(value == this.editable){
14257 this.editable = value;
14259 this.inputEl().dom.setAttribute('readOnly', true);
14260 this.inputEl().on('mousedown', this.onTriggerClick, this);
14261 this.inputEl().addClass('x-combo-noedit');
14263 this.inputEl().dom.setAttribute('readOnly', false);
14264 this.inputEl().un('mousedown', this.onTriggerClick, this);
14265 this.inputEl().removeClass('x-combo-noedit');
14271 onBeforeLoad : function(combo,opts){
14272 if(!this.hasFocus){
14276 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14278 this.restrictHeight();
14279 this.selectedIndex = -1;
14283 onLoad : function(){
14285 this.hasQuery = false;
14287 if(!this.hasFocus){
14291 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14292 this.loading.hide();
14295 if(this.store.getCount() > 0){
14298 this.restrictHeight();
14299 if(this.lastQuery == this.allQuery){
14300 if(this.editable && !this.tickable){
14301 this.inputEl().dom.select();
14305 !this.selectByValue(this.value, true) &&
14308 !this.store.lastOptions ||
14309 typeof(this.store.lastOptions.add) == 'undefined' ||
14310 this.store.lastOptions.add != true
14313 this.select(0, true);
14316 if(this.autoFocus){
14319 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14320 this.taTask.delay(this.typeAheadDelay);
14324 this.onEmptyResults();
14330 onLoadException : function()
14332 this.hasQuery = false;
14334 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14335 this.loading.hide();
14338 if(this.tickable && this.editable){
14343 // only causes errors at present
14344 //Roo.log(this.store.reader.jsonData);
14345 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14347 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14353 onTypeAhead : function(){
14354 if(this.store.getCount() > 0){
14355 var r = this.store.getAt(0);
14356 var newValue = r.data[this.displayField];
14357 var len = newValue.length;
14358 var selStart = this.getRawValue().length;
14360 if(selStart != len){
14361 this.setRawValue(newValue);
14362 this.selectText(selStart, newValue.length);
14368 onSelect : function(record, index){
14370 if(this.fireEvent('beforeselect', this, record, index) !== false){
14372 this.setFromData(index > -1 ? record.data : false);
14375 this.fireEvent('select', this, record, index);
14380 * Returns the currently selected field value or empty string if no value is set.
14381 * @return {String} value The selected value
14383 getValue : function()
14385 if(Roo.isIOS && this.useNativeIOS){
14386 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14390 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14393 if(this.valueField){
14394 return typeof this.value != 'undefined' ? this.value : '';
14396 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14400 getRawValue : function()
14402 if(Roo.isIOS && this.useNativeIOS){
14403 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14406 var v = this.inputEl().getValue();
14412 * Clears any text/value currently set in the field
14414 clearValue : function(){
14416 if(this.hiddenField){
14417 this.hiddenField.dom.value = '';
14420 this.setRawValue('');
14421 this.lastSelectionText = '';
14422 this.lastData = false;
14424 var close = this.closeTriggerEl();
14435 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14436 * will be displayed in the field. If the value does not match the data value of an existing item,
14437 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14438 * Otherwise the field will be blank (although the value will still be set).
14439 * @param {String} value The value to match
14441 setValue : function(v)
14443 if(Roo.isIOS && this.useNativeIOS){
14444 this.setIOSValue(v);
14454 if(this.valueField){
14455 var r = this.findRecord(this.valueField, v);
14457 text = r.data[this.displayField];
14458 }else if(this.valueNotFoundText !== undefined){
14459 text = this.valueNotFoundText;
14462 this.lastSelectionText = text;
14463 if(this.hiddenField){
14464 this.hiddenField.dom.value = v;
14466 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14469 var close = this.closeTriggerEl();
14472 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14478 * @property {Object} the last set data for the element
14483 * Sets the value of the field based on a object which is related to the record format for the store.
14484 * @param {Object} value the value to set as. or false on reset?
14486 setFromData : function(o){
14493 var dv = ''; // display value
14494 var vv = ''; // value value..
14496 if (this.displayField) {
14497 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14499 // this is an error condition!!!
14500 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14503 if(this.valueField){
14504 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14507 var close = this.closeTriggerEl();
14510 if(dv.length || vv * 1 > 0){
14512 this.blockFocus=true;
14518 if(this.hiddenField){
14519 this.hiddenField.dom.value = vv;
14521 this.lastSelectionText = dv;
14522 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14526 // no hidden field.. - we store the value in 'value', but still display
14527 // display field!!!!
14528 this.lastSelectionText = dv;
14529 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14536 reset : function(){
14537 // overridden so that last data is reset..
14544 this.setValue(this.originalValue);
14545 //this.clearInvalid();
14546 this.lastData = false;
14548 this.view.clearSelections();
14554 findRecord : function(prop, value){
14556 if(this.store.getCount() > 0){
14557 this.store.each(function(r){
14558 if(r.data[prop] == value){
14568 getName: function()
14570 // returns hidden if it's set..
14571 if (!this.rendered) {return ''};
14572 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14576 onViewMove : function(e, t){
14577 this.inKeyMode = false;
14581 onViewOver : function(e, t){
14582 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14585 var item = this.view.findItemFromChild(t);
14588 var index = this.view.indexOf(item);
14589 this.select(index, false);
14594 onViewClick : function(view, doFocus, el, e)
14596 var index = this.view.getSelectedIndexes()[0];
14598 var r = this.store.getAt(index);
14602 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14609 Roo.each(this.tickItems, function(v,k){
14611 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14613 _this.tickItems.splice(k, 1);
14615 if(typeof(e) == 'undefined' && view == false){
14616 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14628 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14629 this.tickItems.push(r.data);
14632 if(typeof(e) == 'undefined' && view == false){
14633 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14640 this.onSelect(r, index);
14642 if(doFocus !== false && !this.blockFocus){
14643 this.inputEl().focus();
14648 restrictHeight : function(){
14649 //this.innerList.dom.style.height = '';
14650 //var inner = this.innerList.dom;
14651 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14652 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14653 //this.list.beginUpdate();
14654 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14655 this.list.alignTo(this.inputEl(), this.listAlign);
14656 this.list.alignTo(this.inputEl(), this.listAlign);
14657 //this.list.endUpdate();
14661 onEmptyResults : function(){
14663 if(this.tickable && this.editable){
14664 this.hasFocus = false;
14665 this.restrictHeight();
14673 * Returns true if the dropdown list is expanded, else false.
14675 isExpanded : function(){
14676 return this.list.isVisible();
14680 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14681 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14682 * @param {String} value The data value of the item to select
14683 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14684 * selected item if it is not currently in view (defaults to true)
14685 * @return {Boolean} True if the value matched an item in the list, else false
14687 selectByValue : function(v, scrollIntoView){
14688 if(v !== undefined && v !== null){
14689 var r = this.findRecord(this.valueField || this.displayField, v);
14691 this.select(this.store.indexOf(r), scrollIntoView);
14699 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14700 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14701 * @param {Number} index The zero-based index of the list item to select
14702 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14703 * selected item if it is not currently in view (defaults to true)
14705 select : function(index, scrollIntoView){
14706 this.selectedIndex = index;
14707 this.view.select(index);
14708 if(scrollIntoView !== false){
14709 var el = this.view.getNode(index);
14711 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14714 this.list.scrollChildIntoView(el, false);
14720 selectNext : function(){
14721 var ct = this.store.getCount();
14723 if(this.selectedIndex == -1){
14725 }else if(this.selectedIndex < ct-1){
14726 this.select(this.selectedIndex+1);
14732 selectPrev : function(){
14733 var ct = this.store.getCount();
14735 if(this.selectedIndex == -1){
14737 }else if(this.selectedIndex != 0){
14738 this.select(this.selectedIndex-1);
14744 onKeyUp : function(e){
14745 if(this.editable !== false && !e.isSpecialKey()){
14746 this.lastKey = e.getKey();
14747 this.dqTask.delay(this.queryDelay);
14752 validateBlur : function(){
14753 return !this.list || !this.list.isVisible();
14757 initQuery : function(){
14759 var v = this.getRawValue();
14761 if(this.tickable && this.editable){
14762 v = this.tickableInputEl().getValue();
14769 doForce : function(){
14770 if(this.inputEl().dom.value.length > 0){
14771 this.inputEl().dom.value =
14772 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14778 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14779 * query allowing the query action to be canceled if needed.
14780 * @param {String} query The SQL query to execute
14781 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14782 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14783 * saved in the current store (defaults to false)
14785 doQuery : function(q, forceAll){
14787 if(q === undefined || q === null){
14792 forceAll: forceAll,
14796 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14801 forceAll = qe.forceAll;
14802 if(forceAll === true || (q.length >= this.minChars)){
14804 this.hasQuery = true;
14806 if(this.lastQuery != q || this.alwaysQuery){
14807 this.lastQuery = q;
14808 if(this.mode == 'local'){
14809 this.selectedIndex = -1;
14811 this.store.clearFilter();
14814 if(this.specialFilter){
14815 this.fireEvent('specialfilter', this);
14820 this.store.filter(this.displayField, q);
14823 this.store.fireEvent("datachanged", this.store);
14830 this.store.baseParams[this.queryParam] = q;
14832 var options = {params : this.getParams(q)};
14835 options.add = true;
14836 options.params.start = this.page * this.pageSize;
14839 this.store.load(options);
14842 * this code will make the page width larger, at the beginning, the list not align correctly,
14843 * we should expand the list on onLoad
14844 * so command out it
14849 this.selectedIndex = -1;
14854 this.loadNext = false;
14858 getParams : function(q){
14860 //p[this.queryParam] = q;
14864 p.limit = this.pageSize;
14870 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14872 collapse : function(){
14873 if(!this.isExpanded()){
14879 this.hasFocus = false;
14883 this.cancelBtn.hide();
14884 this.trigger.show();
14887 this.tickableInputEl().dom.value = '';
14888 this.tickableInputEl().blur();
14893 Roo.get(document).un('mousedown', this.collapseIf, this);
14894 Roo.get(document).un('mousewheel', this.collapseIf, this);
14895 if (!this.editable) {
14896 Roo.get(document).un('keydown', this.listKeyPress, this);
14898 this.fireEvent('collapse', this);
14904 collapseIf : function(e){
14905 var in_combo = e.within(this.el);
14906 var in_list = e.within(this.list);
14907 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14909 if (in_combo || in_list || is_list) {
14910 //e.stopPropagation();
14915 this.onTickableFooterButtonClick(e, false, false);
14923 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14925 expand : function(){
14927 if(this.isExpanded() || !this.hasFocus){
14931 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14932 this.list.setWidth(lw);
14938 this.restrictHeight();
14942 this.tickItems = Roo.apply([], this.item);
14945 this.cancelBtn.show();
14946 this.trigger.hide();
14949 this.tickableInputEl().focus();
14954 Roo.get(document).on('mousedown', this.collapseIf, this);
14955 Roo.get(document).on('mousewheel', this.collapseIf, this);
14956 if (!this.editable) {
14957 Roo.get(document).on('keydown', this.listKeyPress, this);
14960 this.fireEvent('expand', this);
14964 // Implements the default empty TriggerField.onTriggerClick function
14965 onTriggerClick : function(e)
14967 Roo.log('trigger click');
14969 if(this.disabled || !this.triggerList){
14974 this.loadNext = false;
14976 if(this.isExpanded()){
14978 if (!this.blockFocus) {
14979 this.inputEl().focus();
14983 this.hasFocus = true;
14984 if(this.triggerAction == 'all') {
14985 this.doQuery(this.allQuery, true);
14987 this.doQuery(this.getRawValue());
14989 if (!this.blockFocus) {
14990 this.inputEl().focus();
14995 onTickableTriggerClick : function(e)
15002 this.loadNext = false;
15003 this.hasFocus = true;
15005 if(this.triggerAction == 'all') {
15006 this.doQuery(this.allQuery, true);
15008 this.doQuery(this.getRawValue());
15012 onSearchFieldClick : function(e)
15014 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
15015 this.onTickableFooterButtonClick(e, false, false);
15019 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
15024 this.loadNext = false;
15025 this.hasFocus = true;
15027 if(this.triggerAction == 'all') {
15028 this.doQuery(this.allQuery, true);
15030 this.doQuery(this.getRawValue());
15034 listKeyPress : function(e)
15036 //Roo.log('listkeypress');
15037 // scroll to first matching element based on key pres..
15038 if (e.isSpecialKey()) {
15041 var k = String.fromCharCode(e.getKey()).toUpperCase();
15044 var csel = this.view.getSelectedNodes();
15045 var cselitem = false;
15047 var ix = this.view.indexOf(csel[0]);
15048 cselitem = this.store.getAt(ix);
15049 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15055 this.store.each(function(v) {
15057 // start at existing selection.
15058 if (cselitem.id == v.id) {
15064 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15065 match = this.store.indexOf(v);
15071 if (match === false) {
15072 return true; // no more action?
15075 this.view.select(match);
15076 var sn = Roo.get(this.view.getSelectedNodes()[0]);
15077 sn.scrollIntoView(sn.dom.parentNode, false);
15080 onViewScroll : function(e, t){
15082 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){
15086 this.hasQuery = true;
15088 this.loading = this.list.select('.loading', true).first();
15090 if(this.loading === null){
15091 this.list.createChild({
15093 cls: 'loading roo-select2-more-results roo-select2-active',
15094 html: 'Loading more results...'
15097 this.loading = this.list.select('.loading', true).first();
15099 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15101 this.loading.hide();
15104 this.loading.show();
15109 this.loadNext = true;
15111 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15116 addItem : function(o)
15118 var dv = ''; // display value
15120 if (this.displayField) {
15121 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15123 // this is an error condition!!!
15124 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
15131 var choice = this.choices.createChild({
15133 cls: 'roo-select2-search-choice',
15142 cls: 'roo-select2-search-choice-close fa fa-times',
15147 }, this.searchField);
15149 var close = choice.select('a.roo-select2-search-choice-close', true).first();
15151 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15159 this.inputEl().dom.value = '';
15164 onRemoveItem : function(e, _self, o)
15166 e.preventDefault();
15168 this.lastItem = Roo.apply([], this.item);
15170 var index = this.item.indexOf(o.data) * 1;
15173 Roo.log('not this item?!');
15177 this.item.splice(index, 1);
15182 this.fireEvent('remove', this, e);
15188 syncValue : function()
15190 if(!this.item.length){
15197 Roo.each(this.item, function(i){
15198 if(_this.valueField){
15199 value.push(i[_this.valueField]);
15206 this.value = value.join(',');
15208 if(this.hiddenField){
15209 this.hiddenField.dom.value = this.value;
15212 this.store.fireEvent("datachanged", this.store);
15217 clearItem : function()
15219 if(!this.multiple){
15225 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15233 if(this.tickable && !Roo.isTouch){
15234 this.view.refresh();
15238 inputEl: function ()
15240 if(Roo.isIOS && this.useNativeIOS){
15241 return this.el.select('select.roo-ios-select', true).first();
15244 if(Roo.isTouch && this.mobileTouchView){
15245 return this.el.select('input.form-control',true).first();
15249 return this.searchField;
15252 return this.el.select('input.form-control',true).first();
15255 onTickableFooterButtonClick : function(e, btn, el)
15257 e.preventDefault();
15259 this.lastItem = Roo.apply([], this.item);
15261 if(btn && btn.name == 'cancel'){
15262 this.tickItems = Roo.apply([], this.item);
15271 Roo.each(this.tickItems, function(o){
15279 validate : function()
15281 if(this.getVisibilityEl().hasClass('hidden')){
15285 var v = this.getRawValue();
15288 v = this.getValue();
15291 if(this.disabled || this.allowBlank || v.length){
15296 this.markInvalid();
15300 tickableInputEl : function()
15302 if(!this.tickable || !this.editable){
15303 return this.inputEl();
15306 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15310 getAutoCreateTouchView : function()
15315 cls: 'form-group' //input-group
15321 type : this.inputType,
15322 cls : 'form-control x-combo-noedit',
15323 autocomplete: 'new-password',
15324 placeholder : this.placeholder || '',
15329 input.name = this.name;
15333 input.cls += ' input-' + this.size;
15336 if (this.disabled) {
15337 input.disabled = true;
15348 inputblock.cls += ' input-group';
15350 inputblock.cn.unshift({
15352 cls : 'input-group-addon input-group-prepend input-group-text',
15357 if(this.removable && !this.multiple){
15358 inputblock.cls += ' roo-removable';
15360 inputblock.cn.push({
15363 cls : 'roo-combo-removable-btn close'
15367 if(this.hasFeedback && !this.allowBlank){
15369 inputblock.cls += ' has-feedback';
15371 inputblock.cn.push({
15373 cls: 'glyphicon form-control-feedback'
15380 inputblock.cls += (this.before) ? '' : ' input-group';
15382 inputblock.cn.push({
15384 cls : 'input-group-addon input-group-append input-group-text',
15390 var ibwrap = inputblock;
15395 cls: 'roo-select2-choices',
15399 cls: 'roo-select2-search-field',
15412 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15417 cls: 'form-hidden-field'
15423 if(!this.multiple && this.showToggleBtn){
15429 if (this.caret != false) {
15432 cls: 'fa fa-' + this.caret
15439 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15441 Roo.bootstrap.version == 3 ? caret : '',
15444 cls: 'combobox-clear',
15458 combobox.cls += ' roo-select2-container-multi';
15461 var align = this.labelAlign || this.parentLabelAlign();
15463 if (align ==='left' && this.fieldLabel.length) {
15468 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15469 tooltip : 'This field is required'
15473 cls : 'control-label col-form-label',
15474 html : this.fieldLabel
15485 var labelCfg = cfg.cn[1];
15486 var contentCfg = cfg.cn[2];
15489 if(this.indicatorpos == 'right'){
15494 cls : 'control-label col-form-label',
15498 html : this.fieldLabel
15502 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15503 tooltip : 'This field is required'
15516 labelCfg = cfg.cn[0];
15517 contentCfg = cfg.cn[1];
15522 if(this.labelWidth > 12){
15523 labelCfg.style = "width: " + this.labelWidth + 'px';
15526 if(this.labelWidth < 13 && this.labelmd == 0){
15527 this.labelmd = this.labelWidth;
15530 if(this.labellg > 0){
15531 labelCfg.cls += ' col-lg-' + this.labellg;
15532 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15535 if(this.labelmd > 0){
15536 labelCfg.cls += ' col-md-' + this.labelmd;
15537 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15540 if(this.labelsm > 0){
15541 labelCfg.cls += ' col-sm-' + this.labelsm;
15542 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15545 if(this.labelxs > 0){
15546 labelCfg.cls += ' col-xs-' + this.labelxs;
15547 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15551 } else if ( this.fieldLabel.length) {
15555 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15556 tooltip : 'This field is required'
15560 cls : 'control-label',
15561 html : this.fieldLabel
15572 if(this.indicatorpos == 'right'){
15576 cls : 'control-label',
15577 html : this.fieldLabel,
15581 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15582 tooltip : 'This field is required'
15599 var settings = this;
15601 ['xs','sm','md','lg'].map(function(size){
15602 if (settings[size]) {
15603 cfg.cls += ' col-' + size + '-' + settings[size];
15610 initTouchView : function()
15612 this.renderTouchView();
15614 this.touchViewEl.on('scroll', function(){
15615 this.el.dom.scrollTop = 0;
15618 this.originalValue = this.getValue();
15620 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15622 this.inputEl().on("click", this.showTouchView, this);
15623 if (this.triggerEl) {
15624 this.triggerEl.on("click", this.showTouchView, this);
15628 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15629 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15631 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15633 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15634 this.store.on('load', this.onTouchViewLoad, this);
15635 this.store.on('loadexception', this.onTouchViewLoadException, this);
15637 if(this.hiddenName){
15639 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15641 this.hiddenField.dom.value =
15642 this.hiddenValue !== undefined ? this.hiddenValue :
15643 this.value !== undefined ? this.value : '';
15645 this.el.dom.removeAttribute('name');
15646 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15650 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15651 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15654 if(this.removable && !this.multiple){
15655 var close = this.closeTriggerEl();
15657 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15658 close.on('click', this.removeBtnClick, this, close);
15662 * fix the bug in Safari iOS8
15664 this.inputEl().on("focus", function(e){
15665 document.activeElement.blur();
15668 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15675 renderTouchView : function()
15677 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15678 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15680 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15681 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15683 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15684 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15685 this.touchViewBodyEl.setStyle('overflow', 'auto');
15687 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15688 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15690 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15691 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15695 showTouchView : function()
15701 this.touchViewHeaderEl.hide();
15703 if(this.modalTitle.length){
15704 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15705 this.touchViewHeaderEl.show();
15708 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15709 this.touchViewEl.show();
15711 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15713 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15714 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15716 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15718 if(this.modalTitle.length){
15719 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15722 this.touchViewBodyEl.setHeight(bodyHeight);
15726 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15728 this.touchViewEl.addClass('in');
15731 if(this._touchViewMask){
15732 Roo.get(document.body).addClass("x-body-masked");
15733 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15734 this._touchViewMask.setStyle('z-index', 10000);
15735 this._touchViewMask.addClass('show');
15738 this.doTouchViewQuery();
15742 hideTouchView : function()
15744 this.touchViewEl.removeClass('in');
15748 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15750 this.touchViewEl.setStyle('display', 'none');
15753 if(this._touchViewMask){
15754 this._touchViewMask.removeClass('show');
15755 Roo.get(document.body).removeClass("x-body-masked");
15759 setTouchViewValue : function()
15766 Roo.each(this.tickItems, function(o){
15771 this.hideTouchView();
15774 doTouchViewQuery : function()
15783 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15787 if(!this.alwaysQuery || this.mode == 'local'){
15788 this.onTouchViewLoad();
15795 onTouchViewBeforeLoad : function(combo,opts)
15801 onTouchViewLoad : function()
15803 if(this.store.getCount() < 1){
15804 this.onTouchViewEmptyResults();
15808 this.clearTouchView();
15810 var rawValue = this.getRawValue();
15812 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15814 this.tickItems = [];
15816 this.store.data.each(function(d, rowIndex){
15817 var row = this.touchViewListGroup.createChild(template);
15819 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15820 row.addClass(d.data.cls);
15823 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15826 html : d.data[this.displayField]
15829 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15830 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15833 row.removeClass('selected');
15834 if(!this.multiple && this.valueField &&
15835 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15838 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15839 row.addClass('selected');
15842 if(this.multiple && this.valueField &&
15843 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15847 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15848 this.tickItems.push(d.data);
15851 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15855 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15857 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15859 if(this.modalTitle.length){
15860 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15863 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15865 if(this.mobile_restrict_height && listHeight < bodyHeight){
15866 this.touchViewBodyEl.setHeight(listHeight);
15871 if(firstChecked && listHeight > bodyHeight){
15872 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15877 onTouchViewLoadException : function()
15879 this.hideTouchView();
15882 onTouchViewEmptyResults : function()
15884 this.clearTouchView();
15886 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15888 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15892 clearTouchView : function()
15894 this.touchViewListGroup.dom.innerHTML = '';
15897 onTouchViewClick : function(e, el, o)
15899 e.preventDefault();
15902 var rowIndex = o.rowIndex;
15904 var r = this.store.getAt(rowIndex);
15906 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15908 if(!this.multiple){
15909 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15910 c.dom.removeAttribute('checked');
15913 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15915 this.setFromData(r.data);
15917 var close = this.closeTriggerEl();
15923 this.hideTouchView();
15925 this.fireEvent('select', this, r, rowIndex);
15930 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15931 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15932 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15936 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15937 this.addItem(r.data);
15938 this.tickItems.push(r.data);
15942 getAutoCreateNativeIOS : function()
15945 cls: 'form-group' //input-group,
15950 cls : 'roo-ios-select'
15954 combobox.name = this.name;
15957 if (this.disabled) {
15958 combobox.disabled = true;
15961 var settings = this;
15963 ['xs','sm','md','lg'].map(function(size){
15964 if (settings[size]) {
15965 cfg.cls += ' col-' + size + '-' + settings[size];
15975 initIOSView : function()
15977 this.store.on('load', this.onIOSViewLoad, this);
15982 onIOSViewLoad : function()
15984 if(this.store.getCount() < 1){
15988 this.clearIOSView();
15990 if(this.allowBlank) {
15992 var default_text = '-- SELECT --';
15994 if(this.placeholder.length){
15995 default_text = this.placeholder;
15998 if(this.emptyTitle.length){
15999 default_text += ' - ' + this.emptyTitle + ' -';
16002 var opt = this.inputEl().createChild({
16005 html : default_text
16009 o[this.valueField] = 0;
16010 o[this.displayField] = default_text;
16012 this.ios_options.push({
16019 this.store.data.each(function(d, rowIndex){
16023 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16024 html = d.data[this.displayField];
16029 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16030 value = d.data[this.valueField];
16039 if(this.value == d.data[this.valueField]){
16040 option['selected'] = true;
16043 var opt = this.inputEl().createChild(option);
16045 this.ios_options.push({
16052 this.inputEl().on('change', function(){
16053 this.fireEvent('select', this);
16058 clearIOSView: function()
16060 this.inputEl().dom.innerHTML = '';
16062 this.ios_options = [];
16065 setIOSValue: function(v)
16069 if(!this.ios_options){
16073 Roo.each(this.ios_options, function(opts){
16075 opts.el.dom.removeAttribute('selected');
16077 if(opts.data[this.valueField] != v){
16081 opts.el.dom.setAttribute('selected', true);
16087 * @cfg {Boolean} grow
16091 * @cfg {Number} growMin
16095 * @cfg {Number} growMax
16104 Roo.apply(Roo.bootstrap.ComboBox, {
16108 cls: 'modal-header',
16130 cls: 'list-group-item',
16134 cls: 'roo-combobox-list-group-item-value'
16138 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16152 listItemCheckbox : {
16154 cls: 'list-group-item',
16158 cls: 'roo-combobox-list-group-item-value'
16162 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16178 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16183 cls: 'modal-footer',
16191 cls: 'col-xs-6 text-left',
16194 cls: 'btn btn-danger roo-touch-view-cancel',
16200 cls: 'col-xs-6 text-right',
16203 cls: 'btn btn-success roo-touch-view-ok',
16214 Roo.apply(Roo.bootstrap.ComboBox, {
16216 touchViewTemplate : {
16218 cls: 'modal fade roo-combobox-touch-view',
16222 cls: 'modal-dialog',
16223 style : 'position:fixed', // we have to fix position....
16227 cls: 'modal-content',
16229 Roo.bootstrap.ComboBox.header,
16230 Roo.bootstrap.ComboBox.body,
16231 Roo.bootstrap.ComboBox.footer
16240 * Ext JS Library 1.1.1
16241 * Copyright(c) 2006-2007, Ext JS, LLC.
16243 * Originally Released Under LGPL - original licence link has changed is not relivant.
16246 * <script type="text/javascript">
16251 * @extends Roo.util.Observable
16252 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
16253 * This class also supports single and multi selection modes. <br>
16254 * Create a data model bound view:
16256 var store = new Roo.data.Store(...);
16258 var view = new Roo.View({
16260 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
16262 singleSelect: true,
16263 selectedClass: "ydataview-selected",
16267 // listen for node click?
16268 view.on("click", function(vw, index, node, e){
16269 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16273 dataModel.load("foobar.xml");
16275 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16277 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16278 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16280 * Note: old style constructor is still suported (container, template, config)
16283 * Create a new View
16284 * @param {Object} config The config object
16287 Roo.View = function(config, depreciated_tpl, depreciated_config){
16289 this.parent = false;
16291 if (typeof(depreciated_tpl) == 'undefined') {
16292 // new way.. - universal constructor.
16293 Roo.apply(this, config);
16294 this.el = Roo.get(this.el);
16297 this.el = Roo.get(config);
16298 this.tpl = depreciated_tpl;
16299 Roo.apply(this, depreciated_config);
16301 this.wrapEl = this.el.wrap().wrap();
16302 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16305 if(typeof(this.tpl) == "string"){
16306 this.tpl = new Roo.Template(this.tpl);
16308 // support xtype ctors..
16309 this.tpl = new Roo.factory(this.tpl, Roo);
16313 this.tpl.compile();
16318 * @event beforeclick
16319 * Fires before a click is processed. Returns false to cancel the default action.
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
16325 "beforeclick" : true,
16328 * Fires when a template node is 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
16337 * Fires when a template node is double clicked.
16338 * @param {Roo.View} this
16339 * @param {Number} index The index of the target node
16340 * @param {HTMLElement} node The target node
16341 * @param {Roo.EventObject} e The raw event object
16345 * @event contextmenu
16346 * Fires when a template node is right clicked.
16347 * @param {Roo.View} this
16348 * @param {Number} index The index of the target node
16349 * @param {HTMLElement} node The target node
16350 * @param {Roo.EventObject} e The raw event object
16352 "contextmenu" : true,
16354 * @event selectionchange
16355 * Fires when the selected nodes change.
16356 * @param {Roo.View} this
16357 * @param {Array} selections Array of the selected nodes
16359 "selectionchange" : true,
16362 * @event beforeselect
16363 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16364 * @param {Roo.View} this
16365 * @param {HTMLElement} node The node to be selected
16366 * @param {Array} selections Array of currently selected nodes
16368 "beforeselect" : true,
16370 * @event preparedata
16371 * Fires on every row to render, to allow you to change the data.
16372 * @param {Roo.View} this
16373 * @param {Object} data to be rendered (change this)
16375 "preparedata" : true
16383 "click": this.onClick,
16384 "dblclick": this.onDblClick,
16385 "contextmenu": this.onContextMenu,
16389 this.selections = [];
16391 this.cmp = new Roo.CompositeElementLite([]);
16393 this.store = Roo.factory(this.store, Roo.data);
16394 this.setStore(this.store, true);
16397 if ( this.footer && this.footer.xtype) {
16399 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16401 this.footer.dataSource = this.store;
16402 this.footer.container = fctr;
16403 this.footer = Roo.factory(this.footer, Roo);
16404 fctr.insertFirst(this.el);
16406 // this is a bit insane - as the paging toolbar seems to detach the el..
16407 // dom.parentNode.parentNode.parentNode
16408 // they get detached?
16412 Roo.View.superclass.constructor.call(this);
16417 Roo.extend(Roo.View, Roo.util.Observable, {
16420 * @cfg {Roo.data.Store} store Data store to load data from.
16425 * @cfg {String|Roo.Element} el The container element.
16430 * @cfg {String|Roo.Template} tpl The template used by this View
16434 * @cfg {String} dataName the named area of the template to use as the data area
16435 * Works with domtemplates roo-name="name"
16439 * @cfg {String} selectedClass The css class to add to selected nodes
16441 selectedClass : "x-view-selected",
16443 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16448 * @cfg {String} text to display on mask (default Loading)
16452 * @cfg {Boolean} multiSelect Allow multiple selection
16454 multiSelect : false,
16456 * @cfg {Boolean} singleSelect Allow single selection
16458 singleSelect: false,
16461 * @cfg {Boolean} toggleSelect - selecting
16463 toggleSelect : false,
16466 * @cfg {Boolean} tickable - selecting
16471 * Returns the element this view is bound to.
16472 * @return {Roo.Element}
16474 getEl : function(){
16475 return this.wrapEl;
16481 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16483 refresh : function(){
16484 //Roo.log('refresh');
16487 // if we are using something like 'domtemplate', then
16488 // the what gets used is:
16489 // t.applySubtemplate(NAME, data, wrapping data..)
16490 // the outer template then get' applied with
16491 // the store 'extra data'
16492 // and the body get's added to the
16493 // roo-name="data" node?
16494 // <span class='roo-tpl-{name}'></span> ?????
16498 this.clearSelections();
16499 this.el.update("");
16501 var records = this.store.getRange();
16502 if(records.length < 1) {
16504 // is this valid?? = should it render a template??
16506 this.el.update(this.emptyText);
16510 if (this.dataName) {
16511 this.el.update(t.apply(this.store.meta)); //????
16512 el = this.el.child('.roo-tpl-' + this.dataName);
16515 for(var i = 0, len = records.length; i < len; i++){
16516 var data = this.prepareData(records[i].data, i, records[i]);
16517 this.fireEvent("preparedata", this, data, i, records[i]);
16519 var d = Roo.apply({}, data);
16522 Roo.apply(d, {'roo-id' : Roo.id()});
16526 Roo.each(this.parent.item, function(item){
16527 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16530 Roo.apply(d, {'roo-data-checked' : 'checked'});
16534 html[html.length] = Roo.util.Format.trim(
16536 t.applySubtemplate(this.dataName, d, this.store.meta) :
16543 el.update(html.join(""));
16544 this.nodes = el.dom.childNodes;
16545 this.updateIndexes(0);
16550 * Function to override to reformat the data that is sent to
16551 * the template for each node.
16552 * DEPRICATED - use the preparedata event handler.
16553 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16554 * a JSON object for an UpdateManager bound view).
16556 prepareData : function(data, index, record)
16558 this.fireEvent("preparedata", this, data, index, record);
16562 onUpdate : function(ds, record){
16563 // Roo.log('on update');
16564 this.clearSelections();
16565 var index = this.store.indexOf(record);
16566 var n = this.nodes[index];
16567 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16568 n.parentNode.removeChild(n);
16569 this.updateIndexes(index, index);
16575 onAdd : function(ds, records, index)
16577 //Roo.log(['on Add', ds, records, index] );
16578 this.clearSelections();
16579 if(this.nodes.length == 0){
16583 var n = this.nodes[index];
16584 for(var i = 0, len = records.length; i < len; i++){
16585 var d = this.prepareData(records[i].data, i, records[i]);
16587 this.tpl.insertBefore(n, d);
16590 this.tpl.append(this.el, d);
16593 this.updateIndexes(index);
16596 onRemove : function(ds, record, index){
16597 // Roo.log('onRemove');
16598 this.clearSelections();
16599 var el = this.dataName ?
16600 this.el.child('.roo-tpl-' + this.dataName) :
16603 el.dom.removeChild(this.nodes[index]);
16604 this.updateIndexes(index);
16608 * Refresh an individual node.
16609 * @param {Number} index
16611 refreshNode : function(index){
16612 this.onUpdate(this.store, this.store.getAt(index));
16615 updateIndexes : function(startIndex, endIndex){
16616 var ns = this.nodes;
16617 startIndex = startIndex || 0;
16618 endIndex = endIndex || ns.length - 1;
16619 for(var i = startIndex; i <= endIndex; i++){
16620 ns[i].nodeIndex = i;
16625 * Changes the data store this view uses and refresh the view.
16626 * @param {Store} store
16628 setStore : function(store, initial){
16629 if(!initial && this.store){
16630 this.store.un("datachanged", this.refresh);
16631 this.store.un("add", this.onAdd);
16632 this.store.un("remove", this.onRemove);
16633 this.store.un("update", this.onUpdate);
16634 this.store.un("clear", this.refresh);
16635 this.store.un("beforeload", this.onBeforeLoad);
16636 this.store.un("load", this.onLoad);
16637 this.store.un("loadexception", this.onLoad);
16641 store.on("datachanged", this.refresh, this);
16642 store.on("add", this.onAdd, this);
16643 store.on("remove", this.onRemove, this);
16644 store.on("update", this.onUpdate, this);
16645 store.on("clear", this.refresh, this);
16646 store.on("beforeload", this.onBeforeLoad, this);
16647 store.on("load", this.onLoad, this);
16648 store.on("loadexception", this.onLoad, this);
16656 * onbeforeLoad - masks the loading area.
16659 onBeforeLoad : function(store,opts)
16661 //Roo.log('onBeforeLoad');
16663 this.el.update("");
16665 this.el.mask(this.mask ? this.mask : "Loading" );
16667 onLoad : function ()
16674 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16675 * @param {HTMLElement} node
16676 * @return {HTMLElement} The template node
16678 findItemFromChild : function(node){
16679 var el = this.dataName ?
16680 this.el.child('.roo-tpl-' + this.dataName,true) :
16683 if(!node || node.parentNode == el){
16686 var p = node.parentNode;
16687 while(p && p != el){
16688 if(p.parentNode == el){
16697 onClick : function(e){
16698 var item = this.findItemFromChild(e.getTarget());
16700 var index = this.indexOf(item);
16701 if(this.onItemClick(item, index, e) !== false){
16702 this.fireEvent("click", this, index, item, e);
16705 this.clearSelections();
16710 onContextMenu : function(e){
16711 var item = this.findItemFromChild(e.getTarget());
16713 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16718 onDblClick : function(e){
16719 var item = this.findItemFromChild(e.getTarget());
16721 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16725 onItemClick : function(item, index, e)
16727 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16730 if (this.toggleSelect) {
16731 var m = this.isSelected(item) ? 'unselect' : 'select';
16734 _t[m](item, true, false);
16737 if(this.multiSelect || this.singleSelect){
16738 if(this.multiSelect && e.shiftKey && this.lastSelection){
16739 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16741 this.select(item, this.multiSelect && e.ctrlKey);
16742 this.lastSelection = item;
16745 if(!this.tickable){
16746 e.preventDefault();
16754 * Get the number of selected nodes.
16757 getSelectionCount : function(){
16758 return this.selections.length;
16762 * Get the currently selected nodes.
16763 * @return {Array} An array of HTMLElements
16765 getSelectedNodes : function(){
16766 return this.selections;
16770 * Get the indexes of the selected nodes.
16773 getSelectedIndexes : function(){
16774 var indexes = [], s = this.selections;
16775 for(var i = 0, len = s.length; i < len; i++){
16776 indexes.push(s[i].nodeIndex);
16782 * Clear all selections
16783 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16785 clearSelections : function(suppressEvent){
16786 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16787 this.cmp.elements = this.selections;
16788 this.cmp.removeClass(this.selectedClass);
16789 this.selections = [];
16790 if(!suppressEvent){
16791 this.fireEvent("selectionchange", this, this.selections);
16797 * Returns true if the passed node is selected
16798 * @param {HTMLElement/Number} node The node or node index
16799 * @return {Boolean}
16801 isSelected : function(node){
16802 var s = this.selections;
16806 node = this.getNode(node);
16807 return s.indexOf(node) !== -1;
16812 * @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
16813 * @param {Boolean} keepExisting (optional) true to keep existing selections
16814 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16816 select : function(nodeInfo, keepExisting, suppressEvent){
16817 if(nodeInfo instanceof Array){
16819 this.clearSelections(true);
16821 for(var i = 0, len = nodeInfo.length; i < len; i++){
16822 this.select(nodeInfo[i], true, true);
16826 var node = this.getNode(nodeInfo);
16827 if(!node || this.isSelected(node)){
16828 return; // already selected.
16831 this.clearSelections(true);
16834 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16835 Roo.fly(node).addClass(this.selectedClass);
16836 this.selections.push(node);
16837 if(!suppressEvent){
16838 this.fireEvent("selectionchange", this, this.selections);
16846 * @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
16847 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16848 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16850 unselect : function(nodeInfo, keepExisting, suppressEvent)
16852 if(nodeInfo instanceof Array){
16853 Roo.each(this.selections, function(s) {
16854 this.unselect(s, nodeInfo);
16858 var node = this.getNode(nodeInfo);
16859 if(!node || !this.isSelected(node)){
16860 //Roo.log("not selected");
16861 return; // not selected.
16865 Roo.each(this.selections, function(s) {
16867 Roo.fly(node).removeClass(this.selectedClass);
16874 this.selections= ns;
16875 this.fireEvent("selectionchange", this, this.selections);
16879 * Gets a template node.
16880 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16881 * @return {HTMLElement} The node or null if it wasn't found
16883 getNode : function(nodeInfo){
16884 if(typeof nodeInfo == "string"){
16885 return document.getElementById(nodeInfo);
16886 }else if(typeof nodeInfo == "number"){
16887 return this.nodes[nodeInfo];
16893 * Gets a range template nodes.
16894 * @param {Number} startIndex
16895 * @param {Number} endIndex
16896 * @return {Array} An array of nodes
16898 getNodes : function(start, end){
16899 var ns = this.nodes;
16900 start = start || 0;
16901 end = typeof end == "undefined" ? ns.length - 1 : end;
16904 for(var i = start; i <= end; i++){
16908 for(var i = start; i >= end; i--){
16916 * Finds the index of the passed node
16917 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16918 * @return {Number} The index of the node or -1
16920 indexOf : function(node){
16921 node = this.getNode(node);
16922 if(typeof node.nodeIndex == "number"){
16923 return node.nodeIndex;
16925 var ns = this.nodes;
16926 for(var i = 0, len = ns.length; i < len; i++){
16937 * based on jquery fullcalendar
16941 Roo.bootstrap = Roo.bootstrap || {};
16943 * @class Roo.bootstrap.Calendar
16944 * @extends Roo.bootstrap.Component
16945 * Bootstrap Calendar class
16946 * @cfg {Boolean} loadMask (true|false) default false
16947 * @cfg {Object} header generate the user specific header of the calendar, default false
16950 * Create a new Container
16951 * @param {Object} config The config object
16956 Roo.bootstrap.Calendar = function(config){
16957 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16961 * Fires when a date is selected
16962 * @param {DatePicker} this
16963 * @param {Date} date The selected date
16967 * @event monthchange
16968 * Fires when the displayed month changes
16969 * @param {DatePicker} this
16970 * @param {Date} date The selected month
16972 'monthchange': true,
16974 * @event evententer
16975 * Fires when mouse over an event
16976 * @param {Calendar} this
16977 * @param {event} Event
16979 'evententer': true,
16981 * @event eventleave
16982 * Fires when the mouse leaves an
16983 * @param {Calendar} this
16986 'eventleave': true,
16988 * @event eventclick
16989 * Fires when the mouse click an
16990 * @param {Calendar} this
16999 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
17002 * @cfg {Number} startDay
17003 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
17011 getAutoCreate : function(){
17014 var fc_button = function(name, corner, style, content ) {
17015 return Roo.apply({},{
17017 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
17019 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
17022 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
17033 style : 'width:100%',
17040 cls : 'fc-header-left',
17042 fc_button('prev', 'left', 'arrow', '‹' ),
17043 fc_button('next', 'right', 'arrow', '›' ),
17044 { tag: 'span', cls: 'fc-header-space' },
17045 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
17053 cls : 'fc-header-center',
17057 cls: 'fc-header-title',
17060 html : 'month / year'
17068 cls : 'fc-header-right',
17070 /* fc_button('month', 'left', '', 'month' ),
17071 fc_button('week', '', '', 'week' ),
17072 fc_button('day', 'right', '', 'day' )
17084 header = this.header;
17087 var cal_heads = function() {
17089 // fixme - handle this.
17091 for (var i =0; i < Date.dayNames.length; i++) {
17092 var d = Date.dayNames[i];
17095 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17096 html : d.substring(0,3)
17100 ret[0].cls += ' fc-first';
17101 ret[6].cls += ' fc-last';
17104 var cal_cell = function(n) {
17107 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17112 cls: 'fc-day-number',
17116 cls: 'fc-day-content',
17120 style: 'position: relative;' // height: 17px;
17132 var cal_rows = function() {
17135 for (var r = 0; r < 6; r++) {
17142 for (var i =0; i < Date.dayNames.length; i++) {
17143 var d = Date.dayNames[i];
17144 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17147 row.cn[0].cls+=' fc-first';
17148 row.cn[0].cn[0].style = 'min-height:90px';
17149 row.cn[6].cls+=' fc-last';
17153 ret[0].cls += ' fc-first';
17154 ret[4].cls += ' fc-prev-last';
17155 ret[5].cls += ' fc-last';
17162 cls: 'fc-border-separate',
17163 style : 'width:100%',
17171 cls : 'fc-first fc-last',
17189 cls : 'fc-content',
17190 style : "position: relative;",
17193 cls : 'fc-view fc-view-month fc-grid',
17194 style : 'position: relative',
17195 unselectable : 'on',
17198 cls : 'fc-event-container',
17199 style : 'position:absolute;z-index:8;top:0;left:0;'
17217 initEvents : function()
17220 throw "can not find store for calendar";
17226 style: "text-align:center",
17230 style: "background-color:white;width:50%;margin:250 auto",
17234 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
17245 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17247 var size = this.el.select('.fc-content', true).first().getSize();
17248 this.maskEl.setSize(size.width, size.height);
17249 this.maskEl.enableDisplayMode("block");
17250 if(!this.loadMask){
17251 this.maskEl.hide();
17254 this.store = Roo.factory(this.store, Roo.data);
17255 this.store.on('load', this.onLoad, this);
17256 this.store.on('beforeload', this.onBeforeLoad, this);
17260 this.cells = this.el.select('.fc-day',true);
17261 //Roo.log(this.cells);
17262 this.textNodes = this.el.query('.fc-day-number');
17263 this.cells.addClassOnOver('fc-state-hover');
17265 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17266 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17267 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17268 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17270 this.on('monthchange', this.onMonthChange, this);
17272 this.update(new Date().clearTime());
17275 resize : function() {
17276 var sz = this.el.getSize();
17278 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17279 this.el.select('.fc-day-content div',true).setHeight(34);
17284 showPrevMonth : function(e){
17285 this.update(this.activeDate.add("mo", -1));
17287 showToday : function(e){
17288 this.update(new Date().clearTime());
17291 showNextMonth : function(e){
17292 this.update(this.activeDate.add("mo", 1));
17296 showPrevYear : function(){
17297 this.update(this.activeDate.add("y", -1));
17301 showNextYear : function(){
17302 this.update(this.activeDate.add("y", 1));
17307 update : function(date)
17309 var vd = this.activeDate;
17310 this.activeDate = date;
17311 // if(vd && this.el){
17312 // var t = date.getTime();
17313 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17314 // Roo.log('using add remove');
17316 // this.fireEvent('monthchange', this, date);
17318 // this.cells.removeClass("fc-state-highlight");
17319 // this.cells.each(function(c){
17320 // if(c.dateValue == t){
17321 // c.addClass("fc-state-highlight");
17322 // setTimeout(function(){
17323 // try{c.dom.firstChild.focus();}catch(e){}
17333 var days = date.getDaysInMonth();
17335 var firstOfMonth = date.getFirstDateOfMonth();
17336 var startingPos = firstOfMonth.getDay()-this.startDay;
17338 if(startingPos < this.startDay){
17342 var pm = date.add(Date.MONTH, -1);
17343 var prevStart = pm.getDaysInMonth()-startingPos;
17345 this.cells = this.el.select('.fc-day',true);
17346 this.textNodes = this.el.query('.fc-day-number');
17347 this.cells.addClassOnOver('fc-state-hover');
17349 var cells = this.cells.elements;
17350 var textEls = this.textNodes;
17352 Roo.each(cells, function(cell){
17353 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17356 days += startingPos;
17358 // convert everything to numbers so it's fast
17359 var day = 86400000;
17360 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17363 //Roo.log(prevStart);
17365 var today = new Date().clearTime().getTime();
17366 var sel = date.clearTime().getTime();
17367 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17368 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17369 var ddMatch = this.disabledDatesRE;
17370 var ddText = this.disabledDatesText;
17371 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17372 var ddaysText = this.disabledDaysText;
17373 var format = this.format;
17375 var setCellClass = function(cal, cell){
17379 //Roo.log('set Cell Class');
17381 var t = d.getTime();
17385 cell.dateValue = t;
17387 cell.className += " fc-today";
17388 cell.className += " fc-state-highlight";
17389 cell.title = cal.todayText;
17392 // disable highlight in other month..
17393 //cell.className += " fc-state-highlight";
17398 cell.className = " fc-state-disabled";
17399 cell.title = cal.minText;
17403 cell.className = " fc-state-disabled";
17404 cell.title = cal.maxText;
17408 if(ddays.indexOf(d.getDay()) != -1){
17409 cell.title = ddaysText;
17410 cell.className = " fc-state-disabled";
17413 if(ddMatch && format){
17414 var fvalue = d.dateFormat(format);
17415 if(ddMatch.test(fvalue)){
17416 cell.title = ddText.replace("%0", fvalue);
17417 cell.className = " fc-state-disabled";
17421 if (!cell.initialClassName) {
17422 cell.initialClassName = cell.dom.className;
17425 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17430 for(; i < startingPos; i++) {
17431 textEls[i].innerHTML = (++prevStart);
17432 d.setDate(d.getDate()+1);
17434 cells[i].className = "fc-past fc-other-month";
17435 setCellClass(this, cells[i]);
17440 for(; i < days; i++){
17441 intDay = i - startingPos + 1;
17442 textEls[i].innerHTML = (intDay);
17443 d.setDate(d.getDate()+1);
17445 cells[i].className = ''; // "x-date-active";
17446 setCellClass(this, cells[i]);
17450 for(; i < 42; i++) {
17451 textEls[i].innerHTML = (++extraDays);
17452 d.setDate(d.getDate()+1);
17454 cells[i].className = "fc-future fc-other-month";
17455 setCellClass(this, cells[i]);
17458 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17460 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17462 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17463 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17465 if(totalRows != 6){
17466 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17467 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17470 this.fireEvent('monthchange', this, date);
17474 if(!this.internalRender){
17475 var main = this.el.dom.firstChild;
17476 var w = main.offsetWidth;
17477 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17478 Roo.fly(main).setWidth(w);
17479 this.internalRender = true;
17480 // opera does not respect the auto grow header center column
17481 // then, after it gets a width opera refuses to recalculate
17482 // without a second pass
17483 if(Roo.isOpera && !this.secondPass){
17484 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17485 this.secondPass = true;
17486 this.update.defer(10, this, [date]);
17493 findCell : function(dt) {
17494 dt = dt.clearTime().getTime();
17496 this.cells.each(function(c){
17497 //Roo.log("check " +c.dateValue + '?=' + dt);
17498 if(c.dateValue == dt){
17508 findCells : function(ev) {
17509 var s = ev.start.clone().clearTime().getTime();
17511 var e= ev.end.clone().clearTime().getTime();
17514 this.cells.each(function(c){
17515 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17517 if(c.dateValue > e){
17520 if(c.dateValue < s){
17529 // findBestRow: function(cells)
17533 // for (var i =0 ; i < cells.length;i++) {
17534 // ret = Math.max(cells[i].rows || 0,ret);
17541 addItem : function(ev)
17543 // look for vertical location slot in
17544 var cells = this.findCells(ev);
17546 // ev.row = this.findBestRow(cells);
17548 // work out the location.
17552 for(var i =0; i < cells.length; i++) {
17554 cells[i].row = cells[0].row;
17557 cells[i].row = cells[i].row + 1;
17567 if (crow.start.getY() == cells[i].getY()) {
17569 crow.end = cells[i];
17586 cells[0].events.push(ev);
17588 this.calevents.push(ev);
17591 clearEvents: function() {
17593 if(!this.calevents){
17597 Roo.each(this.cells.elements, function(c){
17603 Roo.each(this.calevents, function(e) {
17604 Roo.each(e.els, function(el) {
17605 el.un('mouseenter' ,this.onEventEnter, this);
17606 el.un('mouseleave' ,this.onEventLeave, this);
17611 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17617 renderEvents: function()
17621 this.cells.each(function(c) {
17630 if(c.row != c.events.length){
17631 r = 4 - (4 - (c.row - c.events.length));
17634 c.events = ev.slice(0, r);
17635 c.more = ev.slice(r);
17637 if(c.more.length && c.more.length == 1){
17638 c.events.push(c.more.pop());
17641 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17645 this.cells.each(function(c) {
17647 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17650 for (var e = 0; e < c.events.length; e++){
17651 var ev = c.events[e];
17652 var rows = ev.rows;
17654 for(var i = 0; i < rows.length; i++) {
17656 // how many rows should it span..
17659 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17660 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17662 unselectable : "on",
17665 cls: 'fc-event-inner',
17669 // cls: 'fc-event-time',
17670 // html : cells.length > 1 ? '' : ev.time
17674 cls: 'fc-event-title',
17675 html : String.format('{0}', ev.title)
17682 cls: 'ui-resizable-handle ui-resizable-e',
17683 html : '  '
17690 cfg.cls += ' fc-event-start';
17692 if ((i+1) == rows.length) {
17693 cfg.cls += ' fc-event-end';
17696 var ctr = _this.el.select('.fc-event-container',true).first();
17697 var cg = ctr.createChild(cfg);
17699 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17700 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17702 var r = (c.more.length) ? 1 : 0;
17703 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17704 cg.setWidth(ebox.right - sbox.x -2);
17706 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17707 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17708 cg.on('click', _this.onEventClick, _this, ev);
17719 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17720 style : 'position: absolute',
17721 unselectable : "on",
17724 cls: 'fc-event-inner',
17728 cls: 'fc-event-title',
17736 cls: 'ui-resizable-handle ui-resizable-e',
17737 html : '  '
17743 var ctr = _this.el.select('.fc-event-container',true).first();
17744 var cg = ctr.createChild(cfg);
17746 var sbox = c.select('.fc-day-content',true).first().getBox();
17747 var ebox = c.select('.fc-day-content',true).first().getBox();
17749 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17750 cg.setWidth(ebox.right - sbox.x -2);
17752 cg.on('click', _this.onMoreEventClick, _this, c.more);
17762 onEventEnter: function (e, el,event,d) {
17763 this.fireEvent('evententer', this, el, event);
17766 onEventLeave: function (e, el,event,d) {
17767 this.fireEvent('eventleave', this, el, event);
17770 onEventClick: function (e, el,event,d) {
17771 this.fireEvent('eventclick', this, el, event);
17774 onMonthChange: function () {
17778 onMoreEventClick: function(e, el, more)
17782 this.calpopover.placement = 'right';
17783 this.calpopover.setTitle('More');
17785 this.calpopover.setContent('');
17787 var ctr = this.calpopover.el.select('.popover-content', true).first();
17789 Roo.each(more, function(m){
17791 cls : 'fc-event-hori fc-event-draggable',
17794 var cg = ctr.createChild(cfg);
17796 cg.on('click', _this.onEventClick, _this, m);
17799 this.calpopover.show(el);
17804 onLoad: function ()
17806 this.calevents = [];
17809 if(this.store.getCount() > 0){
17810 this.store.data.each(function(d){
17813 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17814 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17815 time : d.data.start_time,
17816 title : d.data.title,
17817 description : d.data.description,
17818 venue : d.data.venue
17823 this.renderEvents();
17825 if(this.calevents.length && this.loadMask){
17826 this.maskEl.hide();
17830 onBeforeLoad: function()
17832 this.clearEvents();
17834 this.maskEl.show();
17848 * @class Roo.bootstrap.Popover
17849 * @extends Roo.bootstrap.Component
17850 * Bootstrap Popover class
17851 * @cfg {String} html contents of the popover (or false to use children..)
17852 * @cfg {String} title of popover (or false to hide)
17853 * @cfg {String} placement how it is placed
17854 * @cfg {String} trigger click || hover (or false to trigger manually)
17855 * @cfg {String} over what (parent or false to trigger manually.)
17856 * @cfg {Number} delay - delay before showing
17859 * Create a new Popover
17860 * @param {Object} config The config object
17863 Roo.bootstrap.Popover = function(config){
17864 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17870 * After the popover show
17872 * @param {Roo.bootstrap.Popover} this
17877 * After the popover hide
17879 * @param {Roo.bootstrap.Popover} this
17885 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17887 title: 'Fill in a title',
17890 placement : 'right',
17891 trigger : 'hover', // hover
17897 can_build_overlaid : false,
17899 getChildContainer : function()
17901 return this.el.select('.popover-content',true).first();
17904 getAutoCreate : function(){
17907 cls : 'popover roo-dynamic',
17908 style: 'display:block',
17914 cls : 'popover-inner',
17918 cls: 'popover-title popover-header',
17922 cls : 'popover-content popover-body',
17933 setTitle: function(str)
17936 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17938 setContent: function(str)
17941 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17943 // as it get's added to the bottom of the page.
17944 onRender : function(ct, position)
17946 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17948 var cfg = Roo.apply({}, this.getAutoCreate());
17952 cfg.cls += ' ' + this.cls;
17955 cfg.style = this.style;
17957 //Roo.log("adding to ");
17958 this.el = Roo.get(document.body).createChild(cfg, position);
17959 // Roo.log(this.el);
17964 initEvents : function()
17966 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17967 this.el.enableDisplayMode('block');
17969 if (this.over === false) {
17972 if (this.triggers === false) {
17975 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17976 var triggers = this.trigger ? this.trigger.split(' ') : [];
17977 Roo.each(triggers, function(trigger) {
17979 if (trigger == 'click') {
17980 on_el.on('click', this.toggle, this);
17981 } else if (trigger != 'manual') {
17982 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17983 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17985 on_el.on(eventIn ,this.enter, this);
17986 on_el.on(eventOut, this.leave, this);
17997 toggle : function () {
17998 this.hoverState == 'in' ? this.leave() : this.enter();
18001 enter : function () {
18003 clearTimeout(this.timeout);
18005 this.hoverState = 'in';
18007 if (!this.delay || !this.delay.show) {
18012 this.timeout = setTimeout(function () {
18013 if (_t.hoverState == 'in') {
18016 }, this.delay.show)
18019 leave : function() {
18020 clearTimeout(this.timeout);
18022 this.hoverState = 'out';
18024 if (!this.delay || !this.delay.hide) {
18029 this.timeout = setTimeout(function () {
18030 if (_t.hoverState == 'out') {
18033 }, this.delay.hide)
18036 show : function (on_el)
18039 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18043 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18044 if (this.html !== false) {
18045 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18047 this.el.removeClass([
18048 'fade','top','bottom', 'left', 'right','in',
18049 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18051 if (!this.title.length) {
18052 this.el.select('.popover-title',true).hide();
18055 var placement = typeof this.placement == 'function' ?
18056 this.placement.call(this, this.el, on_el) :
18059 var autoToken = /\s?auto?\s?/i;
18060 var autoPlace = autoToken.test(placement);
18062 placement = placement.replace(autoToken, '') || 'top';
18066 //this.el.setXY([0,0]);
18068 this.el.dom.style.display='block';
18069 this.el.addClass(placement);
18071 //this.el.appendTo(on_el);
18073 var p = this.getPosition();
18074 var box = this.el.getBox();
18079 var align = Roo.bootstrap.Popover.alignment[placement];
18082 this.el.alignTo(on_el, align[0],align[1]);
18083 //var arrow = this.el.select('.arrow',true).first();
18084 //arrow.set(align[2],
18086 this.el.addClass('in');
18089 if (this.el.hasClass('fade')) {
18093 this.hoverState = 'in';
18095 this.fireEvent('show', this);
18100 this.el.setXY([0,0]);
18101 this.el.removeClass('in');
18103 this.hoverState = null;
18105 this.fireEvent('hide', this);
18110 Roo.bootstrap.Popover.alignment = {
18111 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18112 'right' : ['l-r', [10,0], 'left bs-popover-left'],
18113 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18114 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18125 * @class Roo.bootstrap.Progress
18126 * @extends Roo.bootstrap.Component
18127 * Bootstrap Progress class
18128 * @cfg {Boolean} striped striped of the progress bar
18129 * @cfg {Boolean} active animated of the progress bar
18133 * Create a new Progress
18134 * @param {Object} config The config object
18137 Roo.bootstrap.Progress = function(config){
18138 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18141 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
18146 getAutoCreate : function(){
18154 cfg.cls += ' progress-striped';
18158 cfg.cls += ' active';
18177 * @class Roo.bootstrap.ProgressBar
18178 * @extends Roo.bootstrap.Component
18179 * Bootstrap ProgressBar class
18180 * @cfg {Number} aria_valuenow aria-value now
18181 * @cfg {Number} aria_valuemin aria-value min
18182 * @cfg {Number} aria_valuemax aria-value max
18183 * @cfg {String} label label for the progress bar
18184 * @cfg {String} panel (success | info | warning | danger )
18185 * @cfg {String} role role of the progress bar
18186 * @cfg {String} sr_only text
18190 * Create a new ProgressBar
18191 * @param {Object} config The config object
18194 Roo.bootstrap.ProgressBar = function(config){
18195 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18198 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
18202 aria_valuemax : 100,
18208 getAutoCreate : function()
18213 cls: 'progress-bar',
18214 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18226 cfg.role = this.role;
18229 if(this.aria_valuenow){
18230 cfg['aria-valuenow'] = this.aria_valuenow;
18233 if(this.aria_valuemin){
18234 cfg['aria-valuemin'] = this.aria_valuemin;
18237 if(this.aria_valuemax){
18238 cfg['aria-valuemax'] = this.aria_valuemax;
18241 if(this.label && !this.sr_only){
18242 cfg.html = this.label;
18246 cfg.cls += ' progress-bar-' + this.panel;
18252 update : function(aria_valuenow)
18254 this.aria_valuenow = aria_valuenow;
18256 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18271 * @class Roo.bootstrap.TabGroup
18272 * @extends Roo.bootstrap.Column
18273 * Bootstrap Column class
18274 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18275 * @cfg {Boolean} carousel true to make the group behave like a carousel
18276 * @cfg {Boolean} bullets show bullets for the panels
18277 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18278 * @cfg {Number} timer auto slide timer .. default 0 millisecond
18279 * @cfg {Boolean} showarrow (true|false) show arrow default true
18282 * Create a new TabGroup
18283 * @param {Object} config The config object
18286 Roo.bootstrap.TabGroup = function(config){
18287 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18289 this.navId = Roo.id();
18292 Roo.bootstrap.TabGroup.register(this);
18296 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18299 transition : false,
18304 slideOnTouch : false,
18307 getAutoCreate : function()
18309 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18311 cfg.cls += ' tab-content';
18313 if (this.carousel) {
18314 cfg.cls += ' carousel slide';
18317 cls : 'carousel-inner',
18321 if(this.bullets && !Roo.isTouch){
18324 cls : 'carousel-bullets',
18328 if(this.bullets_cls){
18329 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18336 cfg.cn[0].cn.push(bullets);
18339 if(this.showarrow){
18340 cfg.cn[0].cn.push({
18342 class : 'carousel-arrow',
18346 class : 'carousel-prev',
18350 class : 'fa fa-chevron-left'
18356 class : 'carousel-next',
18360 class : 'fa fa-chevron-right'
18373 initEvents: function()
18375 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18376 // this.el.on("touchstart", this.onTouchStart, this);
18379 if(this.autoslide){
18382 this.slideFn = window.setInterval(function() {
18383 _this.showPanelNext();
18387 if(this.showarrow){
18388 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18389 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18395 // onTouchStart : function(e, el, o)
18397 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18401 // this.showPanelNext();
18405 getChildContainer : function()
18407 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18411 * register a Navigation item
18412 * @param {Roo.bootstrap.NavItem} the navitem to add
18414 register : function(item)
18416 this.tabs.push( item);
18417 item.navId = this.navId; // not really needed..
18422 getActivePanel : function()
18425 Roo.each(this.tabs, function(t) {
18435 getPanelByName : function(n)
18438 Roo.each(this.tabs, function(t) {
18439 if (t.tabId == n) {
18447 indexOfPanel : function(p)
18450 Roo.each(this.tabs, function(t,i) {
18451 if (t.tabId == p.tabId) {
18460 * show a specific panel
18461 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18462 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18464 showPanel : function (pan)
18466 if(this.transition || typeof(pan) == 'undefined'){
18467 Roo.log("waiting for the transitionend");
18471 if (typeof(pan) == 'number') {
18472 pan = this.tabs[pan];
18475 if (typeof(pan) == 'string') {
18476 pan = this.getPanelByName(pan);
18479 var cur = this.getActivePanel();
18482 Roo.log('pan or acitve pan is undefined');
18486 if (pan.tabId == this.getActivePanel().tabId) {
18490 if (false === cur.fireEvent('beforedeactivate')) {
18494 if(this.bullets > 0 && !Roo.isTouch){
18495 this.setActiveBullet(this.indexOfPanel(pan));
18498 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18500 //class="carousel-item carousel-item-next carousel-item-left"
18502 this.transition = true;
18503 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18504 var lr = dir == 'next' ? 'left' : 'right';
18505 pan.el.addClass(dir); // or prev
18506 pan.el.addClass('carousel-item-' + dir); // or prev
18507 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18508 cur.el.addClass(lr); // or right
18509 pan.el.addClass(lr);
18510 cur.el.addClass('carousel-item-' +lr); // or right
18511 pan.el.addClass('carousel-item-' +lr);
18515 cur.el.on('transitionend', function() {
18516 Roo.log("trans end?");
18518 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18519 pan.setActive(true);
18521 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18522 cur.setActive(false);
18524 _this.transition = false;
18526 }, this, { single: true } );
18531 cur.setActive(false);
18532 pan.setActive(true);
18537 showPanelNext : function()
18539 var i = this.indexOfPanel(this.getActivePanel());
18541 if (i >= this.tabs.length - 1 && !this.autoslide) {
18545 if (i >= this.tabs.length - 1 && this.autoslide) {
18549 this.showPanel(this.tabs[i+1]);
18552 showPanelPrev : function()
18554 var i = this.indexOfPanel(this.getActivePanel());
18556 if (i < 1 && !this.autoslide) {
18560 if (i < 1 && this.autoslide) {
18561 i = this.tabs.length;
18564 this.showPanel(this.tabs[i-1]);
18568 addBullet: function()
18570 if(!this.bullets || Roo.isTouch){
18573 var ctr = this.el.select('.carousel-bullets',true).first();
18574 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18575 var bullet = ctr.createChild({
18576 cls : 'bullet bullet-' + i
18577 },ctr.dom.lastChild);
18582 bullet.on('click', (function(e, el, o, ii, t){
18584 e.preventDefault();
18586 this.showPanel(ii);
18588 if(this.autoslide && this.slideFn){
18589 clearInterval(this.slideFn);
18590 this.slideFn = window.setInterval(function() {
18591 _this.showPanelNext();
18595 }).createDelegate(this, [i, bullet], true));
18600 setActiveBullet : function(i)
18606 Roo.each(this.el.select('.bullet', true).elements, function(el){
18607 el.removeClass('selected');
18610 var bullet = this.el.select('.bullet-' + i, true).first();
18616 bullet.addClass('selected');
18627 Roo.apply(Roo.bootstrap.TabGroup, {
18631 * register a Navigation Group
18632 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18634 register : function(navgrp)
18636 this.groups[navgrp.navId] = navgrp;
18640 * fetch a Navigation Group based on the navigation ID
18641 * if one does not exist , it will get created.
18642 * @param {string} the navgroup to add
18643 * @returns {Roo.bootstrap.NavGroup} the navgroup
18645 get: function(navId) {
18646 if (typeof(this.groups[navId]) == 'undefined') {
18647 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18649 return this.groups[navId] ;
18664 * @class Roo.bootstrap.TabPanel
18665 * @extends Roo.bootstrap.Component
18666 * Bootstrap TabPanel class
18667 * @cfg {Boolean} active panel active
18668 * @cfg {String} html panel content
18669 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18670 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18671 * @cfg {String} href click to link..
18675 * Create a new TabPanel
18676 * @param {Object} config The config object
18679 Roo.bootstrap.TabPanel = function(config){
18680 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18684 * Fires when the active status changes
18685 * @param {Roo.bootstrap.TabPanel} this
18686 * @param {Boolean} state the new state
18691 * @event beforedeactivate
18692 * Fires before a tab is de-activated - can be used to do validation on a form.
18693 * @param {Roo.bootstrap.TabPanel} this
18694 * @return {Boolean} false if there is an error
18697 'beforedeactivate': true
18700 this.tabId = this.tabId || Roo.id();
18704 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18712 getAutoCreate : function(){
18717 // item is needed for carousel - not sure if it has any effect otherwise
18718 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18719 html: this.html || ''
18723 cfg.cls += ' active';
18727 cfg.tabId = this.tabId;
18735 initEvents: function()
18737 var p = this.parent();
18739 this.navId = this.navId || p.navId;
18741 if (typeof(this.navId) != 'undefined') {
18742 // not really needed.. but just in case.. parent should be a NavGroup.
18743 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18747 var i = tg.tabs.length - 1;
18749 if(this.active && tg.bullets > 0 && i < tg.bullets){
18750 tg.setActiveBullet(i);
18754 this.el.on('click', this.onClick, this);
18757 this.el.on("touchstart", this.onTouchStart, this);
18758 this.el.on("touchmove", this.onTouchMove, this);
18759 this.el.on("touchend", this.onTouchEnd, this);
18764 onRender : function(ct, position)
18766 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18769 setActive : function(state)
18771 Roo.log("panel - set active " + this.tabId + "=" + state);
18773 this.active = state;
18775 this.el.removeClass('active');
18777 } else if (!this.el.hasClass('active')) {
18778 this.el.addClass('active');
18781 this.fireEvent('changed', this, state);
18784 onClick : function(e)
18786 e.preventDefault();
18788 if(!this.href.length){
18792 window.location.href = this.href;
18801 onTouchStart : function(e)
18803 this.swiping = false;
18805 this.startX = e.browserEvent.touches[0].clientX;
18806 this.startY = e.browserEvent.touches[0].clientY;
18809 onTouchMove : function(e)
18811 this.swiping = true;
18813 this.endX = e.browserEvent.touches[0].clientX;
18814 this.endY = e.browserEvent.touches[0].clientY;
18817 onTouchEnd : function(e)
18824 var tabGroup = this.parent();
18826 if(this.endX > this.startX){ // swiping right
18827 tabGroup.showPanelPrev();
18831 if(this.startX > this.endX){ // swiping left
18832 tabGroup.showPanelNext();
18851 * @class Roo.bootstrap.DateField
18852 * @extends Roo.bootstrap.Input
18853 * Bootstrap DateField class
18854 * @cfg {Number} weekStart default 0
18855 * @cfg {String} viewMode default empty, (months|years)
18856 * @cfg {String} minViewMode default empty, (months|years)
18857 * @cfg {Number} startDate default -Infinity
18858 * @cfg {Number} endDate default Infinity
18859 * @cfg {Boolean} todayHighlight default false
18860 * @cfg {Boolean} todayBtn default false
18861 * @cfg {Boolean} calendarWeeks default false
18862 * @cfg {Object} daysOfWeekDisabled default empty
18863 * @cfg {Boolean} singleMode default false (true | false)
18865 * @cfg {Boolean} keyboardNavigation default true
18866 * @cfg {String} language default en
18869 * Create a new DateField
18870 * @param {Object} config The config object
18873 Roo.bootstrap.DateField = function(config){
18874 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18878 * Fires when this field show.
18879 * @param {Roo.bootstrap.DateField} this
18880 * @param {Mixed} date The date value
18885 * Fires when this field hide.
18886 * @param {Roo.bootstrap.DateField} this
18887 * @param {Mixed} date The date value
18892 * Fires when select a date.
18893 * @param {Roo.bootstrap.DateField} this
18894 * @param {Mixed} date The date value
18898 * @event beforeselect
18899 * Fires when before select a date.
18900 * @param {Roo.bootstrap.DateField} this
18901 * @param {Mixed} date The date value
18903 beforeselect : true
18907 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18910 * @cfg {String} format
18911 * The default date format string which can be overriden for localization support. The format must be
18912 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18916 * @cfg {String} altFormats
18917 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18918 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18920 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18928 todayHighlight : false,
18934 keyboardNavigation: true,
18936 calendarWeeks: false,
18938 startDate: -Infinity,
18942 daysOfWeekDisabled: [],
18946 singleMode : false,
18948 UTCDate: function()
18950 return new Date(Date.UTC.apply(Date, arguments));
18953 UTCToday: function()
18955 var today = new Date();
18956 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18959 getDate: function() {
18960 var d = this.getUTCDate();
18961 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18964 getUTCDate: function() {
18968 setDate: function(d) {
18969 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18972 setUTCDate: function(d) {
18974 this.setValue(this.formatDate(this.date));
18977 onRender: function(ct, position)
18980 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18982 this.language = this.language || 'en';
18983 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18984 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18986 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18987 this.format = this.format || 'm/d/y';
18988 this.isInline = false;
18989 this.isInput = true;
18990 this.component = this.el.select('.add-on', true).first() || false;
18991 this.component = (this.component && this.component.length === 0) ? false : this.component;
18992 this.hasInput = this.component && this.inputEl().length;
18994 if (typeof(this.minViewMode === 'string')) {
18995 switch (this.minViewMode) {
18997 this.minViewMode = 1;
19000 this.minViewMode = 2;
19003 this.minViewMode = 0;
19008 if (typeof(this.viewMode === 'string')) {
19009 switch (this.viewMode) {
19022 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
19024 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
19026 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19028 this.picker().on('mousedown', this.onMousedown, this);
19029 this.picker().on('click', this.onClick, this);
19031 this.picker().addClass('datepicker-dropdown');
19033 this.startViewMode = this.viewMode;
19035 if(this.singleMode){
19036 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19037 v.setVisibilityMode(Roo.Element.DISPLAY);
19041 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19042 v.setStyle('width', '189px');
19046 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19047 if(!this.calendarWeeks){
19052 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19053 v.attr('colspan', function(i, val){
19054 return parseInt(val) + 1;
19059 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19061 this.setStartDate(this.startDate);
19062 this.setEndDate(this.endDate);
19064 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19071 if(this.isInline) {
19076 picker : function()
19078 return this.pickerEl;
19079 // return this.el.select('.datepicker', true).first();
19082 fillDow: function()
19084 var dowCnt = this.weekStart;
19093 if(this.calendarWeeks){
19101 while (dowCnt < this.weekStart + 7) {
19105 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19109 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19112 fillMonths: function()
19115 var months = this.picker().select('>.datepicker-months td', true).first();
19117 months.dom.innerHTML = '';
19123 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19126 months.createChild(month);
19133 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;
19135 if (this.date < this.startDate) {
19136 this.viewDate = new Date(this.startDate);
19137 } else if (this.date > this.endDate) {
19138 this.viewDate = new Date(this.endDate);
19140 this.viewDate = new Date(this.date);
19148 var d = new Date(this.viewDate),
19149 year = d.getUTCFullYear(),
19150 month = d.getUTCMonth(),
19151 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19152 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19153 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19154 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19155 currentDate = this.date && this.date.valueOf(),
19156 today = this.UTCToday();
19158 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19160 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19162 // this.picker.select('>tfoot th.today').
19163 // .text(dates[this.language].today)
19164 // .toggle(this.todayBtn !== false);
19166 this.updateNavArrows();
19169 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19171 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19173 prevMonth.setUTCDate(day);
19175 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19177 var nextMonth = new Date(prevMonth);
19179 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19181 nextMonth = nextMonth.valueOf();
19183 var fillMonths = false;
19185 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19187 while(prevMonth.valueOf() <= nextMonth) {
19190 if (prevMonth.getUTCDay() === this.weekStart) {
19192 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19200 if(this.calendarWeeks){
19201 // ISO 8601: First week contains first thursday.
19202 // ISO also states week starts on Monday, but we can be more abstract here.
19204 // Start of current week: based on weekstart/current date
19205 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19206 // Thursday of this week
19207 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19208 // First Thursday of year, year from thursday
19209 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19210 // Calendar week: ms between thursdays, div ms per day, div 7 days
19211 calWeek = (th - yth) / 864e5 / 7 + 1;
19213 fillMonths.cn.push({
19221 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19223 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19226 if (this.todayHighlight &&
19227 prevMonth.getUTCFullYear() == today.getFullYear() &&
19228 prevMonth.getUTCMonth() == today.getMonth() &&
19229 prevMonth.getUTCDate() == today.getDate()) {
19230 clsName += ' today';
19233 if (currentDate && prevMonth.valueOf() === currentDate) {
19234 clsName += ' active';
19237 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19238 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19239 clsName += ' disabled';
19242 fillMonths.cn.push({
19244 cls: 'day ' + clsName,
19245 html: prevMonth.getDate()
19248 prevMonth.setDate(prevMonth.getDate()+1);
19251 var currentYear = this.date && this.date.getUTCFullYear();
19252 var currentMonth = this.date && this.date.getUTCMonth();
19254 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19256 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19257 v.removeClass('active');
19259 if(currentYear === year && k === currentMonth){
19260 v.addClass('active');
19263 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19264 v.addClass('disabled');
19270 year = parseInt(year/10, 10) * 10;
19272 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19274 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19277 for (var i = -1; i < 11; i++) {
19278 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19280 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19288 showMode: function(dir)
19291 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19294 Roo.each(this.picker().select('>div',true).elements, function(v){
19295 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19298 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19303 if(this.isInline) {
19307 this.picker().removeClass(['bottom', 'top']);
19309 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19311 * place to the top of element!
19315 this.picker().addClass('top');
19316 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19321 this.picker().addClass('bottom');
19323 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19326 parseDate : function(value)
19328 if(!value || value instanceof Date){
19331 var v = Date.parseDate(value, this.format);
19332 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19333 v = Date.parseDate(value, 'Y-m-d');
19335 if(!v && this.altFormats){
19336 if(!this.altFormatsArray){
19337 this.altFormatsArray = this.altFormats.split("|");
19339 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19340 v = Date.parseDate(value, this.altFormatsArray[i]);
19346 formatDate : function(date, fmt)
19348 return (!date || !(date instanceof Date)) ?
19349 date : date.dateFormat(fmt || this.format);
19352 onFocus : function()
19354 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19358 onBlur : function()
19360 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19362 var d = this.inputEl().getValue();
19369 showPopup : function()
19371 this.picker().show();
19375 this.fireEvent('showpopup', this, this.date);
19378 hidePopup : function()
19380 if(this.isInline) {
19383 this.picker().hide();
19384 this.viewMode = this.startViewMode;
19387 this.fireEvent('hidepopup', this, this.date);
19391 onMousedown: function(e)
19393 e.stopPropagation();
19394 e.preventDefault();
19399 Roo.bootstrap.DateField.superclass.keyup.call(this);
19403 setValue: function(v)
19405 if(this.fireEvent('beforeselect', this, v) !== false){
19406 var d = new Date(this.parseDate(v) ).clearTime();
19408 if(isNaN(d.getTime())){
19409 this.date = this.viewDate = '';
19410 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19414 v = this.formatDate(d);
19416 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19418 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19422 this.fireEvent('select', this, this.date);
19426 getValue: function()
19428 return this.formatDate(this.date);
19431 fireKey: function(e)
19433 if (!this.picker().isVisible()){
19434 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19440 var dateChanged = false,
19442 newDate, newViewDate;
19447 e.preventDefault();
19451 if (!this.keyboardNavigation) {
19454 dir = e.keyCode == 37 ? -1 : 1;
19457 newDate = this.moveYear(this.date, dir);
19458 newViewDate = this.moveYear(this.viewDate, dir);
19459 } else if (e.shiftKey){
19460 newDate = this.moveMonth(this.date, dir);
19461 newViewDate = this.moveMonth(this.viewDate, dir);
19463 newDate = new Date(this.date);
19464 newDate.setUTCDate(this.date.getUTCDate() + dir);
19465 newViewDate = new Date(this.viewDate);
19466 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19468 if (this.dateWithinRange(newDate)){
19469 this.date = newDate;
19470 this.viewDate = newViewDate;
19471 this.setValue(this.formatDate(this.date));
19473 e.preventDefault();
19474 dateChanged = true;
19479 if (!this.keyboardNavigation) {
19482 dir = e.keyCode == 38 ? -1 : 1;
19484 newDate = this.moveYear(this.date, dir);
19485 newViewDate = this.moveYear(this.viewDate, dir);
19486 } else if (e.shiftKey){
19487 newDate = this.moveMonth(this.date, dir);
19488 newViewDate = this.moveMonth(this.viewDate, dir);
19490 newDate = new Date(this.date);
19491 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19492 newViewDate = new Date(this.viewDate);
19493 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19495 if (this.dateWithinRange(newDate)){
19496 this.date = newDate;
19497 this.viewDate = newViewDate;
19498 this.setValue(this.formatDate(this.date));
19500 e.preventDefault();
19501 dateChanged = true;
19505 this.setValue(this.formatDate(this.date));
19507 e.preventDefault();
19510 this.setValue(this.formatDate(this.date));
19524 onClick: function(e)
19526 e.stopPropagation();
19527 e.preventDefault();
19529 var target = e.getTarget();
19531 if(target.nodeName.toLowerCase() === 'i'){
19532 target = Roo.get(target).dom.parentNode;
19535 var nodeName = target.nodeName;
19536 var className = target.className;
19537 var html = target.innerHTML;
19538 //Roo.log(nodeName);
19540 switch(nodeName.toLowerCase()) {
19542 switch(className) {
19548 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19549 switch(this.viewMode){
19551 this.viewDate = this.moveMonth(this.viewDate, dir);
19555 this.viewDate = this.moveYear(this.viewDate, dir);
19561 var date = new Date();
19562 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19564 this.setValue(this.formatDate(this.date));
19571 if (className.indexOf('disabled') < 0) {
19572 this.viewDate.setUTCDate(1);
19573 if (className.indexOf('month') > -1) {
19574 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19576 var year = parseInt(html, 10) || 0;
19577 this.viewDate.setUTCFullYear(year);
19581 if(this.singleMode){
19582 this.setValue(this.formatDate(this.viewDate));
19593 //Roo.log(className);
19594 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19595 var day = parseInt(html, 10) || 1;
19596 var year = this.viewDate.getUTCFullYear(),
19597 month = this.viewDate.getUTCMonth();
19599 if (className.indexOf('old') > -1) {
19606 } else if (className.indexOf('new') > -1) {
19614 //Roo.log([year,month,day]);
19615 this.date = this.UTCDate(year, month, day,0,0,0,0);
19616 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19618 //Roo.log(this.formatDate(this.date));
19619 this.setValue(this.formatDate(this.date));
19626 setStartDate: function(startDate)
19628 this.startDate = startDate || -Infinity;
19629 if (this.startDate !== -Infinity) {
19630 this.startDate = this.parseDate(this.startDate);
19633 this.updateNavArrows();
19636 setEndDate: function(endDate)
19638 this.endDate = endDate || Infinity;
19639 if (this.endDate !== Infinity) {
19640 this.endDate = this.parseDate(this.endDate);
19643 this.updateNavArrows();
19646 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19648 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19649 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19650 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19652 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19653 return parseInt(d, 10);
19656 this.updateNavArrows();
19659 updateNavArrows: function()
19661 if(this.singleMode){
19665 var d = new Date(this.viewDate),
19666 year = d.getUTCFullYear(),
19667 month = d.getUTCMonth();
19669 Roo.each(this.picker().select('.prev', true).elements, function(v){
19671 switch (this.viewMode) {
19674 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19680 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19687 Roo.each(this.picker().select('.next', true).elements, function(v){
19689 switch (this.viewMode) {
19692 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19698 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19706 moveMonth: function(date, dir)
19711 var new_date = new Date(date.valueOf()),
19712 day = new_date.getUTCDate(),
19713 month = new_date.getUTCMonth(),
19714 mag = Math.abs(dir),
19716 dir = dir > 0 ? 1 : -1;
19719 // If going back one month, make sure month is not current month
19720 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19722 return new_date.getUTCMonth() == month;
19724 // If going forward one month, make sure month is as expected
19725 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19727 return new_date.getUTCMonth() != new_month;
19729 new_month = month + dir;
19730 new_date.setUTCMonth(new_month);
19731 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19732 if (new_month < 0 || new_month > 11) {
19733 new_month = (new_month + 12) % 12;
19736 // For magnitudes >1, move one month at a time...
19737 for (var i=0; i<mag; i++) {
19738 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19739 new_date = this.moveMonth(new_date, dir);
19741 // ...then reset the day, keeping it in the new month
19742 new_month = new_date.getUTCMonth();
19743 new_date.setUTCDate(day);
19745 return new_month != new_date.getUTCMonth();
19748 // Common date-resetting loop -- if date is beyond end of month, make it
19751 new_date.setUTCDate(--day);
19752 new_date.setUTCMonth(new_month);
19757 moveYear: function(date, dir)
19759 return this.moveMonth(date, dir*12);
19762 dateWithinRange: function(date)
19764 return date >= this.startDate && date <= this.endDate;
19770 this.picker().remove();
19773 validateValue : function(value)
19775 if(this.getVisibilityEl().hasClass('hidden')){
19779 if(value.length < 1) {
19780 if(this.allowBlank){
19786 if(value.length < this.minLength){
19789 if(value.length > this.maxLength){
19793 var vt = Roo.form.VTypes;
19794 if(!vt[this.vtype](value, this)){
19798 if(typeof this.validator == "function"){
19799 var msg = this.validator(value);
19805 if(this.regex && !this.regex.test(value)){
19809 if(typeof(this.parseDate(value)) == 'undefined'){
19813 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19817 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19827 this.date = this.viewDate = '';
19829 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19834 Roo.apply(Roo.bootstrap.DateField, {
19845 html: '<i class="fa fa-arrow-left"/>'
19855 html: '<i class="fa fa-arrow-right"/>'
19897 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19898 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19899 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19900 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19901 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19914 navFnc: 'FullYear',
19919 navFnc: 'FullYear',
19924 Roo.apply(Roo.bootstrap.DateField, {
19928 cls: 'datepicker dropdown-menu roo-dynamic',
19932 cls: 'datepicker-days',
19936 cls: 'table-condensed',
19938 Roo.bootstrap.DateField.head,
19942 Roo.bootstrap.DateField.footer
19949 cls: 'datepicker-months',
19953 cls: 'table-condensed',
19955 Roo.bootstrap.DateField.head,
19956 Roo.bootstrap.DateField.content,
19957 Roo.bootstrap.DateField.footer
19964 cls: 'datepicker-years',
19968 cls: 'table-condensed',
19970 Roo.bootstrap.DateField.head,
19971 Roo.bootstrap.DateField.content,
19972 Roo.bootstrap.DateField.footer
19991 * @class Roo.bootstrap.TimeField
19992 * @extends Roo.bootstrap.Input
19993 * Bootstrap DateField class
19997 * Create a new TimeField
19998 * @param {Object} config The config object
20001 Roo.bootstrap.TimeField = function(config){
20002 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
20006 * Fires when this field show.
20007 * @param {Roo.bootstrap.DateField} thisthis
20008 * @param {Mixed} date The date value
20013 * Fires when this field hide.
20014 * @param {Roo.bootstrap.DateField} this
20015 * @param {Mixed} date The date value
20020 * Fires when select a date.
20021 * @param {Roo.bootstrap.DateField} this
20022 * @param {Mixed} date The date value
20028 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
20031 * @cfg {String} format
20032 * The default time format string which can be overriden for localization support. The format must be
20033 * valid according to {@link Date#parseDate} (defaults to 'H:i').
20037 onRender: function(ct, position)
20040 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20042 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20044 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20046 this.pop = this.picker().select('>.datepicker-time',true).first();
20047 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20049 this.picker().on('mousedown', this.onMousedown, this);
20050 this.picker().on('click', this.onClick, this);
20052 this.picker().addClass('datepicker-dropdown');
20057 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20058 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20059 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20060 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20061 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20062 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20066 fireKey: function(e){
20067 if (!this.picker().isVisible()){
20068 if (e.keyCode == 27) { // allow escape to hide and re-show picker
20074 e.preventDefault();
20082 this.onTogglePeriod();
20085 this.onIncrementMinutes();
20088 this.onDecrementMinutes();
20097 onClick: function(e) {
20098 e.stopPropagation();
20099 e.preventDefault();
20102 picker : function()
20104 return this.el.select('.datepicker', true).first();
20107 fillTime: function()
20109 var time = this.pop.select('tbody', true).first();
20111 time.dom.innerHTML = '';
20126 cls: 'hours-up glyphicon glyphicon-chevron-up'
20146 cls: 'minutes-up glyphicon glyphicon-chevron-up'
20167 cls: 'timepicker-hour',
20182 cls: 'timepicker-minute',
20197 cls: 'btn btn-primary period',
20219 cls: 'hours-down glyphicon glyphicon-chevron-down'
20239 cls: 'minutes-down glyphicon glyphicon-chevron-down'
20257 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20264 var hours = this.time.getHours();
20265 var minutes = this.time.getMinutes();
20278 hours = hours - 12;
20282 hours = '0' + hours;
20286 minutes = '0' + minutes;
20289 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20290 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20291 this.pop.select('button', true).first().dom.innerHTML = period;
20297 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20299 var cls = ['bottom'];
20301 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20308 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20313 this.picker().addClass(cls.join('-'));
20317 Roo.each(cls, function(c){
20319 _this.picker().setTop(_this.inputEl().getHeight());
20323 _this.picker().setTop(0 - _this.picker().getHeight());
20328 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20332 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20339 onFocus : function()
20341 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20345 onBlur : function()
20347 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20353 this.picker().show();
20358 this.fireEvent('show', this, this.date);
20363 this.picker().hide();
20366 this.fireEvent('hide', this, this.date);
20369 setTime : function()
20372 this.setValue(this.time.format(this.format));
20374 this.fireEvent('select', this, this.date);
20379 onMousedown: function(e){
20380 e.stopPropagation();
20381 e.preventDefault();
20384 onIncrementHours: function()
20386 Roo.log('onIncrementHours');
20387 this.time = this.time.add(Date.HOUR, 1);
20392 onDecrementHours: function()
20394 Roo.log('onDecrementHours');
20395 this.time = this.time.add(Date.HOUR, -1);
20399 onIncrementMinutes: function()
20401 Roo.log('onIncrementMinutes');
20402 this.time = this.time.add(Date.MINUTE, 1);
20406 onDecrementMinutes: function()
20408 Roo.log('onDecrementMinutes');
20409 this.time = this.time.add(Date.MINUTE, -1);
20413 onTogglePeriod: function()
20415 Roo.log('onTogglePeriod');
20416 this.time = this.time.add(Date.HOUR, 12);
20423 Roo.apply(Roo.bootstrap.TimeField, {
20453 cls: 'btn btn-info ok',
20465 Roo.apply(Roo.bootstrap.TimeField, {
20469 cls: 'datepicker dropdown-menu',
20473 cls: 'datepicker-time',
20477 cls: 'table-condensed',
20479 Roo.bootstrap.TimeField.content,
20480 Roo.bootstrap.TimeField.footer
20499 * @class Roo.bootstrap.MonthField
20500 * @extends Roo.bootstrap.Input
20501 * Bootstrap MonthField class
20503 * @cfg {String} language default en
20506 * Create a new MonthField
20507 * @param {Object} config The config object
20510 Roo.bootstrap.MonthField = function(config){
20511 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20516 * Fires when this field show.
20517 * @param {Roo.bootstrap.MonthField} this
20518 * @param {Mixed} date The date value
20523 * Fires when this field hide.
20524 * @param {Roo.bootstrap.MonthField} this
20525 * @param {Mixed} date The date value
20530 * Fires when select a date.
20531 * @param {Roo.bootstrap.MonthField} this
20532 * @param {String} oldvalue The old value
20533 * @param {String} newvalue The new value
20539 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20541 onRender: function(ct, position)
20544 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20546 this.language = this.language || 'en';
20547 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20548 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20550 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20551 this.isInline = false;
20552 this.isInput = true;
20553 this.component = this.el.select('.add-on', true).first() || false;
20554 this.component = (this.component && this.component.length === 0) ? false : this.component;
20555 this.hasInput = this.component && this.inputEL().length;
20557 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20559 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20561 this.picker().on('mousedown', this.onMousedown, this);
20562 this.picker().on('click', this.onClick, this);
20564 this.picker().addClass('datepicker-dropdown');
20566 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20567 v.setStyle('width', '189px');
20574 if(this.isInline) {
20580 setValue: function(v, suppressEvent)
20582 var o = this.getValue();
20584 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20588 if(suppressEvent !== true){
20589 this.fireEvent('select', this, o, v);
20594 getValue: function()
20599 onClick: function(e)
20601 e.stopPropagation();
20602 e.preventDefault();
20604 var target = e.getTarget();
20606 if(target.nodeName.toLowerCase() === 'i'){
20607 target = Roo.get(target).dom.parentNode;
20610 var nodeName = target.nodeName;
20611 var className = target.className;
20612 var html = target.innerHTML;
20614 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20618 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20620 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20626 picker : function()
20628 return this.pickerEl;
20631 fillMonths: function()
20634 var months = this.picker().select('>.datepicker-months td', true).first();
20636 months.dom.innerHTML = '';
20642 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20645 months.createChild(month);
20654 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20655 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20658 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20659 e.removeClass('active');
20661 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20662 e.addClass('active');
20669 if(this.isInline) {
20673 this.picker().removeClass(['bottom', 'top']);
20675 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20677 * place to the top of element!
20681 this.picker().addClass('top');
20682 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20687 this.picker().addClass('bottom');
20689 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20692 onFocus : function()
20694 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20698 onBlur : function()
20700 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20702 var d = this.inputEl().getValue();
20711 this.picker().show();
20712 this.picker().select('>.datepicker-months', true).first().show();
20716 this.fireEvent('show', this, this.date);
20721 if(this.isInline) {
20724 this.picker().hide();
20725 this.fireEvent('hide', this, this.date);
20729 onMousedown: function(e)
20731 e.stopPropagation();
20732 e.preventDefault();
20737 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20741 fireKey: function(e)
20743 if (!this.picker().isVisible()){
20744 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20755 e.preventDefault();
20759 dir = e.keyCode == 37 ? -1 : 1;
20761 this.vIndex = this.vIndex + dir;
20763 if(this.vIndex < 0){
20767 if(this.vIndex > 11){
20771 if(isNaN(this.vIndex)){
20775 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20781 dir = e.keyCode == 38 ? -1 : 1;
20783 this.vIndex = this.vIndex + dir * 4;
20785 if(this.vIndex < 0){
20789 if(this.vIndex > 11){
20793 if(isNaN(this.vIndex)){
20797 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20802 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20803 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20807 e.preventDefault();
20810 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20811 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20827 this.picker().remove();
20832 Roo.apply(Roo.bootstrap.MonthField, {
20851 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20852 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20857 Roo.apply(Roo.bootstrap.MonthField, {
20861 cls: 'datepicker dropdown-menu roo-dynamic',
20865 cls: 'datepicker-months',
20869 cls: 'table-condensed',
20871 Roo.bootstrap.DateField.content
20891 * @class Roo.bootstrap.CheckBox
20892 * @extends Roo.bootstrap.Input
20893 * Bootstrap CheckBox class
20895 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20896 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20897 * @cfg {String} boxLabel The text that appears beside the checkbox
20898 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20899 * @cfg {Boolean} checked initnal the element
20900 * @cfg {Boolean} inline inline the element (default false)
20901 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20902 * @cfg {String} tooltip label tooltip
20905 * Create a new CheckBox
20906 * @param {Object} config The config object
20909 Roo.bootstrap.CheckBox = function(config){
20910 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20915 * Fires when the element is checked or unchecked.
20916 * @param {Roo.bootstrap.CheckBox} this This input
20917 * @param {Boolean} checked The new checked value
20922 * Fires when the element is click.
20923 * @param {Roo.bootstrap.CheckBox} this This input
20930 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20932 inputType: 'checkbox',
20941 // checkbox success does not make any sense really..
20946 getAutoCreate : function()
20948 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20954 cfg.cls = 'form-group ' + this.inputType; //input-group
20957 cfg.cls += ' ' + this.inputType + '-inline';
20963 type : this.inputType,
20964 value : this.inputValue,
20965 cls : 'roo-' + this.inputType, //'form-box',
20966 placeholder : this.placeholder || ''
20970 if(this.inputType != 'radio'){
20974 cls : 'roo-hidden-value',
20975 value : this.checked ? this.inputValue : this.valueOff
20980 if (this.weight) { // Validity check?
20981 cfg.cls += " " + this.inputType + "-" + this.weight;
20984 if (this.disabled) {
20985 input.disabled=true;
20989 input.checked = this.checked;
20994 input.name = this.name;
20996 if(this.inputType != 'radio'){
20997 hidden.name = this.name;
20998 input.name = '_hidden_' + this.name;
21003 input.cls += ' input-' + this.size;
21008 ['xs','sm','md','lg'].map(function(size){
21009 if (settings[size]) {
21010 cfg.cls += ' col-' + size + '-' + settings[size];
21014 var inputblock = input;
21016 if (this.before || this.after) {
21019 cls : 'input-group',
21024 inputblock.cn.push({
21026 cls : 'input-group-addon',
21031 inputblock.cn.push(input);
21033 if(this.inputType != 'radio'){
21034 inputblock.cn.push(hidden);
21038 inputblock.cn.push({
21040 cls : 'input-group-addon',
21046 var boxLabelCfg = false;
21052 //'for': id, // box label is handled by onclick - so no for...
21054 html: this.boxLabel
21057 boxLabelCfg.tooltip = this.tooltip;
21063 if (align ==='left' && this.fieldLabel.length) {
21064 // Roo.log("left and has label");
21069 cls : 'control-label',
21070 html : this.fieldLabel
21081 cfg.cn[1].cn.push(boxLabelCfg);
21084 if(this.labelWidth > 12){
21085 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21088 if(this.labelWidth < 13 && this.labelmd == 0){
21089 this.labelmd = this.labelWidth;
21092 if(this.labellg > 0){
21093 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21094 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21097 if(this.labelmd > 0){
21098 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21099 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21102 if(this.labelsm > 0){
21103 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21104 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21107 if(this.labelxs > 0){
21108 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21109 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21112 } else if ( this.fieldLabel.length) {
21113 // Roo.log(" label");
21117 tag: this.boxLabel ? 'span' : 'label',
21119 cls: 'control-label box-input-label',
21120 //cls : 'input-group-addon',
21121 html : this.fieldLabel
21128 cfg.cn.push(boxLabelCfg);
21133 // Roo.log(" no label && no align");
21134 cfg.cn = [ inputblock ] ;
21136 cfg.cn.push(boxLabelCfg);
21144 if(this.inputType != 'radio'){
21145 cfg.cn.push(hidden);
21153 * return the real input element.
21155 inputEl: function ()
21157 return this.el.select('input.roo-' + this.inputType,true).first();
21159 hiddenEl: function ()
21161 return this.el.select('input.roo-hidden-value',true).first();
21164 labelEl: function()
21166 return this.el.select('label.control-label',true).first();
21168 /* depricated... */
21172 return this.labelEl();
21175 boxLabelEl: function()
21177 return this.el.select('label.box-label',true).first();
21180 initEvents : function()
21182 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21184 this.inputEl().on('click', this.onClick, this);
21186 if (this.boxLabel) {
21187 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
21190 this.startValue = this.getValue();
21193 Roo.bootstrap.CheckBox.register(this);
21197 onClick : function(e)
21199 if(this.fireEvent('click', this, e) !== false){
21200 this.setChecked(!this.checked);
21205 setChecked : function(state,suppressEvent)
21207 this.startValue = this.getValue();
21209 if(this.inputType == 'radio'){
21211 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21212 e.dom.checked = false;
21215 this.inputEl().dom.checked = true;
21217 this.inputEl().dom.value = this.inputValue;
21219 if(suppressEvent !== true){
21220 this.fireEvent('check', this, true);
21228 this.checked = state;
21230 this.inputEl().dom.checked = state;
21233 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21235 if(suppressEvent !== true){
21236 this.fireEvent('check', this, state);
21242 getValue : function()
21244 if(this.inputType == 'radio'){
21245 return this.getGroupValue();
21248 return this.hiddenEl().dom.value;
21252 getGroupValue : function()
21254 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21258 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21261 setValue : function(v,suppressEvent)
21263 if(this.inputType == 'radio'){
21264 this.setGroupValue(v, suppressEvent);
21268 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21273 setGroupValue : function(v, suppressEvent)
21275 this.startValue = this.getValue();
21277 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21278 e.dom.checked = false;
21280 if(e.dom.value == v){
21281 e.dom.checked = true;
21285 if(suppressEvent !== true){
21286 this.fireEvent('check', this, true);
21294 validate : function()
21296 if(this.getVisibilityEl().hasClass('hidden')){
21302 (this.inputType == 'radio' && this.validateRadio()) ||
21303 (this.inputType == 'checkbox' && this.validateCheckbox())
21309 this.markInvalid();
21313 validateRadio : function()
21315 if(this.getVisibilityEl().hasClass('hidden')){
21319 if(this.allowBlank){
21325 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21326 if(!e.dom.checked){
21338 validateCheckbox : function()
21341 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21342 //return (this.getValue() == this.inputValue) ? true : false;
21345 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21353 for(var i in group){
21354 if(group[i].el.isVisible(true)){
21362 for(var i in group){
21367 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21374 * Mark this field as valid
21376 markValid : function()
21380 this.fireEvent('valid', this);
21382 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21385 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21392 if(this.inputType == 'radio'){
21393 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21394 var fg = e.findParent('.form-group', false, true);
21395 if (Roo.bootstrap.version == 3) {
21396 fg.removeClass([_this.invalidClass, _this.validClass]);
21397 fg.addClass(_this.validClass);
21399 fg.removeClass(['is-valid', 'is-invalid']);
21400 fg.addClass('is-valid');
21408 var fg = this.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');
21419 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21425 for(var i in group){
21426 var fg = group[i].el.findParent('.form-group', false, true);
21427 if (Roo.bootstrap.version == 3) {
21428 fg.removeClass([this.invalidClass, this.validClass]);
21429 fg.addClass(this.validClass);
21431 fg.removeClass(['is-valid', 'is-invalid']);
21432 fg.addClass('is-valid');
21438 * Mark this field as invalid
21439 * @param {String} msg The validation message
21441 markInvalid : function(msg)
21443 if(this.allowBlank){
21449 this.fireEvent('invalid', this, msg);
21451 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21454 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21458 label.markInvalid();
21461 if(this.inputType == 'radio'){
21463 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21464 var fg = e.findParent('.form-group', false, true);
21465 if (Roo.bootstrap.version == 3) {
21466 fg.removeClass([_this.invalidClass, _this.validClass]);
21467 fg.addClass(_this.invalidClass);
21469 fg.removeClass(['is-invalid', 'is-valid']);
21470 fg.addClass('is-invalid');
21478 var fg = this.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');
21489 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21495 for(var i in group){
21496 var fg = group[i].el.findParent('.form-group', false, true);
21497 if (Roo.bootstrap.version == 3) {
21498 fg.removeClass([_this.invalidClass, _this.validClass]);
21499 fg.addClass(_this.invalidClass);
21501 fg.removeClass(['is-invalid', 'is-valid']);
21502 fg.addClass('is-invalid');
21508 clearInvalid : function()
21510 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21512 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21514 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21516 if (label && label.iconEl) {
21517 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21518 label.iconEl.removeClass(['is-invalid', 'is-valid']);
21522 disable : function()
21524 if(this.inputType != 'radio'){
21525 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21532 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21533 _this.getActionEl().addClass(this.disabledClass);
21534 e.dom.disabled = true;
21538 this.disabled = true;
21539 this.fireEvent("disable", this);
21543 enable : function()
21545 if(this.inputType != 'radio'){
21546 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21553 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21554 _this.getActionEl().removeClass(this.disabledClass);
21555 e.dom.disabled = false;
21559 this.disabled = false;
21560 this.fireEvent("enable", this);
21564 setBoxLabel : function(v)
21569 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21575 Roo.apply(Roo.bootstrap.CheckBox, {
21580 * register a CheckBox Group
21581 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21583 register : function(checkbox)
21585 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21586 this.groups[checkbox.groupId] = {};
21589 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21593 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21597 * fetch a CheckBox Group based on the group ID
21598 * @param {string} the group ID
21599 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21601 get: function(groupId) {
21602 if (typeof(this.groups[groupId]) == 'undefined') {
21606 return this.groups[groupId] ;
21619 * @class Roo.bootstrap.Radio
21620 * @extends Roo.bootstrap.Component
21621 * Bootstrap Radio class
21622 * @cfg {String} boxLabel - the label associated
21623 * @cfg {String} value - the value of radio
21626 * Create a new Radio
21627 * @param {Object} config The config object
21629 Roo.bootstrap.Radio = function(config){
21630 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21634 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21640 getAutoCreate : function()
21644 cls : 'form-group radio',
21649 html : this.boxLabel
21657 initEvents : function()
21659 this.parent().register(this);
21661 this.el.on('click', this.onClick, this);
21665 onClick : function(e)
21667 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21668 this.setChecked(true);
21672 setChecked : function(state, suppressEvent)
21674 this.parent().setValue(this.value, suppressEvent);
21678 setBoxLabel : function(v)
21683 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21698 * @class Roo.bootstrap.SecurePass
21699 * @extends Roo.bootstrap.Input
21700 * Bootstrap SecurePass class
21704 * Create a new SecurePass
21705 * @param {Object} config The config object
21708 Roo.bootstrap.SecurePass = function (config) {
21709 // these go here, so the translation tool can replace them..
21711 PwdEmpty: "Please type a password, and then retype it to confirm.",
21712 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21713 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21714 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21715 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21716 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21717 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21718 TooWeak: "Your password is Too Weak."
21720 this.meterLabel = "Password strength:";
21721 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21722 this.meterClass = [
21723 "roo-password-meter-tooweak",
21724 "roo-password-meter-weak",
21725 "roo-password-meter-medium",
21726 "roo-password-meter-strong",
21727 "roo-password-meter-grey"
21732 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21735 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21737 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21739 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21740 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21741 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21742 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21743 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21744 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21745 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21755 * @cfg {String/Object} Label for the strength meter (defaults to
21756 * 'Password strength:')
21761 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21762 * ['Weak', 'Medium', 'Strong'])
21765 pwdStrengths: false,
21778 initEvents: function ()
21780 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21782 if (this.el.is('input[type=password]') && Roo.isSafari) {
21783 this.el.on('keydown', this.SafariOnKeyDown, this);
21786 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21789 onRender: function (ct, position)
21791 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21792 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21793 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21795 this.trigger.createChild({
21800 cls: 'roo-password-meter-grey col-xs-12',
21803 //width: this.meterWidth + 'px'
21807 cls: 'roo-password-meter-text'
21813 if (this.hideTrigger) {
21814 this.trigger.setDisplayed(false);
21816 this.setSize(this.width || '', this.height || '');
21819 onDestroy: function ()
21821 if (this.trigger) {
21822 this.trigger.removeAllListeners();
21823 this.trigger.remove();
21826 this.wrap.remove();
21828 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21831 checkStrength: function ()
21833 var pwd = this.inputEl().getValue();
21834 if (pwd == this._lastPwd) {
21839 if (this.ClientSideStrongPassword(pwd)) {
21841 } else if (this.ClientSideMediumPassword(pwd)) {
21843 } else if (this.ClientSideWeakPassword(pwd)) {
21849 Roo.log('strength1: ' + strength);
21851 //var pm = this.trigger.child('div/div/div').dom;
21852 var pm = this.trigger.child('div/div');
21853 pm.removeClass(this.meterClass);
21854 pm.addClass(this.meterClass[strength]);
21857 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21859 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21861 this._lastPwd = pwd;
21865 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21867 this._lastPwd = '';
21869 var pm = this.trigger.child('div/div');
21870 pm.removeClass(this.meterClass);
21871 pm.addClass('roo-password-meter-grey');
21874 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21877 this.inputEl().dom.type='password';
21880 validateValue: function (value)
21883 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21886 if (value.length == 0) {
21887 if (this.allowBlank) {
21888 this.clearInvalid();
21892 this.markInvalid(this.errors.PwdEmpty);
21893 this.errorMsg = this.errors.PwdEmpty;
21901 if ('[\x21-\x7e]*'.match(value)) {
21902 this.markInvalid(this.errors.PwdBadChar);
21903 this.errorMsg = this.errors.PwdBadChar;
21906 if (value.length < 6) {
21907 this.markInvalid(this.errors.PwdShort);
21908 this.errorMsg = this.errors.PwdShort;
21911 if (value.length > 16) {
21912 this.markInvalid(this.errors.PwdLong);
21913 this.errorMsg = this.errors.PwdLong;
21917 if (this.ClientSideStrongPassword(value)) {
21919 } else if (this.ClientSideMediumPassword(value)) {
21921 } else if (this.ClientSideWeakPassword(value)) {
21928 if (strength < 2) {
21929 //this.markInvalid(this.errors.TooWeak);
21930 this.errorMsg = this.errors.TooWeak;
21935 console.log('strength2: ' + strength);
21937 //var pm = this.trigger.child('div/div/div').dom;
21939 var pm = this.trigger.child('div/div');
21940 pm.removeClass(this.meterClass);
21941 pm.addClass(this.meterClass[strength]);
21943 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21945 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21947 this.errorMsg = '';
21951 CharacterSetChecks: function (type)
21954 this.fResult = false;
21957 isctype: function (character, type)
21960 case this.kCapitalLetter:
21961 if (character >= 'A' && character <= 'Z') {
21966 case this.kSmallLetter:
21967 if (character >= 'a' && character <= 'z') {
21973 if (character >= '0' && character <= '9') {
21978 case this.kPunctuation:
21979 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21990 IsLongEnough: function (pwd, size)
21992 return !(pwd == null || isNaN(size) || pwd.length < size);
21995 SpansEnoughCharacterSets: function (word, nb)
21997 if (!this.IsLongEnough(word, nb))
22002 var characterSetChecks = new Array(
22003 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
22004 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
22007 for (var index = 0; index < word.length; ++index) {
22008 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22009 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
22010 characterSetChecks[nCharSet].fResult = true;
22017 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22018 if (characterSetChecks[nCharSet].fResult) {
22023 if (nCharSets < nb) {
22029 ClientSideStrongPassword: function (pwd)
22031 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22034 ClientSideMediumPassword: function (pwd)
22036 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22039 ClientSideWeakPassword: function (pwd)
22041 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22044 })//<script type="text/javascript">
22047 * Based Ext JS Library 1.1.1
22048 * Copyright(c) 2006-2007, Ext JS, LLC.
22054 * @class Roo.HtmlEditorCore
22055 * @extends Roo.Component
22056 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22058 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22061 Roo.HtmlEditorCore = function(config){
22064 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22069 * @event initialize
22070 * Fires when the editor is fully initialized (including the iframe)
22071 * @param {Roo.HtmlEditorCore} this
22076 * Fires when the editor is first receives the focus. Any insertion must wait
22077 * until after this event.
22078 * @param {Roo.HtmlEditorCore} this
22082 * @event beforesync
22083 * Fires before the textarea is updated with content from the editor iframe. Return false
22084 * to cancel the sync.
22085 * @param {Roo.HtmlEditorCore} this
22086 * @param {String} html
22090 * @event beforepush
22091 * Fires before the iframe editor is updated with content from the textarea. Return false
22092 * to cancel the push.
22093 * @param {Roo.HtmlEditorCore} this
22094 * @param {String} html
22099 * Fires when the textarea is updated with content from the editor iframe.
22100 * @param {Roo.HtmlEditorCore} this
22101 * @param {String} html
22106 * Fires when the iframe editor is updated with content from the textarea.
22107 * @param {Roo.HtmlEditorCore} this
22108 * @param {String} html
22113 * @event editorevent
22114 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22115 * @param {Roo.HtmlEditorCore} this
22121 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22123 // defaults : white / black...
22124 this.applyBlacklists();
22131 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
22135 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
22141 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22146 * @cfg {Number} height (in pixels)
22150 * @cfg {Number} width (in pixels)
22155 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22158 stylesheets: false,
22163 // private properties
22164 validationEvent : false,
22166 initialized : false,
22168 sourceEditMode : false,
22169 onFocus : Roo.emptyFn,
22171 hideMode:'offsets',
22175 // blacklist + whitelisted elements..
22182 * Protected method that will not generally be called directly. It
22183 * is called when the editor initializes the iframe with HTML contents. Override this method if you
22184 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22186 getDocMarkup : function(){
22190 // inherit styels from page...??
22191 if (this.stylesheets === false) {
22193 Roo.get(document.head).select('style').each(function(node) {
22194 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22197 Roo.get(document.head).select('link').each(function(node) {
22198 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22201 } else if (!this.stylesheets.length) {
22203 st = '<style type="text/css">' +
22204 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22207 st = '<style type="text/css">' +
22212 st += '<style type="text/css">' +
22213 'IMG { cursor: pointer } ' +
22216 var cls = 'roo-htmleditor-body';
22218 if(this.bodyCls.length){
22219 cls += ' ' + this.bodyCls;
22222 return '<html><head>' + st +
22223 //<style type="text/css">' +
22224 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22226 ' </head><body class="' + cls + '"></body></html>';
22230 onRender : function(ct, position)
22233 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22234 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22237 this.el.dom.style.border = '0 none';
22238 this.el.dom.setAttribute('tabIndex', -1);
22239 this.el.addClass('x-hidden hide');
22243 if(Roo.isIE){ // fix IE 1px bogus margin
22244 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22248 this.frameId = Roo.id();
22252 var iframe = this.owner.wrap.createChild({
22254 cls: 'form-control', // bootstrap..
22256 name: this.frameId,
22257 frameBorder : 'no',
22258 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
22263 this.iframe = iframe.dom;
22265 this.assignDocWin();
22267 this.doc.designMode = 'on';
22270 this.doc.write(this.getDocMarkup());
22274 var task = { // must defer to wait for browser to be ready
22276 //console.log("run task?" + this.doc.readyState);
22277 this.assignDocWin();
22278 if(this.doc.body || this.doc.readyState == 'complete'){
22280 this.doc.designMode="on";
22284 Roo.TaskMgr.stop(task);
22285 this.initEditor.defer(10, this);
22292 Roo.TaskMgr.start(task);
22297 onResize : function(w, h)
22299 Roo.log('resize: ' +w + ',' + h );
22300 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22304 if(typeof w == 'number'){
22306 this.iframe.style.width = w + 'px';
22308 if(typeof h == 'number'){
22310 this.iframe.style.height = h + 'px';
22312 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22319 * Toggles the editor between standard and source edit mode.
22320 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22322 toggleSourceEdit : function(sourceEditMode){
22324 this.sourceEditMode = sourceEditMode === true;
22326 if(this.sourceEditMode){
22328 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
22331 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22332 //this.iframe.className = '';
22335 //this.setSize(this.owner.wrap.getSize());
22336 //this.fireEvent('editmodechange', this, this.sourceEditMode);
22343 * Protected method that will not generally be called directly. If you need/want
22344 * custom HTML cleanup, this is the method you should override.
22345 * @param {String} html The HTML to be cleaned
22346 * return {String} The cleaned HTML
22348 cleanHtml : function(html){
22349 html = String(html);
22350 if(html.length > 5){
22351 if(Roo.isSafari){ // strip safari nonsense
22352 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22355 if(html == ' '){
22362 * HTML Editor -> Textarea
22363 * Protected method that will not generally be called directly. Syncs the contents
22364 * of the editor iframe with the textarea.
22366 syncValue : function(){
22367 if(this.initialized){
22368 var bd = (this.doc.body || this.doc.documentElement);
22369 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22370 var html = bd.innerHTML;
22372 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22373 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22375 html = '<div style="'+m[0]+'">' + html + '</div>';
22378 html = this.cleanHtml(html);
22379 // fix up the special chars.. normaly like back quotes in word...
22380 // however we do not want to do this with chinese..
22381 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
22383 var cc = match.charCodeAt();
22385 // Get the character value, handling surrogate pairs
22386 if (match.length == 2) {
22387 // It's a surrogate pair, calculate the Unicode code point
22388 var high = match.charCodeAt(0) - 0xD800;
22389 var low = match.charCodeAt(1) - 0xDC00;
22390 cc = (high * 0x400) + low + 0x10000;
22392 (cc >= 0x4E00 && cc < 0xA000 ) ||
22393 (cc >= 0x3400 && cc < 0x4E00 ) ||
22394 (cc >= 0xf900 && cc < 0xfb00 )
22399 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
22400 return "&#" + cc + ";";
22407 if(this.owner.fireEvent('beforesync', this, html) !== false){
22408 this.el.dom.value = html;
22409 this.owner.fireEvent('sync', this, html);
22415 * Protected method that will not generally be called directly. Pushes the value of the textarea
22416 * into the iframe editor.
22418 pushValue : function(){
22419 if(this.initialized){
22420 var v = this.el.dom.value.trim();
22422 // if(v.length < 1){
22426 if(this.owner.fireEvent('beforepush', this, v) !== false){
22427 var d = (this.doc.body || this.doc.documentElement);
22429 this.cleanUpPaste();
22430 this.el.dom.value = d.innerHTML;
22431 this.owner.fireEvent('push', this, v);
22437 deferFocus : function(){
22438 this.focus.defer(10, this);
22442 focus : function(){
22443 if(this.win && !this.sourceEditMode){
22450 assignDocWin: function()
22452 var iframe = this.iframe;
22455 this.doc = iframe.contentWindow.document;
22456 this.win = iframe.contentWindow;
22458 // if (!Roo.get(this.frameId)) {
22461 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22462 // this.win = Roo.get(this.frameId).dom.contentWindow;
22464 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22468 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22469 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22474 initEditor : function(){
22475 //console.log("INIT EDITOR");
22476 this.assignDocWin();
22480 this.doc.designMode="on";
22482 this.doc.write(this.getDocMarkup());
22485 var dbody = (this.doc.body || this.doc.documentElement);
22486 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22487 // this copies styles from the containing element into thsi one..
22488 // not sure why we need all of this..
22489 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22491 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22492 //ss['background-attachment'] = 'fixed'; // w3c
22493 dbody.bgProperties = 'fixed'; // ie
22494 //Roo.DomHelper.applyStyles(dbody, ss);
22495 Roo.EventManager.on(this.doc, {
22496 //'mousedown': this.onEditorEvent,
22497 'mouseup': this.onEditorEvent,
22498 'dblclick': this.onEditorEvent,
22499 'click': this.onEditorEvent,
22500 'keyup': this.onEditorEvent,
22505 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22507 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22508 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22510 this.initialized = true;
22512 this.owner.fireEvent('initialize', this);
22517 onDestroy : function(){
22523 //for (var i =0; i < this.toolbars.length;i++) {
22524 // // fixme - ask toolbars for heights?
22525 // this.toolbars[i].onDestroy();
22528 //this.wrap.dom.innerHTML = '';
22529 //this.wrap.remove();
22534 onFirstFocus : function(){
22536 this.assignDocWin();
22539 this.activated = true;
22542 if(Roo.isGecko){ // prevent silly gecko errors
22544 var s = this.win.getSelection();
22545 if(!s.focusNode || s.focusNode.nodeType != 3){
22546 var r = s.getRangeAt(0);
22547 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22552 this.execCmd('useCSS', true);
22553 this.execCmd('styleWithCSS', false);
22556 this.owner.fireEvent('activate', this);
22560 adjustFont: function(btn){
22561 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22562 //if(Roo.isSafari){ // safari
22565 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22566 if(Roo.isSafari){ // safari
22567 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22568 v = (v < 10) ? 10 : v;
22569 v = (v > 48) ? 48 : v;
22570 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22575 v = Math.max(1, v+adjust);
22577 this.execCmd('FontSize', v );
22580 onEditorEvent : function(e)
22582 this.owner.fireEvent('editorevent', this, e);
22583 // this.updateToolbar();
22584 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22587 insertTag : function(tg)
22589 // could be a bit smarter... -> wrap the current selected tRoo..
22590 if (tg.toLowerCase() == 'span' ||
22591 tg.toLowerCase() == 'code' ||
22592 tg.toLowerCase() == 'sup' ||
22593 tg.toLowerCase() == 'sub'
22596 range = this.createRange(this.getSelection());
22597 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22598 wrappingNode.appendChild(range.extractContents());
22599 range.insertNode(wrappingNode);
22606 this.execCmd("formatblock", tg);
22610 insertText : function(txt)
22614 var range = this.createRange();
22615 range.deleteContents();
22616 //alert(Sender.getAttribute('label'));
22618 range.insertNode(this.doc.createTextNode(txt));
22624 * Executes a Midas editor command on the editor document and performs necessary focus and
22625 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22626 * @param {String} cmd The Midas command
22627 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22629 relayCmd : function(cmd, value){
22631 this.execCmd(cmd, value);
22632 this.owner.fireEvent('editorevent', this);
22633 //this.updateToolbar();
22634 this.owner.deferFocus();
22638 * Executes a Midas editor command directly on the editor document.
22639 * For visual commands, you should use {@link #relayCmd} instead.
22640 * <b>This should only be called after the editor is initialized.</b>
22641 * @param {String} cmd The Midas command
22642 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22644 execCmd : function(cmd, value){
22645 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22652 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22654 * @param {String} text | dom node..
22656 insertAtCursor : function(text)
22659 if(!this.activated){
22665 var r = this.doc.selection.createRange();
22676 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22680 // from jquery ui (MIT licenced)
22682 var win = this.win;
22684 if (win.getSelection && win.getSelection().getRangeAt) {
22685 range = win.getSelection().getRangeAt(0);
22686 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22687 range.insertNode(node);
22688 } else if (win.document.selection && win.document.selection.createRange) {
22689 // no firefox support
22690 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22691 win.document.selection.createRange().pasteHTML(txt);
22693 // no firefox support
22694 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22695 this.execCmd('InsertHTML', txt);
22704 mozKeyPress : function(e){
22706 var c = e.getCharCode(), cmd;
22709 c = String.fromCharCode(c).toLowerCase();
22723 this.cleanUpPaste.defer(100, this);
22731 e.preventDefault();
22739 fixKeys : function(){ // load time branching for fastest keydown performance
22741 return function(e){
22742 var k = e.getKey(), r;
22745 r = this.doc.selection.createRange();
22748 r.pasteHTML('    ');
22755 r = this.doc.selection.createRange();
22757 var target = r.parentElement();
22758 if(!target || target.tagName.toLowerCase() != 'li'){
22760 r.pasteHTML('<br />');
22766 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22767 this.cleanUpPaste.defer(100, this);
22773 }else if(Roo.isOpera){
22774 return function(e){
22775 var k = e.getKey();
22779 this.execCmd('InsertHTML','    ');
22782 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22783 this.cleanUpPaste.defer(100, this);
22788 }else if(Roo.isSafari){
22789 return function(e){
22790 var k = e.getKey();
22794 this.execCmd('InsertText','\t');
22798 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22799 this.cleanUpPaste.defer(100, this);
22807 getAllAncestors: function()
22809 var p = this.getSelectedNode();
22812 a.push(p); // push blank onto stack..
22813 p = this.getParentElement();
22817 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22821 a.push(this.doc.body);
22825 lastSelNode : false,
22828 getSelection : function()
22830 this.assignDocWin();
22831 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22834 getSelectedNode: function()
22836 // this may only work on Gecko!!!
22838 // should we cache this!!!!
22843 var range = this.createRange(this.getSelection()).cloneRange();
22846 var parent = range.parentElement();
22848 var testRange = range.duplicate();
22849 testRange.moveToElementText(parent);
22850 if (testRange.inRange(range)) {
22853 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22856 parent = parent.parentElement;
22861 // is ancestor a text element.
22862 var ac = range.commonAncestorContainer;
22863 if (ac.nodeType == 3) {
22864 ac = ac.parentNode;
22867 var ar = ac.childNodes;
22870 var other_nodes = [];
22871 var has_other_nodes = false;
22872 for (var i=0;i<ar.length;i++) {
22873 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22876 // fullly contained node.
22878 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22883 // probably selected..
22884 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22885 other_nodes.push(ar[i]);
22889 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22894 has_other_nodes = true;
22896 if (!nodes.length && other_nodes.length) {
22897 nodes= other_nodes;
22899 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22905 createRange: function(sel)
22907 // this has strange effects when using with
22908 // top toolbar - not sure if it's a great idea.
22909 //this.editor.contentWindow.focus();
22910 if (typeof sel != "undefined") {
22912 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22914 return this.doc.createRange();
22917 return this.doc.createRange();
22920 getParentElement: function()
22923 this.assignDocWin();
22924 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22926 var range = this.createRange(sel);
22929 var p = range.commonAncestorContainer;
22930 while (p.nodeType == 3) { // text node
22941 * Range intersection.. the hard stuff...
22945 * [ -- selected range --- ]
22949 * if end is before start or hits it. fail.
22950 * if start is after end or hits it fail.
22952 * if either hits (but other is outside. - then it's not
22958 // @see http://www.thismuchiknow.co.uk/?p=64.
22959 rangeIntersectsNode : function(range, node)
22961 var nodeRange = node.ownerDocument.createRange();
22963 nodeRange.selectNode(node);
22965 nodeRange.selectNodeContents(node);
22968 var rangeStartRange = range.cloneRange();
22969 rangeStartRange.collapse(true);
22971 var rangeEndRange = range.cloneRange();
22972 rangeEndRange.collapse(false);
22974 var nodeStartRange = nodeRange.cloneRange();
22975 nodeStartRange.collapse(true);
22977 var nodeEndRange = nodeRange.cloneRange();
22978 nodeEndRange.collapse(false);
22980 return rangeStartRange.compareBoundaryPoints(
22981 Range.START_TO_START, nodeEndRange) == -1 &&
22982 rangeEndRange.compareBoundaryPoints(
22983 Range.START_TO_START, nodeStartRange) == 1;
22987 rangeCompareNode : function(range, node)
22989 var nodeRange = node.ownerDocument.createRange();
22991 nodeRange.selectNode(node);
22993 nodeRange.selectNodeContents(node);
22997 range.collapse(true);
22999 nodeRange.collapse(true);
23001 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23002 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
23004 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23006 var nodeIsBefore = ss == 1;
23007 var nodeIsAfter = ee == -1;
23009 if (nodeIsBefore && nodeIsAfter) {
23012 if (!nodeIsBefore && nodeIsAfter) {
23013 return 1; //right trailed.
23016 if (nodeIsBefore && !nodeIsAfter) {
23017 return 2; // left trailed.
23023 // private? - in a new class?
23024 cleanUpPaste : function()
23026 // cleans up the whole document..
23027 Roo.log('cleanuppaste');
23029 this.cleanUpChildren(this.doc.body);
23030 var clean = this.cleanWordChars(this.doc.body.innerHTML);
23031 if (clean != this.doc.body.innerHTML) {
23032 this.doc.body.innerHTML = clean;
23037 cleanWordChars : function(input) {// change the chars to hex code
23038 var he = Roo.HtmlEditorCore;
23040 var output = input;
23041 Roo.each(he.swapCodes, function(sw) {
23042 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23044 output = output.replace(swapper, sw[1]);
23051 cleanUpChildren : function (n)
23053 if (!n.childNodes.length) {
23056 for (var i = n.childNodes.length-1; i > -1 ; i--) {
23057 this.cleanUpChild(n.childNodes[i]);
23064 cleanUpChild : function (node)
23067 //console.log(node);
23068 if (node.nodeName == "#text") {
23069 // clean up silly Windows -- stuff?
23072 if (node.nodeName == "#comment") {
23073 node.parentNode.removeChild(node);
23074 // clean up silly Windows -- stuff?
23077 var lcname = node.tagName.toLowerCase();
23078 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23079 // whitelist of tags..
23081 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23083 node.parentNode.removeChild(node);
23088 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23090 // spans with no attributes - just remove them..
23091 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
23092 remove_keep_children = true;
23095 // remove <a name=....> as rendering on yahoo mailer is borked with this.
23096 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23098 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23099 // remove_keep_children = true;
23102 if (remove_keep_children) {
23103 this.cleanUpChildren(node);
23104 // inserts everything just before this node...
23105 while (node.childNodes.length) {
23106 var cn = node.childNodes[0];
23107 node.removeChild(cn);
23108 node.parentNode.insertBefore(cn, node);
23110 node.parentNode.removeChild(node);
23114 if (!node.attributes || !node.attributes.length) {
23119 this.cleanUpChildren(node);
23123 function cleanAttr(n,v)
23126 if (v.match(/^\./) || v.match(/^\//)) {
23129 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23132 if (v.match(/^#/)) {
23135 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23136 node.removeAttribute(n);
23140 var cwhite = this.cwhite;
23141 var cblack = this.cblack;
23143 function cleanStyle(n,v)
23145 if (v.match(/expression/)) { //XSS?? should we even bother..
23146 node.removeAttribute(n);
23150 var parts = v.split(/;/);
23153 Roo.each(parts, function(p) {
23154 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23158 var l = p.split(':').shift().replace(/\s+/g,'');
23159 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23161 if ( cwhite.length && cblack.indexOf(l) > -1) {
23162 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23163 //node.removeAttribute(n);
23167 // only allow 'c whitelisted system attributes'
23168 if ( cwhite.length && cwhite.indexOf(l) < 0) {
23169 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23170 //node.removeAttribute(n);
23180 if (clean.length) {
23181 node.setAttribute(n, clean.join(';'));
23183 node.removeAttribute(n);
23189 for (var i = node.attributes.length-1; i > -1 ; i--) {
23190 var a = node.attributes[i];
23193 if (a.name.toLowerCase().substr(0,2)=='on') {
23194 node.removeAttribute(a.name);
23197 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23198 node.removeAttribute(a.name);
23201 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23202 cleanAttr(a.name,a.value); // fixme..
23205 if (a.name == 'style') {
23206 cleanStyle(a.name,a.value);
23209 /// clean up MS crap..
23210 // tecnically this should be a list of valid class'es..
23213 if (a.name == 'class') {
23214 if (a.value.match(/^Mso/)) {
23215 node.removeAttribute('class');
23218 if (a.value.match(/^body$/)) {
23219 node.removeAttribute('class');
23230 this.cleanUpChildren(node);
23236 * Clean up MS wordisms...
23238 cleanWord : function(node)
23241 this.cleanWord(this.doc.body);
23246 node.nodeName == 'SPAN' &&
23247 !node.hasAttributes() &&
23248 node.childNodes.length == 1 &&
23249 node.firstChild.nodeName == "#text"
23251 var textNode = node.firstChild;
23252 node.removeChild(textNode);
23253 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
23254 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
23256 node.parentNode.insertBefore(textNode, node);
23257 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
23258 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
23260 node.parentNode.removeChild(node);
23263 if (node.nodeName == "#text") {
23264 // clean up silly Windows -- stuff?
23267 if (node.nodeName == "#comment") {
23268 node.parentNode.removeChild(node);
23269 // clean up silly Windows -- stuff?
23273 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23274 node.parentNode.removeChild(node);
23277 //Roo.log(node.tagName);
23278 // remove - but keep children..
23279 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
23280 //Roo.log('-- removed');
23281 while (node.childNodes.length) {
23282 var cn = node.childNodes[0];
23283 node.removeChild(cn);
23284 node.parentNode.insertBefore(cn, node);
23285 // move node to parent - and clean it..
23286 this.cleanWord(cn);
23288 node.parentNode.removeChild(node);
23289 /// no need to iterate chidlren = it's got none..
23290 //this.iterateChildren(node, this.cleanWord);
23294 if (node.className.length) {
23296 var cn = node.className.split(/\W+/);
23298 Roo.each(cn, function(cls) {
23299 if (cls.match(/Mso[a-zA-Z]+/)) {
23304 node.className = cna.length ? cna.join(' ') : '';
23306 node.removeAttribute("class");
23310 if (node.hasAttribute("lang")) {
23311 node.removeAttribute("lang");
23314 if (node.hasAttribute("style")) {
23316 var styles = node.getAttribute("style").split(";");
23318 Roo.each(styles, function(s) {
23319 if (!s.match(/:/)) {
23322 var kv = s.split(":");
23323 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23326 // what ever is left... we allow.
23329 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23330 if (!nstyle.length) {
23331 node.removeAttribute('style');
23334 this.iterateChildren(node, this.cleanWord);
23340 * iterateChildren of a Node, calling fn each time, using this as the scole..
23341 * @param {DomNode} node node to iterate children of.
23342 * @param {Function} fn method of this class to call on each item.
23344 iterateChildren : function(node, fn)
23346 if (!node.childNodes.length) {
23349 for (var i = node.childNodes.length-1; i > -1 ; i--) {
23350 fn.call(this, node.childNodes[i])
23356 * cleanTableWidths.
23358 * Quite often pasting from word etc.. results in tables with column and widths.
23359 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23362 cleanTableWidths : function(node)
23367 this.cleanTableWidths(this.doc.body);
23372 if (node.nodeName == "#text" || node.nodeName == "#comment") {
23375 Roo.log(node.tagName);
23376 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23377 this.iterateChildren(node, this.cleanTableWidths);
23380 if (node.hasAttribute('width')) {
23381 node.removeAttribute('width');
23385 if (node.hasAttribute("style")) {
23388 var styles = node.getAttribute("style").split(";");
23390 Roo.each(styles, function(s) {
23391 if (!s.match(/:/)) {
23394 var kv = s.split(":");
23395 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23398 // what ever is left... we allow.
23401 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23402 if (!nstyle.length) {
23403 node.removeAttribute('style');
23407 this.iterateChildren(node, this.cleanTableWidths);
23415 domToHTML : function(currentElement, depth, nopadtext) {
23417 depth = depth || 0;
23418 nopadtext = nopadtext || false;
23420 if (!currentElement) {
23421 return this.domToHTML(this.doc.body);
23424 //Roo.log(currentElement);
23426 var allText = false;
23427 var nodeName = currentElement.nodeName;
23428 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23430 if (nodeName == '#text') {
23432 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23437 if (nodeName != 'BODY') {
23440 // Prints the node tagName, such as <A>, <IMG>, etc
23443 for(i = 0; i < currentElement.attributes.length;i++) {
23445 var aname = currentElement.attributes.item(i).name;
23446 if (!currentElement.attributes.item(i).value.length) {
23449 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23452 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23461 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23464 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23469 // Traverse the tree
23471 var currentElementChild = currentElement.childNodes.item(i);
23472 var allText = true;
23473 var innerHTML = '';
23475 while (currentElementChild) {
23476 // Formatting code (indent the tree so it looks nice on the screen)
23477 var nopad = nopadtext;
23478 if (lastnode == 'SPAN') {
23482 if (currentElementChild.nodeName == '#text') {
23483 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23484 toadd = nopadtext ? toadd : toadd.trim();
23485 if (!nopad && toadd.length > 80) {
23486 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23488 innerHTML += toadd;
23491 currentElementChild = currentElement.childNodes.item(i);
23497 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23499 // Recursively traverse the tree structure of the child node
23500 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23501 lastnode = currentElementChild.nodeName;
23503 currentElementChild=currentElement.childNodes.item(i);
23509 // The remaining code is mostly for formatting the tree
23510 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23515 ret+= "</"+tagName+">";
23521 applyBlacklists : function()
23523 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23524 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23528 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23529 if (b.indexOf(tag) > -1) {
23532 this.white.push(tag);
23536 Roo.each(w, function(tag) {
23537 if (b.indexOf(tag) > -1) {
23540 if (this.white.indexOf(tag) > -1) {
23543 this.white.push(tag);
23548 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23549 if (w.indexOf(tag) > -1) {
23552 this.black.push(tag);
23556 Roo.each(b, function(tag) {
23557 if (w.indexOf(tag) > -1) {
23560 if (this.black.indexOf(tag) > -1) {
23563 this.black.push(tag);
23568 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23569 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23573 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23574 if (b.indexOf(tag) > -1) {
23577 this.cwhite.push(tag);
23581 Roo.each(w, function(tag) {
23582 if (b.indexOf(tag) > -1) {
23585 if (this.cwhite.indexOf(tag) > -1) {
23588 this.cwhite.push(tag);
23593 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23594 if (w.indexOf(tag) > -1) {
23597 this.cblack.push(tag);
23601 Roo.each(b, function(tag) {
23602 if (w.indexOf(tag) > -1) {
23605 if (this.cblack.indexOf(tag) > -1) {
23608 this.cblack.push(tag);
23613 setStylesheets : function(stylesheets)
23615 if(typeof(stylesheets) == 'string'){
23616 Roo.get(this.iframe.contentDocument.head).createChild({
23618 rel : 'stylesheet',
23627 Roo.each(stylesheets, function(s) {
23632 Roo.get(_this.iframe.contentDocument.head).createChild({
23634 rel : 'stylesheet',
23643 removeStylesheets : function()
23647 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23652 setStyle : function(style)
23654 Roo.get(this.iframe.contentDocument.head).createChild({
23663 // hide stuff that is not compatible
23677 * @event specialkey
23681 * @cfg {String} fieldClass @hide
23684 * @cfg {String} focusClass @hide
23687 * @cfg {String} autoCreate @hide
23690 * @cfg {String} inputType @hide
23693 * @cfg {String} invalidClass @hide
23696 * @cfg {String} invalidText @hide
23699 * @cfg {String} msgFx @hide
23702 * @cfg {String} validateOnBlur @hide
23706 Roo.HtmlEditorCore.white = [
23707 'area', 'br', 'img', 'input', 'hr', 'wbr',
23709 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23710 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23711 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23712 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23713 'table', 'ul', 'xmp',
23715 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23718 'dir', 'menu', 'ol', 'ul', 'dl',
23724 Roo.HtmlEditorCore.black = [
23725 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23727 'base', 'basefont', 'bgsound', 'blink', 'body',
23728 'frame', 'frameset', 'head', 'html', 'ilayer',
23729 'iframe', 'layer', 'link', 'meta', 'object',
23730 'script', 'style' ,'title', 'xml' // clean later..
23732 Roo.HtmlEditorCore.clean = [
23733 'script', 'style', 'title', 'xml'
23735 Roo.HtmlEditorCore.remove = [
23740 Roo.HtmlEditorCore.ablack = [
23744 Roo.HtmlEditorCore.aclean = [
23745 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23749 Roo.HtmlEditorCore.pwhite= [
23750 'http', 'https', 'mailto'
23753 // white listed style attributes.
23754 Roo.HtmlEditorCore.cwhite= [
23755 // 'text-align', /// default is to allow most things..
23761 // black listed style attributes.
23762 Roo.HtmlEditorCore.cblack= [
23763 // 'font-size' -- this can be set by the project
23767 Roo.HtmlEditorCore.swapCodes =[
23786 * @class Roo.bootstrap.HtmlEditor
23787 * @extends Roo.bootstrap.TextArea
23788 * Bootstrap HtmlEditor class
23791 * Create a new HtmlEditor
23792 * @param {Object} config The config object
23795 Roo.bootstrap.HtmlEditor = function(config){
23796 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23797 if (!this.toolbars) {
23798 this.toolbars = [];
23801 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23804 * @event initialize
23805 * Fires when the editor is fully initialized (including the iframe)
23806 * @param {HtmlEditor} this
23811 * Fires when the editor is first receives the focus. Any insertion must wait
23812 * until after this event.
23813 * @param {HtmlEditor} this
23817 * @event beforesync
23818 * Fires before the textarea is updated with content from the editor iframe. Return false
23819 * to cancel the sync.
23820 * @param {HtmlEditor} this
23821 * @param {String} html
23825 * @event beforepush
23826 * Fires before the iframe editor is updated with content from the textarea. Return false
23827 * to cancel the push.
23828 * @param {HtmlEditor} this
23829 * @param {String} html
23834 * Fires when the textarea is updated with content from the editor iframe.
23835 * @param {HtmlEditor} this
23836 * @param {String} html
23841 * Fires when the iframe editor is updated with content from the textarea.
23842 * @param {HtmlEditor} this
23843 * @param {String} html
23847 * @event editmodechange
23848 * Fires when the editor switches edit modes
23849 * @param {HtmlEditor} this
23850 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23852 editmodechange: true,
23854 * @event editorevent
23855 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23856 * @param {HtmlEditor} this
23860 * @event firstfocus
23861 * Fires when on first focus - needed by toolbars..
23862 * @param {HtmlEditor} this
23867 * Auto save the htmlEditor value as a file into Events
23868 * @param {HtmlEditor} this
23872 * @event savedpreview
23873 * preview the saved version of htmlEditor
23874 * @param {HtmlEditor} this
23881 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23885 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23890 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23895 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23900 * @cfg {Number} height (in pixels)
23904 * @cfg {Number} width (in pixels)
23909 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23912 stylesheets: false,
23917 // private properties
23918 validationEvent : false,
23920 initialized : false,
23923 onFocus : Roo.emptyFn,
23925 hideMode:'offsets',
23927 tbContainer : false,
23931 toolbarContainer :function() {
23932 return this.wrap.select('.x-html-editor-tb',true).first();
23936 * Protected method that will not generally be called directly. It
23937 * is called when the editor creates its toolbar. Override this method if you need to
23938 * add custom toolbar buttons.
23939 * @param {HtmlEditor} editor
23941 createToolbar : function(){
23942 Roo.log('renewing');
23943 Roo.log("create toolbars");
23945 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23946 this.toolbars[0].render(this.toolbarContainer());
23950 // if (!editor.toolbars || !editor.toolbars.length) {
23951 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23954 // for (var i =0 ; i < editor.toolbars.length;i++) {
23955 // editor.toolbars[i] = Roo.factory(
23956 // typeof(editor.toolbars[i]) == 'string' ?
23957 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23958 // Roo.bootstrap.HtmlEditor);
23959 // editor.toolbars[i].init(editor);
23965 onRender : function(ct, position)
23967 // Roo.log("Call onRender: " + this.xtype);
23969 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23971 this.wrap = this.inputEl().wrap({
23972 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23975 this.editorcore.onRender(ct, position);
23977 if (this.resizable) {
23978 this.resizeEl = new Roo.Resizable(this.wrap, {
23982 minHeight : this.height,
23983 height: this.height,
23984 handles : this.resizable,
23987 resize : function(r, w, h) {
23988 _t.onResize(w,h); // -something
23994 this.createToolbar(this);
23997 if(!this.width && this.resizable){
23998 this.setSize(this.wrap.getSize());
24000 if (this.resizeEl) {
24001 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24002 // should trigger onReize..
24008 onResize : function(w, h)
24010 Roo.log('resize: ' +w + ',' + h );
24011 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
24015 if(this.inputEl() ){
24016 if(typeof w == 'number'){
24017 var aw = w - this.wrap.getFrameWidth('lr');
24018 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
24021 if(typeof h == 'number'){
24022 var tbh = -11; // fixme it needs to tool bar size!
24023 for (var i =0; i < this.toolbars.length;i++) {
24024 // fixme - ask toolbars for heights?
24025 tbh += this.toolbars[i].el.getHeight();
24026 //if (this.toolbars[i].footer) {
24027 // tbh += this.toolbars[i].footer.el.getHeight();
24035 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24036 ah -= 5; // knock a few pixes off for look..
24037 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24041 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24042 this.editorcore.onResize(ew,eh);
24047 * Toggles the editor between standard and source edit mode.
24048 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24050 toggleSourceEdit : function(sourceEditMode)
24052 this.editorcore.toggleSourceEdit(sourceEditMode);
24054 if(this.editorcore.sourceEditMode){
24055 Roo.log('editor - showing textarea');
24058 // Roo.log(this.syncValue());
24060 this.inputEl().removeClass(['hide', 'x-hidden']);
24061 this.inputEl().dom.removeAttribute('tabIndex');
24062 this.inputEl().focus();
24064 Roo.log('editor - hiding textarea');
24066 // Roo.log(this.pushValue());
24069 this.inputEl().addClass(['hide', 'x-hidden']);
24070 this.inputEl().dom.setAttribute('tabIndex', -1);
24071 //this.deferFocus();
24074 if(this.resizable){
24075 this.setSize(this.wrap.getSize());
24078 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24081 // private (for BoxComponent)
24082 adjustSize : Roo.BoxComponent.prototype.adjustSize,
24084 // private (for BoxComponent)
24085 getResizeEl : function(){
24089 // private (for BoxComponent)
24090 getPositionEl : function(){
24095 initEvents : function(){
24096 this.originalValue = this.getValue();
24100 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24103 // markInvalid : Roo.emptyFn,
24105 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24108 // clearInvalid : Roo.emptyFn,
24110 setValue : function(v){
24111 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24112 this.editorcore.pushValue();
24117 deferFocus : function(){
24118 this.focus.defer(10, this);
24122 focus : function(){
24123 this.editorcore.focus();
24129 onDestroy : function(){
24135 for (var i =0; i < this.toolbars.length;i++) {
24136 // fixme - ask toolbars for heights?
24137 this.toolbars[i].onDestroy();
24140 this.wrap.dom.innerHTML = '';
24141 this.wrap.remove();
24146 onFirstFocus : function(){
24147 //Roo.log("onFirstFocus");
24148 this.editorcore.onFirstFocus();
24149 for (var i =0; i < this.toolbars.length;i++) {
24150 this.toolbars[i].onFirstFocus();
24156 syncValue : function()
24158 this.editorcore.syncValue();
24161 pushValue : function()
24163 this.editorcore.pushValue();
24167 // hide stuff that is not compatible
24181 * @event specialkey
24185 * @cfg {String} fieldClass @hide
24188 * @cfg {String} focusClass @hide
24191 * @cfg {String} autoCreate @hide
24194 * @cfg {String} inputType @hide
24198 * @cfg {String} invalidText @hide
24201 * @cfg {String} msgFx @hide
24204 * @cfg {String} validateOnBlur @hide
24213 Roo.namespace('Roo.bootstrap.htmleditor');
24215 * @class Roo.bootstrap.HtmlEditorToolbar1
24221 new Roo.bootstrap.HtmlEditor({
24224 new Roo.bootstrap.HtmlEditorToolbar1({
24225 disable : { fonts: 1 , format: 1, ..., ... , ...],
24231 * @cfg {Object} disable List of elements to disable..
24232 * @cfg {Array} btns List of additional buttons.
24236 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24239 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24242 Roo.apply(this, config);
24244 // default disabled, based on 'good practice'..
24245 this.disable = this.disable || {};
24246 Roo.applyIf(this.disable, {
24249 specialElements : true
24251 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24253 this.editor = config.editor;
24254 this.editorcore = config.editor.editorcore;
24256 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24258 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24259 // dont call parent... till later.
24261 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
24266 editorcore : false,
24271 "h1","h2","h3","h4","h5","h6",
24273 "abbr", "acronym", "address", "cite", "samp", "var",
24277 onRender : function(ct, position)
24279 // Roo.log("Call onRender: " + this.xtype);
24281 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24283 this.el.dom.style.marginBottom = '0';
24285 var editorcore = this.editorcore;
24286 var editor= this.editor;
24289 var btn = function(id,cmd , toggle, handler, html){
24291 var event = toggle ? 'toggle' : 'click';
24296 xns: Roo.bootstrap,
24300 enableToggle:toggle !== false,
24302 pressed : toggle ? false : null,
24305 a.listeners[toggle ? 'toggle' : 'click'] = function() {
24306 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
24312 // var cb_box = function...
24317 xns: Roo.bootstrap,
24322 xns: Roo.bootstrap,
24326 Roo.each(this.formats, function(f) {
24327 style.menu.items.push({
24329 xns: Roo.bootstrap,
24330 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24335 editorcore.insertTag(this.tagname);
24342 children.push(style);
24344 btn('bold',false,true);
24345 btn('italic',false,true);
24346 btn('align-left', 'justifyleft',true);
24347 btn('align-center', 'justifycenter',true);
24348 btn('align-right' , 'justifyright',true);
24349 btn('link', false, false, function(btn) {
24350 //Roo.log("create link?");
24351 var url = prompt(this.createLinkText, this.defaultLinkValue);
24352 if(url && url != 'http:/'+'/'){
24353 this.editorcore.relayCmd('createlink', url);
24356 btn('list','insertunorderedlist',true);
24357 btn('pencil', false,true, function(btn){
24359 this.toggleSourceEdit(btn.pressed);
24362 if (this.editor.btns.length > 0) {
24363 for (var i = 0; i<this.editor.btns.length; i++) {
24364 children.push(this.editor.btns[i]);
24372 xns: Roo.bootstrap,
24377 xns: Roo.bootstrap,
24382 cog.menu.items.push({
24384 xns: Roo.bootstrap,
24385 html : Clean styles,
24390 editorcore.insertTag(this.tagname);
24399 this.xtype = 'NavSimplebar';
24401 for(var i=0;i< children.length;i++) {
24403 this.buttons.add(this.addxtypeChild(children[i]));
24407 editor.on('editorevent', this.updateToolbar, this);
24409 onBtnClick : function(id)
24411 this.editorcore.relayCmd(id);
24412 this.editorcore.focus();
24416 * Protected method that will not generally be called directly. It triggers
24417 * a toolbar update by reading the markup state of the current selection in the editor.
24419 updateToolbar: function(){
24421 if(!this.editorcore.activated){
24422 this.editor.onFirstFocus(); // is this neeed?
24426 var btns = this.buttons;
24427 var doc = this.editorcore.doc;
24428 btns.get('bold').setActive(doc.queryCommandState('bold'));
24429 btns.get('italic').setActive(doc.queryCommandState('italic'));
24430 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24432 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24433 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24434 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24436 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24437 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24440 var ans = this.editorcore.getAllAncestors();
24441 if (this.formatCombo) {
24444 var store = this.formatCombo.store;
24445 this.formatCombo.setValue("");
24446 for (var i =0; i < ans.length;i++) {
24447 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24449 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24457 // hides menus... - so this cant be on a menu...
24458 Roo.bootstrap.MenuMgr.hideAll();
24460 Roo.bootstrap.MenuMgr.hideAll();
24461 //this.editorsyncValue();
24463 onFirstFocus: function() {
24464 this.buttons.each(function(item){
24468 toggleSourceEdit : function(sourceEditMode){
24471 if(sourceEditMode){
24472 Roo.log("disabling buttons");
24473 this.buttons.each( function(item){
24474 if(item.cmd != 'pencil'){
24480 Roo.log("enabling buttons");
24481 if(this.editorcore.initialized){
24482 this.buttons.each( function(item){
24488 Roo.log("calling toggole on editor");
24489 // tell the editor that it's been pressed..
24490 this.editor.toggleSourceEdit(sourceEditMode);
24500 * @class Roo.bootstrap.Table.AbstractSelectionModel
24501 * @extends Roo.util.Observable
24502 * Abstract base class for grid SelectionModels. It provides the interface that should be
24503 * implemented by descendant classes. This class should not be directly instantiated.
24506 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24507 this.locked = false;
24508 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24512 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24513 /** @ignore Called by the grid automatically. Do not call directly. */
24514 init : function(grid){
24520 * Locks the selections.
24523 this.locked = true;
24527 * Unlocks the selections.
24529 unlock : function(){
24530 this.locked = false;
24534 * Returns true if the selections are locked.
24535 * @return {Boolean}
24537 isLocked : function(){
24538 return this.locked;
24542 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24543 * @class Roo.bootstrap.Table.RowSelectionModel
24544 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24545 * It supports multiple selections and keyboard selection/navigation.
24547 * @param {Object} config
24550 Roo.bootstrap.Table.RowSelectionModel = function(config){
24551 Roo.apply(this, config);
24552 this.selections = new Roo.util.MixedCollection(false, function(o){
24557 this.lastActive = false;
24561 * @event selectionchange
24562 * Fires when the selection changes
24563 * @param {SelectionModel} this
24565 "selectionchange" : true,
24567 * @event afterselectionchange
24568 * Fires after the selection changes (eg. by key press or clicking)
24569 * @param {SelectionModel} this
24571 "afterselectionchange" : true,
24573 * @event beforerowselect
24574 * Fires when a row is selected being selected, return false to cancel.
24575 * @param {SelectionModel} this
24576 * @param {Number} rowIndex The selected index
24577 * @param {Boolean} keepExisting False if other selections will be cleared
24579 "beforerowselect" : true,
24582 * Fires when a row is selected.
24583 * @param {SelectionModel} this
24584 * @param {Number} rowIndex The selected index
24585 * @param {Roo.data.Record} r The record
24587 "rowselect" : true,
24589 * @event rowdeselect
24590 * Fires when a row is deselected.
24591 * @param {SelectionModel} this
24592 * @param {Number} rowIndex The selected index
24594 "rowdeselect" : true
24596 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24597 this.locked = false;
24600 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24602 * @cfg {Boolean} singleSelect
24603 * True to allow selection of only one row at a time (defaults to false)
24605 singleSelect : false,
24608 initEvents : function()
24611 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24612 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24613 //}else{ // allow click to work like normal
24614 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24616 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24617 this.grid.on("rowclick", this.handleMouseDown, this);
24619 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24620 "up" : function(e){
24622 this.selectPrevious(e.shiftKey);
24623 }else if(this.last !== false && this.lastActive !== false){
24624 var last = this.last;
24625 this.selectRange(this.last, this.lastActive-1);
24626 this.grid.getView().focusRow(this.lastActive);
24627 if(last !== false){
24631 this.selectFirstRow();
24633 this.fireEvent("afterselectionchange", this);
24635 "down" : function(e){
24637 this.selectNext(e.shiftKey);
24638 }else if(this.last !== false && this.lastActive !== false){
24639 var last = this.last;
24640 this.selectRange(this.last, this.lastActive+1);
24641 this.grid.getView().focusRow(this.lastActive);
24642 if(last !== false){
24646 this.selectFirstRow();
24648 this.fireEvent("afterselectionchange", this);
24652 this.grid.store.on('load', function(){
24653 this.selections.clear();
24656 var view = this.grid.view;
24657 view.on("refresh", this.onRefresh, this);
24658 view.on("rowupdated", this.onRowUpdated, this);
24659 view.on("rowremoved", this.onRemove, this);
24664 onRefresh : function()
24666 var ds = this.grid.store, i, v = this.grid.view;
24667 var s = this.selections;
24668 s.each(function(r){
24669 if((i = ds.indexOfId(r.id)) != -1){
24678 onRemove : function(v, index, r){
24679 this.selections.remove(r);
24683 onRowUpdated : function(v, index, r){
24684 if(this.isSelected(r)){
24685 v.onRowSelect(index);
24691 * @param {Array} records The records to select
24692 * @param {Boolean} keepExisting (optional) True to keep existing selections
24694 selectRecords : function(records, keepExisting)
24697 this.clearSelections();
24699 var ds = this.grid.store;
24700 for(var i = 0, len = records.length; i < len; i++){
24701 this.selectRow(ds.indexOf(records[i]), true);
24706 * Gets the number of selected rows.
24709 getCount : function(){
24710 return this.selections.length;
24714 * Selects the first row in the grid.
24716 selectFirstRow : function(){
24721 * Select the last row.
24722 * @param {Boolean} keepExisting (optional) True to keep existing selections
24724 selectLastRow : function(keepExisting){
24725 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24726 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24730 * Selects the row immediately following the last selected row.
24731 * @param {Boolean} keepExisting (optional) True to keep existing selections
24733 selectNext : function(keepExisting)
24735 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24736 this.selectRow(this.last+1, keepExisting);
24737 this.grid.getView().focusRow(this.last);
24742 * Selects the row that precedes the last selected row.
24743 * @param {Boolean} keepExisting (optional) True to keep existing selections
24745 selectPrevious : function(keepExisting){
24747 this.selectRow(this.last-1, keepExisting);
24748 this.grid.getView().focusRow(this.last);
24753 * Returns the selected records
24754 * @return {Array} Array of selected records
24756 getSelections : function(){
24757 return [].concat(this.selections.items);
24761 * Returns the first selected record.
24764 getSelected : function(){
24765 return this.selections.itemAt(0);
24770 * Clears all selections.
24772 clearSelections : function(fast)
24778 var ds = this.grid.store;
24779 var s = this.selections;
24780 s.each(function(r){
24781 this.deselectRow(ds.indexOfId(r.id));
24785 this.selections.clear();
24792 * Selects all rows.
24794 selectAll : function(){
24798 this.selections.clear();
24799 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24800 this.selectRow(i, true);
24805 * Returns True if there is a selection.
24806 * @return {Boolean}
24808 hasSelection : function(){
24809 return this.selections.length > 0;
24813 * Returns True if the specified row is selected.
24814 * @param {Number/Record} record The record or index of the record to check
24815 * @return {Boolean}
24817 isSelected : function(index){
24818 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24819 return (r && this.selections.key(r.id) ? true : false);
24823 * Returns True if the specified record id is selected.
24824 * @param {String} id The id of record to check
24825 * @return {Boolean}
24827 isIdSelected : function(id){
24828 return (this.selections.key(id) ? true : false);
24833 handleMouseDBClick : function(e, t){
24837 handleMouseDown : function(e, t)
24839 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24840 if(this.isLocked() || rowIndex < 0 ){
24843 if(e.shiftKey && this.last !== false){
24844 var last = this.last;
24845 this.selectRange(last, rowIndex, e.ctrlKey);
24846 this.last = last; // reset the last
24850 var isSelected = this.isSelected(rowIndex);
24851 //Roo.log("select row:" + rowIndex);
24853 this.deselectRow(rowIndex);
24855 this.selectRow(rowIndex, true);
24859 if(e.button !== 0 && isSelected){
24860 alert('rowIndex 2: ' + rowIndex);
24861 view.focusRow(rowIndex);
24862 }else if(e.ctrlKey && isSelected){
24863 this.deselectRow(rowIndex);
24864 }else if(!isSelected){
24865 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24866 view.focusRow(rowIndex);
24870 this.fireEvent("afterselectionchange", this);
24873 handleDragableRowClick : function(grid, rowIndex, e)
24875 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24876 this.selectRow(rowIndex, false);
24877 grid.view.focusRow(rowIndex);
24878 this.fireEvent("afterselectionchange", this);
24883 * Selects multiple rows.
24884 * @param {Array} rows Array of the indexes of the row to select
24885 * @param {Boolean} keepExisting (optional) True to keep existing selections
24887 selectRows : function(rows, keepExisting){
24889 this.clearSelections();
24891 for(var i = 0, len = rows.length; i < len; i++){
24892 this.selectRow(rows[i], true);
24897 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24898 * @param {Number} startRow The index of the first row in the range
24899 * @param {Number} endRow The index of the last row in the range
24900 * @param {Boolean} keepExisting (optional) True to retain existing selections
24902 selectRange : function(startRow, endRow, keepExisting){
24907 this.clearSelections();
24909 if(startRow <= endRow){
24910 for(var i = startRow; i <= endRow; i++){
24911 this.selectRow(i, true);
24914 for(var i = startRow; i >= endRow; i--){
24915 this.selectRow(i, true);
24921 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24922 * @param {Number} startRow The index of the first row in the range
24923 * @param {Number} endRow The index of the last row in the range
24925 deselectRange : function(startRow, endRow, preventViewNotify){
24929 for(var i = startRow; i <= endRow; i++){
24930 this.deselectRow(i, preventViewNotify);
24936 * @param {Number} row The index of the row to select
24937 * @param {Boolean} keepExisting (optional) True to keep existing selections
24939 selectRow : function(index, keepExisting, preventViewNotify)
24941 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24944 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24945 if(!keepExisting || this.singleSelect){
24946 this.clearSelections();
24949 var r = this.grid.store.getAt(index);
24950 //console.log('selectRow - record id :' + r.id);
24952 this.selections.add(r);
24953 this.last = this.lastActive = index;
24954 if(!preventViewNotify){
24955 var proxy = new Roo.Element(
24956 this.grid.getRowDom(index)
24958 proxy.addClass('bg-info info');
24960 this.fireEvent("rowselect", this, index, r);
24961 this.fireEvent("selectionchange", this);
24967 * @param {Number} row The index of the row to deselect
24969 deselectRow : function(index, preventViewNotify)
24974 if(this.last == index){
24977 if(this.lastActive == index){
24978 this.lastActive = false;
24981 var r = this.grid.store.getAt(index);
24986 this.selections.remove(r);
24987 //.console.log('deselectRow - record id :' + r.id);
24988 if(!preventViewNotify){
24990 var proxy = new Roo.Element(
24991 this.grid.getRowDom(index)
24993 proxy.removeClass('bg-info info');
24995 this.fireEvent("rowdeselect", this, index);
24996 this.fireEvent("selectionchange", this);
25000 restoreLast : function(){
25002 this.last = this._last;
25007 acceptsNav : function(row, col, cm){
25008 return !cm.isHidden(col) && cm.isCellEditable(col, row);
25012 onEditorKey : function(field, e){
25013 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
25018 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
25020 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
25022 }else if(k == e.ENTER && !e.ctrlKey){
25026 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
25028 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25030 }else if(k == e.ESC){
25034 g.startEditing(newCell[0], newCell[1]);
25040 * Ext JS Library 1.1.1
25041 * Copyright(c) 2006-2007, Ext JS, LLC.
25043 * Originally Released Under LGPL - original licence link has changed is not relivant.
25046 * <script type="text/javascript">
25050 * @class Roo.bootstrap.PagingToolbar
25051 * @extends Roo.bootstrap.NavSimplebar
25052 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
25054 * Create a new PagingToolbar
25055 * @param {Object} config The config object
25056 * @param {Roo.data.Store} store
25058 Roo.bootstrap.PagingToolbar = function(config)
25060 // old args format still supported... - xtype is prefered..
25061 // created from xtype...
25063 this.ds = config.dataSource;
25065 if (config.store && !this.ds) {
25066 this.store= Roo.factory(config.store, Roo.data);
25067 this.ds = this.store;
25068 this.ds.xmodule = this.xmodule || false;
25071 this.toolbarItems = [];
25072 if (config.items) {
25073 this.toolbarItems = config.items;
25076 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
25081 this.bind(this.ds);
25084 if (Roo.bootstrap.version == 4) {
25085 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
25087 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
25092 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
25094 * @cfg {Roo.data.Store} dataSource
25095 * The underlying data store providing the paged data
25098 * @cfg {String/HTMLElement/Element} container
25099 * container The id or element that will contain the toolbar
25102 * @cfg {Boolean} displayInfo
25103 * True to display the displayMsg (defaults to false)
25106 * @cfg {Number} pageSize
25107 * The number of records to display per page (defaults to 20)
25111 * @cfg {String} displayMsg
25112 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25114 displayMsg : 'Displaying {0} - {1} of {2}',
25116 * @cfg {String} emptyMsg
25117 * The message to display when no records are found (defaults to "No data to display")
25119 emptyMsg : 'No data to display',
25121 * Customizable piece of the default paging text (defaults to "Page")
25124 beforePageText : "Page",
25126 * Customizable piece of the default paging text (defaults to "of %0")
25129 afterPageText : "of {0}",
25131 * Customizable piece of the default paging text (defaults to "First Page")
25134 firstText : "First Page",
25136 * Customizable piece of the default paging text (defaults to "Previous Page")
25139 prevText : "Previous Page",
25141 * Customizable piece of the default paging text (defaults to "Next Page")
25144 nextText : "Next Page",
25146 * Customizable piece of the default paging text (defaults to "Last Page")
25149 lastText : "Last Page",
25151 * Customizable piece of the default paging text (defaults to "Refresh")
25154 refreshText : "Refresh",
25158 onRender : function(ct, position)
25160 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25161 this.navgroup.parentId = this.id;
25162 this.navgroup.onRender(this.el, null);
25163 // add the buttons to the navgroup
25165 if(this.displayInfo){
25166 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25167 this.displayEl = this.el.select('.x-paging-info', true).first();
25168 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25169 // this.displayEl = navel.el.select('span',true).first();
25175 Roo.each(_this.buttons, function(e){ // this might need to use render????
25176 Roo.factory(e).render(_this.el);
25180 Roo.each(_this.toolbarItems, function(e) {
25181 _this.navgroup.addItem(e);
25185 this.first = this.navgroup.addItem({
25186 tooltip: this.firstText,
25187 cls: "prev btn-outline-secondary",
25188 html : ' <i class="fa fa-step-backward"></i>',
25190 preventDefault: true,
25191 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25194 this.prev = this.navgroup.addItem({
25195 tooltip: this.prevText,
25196 cls: "prev btn-outline-secondary",
25197 html : ' <i class="fa fa-backward"></i>',
25199 preventDefault: true,
25200 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
25202 //this.addSeparator();
25205 var field = this.navgroup.addItem( {
25207 cls : 'x-paging-position btn-outline-secondary',
25209 html : this.beforePageText +
25210 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25211 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
25214 this.field = field.el.select('input', true).first();
25215 this.field.on("keydown", this.onPagingKeydown, this);
25216 this.field.on("focus", function(){this.dom.select();});
25219 this.afterTextEl = field.el.select('.x-paging-after',true).first();
25220 //this.field.setHeight(18);
25221 //this.addSeparator();
25222 this.next = this.navgroup.addItem({
25223 tooltip: this.nextText,
25224 cls: "next btn-outline-secondary",
25225 html : ' <i class="fa fa-forward"></i>',
25227 preventDefault: true,
25228 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
25230 this.last = this.navgroup.addItem({
25231 tooltip: this.lastText,
25232 html : ' <i class="fa fa-step-forward"></i>',
25233 cls: "next btn-outline-secondary",
25235 preventDefault: true,
25236 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
25238 //this.addSeparator();
25239 this.loading = this.navgroup.addItem({
25240 tooltip: this.refreshText,
25241 cls: "btn-outline-secondary",
25242 html : ' <i class="fa fa-refresh"></i>',
25243 preventDefault: true,
25244 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25250 updateInfo : function(){
25251 if(this.displayEl){
25252 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25253 var msg = count == 0 ?
25257 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
25259 this.displayEl.update(msg);
25264 onLoad : function(ds, r, o)
25266 this.cursor = o.params.start ? o.params.start : 0;
25268 var d = this.getPageData(),
25273 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25274 this.field.dom.value = ap;
25275 this.first.setDisabled(ap == 1);
25276 this.prev.setDisabled(ap == 1);
25277 this.next.setDisabled(ap == ps);
25278 this.last.setDisabled(ap == ps);
25279 this.loading.enable();
25284 getPageData : function(){
25285 var total = this.ds.getTotalCount();
25288 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25289 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25294 onLoadError : function(){
25295 this.loading.enable();
25299 onPagingKeydown : function(e){
25300 var k = e.getKey();
25301 var d = this.getPageData();
25303 var v = this.field.dom.value, pageNum;
25304 if(!v || isNaN(pageNum = parseInt(v, 10))){
25305 this.field.dom.value = d.activePage;
25308 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25309 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25312 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))
25314 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25315 this.field.dom.value = pageNum;
25316 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25319 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25321 var v = this.field.dom.value, pageNum;
25322 var increment = (e.shiftKey) ? 10 : 1;
25323 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25326 if(!v || isNaN(pageNum = parseInt(v, 10))) {
25327 this.field.dom.value = d.activePage;
25330 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25332 this.field.dom.value = parseInt(v, 10) + increment;
25333 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25334 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25341 beforeLoad : function(){
25343 this.loading.disable();
25348 onClick : function(which){
25357 ds.load({params:{start: 0, limit: this.pageSize}});
25360 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25363 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25366 var total = ds.getTotalCount();
25367 var extra = total % this.pageSize;
25368 var lastStart = extra ? (total - extra) : total-this.pageSize;
25369 ds.load({params:{start: lastStart, limit: this.pageSize}});
25372 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25378 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25379 * @param {Roo.data.Store} store The data store to unbind
25381 unbind : function(ds){
25382 ds.un("beforeload", this.beforeLoad, this);
25383 ds.un("load", this.onLoad, this);
25384 ds.un("loadexception", this.onLoadError, this);
25385 ds.un("remove", this.updateInfo, this);
25386 ds.un("add", this.updateInfo, this);
25387 this.ds = undefined;
25391 * Binds the paging toolbar to the specified {@link Roo.data.Store}
25392 * @param {Roo.data.Store} store The data store to bind
25394 bind : function(ds){
25395 ds.on("beforeload", this.beforeLoad, this);
25396 ds.on("load", this.onLoad, this);
25397 ds.on("loadexception", this.onLoadError, this);
25398 ds.on("remove", this.updateInfo, this);
25399 ds.on("add", this.updateInfo, this);
25410 * @class Roo.bootstrap.MessageBar
25411 * @extends Roo.bootstrap.Component
25412 * Bootstrap MessageBar class
25413 * @cfg {String} html contents of the MessageBar
25414 * @cfg {String} weight (info | success | warning | danger) default info
25415 * @cfg {String} beforeClass insert the bar before the given class
25416 * @cfg {Boolean} closable (true | false) default false
25417 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25420 * Create a new Element
25421 * @param {Object} config The config object
25424 Roo.bootstrap.MessageBar = function(config){
25425 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25428 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25434 beforeClass: 'bootstrap-sticky-wrap',
25436 getAutoCreate : function(){
25440 cls: 'alert alert-dismissable alert-' + this.weight,
25445 html: this.html || ''
25451 cfg.cls += ' alert-messages-fixed';
25465 onRender : function(ct, position)
25467 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25470 var cfg = Roo.apply({}, this.getAutoCreate());
25474 cfg.cls += ' ' + this.cls;
25477 cfg.style = this.style;
25479 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25481 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25484 this.el.select('>button.close').on('click', this.hide, this);
25490 if (!this.rendered) {
25496 this.fireEvent('show', this);
25502 if (!this.rendered) {
25508 this.fireEvent('hide', this);
25511 update : function()
25513 // var e = this.el.dom.firstChild;
25515 // if(this.closable){
25516 // e = e.nextSibling;
25519 // e.data = this.html || '';
25521 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25537 * @class Roo.bootstrap.Graph
25538 * @extends Roo.bootstrap.Component
25539 * Bootstrap Graph class
25543 @cfg {String} graphtype bar | vbar | pie
25544 @cfg {number} g_x coodinator | centre x (pie)
25545 @cfg {number} g_y coodinator | centre y (pie)
25546 @cfg {number} g_r radius (pie)
25547 @cfg {number} g_height height of the chart (respected by all elements in the set)
25548 @cfg {number} g_width width of the chart (respected by all elements in the set)
25549 @cfg {Object} title The title of the chart
25552 -opts (object) options for the chart
25554 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25555 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25557 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.
25558 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25560 o stretch (boolean)
25562 -opts (object) options for the pie
25565 o startAngle (number)
25566 o endAngle (number)
25570 * Create a new Input
25571 * @param {Object} config The config object
25574 Roo.bootstrap.Graph = function(config){
25575 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25581 * The img click event for the img.
25582 * @param {Roo.EventObject} e
25588 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25599 //g_colors: this.colors,
25606 getAutoCreate : function(){
25617 onRender : function(ct,position){
25620 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25622 if (typeof(Raphael) == 'undefined') {
25623 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25627 this.raphael = Raphael(this.el.dom);
25629 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25630 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25631 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25632 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25634 r.text(160, 10, "Single Series Chart").attr(txtattr);
25635 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25636 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25637 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25639 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25640 r.barchart(330, 10, 300, 220, data1);
25641 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25642 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25645 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25646 // r.barchart(30, 30, 560, 250, xdata, {
25647 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25648 // axis : "0 0 1 1",
25649 // axisxlabels : xdata
25650 // //yvalues : cols,
25653 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25655 // this.load(null,xdata,{
25656 // axis : "0 0 1 1",
25657 // axisxlabels : xdata
25662 load : function(graphtype,xdata,opts)
25664 this.raphael.clear();
25666 graphtype = this.graphtype;
25671 var r = this.raphael,
25672 fin = function () {
25673 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25675 fout = function () {
25676 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25678 pfin = function() {
25679 this.sector.stop();
25680 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25683 this.label[0].stop();
25684 this.label[0].attr({ r: 7.5 });
25685 this.label[1].attr({ "font-weight": 800 });
25688 pfout = function() {
25689 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25692 this.label[0].animate({ r: 5 }, 500, "bounce");
25693 this.label[1].attr({ "font-weight": 400 });
25699 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25702 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25705 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25706 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25708 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25715 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25720 setTitle: function(o)
25725 initEvents: function() {
25728 this.el.on('click', this.onClick, this);
25732 onClick : function(e)
25734 Roo.log('img onclick');
25735 this.fireEvent('click', this, e);
25747 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25750 * @class Roo.bootstrap.dash.NumberBox
25751 * @extends Roo.bootstrap.Component
25752 * Bootstrap NumberBox class
25753 * @cfg {String} headline Box headline
25754 * @cfg {String} content Box content
25755 * @cfg {String} icon Box icon
25756 * @cfg {String} footer Footer text
25757 * @cfg {String} fhref Footer href
25760 * Create a new NumberBox
25761 * @param {Object} config The config object
25765 Roo.bootstrap.dash.NumberBox = function(config){
25766 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25770 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25779 getAutoCreate : function(){
25783 cls : 'small-box ',
25791 cls : 'roo-headline',
25792 html : this.headline
25796 cls : 'roo-content',
25797 html : this.content
25811 cls : 'ion ' + this.icon
25820 cls : 'small-box-footer',
25821 href : this.fhref || '#',
25825 cfg.cn.push(footer);
25832 onRender : function(ct,position){
25833 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25840 setHeadline: function (value)
25842 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25845 setFooter: function (value, href)
25847 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25850 this.el.select('a.small-box-footer',true).first().attr('href', href);
25855 setContent: function (value)
25857 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25860 initEvents: function()
25874 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25877 * @class Roo.bootstrap.dash.TabBox
25878 * @extends Roo.bootstrap.Component
25879 * Bootstrap TabBox class
25880 * @cfg {String} title Title of the TabBox
25881 * @cfg {String} icon Icon of the TabBox
25882 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25883 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25886 * Create a new TabBox
25887 * @param {Object} config The config object
25891 Roo.bootstrap.dash.TabBox = function(config){
25892 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25897 * When a pane is added
25898 * @param {Roo.bootstrap.dash.TabPane} pane
25902 * @event activatepane
25903 * When a pane is activated
25904 * @param {Roo.bootstrap.dash.TabPane} pane
25906 "activatepane" : true
25914 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25919 tabScrollable : false,
25921 getChildContainer : function()
25923 return this.el.select('.tab-content', true).first();
25926 getAutoCreate : function(){
25930 cls: 'pull-left header',
25938 cls: 'fa ' + this.icon
25944 cls: 'nav nav-tabs pull-right',
25950 if(this.tabScrollable){
25957 cls: 'nav nav-tabs pull-right',
25968 cls: 'nav-tabs-custom',
25973 cls: 'tab-content no-padding',
25981 initEvents : function()
25983 //Roo.log('add add pane handler');
25984 this.on('addpane', this.onAddPane, this);
25987 * Updates the box title
25988 * @param {String} html to set the title to.
25990 setTitle : function(value)
25992 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25994 onAddPane : function(pane)
25996 this.panes.push(pane);
25997 //Roo.log('addpane');
25999 // tabs are rendere left to right..
26000 if(!this.showtabs){
26004 var ctr = this.el.select('.nav-tabs', true).first();
26007 var existing = ctr.select('.nav-tab',true);
26008 var qty = existing.getCount();;
26011 var tab = ctr.createChild({
26013 cls : 'nav-tab' + (qty ? '' : ' active'),
26021 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
26024 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
26026 pane.el.addClass('active');
26031 onTabClick : function(ev,un,ob,pane)
26033 //Roo.log('tab - prev default');
26034 ev.preventDefault();
26037 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26038 pane.tab.addClass('active');
26039 //Roo.log(pane.title);
26040 this.getChildContainer().select('.tab-pane',true).removeClass('active');
26041 // technically we should have a deactivate event.. but maybe add later.
26042 // and it should not de-activate the selected tab...
26043 this.fireEvent('activatepane', pane);
26044 pane.el.addClass('active');
26045 pane.fireEvent('activate');
26050 getActivePane : function()
26053 Roo.each(this.panes, function(p) {
26054 if(p.el.hasClass('active')){
26075 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26077 * @class Roo.bootstrap.TabPane
26078 * @extends Roo.bootstrap.Component
26079 * Bootstrap TabPane class
26080 * @cfg {Boolean} active (false | true) Default false
26081 * @cfg {String} title title of panel
26085 * Create a new TabPane
26086 * @param {Object} config The config object
26089 Roo.bootstrap.dash.TabPane = function(config){
26090 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
26096 * When a pane is activated
26097 * @param {Roo.bootstrap.dash.TabPane} pane
26104 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
26109 // the tabBox that this is attached to.
26112 getAutoCreate : function()
26120 cfg.cls += ' active';
26125 initEvents : function()
26127 //Roo.log('trigger add pane handler');
26128 this.parent().fireEvent('addpane', this)
26132 * Updates the tab title
26133 * @param {String} html to set the title to.
26135 setTitle: function(str)
26141 this.tab.select('a', true).first().dom.innerHTML = str;
26158 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26161 * @class Roo.bootstrap.menu.Menu
26162 * @extends Roo.bootstrap.Component
26163 * Bootstrap Menu class - container for Menu
26164 * @cfg {String} html Text of the menu
26165 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26166 * @cfg {String} icon Font awesome icon
26167 * @cfg {String} pos Menu align to (top | bottom) default bottom
26171 * Create a new Menu
26172 * @param {Object} config The config object
26176 Roo.bootstrap.menu.Menu = function(config){
26177 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26181 * @event beforeshow
26182 * Fires before this menu is displayed
26183 * @param {Roo.bootstrap.menu.Menu} this
26187 * @event beforehide
26188 * Fires before this menu is hidden
26189 * @param {Roo.bootstrap.menu.Menu} this
26194 * Fires after this menu is displayed
26195 * @param {Roo.bootstrap.menu.Menu} this
26200 * Fires after this menu is hidden
26201 * @param {Roo.bootstrap.menu.Menu} this
26206 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26207 * @param {Roo.bootstrap.menu.Menu} this
26208 * @param {Roo.EventObject} e
26215 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
26219 weight : 'default',
26224 getChildContainer : function() {
26225 if(this.isSubMenu){
26229 return this.el.select('ul.dropdown-menu', true).first();
26232 getAutoCreate : function()
26237 cls : 'roo-menu-text',
26245 cls : 'fa ' + this.icon
26256 cls : 'dropdown-button btn btn-' + this.weight,
26261 cls : 'dropdown-toggle btn btn-' + this.weight,
26271 cls : 'dropdown-menu'
26277 if(this.pos == 'top'){
26278 cfg.cls += ' dropup';
26281 if(this.isSubMenu){
26284 cls : 'dropdown-menu'
26291 onRender : function(ct, position)
26293 this.isSubMenu = ct.hasClass('dropdown-submenu');
26295 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26298 initEvents : function()
26300 if(this.isSubMenu){
26304 this.hidden = true;
26306 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26307 this.triggerEl.on('click', this.onTriggerPress, this);
26309 this.buttonEl = this.el.select('button.dropdown-button', true).first();
26310 this.buttonEl.on('click', this.onClick, this);
26316 if(this.isSubMenu){
26320 return this.el.select('ul.dropdown-menu', true).first();
26323 onClick : function(e)
26325 this.fireEvent("click", this, e);
26328 onTriggerPress : function(e)
26330 if (this.isVisible()) {
26337 isVisible : function(){
26338 return !this.hidden;
26343 this.fireEvent("beforeshow", this);
26345 this.hidden = false;
26346 this.el.addClass('open');
26348 Roo.get(document).on("mouseup", this.onMouseUp, this);
26350 this.fireEvent("show", this);
26357 this.fireEvent("beforehide", this);
26359 this.hidden = true;
26360 this.el.removeClass('open');
26362 Roo.get(document).un("mouseup", this.onMouseUp);
26364 this.fireEvent("hide", this);
26367 onMouseUp : function()
26381 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26384 * @class Roo.bootstrap.menu.Item
26385 * @extends Roo.bootstrap.Component
26386 * Bootstrap MenuItem class
26387 * @cfg {Boolean} submenu (true | false) default false
26388 * @cfg {String} html text of the item
26389 * @cfg {String} href the link
26390 * @cfg {Boolean} disable (true | false) default false
26391 * @cfg {Boolean} preventDefault (true | false) default true
26392 * @cfg {String} icon Font awesome icon
26393 * @cfg {String} pos Submenu align to (left | right) default right
26397 * Create a new Item
26398 * @param {Object} config The config object
26402 Roo.bootstrap.menu.Item = function(config){
26403 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26407 * Fires when the mouse is hovering over this menu
26408 * @param {Roo.bootstrap.menu.Item} this
26409 * @param {Roo.EventObject} e
26414 * Fires when the mouse exits this menu
26415 * @param {Roo.bootstrap.menu.Item} this
26416 * @param {Roo.EventObject} e
26422 * The raw click event for the entire grid.
26423 * @param {Roo.EventObject} e
26429 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26434 preventDefault: true,
26439 getAutoCreate : function()
26444 cls : 'roo-menu-item-text',
26452 cls : 'fa ' + this.icon
26461 href : this.href || '#',
26468 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26472 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26474 if(this.pos == 'left'){
26475 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26482 initEvents : function()
26484 this.el.on('mouseover', this.onMouseOver, this);
26485 this.el.on('mouseout', this.onMouseOut, this);
26487 this.el.select('a', true).first().on('click', this.onClick, this);
26491 onClick : function(e)
26493 if(this.preventDefault){
26494 e.preventDefault();
26497 this.fireEvent("click", this, e);
26500 onMouseOver : function(e)
26502 if(this.submenu && this.pos == 'left'){
26503 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26506 this.fireEvent("mouseover", this, e);
26509 onMouseOut : function(e)
26511 this.fireEvent("mouseout", this, e);
26523 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26526 * @class Roo.bootstrap.menu.Separator
26527 * @extends Roo.bootstrap.Component
26528 * Bootstrap Separator class
26531 * Create a new Separator
26532 * @param {Object} config The config object
26536 Roo.bootstrap.menu.Separator = function(config){
26537 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26540 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26542 getAutoCreate : function(){
26563 * @class Roo.bootstrap.Tooltip
26564 * Bootstrap Tooltip class
26565 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26566 * to determine which dom element triggers the tooltip.
26568 * It needs to add support for additional attributes like tooltip-position
26571 * Create a new Toolti
26572 * @param {Object} config The config object
26575 Roo.bootstrap.Tooltip = function(config){
26576 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26578 this.alignment = Roo.bootstrap.Tooltip.alignment;
26580 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26581 this.alignment = config.alignment;
26586 Roo.apply(Roo.bootstrap.Tooltip, {
26588 * @function init initialize tooltip monitoring.
26592 currentTip : false,
26593 currentRegion : false,
26599 Roo.get(document).on('mouseover', this.enter ,this);
26600 Roo.get(document).on('mouseout', this.leave, this);
26603 this.currentTip = new Roo.bootstrap.Tooltip();
26606 enter : function(ev)
26608 var dom = ev.getTarget();
26610 //Roo.log(['enter',dom]);
26611 var el = Roo.fly(dom);
26612 if (this.currentEl) {
26614 //Roo.log(this.currentEl);
26615 //Roo.log(this.currentEl.contains(dom));
26616 if (this.currentEl == el) {
26619 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26625 if (this.currentTip.el) {
26626 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26630 if(!el || el.dom == document){
26636 // you can not look for children, as if el is the body.. then everythign is the child..
26637 if (!el.attr('tooltip')) { //
26638 if (!el.select("[tooltip]").elements.length) {
26641 // is the mouse over this child...?
26642 bindEl = el.select("[tooltip]").first();
26643 var xy = ev.getXY();
26644 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26645 //Roo.log("not in region.");
26648 //Roo.log("child element over..");
26651 this.currentEl = bindEl;
26652 this.currentTip.bind(bindEl);
26653 this.currentRegion = Roo.lib.Region.getRegion(dom);
26654 this.currentTip.enter();
26657 leave : function(ev)
26659 var dom = ev.getTarget();
26660 //Roo.log(['leave',dom]);
26661 if (!this.currentEl) {
26666 if (dom != this.currentEl.dom) {
26669 var xy = ev.getXY();
26670 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26673 // only activate leave if mouse cursor is outside... bounding box..
26678 if (this.currentTip) {
26679 this.currentTip.leave();
26681 //Roo.log('clear currentEl');
26682 this.currentEl = false;
26687 'left' : ['r-l', [-2,0], 'right'],
26688 'right' : ['l-r', [2,0], 'left'],
26689 'bottom' : ['t-b', [0,2], 'top'],
26690 'top' : [ 'b-t', [0,-2], 'bottom']
26696 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26701 delay : null, // can be { show : 300 , hide: 500}
26705 hoverState : null, //???
26707 placement : 'bottom',
26711 getAutoCreate : function(){
26718 cls : 'tooltip-arrow'
26721 cls : 'tooltip-inner'
26728 bind : function(el)
26734 enter : function () {
26736 if (this.timeout != null) {
26737 clearTimeout(this.timeout);
26740 this.hoverState = 'in';
26741 //Roo.log("enter - show");
26742 if (!this.delay || !this.delay.show) {
26747 this.timeout = setTimeout(function () {
26748 if (_t.hoverState == 'in') {
26751 }, this.delay.show);
26755 clearTimeout(this.timeout);
26757 this.hoverState = 'out';
26758 if (!this.delay || !this.delay.hide) {
26764 this.timeout = setTimeout(function () {
26765 //Roo.log("leave - timeout");
26767 if (_t.hoverState == 'out') {
26769 Roo.bootstrap.Tooltip.currentEl = false;
26774 show : function (msg)
26777 this.render(document.body);
26780 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26782 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26784 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26786 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26788 var placement = typeof this.placement == 'function' ?
26789 this.placement.call(this, this.el, on_el) :
26792 var autoToken = /\s?auto?\s?/i;
26793 var autoPlace = autoToken.test(placement);
26795 placement = placement.replace(autoToken, '') || 'top';
26799 //this.el.setXY([0,0]);
26801 //this.el.dom.style.display='block';
26803 //this.el.appendTo(on_el);
26805 var p = this.getPosition();
26806 var box = this.el.getBox();
26812 var align = this.alignment[placement];
26814 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26816 if(placement == 'top' || placement == 'bottom'){
26818 placement = 'right';
26821 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26822 placement = 'left';
26825 var scroll = Roo.select('body', true).first().getScroll();
26827 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26831 align = this.alignment[placement];
26834 this.el.alignTo(this.bindEl, align[0],align[1]);
26835 //var arrow = this.el.select('.arrow',true).first();
26836 //arrow.set(align[2],
26838 this.el.addClass(placement);
26840 this.el.addClass('in fade');
26842 this.hoverState = null;
26844 if (this.el.hasClass('fade')) {
26855 //this.el.setXY([0,0]);
26856 this.el.removeClass('in');
26872 * @class Roo.bootstrap.LocationPicker
26873 * @extends Roo.bootstrap.Component
26874 * Bootstrap LocationPicker class
26875 * @cfg {Number} latitude Position when init default 0
26876 * @cfg {Number} longitude Position when init default 0
26877 * @cfg {Number} zoom default 15
26878 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26879 * @cfg {Boolean} mapTypeControl default false
26880 * @cfg {Boolean} disableDoubleClickZoom default false
26881 * @cfg {Boolean} scrollwheel default true
26882 * @cfg {Boolean} streetViewControl default false
26883 * @cfg {Number} radius default 0
26884 * @cfg {String} locationName
26885 * @cfg {Boolean} draggable default true
26886 * @cfg {Boolean} enableAutocomplete default false
26887 * @cfg {Boolean} enableReverseGeocode default true
26888 * @cfg {String} markerTitle
26891 * Create a new LocationPicker
26892 * @param {Object} config The config object
26896 Roo.bootstrap.LocationPicker = function(config){
26898 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26903 * Fires when the picker initialized.
26904 * @param {Roo.bootstrap.LocationPicker} this
26905 * @param {Google Location} location
26909 * @event positionchanged
26910 * Fires when the picker position changed.
26911 * @param {Roo.bootstrap.LocationPicker} this
26912 * @param {Google Location} location
26914 positionchanged : true,
26917 * Fires when the map resize.
26918 * @param {Roo.bootstrap.LocationPicker} this
26923 * Fires when the map show.
26924 * @param {Roo.bootstrap.LocationPicker} this
26929 * Fires when the map hide.
26930 * @param {Roo.bootstrap.LocationPicker} this
26935 * Fires when click the map.
26936 * @param {Roo.bootstrap.LocationPicker} this
26937 * @param {Map event} e
26941 * @event mapRightClick
26942 * Fires when right click the map.
26943 * @param {Roo.bootstrap.LocationPicker} this
26944 * @param {Map event} e
26946 mapRightClick : true,
26948 * @event markerClick
26949 * Fires when click the marker.
26950 * @param {Roo.bootstrap.LocationPicker} this
26951 * @param {Map event} e
26953 markerClick : true,
26955 * @event markerRightClick
26956 * Fires when right click the marker.
26957 * @param {Roo.bootstrap.LocationPicker} this
26958 * @param {Map event} e
26960 markerRightClick : true,
26962 * @event OverlayViewDraw
26963 * Fires when OverlayView Draw
26964 * @param {Roo.bootstrap.LocationPicker} this
26966 OverlayViewDraw : true,
26968 * @event OverlayViewOnAdd
26969 * Fires when OverlayView Draw
26970 * @param {Roo.bootstrap.LocationPicker} this
26972 OverlayViewOnAdd : true,
26974 * @event OverlayViewOnRemove
26975 * Fires when OverlayView Draw
26976 * @param {Roo.bootstrap.LocationPicker} this
26978 OverlayViewOnRemove : true,
26980 * @event OverlayViewShow
26981 * Fires when OverlayView Draw
26982 * @param {Roo.bootstrap.LocationPicker} this
26983 * @param {Pixel} cpx
26985 OverlayViewShow : true,
26987 * @event OverlayViewHide
26988 * Fires when OverlayView Draw
26989 * @param {Roo.bootstrap.LocationPicker} this
26991 OverlayViewHide : true,
26993 * @event loadexception
26994 * Fires when load google lib failed.
26995 * @param {Roo.bootstrap.LocationPicker} this
26997 loadexception : true
27002 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
27004 gMapContext: false,
27010 mapTypeControl: false,
27011 disableDoubleClickZoom: false,
27013 streetViewControl: false,
27017 enableAutocomplete: false,
27018 enableReverseGeocode: true,
27021 getAutoCreate: function()
27026 cls: 'roo-location-picker'
27032 initEvents: function(ct, position)
27034 if(!this.el.getWidth() || this.isApplied()){
27038 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27043 initial: function()
27045 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27046 this.fireEvent('loadexception', this);
27050 if(!this.mapTypeId){
27051 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
27054 this.gMapContext = this.GMapContext();
27056 this.initOverlayView();
27058 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
27062 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
27063 _this.setPosition(_this.gMapContext.marker.position);
27066 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
27067 _this.fireEvent('mapClick', this, event);
27071 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
27072 _this.fireEvent('mapRightClick', this, event);
27076 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
27077 _this.fireEvent('markerClick', this, event);
27081 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
27082 _this.fireEvent('markerRightClick', this, event);
27086 this.setPosition(this.gMapContext.location);
27088 this.fireEvent('initial', this, this.gMapContext.location);
27091 initOverlayView: function()
27095 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
27099 _this.fireEvent('OverlayViewDraw', _this);
27104 _this.fireEvent('OverlayViewOnAdd', _this);
27107 onRemove: function()
27109 _this.fireEvent('OverlayViewOnRemove', _this);
27112 show: function(cpx)
27114 _this.fireEvent('OverlayViewShow', _this, cpx);
27119 _this.fireEvent('OverlayViewHide', _this);
27125 fromLatLngToContainerPixel: function(event)
27127 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27130 isApplied: function()
27132 return this.getGmapContext() == false ? false : true;
27135 getGmapContext: function()
27137 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27140 GMapContext: function()
27142 var position = new google.maps.LatLng(this.latitude, this.longitude);
27144 var _map = new google.maps.Map(this.el.dom, {
27147 mapTypeId: this.mapTypeId,
27148 mapTypeControl: this.mapTypeControl,
27149 disableDoubleClickZoom: this.disableDoubleClickZoom,
27150 scrollwheel: this.scrollwheel,
27151 streetViewControl: this.streetViewControl,
27152 locationName: this.locationName,
27153 draggable: this.draggable,
27154 enableAutocomplete: this.enableAutocomplete,
27155 enableReverseGeocode: this.enableReverseGeocode
27158 var _marker = new google.maps.Marker({
27159 position: position,
27161 title: this.markerTitle,
27162 draggable: this.draggable
27169 location: position,
27170 radius: this.radius,
27171 locationName: this.locationName,
27172 addressComponents: {
27173 formatted_address: null,
27174 addressLine1: null,
27175 addressLine2: null,
27177 streetNumber: null,
27181 stateOrProvince: null
27184 domContainer: this.el.dom,
27185 geodecoder: new google.maps.Geocoder()
27189 drawCircle: function(center, radius, options)
27191 if (this.gMapContext.circle != null) {
27192 this.gMapContext.circle.setMap(null);
27196 options = Roo.apply({}, options, {
27197 strokeColor: "#0000FF",
27198 strokeOpacity: .35,
27200 fillColor: "#0000FF",
27204 options.map = this.gMapContext.map;
27205 options.radius = radius;
27206 options.center = center;
27207 this.gMapContext.circle = new google.maps.Circle(options);
27208 return this.gMapContext.circle;
27214 setPosition: function(location)
27216 this.gMapContext.location = location;
27217 this.gMapContext.marker.setPosition(location);
27218 this.gMapContext.map.panTo(location);
27219 this.drawCircle(location, this.gMapContext.radius, {});
27223 if (this.gMapContext.settings.enableReverseGeocode) {
27224 this.gMapContext.geodecoder.geocode({
27225 latLng: this.gMapContext.location
27226 }, function(results, status) {
27228 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27229 _this.gMapContext.locationName = results[0].formatted_address;
27230 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27232 _this.fireEvent('positionchanged', this, location);
27239 this.fireEvent('positionchanged', this, location);
27244 google.maps.event.trigger(this.gMapContext.map, "resize");
27246 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27248 this.fireEvent('resize', this);
27251 setPositionByLatLng: function(latitude, longitude)
27253 this.setPosition(new google.maps.LatLng(latitude, longitude));
27256 getCurrentPosition: function()
27259 latitude: this.gMapContext.location.lat(),
27260 longitude: this.gMapContext.location.lng()
27264 getAddressName: function()
27266 return this.gMapContext.locationName;
27269 getAddressComponents: function()
27271 return this.gMapContext.addressComponents;
27274 address_component_from_google_geocode: function(address_components)
27278 for (var i = 0; i < address_components.length; i++) {
27279 var component = address_components[i];
27280 if (component.types.indexOf("postal_code") >= 0) {
27281 result.postalCode = component.short_name;
27282 } else if (component.types.indexOf("street_number") >= 0) {
27283 result.streetNumber = component.short_name;
27284 } else if (component.types.indexOf("route") >= 0) {
27285 result.streetName = component.short_name;
27286 } else if (component.types.indexOf("neighborhood") >= 0) {
27287 result.city = component.short_name;
27288 } else if (component.types.indexOf("locality") >= 0) {
27289 result.city = component.short_name;
27290 } else if (component.types.indexOf("sublocality") >= 0) {
27291 result.district = component.short_name;
27292 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27293 result.stateOrProvince = component.short_name;
27294 } else if (component.types.indexOf("country") >= 0) {
27295 result.country = component.short_name;
27299 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27300 result.addressLine2 = "";
27304 setZoomLevel: function(zoom)
27306 this.gMapContext.map.setZoom(zoom);
27319 this.fireEvent('show', this);
27330 this.fireEvent('hide', this);
27335 Roo.apply(Roo.bootstrap.LocationPicker, {
27337 OverlayView : function(map, options)
27339 options = options || {};
27346 * @class Roo.bootstrap.Alert
27347 * @extends Roo.bootstrap.Component
27348 * Bootstrap Alert class - shows an alert area box
27350 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27351 Enter a valid email address
27354 * @cfg {String} title The title of alert
27355 * @cfg {String} html The content of alert
27356 * @cfg {String} weight ( success | info | warning | danger )
27357 * @cfg {String} faicon font-awesomeicon
27360 * Create a new alert
27361 * @param {Object} config The config object
27365 Roo.bootstrap.Alert = function(config){
27366 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27370 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
27377 getAutoCreate : function()
27386 cls : 'roo-alert-icon'
27391 cls : 'roo-alert-title',
27396 cls : 'roo-alert-text',
27403 cfg.cn[0].cls += ' fa ' + this.faicon;
27407 cfg.cls += ' alert-' + this.weight;
27413 initEvents: function()
27415 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27418 setTitle : function(str)
27420 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27423 setText : function(str)
27425 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27428 setWeight : function(weight)
27431 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27434 this.weight = weight;
27436 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27439 setIcon : function(icon)
27442 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27445 this.faicon = icon;
27447 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27468 * @class Roo.bootstrap.UploadCropbox
27469 * @extends Roo.bootstrap.Component
27470 * Bootstrap UploadCropbox class
27471 * @cfg {String} emptyText show when image has been loaded
27472 * @cfg {String} rotateNotify show when image too small to rotate
27473 * @cfg {Number} errorTimeout default 3000
27474 * @cfg {Number} minWidth default 300
27475 * @cfg {Number} minHeight default 300
27476 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27477 * @cfg {Boolean} isDocument (true|false) default false
27478 * @cfg {String} url action url
27479 * @cfg {String} paramName default 'imageUpload'
27480 * @cfg {String} method default POST
27481 * @cfg {Boolean} loadMask (true|false) default true
27482 * @cfg {Boolean} loadingText default 'Loading...'
27485 * Create a new UploadCropbox
27486 * @param {Object} config The config object
27489 Roo.bootstrap.UploadCropbox = function(config){
27490 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27494 * @event beforeselectfile
27495 * Fire before select file
27496 * @param {Roo.bootstrap.UploadCropbox} this
27498 "beforeselectfile" : true,
27501 * Fire after initEvent
27502 * @param {Roo.bootstrap.UploadCropbox} this
27507 * Fire after initEvent
27508 * @param {Roo.bootstrap.UploadCropbox} this
27509 * @param {String} data
27514 * Fire when preparing the file data
27515 * @param {Roo.bootstrap.UploadCropbox} this
27516 * @param {Object} file
27521 * Fire when get exception
27522 * @param {Roo.bootstrap.UploadCropbox} this
27523 * @param {XMLHttpRequest} xhr
27525 "exception" : true,
27527 * @event beforeloadcanvas
27528 * Fire before load the canvas
27529 * @param {Roo.bootstrap.UploadCropbox} this
27530 * @param {String} src
27532 "beforeloadcanvas" : true,
27535 * Fire when trash image
27536 * @param {Roo.bootstrap.UploadCropbox} this
27541 * Fire when download the image
27542 * @param {Roo.bootstrap.UploadCropbox} this
27546 * @event footerbuttonclick
27547 * Fire when footerbuttonclick
27548 * @param {Roo.bootstrap.UploadCropbox} this
27549 * @param {String} type
27551 "footerbuttonclick" : true,
27555 * @param {Roo.bootstrap.UploadCropbox} this
27560 * Fire when rotate the image
27561 * @param {Roo.bootstrap.UploadCropbox} this
27562 * @param {String} pos
27567 * Fire when inspect the file
27568 * @param {Roo.bootstrap.UploadCropbox} this
27569 * @param {Object} file
27574 * Fire when xhr upload the file
27575 * @param {Roo.bootstrap.UploadCropbox} this
27576 * @param {Object} data
27581 * Fire when arrange the file data
27582 * @param {Roo.bootstrap.UploadCropbox} this
27583 * @param {Object} formData
27588 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27591 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27593 emptyText : 'Click to upload image',
27594 rotateNotify : 'Image is too small to rotate',
27595 errorTimeout : 3000,
27609 cropType : 'image/jpeg',
27611 canvasLoaded : false,
27612 isDocument : false,
27614 paramName : 'imageUpload',
27616 loadingText : 'Loading...',
27619 getAutoCreate : function()
27623 cls : 'roo-upload-cropbox',
27627 cls : 'roo-upload-cropbox-selector',
27632 cls : 'roo-upload-cropbox-body',
27633 style : 'cursor:pointer',
27637 cls : 'roo-upload-cropbox-preview'
27641 cls : 'roo-upload-cropbox-thumb'
27645 cls : 'roo-upload-cropbox-empty-notify',
27646 html : this.emptyText
27650 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27651 html : this.rotateNotify
27657 cls : 'roo-upload-cropbox-footer',
27660 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27670 onRender : function(ct, position)
27672 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27674 if (this.buttons.length) {
27676 Roo.each(this.buttons, function(bb) {
27678 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27680 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27686 this.maskEl = this.el;
27690 initEvents : function()
27692 this.urlAPI = (window.createObjectURL && window) ||
27693 (window.URL && URL.revokeObjectURL && URL) ||
27694 (window.webkitURL && webkitURL);
27696 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27697 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27699 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27700 this.selectorEl.hide();
27702 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27703 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27705 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27706 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27707 this.thumbEl.hide();
27709 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27710 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27712 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27713 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27714 this.errorEl.hide();
27716 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27717 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27718 this.footerEl.hide();
27720 this.setThumbBoxSize();
27726 this.fireEvent('initial', this);
27733 window.addEventListener("resize", function() { _this.resize(); } );
27735 this.bodyEl.on('click', this.beforeSelectFile, this);
27738 this.bodyEl.on('touchstart', this.onTouchStart, this);
27739 this.bodyEl.on('touchmove', this.onTouchMove, this);
27740 this.bodyEl.on('touchend', this.onTouchEnd, this);
27744 this.bodyEl.on('mousedown', this.onMouseDown, this);
27745 this.bodyEl.on('mousemove', this.onMouseMove, this);
27746 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27747 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27748 Roo.get(document).on('mouseup', this.onMouseUp, this);
27751 this.selectorEl.on('change', this.onFileSelected, this);
27757 this.baseScale = 1;
27759 this.baseRotate = 1;
27760 this.dragable = false;
27761 this.pinching = false;
27764 this.cropData = false;
27765 this.notifyEl.dom.innerHTML = this.emptyText;
27767 this.selectorEl.dom.value = '';
27771 resize : function()
27773 if(this.fireEvent('resize', this) != false){
27774 this.setThumbBoxPosition();
27775 this.setCanvasPosition();
27779 onFooterButtonClick : function(e, el, o, type)
27782 case 'rotate-left' :
27783 this.onRotateLeft(e);
27785 case 'rotate-right' :
27786 this.onRotateRight(e);
27789 this.beforeSelectFile(e);
27804 this.fireEvent('footerbuttonclick', this, type);
27807 beforeSelectFile : function(e)
27809 e.preventDefault();
27811 if(this.fireEvent('beforeselectfile', this) != false){
27812 this.selectorEl.dom.click();
27816 onFileSelected : function(e)
27818 e.preventDefault();
27820 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27824 var file = this.selectorEl.dom.files[0];
27826 if(this.fireEvent('inspect', this, file) != false){
27827 this.prepare(file);
27832 trash : function(e)
27834 this.fireEvent('trash', this);
27837 download : function(e)
27839 this.fireEvent('download', this);
27842 loadCanvas : function(src)
27844 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27848 this.imageEl = document.createElement('img');
27852 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27854 this.imageEl.src = src;
27858 onLoadCanvas : function()
27860 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27861 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27863 this.bodyEl.un('click', this.beforeSelectFile, this);
27865 this.notifyEl.hide();
27866 this.thumbEl.show();
27867 this.footerEl.show();
27869 this.baseRotateLevel();
27871 if(this.isDocument){
27872 this.setThumbBoxSize();
27875 this.setThumbBoxPosition();
27877 this.baseScaleLevel();
27883 this.canvasLoaded = true;
27886 this.maskEl.unmask();
27891 setCanvasPosition : function()
27893 if(!this.canvasEl){
27897 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27898 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27900 this.previewEl.setLeft(pw);
27901 this.previewEl.setTop(ph);
27905 onMouseDown : function(e)
27909 this.dragable = true;
27910 this.pinching = false;
27912 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27913 this.dragable = false;
27917 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27918 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27922 onMouseMove : function(e)
27926 if(!this.canvasLoaded){
27930 if (!this.dragable){
27934 var minX = Math.ceil(this.thumbEl.getLeft(true));
27935 var minY = Math.ceil(this.thumbEl.getTop(true));
27937 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27938 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27940 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27941 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27943 x = x - this.mouseX;
27944 y = y - this.mouseY;
27946 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27947 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27949 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27950 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27952 this.previewEl.setLeft(bgX);
27953 this.previewEl.setTop(bgY);
27955 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27956 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27959 onMouseUp : function(e)
27963 this.dragable = false;
27966 onMouseWheel : function(e)
27970 this.startScale = this.scale;
27972 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27974 if(!this.zoomable()){
27975 this.scale = this.startScale;
27984 zoomable : function()
27986 var minScale = this.thumbEl.getWidth() / this.minWidth;
27988 if(this.minWidth < this.minHeight){
27989 minScale = this.thumbEl.getHeight() / this.minHeight;
27992 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27993 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27997 (this.rotate == 0 || this.rotate == 180) &&
27999 width > this.imageEl.OriginWidth ||
28000 height > this.imageEl.OriginHeight ||
28001 (width < this.minWidth && height < this.minHeight)
28009 (this.rotate == 90 || this.rotate == 270) &&
28011 width > this.imageEl.OriginWidth ||
28012 height > this.imageEl.OriginHeight ||
28013 (width < this.minHeight && height < this.minWidth)
28020 !this.isDocument &&
28021 (this.rotate == 0 || this.rotate == 180) &&
28023 width < this.minWidth ||
28024 width > this.imageEl.OriginWidth ||
28025 height < this.minHeight ||
28026 height > this.imageEl.OriginHeight
28033 !this.isDocument &&
28034 (this.rotate == 90 || this.rotate == 270) &&
28036 width < this.minHeight ||
28037 width > this.imageEl.OriginWidth ||
28038 height < this.minWidth ||
28039 height > this.imageEl.OriginHeight
28049 onRotateLeft : function(e)
28051 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28053 var minScale = this.thumbEl.getWidth() / this.minWidth;
28055 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28056 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28058 this.startScale = this.scale;
28060 while (this.getScaleLevel() < minScale){
28062 this.scale = this.scale + 1;
28064 if(!this.zoomable()){
28069 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28070 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28075 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28082 this.scale = this.startScale;
28084 this.onRotateFail();
28089 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28091 if(this.isDocument){
28092 this.setThumbBoxSize();
28093 this.setThumbBoxPosition();
28094 this.setCanvasPosition();
28099 this.fireEvent('rotate', this, 'left');
28103 onRotateRight : function(e)
28105 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28107 var minScale = this.thumbEl.getWidth() / this.minWidth;
28109 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28110 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28112 this.startScale = this.scale;
28114 while (this.getScaleLevel() < minScale){
28116 this.scale = this.scale + 1;
28118 if(!this.zoomable()){
28123 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28124 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28129 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28136 this.scale = this.startScale;
28138 this.onRotateFail();
28143 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28145 if(this.isDocument){
28146 this.setThumbBoxSize();
28147 this.setThumbBoxPosition();
28148 this.setCanvasPosition();
28153 this.fireEvent('rotate', this, 'right');
28156 onRotateFail : function()
28158 this.errorEl.show(true);
28162 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28167 this.previewEl.dom.innerHTML = '';
28169 var canvasEl = document.createElement("canvas");
28171 var contextEl = canvasEl.getContext("2d");
28173 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28174 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28175 var center = this.imageEl.OriginWidth / 2;
28177 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28178 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28179 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28180 center = this.imageEl.OriginHeight / 2;
28183 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28185 contextEl.translate(center, center);
28186 contextEl.rotate(this.rotate * Math.PI / 180);
28188 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28190 this.canvasEl = document.createElement("canvas");
28192 this.contextEl = this.canvasEl.getContext("2d");
28194 switch (this.rotate) {
28197 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28198 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28200 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28205 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28206 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28208 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28209 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.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28218 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28219 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28221 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28222 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);
28226 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);
28231 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28232 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28234 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28235 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28239 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);
28246 this.previewEl.appendChild(this.canvasEl);
28248 this.setCanvasPosition();
28253 if(!this.canvasLoaded){
28257 var imageCanvas = document.createElement("canvas");
28259 var imageContext = imageCanvas.getContext("2d");
28261 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28262 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28264 var center = imageCanvas.width / 2;
28266 imageContext.translate(center, center);
28268 imageContext.rotate(this.rotate * Math.PI / 180);
28270 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28272 var canvas = document.createElement("canvas");
28274 var context = canvas.getContext("2d");
28276 canvas.width = this.minWidth;
28277 canvas.height = this.minHeight;
28279 switch (this.rotate) {
28282 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28283 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28285 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28286 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28288 var targetWidth = this.minWidth - 2 * x;
28289 var targetHeight = this.minHeight - 2 * y;
28293 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28294 scale = targetWidth / width;
28297 if(x > 0 && y == 0){
28298 scale = targetHeight / height;
28301 if(x > 0 && y > 0){
28302 scale = targetWidth / width;
28304 if(width < height){
28305 scale = targetHeight / height;
28309 context.scale(scale, scale);
28311 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28312 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28314 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28315 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28317 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28322 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28323 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28325 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28326 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28328 var targetWidth = this.minWidth - 2 * x;
28329 var targetHeight = this.minHeight - 2 * y;
28333 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28334 scale = targetWidth / width;
28337 if(x > 0 && y == 0){
28338 scale = targetHeight / height;
28341 if(x > 0 && y > 0){
28342 scale = targetWidth / width;
28344 if(width < height){
28345 scale = targetHeight / height;
28349 context.scale(scale, scale);
28351 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28352 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28354 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28355 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28357 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28359 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28364 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28365 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28367 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28368 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28370 var targetWidth = this.minWidth - 2 * x;
28371 var targetHeight = this.minHeight - 2 * y;
28375 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28376 scale = targetWidth / width;
28379 if(x > 0 && y == 0){
28380 scale = targetHeight / height;
28383 if(x > 0 && y > 0){
28384 scale = targetWidth / width;
28386 if(width < height){
28387 scale = targetHeight / height;
28391 context.scale(scale, scale);
28393 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28394 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28396 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28397 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28399 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28400 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28402 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28407 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28408 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28410 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28411 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28413 var targetWidth = this.minWidth - 2 * x;
28414 var targetHeight = this.minHeight - 2 * y;
28418 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28419 scale = targetWidth / width;
28422 if(x > 0 && y == 0){
28423 scale = targetHeight / height;
28426 if(x > 0 && y > 0){
28427 scale = targetWidth / width;
28429 if(width < height){
28430 scale = targetHeight / height;
28434 context.scale(scale, scale);
28436 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28437 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28439 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28440 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28442 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28444 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28451 this.cropData = canvas.toDataURL(this.cropType);
28453 if(this.fireEvent('crop', this, this.cropData) !== false){
28454 this.process(this.file, this.cropData);
28461 setThumbBoxSize : function()
28465 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28466 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28467 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28469 this.minWidth = width;
28470 this.minHeight = height;
28472 if(this.rotate == 90 || this.rotate == 270){
28473 this.minWidth = height;
28474 this.minHeight = width;
28479 width = Math.ceil(this.minWidth * height / this.minHeight);
28481 if(this.minWidth > this.minHeight){
28483 height = Math.ceil(this.minHeight * width / this.minWidth);
28486 this.thumbEl.setStyle({
28487 width : width + 'px',
28488 height : height + 'px'
28495 setThumbBoxPosition : function()
28497 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28498 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28500 this.thumbEl.setLeft(x);
28501 this.thumbEl.setTop(y);
28505 baseRotateLevel : function()
28507 this.baseRotate = 1;
28510 typeof(this.exif) != 'undefined' &&
28511 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28512 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28514 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28517 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28521 baseScaleLevel : function()
28525 if(this.isDocument){
28527 if(this.baseRotate == 6 || this.baseRotate == 8){
28529 height = this.thumbEl.getHeight();
28530 this.baseScale = height / this.imageEl.OriginWidth;
28532 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28533 width = this.thumbEl.getWidth();
28534 this.baseScale = width / this.imageEl.OriginHeight;
28540 height = this.thumbEl.getHeight();
28541 this.baseScale = height / this.imageEl.OriginHeight;
28543 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28544 width = this.thumbEl.getWidth();
28545 this.baseScale = width / this.imageEl.OriginWidth;
28551 if(this.baseRotate == 6 || this.baseRotate == 8){
28553 width = this.thumbEl.getHeight();
28554 this.baseScale = width / this.imageEl.OriginHeight;
28556 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28557 height = this.thumbEl.getWidth();
28558 this.baseScale = height / this.imageEl.OriginHeight;
28561 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28562 height = this.thumbEl.getWidth();
28563 this.baseScale = height / this.imageEl.OriginHeight;
28565 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28566 width = this.thumbEl.getHeight();
28567 this.baseScale = width / this.imageEl.OriginWidth;
28574 width = this.thumbEl.getWidth();
28575 this.baseScale = width / this.imageEl.OriginWidth;
28577 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28578 height = this.thumbEl.getHeight();
28579 this.baseScale = height / this.imageEl.OriginHeight;
28582 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28584 height = this.thumbEl.getHeight();
28585 this.baseScale = height / this.imageEl.OriginHeight;
28587 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28588 width = this.thumbEl.getWidth();
28589 this.baseScale = width / this.imageEl.OriginWidth;
28597 getScaleLevel : function()
28599 return this.baseScale * Math.pow(1.1, this.scale);
28602 onTouchStart : function(e)
28604 if(!this.canvasLoaded){
28605 this.beforeSelectFile(e);
28609 var touches = e.browserEvent.touches;
28615 if(touches.length == 1){
28616 this.onMouseDown(e);
28620 if(touches.length != 2){
28626 for(var i = 0, finger; finger = touches[i]; i++){
28627 coords.push(finger.pageX, finger.pageY);
28630 var x = Math.pow(coords[0] - coords[2], 2);
28631 var y = Math.pow(coords[1] - coords[3], 2);
28633 this.startDistance = Math.sqrt(x + y);
28635 this.startScale = this.scale;
28637 this.pinching = true;
28638 this.dragable = false;
28642 onTouchMove : function(e)
28644 if(!this.pinching && !this.dragable){
28648 var touches = e.browserEvent.touches;
28655 this.onMouseMove(e);
28661 for(var i = 0, finger; finger = touches[i]; i++){
28662 coords.push(finger.pageX, finger.pageY);
28665 var x = Math.pow(coords[0] - coords[2], 2);
28666 var y = Math.pow(coords[1] - coords[3], 2);
28668 this.endDistance = Math.sqrt(x + y);
28670 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28672 if(!this.zoomable()){
28673 this.scale = this.startScale;
28681 onTouchEnd : function(e)
28683 this.pinching = false;
28684 this.dragable = false;
28688 process : function(file, crop)
28691 this.maskEl.mask(this.loadingText);
28694 this.xhr = new XMLHttpRequest();
28696 file.xhr = this.xhr;
28698 this.xhr.open(this.method, this.url, true);
28701 "Accept": "application/json",
28702 "Cache-Control": "no-cache",
28703 "X-Requested-With": "XMLHttpRequest"
28706 for (var headerName in headers) {
28707 var headerValue = headers[headerName];
28709 this.xhr.setRequestHeader(headerName, headerValue);
28715 this.xhr.onload = function()
28717 _this.xhrOnLoad(_this.xhr);
28720 this.xhr.onerror = function()
28722 _this.xhrOnError(_this.xhr);
28725 var formData = new FormData();
28727 formData.append('returnHTML', 'NO');
28730 formData.append('crop', crop);
28733 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28734 formData.append(this.paramName, file, file.name);
28737 if(typeof(file.filename) != 'undefined'){
28738 formData.append('filename', file.filename);
28741 if(typeof(file.mimetype) != 'undefined'){
28742 formData.append('mimetype', file.mimetype);
28745 if(this.fireEvent('arrange', this, formData) != false){
28746 this.xhr.send(formData);
28750 xhrOnLoad : function(xhr)
28753 this.maskEl.unmask();
28756 if (xhr.readyState !== 4) {
28757 this.fireEvent('exception', this, xhr);
28761 var response = Roo.decode(xhr.responseText);
28763 if(!response.success){
28764 this.fireEvent('exception', this, xhr);
28768 var response = Roo.decode(xhr.responseText);
28770 this.fireEvent('upload', this, response);
28774 xhrOnError : function()
28777 this.maskEl.unmask();
28780 Roo.log('xhr on error');
28782 var response = Roo.decode(xhr.responseText);
28788 prepare : function(file)
28791 this.maskEl.mask(this.loadingText);
28797 if(typeof(file) === 'string'){
28798 this.loadCanvas(file);
28802 if(!file || !this.urlAPI){
28807 this.cropType = file.type;
28811 if(this.fireEvent('prepare', this, this.file) != false){
28813 var reader = new FileReader();
28815 reader.onload = function (e) {
28816 if (e.target.error) {
28817 Roo.log(e.target.error);
28821 var buffer = e.target.result,
28822 dataView = new DataView(buffer),
28824 maxOffset = dataView.byteLength - 4,
28828 if (dataView.getUint16(0) === 0xffd8) {
28829 while (offset < maxOffset) {
28830 markerBytes = dataView.getUint16(offset);
28832 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28833 markerLength = dataView.getUint16(offset + 2) + 2;
28834 if (offset + markerLength > dataView.byteLength) {
28835 Roo.log('Invalid meta data: Invalid segment size.');
28839 if(markerBytes == 0xffe1){
28840 _this.parseExifData(
28847 offset += markerLength;
28857 var url = _this.urlAPI.createObjectURL(_this.file);
28859 _this.loadCanvas(url);
28864 reader.readAsArrayBuffer(this.file);
28870 parseExifData : function(dataView, offset, length)
28872 var tiffOffset = offset + 10,
28876 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28877 // No Exif data, might be XMP data instead
28881 // Check for the ASCII code for "Exif" (0x45786966):
28882 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28883 // No Exif data, might be XMP data instead
28886 if (tiffOffset + 8 > dataView.byteLength) {
28887 Roo.log('Invalid Exif data: Invalid segment size.');
28890 // Check for the two null bytes:
28891 if (dataView.getUint16(offset + 8) !== 0x0000) {
28892 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28895 // Check the byte alignment:
28896 switch (dataView.getUint16(tiffOffset)) {
28898 littleEndian = true;
28901 littleEndian = false;
28904 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28907 // Check for the TIFF tag marker (0x002A):
28908 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28909 Roo.log('Invalid Exif data: Missing TIFF marker.');
28912 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28913 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28915 this.parseExifTags(
28918 tiffOffset + dirOffset,
28923 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28928 if (dirOffset + 6 > dataView.byteLength) {
28929 Roo.log('Invalid Exif data: Invalid directory offset.');
28932 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28933 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28934 if (dirEndOffset + 4 > dataView.byteLength) {
28935 Roo.log('Invalid Exif data: Invalid directory size.');
28938 for (i = 0; i < tagsNumber; i += 1) {
28942 dirOffset + 2 + 12 * i, // tag offset
28946 // Return the offset to the next directory:
28947 return dataView.getUint32(dirEndOffset, littleEndian);
28950 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28952 var tag = dataView.getUint16(offset, littleEndian);
28954 this.exif[tag] = this.getExifValue(
28958 dataView.getUint16(offset + 2, littleEndian), // tag type
28959 dataView.getUint32(offset + 4, littleEndian), // tag length
28964 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28966 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28975 Roo.log('Invalid Exif data: Invalid tag type.');
28979 tagSize = tagType.size * length;
28980 // Determine if the value is contained in the dataOffset bytes,
28981 // or if the value at the dataOffset is a pointer to the actual data:
28982 dataOffset = tagSize > 4 ?
28983 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28984 if (dataOffset + tagSize > dataView.byteLength) {
28985 Roo.log('Invalid Exif data: Invalid data offset.');
28988 if (length === 1) {
28989 return tagType.getValue(dataView, dataOffset, littleEndian);
28992 for (i = 0; i < length; i += 1) {
28993 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28996 if (tagType.ascii) {
28998 // Concatenate the chars:
28999 for (i = 0; i < values.length; i += 1) {
29001 // Ignore the terminating NULL byte(s):
29002 if (c === '\u0000') {
29014 Roo.apply(Roo.bootstrap.UploadCropbox, {
29016 'Orientation': 0x0112
29020 1: 0, //'top-left',
29022 3: 180, //'bottom-right',
29023 // 4: 'bottom-left',
29025 6: 90, //'right-top',
29026 // 7: 'right-bottom',
29027 8: 270 //'left-bottom'
29031 // byte, 8-bit unsigned int:
29033 getValue: function (dataView, dataOffset) {
29034 return dataView.getUint8(dataOffset);
29038 // ascii, 8-bit byte:
29040 getValue: function (dataView, dataOffset) {
29041 return String.fromCharCode(dataView.getUint8(dataOffset));
29046 // short, 16 bit int:
29048 getValue: function (dataView, dataOffset, littleEndian) {
29049 return dataView.getUint16(dataOffset, littleEndian);
29053 // long, 32 bit int:
29055 getValue: function (dataView, dataOffset, littleEndian) {
29056 return dataView.getUint32(dataOffset, littleEndian);
29060 // rational = two long values, first is numerator, second is denominator:
29062 getValue: function (dataView, dataOffset, littleEndian) {
29063 return dataView.getUint32(dataOffset, littleEndian) /
29064 dataView.getUint32(dataOffset + 4, littleEndian);
29068 // slong, 32 bit signed int:
29070 getValue: function (dataView, dataOffset, littleEndian) {
29071 return dataView.getInt32(dataOffset, littleEndian);
29075 // srational, two slongs, first is numerator, second is denominator:
29077 getValue: function (dataView, dataOffset, littleEndian) {
29078 return dataView.getInt32(dataOffset, littleEndian) /
29079 dataView.getInt32(dataOffset + 4, littleEndian);
29089 cls : 'btn-group roo-upload-cropbox-rotate-left',
29090 action : 'rotate-left',
29094 cls : 'btn btn-default',
29095 html : '<i class="fa fa-undo"></i>'
29101 cls : 'btn-group roo-upload-cropbox-picture',
29102 action : 'picture',
29106 cls : 'btn btn-default',
29107 html : '<i class="fa fa-picture-o"></i>'
29113 cls : 'btn-group roo-upload-cropbox-rotate-right',
29114 action : 'rotate-right',
29118 cls : 'btn btn-default',
29119 html : '<i class="fa fa-repeat"></i>'
29127 cls : 'btn-group roo-upload-cropbox-rotate-left',
29128 action : 'rotate-left',
29132 cls : 'btn btn-default',
29133 html : '<i class="fa fa-undo"></i>'
29139 cls : 'btn-group roo-upload-cropbox-download',
29140 action : 'download',
29144 cls : 'btn btn-default',
29145 html : '<i class="fa fa-download"></i>'
29151 cls : 'btn-group roo-upload-cropbox-crop',
29156 cls : 'btn btn-default',
29157 html : '<i class="fa fa-crop"></i>'
29163 cls : 'btn-group roo-upload-cropbox-trash',
29168 cls : 'btn btn-default',
29169 html : '<i class="fa fa-trash"></i>'
29175 cls : 'btn-group roo-upload-cropbox-rotate-right',
29176 action : 'rotate-right',
29180 cls : 'btn btn-default',
29181 html : '<i class="fa fa-repeat"></i>'
29189 cls : 'btn-group roo-upload-cropbox-rotate-left',
29190 action : 'rotate-left',
29194 cls : 'btn btn-default',
29195 html : '<i class="fa fa-undo"></i>'
29201 cls : 'btn-group roo-upload-cropbox-rotate-right',
29202 action : 'rotate-right',
29206 cls : 'btn btn-default',
29207 html : '<i class="fa fa-repeat"></i>'
29220 * @class Roo.bootstrap.DocumentManager
29221 * @extends Roo.bootstrap.Component
29222 * Bootstrap DocumentManager class
29223 * @cfg {String} paramName default 'imageUpload'
29224 * @cfg {String} toolTipName default 'filename'
29225 * @cfg {String} method default POST
29226 * @cfg {String} url action url
29227 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29228 * @cfg {Boolean} multiple multiple upload default true
29229 * @cfg {Number} thumbSize default 300
29230 * @cfg {String} fieldLabel
29231 * @cfg {Number} labelWidth default 4
29232 * @cfg {String} labelAlign (left|top) default left
29233 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29234 * @cfg {Number} labellg set the width of label (1-12)
29235 * @cfg {Number} labelmd set the width of label (1-12)
29236 * @cfg {Number} labelsm set the width of label (1-12)
29237 * @cfg {Number} labelxs set the width of label (1-12)
29240 * Create a new DocumentManager
29241 * @param {Object} config The config object
29244 Roo.bootstrap.DocumentManager = function(config){
29245 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29248 this.delegates = [];
29253 * Fire when initial the DocumentManager
29254 * @param {Roo.bootstrap.DocumentManager} this
29259 * inspect selected file
29260 * @param {Roo.bootstrap.DocumentManager} this
29261 * @param {File} file
29266 * Fire when xhr load exception
29267 * @param {Roo.bootstrap.DocumentManager} this
29268 * @param {XMLHttpRequest} xhr
29270 "exception" : true,
29272 * @event afterupload
29273 * Fire when xhr load exception
29274 * @param {Roo.bootstrap.DocumentManager} this
29275 * @param {XMLHttpRequest} xhr
29277 "afterupload" : true,
29280 * prepare the form data
29281 * @param {Roo.bootstrap.DocumentManager} this
29282 * @param {Object} formData
29287 * Fire when remove the file
29288 * @param {Roo.bootstrap.DocumentManager} this
29289 * @param {Object} file
29294 * Fire after refresh the file
29295 * @param {Roo.bootstrap.DocumentManager} this
29300 * Fire after click the image
29301 * @param {Roo.bootstrap.DocumentManager} this
29302 * @param {Object} file
29307 * Fire when upload a image and editable set to true
29308 * @param {Roo.bootstrap.DocumentManager} this
29309 * @param {Object} file
29313 * @event beforeselectfile
29314 * Fire before select file
29315 * @param {Roo.bootstrap.DocumentManager} this
29317 "beforeselectfile" : true,
29320 * Fire before process file
29321 * @param {Roo.bootstrap.DocumentManager} this
29322 * @param {Object} file
29326 * @event previewrendered
29327 * Fire when preview rendered
29328 * @param {Roo.bootstrap.DocumentManager} this
29329 * @param {Object} file
29331 "previewrendered" : true,
29334 "previewResize" : true
29339 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
29348 paramName : 'imageUpload',
29349 toolTipName : 'filename',
29352 labelAlign : 'left',
29362 getAutoCreate : function()
29364 var managerWidget = {
29366 cls : 'roo-document-manager',
29370 cls : 'roo-document-manager-selector',
29375 cls : 'roo-document-manager-uploader',
29379 cls : 'roo-document-manager-upload-btn',
29380 html : '<i class="fa fa-plus"></i>'
29391 cls : 'column col-md-12',
29396 if(this.fieldLabel.length){
29401 cls : 'column col-md-12',
29402 html : this.fieldLabel
29406 cls : 'column col-md-12',
29411 if(this.labelAlign == 'left'){
29416 html : this.fieldLabel
29425 if(this.labelWidth > 12){
29426 content[0].style = "width: " + this.labelWidth + 'px';
29429 if(this.labelWidth < 13 && this.labelmd == 0){
29430 this.labelmd = this.labelWidth;
29433 if(this.labellg > 0){
29434 content[0].cls += ' col-lg-' + this.labellg;
29435 content[1].cls += ' col-lg-' + (12 - this.labellg);
29438 if(this.labelmd > 0){
29439 content[0].cls += ' col-md-' + this.labelmd;
29440 content[1].cls += ' col-md-' + (12 - this.labelmd);
29443 if(this.labelsm > 0){
29444 content[0].cls += ' col-sm-' + this.labelsm;
29445 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29448 if(this.labelxs > 0){
29449 content[0].cls += ' col-xs-' + this.labelxs;
29450 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29458 cls : 'row clearfix',
29466 initEvents : function()
29468 this.managerEl = this.el.select('.roo-document-manager', true).first();
29469 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29471 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29472 this.selectorEl.hide();
29475 this.selectorEl.attr('multiple', 'multiple');
29478 this.selectorEl.on('change', this.onFileSelected, this);
29480 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29481 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29483 this.uploader.on('click', this.onUploaderClick, this);
29485 this.renderProgressDialog();
29489 window.addEventListener("resize", function() { _this.refresh(); } );
29491 this.fireEvent('initial', this);
29494 renderProgressDialog : function()
29498 this.progressDialog = new Roo.bootstrap.Modal({
29499 cls : 'roo-document-manager-progress-dialog',
29500 allow_close : false,
29511 btnclick : function() {
29512 _this.uploadCancel();
29518 this.progressDialog.render(Roo.get(document.body));
29520 this.progress = new Roo.bootstrap.Progress({
29521 cls : 'roo-document-manager-progress',
29526 this.progress.render(this.progressDialog.getChildContainer());
29528 this.progressBar = new Roo.bootstrap.ProgressBar({
29529 cls : 'roo-document-manager-progress-bar',
29532 aria_valuemax : 12,
29536 this.progressBar.render(this.progress.getChildContainer());
29539 onUploaderClick : function(e)
29541 e.preventDefault();
29543 if(this.fireEvent('beforeselectfile', this) != false){
29544 this.selectorEl.dom.click();
29549 onFileSelected : function(e)
29551 e.preventDefault();
29553 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29557 Roo.each(this.selectorEl.dom.files, function(file){
29558 if(this.fireEvent('inspect', this, file) != false){
29559 this.files.push(file);
29569 this.selectorEl.dom.value = '';
29571 if(!this.files || !this.files.length){
29575 if(this.boxes > 0 && this.files.length > this.boxes){
29576 this.files = this.files.slice(0, this.boxes);
29579 this.uploader.show();
29581 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29582 this.uploader.hide();
29591 Roo.each(this.files, function(file){
29593 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29594 var f = this.renderPreview(file);
29599 if(file.type.indexOf('image') != -1){
29600 this.delegates.push(
29602 _this.process(file);
29603 }).createDelegate(this)
29611 _this.process(file);
29612 }).createDelegate(this)
29617 this.files = files;
29619 this.delegates = this.delegates.concat(docs);
29621 if(!this.delegates.length){
29626 this.progressBar.aria_valuemax = this.delegates.length;
29633 arrange : function()
29635 if(!this.delegates.length){
29636 this.progressDialog.hide();
29641 var delegate = this.delegates.shift();
29643 this.progressDialog.show();
29645 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29647 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29652 refresh : function()
29654 this.uploader.show();
29656 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29657 this.uploader.hide();
29660 Roo.isTouch ? this.closable(false) : this.closable(true);
29662 this.fireEvent('refresh', this);
29665 onRemove : function(e, el, o)
29667 e.preventDefault();
29669 this.fireEvent('remove', this, o);
29673 remove : function(o)
29677 Roo.each(this.files, function(file){
29678 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29687 this.files = files;
29694 Roo.each(this.files, function(file){
29699 file.target.remove();
29708 onClick : function(e, el, o)
29710 e.preventDefault();
29712 this.fireEvent('click', this, o);
29716 closable : function(closable)
29718 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29720 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29732 xhrOnLoad : function(xhr)
29734 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29738 if (xhr.readyState !== 4) {
29740 this.fireEvent('exception', this, xhr);
29744 var response = Roo.decode(xhr.responseText);
29746 if(!response.success){
29748 this.fireEvent('exception', this, xhr);
29752 var file = this.renderPreview(response.data);
29754 this.files.push(file);
29758 this.fireEvent('afterupload', this, xhr);
29762 xhrOnError : function(xhr)
29764 Roo.log('xhr on error');
29766 var response = Roo.decode(xhr.responseText);
29773 process : function(file)
29775 if(this.fireEvent('process', this, file) !== false){
29776 if(this.editable && file.type.indexOf('image') != -1){
29777 this.fireEvent('edit', this, file);
29781 this.uploadStart(file, false);
29788 uploadStart : function(file, crop)
29790 this.xhr = new XMLHttpRequest();
29792 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29797 file.xhr = this.xhr;
29799 this.managerEl.createChild({
29801 cls : 'roo-document-manager-loading',
29805 tooltip : file.name,
29806 cls : 'roo-document-manager-thumb',
29807 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29813 this.xhr.open(this.method, this.url, true);
29816 "Accept": "application/json",
29817 "Cache-Control": "no-cache",
29818 "X-Requested-With": "XMLHttpRequest"
29821 for (var headerName in headers) {
29822 var headerValue = headers[headerName];
29824 this.xhr.setRequestHeader(headerName, headerValue);
29830 this.xhr.onload = function()
29832 _this.xhrOnLoad(_this.xhr);
29835 this.xhr.onerror = function()
29837 _this.xhrOnError(_this.xhr);
29840 var formData = new FormData();
29842 formData.append('returnHTML', 'NO');
29845 formData.append('crop', crop);
29848 formData.append(this.paramName, file, file.name);
29855 if(this.fireEvent('prepare', this, formData, options) != false){
29857 if(options.manually){
29861 this.xhr.send(formData);
29865 this.uploadCancel();
29868 uploadCancel : function()
29874 this.delegates = [];
29876 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29883 renderPreview : function(file)
29885 if(typeof(file.target) != 'undefined' && file.target){
29889 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29891 var previewEl = this.managerEl.createChild({
29893 cls : 'roo-document-manager-preview',
29897 tooltip : file[this.toolTipName],
29898 cls : 'roo-document-manager-thumb',
29899 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29904 html : '<i class="fa fa-times-circle"></i>'
29909 var close = previewEl.select('button.close', true).first();
29911 close.on('click', this.onRemove, this, file);
29913 file.target = previewEl;
29915 var image = previewEl.select('img', true).first();
29919 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29921 image.on('click', this.onClick, this, file);
29923 this.fireEvent('previewrendered', this, file);
29929 onPreviewLoad : function(file, image)
29931 if(typeof(file.target) == 'undefined' || !file.target){
29935 var width = image.dom.naturalWidth || image.dom.width;
29936 var height = image.dom.naturalHeight || image.dom.height;
29938 if(!this.previewResize) {
29942 if(width > height){
29943 file.target.addClass('wide');
29947 file.target.addClass('tall');
29952 uploadFromSource : function(file, crop)
29954 this.xhr = new XMLHttpRequest();
29956 this.managerEl.createChild({
29958 cls : 'roo-document-manager-loading',
29962 tooltip : file.name,
29963 cls : 'roo-document-manager-thumb',
29964 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29970 this.xhr.open(this.method, this.url, true);
29973 "Accept": "application/json",
29974 "Cache-Control": "no-cache",
29975 "X-Requested-With": "XMLHttpRequest"
29978 for (var headerName in headers) {
29979 var headerValue = headers[headerName];
29981 this.xhr.setRequestHeader(headerName, headerValue);
29987 this.xhr.onload = function()
29989 _this.xhrOnLoad(_this.xhr);
29992 this.xhr.onerror = function()
29994 _this.xhrOnError(_this.xhr);
29997 var formData = new FormData();
29999 formData.append('returnHTML', 'NO');
30001 formData.append('crop', crop);
30003 if(typeof(file.filename) != 'undefined'){
30004 formData.append('filename', file.filename);
30007 if(typeof(file.mimetype) != 'undefined'){
30008 formData.append('mimetype', file.mimetype);
30013 if(this.fireEvent('prepare', this, formData) != false){
30014 this.xhr.send(formData);
30024 * @class Roo.bootstrap.DocumentViewer
30025 * @extends Roo.bootstrap.Component
30026 * Bootstrap DocumentViewer class
30027 * @cfg {Boolean} showDownload (true|false) show download button (default true)
30028 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30031 * Create a new DocumentViewer
30032 * @param {Object} config The config object
30035 Roo.bootstrap.DocumentViewer = function(config){
30036 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30041 * Fire after initEvent
30042 * @param {Roo.bootstrap.DocumentViewer} this
30048 * @param {Roo.bootstrap.DocumentViewer} this
30053 * Fire after download button
30054 * @param {Roo.bootstrap.DocumentViewer} this
30059 * Fire after trash button
30060 * @param {Roo.bootstrap.DocumentViewer} this
30067 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
30069 showDownload : true,
30073 getAutoCreate : function()
30077 cls : 'roo-document-viewer',
30081 cls : 'roo-document-viewer-body',
30085 cls : 'roo-document-viewer-thumb',
30089 cls : 'roo-document-viewer-image'
30097 cls : 'roo-document-viewer-footer',
30100 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
30104 cls : 'btn-group roo-document-viewer-download',
30108 cls : 'btn btn-default',
30109 html : '<i class="fa fa-download"></i>'
30115 cls : 'btn-group roo-document-viewer-trash',
30119 cls : 'btn btn-default',
30120 html : '<i class="fa fa-trash"></i>'
30133 initEvents : function()
30135 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30136 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30138 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30139 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30141 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30142 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30144 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30145 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30147 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30148 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30150 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30151 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30153 this.bodyEl.on('click', this.onClick, this);
30154 this.downloadBtn.on('click', this.onDownload, this);
30155 this.trashBtn.on('click', this.onTrash, this);
30157 this.downloadBtn.hide();
30158 this.trashBtn.hide();
30160 if(this.showDownload){
30161 this.downloadBtn.show();
30164 if(this.showTrash){
30165 this.trashBtn.show();
30168 if(!this.showDownload && !this.showTrash) {
30169 this.footerEl.hide();
30174 initial : function()
30176 this.fireEvent('initial', this);
30180 onClick : function(e)
30182 e.preventDefault();
30184 this.fireEvent('click', this);
30187 onDownload : function(e)
30189 e.preventDefault();
30191 this.fireEvent('download', this);
30194 onTrash : function(e)
30196 e.preventDefault();
30198 this.fireEvent('trash', this);
30210 * @class Roo.bootstrap.NavProgressBar
30211 * @extends Roo.bootstrap.Component
30212 * Bootstrap NavProgressBar class
30215 * Create a new nav progress bar
30216 * @param {Object} config The config object
30219 Roo.bootstrap.NavProgressBar = function(config){
30220 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30222 this.bullets = this.bullets || [];
30224 // Roo.bootstrap.NavProgressBar.register(this);
30228 * Fires when the active item changes
30229 * @param {Roo.bootstrap.NavProgressBar} this
30230 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30231 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
30238 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
30243 getAutoCreate : function()
30245 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30249 cls : 'roo-navigation-bar-group',
30253 cls : 'roo-navigation-top-bar'
30257 cls : 'roo-navigation-bullets-bar',
30261 cls : 'roo-navigation-bar'
30268 cls : 'roo-navigation-bottom-bar'
30278 initEvents: function()
30283 onRender : function(ct, position)
30285 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30287 if(this.bullets.length){
30288 Roo.each(this.bullets, function(b){
30297 addItem : function(cfg)
30299 var item = new Roo.bootstrap.NavProgressItem(cfg);
30301 item.parentId = this.id;
30302 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30305 var top = new Roo.bootstrap.Element({
30307 cls : 'roo-navigation-bar-text'
30310 var bottom = new Roo.bootstrap.Element({
30312 cls : 'roo-navigation-bar-text'
30315 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30316 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30318 var topText = new Roo.bootstrap.Element({
30320 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30323 var bottomText = new Roo.bootstrap.Element({
30325 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30328 topText.onRender(top.el, null);
30329 bottomText.onRender(bottom.el, null);
30332 item.bottomEl = bottom;
30335 this.barItems.push(item);
30340 getActive : function()
30342 var active = false;
30344 Roo.each(this.barItems, function(v){
30346 if (!v.isActive()) {
30358 setActiveItem : function(item)
30362 Roo.each(this.barItems, function(v){
30363 if (v.rid == item.rid) {
30367 if (v.isActive()) {
30368 v.setActive(false);
30373 item.setActive(true);
30375 this.fireEvent('changed', this, item, prev);
30378 getBarItem: function(rid)
30382 Roo.each(this.barItems, function(e) {
30383 if (e.rid != rid) {
30394 indexOfItem : function(item)
30398 Roo.each(this.barItems, function(v, i){
30400 if (v.rid != item.rid) {
30411 setActiveNext : function()
30413 var i = this.indexOfItem(this.getActive());
30415 if (i > this.barItems.length) {
30419 this.setActiveItem(this.barItems[i+1]);
30422 setActivePrev : function()
30424 var i = this.indexOfItem(this.getActive());
30430 this.setActiveItem(this.barItems[i-1]);
30433 format : function()
30435 if(!this.barItems.length){
30439 var width = 100 / this.barItems.length;
30441 Roo.each(this.barItems, function(i){
30442 i.el.setStyle('width', width + '%');
30443 i.topEl.el.setStyle('width', width + '%');
30444 i.bottomEl.el.setStyle('width', width + '%');
30453 * Nav Progress Item
30458 * @class Roo.bootstrap.NavProgressItem
30459 * @extends Roo.bootstrap.Component
30460 * Bootstrap NavProgressItem class
30461 * @cfg {String} rid the reference id
30462 * @cfg {Boolean} active (true|false) Is item active default false
30463 * @cfg {Boolean} disabled (true|false) Is item active default false
30464 * @cfg {String} html
30465 * @cfg {String} position (top|bottom) text position default bottom
30466 * @cfg {String} icon show icon instead of number
30469 * Create a new NavProgressItem
30470 * @param {Object} config The config object
30472 Roo.bootstrap.NavProgressItem = function(config){
30473 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30478 * The raw click event for the entire grid.
30479 * @param {Roo.bootstrap.NavProgressItem} this
30480 * @param {Roo.EventObject} e
30487 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30493 position : 'bottom',
30496 getAutoCreate : function()
30498 var iconCls = 'roo-navigation-bar-item-icon';
30500 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30504 cls: 'roo-navigation-bar-item',
30514 cfg.cls += ' active';
30517 cfg.cls += ' disabled';
30523 disable : function()
30525 this.setDisabled(true);
30528 enable : function()
30530 this.setDisabled(false);
30533 initEvents: function()
30535 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30537 this.iconEl.on('click', this.onClick, this);
30540 onClick : function(e)
30542 e.preventDefault();
30548 if(this.fireEvent('click', this, e) === false){
30552 this.parent().setActiveItem(this);
30555 isActive: function ()
30557 return this.active;
30560 setActive : function(state)
30562 if(this.active == state){
30566 this.active = state;
30569 this.el.addClass('active');
30573 this.el.removeClass('active');
30578 setDisabled : function(state)
30580 if(this.disabled == state){
30584 this.disabled = state;
30587 this.el.addClass('disabled');
30591 this.el.removeClass('disabled');
30594 tooltipEl : function()
30596 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30609 * @class Roo.bootstrap.FieldLabel
30610 * @extends Roo.bootstrap.Component
30611 * Bootstrap FieldLabel class
30612 * @cfg {String} html contents of the element
30613 * @cfg {String} tag tag of the element default label
30614 * @cfg {String} cls class of the element
30615 * @cfg {String} target label target
30616 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30617 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30618 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30619 * @cfg {String} iconTooltip default "This field is required"
30620 * @cfg {String} indicatorpos (left|right) default left
30623 * Create a new FieldLabel
30624 * @param {Object} config The config object
30627 Roo.bootstrap.FieldLabel = function(config){
30628 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30633 * Fires after the field has been marked as invalid.
30634 * @param {Roo.form.FieldLabel} this
30635 * @param {String} msg The validation message
30640 * Fires after the field has been validated with no errors.
30641 * @param {Roo.form.FieldLabel} this
30647 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30654 invalidClass : 'has-warning',
30655 validClass : 'has-success',
30656 iconTooltip : 'This field is required',
30657 indicatorpos : 'left',
30659 getAutoCreate : function(){
30662 if (!this.allowBlank) {
30668 cls : 'roo-bootstrap-field-label ' + this.cls,
30673 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30674 tooltip : this.iconTooltip
30683 if(this.indicatorpos == 'right'){
30686 cls : 'roo-bootstrap-field-label ' + this.cls,
30695 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30696 tooltip : this.iconTooltip
30705 initEvents: function()
30707 Roo.bootstrap.Element.superclass.initEvents.call(this);
30709 this.indicator = this.indicatorEl();
30711 if(this.indicator){
30712 this.indicator.removeClass('visible');
30713 this.indicator.addClass('invisible');
30716 Roo.bootstrap.FieldLabel.register(this);
30719 indicatorEl : function()
30721 var indicator = this.el.select('i.roo-required-indicator',true).first();
30732 * Mark this field as valid
30734 markValid : function()
30736 if(this.indicator){
30737 this.indicator.removeClass('visible');
30738 this.indicator.addClass('invisible');
30740 if (Roo.bootstrap.version == 3) {
30741 this.el.removeClass(this.invalidClass);
30742 this.el.addClass(this.validClass);
30744 this.el.removeClass('is-invalid');
30745 this.el.addClass('is-valid');
30749 this.fireEvent('valid', this);
30753 * Mark this field as invalid
30754 * @param {String} msg The validation message
30756 markInvalid : function(msg)
30758 if(this.indicator){
30759 this.indicator.removeClass('invisible');
30760 this.indicator.addClass('visible');
30762 if (Roo.bootstrap.version == 3) {
30763 this.el.removeClass(this.validClass);
30764 this.el.addClass(this.invalidClass);
30766 this.el.removeClass('is-valid');
30767 this.el.addClass('is-invalid');
30771 this.fireEvent('invalid', this, msg);
30777 Roo.apply(Roo.bootstrap.FieldLabel, {
30782 * register a FieldLabel Group
30783 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30785 register : function(label)
30787 if(this.groups.hasOwnProperty(label.target)){
30791 this.groups[label.target] = label;
30795 * fetch a FieldLabel Group based on the target
30796 * @param {string} target
30797 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30799 get: function(target) {
30800 if (typeof(this.groups[target]) == 'undefined') {
30804 return this.groups[target] ;
30813 * page DateSplitField.
30819 * @class Roo.bootstrap.DateSplitField
30820 * @extends Roo.bootstrap.Component
30821 * Bootstrap DateSplitField class
30822 * @cfg {string} fieldLabel - the label associated
30823 * @cfg {Number} labelWidth set the width of label (0-12)
30824 * @cfg {String} labelAlign (top|left)
30825 * @cfg {Boolean} dayAllowBlank (true|false) default false
30826 * @cfg {Boolean} monthAllowBlank (true|false) default false
30827 * @cfg {Boolean} yearAllowBlank (true|false) default false
30828 * @cfg {string} dayPlaceholder
30829 * @cfg {string} monthPlaceholder
30830 * @cfg {string} yearPlaceholder
30831 * @cfg {string} dayFormat default 'd'
30832 * @cfg {string} monthFormat default 'm'
30833 * @cfg {string} yearFormat default 'Y'
30834 * @cfg {Number} labellg set the width of label (1-12)
30835 * @cfg {Number} labelmd set the width of label (1-12)
30836 * @cfg {Number} labelsm set the width of label (1-12)
30837 * @cfg {Number} labelxs set the width of label (1-12)
30841 * Create a new DateSplitField
30842 * @param {Object} config The config object
30845 Roo.bootstrap.DateSplitField = function(config){
30846 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30852 * getting the data of years
30853 * @param {Roo.bootstrap.DateSplitField} this
30854 * @param {Object} years
30859 * getting the data of days
30860 * @param {Roo.bootstrap.DateSplitField} this
30861 * @param {Object} days
30866 * Fires after the field has been marked as invalid.
30867 * @param {Roo.form.Field} this
30868 * @param {String} msg The validation message
30873 * Fires after the field has been validated with no errors.
30874 * @param {Roo.form.Field} this
30880 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30883 labelAlign : 'top',
30885 dayAllowBlank : false,
30886 monthAllowBlank : false,
30887 yearAllowBlank : false,
30888 dayPlaceholder : '',
30889 monthPlaceholder : '',
30890 yearPlaceholder : '',
30894 isFormField : true,
30900 getAutoCreate : function()
30904 cls : 'row roo-date-split-field-group',
30909 cls : 'form-hidden-field roo-date-split-field-group-value',
30915 var labelCls = 'col-md-12';
30916 var contentCls = 'col-md-4';
30918 if(this.fieldLabel){
30922 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30926 html : this.fieldLabel
30931 if(this.labelAlign == 'left'){
30933 if(this.labelWidth > 12){
30934 label.style = "width: " + this.labelWidth + 'px';
30937 if(this.labelWidth < 13 && this.labelmd == 0){
30938 this.labelmd = this.labelWidth;
30941 if(this.labellg > 0){
30942 labelCls = ' col-lg-' + this.labellg;
30943 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30946 if(this.labelmd > 0){
30947 labelCls = ' col-md-' + this.labelmd;
30948 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30951 if(this.labelsm > 0){
30952 labelCls = ' col-sm-' + this.labelsm;
30953 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30956 if(this.labelxs > 0){
30957 labelCls = ' col-xs-' + this.labelxs;
30958 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30962 label.cls += ' ' + labelCls;
30964 cfg.cn.push(label);
30967 Roo.each(['day', 'month', 'year'], function(t){
30970 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30977 inputEl: function ()
30979 return this.el.select('.roo-date-split-field-group-value', true).first();
30982 onRender : function(ct, position)
30986 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30988 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30990 this.dayField = new Roo.bootstrap.ComboBox({
30991 allowBlank : this.dayAllowBlank,
30992 alwaysQuery : true,
30993 displayField : 'value',
30996 forceSelection : true,
30998 placeholder : this.dayPlaceholder,
30999 selectOnFocus : true,
31000 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31001 triggerAction : 'all',
31003 valueField : 'value',
31004 store : new Roo.data.SimpleStore({
31005 data : (function() {
31007 _this.fireEvent('days', _this, days);
31010 fields : [ 'value' ]
31013 select : function (_self, record, index)
31015 _this.setValue(_this.getValue());
31020 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
31022 this.monthField = new Roo.bootstrap.MonthField({
31023 after : '<i class=\"fa fa-calendar\"></i>',
31024 allowBlank : this.monthAllowBlank,
31025 placeholder : this.monthPlaceholder,
31028 render : function (_self)
31030 this.el.select('span.input-group-addon', true).first().on('click', function(e){
31031 e.preventDefault();
31035 select : function (_self, oldvalue, newvalue)
31037 _this.setValue(_this.getValue());
31042 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31044 this.yearField = new Roo.bootstrap.ComboBox({
31045 allowBlank : this.yearAllowBlank,
31046 alwaysQuery : true,
31047 displayField : 'value',
31050 forceSelection : true,
31052 placeholder : this.yearPlaceholder,
31053 selectOnFocus : true,
31054 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31055 triggerAction : 'all',
31057 valueField : 'value',
31058 store : new Roo.data.SimpleStore({
31059 data : (function() {
31061 _this.fireEvent('years', _this, years);
31064 fields : [ 'value' ]
31067 select : function (_self, record, index)
31069 _this.setValue(_this.getValue());
31074 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
31077 setValue : function(v, format)
31079 this.inputEl.dom.value = v;
31081 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
31083 var d = Date.parseDate(v, f);
31090 this.setDay(d.format(this.dayFormat));
31091 this.setMonth(d.format(this.monthFormat));
31092 this.setYear(d.format(this.yearFormat));
31099 setDay : function(v)
31101 this.dayField.setValue(v);
31102 this.inputEl.dom.value = this.getValue();
31107 setMonth : function(v)
31109 this.monthField.setValue(v, true);
31110 this.inputEl.dom.value = this.getValue();
31115 setYear : function(v)
31117 this.yearField.setValue(v);
31118 this.inputEl.dom.value = this.getValue();
31123 getDay : function()
31125 return this.dayField.getValue();
31128 getMonth : function()
31130 return this.monthField.getValue();
31133 getYear : function()
31135 return this.yearField.getValue();
31138 getValue : function()
31140 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31142 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31152 this.inputEl.dom.value = '';
31157 validate : function()
31159 var d = this.dayField.validate();
31160 var m = this.monthField.validate();
31161 var y = this.yearField.validate();
31166 (!this.dayAllowBlank && !d) ||
31167 (!this.monthAllowBlank && !m) ||
31168 (!this.yearAllowBlank && !y)
31173 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31182 this.markInvalid();
31187 markValid : function()
31190 var label = this.el.select('label', true).first();
31191 var icon = this.el.select('i.fa-star', true).first();
31197 this.fireEvent('valid', this);
31201 * Mark this field as invalid
31202 * @param {String} msg The validation message
31204 markInvalid : function(msg)
31207 var label = this.el.select('label', true).first();
31208 var icon = this.el.select('i.fa-star', true).first();
31210 if(label && !icon){
31211 this.el.select('.roo-date-split-field-label', true).createChild({
31213 cls : 'text-danger fa fa-lg fa-star',
31214 tooltip : 'This field is required',
31215 style : 'margin-right:5px;'
31219 this.fireEvent('invalid', this, msg);
31222 clearInvalid : function()
31224 var label = this.el.select('label', true).first();
31225 var icon = this.el.select('i.fa-star', true).first();
31231 this.fireEvent('valid', this);
31234 getName: function()
31244 * http://masonry.desandro.com
31246 * The idea is to render all the bricks based on vertical width...
31248 * The original code extends 'outlayer' - we might need to use that....
31254 * @class Roo.bootstrap.LayoutMasonry
31255 * @extends Roo.bootstrap.Component
31256 * Bootstrap Layout Masonry class
31259 * Create a new Element
31260 * @param {Object} config The config object
31263 Roo.bootstrap.LayoutMasonry = function(config){
31265 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31269 Roo.bootstrap.LayoutMasonry.register(this);
31275 * Fire after layout the items
31276 * @param {Roo.bootstrap.LayoutMasonry} this
31277 * @param {Roo.EventObject} e
31284 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
31287 * @cfg {Boolean} isLayoutInstant = no animation?
31289 isLayoutInstant : false, // needed?
31292 * @cfg {Number} boxWidth width of the columns
31297 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
31302 * @cfg {Number} padWidth padding below box..
31307 * @cfg {Number} gutter gutter width..
31312 * @cfg {Number} maxCols maximum number of columns
31318 * @cfg {Boolean} isAutoInitial defalut true
31320 isAutoInitial : true,
31325 * @cfg {Boolean} isHorizontal defalut false
31327 isHorizontal : false,
31329 currentSize : null,
31335 bricks: null, //CompositeElement
31339 _isLayoutInited : false,
31341 // isAlternative : false, // only use for vertical layout...
31344 * @cfg {Number} alternativePadWidth padding below box..
31346 alternativePadWidth : 50,
31348 selectedBrick : [],
31350 getAutoCreate : function(){
31352 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31356 cls: 'blog-masonary-wrapper ' + this.cls,
31358 cls : 'mas-boxes masonary'
31365 getChildContainer: function( )
31367 if (this.boxesEl) {
31368 return this.boxesEl;
31371 this.boxesEl = this.el.select('.mas-boxes').first();
31373 return this.boxesEl;
31377 initEvents : function()
31381 if(this.isAutoInitial){
31382 Roo.log('hook children rendered');
31383 this.on('childrenrendered', function() {
31384 Roo.log('children rendered');
31390 initial : function()
31392 this.selectedBrick = [];
31394 this.currentSize = this.el.getBox(true);
31396 Roo.EventManager.onWindowResize(this.resize, this);
31398 if(!this.isAutoInitial){
31406 //this.layout.defer(500,this);
31410 resize : function()
31412 var cs = this.el.getBox(true);
31415 this.currentSize.width == cs.width &&
31416 this.currentSize.x == cs.x &&
31417 this.currentSize.height == cs.height &&
31418 this.currentSize.y == cs.y
31420 Roo.log("no change in with or X or Y");
31424 this.currentSize = cs;
31430 layout : function()
31432 this._resetLayout();
31434 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31436 this.layoutItems( isInstant );
31438 this._isLayoutInited = true;
31440 this.fireEvent('layout', this);
31444 _resetLayout : function()
31446 if(this.isHorizontal){
31447 this.horizontalMeasureColumns();
31451 this.verticalMeasureColumns();
31455 verticalMeasureColumns : function()
31457 this.getContainerWidth();
31459 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31460 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31464 var boxWidth = this.boxWidth + this.padWidth;
31466 if(this.containerWidth < this.boxWidth){
31467 boxWidth = this.containerWidth
31470 var containerWidth = this.containerWidth;
31472 var cols = Math.floor(containerWidth / boxWidth);
31474 this.cols = Math.max( cols, 1 );
31476 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31478 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31480 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31482 this.colWidth = boxWidth + avail - this.padWidth;
31484 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31485 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31488 horizontalMeasureColumns : function()
31490 this.getContainerWidth();
31492 var boxWidth = this.boxWidth;
31494 if(this.containerWidth < boxWidth){
31495 boxWidth = this.containerWidth;
31498 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31500 this.el.setHeight(boxWidth);
31504 getContainerWidth : function()
31506 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31509 layoutItems : function( isInstant )
31511 Roo.log(this.bricks);
31513 var items = Roo.apply([], this.bricks);
31515 if(this.isHorizontal){
31516 this._horizontalLayoutItems( items , isInstant );
31520 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31521 // this._verticalAlternativeLayoutItems( items , isInstant );
31525 this._verticalLayoutItems( items , isInstant );
31529 _verticalLayoutItems : function ( items , isInstant)
31531 if ( !items || !items.length ) {
31536 ['xs', 'xs', 'xs', 'tall'],
31537 ['xs', 'xs', 'tall'],
31538 ['xs', 'xs', 'sm'],
31539 ['xs', 'xs', 'xs'],
31545 ['sm', 'xs', 'xs'],
31549 ['tall', 'xs', 'xs', 'xs'],
31550 ['tall', 'xs', 'xs'],
31562 Roo.each(items, function(item, k){
31564 switch (item.size) {
31565 // these layouts take up a full box,
31576 boxes.push([item]);
31599 var filterPattern = function(box, length)
31607 var pattern = box.slice(0, length);
31611 Roo.each(pattern, function(i){
31612 format.push(i.size);
31615 Roo.each(standard, function(s){
31617 if(String(s) != String(format)){
31626 if(!match && length == 1){
31631 filterPattern(box, length - 1);
31635 queue.push(pattern);
31637 box = box.slice(length, box.length);
31639 filterPattern(box, 4);
31645 Roo.each(boxes, function(box, k){
31651 if(box.length == 1){
31656 filterPattern(box, 4);
31660 this._processVerticalLayoutQueue( queue, isInstant );
31664 // _verticalAlternativeLayoutItems : function( items , isInstant )
31666 // if ( !items || !items.length ) {
31670 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31674 _horizontalLayoutItems : function ( items , isInstant)
31676 if ( !items || !items.length || items.length < 3) {
31682 var eItems = items.slice(0, 3);
31684 items = items.slice(3, items.length);
31687 ['xs', 'xs', 'xs', 'wide'],
31688 ['xs', 'xs', 'wide'],
31689 ['xs', 'xs', 'sm'],
31690 ['xs', 'xs', 'xs'],
31696 ['sm', 'xs', 'xs'],
31700 ['wide', 'xs', 'xs', 'xs'],
31701 ['wide', 'xs', 'xs'],
31714 Roo.each(items, function(item, k){
31716 switch (item.size) {
31727 boxes.push([item]);
31751 var filterPattern = function(box, length)
31759 var pattern = box.slice(0, length);
31763 Roo.each(pattern, function(i){
31764 format.push(i.size);
31767 Roo.each(standard, function(s){
31769 if(String(s) != String(format)){
31778 if(!match && length == 1){
31783 filterPattern(box, length - 1);
31787 queue.push(pattern);
31789 box = box.slice(length, box.length);
31791 filterPattern(box, 4);
31797 Roo.each(boxes, function(box, k){
31803 if(box.length == 1){
31808 filterPattern(box, 4);
31815 var pos = this.el.getBox(true);
31819 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31821 var hit_end = false;
31823 Roo.each(queue, function(box){
31827 Roo.each(box, function(b){
31829 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31839 Roo.each(box, function(b){
31841 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31844 mx = Math.max(mx, b.x);
31848 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31852 Roo.each(box, function(b){
31854 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31868 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31871 /** Sets position of item in DOM
31872 * @param {Element} item
31873 * @param {Number} x - horizontal position
31874 * @param {Number} y - vertical position
31875 * @param {Boolean} isInstant - disables transitions
31877 _processVerticalLayoutQueue : function( queue, isInstant )
31879 var pos = this.el.getBox(true);
31884 for (var i = 0; i < this.cols; i++){
31888 Roo.each(queue, function(box, k){
31890 var col = k % this.cols;
31892 Roo.each(box, function(b,kk){
31894 b.el.position('absolute');
31896 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31897 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31899 if(b.size == 'md-left' || b.size == 'md-right'){
31900 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31901 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31904 b.el.setWidth(width);
31905 b.el.setHeight(height);
31907 b.el.select('iframe',true).setSize(width,height);
31911 for (var i = 0; i < this.cols; i++){
31913 if(maxY[i] < maxY[col]){
31918 col = Math.min(col, i);
31922 x = pos.x + col * (this.colWidth + this.padWidth);
31926 var positions = [];
31928 switch (box.length){
31930 positions = this.getVerticalOneBoxColPositions(x, y, box);
31933 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31936 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31939 positions = this.getVerticalFourBoxColPositions(x, y, box);
31945 Roo.each(box, function(b,kk){
31947 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31949 var sz = b.el.getSize();
31951 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31959 for (var i = 0; i < this.cols; i++){
31960 mY = Math.max(mY, maxY[i]);
31963 this.el.setHeight(mY - pos.y);
31967 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31969 // var pos = this.el.getBox(true);
31972 // var maxX = pos.right;
31974 // var maxHeight = 0;
31976 // Roo.each(items, function(item, k){
31980 // item.el.position('absolute');
31982 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31984 // item.el.setWidth(width);
31986 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31988 // item.el.setHeight(height);
31991 // item.el.setXY([x, y], isInstant ? false : true);
31993 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31996 // y = y + height + this.alternativePadWidth;
31998 // maxHeight = maxHeight + height + this.alternativePadWidth;
32002 // this.el.setHeight(maxHeight);
32006 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
32008 var pos = this.el.getBox(true);
32013 var maxX = pos.right;
32015 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
32017 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32019 Roo.each(queue, function(box, k){
32021 Roo.each(box, function(b, kk){
32023 b.el.position('absolute');
32025 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32026 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32028 if(b.size == 'md-left' || b.size == 'md-right'){
32029 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32030 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32033 b.el.setWidth(width);
32034 b.el.setHeight(height);
32042 var positions = [];
32044 switch (box.length){
32046 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32049 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
32052 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
32055 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
32061 Roo.each(box, function(b,kk){
32063 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32065 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
32073 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
32075 Roo.each(eItems, function(b,k){
32077 b.size = (k == 0) ? 'sm' : 'xs';
32078 b.x = (k == 0) ? 2 : 1;
32079 b.y = (k == 0) ? 2 : 1;
32081 b.el.position('absolute');
32083 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32085 b.el.setWidth(width);
32087 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32089 b.el.setHeight(height);
32093 var positions = [];
32096 x : maxX - this.unitWidth * 2 - this.gutter,
32101 x : maxX - this.unitWidth,
32102 y : minY + (this.unitWidth + this.gutter) * 2
32106 x : maxX - this.unitWidth * 3 - this.gutter * 2,
32110 Roo.each(eItems, function(b,k){
32112 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32118 getVerticalOneBoxColPositions : function(x, y, box)
32122 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32124 if(box[0].size == 'md-left'){
32128 if(box[0].size == 'md-right'){
32133 x : x + (this.unitWidth + this.gutter) * rand,
32140 getVerticalTwoBoxColPositions : function(x, y, box)
32144 if(box[0].size == 'xs'){
32148 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32152 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32166 x : x + (this.unitWidth + this.gutter) * 2,
32167 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32174 getVerticalThreeBoxColPositions : function(x, y, box)
32178 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32186 x : x + (this.unitWidth + this.gutter) * 1,
32191 x : x + (this.unitWidth + this.gutter) * 2,
32199 if(box[0].size == 'xs' && box[1].size == 'xs'){
32208 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32212 x : x + (this.unitWidth + this.gutter) * 1,
32226 x : x + (this.unitWidth + this.gutter) * 2,
32231 x : x + (this.unitWidth + this.gutter) * 2,
32232 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32239 getVerticalFourBoxColPositions : function(x, y, box)
32243 if(box[0].size == 'xs'){
32252 y : y + (this.unitHeight + this.gutter) * 1
32257 y : y + (this.unitHeight + this.gutter) * 2
32261 x : x + (this.unitWidth + this.gutter) * 1,
32275 x : x + (this.unitWidth + this.gutter) * 2,
32280 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32281 y : y + (this.unitHeight + this.gutter) * 1
32285 x : x + (this.unitWidth + this.gutter) * 2,
32286 y : y + (this.unitWidth + this.gutter) * 2
32293 getHorizontalOneBoxColPositions : function(maxX, minY, box)
32297 if(box[0].size == 'md-left'){
32299 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32306 if(box[0].size == 'md-right'){
32308 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32309 y : minY + (this.unitWidth + this.gutter) * 1
32315 var rand = Math.floor(Math.random() * (4 - box[0].y));
32318 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32319 y : minY + (this.unitWidth + this.gutter) * rand
32326 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32330 if(box[0].size == 'xs'){
32333 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32338 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32339 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32347 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32352 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32353 y : minY + (this.unitWidth + this.gutter) * 2
32360 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32364 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32367 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32372 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32373 y : minY + (this.unitWidth + this.gutter) * 1
32377 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32378 y : minY + (this.unitWidth + this.gutter) * 2
32385 if(box[0].size == 'xs' && box[1].size == 'xs'){
32388 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32393 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32398 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32399 y : minY + (this.unitWidth + this.gutter) * 1
32407 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32412 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32413 y : minY + (this.unitWidth + this.gutter) * 2
32417 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32418 y : minY + (this.unitWidth + this.gutter) * 2
32425 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32429 if(box[0].size == 'xs'){
32432 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32437 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32442 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),
32447 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32448 y : minY + (this.unitWidth + this.gutter) * 1
32456 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32461 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32462 y : minY + (this.unitWidth + this.gutter) * 2
32466 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32467 y : minY + (this.unitWidth + this.gutter) * 2
32471 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),
32472 y : minY + (this.unitWidth + this.gutter) * 2
32480 * remove a Masonry Brick
32481 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32483 removeBrick : function(brick_id)
32489 for (var i = 0; i<this.bricks.length; i++) {
32490 if (this.bricks[i].id == brick_id) {
32491 this.bricks.splice(i,1);
32492 this.el.dom.removeChild(Roo.get(brick_id).dom);
32499 * adds a Masonry Brick
32500 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32502 addBrick : function(cfg)
32504 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32505 //this.register(cn);
32506 cn.parentId = this.id;
32507 cn.render(this.el);
32512 * register a Masonry Brick
32513 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32516 register : function(brick)
32518 this.bricks.push(brick);
32519 brick.masonryId = this.id;
32523 * clear all the Masonry Brick
32525 clearAll : function()
32528 //this.getChildContainer().dom.innerHTML = "";
32529 this.el.dom.innerHTML = '';
32532 getSelected : function()
32534 if (!this.selectedBrick) {
32538 return this.selectedBrick;
32542 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32546 * register a Masonry Layout
32547 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32550 register : function(layout)
32552 this.groups[layout.id] = layout;
32555 * fetch a Masonry Layout based on the masonry layout ID
32556 * @param {string} the masonry layout to add
32557 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32560 get: function(layout_id) {
32561 if (typeof(this.groups[layout_id]) == 'undefined') {
32564 return this.groups[layout_id] ;
32576 * http://masonry.desandro.com
32578 * The idea is to render all the bricks based on vertical width...
32580 * The original code extends 'outlayer' - we might need to use that....
32586 * @class Roo.bootstrap.LayoutMasonryAuto
32587 * @extends Roo.bootstrap.Component
32588 * Bootstrap Layout Masonry class
32591 * Create a new Element
32592 * @param {Object} config The config object
32595 Roo.bootstrap.LayoutMasonryAuto = function(config){
32596 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32599 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32602 * @cfg {Boolean} isFitWidth - resize the width..
32604 isFitWidth : false, // options..
32606 * @cfg {Boolean} isOriginLeft = left align?
32608 isOriginLeft : true,
32610 * @cfg {Boolean} isOriginTop = top align?
32612 isOriginTop : false,
32614 * @cfg {Boolean} isLayoutInstant = no animation?
32616 isLayoutInstant : false, // needed?
32618 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32620 isResizingContainer : true,
32622 * @cfg {Number} columnWidth width of the columns
32628 * @cfg {Number} maxCols maximum number of columns
32633 * @cfg {Number} padHeight padding below box..
32639 * @cfg {Boolean} isAutoInitial defalut true
32642 isAutoInitial : true,
32648 initialColumnWidth : 0,
32649 currentSize : null,
32651 colYs : null, // array.
32658 bricks: null, //CompositeElement
32659 cols : 0, // array?
32660 // element : null, // wrapped now this.el
32661 _isLayoutInited : null,
32664 getAutoCreate : function(){
32668 cls: 'blog-masonary-wrapper ' + this.cls,
32670 cls : 'mas-boxes masonary'
32677 getChildContainer: function( )
32679 if (this.boxesEl) {
32680 return this.boxesEl;
32683 this.boxesEl = this.el.select('.mas-boxes').first();
32685 return this.boxesEl;
32689 initEvents : function()
32693 if(this.isAutoInitial){
32694 Roo.log('hook children rendered');
32695 this.on('childrenrendered', function() {
32696 Roo.log('children rendered');
32703 initial : function()
32705 this.reloadItems();
32707 this.currentSize = this.el.getBox(true);
32709 /// was window resize... - let's see if this works..
32710 Roo.EventManager.onWindowResize(this.resize, this);
32712 if(!this.isAutoInitial){
32717 this.layout.defer(500,this);
32720 reloadItems: function()
32722 this.bricks = this.el.select('.masonry-brick', true);
32724 this.bricks.each(function(b) {
32725 //Roo.log(b.getSize());
32726 if (!b.attr('originalwidth')) {
32727 b.attr('originalwidth', b.getSize().width);
32732 Roo.log(this.bricks.elements.length);
32735 resize : function()
32738 var cs = this.el.getBox(true);
32740 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32741 Roo.log("no change in with or X");
32744 this.currentSize = cs;
32748 layout : function()
32751 this._resetLayout();
32752 //this._manageStamps();
32754 // don't animate first layout
32755 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32756 this.layoutItems( isInstant );
32758 // flag for initalized
32759 this._isLayoutInited = true;
32762 layoutItems : function( isInstant )
32764 //var items = this._getItemsForLayout( this.items );
32765 // original code supports filtering layout items.. we just ignore it..
32767 this._layoutItems( this.bricks , isInstant );
32769 this._postLayout();
32771 _layoutItems : function ( items , isInstant)
32773 //this.fireEvent( 'layout', this, items );
32776 if ( !items || !items.elements.length ) {
32777 // no items, emit event with empty array
32782 items.each(function(item) {
32783 Roo.log("layout item");
32785 // get x/y object from method
32786 var position = this._getItemLayoutPosition( item );
32788 position.item = item;
32789 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32790 queue.push( position );
32793 this._processLayoutQueue( queue );
32795 /** Sets position of item in DOM
32796 * @param {Element} item
32797 * @param {Number} x - horizontal position
32798 * @param {Number} y - vertical position
32799 * @param {Boolean} isInstant - disables transitions
32801 _processLayoutQueue : function( queue )
32803 for ( var i=0, len = queue.length; i < len; i++ ) {
32804 var obj = queue[i];
32805 obj.item.position('absolute');
32806 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32812 * Any logic you want to do after each layout,
32813 * i.e. size the container
32815 _postLayout : function()
32817 this.resizeContainer();
32820 resizeContainer : function()
32822 if ( !this.isResizingContainer ) {
32825 var size = this._getContainerSize();
32827 this.el.setSize(size.width,size.height);
32828 this.boxesEl.setSize(size.width,size.height);
32834 _resetLayout : function()
32836 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32837 this.colWidth = this.el.getWidth();
32838 //this.gutter = this.el.getWidth();
32840 this.measureColumns();
32846 this.colYs.push( 0 );
32852 measureColumns : function()
32854 this.getContainerWidth();
32855 // if columnWidth is 0, default to outerWidth of first item
32856 if ( !this.columnWidth ) {
32857 var firstItem = this.bricks.first();
32858 Roo.log(firstItem);
32859 this.columnWidth = this.containerWidth;
32860 if (firstItem && firstItem.attr('originalwidth') ) {
32861 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32863 // columnWidth fall back to item of first element
32864 Roo.log("set column width?");
32865 this.initialColumnWidth = this.columnWidth ;
32867 // if first elem has no width, default to size of container
32872 if (this.initialColumnWidth) {
32873 this.columnWidth = this.initialColumnWidth;
32878 // column width is fixed at the top - however if container width get's smaller we should
32881 // this bit calcs how man columns..
32883 var columnWidth = this.columnWidth += this.gutter;
32885 // calculate columns
32886 var containerWidth = this.containerWidth + this.gutter;
32888 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32889 // fix rounding errors, typically with gutters
32890 var excess = columnWidth - containerWidth % columnWidth;
32893 // if overshoot is less than a pixel, round up, otherwise floor it
32894 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32895 cols = Math[ mathMethod ]( cols );
32896 this.cols = Math.max( cols, 1 );
32897 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32899 // padding positioning..
32900 var totalColWidth = this.cols * this.columnWidth;
32901 var padavail = this.containerWidth - totalColWidth;
32902 // so for 2 columns - we need 3 'pads'
32904 var padNeeded = (1+this.cols) * this.padWidth;
32906 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32908 this.columnWidth += padExtra
32909 //this.padWidth = Math.floor(padavail / ( this.cols));
32911 // adjust colum width so that padding is fixed??
32913 // we have 3 columns ... total = width * 3
32914 // we have X left over... that should be used by
32916 //if (this.expandC) {
32924 getContainerWidth : function()
32926 /* // container is parent if fit width
32927 var container = this.isFitWidth ? this.element.parentNode : this.element;
32928 // check that this.size and size are there
32929 // IE8 triggers resize on body size change, so they might not be
32931 var size = getSize( container ); //FIXME
32932 this.containerWidth = size && size.innerWidth; //FIXME
32935 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32939 _getItemLayoutPosition : function( item ) // what is item?
32941 // we resize the item to our columnWidth..
32943 item.setWidth(this.columnWidth);
32944 item.autoBoxAdjust = false;
32946 var sz = item.getSize();
32948 // how many columns does this brick span
32949 var remainder = this.containerWidth % this.columnWidth;
32951 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32952 // round if off by 1 pixel, otherwise use ceil
32953 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32954 colSpan = Math.min( colSpan, this.cols );
32956 // normally this should be '1' as we dont' currently allow multi width columns..
32958 var colGroup = this._getColGroup( colSpan );
32959 // get the minimum Y value from the columns
32960 var minimumY = Math.min.apply( Math, colGroup );
32961 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32963 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32965 // position the brick
32967 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32968 y: this.currentSize.y + minimumY + this.padHeight
32972 // apply setHeight to necessary columns
32973 var setHeight = minimumY + sz.height + this.padHeight;
32974 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32976 var setSpan = this.cols + 1 - colGroup.length;
32977 for ( var i = 0; i < setSpan; i++ ) {
32978 this.colYs[ shortColIndex + i ] = setHeight ;
32985 * @param {Number} colSpan - number of columns the element spans
32986 * @returns {Array} colGroup
32988 _getColGroup : function( colSpan )
32990 if ( colSpan < 2 ) {
32991 // if brick spans only one column, use all the column Ys
32996 // how many different places could this brick fit horizontally
32997 var groupCount = this.cols + 1 - colSpan;
32998 // for each group potential horizontal position
32999 for ( var i = 0; i < groupCount; i++ ) {
33000 // make an array of colY values for that one group
33001 var groupColYs = this.colYs.slice( i, i + colSpan );
33002 // and get the max value of the array
33003 colGroup[i] = Math.max.apply( Math, groupColYs );
33008 _manageStamp : function( stamp )
33010 var stampSize = stamp.getSize();
33011 var offset = stamp.getBox();
33012 // get the columns that this stamp affects
33013 var firstX = this.isOriginLeft ? offset.x : offset.right;
33014 var lastX = firstX + stampSize.width;
33015 var firstCol = Math.floor( firstX / this.columnWidth );
33016 firstCol = Math.max( 0, firstCol );
33018 var lastCol = Math.floor( lastX / this.columnWidth );
33019 // lastCol should not go over if multiple of columnWidth #425
33020 lastCol -= lastX % this.columnWidth ? 0 : 1;
33021 lastCol = Math.min( this.cols - 1, lastCol );
33023 // set colYs to bottom of the stamp
33024 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
33027 for ( var i = firstCol; i <= lastCol; i++ ) {
33028 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33033 _getContainerSize : function()
33035 this.maxY = Math.max.apply( Math, this.colYs );
33040 if ( this.isFitWidth ) {
33041 size.width = this._getContainerFitWidth();
33047 _getContainerFitWidth : function()
33049 var unusedCols = 0;
33050 // count unused columns
33053 if ( this.colYs[i] !== 0 ) {
33058 // fit container to columns that have been used
33059 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
33062 needsResizeLayout : function()
33064 var previousWidth = this.containerWidth;
33065 this.getContainerWidth();
33066 return previousWidth !== this.containerWidth;
33081 * @class Roo.bootstrap.MasonryBrick
33082 * @extends Roo.bootstrap.Component
33083 * Bootstrap MasonryBrick class
33086 * Create a new MasonryBrick
33087 * @param {Object} config The config object
33090 Roo.bootstrap.MasonryBrick = function(config){
33092 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
33094 Roo.bootstrap.MasonryBrick.register(this);
33100 * When a MasonryBrick is clcik
33101 * @param {Roo.bootstrap.MasonryBrick} this
33102 * @param {Roo.EventObject} e
33108 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
33111 * @cfg {String} title
33115 * @cfg {String} html
33119 * @cfg {String} bgimage
33123 * @cfg {String} videourl
33127 * @cfg {String} cls
33131 * @cfg {String} href
33135 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33140 * @cfg {String} placetitle (center|bottom)
33145 * @cfg {Boolean} isFitContainer defalut true
33147 isFitContainer : true,
33150 * @cfg {Boolean} preventDefault defalut false
33152 preventDefault : false,
33155 * @cfg {Boolean} inverse defalut false
33157 maskInverse : false,
33159 getAutoCreate : function()
33161 if(!this.isFitContainer){
33162 return this.getSplitAutoCreate();
33165 var cls = 'masonry-brick masonry-brick-full';
33167 if(this.href.length){
33168 cls += ' masonry-brick-link';
33171 if(this.bgimage.length){
33172 cls += ' masonry-brick-image';
33175 if(this.maskInverse){
33176 cls += ' mask-inverse';
33179 if(!this.html.length && !this.maskInverse && !this.videourl.length){
33180 cls += ' enable-mask';
33184 cls += ' masonry-' + this.size + '-brick';
33187 if(this.placetitle.length){
33189 switch (this.placetitle) {
33191 cls += ' masonry-center-title';
33194 cls += ' masonry-bottom-title';
33201 if(!this.html.length && !this.bgimage.length){
33202 cls += ' masonry-center-title';
33205 if(!this.html.length && this.bgimage.length){
33206 cls += ' masonry-bottom-title';
33211 cls += ' ' + this.cls;
33215 tag: (this.href.length) ? 'a' : 'div',
33220 cls: 'masonry-brick-mask'
33224 cls: 'masonry-brick-paragraph',
33230 if(this.href.length){
33231 cfg.href = this.href;
33234 var cn = cfg.cn[1].cn;
33236 if(this.title.length){
33239 cls: 'masonry-brick-title',
33244 if(this.html.length){
33247 cls: 'masonry-brick-text',
33252 if (!this.title.length && !this.html.length) {
33253 cfg.cn[1].cls += ' hide';
33256 if(this.bgimage.length){
33259 cls: 'masonry-brick-image-view',
33264 if(this.videourl.length){
33265 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33266 // youtube support only?
33269 cls: 'masonry-brick-image-view',
33272 allowfullscreen : true
33280 getSplitAutoCreate : function()
33282 var cls = 'masonry-brick masonry-brick-split';
33284 if(this.href.length){
33285 cls += ' masonry-brick-link';
33288 if(this.bgimage.length){
33289 cls += ' masonry-brick-image';
33293 cls += ' masonry-' + this.size + '-brick';
33296 switch (this.placetitle) {
33298 cls += ' masonry-center-title';
33301 cls += ' masonry-bottom-title';
33304 if(!this.bgimage.length){
33305 cls += ' masonry-center-title';
33308 if(this.bgimage.length){
33309 cls += ' masonry-bottom-title';
33315 cls += ' ' + this.cls;
33319 tag: (this.href.length) ? 'a' : 'div',
33324 cls: 'masonry-brick-split-head',
33328 cls: 'masonry-brick-paragraph',
33335 cls: 'masonry-brick-split-body',
33341 if(this.href.length){
33342 cfg.href = this.href;
33345 if(this.title.length){
33346 cfg.cn[0].cn[0].cn.push({
33348 cls: 'masonry-brick-title',
33353 if(this.html.length){
33354 cfg.cn[1].cn.push({
33356 cls: 'masonry-brick-text',
33361 if(this.bgimage.length){
33362 cfg.cn[0].cn.push({
33364 cls: 'masonry-brick-image-view',
33369 if(this.videourl.length){
33370 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33371 // youtube support only?
33372 cfg.cn[0].cn.cn.push({
33374 cls: 'masonry-brick-image-view',
33377 allowfullscreen : true
33384 initEvents: function()
33386 switch (this.size) {
33419 this.el.on('touchstart', this.onTouchStart, this);
33420 this.el.on('touchmove', this.onTouchMove, this);
33421 this.el.on('touchend', this.onTouchEnd, this);
33422 this.el.on('contextmenu', this.onContextMenu, this);
33424 this.el.on('mouseenter' ,this.enter, this);
33425 this.el.on('mouseleave', this.leave, this);
33426 this.el.on('click', this.onClick, this);
33429 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33430 this.parent().bricks.push(this);
33435 onClick: function(e, el)
33437 var time = this.endTimer - this.startTimer;
33438 // Roo.log(e.preventDefault());
33441 e.preventDefault();
33446 if(!this.preventDefault){
33450 e.preventDefault();
33452 if (this.activeClass != '') {
33453 this.selectBrick();
33456 this.fireEvent('click', this, e);
33459 enter: function(e, el)
33461 e.preventDefault();
33463 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33467 if(this.bgimage.length && this.html.length){
33468 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33472 leave: function(e, el)
33474 e.preventDefault();
33476 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33480 if(this.bgimage.length && this.html.length){
33481 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33485 onTouchStart: function(e, el)
33487 // e.preventDefault();
33489 this.touchmoved = false;
33491 if(!this.isFitContainer){
33495 if(!this.bgimage.length || !this.html.length){
33499 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33501 this.timer = new Date().getTime();
33505 onTouchMove: function(e, el)
33507 this.touchmoved = true;
33510 onContextMenu : function(e,el)
33512 e.preventDefault();
33513 e.stopPropagation();
33517 onTouchEnd: function(e, el)
33519 // e.preventDefault();
33521 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33528 if(!this.bgimage.length || !this.html.length){
33530 if(this.href.length){
33531 window.location.href = this.href;
33537 if(!this.isFitContainer){
33541 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33543 window.location.href = this.href;
33546 //selection on single brick only
33547 selectBrick : function() {
33549 if (!this.parentId) {
33553 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33554 var index = m.selectedBrick.indexOf(this.id);
33557 m.selectedBrick.splice(index,1);
33558 this.el.removeClass(this.activeClass);
33562 for(var i = 0; i < m.selectedBrick.length; i++) {
33563 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33564 b.el.removeClass(b.activeClass);
33567 m.selectedBrick = [];
33569 m.selectedBrick.push(this.id);
33570 this.el.addClass(this.activeClass);
33574 isSelected : function(){
33575 return this.el.hasClass(this.activeClass);
33580 Roo.apply(Roo.bootstrap.MasonryBrick, {
33583 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33585 * register a Masonry Brick
33586 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33589 register : function(brick)
33591 //this.groups[brick.id] = brick;
33592 this.groups.add(brick.id, brick);
33595 * fetch a masonry brick based on the masonry brick ID
33596 * @param {string} the masonry brick to add
33597 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33600 get: function(brick_id)
33602 // if (typeof(this.groups[brick_id]) == 'undefined') {
33605 // return this.groups[brick_id] ;
33607 if(this.groups.key(brick_id)) {
33608 return this.groups.key(brick_id);
33626 * @class Roo.bootstrap.Brick
33627 * @extends Roo.bootstrap.Component
33628 * Bootstrap Brick class
33631 * Create a new Brick
33632 * @param {Object} config The config object
33635 Roo.bootstrap.Brick = function(config){
33636 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33642 * When a Brick is click
33643 * @param {Roo.bootstrap.Brick} this
33644 * @param {Roo.EventObject} e
33650 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33653 * @cfg {String} title
33657 * @cfg {String} html
33661 * @cfg {String} bgimage
33665 * @cfg {String} cls
33669 * @cfg {String} href
33673 * @cfg {String} video
33677 * @cfg {Boolean} square
33681 getAutoCreate : function()
33683 var cls = 'roo-brick';
33685 if(this.href.length){
33686 cls += ' roo-brick-link';
33689 if(this.bgimage.length){
33690 cls += ' roo-brick-image';
33693 if(!this.html.length && !this.bgimage.length){
33694 cls += ' roo-brick-center-title';
33697 if(!this.html.length && this.bgimage.length){
33698 cls += ' roo-brick-bottom-title';
33702 cls += ' ' + this.cls;
33706 tag: (this.href.length) ? 'a' : 'div',
33711 cls: 'roo-brick-paragraph',
33717 if(this.href.length){
33718 cfg.href = this.href;
33721 var cn = cfg.cn[0].cn;
33723 if(this.title.length){
33726 cls: 'roo-brick-title',
33731 if(this.html.length){
33734 cls: 'roo-brick-text',
33741 if(this.bgimage.length){
33744 cls: 'roo-brick-image-view',
33752 initEvents: function()
33754 if(this.title.length || this.html.length){
33755 this.el.on('mouseenter' ,this.enter, this);
33756 this.el.on('mouseleave', this.leave, this);
33759 Roo.EventManager.onWindowResize(this.resize, this);
33761 if(this.bgimage.length){
33762 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33763 this.imageEl.on('load', this.onImageLoad, this);
33770 onImageLoad : function()
33775 resize : function()
33777 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33779 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33781 if(this.bgimage.length){
33782 var image = this.el.select('.roo-brick-image-view', true).first();
33784 image.setWidth(paragraph.getWidth());
33787 image.setHeight(paragraph.getWidth());
33790 this.el.setHeight(image.getHeight());
33791 paragraph.setHeight(image.getHeight());
33797 enter: function(e, el)
33799 e.preventDefault();
33801 if(this.bgimage.length){
33802 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33803 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33807 leave: function(e, el)
33809 e.preventDefault();
33811 if(this.bgimage.length){
33812 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33813 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33828 * @class Roo.bootstrap.NumberField
33829 * @extends Roo.bootstrap.Input
33830 * Bootstrap NumberField class
33836 * Create a new NumberField
33837 * @param {Object} config The config object
33840 Roo.bootstrap.NumberField = function(config){
33841 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33844 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33847 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33849 allowDecimals : true,
33851 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33853 decimalSeparator : ".",
33855 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33857 decimalPrecision : 2,
33859 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33861 allowNegative : true,
33864 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33868 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33870 minValue : Number.NEGATIVE_INFINITY,
33872 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33874 maxValue : Number.MAX_VALUE,
33876 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33878 minText : "The minimum value for this field is {0}",
33880 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33882 maxText : "The maximum value for this field is {0}",
33884 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33885 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33887 nanText : "{0} is not a valid number",
33889 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33891 thousandsDelimiter : false,
33893 * @cfg {String} valueAlign alignment of value
33895 valueAlign : "left",
33897 getAutoCreate : function()
33899 var hiddenInput = {
33903 cls: 'hidden-number-input'
33907 hiddenInput.name = this.name;
33912 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33914 this.name = hiddenInput.name;
33916 if(cfg.cn.length > 0) {
33917 cfg.cn.push(hiddenInput);
33924 initEvents : function()
33926 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33928 var allowed = "0123456789";
33930 if(this.allowDecimals){
33931 allowed += this.decimalSeparator;
33934 if(this.allowNegative){
33938 if(this.thousandsDelimiter) {
33942 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33944 var keyPress = function(e){
33946 var k = e.getKey();
33948 var c = e.getCharCode();
33951 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33952 allowed.indexOf(String.fromCharCode(c)) === -1
33958 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33962 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33967 this.el.on("keypress", keyPress, this);
33970 validateValue : function(value)
33973 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33977 var num = this.parseValue(value);
33980 this.markInvalid(String.format(this.nanText, value));
33984 if(num < this.minValue){
33985 this.markInvalid(String.format(this.minText, this.minValue));
33989 if(num > this.maxValue){
33990 this.markInvalid(String.format(this.maxText, this.maxValue));
33997 getValue : function()
33999 var v = this.hiddenEl().getValue();
34001 return this.fixPrecision(this.parseValue(v));
34004 parseValue : function(value)
34006 if(this.thousandsDelimiter) {
34008 r = new RegExp(",", "g");
34009 value = value.replace(r, "");
34012 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
34013 return isNaN(value) ? '' : value;
34016 fixPrecision : function(value)
34018 if(this.thousandsDelimiter) {
34020 r = new RegExp(",", "g");
34021 value = value.replace(r, "");
34024 var nan = isNaN(value);
34026 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
34027 return nan ? '' : value;
34029 return parseFloat(value).toFixed(this.decimalPrecision);
34032 setValue : function(v)
34034 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34040 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34042 this.inputEl().dom.value = (v == '') ? '' :
34043 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34045 if(!this.allowZero && v === '0') {
34046 this.hiddenEl().dom.value = '';
34047 this.inputEl().dom.value = '';
34054 decimalPrecisionFcn : function(v)
34056 return Math.floor(v);
34059 beforeBlur : function()
34061 var v = this.parseValue(this.getRawValue());
34063 if(v || v === 0 || v === ''){
34068 hiddenEl : function()
34070 return this.el.select('input.hidden-number-input',true).first();
34082 * @class Roo.bootstrap.DocumentSlider
34083 * @extends Roo.bootstrap.Component
34084 * Bootstrap DocumentSlider class
34087 * Create a new DocumentViewer
34088 * @param {Object} config The config object
34091 Roo.bootstrap.DocumentSlider = function(config){
34092 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
34099 * Fire after initEvent
34100 * @param {Roo.bootstrap.DocumentSlider} this
34105 * Fire after update
34106 * @param {Roo.bootstrap.DocumentSlider} this
34112 * @param {Roo.bootstrap.DocumentSlider} this
34118 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
34124 getAutoCreate : function()
34128 cls : 'roo-document-slider',
34132 cls : 'roo-document-slider-header',
34136 cls : 'roo-document-slider-header-title'
34142 cls : 'roo-document-slider-body',
34146 cls : 'roo-document-slider-prev',
34150 cls : 'fa fa-chevron-left'
34156 cls : 'roo-document-slider-thumb',
34160 cls : 'roo-document-slider-image'
34166 cls : 'roo-document-slider-next',
34170 cls : 'fa fa-chevron-right'
34182 initEvents : function()
34184 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34185 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34187 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34188 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34190 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34191 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34193 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34194 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34196 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34197 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34199 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34200 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34202 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34203 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34205 this.thumbEl.on('click', this.onClick, this);
34207 this.prevIndicator.on('click', this.prev, this);
34209 this.nextIndicator.on('click', this.next, this);
34213 initial : function()
34215 if(this.files.length){
34216 this.indicator = 1;
34220 this.fireEvent('initial', this);
34223 update : function()
34225 this.imageEl.attr('src', this.files[this.indicator - 1]);
34227 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34229 this.prevIndicator.show();
34231 if(this.indicator == 1){
34232 this.prevIndicator.hide();
34235 this.nextIndicator.show();
34237 if(this.indicator == this.files.length){
34238 this.nextIndicator.hide();
34241 this.thumbEl.scrollTo('top');
34243 this.fireEvent('update', this);
34246 onClick : function(e)
34248 e.preventDefault();
34250 this.fireEvent('click', this);
34255 e.preventDefault();
34257 this.indicator = Math.max(1, this.indicator - 1);
34264 e.preventDefault();
34266 this.indicator = Math.min(this.files.length, this.indicator + 1);
34280 * @class Roo.bootstrap.RadioSet
34281 * @extends Roo.bootstrap.Input
34282 * Bootstrap RadioSet class
34283 * @cfg {String} indicatorpos (left|right) default left
34284 * @cfg {Boolean} inline (true|false) inline the element (default true)
34285 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34287 * Create a new RadioSet
34288 * @param {Object} config The config object
34291 Roo.bootstrap.RadioSet = function(config){
34293 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34297 Roo.bootstrap.RadioSet.register(this);
34302 * Fires when the element is checked or unchecked.
34303 * @param {Roo.bootstrap.RadioSet} this This radio
34304 * @param {Roo.bootstrap.Radio} item The checked item
34309 * Fires when the element is click.
34310 * @param {Roo.bootstrap.RadioSet} this This radio set
34311 * @param {Roo.bootstrap.Radio} item The checked item
34312 * @param {Roo.EventObject} e The event object
34319 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
34327 indicatorpos : 'left',
34329 getAutoCreate : function()
34333 cls : 'roo-radio-set-label',
34337 html : this.fieldLabel
34341 if (Roo.bootstrap.version == 3) {
34344 if(this.indicatorpos == 'left'){
34347 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34348 tooltip : 'This field is required'
34353 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34354 tooltip : 'This field is required'
34360 cls : 'roo-radio-set-items'
34363 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34365 if (align === 'left' && this.fieldLabel.length) {
34368 cls : "roo-radio-set-right",
34374 if(this.labelWidth > 12){
34375 label.style = "width: " + this.labelWidth + 'px';
34378 if(this.labelWidth < 13 && this.labelmd == 0){
34379 this.labelmd = this.labelWidth;
34382 if(this.labellg > 0){
34383 label.cls += ' col-lg-' + this.labellg;
34384 items.cls += ' col-lg-' + (12 - this.labellg);
34387 if(this.labelmd > 0){
34388 label.cls += ' col-md-' + this.labelmd;
34389 items.cls += ' col-md-' + (12 - this.labelmd);
34392 if(this.labelsm > 0){
34393 label.cls += ' col-sm-' + this.labelsm;
34394 items.cls += ' col-sm-' + (12 - this.labelsm);
34397 if(this.labelxs > 0){
34398 label.cls += ' col-xs-' + this.labelxs;
34399 items.cls += ' col-xs-' + (12 - this.labelxs);
34405 cls : 'roo-radio-set',
34409 cls : 'roo-radio-set-input',
34412 value : this.value ? this.value : ''
34419 if(this.weight.length){
34420 cfg.cls += ' roo-radio-' + this.weight;
34424 cfg.cls += ' roo-radio-set-inline';
34428 ['xs','sm','md','lg'].map(function(size){
34429 if (settings[size]) {
34430 cfg.cls += ' col-' + size + '-' + settings[size];
34438 initEvents : function()
34440 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34441 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34443 if(!this.fieldLabel.length){
34444 this.labelEl.hide();
34447 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34448 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34450 this.indicator = this.indicatorEl();
34452 if(this.indicator){
34453 this.indicator.addClass('invisible');
34456 this.originalValue = this.getValue();
34460 inputEl: function ()
34462 return this.el.select('.roo-radio-set-input', true).first();
34465 getChildContainer : function()
34467 return this.itemsEl;
34470 register : function(item)
34472 this.radioes.push(item);
34476 validate : function()
34478 if(this.getVisibilityEl().hasClass('hidden')){
34484 Roo.each(this.radioes, function(i){
34493 if(this.allowBlank) {
34497 if(this.disabled || valid){
34502 this.markInvalid();
34507 markValid : function()
34509 if(this.labelEl.isVisible(true) && this.indicatorEl()){
34510 this.indicatorEl().removeClass('visible');
34511 this.indicatorEl().addClass('invisible');
34515 if (Roo.bootstrap.version == 3) {
34516 this.el.removeClass([this.invalidClass, this.validClass]);
34517 this.el.addClass(this.validClass);
34519 this.el.removeClass(['is-invalid','is-valid']);
34520 this.el.addClass(['is-valid']);
34522 this.fireEvent('valid', this);
34525 markInvalid : function(msg)
34527 if(this.allowBlank || this.disabled){
34531 if(this.labelEl.isVisible(true) && this.indicatorEl()){
34532 this.indicatorEl().removeClass('invisible');
34533 this.indicatorEl().addClass('visible');
34535 if (Roo.bootstrap.version == 3) {
34536 this.el.removeClass([this.invalidClass, this.validClass]);
34537 this.el.addClass(this.invalidClass);
34539 this.el.removeClass(['is-invalid','is-valid']);
34540 this.el.addClass(['is-invalid']);
34543 this.fireEvent('invalid', this, msg);
34547 setValue : function(v, suppressEvent)
34549 if(this.value === v){
34556 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34559 Roo.each(this.radioes, function(i){
34561 i.el.removeClass('checked');
34564 Roo.each(this.radioes, function(i){
34566 if(i.value === v || i.value.toString() === v.toString()){
34568 i.el.addClass('checked');
34570 if(suppressEvent !== true){
34571 this.fireEvent('check', this, i);
34582 clearInvalid : function(){
34584 if(!this.el || this.preventMark){
34588 this.el.removeClass([this.invalidClass]);
34590 this.fireEvent('valid', this);
34595 Roo.apply(Roo.bootstrap.RadioSet, {
34599 register : function(set)
34601 this.groups[set.name] = set;
34604 get: function(name)
34606 if (typeof(this.groups[name]) == 'undefined') {
34610 return this.groups[name] ;
34616 * Ext JS Library 1.1.1
34617 * Copyright(c) 2006-2007, Ext JS, LLC.
34619 * Originally Released Under LGPL - original licence link has changed is not relivant.
34622 * <script type="text/javascript">
34627 * @class Roo.bootstrap.SplitBar
34628 * @extends Roo.util.Observable
34629 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34633 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34634 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34635 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34636 split.minSize = 100;
34637 split.maxSize = 600;
34638 split.animate = true;
34639 split.on('moved', splitterMoved);
34642 * Create a new SplitBar
34643 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34644 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34645 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34646 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34647 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34648 position of the SplitBar).
34650 Roo.bootstrap.SplitBar = function(cfg){
34655 // dragElement : elm
34656 // resizingElement: el,
34658 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34659 // placement : Roo.bootstrap.SplitBar.LEFT ,
34660 // existingProxy ???
34663 this.el = Roo.get(cfg.dragElement, true);
34664 this.el.dom.unselectable = "on";
34666 this.resizingEl = Roo.get(cfg.resizingElement, true);
34670 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34671 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34674 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34677 * The minimum size of the resizing element. (Defaults to 0)
34683 * The maximum size of the resizing element. (Defaults to 2000)
34686 this.maxSize = 2000;
34689 * Whether to animate the transition to the new size
34692 this.animate = false;
34695 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34698 this.useShim = false;
34703 if(!cfg.existingProxy){
34705 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34707 this.proxy = Roo.get(cfg.existingProxy).dom;
34710 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34713 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34716 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34719 this.dragSpecs = {};
34722 * @private The adapter to use to positon and resize elements
34724 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34725 this.adapter.init(this);
34727 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34729 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34730 this.el.addClass("roo-splitbar-h");
34733 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34734 this.el.addClass("roo-splitbar-v");
34740 * Fires when the splitter is moved (alias for {@link #event-moved})
34741 * @param {Roo.bootstrap.SplitBar} this
34742 * @param {Number} newSize the new width or height
34747 * Fires when the splitter is moved
34748 * @param {Roo.bootstrap.SplitBar} this
34749 * @param {Number} newSize the new width or height
34753 * @event beforeresize
34754 * Fires before the splitter is dragged
34755 * @param {Roo.bootstrap.SplitBar} this
34757 "beforeresize" : true,
34759 "beforeapply" : true
34762 Roo.util.Observable.call(this);
34765 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34766 onStartProxyDrag : function(x, y){
34767 this.fireEvent("beforeresize", this);
34769 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34771 o.enableDisplayMode("block");
34772 // all splitbars share the same overlay
34773 Roo.bootstrap.SplitBar.prototype.overlay = o;
34775 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34776 this.overlay.show();
34777 Roo.get(this.proxy).setDisplayed("block");
34778 var size = this.adapter.getElementSize(this);
34779 this.activeMinSize = this.getMinimumSize();;
34780 this.activeMaxSize = this.getMaximumSize();;
34781 var c1 = size - this.activeMinSize;
34782 var c2 = Math.max(this.activeMaxSize - size, 0);
34783 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34784 this.dd.resetConstraints();
34785 this.dd.setXConstraint(
34786 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34787 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34789 this.dd.setYConstraint(0, 0);
34791 this.dd.resetConstraints();
34792 this.dd.setXConstraint(0, 0);
34793 this.dd.setYConstraint(
34794 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34795 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34798 this.dragSpecs.startSize = size;
34799 this.dragSpecs.startPoint = [x, y];
34800 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34804 * @private Called after the drag operation by the DDProxy
34806 onEndProxyDrag : function(e){
34807 Roo.get(this.proxy).setDisplayed(false);
34808 var endPoint = Roo.lib.Event.getXY(e);
34810 this.overlay.hide();
34813 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34814 newSize = this.dragSpecs.startSize +
34815 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34816 endPoint[0] - this.dragSpecs.startPoint[0] :
34817 this.dragSpecs.startPoint[0] - endPoint[0]
34820 newSize = this.dragSpecs.startSize +
34821 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34822 endPoint[1] - this.dragSpecs.startPoint[1] :
34823 this.dragSpecs.startPoint[1] - endPoint[1]
34826 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34827 if(newSize != this.dragSpecs.startSize){
34828 if(this.fireEvent('beforeapply', this, newSize) !== false){
34829 this.adapter.setElementSize(this, newSize);
34830 this.fireEvent("moved", this, newSize);
34831 this.fireEvent("resize", this, newSize);
34837 * Get the adapter this SplitBar uses
34838 * @return The adapter object
34840 getAdapter : function(){
34841 return this.adapter;
34845 * Set the adapter this SplitBar uses
34846 * @param {Object} adapter A SplitBar adapter object
34848 setAdapter : function(adapter){
34849 this.adapter = adapter;
34850 this.adapter.init(this);
34854 * Gets the minimum size for the resizing element
34855 * @return {Number} The minimum size
34857 getMinimumSize : function(){
34858 return this.minSize;
34862 * Sets the minimum size for the resizing element
34863 * @param {Number} minSize The minimum size
34865 setMinimumSize : function(minSize){
34866 this.minSize = minSize;
34870 * Gets the maximum size for the resizing element
34871 * @return {Number} The maximum size
34873 getMaximumSize : function(){
34874 return this.maxSize;
34878 * Sets the maximum size for the resizing element
34879 * @param {Number} maxSize The maximum size
34881 setMaximumSize : function(maxSize){
34882 this.maxSize = maxSize;
34886 * Sets the initialize size for the resizing element
34887 * @param {Number} size The initial size
34889 setCurrentSize : function(size){
34890 var oldAnimate = this.animate;
34891 this.animate = false;
34892 this.adapter.setElementSize(this, size);
34893 this.animate = oldAnimate;
34897 * Destroy this splitbar.
34898 * @param {Boolean} removeEl True to remove the element
34900 destroy : function(removeEl){
34902 this.shim.remove();
34905 this.proxy.parentNode.removeChild(this.proxy);
34913 * @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.
34915 Roo.bootstrap.SplitBar.createProxy = function(dir){
34916 var proxy = new Roo.Element(document.createElement("div"));
34917 proxy.unselectable();
34918 var cls = 'roo-splitbar-proxy';
34919 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34920 document.body.appendChild(proxy.dom);
34925 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34926 * Default Adapter. It assumes the splitter and resizing element are not positioned
34927 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34929 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34932 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34933 // do nothing for now
34934 init : function(s){
34938 * Called before drag operations to get the current size of the resizing element.
34939 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34941 getElementSize : function(s){
34942 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34943 return s.resizingEl.getWidth();
34945 return s.resizingEl.getHeight();
34950 * Called after drag operations to set the size of the resizing element.
34951 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34952 * @param {Number} newSize The new size to set
34953 * @param {Function} onComplete A function to be invoked when resizing is complete
34955 setElementSize : function(s, newSize, onComplete){
34956 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34958 s.resizingEl.setWidth(newSize);
34960 onComplete(s, newSize);
34963 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34968 s.resizingEl.setHeight(newSize);
34970 onComplete(s, newSize);
34973 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34980 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34981 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34982 * Adapter that moves the splitter element to align with the resized sizing element.
34983 * Used with an absolute positioned SplitBar.
34984 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34985 * document.body, make sure you assign an id to the body element.
34987 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34988 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34989 this.container = Roo.get(container);
34992 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34993 init : function(s){
34994 this.basic.init(s);
34997 getElementSize : function(s){
34998 return this.basic.getElementSize(s);
35001 setElementSize : function(s, newSize, onComplete){
35002 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
35005 moveSplitter : function(s){
35006 var yes = Roo.bootstrap.SplitBar;
35007 switch(s.placement){
35009 s.el.setX(s.resizingEl.getRight());
35012 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
35015 s.el.setY(s.resizingEl.getBottom());
35018 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
35025 * Orientation constant - Create a vertical SplitBar
35029 Roo.bootstrap.SplitBar.VERTICAL = 1;
35032 * Orientation constant - Create a horizontal SplitBar
35036 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35039 * Placement constant - The resizing element is to the left of the splitter element
35043 Roo.bootstrap.SplitBar.LEFT = 1;
35046 * Placement constant - The resizing element is to the right of the splitter element
35050 Roo.bootstrap.SplitBar.RIGHT = 2;
35053 * Placement constant - The resizing element is positioned above the splitter element
35057 Roo.bootstrap.SplitBar.TOP = 3;
35060 * Placement constant - The resizing element is positioned under splitter element
35064 Roo.bootstrap.SplitBar.BOTTOM = 4;
35065 Roo.namespace("Roo.bootstrap.layout");/*
35067 * Ext JS Library 1.1.1
35068 * Copyright(c) 2006-2007, Ext JS, LLC.
35070 * Originally Released Under LGPL - original licence link has changed is not relivant.
35073 * <script type="text/javascript">
35077 * @class Roo.bootstrap.layout.Manager
35078 * @extends Roo.bootstrap.Component
35079 * Base class for layout managers.
35081 Roo.bootstrap.layout.Manager = function(config)
35083 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
35089 /** false to disable window resize monitoring @type Boolean */
35090 this.monitorWindowResize = true;
35095 * Fires when a layout is performed.
35096 * @param {Roo.LayoutManager} this
35100 * @event regionresized
35101 * Fires when the user resizes a region.
35102 * @param {Roo.LayoutRegion} region The resized region
35103 * @param {Number} newSize The new size (width for east/west, height for north/south)
35105 "regionresized" : true,
35107 * @event regioncollapsed
35108 * Fires when a region is collapsed.
35109 * @param {Roo.LayoutRegion} region The collapsed region
35111 "regioncollapsed" : true,
35113 * @event regionexpanded
35114 * Fires when a region is expanded.
35115 * @param {Roo.LayoutRegion} region The expanded region
35117 "regionexpanded" : true
35119 this.updating = false;
35122 this.el = Roo.get(config.el);
35128 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35133 monitorWindowResize : true,
35139 onRender : function(ct, position)
35142 this.el = Roo.get(ct);
35145 //this.fireEvent('render',this);
35149 initEvents: function()
35153 // ie scrollbar fix
35154 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35155 document.body.scroll = "no";
35156 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35157 this.el.position('relative');
35159 this.id = this.el.id;
35160 this.el.addClass("roo-layout-container");
35161 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35162 if(this.el.dom != document.body ) {
35163 this.el.on('resize', this.layout,this);
35164 this.el.on('show', this.layout,this);
35170 * Returns true if this layout is currently being updated
35171 * @return {Boolean}
35173 isUpdating : function(){
35174 return this.updating;
35178 * Suspend the LayoutManager from doing auto-layouts while
35179 * making multiple add or remove calls
35181 beginUpdate : function(){
35182 this.updating = true;
35186 * Restore auto-layouts and optionally disable the manager from performing a layout
35187 * @param {Boolean} noLayout true to disable a layout update
35189 endUpdate : function(noLayout){
35190 this.updating = false;
35196 layout: function(){
35200 onRegionResized : function(region, newSize){
35201 this.fireEvent("regionresized", region, newSize);
35205 onRegionCollapsed : function(region){
35206 this.fireEvent("regioncollapsed", region);
35209 onRegionExpanded : function(region){
35210 this.fireEvent("regionexpanded", region);
35214 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35215 * performs box-model adjustments.
35216 * @return {Object} The size as an object {width: (the width), height: (the height)}
35218 getViewSize : function()
35221 if(this.el.dom != document.body){
35222 size = this.el.getSize();
35224 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35226 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35227 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35232 * Returns the Element this layout is bound to.
35233 * @return {Roo.Element}
35235 getEl : function(){
35240 * Returns the specified region.
35241 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35242 * @return {Roo.LayoutRegion}
35244 getRegion : function(target){
35245 return this.regions[target.toLowerCase()];
35248 onWindowResize : function(){
35249 if(this.monitorWindowResize){
35256 * Ext JS Library 1.1.1
35257 * Copyright(c) 2006-2007, Ext JS, LLC.
35259 * Originally Released Under LGPL - original licence link has changed is not relivant.
35262 * <script type="text/javascript">
35265 * @class Roo.bootstrap.layout.Border
35266 * @extends Roo.bootstrap.layout.Manager
35267 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35268 * please see: examples/bootstrap/nested.html<br><br>
35270 <b>The container the layout is rendered into can be either the body element or any other element.
35271 If it is not the body element, the container needs to either be an absolute positioned element,
35272 or you will need to add "position:relative" to the css of the container. You will also need to specify
35273 the container size if it is not the body element.</b>
35276 * Create a new Border
35277 * @param {Object} config Configuration options
35279 Roo.bootstrap.layout.Border = function(config){
35280 config = config || {};
35281 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35285 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35286 if(config[region]){
35287 config[region].region = region;
35288 this.addRegion(config[region]);
35294 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
35296 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35298 parent : false, // this might point to a 'nest' or a ???
35301 * Creates and adds a new region if it doesn't already exist.
35302 * @param {String} target The target region key (north, south, east, west or center).
35303 * @param {Object} config The regions config object
35304 * @return {BorderLayoutRegion} The new region
35306 addRegion : function(config)
35308 if(!this.regions[config.region]){
35309 var r = this.factory(config);
35310 this.bindRegion(r);
35312 return this.regions[config.region];
35316 bindRegion : function(r){
35317 this.regions[r.config.region] = r;
35319 r.on("visibilitychange", this.layout, this);
35320 r.on("paneladded", this.layout, this);
35321 r.on("panelremoved", this.layout, this);
35322 r.on("invalidated", this.layout, this);
35323 r.on("resized", this.onRegionResized, this);
35324 r.on("collapsed", this.onRegionCollapsed, this);
35325 r.on("expanded", this.onRegionExpanded, this);
35329 * Performs a layout update.
35331 layout : function()
35333 if(this.updating) {
35337 // render all the rebions if they have not been done alreayd?
35338 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35339 if(this.regions[region] && !this.regions[region].bodyEl){
35340 this.regions[region].onRender(this.el)
35344 var size = this.getViewSize();
35345 var w = size.width;
35346 var h = size.height;
35351 //var x = 0, y = 0;
35353 var rs = this.regions;
35354 var north = rs["north"];
35355 var south = rs["south"];
35356 var west = rs["west"];
35357 var east = rs["east"];
35358 var center = rs["center"];
35359 //if(this.hideOnLayout){ // not supported anymore
35360 //c.el.setStyle("display", "none");
35362 if(north && north.isVisible()){
35363 var b = north.getBox();
35364 var m = north.getMargins();
35365 b.width = w - (m.left+m.right);
35368 centerY = b.height + b.y + m.bottom;
35369 centerH -= centerY;
35370 north.updateBox(this.safeBox(b));
35372 if(south && south.isVisible()){
35373 var b = south.getBox();
35374 var m = south.getMargins();
35375 b.width = w - (m.left+m.right);
35377 var totalHeight = (b.height + m.top + m.bottom);
35378 b.y = h - totalHeight + m.top;
35379 centerH -= totalHeight;
35380 south.updateBox(this.safeBox(b));
35382 if(west && west.isVisible()){
35383 var b = west.getBox();
35384 var m = west.getMargins();
35385 b.height = centerH - (m.top+m.bottom);
35387 b.y = centerY + m.top;
35388 var totalWidth = (b.width + m.left + m.right);
35389 centerX += totalWidth;
35390 centerW -= totalWidth;
35391 west.updateBox(this.safeBox(b));
35393 if(east && east.isVisible()){
35394 var b = east.getBox();
35395 var m = east.getMargins();
35396 b.height = centerH - (m.top+m.bottom);
35397 var totalWidth = (b.width + m.left + m.right);
35398 b.x = w - totalWidth + m.left;
35399 b.y = centerY + m.top;
35400 centerW -= totalWidth;
35401 east.updateBox(this.safeBox(b));
35404 var m = center.getMargins();
35406 x: centerX + m.left,
35407 y: centerY + m.top,
35408 width: centerW - (m.left+m.right),
35409 height: centerH - (m.top+m.bottom)
35411 //if(this.hideOnLayout){
35412 //center.el.setStyle("display", "block");
35414 center.updateBox(this.safeBox(centerBox));
35417 this.fireEvent("layout", this);
35421 safeBox : function(box){
35422 box.width = Math.max(0, box.width);
35423 box.height = Math.max(0, box.height);
35428 * Adds a ContentPanel (or subclass) to this layout.
35429 * @param {String} target The target region key (north, south, east, west or center).
35430 * @param {Roo.ContentPanel} panel The panel to add
35431 * @return {Roo.ContentPanel} The added panel
35433 add : function(target, panel){
35435 target = target.toLowerCase();
35436 return this.regions[target].add(panel);
35440 * Remove a ContentPanel (or subclass) to this layout.
35441 * @param {String} target The target region key (north, south, east, west or center).
35442 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35443 * @return {Roo.ContentPanel} The removed panel
35445 remove : function(target, panel){
35446 target = target.toLowerCase();
35447 return this.regions[target].remove(panel);
35451 * Searches all regions for a panel with the specified id
35452 * @param {String} panelId
35453 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35455 findPanel : function(panelId){
35456 var rs = this.regions;
35457 for(var target in rs){
35458 if(typeof rs[target] != "function"){
35459 var p = rs[target].getPanel(panelId);
35469 * Searches all regions for a panel with the specified id and activates (shows) it.
35470 * @param {String/ContentPanel} panelId The panels id or the panel itself
35471 * @return {Roo.ContentPanel} The shown panel or null
35473 showPanel : function(panelId) {
35474 var rs = this.regions;
35475 for(var target in rs){
35476 var r = rs[target];
35477 if(typeof r != "function"){
35478 if(r.hasPanel(panelId)){
35479 return r.showPanel(panelId);
35487 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35488 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35491 restoreState : function(provider){
35493 provider = Roo.state.Manager;
35495 var sm = new Roo.LayoutStateManager();
35496 sm.init(this, provider);
35502 * Adds a xtype elements to the layout.
35506 xtype : 'ContentPanel',
35513 xtype : 'NestedLayoutPanel',
35519 items : [ ... list of content panels or nested layout panels.. ]
35523 * @param {Object} cfg Xtype definition of item to add.
35525 addxtype : function(cfg)
35527 // basically accepts a pannel...
35528 // can accept a layout region..!?!?
35529 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35532 // theory? children can only be panels??
35534 //if (!cfg.xtype.match(/Panel$/)) {
35539 if (typeof(cfg.region) == 'undefined') {
35540 Roo.log("Failed to add Panel, region was not set");
35544 var region = cfg.region;
35550 xitems = cfg.items;
35555 if ( region == 'center') {
35556 Roo.log("Center: " + cfg.title);
35562 case 'Content': // ContentPanel (el, cfg)
35563 case 'Scroll': // ContentPanel (el, cfg)
35565 cfg.autoCreate = cfg.autoCreate || true;
35566 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35568 // var el = this.el.createChild();
35569 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35572 this.add(region, ret);
35576 case 'TreePanel': // our new panel!
35577 cfg.el = this.el.createChild();
35578 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35579 this.add(region, ret);
35584 // create a new Layout (which is a Border Layout...
35586 var clayout = cfg.layout;
35587 clayout.el = this.el.createChild();
35588 clayout.items = clayout.items || [];
35592 // replace this exitems with the clayout ones..
35593 xitems = clayout.items;
35595 // force background off if it's in center...
35596 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35597 cfg.background = false;
35599 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35602 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35603 //console.log('adding nested layout panel ' + cfg.toSource());
35604 this.add(region, ret);
35605 nb = {}; /// find first...
35610 // needs grid and region
35612 //var el = this.getRegion(region).el.createChild();
35614 *var el = this.el.createChild();
35615 // create the grid first...
35616 cfg.grid.container = el;
35617 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35620 if (region == 'center' && this.active ) {
35621 cfg.background = false;
35624 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35626 this.add(region, ret);
35628 if (cfg.background) {
35629 // render grid on panel activation (if panel background)
35630 ret.on('activate', function(gp) {
35631 if (!gp.grid.rendered) {
35632 // gp.grid.render(el);
35636 // cfg.grid.render(el);
35642 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35643 // it was the old xcomponent building that caused this before.
35644 // espeically if border is the top element in the tree.
35654 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35656 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35657 this.add(region, ret);
35661 throw "Can not add '" + cfg.xtype + "' to Border";
35667 this.beginUpdate();
35671 Roo.each(xitems, function(i) {
35672 region = nb && i.region ? i.region : false;
35674 var add = ret.addxtype(i);
35677 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35678 if (!i.background) {
35679 abn[region] = nb[region] ;
35686 // make the last non-background panel active..
35687 //if (nb) { Roo.log(abn); }
35690 for(var r in abn) {
35691 region = this.getRegion(r);
35693 // tried using nb[r], but it does not work..
35695 region.showPanel(abn[r]);
35706 factory : function(cfg)
35709 var validRegions = Roo.bootstrap.layout.Border.regions;
35711 var target = cfg.region;
35714 var r = Roo.bootstrap.layout;
35718 return new r.North(cfg);
35720 return new r.South(cfg);
35722 return new r.East(cfg);
35724 return new r.West(cfg);
35726 return new r.Center(cfg);
35728 throw 'Layout region "'+target+'" not supported.';
35735 * Ext JS Library 1.1.1
35736 * Copyright(c) 2006-2007, Ext JS, LLC.
35738 * Originally Released Under LGPL - original licence link has changed is not relivant.
35741 * <script type="text/javascript">
35745 * @class Roo.bootstrap.layout.Basic
35746 * @extends Roo.util.Observable
35747 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35748 * and does not have a titlebar, tabs or any other features. All it does is size and position
35749 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35750 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35751 * @cfg {string} region the region that it inhabits..
35752 * @cfg {bool} skipConfig skip config?
35756 Roo.bootstrap.layout.Basic = function(config){
35758 this.mgr = config.mgr;
35760 this.position = config.region;
35762 var skipConfig = config.skipConfig;
35766 * @scope Roo.BasicLayoutRegion
35770 * @event beforeremove
35771 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35772 * @param {Roo.LayoutRegion} this
35773 * @param {Roo.ContentPanel} panel The panel
35774 * @param {Object} e The cancel event object
35776 "beforeremove" : true,
35778 * @event invalidated
35779 * Fires when the layout for this region is changed.
35780 * @param {Roo.LayoutRegion} this
35782 "invalidated" : true,
35784 * @event visibilitychange
35785 * Fires when this region is shown or hidden
35786 * @param {Roo.LayoutRegion} this
35787 * @param {Boolean} visibility true or false
35789 "visibilitychange" : true,
35791 * @event paneladded
35792 * Fires when a panel is added.
35793 * @param {Roo.LayoutRegion} this
35794 * @param {Roo.ContentPanel} panel The panel
35796 "paneladded" : true,
35798 * @event panelremoved
35799 * Fires when a panel is removed.
35800 * @param {Roo.LayoutRegion} this
35801 * @param {Roo.ContentPanel} panel The panel
35803 "panelremoved" : true,
35805 * @event beforecollapse
35806 * Fires when this region before collapse.
35807 * @param {Roo.LayoutRegion} this
35809 "beforecollapse" : true,
35812 * Fires when this region is collapsed.
35813 * @param {Roo.LayoutRegion} this
35815 "collapsed" : true,
35818 * Fires when this region is expanded.
35819 * @param {Roo.LayoutRegion} this
35824 * Fires when this region is slid into view.
35825 * @param {Roo.LayoutRegion} this
35827 "slideshow" : true,
35830 * Fires when this region slides out of view.
35831 * @param {Roo.LayoutRegion} this
35833 "slidehide" : true,
35835 * @event panelactivated
35836 * Fires when a panel is activated.
35837 * @param {Roo.LayoutRegion} this
35838 * @param {Roo.ContentPanel} panel The activated panel
35840 "panelactivated" : true,
35843 * Fires when the user resizes this region.
35844 * @param {Roo.LayoutRegion} this
35845 * @param {Number} newSize The new size (width for east/west, height for north/south)
35849 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35850 this.panels = new Roo.util.MixedCollection();
35851 this.panels.getKey = this.getPanelId.createDelegate(this);
35853 this.activePanel = null;
35854 // ensure listeners are added...
35856 if (config.listeners || config.events) {
35857 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35858 listeners : config.listeners || {},
35859 events : config.events || {}
35863 if(skipConfig !== true){
35864 this.applyConfig(config);
35868 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35870 getPanelId : function(p){
35874 applyConfig : function(config){
35875 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35876 this.config = config;
35881 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35882 * the width, for horizontal (north, south) the height.
35883 * @param {Number} newSize The new width or height
35885 resizeTo : function(newSize){
35886 var el = this.el ? this.el :
35887 (this.activePanel ? this.activePanel.getEl() : null);
35889 switch(this.position){
35892 el.setWidth(newSize);
35893 this.fireEvent("resized", this, newSize);
35897 el.setHeight(newSize);
35898 this.fireEvent("resized", this, newSize);
35904 getBox : function(){
35905 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35908 getMargins : function(){
35909 return this.margins;
35912 updateBox : function(box){
35914 var el = this.activePanel.getEl();
35915 el.dom.style.left = box.x + "px";
35916 el.dom.style.top = box.y + "px";
35917 this.activePanel.setSize(box.width, box.height);
35921 * Returns the container element for this region.
35922 * @return {Roo.Element}
35924 getEl : function(){
35925 return this.activePanel;
35929 * Returns true if this region is currently visible.
35930 * @return {Boolean}
35932 isVisible : function(){
35933 return this.activePanel ? true : false;
35936 setActivePanel : function(panel){
35937 panel = this.getPanel(panel);
35938 if(this.activePanel && this.activePanel != panel){
35939 this.activePanel.setActiveState(false);
35940 this.activePanel.getEl().setLeftTop(-10000,-10000);
35942 this.activePanel = panel;
35943 panel.setActiveState(true);
35945 panel.setSize(this.box.width, this.box.height);
35947 this.fireEvent("panelactivated", this, panel);
35948 this.fireEvent("invalidated");
35952 * Show the specified panel.
35953 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35954 * @return {Roo.ContentPanel} The shown panel or null
35956 showPanel : function(panel){
35957 panel = this.getPanel(panel);
35959 this.setActivePanel(panel);
35965 * Get the active panel for this region.
35966 * @return {Roo.ContentPanel} The active panel or null
35968 getActivePanel : function(){
35969 return this.activePanel;
35973 * Add the passed ContentPanel(s)
35974 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35975 * @return {Roo.ContentPanel} The panel added (if only one was added)
35977 add : function(panel){
35978 if(arguments.length > 1){
35979 for(var i = 0, len = arguments.length; i < len; i++) {
35980 this.add(arguments[i]);
35984 if(this.hasPanel(panel)){
35985 this.showPanel(panel);
35988 var el = panel.getEl();
35989 if(el.dom.parentNode != this.mgr.el.dom){
35990 this.mgr.el.dom.appendChild(el.dom);
35992 if(panel.setRegion){
35993 panel.setRegion(this);
35995 this.panels.add(panel);
35996 el.setStyle("position", "absolute");
35997 if(!panel.background){
35998 this.setActivePanel(panel);
35999 if(this.config.initialSize && this.panels.getCount()==1){
36000 this.resizeTo(this.config.initialSize);
36003 this.fireEvent("paneladded", this, panel);
36008 * Returns true if the panel is in this region.
36009 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36010 * @return {Boolean}
36012 hasPanel : function(panel){
36013 if(typeof panel == "object"){ // must be panel obj
36014 panel = panel.getId();
36016 return this.getPanel(panel) ? true : false;
36020 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36021 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36022 * @param {Boolean} preservePanel Overrides the config preservePanel option
36023 * @return {Roo.ContentPanel} The panel that was removed
36025 remove : function(panel, preservePanel){
36026 panel = this.getPanel(panel);
36031 this.fireEvent("beforeremove", this, panel, e);
36032 if(e.cancel === true){
36035 var panelId = panel.getId();
36036 this.panels.removeKey(panelId);
36041 * Returns the panel specified or null if it's not in this region.
36042 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36043 * @return {Roo.ContentPanel}
36045 getPanel : function(id){
36046 if(typeof id == "object"){ // must be panel obj
36049 return this.panels.get(id);
36053 * Returns this regions position (north/south/east/west/center).
36056 getPosition: function(){
36057 return this.position;
36061 * Ext JS Library 1.1.1
36062 * Copyright(c) 2006-2007, Ext JS, LLC.
36064 * Originally Released Under LGPL - original licence link has changed is not relivant.
36067 * <script type="text/javascript">
36071 * @class Roo.bootstrap.layout.Region
36072 * @extends Roo.bootstrap.layout.Basic
36073 * This class represents a region in a layout manager.
36075 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
36076 * @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})
36077 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
36078 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
36079 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
36080 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
36081 * @cfg {String} title The title for the region (overrides panel titles)
36082 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
36083 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36084 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
36085 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
36086 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
36087 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
36088 * the space available, similar to FireFox 1.5 tabs (defaults to false)
36089 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
36090 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
36091 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
36093 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
36094 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
36095 * @cfg {Boolean} disableTabTips True to disable tab tooltips
36096 * @cfg {Number} width For East/West panels
36097 * @cfg {Number} height For North/South panels
36098 * @cfg {Boolean} split To show the splitter
36099 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
36101 * @cfg {string} cls Extra CSS classes to add to region
36103 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
36104 * @cfg {string} region the region that it inhabits..
36107 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
36108 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
36110 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
36111 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
36112 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
36114 Roo.bootstrap.layout.Region = function(config)
36116 this.applyConfig(config);
36118 var mgr = config.mgr;
36119 var pos = config.region;
36120 config.skipConfig = true;
36121 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36124 this.onRender(mgr.el);
36127 this.visible = true;
36128 this.collapsed = false;
36129 this.unrendered_panels = [];
36132 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36134 position: '', // set by wrapper (eg. north/south etc..)
36135 unrendered_panels : null, // unrendered panels.
36137 tabPosition : false,
36139 mgr: false, // points to 'Border'
36142 createBody : function(){
36143 /** This region's body element
36144 * @type Roo.Element */
36145 this.bodyEl = this.el.createChild({
36147 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36151 onRender: function(ctr, pos)
36153 var dh = Roo.DomHelper;
36154 /** This region's container element
36155 * @type Roo.Element */
36156 this.el = dh.append(ctr.dom, {
36158 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36160 /** This region's title element
36161 * @type Roo.Element */
36163 this.titleEl = dh.append(this.el.dom, {
36165 unselectable: "on",
36166 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36168 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
36169 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36173 this.titleEl.enableDisplayMode();
36174 /** This region's title text element
36175 * @type HTMLElement */
36176 this.titleTextEl = this.titleEl.dom.firstChild;
36177 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36179 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36180 this.closeBtn.enableDisplayMode();
36181 this.closeBtn.on("click", this.closeClicked, this);
36182 this.closeBtn.hide();
36184 this.createBody(this.config);
36185 if(this.config.hideWhenEmpty){
36187 this.on("paneladded", this.validateVisibility, this);
36188 this.on("panelremoved", this.validateVisibility, this);
36190 if(this.autoScroll){
36191 this.bodyEl.setStyle("overflow", "auto");
36193 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36195 //if(c.titlebar !== false){
36196 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36197 this.titleEl.hide();
36199 this.titleEl.show();
36200 if(this.config.title){
36201 this.titleTextEl.innerHTML = this.config.title;
36205 if(this.config.collapsed){
36206 this.collapse(true);
36208 if(this.config.hidden){
36212 if (this.unrendered_panels && this.unrendered_panels.length) {
36213 for (var i =0;i< this.unrendered_panels.length; i++) {
36214 this.add(this.unrendered_panels[i]);
36216 this.unrendered_panels = null;
36222 applyConfig : function(c)
36225 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36226 var dh = Roo.DomHelper;
36227 if(c.titlebar !== false){
36228 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36229 this.collapseBtn.on("click", this.collapse, this);
36230 this.collapseBtn.enableDisplayMode();
36232 if(c.showPin === true || this.showPin){
36233 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36234 this.stickBtn.enableDisplayMode();
36235 this.stickBtn.on("click", this.expand, this);
36236 this.stickBtn.hide();
36241 /** This region's collapsed element
36242 * @type Roo.Element */
36245 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36246 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36249 if(c.floatable !== false){
36250 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36251 this.collapsedEl.on("click", this.collapseClick, this);
36254 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36255 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36256 id: "message", unselectable: "on", style:{"float":"left"}});
36257 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36259 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36260 this.expandBtn.on("click", this.expand, this);
36264 if(this.collapseBtn){
36265 this.collapseBtn.setVisible(c.collapsible == true);
36268 this.cmargins = c.cmargins || this.cmargins ||
36269 (this.position == "west" || this.position == "east" ?
36270 {top: 0, left: 2, right:2, bottom: 0} :
36271 {top: 2, left: 0, right:0, bottom: 2});
36273 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36276 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36278 this.autoScroll = c.autoScroll || false;
36283 this.duration = c.duration || .30;
36284 this.slideDuration = c.slideDuration || .45;
36289 * Returns true if this region is currently visible.
36290 * @return {Boolean}
36292 isVisible : function(){
36293 return this.visible;
36297 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36298 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
36300 //setCollapsedTitle : function(title){
36301 // title = title || " ";
36302 // if(this.collapsedTitleTextEl){
36303 // this.collapsedTitleTextEl.innerHTML = title;
36307 getBox : function(){
36309 // if(!this.collapsed){
36310 b = this.el.getBox(false, true);
36312 // b = this.collapsedEl.getBox(false, true);
36317 getMargins : function(){
36318 return this.margins;
36319 //return this.collapsed ? this.cmargins : this.margins;
36322 highlight : function(){
36323 this.el.addClass("x-layout-panel-dragover");
36326 unhighlight : function(){
36327 this.el.removeClass("x-layout-panel-dragover");
36330 updateBox : function(box)
36332 if (!this.bodyEl) {
36333 return; // not rendered yet..
36337 if(!this.collapsed){
36338 this.el.dom.style.left = box.x + "px";
36339 this.el.dom.style.top = box.y + "px";
36340 this.updateBody(box.width, box.height);
36342 this.collapsedEl.dom.style.left = box.x + "px";
36343 this.collapsedEl.dom.style.top = box.y + "px";
36344 this.collapsedEl.setSize(box.width, box.height);
36347 this.tabs.autoSizeTabs();
36351 updateBody : function(w, h)
36354 this.el.setWidth(w);
36355 w -= this.el.getBorderWidth("rl");
36356 if(this.config.adjustments){
36357 w += this.config.adjustments[0];
36360 if(h !== null && h > 0){
36361 this.el.setHeight(h);
36362 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36363 h -= this.el.getBorderWidth("tb");
36364 if(this.config.adjustments){
36365 h += this.config.adjustments[1];
36367 this.bodyEl.setHeight(h);
36369 h = this.tabs.syncHeight(h);
36372 if(this.panelSize){
36373 w = w !== null ? w : this.panelSize.width;
36374 h = h !== null ? h : this.panelSize.height;
36376 if(this.activePanel){
36377 var el = this.activePanel.getEl();
36378 w = w !== null ? w : el.getWidth();
36379 h = h !== null ? h : el.getHeight();
36380 this.panelSize = {width: w, height: h};
36381 this.activePanel.setSize(w, h);
36383 if(Roo.isIE && this.tabs){
36384 this.tabs.el.repaint();
36389 * Returns the container element for this region.
36390 * @return {Roo.Element}
36392 getEl : function(){
36397 * Hides this region.
36400 //if(!this.collapsed){
36401 this.el.dom.style.left = "-2000px";
36404 // this.collapsedEl.dom.style.left = "-2000px";
36405 // this.collapsedEl.hide();
36407 this.visible = false;
36408 this.fireEvent("visibilitychange", this, false);
36412 * Shows this region if it was previously hidden.
36415 //if(!this.collapsed){
36418 // this.collapsedEl.show();
36420 this.visible = true;
36421 this.fireEvent("visibilitychange", this, true);
36424 closeClicked : function(){
36425 if(this.activePanel){
36426 this.remove(this.activePanel);
36430 collapseClick : function(e){
36432 e.stopPropagation();
36435 e.stopPropagation();
36441 * Collapses this region.
36442 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36445 collapse : function(skipAnim, skipCheck = false){
36446 if(this.collapsed) {
36450 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36452 this.collapsed = true;
36454 this.split.el.hide();
36456 if(this.config.animate && skipAnim !== true){
36457 this.fireEvent("invalidated", this);
36458 this.animateCollapse();
36460 this.el.setLocation(-20000,-20000);
36462 this.collapsedEl.show();
36463 this.fireEvent("collapsed", this);
36464 this.fireEvent("invalidated", this);
36470 animateCollapse : function(){
36475 * Expands this region if it was previously collapsed.
36476 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36477 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36480 expand : function(e, skipAnim){
36482 e.stopPropagation();
36484 if(!this.collapsed || this.el.hasActiveFx()) {
36488 this.afterSlideIn();
36491 this.collapsed = false;
36492 if(this.config.animate && skipAnim !== true){
36493 this.animateExpand();
36497 this.split.el.show();
36499 this.collapsedEl.setLocation(-2000,-2000);
36500 this.collapsedEl.hide();
36501 this.fireEvent("invalidated", this);
36502 this.fireEvent("expanded", this);
36506 animateExpand : function(){
36510 initTabs : function()
36512 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36514 var ts = new Roo.bootstrap.panel.Tabs({
36515 el: this.bodyEl.dom,
36517 tabPosition: this.tabPosition ? this.tabPosition : 'top',
36518 disableTooltips: this.config.disableTabTips,
36519 toolbar : this.config.toolbar
36522 if(this.config.hideTabs){
36523 ts.stripWrap.setDisplayed(false);
36526 ts.resizeTabs = this.config.resizeTabs === true;
36527 ts.minTabWidth = this.config.minTabWidth || 40;
36528 ts.maxTabWidth = this.config.maxTabWidth || 250;
36529 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36530 ts.monitorResize = false;
36531 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36532 ts.bodyEl.addClass('roo-layout-tabs-body');
36533 this.panels.each(this.initPanelAsTab, this);
36536 initPanelAsTab : function(panel){
36537 var ti = this.tabs.addTab(
36541 this.config.closeOnTab && panel.isClosable(),
36544 if(panel.tabTip !== undefined){
36545 ti.setTooltip(panel.tabTip);
36547 ti.on("activate", function(){
36548 this.setActivePanel(panel);
36551 if(this.config.closeOnTab){
36552 ti.on("beforeclose", function(t, e){
36554 this.remove(panel);
36558 panel.tabItem = ti;
36563 updatePanelTitle : function(panel, title)
36565 if(this.activePanel == panel){
36566 this.updateTitle(title);
36569 var ti = this.tabs.getTab(panel.getEl().id);
36571 if(panel.tabTip !== undefined){
36572 ti.setTooltip(panel.tabTip);
36577 updateTitle : function(title){
36578 if(this.titleTextEl && !this.config.title){
36579 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36583 setActivePanel : function(panel)
36585 panel = this.getPanel(panel);
36586 if(this.activePanel && this.activePanel != panel){
36587 if(this.activePanel.setActiveState(false) === false){
36591 this.activePanel = panel;
36592 panel.setActiveState(true);
36593 if(this.panelSize){
36594 panel.setSize(this.panelSize.width, this.panelSize.height);
36597 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36599 this.updateTitle(panel.getTitle());
36601 this.fireEvent("invalidated", this);
36603 this.fireEvent("panelactivated", this, panel);
36607 * Shows the specified panel.
36608 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36609 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36611 showPanel : function(panel)
36613 panel = this.getPanel(panel);
36616 var tab = this.tabs.getTab(panel.getEl().id);
36617 if(tab.isHidden()){
36618 this.tabs.unhideTab(tab.id);
36622 this.setActivePanel(panel);
36629 * Get the active panel for this region.
36630 * @return {Roo.ContentPanel} The active panel or null
36632 getActivePanel : function(){
36633 return this.activePanel;
36636 validateVisibility : function(){
36637 if(this.panels.getCount() < 1){
36638 this.updateTitle(" ");
36639 this.closeBtn.hide();
36642 if(!this.isVisible()){
36649 * Adds the passed ContentPanel(s) to this region.
36650 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36651 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36653 add : function(panel)
36655 if(arguments.length > 1){
36656 for(var i = 0, len = arguments.length; i < len; i++) {
36657 this.add(arguments[i]);
36662 // if we have not been rendered yet, then we can not really do much of this..
36663 if (!this.bodyEl) {
36664 this.unrendered_panels.push(panel);
36671 if(this.hasPanel(panel)){
36672 this.showPanel(panel);
36675 panel.setRegion(this);
36676 this.panels.add(panel);
36677 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36678 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36679 // and hide them... ???
36680 this.bodyEl.dom.appendChild(panel.getEl().dom);
36681 if(panel.background !== true){
36682 this.setActivePanel(panel);
36684 this.fireEvent("paneladded", this, panel);
36691 this.initPanelAsTab(panel);
36695 if(panel.background !== true){
36696 this.tabs.activate(panel.getEl().id);
36698 this.fireEvent("paneladded", this, panel);
36703 * Hides the tab for the specified panel.
36704 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36706 hidePanel : function(panel){
36707 if(this.tabs && (panel = this.getPanel(panel))){
36708 this.tabs.hideTab(panel.getEl().id);
36713 * Unhides the tab for a previously hidden panel.
36714 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36716 unhidePanel : function(panel){
36717 if(this.tabs && (panel = this.getPanel(panel))){
36718 this.tabs.unhideTab(panel.getEl().id);
36722 clearPanels : function(){
36723 while(this.panels.getCount() > 0){
36724 this.remove(this.panels.first());
36729 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36730 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36731 * @param {Boolean} preservePanel Overrides the config preservePanel option
36732 * @return {Roo.ContentPanel} The panel that was removed
36734 remove : function(panel, preservePanel)
36736 panel = this.getPanel(panel);
36741 this.fireEvent("beforeremove", this, panel, e);
36742 if(e.cancel === true){
36745 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36746 var panelId = panel.getId();
36747 this.panels.removeKey(panelId);
36749 document.body.appendChild(panel.getEl().dom);
36752 this.tabs.removeTab(panel.getEl().id);
36753 }else if (!preservePanel){
36754 this.bodyEl.dom.removeChild(panel.getEl().dom);
36756 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36757 var p = this.panels.first();
36758 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36759 tempEl.appendChild(p.getEl().dom);
36760 this.bodyEl.update("");
36761 this.bodyEl.dom.appendChild(p.getEl().dom);
36763 this.updateTitle(p.getTitle());
36765 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36766 this.setActivePanel(p);
36768 panel.setRegion(null);
36769 if(this.activePanel == panel){
36770 this.activePanel = null;
36772 if(this.config.autoDestroy !== false && preservePanel !== true){
36773 try{panel.destroy();}catch(e){}
36775 this.fireEvent("panelremoved", this, panel);
36780 * Returns the TabPanel component used by this region
36781 * @return {Roo.TabPanel}
36783 getTabs : function(){
36787 createTool : function(parentEl, className){
36788 var btn = Roo.DomHelper.append(parentEl, {
36790 cls: "x-layout-tools-button",
36793 cls: "roo-layout-tools-button-inner " + className,
36797 btn.addClassOnOver("roo-layout-tools-button-over");
36802 * Ext JS Library 1.1.1
36803 * Copyright(c) 2006-2007, Ext JS, LLC.
36805 * Originally Released Under LGPL - original licence link has changed is not relivant.
36808 * <script type="text/javascript">
36814 * @class Roo.SplitLayoutRegion
36815 * @extends Roo.LayoutRegion
36816 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36818 Roo.bootstrap.layout.Split = function(config){
36819 this.cursor = config.cursor;
36820 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36823 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36825 splitTip : "Drag to resize.",
36826 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36827 useSplitTips : false,
36829 applyConfig : function(config){
36830 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36833 onRender : function(ctr,pos) {
36835 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36836 if(!this.config.split){
36841 var splitEl = Roo.DomHelper.append(ctr.dom, {
36843 id: this.el.id + "-split",
36844 cls: "roo-layout-split roo-layout-split-"+this.position,
36847 /** The SplitBar for this region
36848 * @type Roo.SplitBar */
36849 // does not exist yet...
36850 Roo.log([this.position, this.orientation]);
36852 this.split = new Roo.bootstrap.SplitBar({
36853 dragElement : splitEl,
36854 resizingElement: this.el,
36855 orientation : this.orientation
36858 this.split.on("moved", this.onSplitMove, this);
36859 this.split.useShim = this.config.useShim === true;
36860 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36861 if(this.useSplitTips){
36862 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36864 //if(config.collapsible){
36865 // this.split.el.on("dblclick", this.collapse, this);
36868 if(typeof this.config.minSize != "undefined"){
36869 this.split.minSize = this.config.minSize;
36871 if(typeof this.config.maxSize != "undefined"){
36872 this.split.maxSize = this.config.maxSize;
36874 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36875 this.hideSplitter();
36880 getHMaxSize : function(){
36881 var cmax = this.config.maxSize || 10000;
36882 var center = this.mgr.getRegion("center");
36883 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36886 getVMaxSize : function(){
36887 var cmax = this.config.maxSize || 10000;
36888 var center = this.mgr.getRegion("center");
36889 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36892 onSplitMove : function(split, newSize){
36893 this.fireEvent("resized", this, newSize);
36897 * Returns the {@link Roo.SplitBar} for this region.
36898 * @return {Roo.SplitBar}
36900 getSplitBar : function(){
36905 this.hideSplitter();
36906 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36909 hideSplitter : function(){
36911 this.split.el.setLocation(-2000,-2000);
36912 this.split.el.hide();
36918 this.split.el.show();
36920 Roo.bootstrap.layout.Split.superclass.show.call(this);
36923 beforeSlide: function(){
36924 if(Roo.isGecko){// firefox overflow auto bug workaround
36925 this.bodyEl.clip();
36927 this.tabs.bodyEl.clip();
36929 if(this.activePanel){
36930 this.activePanel.getEl().clip();
36932 if(this.activePanel.beforeSlide){
36933 this.activePanel.beforeSlide();
36939 afterSlide : function(){
36940 if(Roo.isGecko){// firefox overflow auto bug workaround
36941 this.bodyEl.unclip();
36943 this.tabs.bodyEl.unclip();
36945 if(this.activePanel){
36946 this.activePanel.getEl().unclip();
36947 if(this.activePanel.afterSlide){
36948 this.activePanel.afterSlide();
36954 initAutoHide : function(){
36955 if(this.autoHide !== false){
36956 if(!this.autoHideHd){
36957 var st = new Roo.util.DelayedTask(this.slideIn, this);
36958 this.autoHideHd = {
36959 "mouseout": function(e){
36960 if(!e.within(this.el, true)){
36964 "mouseover" : function(e){
36970 this.el.on(this.autoHideHd);
36974 clearAutoHide : function(){
36975 if(this.autoHide !== false){
36976 this.el.un("mouseout", this.autoHideHd.mouseout);
36977 this.el.un("mouseover", this.autoHideHd.mouseover);
36981 clearMonitor : function(){
36982 Roo.get(document).un("click", this.slideInIf, this);
36985 // these names are backwards but not changed for compat
36986 slideOut : function(){
36987 if(this.isSlid || this.el.hasActiveFx()){
36990 this.isSlid = true;
36991 if(this.collapseBtn){
36992 this.collapseBtn.hide();
36994 this.closeBtnState = this.closeBtn.getStyle('display');
36995 this.closeBtn.hide();
36997 this.stickBtn.show();
37000 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
37001 this.beforeSlide();
37002 this.el.setStyle("z-index", 10001);
37003 this.el.slideIn(this.getSlideAnchor(), {
37004 callback: function(){
37006 this.initAutoHide();
37007 Roo.get(document).on("click", this.slideInIf, this);
37008 this.fireEvent("slideshow", this);
37015 afterSlideIn : function(){
37016 this.clearAutoHide();
37017 this.isSlid = false;
37018 this.clearMonitor();
37019 this.el.setStyle("z-index", "");
37020 if(this.collapseBtn){
37021 this.collapseBtn.show();
37023 this.closeBtn.setStyle('display', this.closeBtnState);
37025 this.stickBtn.hide();
37027 this.fireEvent("slidehide", this);
37030 slideIn : function(cb){
37031 if(!this.isSlid || this.el.hasActiveFx()){
37035 this.isSlid = false;
37036 this.beforeSlide();
37037 this.el.slideOut(this.getSlideAnchor(), {
37038 callback: function(){
37039 this.el.setLeftTop(-10000, -10000);
37041 this.afterSlideIn();
37049 slideInIf : function(e){
37050 if(!e.within(this.el)){
37055 animateCollapse : function(){
37056 this.beforeSlide();
37057 this.el.setStyle("z-index", 20000);
37058 var anchor = this.getSlideAnchor();
37059 this.el.slideOut(anchor, {
37060 callback : function(){
37061 this.el.setStyle("z-index", "");
37062 this.collapsedEl.slideIn(anchor, {duration:.3});
37064 this.el.setLocation(-10000,-10000);
37066 this.fireEvent("collapsed", this);
37073 animateExpand : function(){
37074 this.beforeSlide();
37075 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
37076 this.el.setStyle("z-index", 20000);
37077 this.collapsedEl.hide({
37080 this.el.slideIn(this.getSlideAnchor(), {
37081 callback : function(){
37082 this.el.setStyle("z-index", "");
37085 this.split.el.show();
37087 this.fireEvent("invalidated", this);
37088 this.fireEvent("expanded", this);
37116 getAnchor : function(){
37117 return this.anchors[this.position];
37120 getCollapseAnchor : function(){
37121 return this.canchors[this.position];
37124 getSlideAnchor : function(){
37125 return this.sanchors[this.position];
37128 getAlignAdj : function(){
37129 var cm = this.cmargins;
37130 switch(this.position){
37146 getExpandAdj : function(){
37147 var c = this.collapsedEl, cm = this.cmargins;
37148 switch(this.position){
37150 return [-(cm.right+c.getWidth()+cm.left), 0];
37153 return [cm.right+c.getWidth()+cm.left, 0];
37156 return [0, -(cm.top+cm.bottom+c.getHeight())];
37159 return [0, cm.top+cm.bottom+c.getHeight()];
37165 * Ext JS Library 1.1.1
37166 * Copyright(c) 2006-2007, Ext JS, LLC.
37168 * Originally Released Under LGPL - original licence link has changed is not relivant.
37171 * <script type="text/javascript">
37174 * These classes are private internal classes
37176 Roo.bootstrap.layout.Center = function(config){
37177 config.region = "center";
37178 Roo.bootstrap.layout.Region.call(this, config);
37179 this.visible = true;
37180 this.minWidth = config.minWidth || 20;
37181 this.minHeight = config.minHeight || 20;
37184 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37186 // center panel can't be hidden
37190 // center panel can't be hidden
37193 getMinWidth: function(){
37194 return this.minWidth;
37197 getMinHeight: function(){
37198 return this.minHeight;
37212 Roo.bootstrap.layout.North = function(config)
37214 config.region = 'north';
37215 config.cursor = 'n-resize';
37217 Roo.bootstrap.layout.Split.call(this, config);
37221 this.split.placement = Roo.bootstrap.SplitBar.TOP;
37222 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37223 this.split.el.addClass("roo-layout-split-v");
37225 var size = config.initialSize || config.height;
37226 if(typeof size != "undefined"){
37227 this.el.setHeight(size);
37230 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37232 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37236 getBox : function(){
37237 if(this.collapsed){
37238 return this.collapsedEl.getBox();
37240 var box = this.el.getBox();
37242 box.height += this.split.el.getHeight();
37247 updateBox : function(box){
37248 if(this.split && !this.collapsed){
37249 box.height -= this.split.el.getHeight();
37250 this.split.el.setLeft(box.x);
37251 this.split.el.setTop(box.y+box.height);
37252 this.split.el.setWidth(box.width);
37254 if(this.collapsed){
37255 this.updateBody(box.width, null);
37257 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37265 Roo.bootstrap.layout.South = function(config){
37266 config.region = 'south';
37267 config.cursor = 's-resize';
37268 Roo.bootstrap.layout.Split.call(this, config);
37270 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37271 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37272 this.split.el.addClass("roo-layout-split-v");
37274 var size = config.initialSize || config.height;
37275 if(typeof size != "undefined"){
37276 this.el.setHeight(size);
37280 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37281 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37282 getBox : function(){
37283 if(this.collapsed){
37284 return this.collapsedEl.getBox();
37286 var box = this.el.getBox();
37288 var sh = this.split.el.getHeight();
37295 updateBox : function(box){
37296 if(this.split && !this.collapsed){
37297 var sh = this.split.el.getHeight();
37300 this.split.el.setLeft(box.x);
37301 this.split.el.setTop(box.y-sh);
37302 this.split.el.setWidth(box.width);
37304 if(this.collapsed){
37305 this.updateBody(box.width, null);
37307 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37311 Roo.bootstrap.layout.East = function(config){
37312 config.region = "east";
37313 config.cursor = "e-resize";
37314 Roo.bootstrap.layout.Split.call(this, config);
37316 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37317 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37318 this.split.el.addClass("roo-layout-split-h");
37320 var size = config.initialSize || config.width;
37321 if(typeof size != "undefined"){
37322 this.el.setWidth(size);
37325 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37326 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37327 getBox : function(){
37328 if(this.collapsed){
37329 return this.collapsedEl.getBox();
37331 var box = this.el.getBox();
37333 var sw = this.split.el.getWidth();
37340 updateBox : function(box){
37341 if(this.split && !this.collapsed){
37342 var sw = this.split.el.getWidth();
37344 this.split.el.setLeft(box.x);
37345 this.split.el.setTop(box.y);
37346 this.split.el.setHeight(box.height);
37349 if(this.collapsed){
37350 this.updateBody(null, box.height);
37352 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37356 Roo.bootstrap.layout.West = function(config){
37357 config.region = "west";
37358 config.cursor = "w-resize";
37360 Roo.bootstrap.layout.Split.call(this, config);
37362 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37363 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37364 this.split.el.addClass("roo-layout-split-h");
37368 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37369 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37371 onRender: function(ctr, pos)
37373 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37374 var size = this.config.initialSize || this.config.width;
37375 if(typeof size != "undefined"){
37376 this.el.setWidth(size);
37380 getBox : function(){
37381 if(this.collapsed){
37382 return this.collapsedEl.getBox();
37384 var box = this.el.getBox();
37386 box.width += this.split.el.getWidth();
37391 updateBox : function(box){
37392 if(this.split && !this.collapsed){
37393 var sw = this.split.el.getWidth();
37395 this.split.el.setLeft(box.x+box.width);
37396 this.split.el.setTop(box.y);
37397 this.split.el.setHeight(box.height);
37399 if(this.collapsed){
37400 this.updateBody(null, box.height);
37402 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37404 });Roo.namespace("Roo.bootstrap.panel");/*
37406 * Ext JS Library 1.1.1
37407 * Copyright(c) 2006-2007, Ext JS, LLC.
37409 * Originally Released Under LGPL - original licence link has changed is not relivant.
37412 * <script type="text/javascript">
37415 * @class Roo.ContentPanel
37416 * @extends Roo.util.Observable
37417 * A basic ContentPanel element.
37418 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
37419 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
37420 * @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
37421 * @cfg {Boolean} closable True if the panel can be closed/removed
37422 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
37423 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37424 * @cfg {Toolbar} toolbar A toolbar for this panel
37425 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37426 * @cfg {String} title The title for this panel
37427 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37428 * @cfg {String} url Calls {@link #setUrl} with this value
37429 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37430 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37431 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37432 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37433 * @cfg {Boolean} badges render the badges
37436 * Create a new ContentPanel.
37437 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37438 * @param {String/Object} config A string to set only the title or a config object
37439 * @param {String} content (optional) Set the HTML content for this panel
37440 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37442 Roo.bootstrap.panel.Content = function( config){
37444 this.tpl = config.tpl || false;
37446 var el = config.el;
37447 var content = config.content;
37449 if(config.autoCreate){ // xtype is available if this is called from factory
37452 this.el = Roo.get(el);
37453 if(!this.el && config && config.autoCreate){
37454 if(typeof config.autoCreate == "object"){
37455 if(!config.autoCreate.id){
37456 config.autoCreate.id = config.id||el;
37458 this.el = Roo.DomHelper.append(document.body,
37459 config.autoCreate, true);
37461 var elcfg = { tag: "div",
37462 cls: "roo-layout-inactive-content",
37466 elcfg.html = config.html;
37470 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37473 this.closable = false;
37474 this.loaded = false;
37475 this.active = false;
37478 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37480 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37482 this.wrapEl = this.el; //this.el.wrap();
37484 if (config.toolbar.items) {
37485 ti = config.toolbar.items ;
37486 delete config.toolbar.items ;
37490 this.toolbar.render(this.wrapEl, 'before');
37491 for(var i =0;i < ti.length;i++) {
37492 // Roo.log(['add child', items[i]]);
37493 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37495 this.toolbar.items = nitems;
37496 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37497 delete config.toolbar;
37501 // xtype created footer. - not sure if will work as we normally have to render first..
37502 if (this.footer && !this.footer.el && this.footer.xtype) {
37503 if (!this.wrapEl) {
37504 this.wrapEl = this.el.wrap();
37507 this.footer.container = this.wrapEl.createChild();
37509 this.footer = Roo.factory(this.footer, Roo);
37514 if(typeof config == "string"){
37515 this.title = config;
37517 Roo.apply(this, config);
37521 this.resizeEl = Roo.get(this.resizeEl, true);
37523 this.resizeEl = this.el;
37525 // handle view.xtype
37533 * Fires when this panel is activated.
37534 * @param {Roo.ContentPanel} this
37538 * @event deactivate
37539 * Fires when this panel is activated.
37540 * @param {Roo.ContentPanel} this
37542 "deactivate" : true,
37546 * Fires when this panel is resized if fitToFrame is true.
37547 * @param {Roo.ContentPanel} this
37548 * @param {Number} width The width after any component adjustments
37549 * @param {Number} height The height after any component adjustments
37555 * Fires when this tab is created
37556 * @param {Roo.ContentPanel} this
37567 if(this.autoScroll){
37568 this.resizeEl.setStyle("overflow", "auto");
37570 // fix randome scrolling
37571 //this.el.on('scroll', function() {
37572 // Roo.log('fix random scolling');
37573 // this.scrollTo('top',0);
37576 content = content || this.content;
37578 this.setContent(content);
37580 if(config && config.url){
37581 this.setUrl(this.url, this.params, this.loadOnce);
37586 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37588 if (this.view && typeof(this.view.xtype) != 'undefined') {
37589 this.view.el = this.el.appendChild(document.createElement("div"));
37590 this.view = Roo.factory(this.view);
37591 this.view.render && this.view.render(false, '');
37595 this.fireEvent('render', this);
37598 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37602 setRegion : function(region){
37603 this.region = region;
37604 this.setActiveClass(region && !this.background);
37608 setActiveClass: function(state)
37611 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37612 this.el.setStyle('position','relative');
37614 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37615 this.el.setStyle('position', 'absolute');
37620 * Returns the toolbar for this Panel if one was configured.
37621 * @return {Roo.Toolbar}
37623 getToolbar : function(){
37624 return this.toolbar;
37627 setActiveState : function(active)
37629 this.active = active;
37630 this.setActiveClass(active);
37632 if(this.fireEvent("deactivate", this) === false){
37637 this.fireEvent("activate", this);
37641 * Updates this panel's element
37642 * @param {String} content The new content
37643 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37645 setContent : function(content, loadScripts){
37646 this.el.update(content, loadScripts);
37649 ignoreResize : function(w, h){
37650 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37653 this.lastSize = {width: w, height: h};
37658 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37659 * @return {Roo.UpdateManager} The UpdateManager
37661 getUpdateManager : function(){
37662 return this.el.getUpdateManager();
37665 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37666 * @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:
37669 url: "your-url.php",
37670 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37671 callback: yourFunction,
37672 scope: yourObject, //(optional scope)
37675 text: "Loading...",
37680 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37681 * 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.
37682 * @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}
37683 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37684 * @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.
37685 * @return {Roo.ContentPanel} this
37688 var um = this.el.getUpdateManager();
37689 um.update.apply(um, arguments);
37695 * 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.
37696 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37697 * @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)
37698 * @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)
37699 * @return {Roo.UpdateManager} The UpdateManager
37701 setUrl : function(url, params, loadOnce){
37702 if(this.refreshDelegate){
37703 this.removeListener("activate", this.refreshDelegate);
37705 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37706 this.on("activate", this.refreshDelegate);
37707 return this.el.getUpdateManager();
37710 _handleRefresh : function(url, params, loadOnce){
37711 if(!loadOnce || !this.loaded){
37712 var updater = this.el.getUpdateManager();
37713 updater.update(url, params, this._setLoaded.createDelegate(this));
37717 _setLoaded : function(){
37718 this.loaded = true;
37722 * Returns this panel's id
37725 getId : function(){
37730 * Returns this panel's element - used by regiosn to add.
37731 * @return {Roo.Element}
37733 getEl : function(){
37734 return this.wrapEl || this.el;
37739 adjustForComponents : function(width, height)
37741 //Roo.log('adjustForComponents ');
37742 if(this.resizeEl != this.el){
37743 width -= this.el.getFrameWidth('lr');
37744 height -= this.el.getFrameWidth('tb');
37747 var te = this.toolbar.getEl();
37748 te.setWidth(width);
37749 height -= te.getHeight();
37752 var te = this.footer.getEl();
37753 te.setWidth(width);
37754 height -= te.getHeight();
37758 if(this.adjustments){
37759 width += this.adjustments[0];
37760 height += this.adjustments[1];
37762 return {"width": width, "height": height};
37765 setSize : function(width, height){
37766 if(this.fitToFrame && !this.ignoreResize(width, height)){
37767 if(this.fitContainer && this.resizeEl != this.el){
37768 this.el.setSize(width, height);
37770 var size = this.adjustForComponents(width, height);
37771 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37772 this.fireEvent('resize', this, size.width, size.height);
37777 * Returns this panel's title
37780 getTitle : function(){
37782 if (typeof(this.title) != 'object') {
37787 for (var k in this.title) {
37788 if (!this.title.hasOwnProperty(k)) {
37792 if (k.indexOf('-') >= 0) {
37793 var s = k.split('-');
37794 for (var i = 0; i<s.length; i++) {
37795 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37798 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37805 * Set this panel's title
37806 * @param {String} title
37808 setTitle : function(title){
37809 this.title = title;
37811 this.region.updatePanelTitle(this, title);
37816 * Returns true is this panel was configured to be closable
37817 * @return {Boolean}
37819 isClosable : function(){
37820 return this.closable;
37823 beforeSlide : function(){
37825 this.resizeEl.clip();
37828 afterSlide : function(){
37830 this.resizeEl.unclip();
37834 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37835 * Will fail silently if the {@link #setUrl} method has not been called.
37836 * This does not activate the panel, just updates its content.
37838 refresh : function(){
37839 if(this.refreshDelegate){
37840 this.loaded = false;
37841 this.refreshDelegate();
37846 * Destroys this panel
37848 destroy : function(){
37849 this.el.removeAllListeners();
37850 var tempEl = document.createElement("span");
37851 tempEl.appendChild(this.el.dom);
37852 tempEl.innerHTML = "";
37858 * form - if the content panel contains a form - this is a reference to it.
37859 * @type {Roo.form.Form}
37863 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37864 * This contains a reference to it.
37870 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37880 * @param {Object} cfg Xtype definition of item to add.
37884 getChildContainer: function () {
37885 return this.getEl();
37890 var ret = new Roo.factory(cfg);
37895 if (cfg.xtype.match(/^Form$/)) {
37898 //if (this.footer) {
37899 // el = this.footer.container.insertSibling(false, 'before');
37901 el = this.el.createChild();
37904 this.form = new Roo.form.Form(cfg);
37907 if ( this.form.allItems.length) {
37908 this.form.render(el.dom);
37912 // should only have one of theses..
37913 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37914 // views.. should not be just added - used named prop 'view''
37916 cfg.el = this.el.appendChild(document.createElement("div"));
37919 var ret = new Roo.factory(cfg);
37921 ret.render && ret.render(false, ''); // render blank..
37931 * @class Roo.bootstrap.panel.Grid
37932 * @extends Roo.bootstrap.panel.Content
37934 * Create a new GridPanel.
37935 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37936 * @param {Object} config A the config object
37942 Roo.bootstrap.panel.Grid = function(config)
37946 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37947 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37949 config.el = this.wrapper;
37950 //this.el = this.wrapper;
37952 if (config.container) {
37953 // ctor'ed from a Border/panel.grid
37956 this.wrapper.setStyle("overflow", "hidden");
37957 this.wrapper.addClass('roo-grid-container');
37962 if(config.toolbar){
37963 var tool_el = this.wrapper.createChild();
37964 this.toolbar = Roo.factory(config.toolbar);
37966 if (config.toolbar.items) {
37967 ti = config.toolbar.items ;
37968 delete config.toolbar.items ;
37972 this.toolbar.render(tool_el);
37973 for(var i =0;i < ti.length;i++) {
37974 // Roo.log(['add child', items[i]]);
37975 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37977 this.toolbar.items = nitems;
37979 delete config.toolbar;
37982 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37983 config.grid.scrollBody = true;;
37984 config.grid.monitorWindowResize = false; // turn off autosizing
37985 config.grid.autoHeight = false;
37986 config.grid.autoWidth = false;
37988 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37990 if (config.background) {
37991 // render grid on panel activation (if panel background)
37992 this.on('activate', function(gp) {
37993 if (!gp.grid.rendered) {
37994 gp.grid.render(this.wrapper);
37995 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
38000 this.grid.render(this.wrapper);
38001 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
38004 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
38005 // ??? needed ??? config.el = this.wrapper;
38010 // xtype created footer. - not sure if will work as we normally have to render first..
38011 if (this.footer && !this.footer.el && this.footer.xtype) {
38013 var ctr = this.grid.getView().getFooterPanel(true);
38014 this.footer.dataSource = this.grid.dataSource;
38015 this.footer = Roo.factory(this.footer, Roo);
38016 this.footer.render(ctr);
38026 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
38027 getId : function(){
38028 return this.grid.id;
38032 * Returns the grid for this panel
38033 * @return {Roo.bootstrap.Table}
38035 getGrid : function(){
38039 setSize : function(width, height){
38040 if(!this.ignoreResize(width, height)){
38041 var grid = this.grid;
38042 var size = this.adjustForComponents(width, height);
38043 var gridel = grid.getGridEl();
38044 gridel.setSize(size.width, size.height);
38046 var thd = grid.getGridEl().select('thead',true).first();
38047 var tbd = grid.getGridEl().select('tbody', true).first();
38049 tbd.setSize(width, height - thd.getHeight());
38058 beforeSlide : function(){
38059 this.grid.getView().scroller.clip();
38062 afterSlide : function(){
38063 this.grid.getView().scroller.unclip();
38066 destroy : function(){
38067 this.grid.destroy();
38069 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
38074 * @class Roo.bootstrap.panel.Nest
38075 * @extends Roo.bootstrap.panel.Content
38077 * Create a new Panel, that can contain a layout.Border.
38080 * @param {Roo.BorderLayout} layout The layout for this panel
38081 * @param {String/Object} config A string to set only the title or a config object
38083 Roo.bootstrap.panel.Nest = function(config)
38085 // construct with only one argument..
38086 /* FIXME - implement nicer consturctors
38087 if (layout.layout) {
38089 layout = config.layout;
38090 delete config.layout;
38092 if (layout.xtype && !layout.getEl) {
38093 // then layout needs constructing..
38094 layout = Roo.factory(layout, Roo);
38098 config.el = config.layout.getEl();
38100 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
38102 config.layout.monitorWindowResize = false; // turn off autosizing
38103 this.layout = config.layout;
38104 this.layout.getEl().addClass("roo-layout-nested-layout");
38105 this.layout.parent = this;
38112 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38114 setSize : function(width, height){
38115 if(!this.ignoreResize(width, height)){
38116 var size = this.adjustForComponents(width, height);
38117 var el = this.layout.getEl();
38118 if (size.height < 1) {
38119 el.setWidth(size.width);
38121 el.setSize(size.width, size.height);
38123 var touch = el.dom.offsetWidth;
38124 this.layout.layout();
38125 // ie requires a double layout on the first pass
38126 if(Roo.isIE && !this.initialized){
38127 this.initialized = true;
38128 this.layout.layout();
38133 // activate all subpanels if not currently active..
38135 setActiveState : function(active){
38136 this.active = active;
38137 this.setActiveClass(active);
38140 this.fireEvent("deactivate", this);
38144 this.fireEvent("activate", this);
38145 // not sure if this should happen before or after..
38146 if (!this.layout) {
38147 return; // should not happen..
38150 for (var r in this.layout.regions) {
38151 reg = this.layout.getRegion(r);
38152 if (reg.getActivePanel()) {
38153 //reg.showPanel(reg.getActivePanel()); // force it to activate..
38154 reg.setActivePanel(reg.getActivePanel());
38157 if (!reg.panels.length) {
38160 reg.showPanel(reg.getPanel(0));
38169 * Returns the nested BorderLayout for this panel
38170 * @return {Roo.BorderLayout}
38172 getLayout : function(){
38173 return this.layout;
38177 * Adds a xtype elements to the layout of the nested panel
38181 xtype : 'ContentPanel',
38188 xtype : 'NestedLayoutPanel',
38194 items : [ ... list of content panels or nested layout panels.. ]
38198 * @param {Object} cfg Xtype definition of item to add.
38200 addxtype : function(cfg) {
38201 return this.layout.addxtype(cfg);
38206 * Ext JS Library 1.1.1
38207 * Copyright(c) 2006-2007, Ext JS, LLC.
38209 * Originally Released Under LGPL - original licence link has changed is not relivant.
38212 * <script type="text/javascript">
38215 * @class Roo.TabPanel
38216 * @extends Roo.util.Observable
38217 * A lightweight tab container.
38221 // basic tabs 1, built from existing content
38222 var tabs = new Roo.TabPanel("tabs1");
38223 tabs.addTab("script", "View Script");
38224 tabs.addTab("markup", "View Markup");
38225 tabs.activate("script");
38227 // more advanced tabs, built from javascript
38228 var jtabs = new Roo.TabPanel("jtabs");
38229 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38231 // set up the UpdateManager
38232 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38233 var updater = tab2.getUpdateManager();
38234 updater.setDefaultUrl("ajax1.htm");
38235 tab2.on('activate', updater.refresh, updater, true);
38237 // Use setUrl for Ajax loading
38238 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38239 tab3.setUrl("ajax2.htm", null, true);
38242 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38245 jtabs.activate("jtabs-1");
38248 * Create a new TabPanel.
38249 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38250 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38252 Roo.bootstrap.panel.Tabs = function(config){
38254 * The container element for this TabPanel.
38255 * @type Roo.Element
38257 this.el = Roo.get(config.el);
38260 if(typeof config == "boolean"){
38261 this.tabPosition = config ? "bottom" : "top";
38263 Roo.apply(this, config);
38267 if(this.tabPosition == "bottom"){
38268 // if tabs are at the bottom = create the body first.
38269 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38270 this.el.addClass("roo-tabs-bottom");
38272 // next create the tabs holders
38274 if (this.tabPosition == "west"){
38276 var reg = this.region; // fake it..
38278 if (!reg.mgr.parent) {
38281 reg = reg.mgr.parent.region;
38283 Roo.log("got nest?");
38285 if (reg.mgr.getRegion('west')) {
38286 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38287 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38288 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38289 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38290 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38298 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38299 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38300 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38301 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38306 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38309 // finally - if tabs are at the top, then create the body last..
38310 if(this.tabPosition != "bottom"){
38311 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38312 * @type Roo.Element
38314 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38315 this.el.addClass("roo-tabs-top");
38319 this.bodyEl.setStyle("position", "relative");
38321 this.active = null;
38322 this.activateDelegate = this.activate.createDelegate(this);
38327 * Fires when the active tab changes
38328 * @param {Roo.TabPanel} this
38329 * @param {Roo.TabPanelItem} activePanel The new active tab
38333 * @event beforetabchange
38334 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38335 * @param {Roo.TabPanel} this
38336 * @param {Object} e Set cancel to true on this object to cancel the tab change
38337 * @param {Roo.TabPanelItem} tab The tab being changed to
38339 "beforetabchange" : true
38342 Roo.EventManager.onWindowResize(this.onResize, this);
38343 this.cpad = this.el.getPadding("lr");
38344 this.hiddenCount = 0;
38347 // toolbar on the tabbar support...
38348 if (this.toolbar) {
38349 alert("no toolbar support yet");
38350 this.toolbar = false;
38352 var tcfg = this.toolbar;
38353 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
38354 this.toolbar = new Roo.Toolbar(tcfg);
38355 if (Roo.isSafari) {
38356 var tbl = tcfg.container.child('table', true);
38357 tbl.setAttribute('width', '100%');
38365 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38368 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38370 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38372 tabPosition : "top",
38374 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38376 currentTabWidth : 0,
38378 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38382 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38386 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38388 preferredTabWidth : 175,
38390 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38392 resizeTabs : false,
38394 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38396 monitorResize : true,
38398 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
38400 toolbar : false, // set by caller..
38402 region : false, /// set by caller
38404 disableTooltips : true, // not used yet...
38407 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38408 * @param {String} id The id of the div to use <b>or create</b>
38409 * @param {String} text The text for the tab
38410 * @param {String} content (optional) Content to put in the TabPanelItem body
38411 * @param {Boolean} closable (optional) True to create a close icon on the tab
38412 * @return {Roo.TabPanelItem} The created TabPanelItem
38414 addTab : function(id, text, content, closable, tpl)
38416 var item = new Roo.bootstrap.panel.TabItem({
38420 closable : closable,
38423 this.addTabItem(item);
38425 item.setContent(content);
38431 * Returns the {@link Roo.TabPanelItem} with the specified id/index
38432 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38433 * @return {Roo.TabPanelItem}
38435 getTab : function(id){
38436 return this.items[id];
38440 * Hides the {@link Roo.TabPanelItem} with the specified id/index
38441 * @param {String/Number} id The id or index of the TabPanelItem to hide.
38443 hideTab : function(id){
38444 var t = this.items[id];
38447 this.hiddenCount++;
38448 this.autoSizeTabs();
38453 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38454 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38456 unhideTab : function(id){
38457 var t = this.items[id];
38459 t.setHidden(false);
38460 this.hiddenCount--;
38461 this.autoSizeTabs();
38466 * Adds an existing {@link Roo.TabPanelItem}.
38467 * @param {Roo.TabPanelItem} item The TabPanelItem to add
38469 addTabItem : function(item)
38471 this.items[item.id] = item;
38472 this.items.push(item);
38473 this.autoSizeTabs();
38474 // if(this.resizeTabs){
38475 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38476 // this.autoSizeTabs();
38478 // item.autoSize();
38483 * Removes a {@link Roo.TabPanelItem}.
38484 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38486 removeTab : function(id){
38487 var items = this.items;
38488 var tab = items[id];
38489 if(!tab) { return; }
38490 var index = items.indexOf(tab);
38491 if(this.active == tab && items.length > 1){
38492 var newTab = this.getNextAvailable(index);
38497 this.stripEl.dom.removeChild(tab.pnode.dom);
38498 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38499 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38501 items.splice(index, 1);
38502 delete this.items[tab.id];
38503 tab.fireEvent("close", tab);
38504 tab.purgeListeners();
38505 this.autoSizeTabs();
38508 getNextAvailable : function(start){
38509 var items = this.items;
38511 // look for a next tab that will slide over to
38512 // replace the one being removed
38513 while(index < items.length){
38514 var item = items[++index];
38515 if(item && !item.isHidden()){
38519 // if one isn't found select the previous tab (on the left)
38522 var item = items[--index];
38523 if(item && !item.isHidden()){
38531 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38532 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38534 disableTab : function(id){
38535 var tab = this.items[id];
38536 if(tab && this.active != tab){
38542 * Enables a {@link Roo.TabPanelItem} that is disabled.
38543 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38545 enableTab : function(id){
38546 var tab = this.items[id];
38551 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38552 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38553 * @return {Roo.TabPanelItem} The TabPanelItem.
38555 activate : function(id)
38557 //Roo.log('activite:' + id);
38559 var tab = this.items[id];
38563 if(tab == this.active || tab.disabled){
38567 this.fireEvent("beforetabchange", this, e, tab);
38568 if(e.cancel !== true && !tab.disabled){
38570 this.active.hide();
38572 this.active = this.items[id];
38573 this.active.show();
38574 this.fireEvent("tabchange", this, this.active);
38580 * Gets the active {@link Roo.TabPanelItem}.
38581 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38583 getActiveTab : function(){
38584 return this.active;
38588 * Updates the tab body element to fit the height of the container element
38589 * for overflow scrolling
38590 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38592 syncHeight : function(targetHeight){
38593 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38594 var bm = this.bodyEl.getMargins();
38595 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38596 this.bodyEl.setHeight(newHeight);
38600 onResize : function(){
38601 if(this.monitorResize){
38602 this.autoSizeTabs();
38607 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38609 beginUpdate : function(){
38610 this.updating = true;
38614 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38616 endUpdate : function(){
38617 this.updating = false;
38618 this.autoSizeTabs();
38622 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38624 autoSizeTabs : function()
38626 var count = this.items.length;
38627 var vcount = count - this.hiddenCount;
38630 this.stripEl.hide();
38632 this.stripEl.show();
38635 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38640 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38641 var availWidth = Math.floor(w / vcount);
38642 var b = this.stripBody;
38643 if(b.getWidth() > w){
38644 var tabs = this.items;
38645 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38646 if(availWidth < this.minTabWidth){
38647 /*if(!this.sleft){ // incomplete scrolling code
38648 this.createScrollButtons();
38651 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38654 if(this.currentTabWidth < this.preferredTabWidth){
38655 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38661 * Returns the number of tabs in this TabPanel.
38664 getCount : function(){
38665 return this.items.length;
38669 * Resizes all the tabs to the passed width
38670 * @param {Number} The new width
38672 setTabWidth : function(width){
38673 this.currentTabWidth = width;
38674 for(var i = 0, len = this.items.length; i < len; i++) {
38675 if(!this.items[i].isHidden()) {
38676 this.items[i].setWidth(width);
38682 * Destroys this TabPanel
38683 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38685 destroy : function(removeEl){
38686 Roo.EventManager.removeResizeListener(this.onResize, this);
38687 for(var i = 0, len = this.items.length; i < len; i++){
38688 this.items[i].purgeListeners();
38690 if(removeEl === true){
38691 this.el.update("");
38696 createStrip : function(container)
38698 var strip = document.createElement("nav");
38699 strip.className = Roo.bootstrap.version == 4 ?
38700 "navbar-light bg-light" :
38701 "navbar navbar-default"; //"x-tabs-wrap";
38702 container.appendChild(strip);
38706 createStripList : function(strip)
38708 // div wrapper for retard IE
38709 // returns the "tr" element.
38710 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38711 //'<div class="x-tabs-strip-wrap">'+
38712 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38713 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38714 return strip.firstChild; //.firstChild.firstChild.firstChild;
38716 createBody : function(container)
38718 var body = document.createElement("div");
38719 Roo.id(body, "tab-body");
38720 //Roo.fly(body).addClass("x-tabs-body");
38721 Roo.fly(body).addClass("tab-content");
38722 container.appendChild(body);
38725 createItemBody :function(bodyEl, id){
38726 var body = Roo.getDom(id);
38728 body = document.createElement("div");
38731 //Roo.fly(body).addClass("x-tabs-item-body");
38732 Roo.fly(body).addClass("tab-pane");
38733 bodyEl.insertBefore(body, bodyEl.firstChild);
38737 createStripElements : function(stripEl, text, closable, tpl)
38739 var td = document.createElement("li"); // was td..
38740 td.className = 'nav-item';
38742 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38745 stripEl.appendChild(td);
38747 td.className = "x-tabs-closable";
38748 if(!this.closeTpl){
38749 this.closeTpl = new Roo.Template(
38750 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38751 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38752 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38755 var el = this.closeTpl.overwrite(td, {"text": text});
38756 var close = el.getElementsByTagName("div")[0];
38757 var inner = el.getElementsByTagName("em")[0];
38758 return {"el": el, "close": close, "inner": inner};
38761 // not sure what this is..
38762 // if(!this.tabTpl){
38763 //this.tabTpl = new Roo.Template(
38764 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38765 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38767 // this.tabTpl = new Roo.Template(
38768 // '<a href="#">' +
38769 // '<span unselectable="on"' +
38770 // (this.disableTooltips ? '' : ' title="{text}"') +
38771 // ' >{text}</span></a>'
38777 var template = tpl || this.tabTpl || false;
38780 template = new Roo.Template(
38781 Roo.bootstrap.version == 4 ?
38783 '<a class="nav-link" href="#" unselectable="on"' +
38784 (this.disableTooltips ? '' : ' title="{text}"') +
38787 '<a class="nav-link" href="#">' +
38788 '<span unselectable="on"' +
38789 (this.disableTooltips ? '' : ' title="{text}"') +
38790 ' >{text}</span></a>'
38795 switch (typeof(template)) {
38799 template = new Roo.Template(template);
38805 var el = template.overwrite(td, {"text": text});
38807 var inner = el.getElementsByTagName("span")[0];
38809 return {"el": el, "inner": inner};
38817 * @class Roo.TabPanelItem
38818 * @extends Roo.util.Observable
38819 * Represents an individual item (tab plus body) in a TabPanel.
38820 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38821 * @param {String} id The id of this TabPanelItem
38822 * @param {String} text The text for the tab of this TabPanelItem
38823 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38825 Roo.bootstrap.panel.TabItem = function(config){
38827 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38828 * @type Roo.TabPanel
38830 this.tabPanel = config.panel;
38832 * The id for this TabPanelItem
38835 this.id = config.id;
38837 this.disabled = false;
38839 this.text = config.text;
38841 this.loaded = false;
38842 this.closable = config.closable;
38845 * The body element for this TabPanelItem.
38846 * @type Roo.Element
38848 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38849 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38850 this.bodyEl.setStyle("display", "block");
38851 this.bodyEl.setStyle("zoom", "1");
38852 //this.hideAction();
38854 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38856 this.el = Roo.get(els.el);
38857 this.inner = Roo.get(els.inner, true);
38858 this.textEl = Roo.bootstrap.version == 4 ?
38859 this.el : Roo.get(this.el.dom.firstChild, true);
38861 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38862 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38865 // this.el.on("mousedown", this.onTabMouseDown, this);
38866 this.el.on("click", this.onTabClick, this);
38868 if(config.closable){
38869 var c = Roo.get(els.close, true);
38870 c.dom.title = this.closeText;
38871 c.addClassOnOver("close-over");
38872 c.on("click", this.closeClick, this);
38878 * Fires when this tab becomes the active tab.
38879 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38880 * @param {Roo.TabPanelItem} this
38884 * @event beforeclose
38885 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38886 * @param {Roo.TabPanelItem} this
38887 * @param {Object} e Set cancel to true on this object to cancel the close.
38889 "beforeclose": true,
38892 * Fires when this tab is closed.
38893 * @param {Roo.TabPanelItem} this
38897 * @event deactivate
38898 * Fires when this tab is no longer the active tab.
38899 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38900 * @param {Roo.TabPanelItem} this
38902 "deactivate" : true
38904 this.hidden = false;
38906 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38909 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38911 purgeListeners : function(){
38912 Roo.util.Observable.prototype.purgeListeners.call(this);
38913 this.el.removeAllListeners();
38916 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38919 this.status_node.addClass("active");
38922 this.tabPanel.stripWrap.repaint();
38924 this.fireEvent("activate", this.tabPanel, this);
38928 * Returns true if this tab is the active tab.
38929 * @return {Boolean}
38931 isActive : function(){
38932 return this.tabPanel.getActiveTab() == this;
38936 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38939 this.status_node.removeClass("active");
38941 this.fireEvent("deactivate", this.tabPanel, this);
38944 hideAction : function(){
38945 this.bodyEl.hide();
38946 this.bodyEl.setStyle("position", "absolute");
38947 this.bodyEl.setLeft("-20000px");
38948 this.bodyEl.setTop("-20000px");
38951 showAction : function(){
38952 this.bodyEl.setStyle("position", "relative");
38953 this.bodyEl.setTop("");
38954 this.bodyEl.setLeft("");
38955 this.bodyEl.show();
38959 * Set the tooltip for the tab.
38960 * @param {String} tooltip The tab's tooltip
38962 setTooltip : function(text){
38963 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38964 this.textEl.dom.qtip = text;
38965 this.textEl.dom.removeAttribute('title');
38967 this.textEl.dom.title = text;
38971 onTabClick : function(e){
38972 e.preventDefault();
38973 this.tabPanel.activate(this.id);
38976 onTabMouseDown : function(e){
38977 e.preventDefault();
38978 this.tabPanel.activate(this.id);
38981 getWidth : function(){
38982 return this.inner.getWidth();
38985 setWidth : function(width){
38986 var iwidth = width - this.linode.getPadding("lr");
38987 this.inner.setWidth(iwidth);
38988 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38989 this.linode.setWidth(width);
38993 * Show or hide the tab
38994 * @param {Boolean} hidden True to hide or false to show.
38996 setHidden : function(hidden){
38997 this.hidden = hidden;
38998 this.linode.setStyle("display", hidden ? "none" : "");
39002 * Returns true if this tab is "hidden"
39003 * @return {Boolean}
39005 isHidden : function(){
39006 return this.hidden;
39010 * Returns the text for this tab
39013 getText : function(){
39017 autoSize : function(){
39018 //this.el.beginMeasure();
39019 this.textEl.setWidth(1);
39021 * #2804 [new] Tabs in Roojs
39022 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
39024 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
39025 //this.el.endMeasure();
39029 * Sets the text for the tab (Note: this also sets the tooltip text)
39030 * @param {String} text The tab's text and tooltip
39032 setText : function(text){
39034 this.textEl.update(text);
39035 this.setTooltip(text);
39036 //if(!this.tabPanel.resizeTabs){
39037 // this.autoSize();
39041 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39043 activate : function(){
39044 this.tabPanel.activate(this.id);
39048 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
39050 disable : function(){
39051 if(this.tabPanel.active != this){
39052 this.disabled = true;
39053 this.status_node.addClass("disabled");
39058 * Enables this TabPanelItem if it was previously disabled.
39060 enable : function(){
39061 this.disabled = false;
39062 this.status_node.removeClass("disabled");
39066 * Sets the content for this TabPanelItem.
39067 * @param {String} content The content
39068 * @param {Boolean} loadScripts true to look for and load scripts
39070 setContent : function(content, loadScripts){
39071 this.bodyEl.update(content, loadScripts);
39075 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
39076 * @return {Roo.UpdateManager} The UpdateManager
39078 getUpdateManager : function(){
39079 return this.bodyEl.getUpdateManager();
39083 * Set a URL to be used to load the content for this TabPanelItem.
39084 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
39085 * @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)
39086 * @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)
39087 * @return {Roo.UpdateManager} The UpdateManager
39089 setUrl : function(url, params, loadOnce){
39090 if(this.refreshDelegate){
39091 this.un('activate', this.refreshDelegate);
39093 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39094 this.on("activate", this.refreshDelegate);
39095 return this.bodyEl.getUpdateManager();
39099 _handleRefresh : function(url, params, loadOnce){
39100 if(!loadOnce || !this.loaded){
39101 var updater = this.bodyEl.getUpdateManager();
39102 updater.update(url, params, this._setLoaded.createDelegate(this));
39107 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
39108 * Will fail silently if the setUrl method has not been called.
39109 * This does not activate the panel, just updates its content.
39111 refresh : function(){
39112 if(this.refreshDelegate){
39113 this.loaded = false;
39114 this.refreshDelegate();
39119 _setLoaded : function(){
39120 this.loaded = true;
39124 closeClick : function(e){
39127 this.fireEvent("beforeclose", this, o);
39128 if(o.cancel !== true){
39129 this.tabPanel.removeTab(this.id);
39133 * The text displayed in the tooltip for the close icon.
39136 closeText : "Close this tab"
39139 * This script refer to:
39140 * Title: International Telephone Input
39141 * Author: Jack O'Connor
39142 * Code version: v12.1.12
39143 * Availability: https://github.com/jackocnr/intl-tel-input.git
39146 Roo.bootstrap.PhoneInputData = function() {
39149 "Afghanistan (افغانستان)",
39154 "Albania (Shqipëri)",
39159 "Algeria (الجزائر)",
39184 "Antigua and Barbuda",
39194 "Armenia (Հայաստան)",
39210 "Austria (Österreich)",
39215 "Azerbaijan (Azərbaycan)",
39225 "Bahrain (البحرين)",
39230 "Bangladesh (বাংলাদেশ)",
39240 "Belarus (Беларусь)",
39245 "Belgium (België)",
39275 "Bosnia and Herzegovina (Босна и Херцеговина)",
39290 "British Indian Ocean Territory",
39295 "British Virgin Islands",
39305 "Bulgaria (България)",
39315 "Burundi (Uburundi)",
39320 "Cambodia (កម្ពុជា)",
39325 "Cameroon (Cameroun)",
39334 ["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"]
39337 "Cape Verde (Kabu Verdi)",
39342 "Caribbean Netherlands",
39353 "Central African Republic (République centrafricaine)",
39373 "Christmas Island",
39379 "Cocos (Keeling) Islands",
39390 "Comoros (جزر القمر)",
39395 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39400 "Congo (Republic) (Congo-Brazzaville)",
39420 "Croatia (Hrvatska)",
39441 "Czech Republic (Česká republika)",
39446 "Denmark (Danmark)",
39461 "Dominican Republic (República Dominicana)",
39465 ["809", "829", "849"]
39483 "Equatorial Guinea (Guinea Ecuatorial)",
39503 "Falkland Islands (Islas Malvinas)",
39508 "Faroe Islands (Føroyar)",
39529 "French Guiana (Guyane française)",
39534 "French Polynesia (Polynésie française)",
39549 "Georgia (საქართველო)",
39554 "Germany (Deutschland)",
39574 "Greenland (Kalaallit Nunaat)",
39611 "Guinea-Bissau (Guiné Bissau)",
39636 "Hungary (Magyarország)",
39641 "Iceland (Ísland)",
39661 "Iraq (العراق)",
39677 "Israel (ישראל)",
39704 "Jordan (الأردن)",
39709 "Kazakhstan (Казахстан)",
39730 "Kuwait (الكويت)",
39735 "Kyrgyzstan (Кыргызстан)",
39745 "Latvia (Latvija)",
39750 "Lebanon (لبنان)",
39765 "Libya (ليبيا)",
39775 "Lithuania (Lietuva)",
39790 "Macedonia (FYROM) (Македонија)",
39795 "Madagascar (Madagasikara)",
39825 "Marshall Islands",
39835 "Mauritania (موريتانيا)",
39840 "Mauritius (Moris)",
39861 "Moldova (Republica Moldova)",
39871 "Mongolia (Монгол)",
39876 "Montenegro (Crna Gora)",
39886 "Morocco (المغرب)",
39892 "Mozambique (Moçambique)",
39897 "Myanmar (Burma) (မြန်မာ)",
39902 "Namibia (Namibië)",
39917 "Netherlands (Nederland)",
39922 "New Caledonia (Nouvelle-Calédonie)",
39957 "North Korea (조선 민주주의 인민 공화국)",
39962 "Northern Mariana Islands",
39978 "Pakistan (پاکستان)",
39988 "Palestine (فلسطين)",
39998 "Papua New Guinea",
40040 "Réunion (La Réunion)",
40046 "Romania (România)",
40062 "Saint Barthélemy",
40073 "Saint Kitts and Nevis",
40083 "Saint Martin (Saint-Martin (partie française))",
40089 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
40094 "Saint Vincent and the Grenadines",
40109 "São Tomé and Príncipe (São Tomé e Príncipe)",
40114 "Saudi Arabia (المملكة العربية السعودية)",
40119 "Senegal (Sénégal)",
40149 "Slovakia (Slovensko)",
40154 "Slovenia (Slovenija)",
40164 "Somalia (Soomaaliya)",
40174 "South Korea (대한민국)",
40179 "South Sudan (جنوب السودان)",
40189 "Sri Lanka (ශ්රී ලංකාව)",
40194 "Sudan (السودان)",
40204 "Svalbard and Jan Mayen",
40215 "Sweden (Sverige)",
40220 "Switzerland (Schweiz)",
40225 "Syria (سوريا)",
40270 "Trinidad and Tobago",
40275 "Tunisia (تونس)",
40280 "Turkey (Türkiye)",
40290 "Turks and Caicos Islands",
40300 "U.S. Virgin Islands",
40310 "Ukraine (Україна)",
40315 "United Arab Emirates (الإمارات العربية المتحدة)",
40337 "Uzbekistan (Oʻzbekiston)",
40347 "Vatican City (Città del Vaticano)",
40358 "Vietnam (Việt Nam)",
40363 "Wallis and Futuna (Wallis-et-Futuna)",
40368 "Western Sahara (الصحراء الغربية)",
40374 "Yemen (اليمن)",
40398 * This script refer to:
40399 * Title: International Telephone Input
40400 * Author: Jack O'Connor
40401 * Code version: v12.1.12
40402 * Availability: https://github.com/jackocnr/intl-tel-input.git
40406 * @class Roo.bootstrap.PhoneInput
40407 * @extends Roo.bootstrap.TriggerField
40408 * An input with International dial-code selection
40410 * @cfg {String} defaultDialCode default '+852'
40411 * @cfg {Array} preferedCountries default []
40414 * Create a new PhoneInput.
40415 * @param {Object} config Configuration options
40418 Roo.bootstrap.PhoneInput = function(config) {
40419 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40422 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40424 listWidth: undefined,
40426 selectedClass: 'active',
40428 invalidClass : "has-warning",
40430 validClass: 'has-success',
40432 allowed: '0123456789',
40437 * @cfg {String} defaultDialCode The default dial code when initializing the input
40439 defaultDialCode: '+852',
40442 * @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
40444 preferedCountries: false,
40446 getAutoCreate : function()
40448 var data = Roo.bootstrap.PhoneInputData();
40449 var align = this.labelAlign || this.parentLabelAlign();
40452 this.allCountries = [];
40453 this.dialCodeMapping = [];
40455 for (var i = 0; i < data.length; i++) {
40457 this.allCountries[i] = {
40461 priority: c[3] || 0,
40462 areaCodes: c[4] || null
40464 this.dialCodeMapping[c[2]] = {
40467 priority: c[3] || 0,
40468 areaCodes: c[4] || null
40480 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40481 maxlength: this.max_length,
40482 cls : 'form-control tel-input',
40483 autocomplete: 'new-password'
40486 var hiddenInput = {
40489 cls: 'hidden-tel-input'
40493 hiddenInput.name = this.name;
40496 if (this.disabled) {
40497 input.disabled = true;
40500 var flag_container = {
40517 cls: this.hasFeedback ? 'has-feedback' : '',
40523 cls: 'dial-code-holder',
40530 cls: 'roo-select2-container input-group',
40537 if (this.fieldLabel.length) {
40540 tooltip: 'This field is required'
40546 cls: 'control-label',
40552 html: this.fieldLabel
40555 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40561 if(this.indicatorpos == 'right') {
40562 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40569 if(align == 'left') {
40577 if(this.labelWidth > 12){
40578 label.style = "width: " + this.labelWidth + 'px';
40580 if(this.labelWidth < 13 && this.labelmd == 0){
40581 this.labelmd = this.labelWidth;
40583 if(this.labellg > 0){
40584 label.cls += ' col-lg-' + this.labellg;
40585 input.cls += ' col-lg-' + (12 - this.labellg);
40587 if(this.labelmd > 0){
40588 label.cls += ' col-md-' + this.labelmd;
40589 container.cls += ' col-md-' + (12 - this.labelmd);
40591 if(this.labelsm > 0){
40592 label.cls += ' col-sm-' + this.labelsm;
40593 container.cls += ' col-sm-' + (12 - this.labelsm);
40595 if(this.labelxs > 0){
40596 label.cls += ' col-xs-' + this.labelxs;
40597 container.cls += ' col-xs-' + (12 - this.labelxs);
40607 var settings = this;
40609 ['xs','sm','md','lg'].map(function(size){
40610 if (settings[size]) {
40611 cfg.cls += ' col-' + size + '-' + settings[size];
40615 this.store = new Roo.data.Store({
40616 proxy : new Roo.data.MemoryProxy({}),
40617 reader : new Roo.data.JsonReader({
40628 'name' : 'dialCode',
40632 'name' : 'priority',
40636 'name' : 'areaCodes',
40643 if(!this.preferedCountries) {
40644 this.preferedCountries = [
40651 var p = this.preferedCountries.reverse();
40654 for (var i = 0; i < p.length; i++) {
40655 for (var j = 0; j < this.allCountries.length; j++) {
40656 if(this.allCountries[j].iso2 == p[i]) {
40657 var t = this.allCountries[j];
40658 this.allCountries.splice(j,1);
40659 this.allCountries.unshift(t);
40665 this.store.proxy.data = {
40667 data: this.allCountries
40673 initEvents : function()
40676 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40678 this.indicator = this.indicatorEl();
40679 this.flag = this.flagEl();
40680 this.dialCodeHolder = this.dialCodeHolderEl();
40682 this.trigger = this.el.select('div.flag-box',true).first();
40683 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40688 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40689 _this.list.setWidth(lw);
40692 this.list.on('mouseover', this.onViewOver, this);
40693 this.list.on('mousemove', this.onViewMove, this);
40694 this.inputEl().on("keyup", this.onKeyUp, this);
40695 this.inputEl().on("keypress", this.onKeyPress, this);
40697 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40699 this.view = new Roo.View(this.list, this.tpl, {
40700 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40703 this.view.on('click', this.onViewClick, this);
40704 this.setValue(this.defaultDialCode);
40707 onTriggerClick : function(e)
40709 Roo.log('trigger click');
40714 if(this.isExpanded()){
40716 this.hasFocus = false;
40718 this.store.load({});
40719 this.hasFocus = true;
40724 isExpanded : function()
40726 return this.list.isVisible();
40729 collapse : function()
40731 if(!this.isExpanded()){
40735 Roo.get(document).un('mousedown', this.collapseIf, this);
40736 Roo.get(document).un('mousewheel', this.collapseIf, this);
40737 this.fireEvent('collapse', this);
40741 expand : function()
40745 if(this.isExpanded() || !this.hasFocus){
40749 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40750 this.list.setWidth(lw);
40753 this.restrictHeight();
40755 Roo.get(document).on('mousedown', this.collapseIf, this);
40756 Roo.get(document).on('mousewheel', this.collapseIf, this);
40758 this.fireEvent('expand', this);
40761 restrictHeight : function()
40763 this.list.alignTo(this.inputEl(), this.listAlign);
40764 this.list.alignTo(this.inputEl(), this.listAlign);
40767 onViewOver : function(e, t)
40769 if(this.inKeyMode){
40772 var item = this.view.findItemFromChild(t);
40775 var index = this.view.indexOf(item);
40776 this.select(index, false);
40781 onViewClick : function(view, doFocus, el, e)
40783 var index = this.view.getSelectedIndexes()[0];
40785 var r = this.store.getAt(index);
40788 this.onSelect(r, index);
40790 if(doFocus !== false && !this.blockFocus){
40791 this.inputEl().focus();
40795 onViewMove : function(e, t)
40797 this.inKeyMode = false;
40800 select : function(index, scrollIntoView)
40802 this.selectedIndex = index;
40803 this.view.select(index);
40804 if(scrollIntoView !== false){
40805 var el = this.view.getNode(index);
40807 this.list.scrollChildIntoView(el, false);
40812 createList : function()
40814 this.list = Roo.get(document.body).createChild({
40816 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40817 style: 'display:none'
40820 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40823 collapseIf : function(e)
40825 var in_combo = e.within(this.el);
40826 var in_list = e.within(this.list);
40827 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40829 if (in_combo || in_list || is_list) {
40835 onSelect : function(record, index)
40837 if(this.fireEvent('beforeselect', this, record, index) !== false){
40839 this.setFlagClass(record.data.iso2);
40840 this.setDialCode(record.data.dialCode);
40841 this.hasFocus = false;
40843 this.fireEvent('select', this, record, index);
40847 flagEl : function()
40849 var flag = this.el.select('div.flag',true).first();
40856 dialCodeHolderEl : function()
40858 var d = this.el.select('input.dial-code-holder',true).first();
40865 setDialCode : function(v)
40867 this.dialCodeHolder.dom.value = '+'+v;
40870 setFlagClass : function(n)
40872 this.flag.dom.className = 'flag '+n;
40875 getValue : function()
40877 var v = this.inputEl().getValue();
40878 if(this.dialCodeHolder) {
40879 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40884 setValue : function(v)
40886 var d = this.getDialCode(v);
40888 //invalid dial code
40889 if(v.length == 0 || !d || d.length == 0) {
40891 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40892 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40898 this.setFlagClass(this.dialCodeMapping[d].iso2);
40899 this.setDialCode(d);
40900 this.inputEl().dom.value = v.replace('+'+d,'');
40901 this.hiddenEl().dom.value = this.getValue();
40906 getDialCode : function(v)
40910 if (v.length == 0) {
40911 return this.dialCodeHolder.dom.value;
40915 if (v.charAt(0) != "+") {
40918 var numericChars = "";
40919 for (var i = 1; i < v.length; i++) {
40920 var c = v.charAt(i);
40923 if (this.dialCodeMapping[numericChars]) {
40924 dialCode = v.substr(1, i);
40926 if (numericChars.length == 4) {
40936 this.setValue(this.defaultDialCode);
40940 hiddenEl : function()
40942 return this.el.select('input.hidden-tel-input',true).first();
40945 // after setting val
40946 onKeyUp : function(e){
40947 this.setValue(this.getValue());
40950 onKeyPress : function(e){
40951 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40958 * @class Roo.bootstrap.MoneyField
40959 * @extends Roo.bootstrap.ComboBox
40960 * Bootstrap MoneyField class
40963 * Create a new MoneyField.
40964 * @param {Object} config Configuration options
40967 Roo.bootstrap.MoneyField = function(config) {
40969 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40973 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40976 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40978 allowDecimals : true,
40980 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40982 decimalSeparator : ".",
40984 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40986 decimalPrecision : 0,
40988 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40990 allowNegative : true,
40992 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40996 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40998 minValue : Number.NEGATIVE_INFINITY,
41000 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41002 maxValue : Number.MAX_VALUE,
41004 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41006 minText : "The minimum value for this field is {0}",
41008 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41010 maxText : "The maximum value for this field is {0}",
41012 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
41013 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41015 nanText : "{0} is not a valid number",
41017 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
41021 * @cfg {String} defaults currency of the MoneyField
41022 * value should be in lkey
41024 defaultCurrency : false,
41026 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41028 thousandsDelimiter : false,
41030 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41041 getAutoCreate : function()
41043 var align = this.labelAlign || this.parentLabelAlign();
41055 cls : 'form-control roo-money-amount-input',
41056 autocomplete: 'new-password'
41059 var hiddenInput = {
41063 cls: 'hidden-number-input'
41066 if(this.max_length) {
41067 input.maxlength = this.max_length;
41071 hiddenInput.name = this.name;
41074 if (this.disabled) {
41075 input.disabled = true;
41078 var clg = 12 - this.inputlg;
41079 var cmd = 12 - this.inputmd;
41080 var csm = 12 - this.inputsm;
41081 var cxs = 12 - this.inputxs;
41085 cls : 'row roo-money-field',
41089 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
41093 cls: 'roo-select2-container input-group',
41097 cls : 'form-control roo-money-currency-input',
41098 autocomplete: 'new-password',
41100 name : this.currencyName
41104 cls : 'input-group-addon',
41118 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41122 cls: this.hasFeedback ? 'has-feedback' : '',
41133 if (this.fieldLabel.length) {
41136 tooltip: 'This field is required'
41142 cls: 'control-label',
41148 html: this.fieldLabel
41151 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41157 if(this.indicatorpos == 'right') {
41158 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41165 if(align == 'left') {
41173 if(this.labelWidth > 12){
41174 label.style = "width: " + this.labelWidth + 'px';
41176 if(this.labelWidth < 13 && this.labelmd == 0){
41177 this.labelmd = this.labelWidth;
41179 if(this.labellg > 0){
41180 label.cls += ' col-lg-' + this.labellg;
41181 input.cls += ' col-lg-' + (12 - this.labellg);
41183 if(this.labelmd > 0){
41184 label.cls += ' col-md-' + this.labelmd;
41185 container.cls += ' col-md-' + (12 - this.labelmd);
41187 if(this.labelsm > 0){
41188 label.cls += ' col-sm-' + this.labelsm;
41189 container.cls += ' col-sm-' + (12 - this.labelsm);
41191 if(this.labelxs > 0){
41192 label.cls += ' col-xs-' + this.labelxs;
41193 container.cls += ' col-xs-' + (12 - this.labelxs);
41204 var settings = this;
41206 ['xs','sm','md','lg'].map(function(size){
41207 if (settings[size]) {
41208 cfg.cls += ' col-' + size + '-' + settings[size];
41215 initEvents : function()
41217 this.indicator = this.indicatorEl();
41219 this.initCurrencyEvent();
41221 this.initNumberEvent();
41224 initCurrencyEvent : function()
41227 throw "can not find store for combo";
41230 this.store = Roo.factory(this.store, Roo.data);
41231 this.store.parent = this;
41235 this.triggerEl = this.el.select('.input-group-addon', true).first();
41237 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41242 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41243 _this.list.setWidth(lw);
41246 this.list.on('mouseover', this.onViewOver, this);
41247 this.list.on('mousemove', this.onViewMove, this);
41248 this.list.on('scroll', this.onViewScroll, this);
41251 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41254 this.view = new Roo.View(this.list, this.tpl, {
41255 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41258 this.view.on('click', this.onViewClick, this);
41260 this.store.on('beforeload', this.onBeforeLoad, this);
41261 this.store.on('load', this.onLoad, this);
41262 this.store.on('loadexception', this.onLoadException, this);
41264 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41265 "up" : function(e){
41266 this.inKeyMode = true;
41270 "down" : function(e){
41271 if(!this.isExpanded()){
41272 this.onTriggerClick();
41274 this.inKeyMode = true;
41279 "enter" : function(e){
41282 if(this.fireEvent("specialkey", this, e)){
41283 this.onViewClick(false);
41289 "esc" : function(e){
41293 "tab" : function(e){
41296 if(this.fireEvent("specialkey", this, e)){
41297 this.onViewClick(false);
41305 doRelay : function(foo, bar, hname){
41306 if(hname == 'down' || this.scope.isExpanded()){
41307 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41315 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41319 initNumberEvent : function(e)
41321 this.inputEl().on("keydown" , this.fireKey, this);
41322 this.inputEl().on("focus", this.onFocus, this);
41323 this.inputEl().on("blur", this.onBlur, this);
41325 this.inputEl().relayEvent('keyup', this);
41327 if(this.indicator){
41328 this.indicator.addClass('invisible');
41331 this.originalValue = this.getValue();
41333 if(this.validationEvent == 'keyup'){
41334 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41335 this.inputEl().on('keyup', this.filterValidation, this);
41337 else if(this.validationEvent !== false){
41338 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41341 if(this.selectOnFocus){
41342 this.on("focus", this.preFocus, this);
41345 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41346 this.inputEl().on("keypress", this.filterKeys, this);
41348 this.inputEl().relayEvent('keypress', this);
41351 var allowed = "0123456789";
41353 if(this.allowDecimals){
41354 allowed += this.decimalSeparator;
41357 if(this.allowNegative){
41361 if(this.thousandsDelimiter) {
41365 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41367 var keyPress = function(e){
41369 var k = e.getKey();
41371 var c = e.getCharCode();
41374 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41375 allowed.indexOf(String.fromCharCode(c)) === -1
41381 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41385 if(allowed.indexOf(String.fromCharCode(c)) === -1){
41390 this.inputEl().on("keypress", keyPress, this);
41394 onTriggerClick : function(e)
41401 this.loadNext = false;
41403 if(this.isExpanded()){
41408 this.hasFocus = true;
41410 if(this.triggerAction == 'all') {
41411 this.doQuery(this.allQuery, true);
41415 this.doQuery(this.getRawValue());
41418 getCurrency : function()
41420 var v = this.currencyEl().getValue();
41425 restrictHeight : function()
41427 this.list.alignTo(this.currencyEl(), this.listAlign);
41428 this.list.alignTo(this.currencyEl(), this.listAlign);
41431 onViewClick : function(view, doFocus, el, e)
41433 var index = this.view.getSelectedIndexes()[0];
41435 var r = this.store.getAt(index);
41438 this.onSelect(r, index);
41442 onSelect : function(record, index){
41444 if(this.fireEvent('beforeselect', this, record, index) !== false){
41446 this.setFromCurrencyData(index > -1 ? record.data : false);
41450 this.fireEvent('select', this, record, index);
41454 setFromCurrencyData : function(o)
41458 this.lastCurrency = o;
41460 if (this.currencyField) {
41461 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41463 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
41466 this.lastSelectionText = currency;
41468 //setting default currency
41469 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41470 this.setCurrency(this.defaultCurrency);
41474 this.setCurrency(currency);
41477 setFromData : function(o)
41481 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41483 this.setFromCurrencyData(c);
41488 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41490 Roo.log('no value set for '+ (this.name ? this.name : this.id));
41493 this.setValue(value);
41497 setCurrency : function(v)
41499 this.currencyValue = v;
41502 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41507 setValue : function(v)
41509 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41515 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41517 this.inputEl().dom.value = (v == '') ? '' :
41518 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41520 if(!this.allowZero && v === '0') {
41521 this.hiddenEl().dom.value = '';
41522 this.inputEl().dom.value = '';
41529 getRawValue : function()
41531 var v = this.inputEl().getValue();
41536 getValue : function()
41538 return this.fixPrecision(this.parseValue(this.getRawValue()));
41541 parseValue : function(value)
41543 if(this.thousandsDelimiter) {
41545 r = new RegExp(",", "g");
41546 value = value.replace(r, "");
41549 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41550 return isNaN(value) ? '' : value;
41554 fixPrecision : function(value)
41556 if(this.thousandsDelimiter) {
41558 r = new RegExp(",", "g");
41559 value = value.replace(r, "");
41562 var nan = isNaN(value);
41564 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41565 return nan ? '' : value;
41567 return parseFloat(value).toFixed(this.decimalPrecision);
41570 decimalPrecisionFcn : function(v)
41572 return Math.floor(v);
41575 validateValue : function(value)
41577 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41581 var num = this.parseValue(value);
41584 this.markInvalid(String.format(this.nanText, value));
41588 if(num < this.minValue){
41589 this.markInvalid(String.format(this.minText, this.minValue));
41593 if(num > this.maxValue){
41594 this.markInvalid(String.format(this.maxText, this.maxValue));
41601 validate : function()
41603 if(this.disabled || this.allowBlank){
41608 var currency = this.getCurrency();
41610 if(this.validateValue(this.getRawValue()) && currency.length){
41615 this.markInvalid();
41619 getName: function()
41624 beforeBlur : function()
41630 var v = this.parseValue(this.getRawValue());
41637 onBlur : function()
41641 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41642 //this.el.removeClass(this.focusClass);
41645 this.hasFocus = false;
41647 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41651 var v = this.getValue();
41653 if(String(v) !== String(this.startValue)){
41654 this.fireEvent('change', this, v, this.startValue);
41657 this.fireEvent("blur", this);
41660 inputEl : function()
41662 return this.el.select('.roo-money-amount-input', true).first();
41665 currencyEl : function()
41667 return this.el.select('.roo-money-currency-input', true).first();
41670 hiddenEl : function()
41672 return this.el.select('input.hidden-number-input',true).first();
41676 * @class Roo.bootstrap.BezierSignature
41677 * @extends Roo.bootstrap.Component
41678 * Bootstrap BezierSignature class
41679 * This script refer to:
41680 * Title: Signature Pad
41682 * Availability: https://github.com/szimek/signature_pad
41685 * Create a new BezierSignature
41686 * @param {Object} config The config object
41689 Roo.bootstrap.BezierSignature = function(config){
41690 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41696 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
41703 mouse_btn_down: true,
41706 * @cfg {int} canvas height
41708 canvas_height: '200px',
41711 * @cfg {float|function} Radius of a single dot.
41716 * @cfg {float} Minimum width of a line. Defaults to 0.5.
41721 * @cfg {float} Maximum width of a line. Defaults to 2.5.
41726 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41731 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41736 * @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.
41738 bg_color: 'rgba(0, 0, 0, 0)',
41741 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41743 dot_color: 'black',
41746 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41748 velocity_filter_weight: 0.7,
41751 * @cfg {function} Callback when stroke begin.
41756 * @cfg {function} Callback when stroke end.
41760 getAutoCreate : function()
41762 var cls = 'roo-signature column';
41765 cls += ' ' + this.cls;
41775 for(var i = 0; i < col_sizes.length; i++) {
41776 if(this[col_sizes[i]]) {
41777 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
41787 cls: 'roo-signature-body',
41791 cls: 'roo-signature-body-canvas',
41792 height: this.canvas_height,
41793 width: this.canvas_width
41800 style: 'display: none'
41808 initEvents: function()
41810 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
41812 var canvas = this.canvasEl();
41814 // mouse && touch event swapping...
41815 canvas.dom.style.touchAction = 'none';
41816 canvas.dom.style.msTouchAction = 'none';
41818 this.mouse_btn_down = false;
41819 canvas.on('mousedown', this._handleMouseDown, this);
41820 canvas.on('mousemove', this._handleMouseMove, this);
41821 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
41823 if (window.PointerEvent) {
41824 canvas.on('pointerdown', this._handleMouseDown, this);
41825 canvas.on('pointermove', this._handleMouseMove, this);
41826 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
41829 if ('ontouchstart' in window) {
41830 canvas.on('touchstart', this._handleTouchStart, this);
41831 canvas.on('touchmove', this._handleTouchMove, this);
41832 canvas.on('touchend', this._handleTouchEnd, this);
41835 Roo.EventManager.onWindowResize(this.resize, this, true);
41837 // file input event
41838 this.fileEl().on('change', this.uploadImage, this);
41845 resize: function(){
41847 var canvas = this.canvasEl().dom;
41848 var ctx = this.canvasElCtx();
41849 var img_data = false;
41851 if(canvas.width > 0) {
41852 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
41854 // setting canvas width will clean img data
41857 var style = window.getComputedStyle ?
41858 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
41860 var padding_left = parseInt(style.paddingLeft) || 0;
41861 var padding_right = parseInt(style.paddingRight) || 0;
41863 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
41866 ctx.putImageData(img_data, 0, 0);
41870 _handleMouseDown: function(e)
41872 if (e.browserEvent.which === 1) {
41873 this.mouse_btn_down = true;
41874 this.strokeBegin(e);
41878 _handleMouseMove: function (e)
41880 if (this.mouse_btn_down) {
41881 this.strokeMoveUpdate(e);
41885 _handleMouseUp: function (e)
41887 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
41888 this.mouse_btn_down = false;
41893 _handleTouchStart: function (e) {
41895 e.preventDefault();
41896 if (e.browserEvent.targetTouches.length === 1) {
41897 // var touch = e.browserEvent.changedTouches[0];
41898 // this.strokeBegin(touch);
41900 this.strokeBegin(e); // assume e catching the correct xy...
41904 _handleTouchMove: function (e) {
41905 e.preventDefault();
41906 // var touch = event.targetTouches[0];
41907 // _this._strokeMoveUpdate(touch);
41908 this.strokeMoveUpdate(e);
41911 _handleTouchEnd: function (e) {
41912 var wasCanvasTouched = e.target === this.canvasEl().dom;
41913 if (wasCanvasTouched) {
41914 e.preventDefault();
41915 // var touch = event.changedTouches[0];
41916 // _this._strokeEnd(touch);
41921 reset: function () {
41922 this._lastPoints = [];
41923 this._lastVelocity = 0;
41924 this._lastWidth = (this.min_width + this.max_width) / 2;
41925 this.canvasElCtx().fillStyle = this.dot_color;
41928 strokeMoveUpdate: function(e)
41930 this.strokeUpdate(e);
41932 if (this.throttle) {
41933 this.throttleStroke(this.strokeUpdate, this.throttle);
41936 this.strokeUpdate(e);
41940 strokeBegin: function(e)
41942 var newPointGroup = {
41943 color: this.dot_color,
41947 if (typeof this.onBegin === 'function') {
41951 this.curve_data.push(newPointGroup);
41953 this.strokeUpdate(e);
41956 strokeUpdate: function(e)
41958 var rect = this.canvasEl().dom.getBoundingClientRect();
41959 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
41960 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
41961 var lastPoints = lastPointGroup.points;
41962 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
41963 var isLastPointTooClose = lastPoint
41964 ? point.distanceTo(lastPoint) <= this.min_distance
41966 var color = lastPointGroup.color;
41967 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
41968 var curve = this.addPoint(point);
41970 this.drawDot({color: color, point: point});
41973 this.drawCurve({color: color, curve: curve});
41983 strokeEnd: function(e)
41985 this.strokeUpdate(e);
41986 if (typeof this.onEnd === 'function') {
41991 addPoint: function (point) {
41992 var _lastPoints = this._lastPoints;
41993 _lastPoints.push(point);
41994 if (_lastPoints.length > 2) {
41995 if (_lastPoints.length === 3) {
41996 _lastPoints.unshift(_lastPoints[0]);
41998 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
41999 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
42000 _lastPoints.shift();
42006 calculateCurveWidths: function (startPoint, endPoint) {
42007 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
42008 (1 - this.velocity_filter_weight) * this._lastVelocity;
42010 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
42013 start: this._lastWidth
42016 this._lastVelocity = velocity;
42017 this._lastWidth = newWidth;
42021 drawDot: function (_a) {
42022 var color = _a.color, point = _a.point;
42023 var ctx = this.canvasElCtx();
42024 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
42026 this.drawCurveSegment(point.x, point.y, width);
42028 ctx.fillStyle = color;
42032 drawCurve: function (_a) {
42033 var color = _a.color, curve = _a.curve;
42034 var ctx = this.canvasElCtx();
42035 var widthDelta = curve.endWidth - curve.startWidth;
42036 var drawSteps = Math.floor(curve.length()) * 2;
42038 ctx.fillStyle = color;
42039 for (var i = 0; i < drawSteps; i += 1) {
42040 var t = i / drawSteps;
42046 var x = uuu * curve.startPoint.x;
42047 x += 3 * uu * t * curve.control1.x;
42048 x += 3 * u * tt * curve.control2.x;
42049 x += ttt * curve.endPoint.x;
42050 var y = uuu * curve.startPoint.y;
42051 y += 3 * uu * t * curve.control1.y;
42052 y += 3 * u * tt * curve.control2.y;
42053 y += ttt * curve.endPoint.y;
42054 var width = curve.startWidth + ttt * widthDelta;
42055 this.drawCurveSegment(x, y, width);
42061 drawCurveSegment: function (x, y, width) {
42062 var ctx = this.canvasElCtx();
42064 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
42065 this.is_empty = false;
42070 var ctx = this.canvasElCtx();
42071 var canvas = this.canvasEl().dom;
42072 ctx.fillStyle = this.bg_color;
42073 ctx.clearRect(0, 0, canvas.width, canvas.height);
42074 ctx.fillRect(0, 0, canvas.width, canvas.height);
42075 this.curve_data = [];
42077 this.is_empty = true;
42082 return this.el.select('input',true).first();
42085 canvasEl: function()
42087 return this.el.select('canvas',true).first();
42090 canvasElCtx: function()
42092 return this.el.select('canvas',true).first().dom.getContext('2d');
42095 getImage: function(type)
42097 if(this.is_empty) {
42102 return this.canvasEl().dom.toDataURL('image/'+type, 1);
42105 drawFromImage: function(img_src)
42107 var img = new Image();
42109 img.onload = function(){
42110 this.canvasElCtx().drawImage(img, 0, 0);
42115 this.is_empty = false;
42118 selectImage: function()
42120 this.fileEl().dom.click();
42123 uploadImage: function(e)
42125 var reader = new FileReader();
42127 reader.onload = function(e){
42128 var img = new Image();
42129 img.onload = function(){
42131 this.canvasElCtx().drawImage(img, 0, 0);
42133 img.src = e.target.result;
42136 reader.readAsDataURL(e.target.files[0]);
42139 // Bezier Point Constructor
42140 Point: (function () {
42141 function Point(x, y, time) {
42144 this.time = time || Date.now();
42146 Point.prototype.distanceTo = function (start) {
42147 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42149 Point.prototype.equals = function (other) {
42150 return this.x === other.x && this.y === other.y && this.time === other.time;
42152 Point.prototype.velocityFrom = function (start) {
42153 return this.time !== start.time
42154 ? this.distanceTo(start) / (this.time - start.time)
42161 // Bezier Constructor
42162 Bezier: (function () {
42163 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42164 this.startPoint = startPoint;
42165 this.control2 = control2;
42166 this.control1 = control1;
42167 this.endPoint = endPoint;
42168 this.startWidth = startWidth;
42169 this.endWidth = endWidth;
42171 Bezier.fromPoints = function (points, widths, scope) {
42172 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42173 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42174 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42176 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42177 var dx1 = s1.x - s2.x;
42178 var dy1 = s1.y - s2.y;
42179 var dx2 = s2.x - s3.x;
42180 var dy2 = s2.y - s3.y;
42181 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42182 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42183 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42184 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42185 var dxm = m1.x - m2.x;
42186 var dym = m1.y - m2.y;
42187 var k = l2 / (l1 + l2);
42188 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42189 var tx = s2.x - cm.x;
42190 var ty = s2.y - cm.y;
42192 c1: new scope.Point(m1.x + tx, m1.y + ty),
42193 c2: new scope.Point(m2.x + tx, m2.y + ty)
42196 Bezier.prototype.length = function () {
42201 for (var i = 0; i <= steps; i += 1) {
42203 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42204 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42206 var xdiff = cx - px;
42207 var ydiff = cy - py;
42208 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42215 Bezier.prototype.point = function (t, start, c1, c2, end) {
42216 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42217 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42218 + (3.0 * c2 * (1.0 - t) * t * t)
42219 + (end * t * t * t);
42224 throttleStroke: function(fn, wait) {
42225 if (wait === void 0) { wait = 250; }
42227 var timeout = null;
42231 var later = function () {
42232 previous = Date.now();
42234 result = fn.apply(storedContext, storedArgs);
42236 storedContext = null;
42240 return function wrapper() {
42242 for (var _i = 0; _i < arguments.length; _i++) {
42243 args[_i] = arguments[_i];
42245 var now = Date.now();
42246 var remaining = wait - (now - previous);
42247 storedContext = this;
42249 if (remaining <= 0 || remaining > wait) {
42251 clearTimeout(timeout);
42255 result = fn.apply(storedContext, storedArgs);
42257 storedContext = null;
42261 else if (!timeout) {
42262 timeout = window.setTimeout(later, remaining);