2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = (
9 Roo.each(document.styleSheets, function(s) {
10 if ( s.href && s.href.match(/css-bootstrap4/)) {
18 * base class for bootstrap elements.
22 Roo.bootstrap = Roo.bootstrap || {};
24 * @class Roo.bootstrap.Component
25 * @extends Roo.Component
26 * Bootstrap Component base class
27 * @cfg {String} cls css class
28 * @cfg {String} style any extra css
29 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
31 * @cfg {string} dataId cutomer id
32 * @cfg {string} name Specifies name attribute
33 * @cfg {string} tooltip Text for the tooltip
34 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
35 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
38 * Do not use directly - it does not do anything..
39 * @param {Object} config The config object
44 Roo.bootstrap.Component = function(config){
45 Roo.bootstrap.Component.superclass.constructor.call(this, config);
49 * @event childrenrendered
50 * Fires when the children have been rendered..
51 * @param {Roo.bootstrap.Component} this
53 "childrenrendered" : true
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
65 allowDomMove : false, // to stop relocations in parent onRender...
75 * Initialize Events for the element
77 initEvents : function() { },
83 can_build_overlaid : true,
85 container_method : false,
92 // returns the parent component..
93 return Roo.ComponentMgr.get(this.parentId)
99 onRender : function(ct, position)
101 // Roo.log("Call onRender: " + this.xtype);
103 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
106 if (this.el.attr('xtype')) {
107 this.el.attr('xtypex', this.el.attr('xtype'));
108 this.el.dom.removeAttribute('xtype');
118 var cfg = Roo.apply({}, this.getAutoCreate());
120 cfg.id = this.id || Roo.id();
122 // fill in the extra attributes
123 if (this.xattr && typeof(this.xattr) =='object') {
124 for (var i in this.xattr) {
125 cfg[i] = this.xattr[i];
130 cfg.dataId = this.dataId;
134 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
137 if (this.style) { // fixme needs to support more complex style data.
138 cfg.style = this.style;
142 cfg.name = this.name;
145 this.el = ct.createChild(cfg, position);
148 this.tooltipEl().attr('tooltip', this.tooltip);
151 if(this.tabIndex !== undefined){
152 this.el.dom.setAttribute('tabIndex', this.tabIndex);
159 * Fetch the element to add children to
160 * @return {Roo.Element} defaults to this.el
162 getChildContainer : function()
167 * Fetch the element to display the tooltip on.
168 * @return {Roo.Element} defaults to this.el
170 tooltipEl : function()
175 addxtype : function(tree,cntr)
179 cn = Roo.factory(tree);
180 //Roo.log(['addxtype', cn]);
182 cn.parentType = this.xtype; //??
183 cn.parentId = this.id;
185 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186 if (typeof(cn.container_method) == 'string') {
187 cntr = cn.container_method;
191 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
193 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
195 var build_from_html = Roo.XComponent.build_from_html;
197 var is_body = (tree.xtype == 'Body') ;
199 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
201 var self_cntr_el = Roo.get(this[cntr](false));
203 // do not try and build conditional elements
204 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
208 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210 return this.addxtypeChild(tree,cntr, is_body);
213 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
216 return this.addxtypeChild(Roo.apply({}, tree),cntr);
219 Roo.log('skipping render');
225 if (!build_from_html) {
229 // this i think handles overlaying multiple children of the same type
230 // with the sam eelement.. - which might be buggy..
232 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
238 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
242 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
249 addxtypeChild : function (tree, cntr, is_body)
251 Roo.debug && Roo.log('addxtypeChild:' + cntr);
253 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
256 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257 (typeof(tree['flexy:foreach']) != 'undefined');
261 skip_children = false;
262 // render the element if it's not BODY.
265 // if parent was disabled, then do not try and create the children..
266 if(!this[cntr](true)){
271 cn = Roo.factory(tree);
273 cn.parentType = this.xtype; //??
274 cn.parentId = this.id;
276 var build_from_html = Roo.XComponent.build_from_html;
279 // does the container contain child eleemnts with 'xtype' attributes.
280 // that match this xtype..
281 // note - when we render we create these as well..
282 // so we should check to see if body has xtype set.
283 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
285 var self_cntr_el = Roo.get(this[cntr](false));
286 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
288 //Roo.log(Roo.XComponent.build_from_html);
289 //Roo.log("got echild:");
292 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293 // and are not displayed -this causes this to use up the wrong element when matching.
294 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
297 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
304 //echild.dom.removeAttribute('xtype');
306 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307 Roo.debug && Roo.log(self_cntr_el);
308 Roo.debug && Roo.log(echild);
309 Roo.debug && Roo.log(cn);
315 // if object has flexy:if - then it may or may not be rendered.
316 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
317 // skip a flexy if element.
318 Roo.debug && Roo.log('skipping render');
319 Roo.debug && Roo.log(tree);
321 Roo.debug && Roo.log('skipping all children');
322 skip_children = true;
327 // actually if flexy:foreach is found, we really want to create
328 // multiple copies here...
330 //Roo.log(this[cntr]());
331 // some elements do not have render methods.. like the layouts...
333 if(this[cntr](true) === false){
338 cn.render && cn.render(this[cntr](true));
341 // then add the element..
348 if (typeof (tree.menu) != 'undefined') {
349 tree.menu.parentType = cn.xtype;
350 tree.menu.triggerEl = cn.el;
351 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
355 if (!tree.items || !tree.items.length) {
357 //Roo.log(["no children", this]);
362 var items = tree.items;
365 //Roo.log(items.length);
367 if (!skip_children) {
368 for(var i =0;i < items.length;i++) {
369 // Roo.log(['add child', items[i]]);
370 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
376 //Roo.log("fire childrenrendered");
378 cn.fireEvent('childrenrendered', this);
384 * Set the element that will be used to show or hide
386 setVisibilityEl : function(el)
388 this.visibilityEl = el;
392 * Get the element that will be used to show or hide
394 getVisibilityEl : function()
396 if (typeof(this.visibilityEl) == 'object') {
397 return this.visibilityEl;
400 if (typeof(this.visibilityEl) == 'string') {
401 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
408 * Show a component - removes 'hidden' class
412 if(!this.getVisibilityEl()){
416 this.getVisibilityEl().removeClass(['hidden','d-none']);
418 this.fireEvent('show', this);
423 * Hide a component - adds 'hidden' class
427 if(!this.getVisibilityEl()){
431 this.getVisibilityEl().addClass(['hidden','d-none']);
433 this.fireEvent('hide', this);
446 * @class Roo.bootstrap.Body
447 * @extends Roo.bootstrap.Component
448 * Bootstrap Body class
452 * @param {Object} config The config object
455 Roo.bootstrap.Body = function(config){
457 config = config || {};
459 Roo.bootstrap.Body.superclass.constructor.call(this, config);
460 this.el = Roo.get(config.el ? config.el : document.body );
461 if (this.cls && this.cls.length) {
462 Roo.get(document.body).addClass(this.cls);
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
468 is_body : true,// just to make sure it's constructed?
473 onRender : function(ct, position)
475 /* Roo.log("Roo.bootstrap.Body - onRender");
476 if (this.cls && this.cls.length) {
477 Roo.get(document.body).addClass(this.cls);
496 * @class Roo.bootstrap.ButtonGroup
497 * @extends Roo.bootstrap.Component
498 * Bootstrap ButtonGroup class
499 * @cfg {String} size lg | sm | xs (default empty normal)
500 * @cfg {String} align vertical | justified (default none)
501 * @cfg {String} direction up | down (default down)
502 * @cfg {Boolean} toolbar false | true
503 * @cfg {Boolean} btn true | false
508 * @param {Object} config The config object
511 Roo.bootstrap.ButtonGroup = function(config){
512 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
523 getAutoCreate : function(){
529 cfg.html = this.html || cfg.html;
540 if (['vertical','justified'].indexOf(this.align)!==-1) {
541 cfg.cls = 'btn-group-' + this.align;
543 if (this.align == 'justified') {
544 console.log(this.items);
548 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549 cfg.cls += ' btn-group-' + this.size;
552 if (this.direction == 'up') {
553 cfg.cls += ' dropup' ;
559 * Add a button to the group (similar to NavItem API.)
561 addItem : function(cfg)
563 var cn = new Roo.bootstrap.Button(cfg);
565 cn.parentId = this.id;
566 cn.onRender(this.el, null);
580 * @class Roo.bootstrap.Button
581 * @extends Roo.bootstrap.Component
582 * Bootstrap Button class
583 * @cfg {String} html The button content
584 * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585 * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587 * @cfg {String} size ( lg | sm | xs)
588 * @cfg {String} tag ( a | input | submit)
589 * @cfg {String} href empty or href
590 * @cfg {Boolean} disabled default false;
591 * @cfg {Boolean} isClose default false;
592 * @cfg {String} glyphicon depricated - use fa
593 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
594 * @cfg {String} badge text for badge
595 * @cfg {String} theme (default|glow)
596 * @cfg {Boolean} inverse dark themed version
597 * @cfg {Boolean} toggle is it a slidy toggle button
598 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
599 * @cfg {String} ontext text for on slidy toggle state
600 * @cfg {String} offtext text for off slidy toggle state
601 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
602 * @cfg {Boolean} removeClass remove the standard class..
603 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
606 * Create a new button
607 * @param {Object} config The config object
611 Roo.bootstrap.Button = function(config){
612 Roo.bootstrap.Button.superclass.constructor.call(this, config);
613 this.weightClass = ["btn-default btn-outline-secondary",
625 * When a butotn is pressed
626 * @param {Roo.bootstrap.Button} btn
627 * @param {Roo.EventObject} e
632 * After the button has been toggles
633 * @param {Roo.bootstrap.Button} btn
634 * @param {Roo.EventObject} e
635 * @param {boolean} pressed (also available as button.pressed)
641 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
662 preventDefault: true,
670 getAutoCreate : function(){
678 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
679 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
684 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
686 if (this.toggle == true) {
689 cls: 'slider-frame roo-button',
694 'data-off-text':'OFF',
695 cls: 'slider-button',
701 if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
702 cfg.cls += ' '+this.weight;
711 cfg["aria-hidden"] = true;
713 cfg.html = "×";
719 if (this.theme==='default') {
720 cfg.cls = 'btn roo-button';
722 //if (this.parentType != 'Navbar') {
723 this.weight = this.weight.length ? this.weight : 'default';
725 if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
727 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
728 var weight = this.weight == 'default' ? 'secondary' : this.weight;
729 cfg.cls += ' btn-' + outline + weight;
730 if (this.weight == 'default') {
732 cfg.cls += ' btn-' + this.weight;
735 } else if (this.theme==='glow') {
738 cfg.cls = 'btn-glow roo-button';
740 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
742 cfg.cls += ' ' + this.weight;
748 this.cls += ' inverse';
752 if (this.active || this.pressed === true) {
753 cfg.cls += ' active';
757 cfg.disabled = 'disabled';
761 Roo.log('changing to ul' );
763 this.glyphicon = 'caret';
764 if (Roo.bootstrap.version == 4) {
765 this.fa = 'caret-down';
770 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
772 //gsRoo.log(this.parentType);
773 if (this.parentType === 'Navbar' && !this.parent().bar) {
774 Roo.log('changing to li?');
783 href : this.href || '#'
786 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
787 cfg.cls += ' dropdown';
794 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
796 if (this.glyphicon) {
797 cfg.html = ' ' + cfg.html;
802 cls: 'glyphicon glyphicon-' + this.glyphicon
807 cfg.html = ' ' + cfg.html;
812 cls: 'fa fas fa-' + this.fa
822 // cfg.cls='btn roo-button';
826 var value = cfg.html;
831 cls: 'glyphicon glyphicon-' + this.glyphicon,
838 cls: 'fa fas fa-' + this.fa,
843 var bw = this.badge_weight.length ? this.badge_weight :
844 (this.weight.length ? this.weight : 'secondary');
845 bw = bw == 'default' ? 'secondary' : bw;
851 cls: 'badge badge-' + bw,
860 cfg.cls += ' dropdown';
861 cfg.html = typeof(cfg.html) != 'undefined' ?
862 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
865 if (cfg.tag !== 'a' && this.href !== '') {
866 throw "Tag must be a to set href.";
867 } else if (this.href.length > 0) {
868 cfg.href = this.href;
871 if(this.removeClass){
876 cfg.target = this.target;
881 initEvents: function() {
882 // Roo.log('init events?');
883 // Roo.log(this.el.dom);
886 if (typeof (this.menu) != 'undefined') {
887 this.menu.parentType = this.xtype;
888 this.menu.triggerEl = this.el;
889 this.addxtype(Roo.apply({}, this.menu));
893 if (this.el.hasClass('roo-button')) {
894 this.el.on('click', this.onClick, this);
896 this.el.select('.roo-button').on('click', this.onClick, this);
899 if(this.removeClass){
900 this.el.on('click', this.onClick, this);
903 this.el.enableDisplayMode();
906 onClick : function(e)
912 Roo.log('button on click ');
913 if(this.preventDefault){
917 if (this.pressed === true || this.pressed === false) {
918 this.toggleActive(e);
922 this.fireEvent('click', this, e);
926 * Enables this button
930 this.disabled = false;
931 this.el.removeClass('disabled');
935 * Disable this button
939 this.disabled = true;
940 this.el.addClass('disabled');
943 * sets the active state on/off,
944 * @param {Boolean} state (optional) Force a particular state
946 setActive : function(v) {
948 this.el[v ? 'addClass' : 'removeClass']('active');
952 * toggles the current active state
954 toggleActive : function(e)
956 this.setActive(!this.pressed);
957 this.fireEvent('toggle', this, e, !this.pressed);
960 * get the current active state
961 * @return {boolean} true if it's active
963 isActive : function()
965 return this.el.hasClass('active');
968 * set the text of the first selected button
970 setText : function(str)
972 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
975 * get the text of the first selected button
979 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
982 setWeight : function(str)
984 this.el.removeClass(this.weightClass);
986 var outline = this.outline ? 'outline-' : '';
987 if (str == 'default') {
988 this.el.addClass('btn-default btn-outline-secondary');
991 this.el.addClass('btn-' + outline + str);
1005 * @class Roo.bootstrap.Column
1006 * @extends Roo.bootstrap.Component
1007 * Bootstrap Column class
1008 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1009 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1010 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1011 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1012 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1013 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1014 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1015 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1018 * @cfg {Boolean} hidden (true|false) hide the element
1019 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1020 * @cfg {String} fa (ban|check|...) font awesome icon
1021 * @cfg {Number} fasize (1|2|....) font awsome size
1023 * @cfg {String} icon (info-sign|check|...) glyphicon name
1025 * @cfg {String} html content of column.
1028 * Create a new Column
1029 * @param {Object} config The config object
1032 Roo.bootstrap.Column = function(config){
1033 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1036 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1054 getAutoCreate : function(){
1055 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1063 ['xs','sm','md','lg'].map(function(size){
1064 //Roo.log( size + ':' + settings[size]);
1066 if (settings[size+'off'] !== false) {
1067 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1070 if (settings[size] === false) {
1074 if (!settings[size]) { // 0 = hidden
1075 cfg.cls += ' hidden-' + size + ' hidden' + size + '-down';;
1078 cfg.cls += ' col-' + size + '-' + settings[size] + (
1079 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1085 cfg.cls += ' hidden';
1088 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1089 cfg.cls +=' alert alert-' + this.alert;
1093 if (this.html.length) {
1094 cfg.html = this.html;
1098 if (this.fasize > 1) {
1099 fasize = ' fa-' + this.fasize + 'x';
1101 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1106 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1125 * @class Roo.bootstrap.Container
1126 * @extends Roo.bootstrap.Component
1127 * Bootstrap Container class
1128 * @cfg {Boolean} jumbotron is it a jumbotron element
1129 * @cfg {String} html content of element
1130 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1131 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1132 * @cfg {String} header content of header (for panel)
1133 * @cfg {String} footer content of footer (for panel)
1134 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1135 * @cfg {String} tag (header|aside|section) type of HTML tag.
1136 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1137 * @cfg {String} fa font awesome icon
1138 * @cfg {String} icon (info-sign|check|...) glyphicon name
1139 * @cfg {Boolean} hidden (true|false) hide the element
1140 * @cfg {Boolean} expandable (true|false) default false
1141 * @cfg {Boolean} expanded (true|false) default true
1142 * @cfg {String} rheader contet on the right of header
1143 * @cfg {Boolean} clickable (true|false) default false
1147 * Create a new Container
1148 * @param {Object} config The config object
1151 Roo.bootstrap.Container = function(config){
1152 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1158 * After the panel has been expand
1160 * @param {Roo.bootstrap.Container} this
1165 * After the panel has been collapsed
1167 * @param {Roo.bootstrap.Container} this
1172 * When a element is chick
1173 * @param {Roo.bootstrap.Container} this
1174 * @param {Roo.EventObject} e
1180 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1198 getChildContainer : function() {
1204 if (this.panel.length) {
1205 return this.el.select('.panel-body',true).first();
1212 getAutoCreate : function(){
1215 tag : this.tag || 'div',
1219 if (this.jumbotron) {
1220 cfg.cls = 'jumbotron';
1225 // - this is applied by the parent..
1227 // cfg.cls = this.cls + '';
1230 if (this.sticky.length) {
1232 var bd = Roo.get(document.body);
1233 if (!bd.hasClass('bootstrap-sticky')) {
1234 bd.addClass('bootstrap-sticky');
1235 Roo.select('html',true).setStyle('height', '100%');
1238 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1242 if (this.well.length) {
1243 switch (this.well) {
1246 cfg.cls +=' well well-' +this.well;
1255 cfg.cls += ' hidden';
1259 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1260 cfg.cls +=' alert alert-' + this.alert;
1265 if (this.panel.length) {
1266 cfg.cls += ' panel panel-' + this.panel;
1268 if (this.header.length) {
1272 if(this.expandable){
1274 cfg.cls = cfg.cls + ' expandable';
1278 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1286 cls : 'panel-title',
1287 html : (this.expandable ? ' ' : '') + this.header
1291 cls: 'panel-header-right',
1297 cls : 'panel-heading',
1298 style : this.expandable ? 'cursor: pointer' : '',
1306 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1311 if (this.footer.length) {
1313 cls : 'panel-footer',
1322 body.html = this.html || cfg.html;
1323 // prefix with the icons..
1325 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1328 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1333 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1334 cfg.cls = 'container';
1340 initEvents: function()
1342 if(this.expandable){
1343 var headerEl = this.headerEl();
1346 headerEl.on('click', this.onToggleClick, this);
1351 this.el.on('click', this.onClick, this);
1356 onToggleClick : function()
1358 var headerEl = this.headerEl();
1374 if(this.fireEvent('expand', this)) {
1376 this.expanded = true;
1378 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1380 this.el.select('.panel-body',true).first().removeClass('hide');
1382 var toggleEl = this.toggleEl();
1388 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1393 collapse : function()
1395 if(this.fireEvent('collapse', this)) {
1397 this.expanded = false;
1399 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1400 this.el.select('.panel-body',true).first().addClass('hide');
1402 var toggleEl = this.toggleEl();
1408 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1412 toggleEl : function()
1414 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1418 return this.el.select('.panel-heading .fa',true).first();
1421 headerEl : function()
1423 if(!this.el || !this.panel.length || !this.header.length){
1427 return this.el.select('.panel-heading',true).first()
1432 if(!this.el || !this.panel.length){
1436 return this.el.select('.panel-body',true).first()
1439 titleEl : function()
1441 if(!this.el || !this.panel.length || !this.header.length){
1445 return this.el.select('.panel-title',true).first();
1448 setTitle : function(v)
1450 var titleEl = this.titleEl();
1456 titleEl.dom.innerHTML = v;
1459 getTitle : function()
1462 var titleEl = this.titleEl();
1468 return titleEl.dom.innerHTML;
1471 setRightTitle : function(v)
1473 var t = this.el.select('.panel-header-right',true).first();
1479 t.dom.innerHTML = v;
1482 onClick : function(e)
1486 this.fireEvent('click', this, e);
1499 * @class Roo.bootstrap.Img
1500 * @extends Roo.bootstrap.Component
1501 * Bootstrap Img class
1502 * @cfg {Boolean} imgResponsive false | true
1503 * @cfg {String} border rounded | circle | thumbnail
1504 * @cfg {String} src image source
1505 * @cfg {String} alt image alternative text
1506 * @cfg {String} href a tag href
1507 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1508 * @cfg {String} xsUrl xs image source
1509 * @cfg {String} smUrl sm image source
1510 * @cfg {String} mdUrl md image source
1511 * @cfg {String} lgUrl lg image source
1514 * Create a new Input
1515 * @param {Object} config The config object
1518 Roo.bootstrap.Img = function(config){
1519 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1525 * The img click event for the img.
1526 * @param {Roo.EventObject} e
1532 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1534 imgResponsive: true,
1544 getAutoCreate : function()
1546 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1547 return this.createSingleImg();
1552 cls: 'roo-image-responsive-group',
1557 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1559 if(!_this[size + 'Url']){
1565 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1566 html: _this.html || cfg.html,
1567 src: _this[size + 'Url']
1570 img.cls += ' roo-image-responsive-' + size;
1572 var s = ['xs', 'sm', 'md', 'lg'];
1574 s.splice(s.indexOf(size), 1);
1576 Roo.each(s, function(ss){
1577 img.cls += ' hidden-' + ss;
1580 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1581 cfg.cls += ' img-' + _this.border;
1585 cfg.alt = _this.alt;
1598 a.target = _this.target;
1602 cfg.cn.push((_this.href) ? a : img);
1609 createSingleImg : function()
1613 cls: (this.imgResponsive) ? 'img-responsive' : '',
1615 src : 'about:blank' // just incase src get's set to undefined?!?
1618 cfg.html = this.html || cfg.html;
1620 cfg.src = this.src || cfg.src;
1622 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1623 cfg.cls += ' img-' + this.border;
1640 a.target = this.target;
1645 return (this.href) ? a : cfg;
1648 initEvents: function()
1651 this.el.on('click', this.onClick, this);
1656 onClick : function(e)
1658 Roo.log('img onclick');
1659 this.fireEvent('click', this, e);
1662 * Sets the url of the image - used to update it
1663 * @param {String} url the url of the image
1666 setSrc : function(url)
1670 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1671 this.el.dom.src = url;
1675 this.el.select('img', true).first().dom.src = url;
1691 * @class Roo.bootstrap.Link
1692 * @extends Roo.bootstrap.Component
1693 * Bootstrap Link Class
1694 * @cfg {String} alt image alternative text
1695 * @cfg {String} href a tag href
1696 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1697 * @cfg {String} html the content of the link.
1698 * @cfg {String} anchor name for the anchor link
1699 * @cfg {String} fa - favicon
1701 * @cfg {Boolean} preventDefault (true | false) default false
1705 * Create a new Input
1706 * @param {Object} config The config object
1709 Roo.bootstrap.Link = function(config){
1710 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1716 * The img click event for the img.
1717 * @param {Roo.EventObject} e
1723 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1727 preventDefault: false,
1733 getAutoCreate : function()
1735 var html = this.html || '';
1737 if (this.fa !== false) {
1738 html = '<i class="fa fa-' + this.fa + '"></i>';
1743 // anchor's do not require html/href...
1744 if (this.anchor === false) {
1746 cfg.href = this.href || '#';
1748 cfg.name = this.anchor;
1749 if (this.html !== false || this.fa !== false) {
1752 if (this.href !== false) {
1753 cfg.href = this.href;
1757 if(this.alt !== false){
1762 if(this.target !== false) {
1763 cfg.target = this.target;
1769 initEvents: function() {
1771 if(!this.href || this.preventDefault){
1772 this.el.on('click', this.onClick, this);
1776 onClick : function(e)
1778 if(this.preventDefault){
1781 //Roo.log('img onclick');
1782 this.fireEvent('click', this, e);
1795 * @class Roo.bootstrap.Header
1796 * @extends Roo.bootstrap.Component
1797 * Bootstrap Header class
1798 * @cfg {String} html content of header
1799 * @cfg {Number} level (1|2|3|4|5|6) default 1
1802 * Create a new Header
1803 * @param {Object} config The config object
1807 Roo.bootstrap.Header = function(config){
1808 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1811 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1819 getAutoCreate : function(){
1824 tag: 'h' + (1 *this.level),
1825 html: this.html || ''
1837 * Ext JS Library 1.1.1
1838 * Copyright(c) 2006-2007, Ext JS, LLC.
1840 * Originally Released Under LGPL - original licence link has changed is not relivant.
1843 * <script type="text/javascript">
1847 * @class Roo.bootstrap.MenuMgr
1848 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1851 Roo.bootstrap.MenuMgr = function(){
1852 var menus, active, groups = {}, attached = false, lastShow = new Date();
1854 // private - called when first menu is created
1857 active = new Roo.util.MixedCollection();
1858 Roo.get(document).addKeyListener(27, function(){
1859 if(active.length > 0){
1867 if(active && active.length > 0){
1868 var c = active.clone();
1878 if(active.length < 1){
1879 Roo.get(document).un("mouseup", onMouseDown);
1887 var last = active.last();
1888 lastShow = new Date();
1891 Roo.get(document).on("mouseup", onMouseDown);
1896 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1897 m.parentMenu.activeChild = m;
1898 }else if(last && last.isVisible()){
1899 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1904 function onBeforeHide(m){
1906 m.activeChild.hide();
1908 if(m.autoHideTimer){
1909 clearTimeout(m.autoHideTimer);
1910 delete m.autoHideTimer;
1915 function onBeforeShow(m){
1916 var pm = m.parentMenu;
1917 if(!pm && !m.allowOtherMenus){
1919 }else if(pm && pm.activeChild && active != m){
1920 pm.activeChild.hide();
1924 // private this should really trigger on mouseup..
1925 function onMouseDown(e){
1926 Roo.log("on Mouse Up");
1928 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1929 Roo.log("MenuManager hideAll");
1938 function onBeforeCheck(mi, state){
1940 var g = groups[mi.group];
1941 for(var i = 0, l = g.length; i < l; i++){
1943 g[i].setChecked(false);
1952 * Hides all menus that are currently visible
1954 hideAll : function(){
1959 register : function(menu){
1963 menus[menu.id] = menu;
1964 menu.on("beforehide", onBeforeHide);
1965 menu.on("hide", onHide);
1966 menu.on("beforeshow", onBeforeShow);
1967 menu.on("show", onShow);
1969 if(g && menu.events["checkchange"]){
1973 groups[g].push(menu);
1974 menu.on("checkchange", onCheck);
1979 * Returns a {@link Roo.menu.Menu} object
1980 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1981 * be used to generate and return a new Menu instance.
1983 get : function(menu){
1984 if(typeof menu == "string"){ // menu id
1986 }else if(menu.events){ // menu instance
1989 /*else if(typeof menu.length == 'number'){ // array of menu items?
1990 return new Roo.bootstrap.Menu({items:menu});
1991 }else{ // otherwise, must be a config
1992 return new Roo.bootstrap.Menu(menu);
1999 unregister : function(menu){
2000 delete menus[menu.id];
2001 menu.un("beforehide", onBeforeHide);
2002 menu.un("hide", onHide);
2003 menu.un("beforeshow", onBeforeShow);
2004 menu.un("show", onShow);
2006 if(g && menu.events["checkchange"]){
2007 groups[g].remove(menu);
2008 menu.un("checkchange", onCheck);
2013 registerCheckable : function(menuItem){
2014 var g = menuItem.group;
2019 groups[g].push(menuItem);
2020 menuItem.on("beforecheckchange", onBeforeCheck);
2025 unregisterCheckable : function(menuItem){
2026 var g = menuItem.group;
2028 groups[g].remove(menuItem);
2029 menuItem.un("beforecheckchange", onBeforeCheck);
2041 * @class Roo.bootstrap.Menu
2042 * @extends Roo.bootstrap.Component
2043 * Bootstrap Menu class - container for MenuItems
2044 * @cfg {String} type (dropdown|treeview|submenu) type of menu
2045 * @cfg {bool} hidden if the menu should be hidden when rendered.
2046 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
2047 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
2051 * @param {Object} config The config object
2055 Roo.bootstrap.Menu = function(config){
2056 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2057 if (this.registerMenu && this.type != 'treeview') {
2058 Roo.bootstrap.MenuMgr.register(this);
2065 * Fires before this menu is displayed (return false to block)
2066 * @param {Roo.menu.Menu} this
2071 * Fires before this menu is hidden (return false to block)
2072 * @param {Roo.menu.Menu} this
2077 * Fires after this menu is displayed
2078 * @param {Roo.menu.Menu} this
2083 * Fires after this menu is hidden
2084 * @param {Roo.menu.Menu} this
2089 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2090 * @param {Roo.menu.Menu} this
2091 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2092 * @param {Roo.EventObject} e
2097 * Fires when the mouse is hovering over this menu
2098 * @param {Roo.menu.Menu} this
2099 * @param {Roo.EventObject} e
2100 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2105 * Fires when the mouse exits this menu
2106 * @param {Roo.menu.Menu} this
2107 * @param {Roo.EventObject} e
2108 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2113 * Fires when a menu item contained in this menu is clicked
2114 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2115 * @param {Roo.EventObject} e
2119 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2122 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2126 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2129 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2131 registerMenu : true,
2133 menuItems :false, // stores the menu items..
2143 getChildContainer : function() {
2147 getAutoCreate : function(){
2149 //if (['right'].indexOf(this.align)!==-1) {
2150 // cfg.cn[1].cls += ' pull-right'
2156 cls : 'dropdown-menu' ,
2157 style : 'z-index:1000'
2161 if (this.type === 'submenu') {
2162 cfg.cls = 'submenu active';
2164 if (this.type === 'treeview') {
2165 cfg.cls = 'treeview-menu';
2170 initEvents : function() {
2172 // Roo.log("ADD event");
2173 // Roo.log(this.triggerEl.dom);
2175 this.triggerEl.on('click', this.onTriggerClick, this);
2177 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2180 if (this.triggerEl.hasClass('nav-item')) {
2181 // dropdown toggle on the 'a' in BS4?
2182 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2184 this.triggerEl.addClass('dropdown-toggle');
2187 this.el.on('touchstart' , this.onTouch, this);
2189 this.el.on('click' , this.onClick, this);
2191 this.el.on("mouseover", this.onMouseOver, this);
2192 this.el.on("mouseout", this.onMouseOut, this);
2196 findTargetItem : function(e)
2198 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2202 //Roo.log(t); Roo.log(t.id);
2204 //Roo.log(this.menuitems);
2205 return this.menuitems.get(t.id);
2207 //return this.items.get(t.menuItemId);
2213 onTouch : function(e)
2215 Roo.log("menu.onTouch");
2216 //e.stopEvent(); this make the user popdown broken
2220 onClick : function(e)
2222 Roo.log("menu.onClick");
2224 var t = this.findTargetItem(e);
2225 if(!t || t.isContainer){
2230 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2231 if(t == this.activeItem && t.shouldDeactivate(e)){
2232 this.activeItem.deactivate();
2233 delete this.activeItem;
2237 this.setActiveItem(t, true);
2245 Roo.log('pass click event');
2249 this.fireEvent("click", this, t, e);
2253 if(!t.href.length || t.href == '#'){
2254 (function() { _this.hide(); }).defer(100);
2259 onMouseOver : function(e){
2260 var t = this.findTargetItem(e);
2263 // if(t.canActivate && !t.disabled){
2264 // this.setActiveItem(t, true);
2268 this.fireEvent("mouseover", this, e, t);
2270 isVisible : function(){
2271 return !this.hidden;
2273 onMouseOut : function(e){
2274 var t = this.findTargetItem(e);
2277 // if(t == this.activeItem && t.shouldDeactivate(e)){
2278 // this.activeItem.deactivate();
2279 // delete this.activeItem;
2282 this.fireEvent("mouseout", this, e, t);
2287 * Displays this menu relative to another element
2288 * @param {String/HTMLElement/Roo.Element} element The element to align to
2289 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2290 * the element (defaults to this.defaultAlign)
2291 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2293 show : function(el, pos, parentMenu)
2295 if (false === this.fireEvent("beforeshow", this)) {
2296 Roo.log("show canceled");
2299 this.parentMenu = parentMenu;
2304 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2307 * Displays this menu at a specific xy position
2308 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2309 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2311 showAt : function(xy, parentMenu, /* private: */_e){
2312 this.parentMenu = parentMenu;
2317 this.fireEvent("beforeshow", this);
2318 //xy = this.el.adjustForConstraints(xy);
2322 this.hideMenuItems();
2323 this.hidden = false;
2324 this.triggerEl.addClass('open');
2325 this.el.addClass('show');
2327 // reassign x when hitting right
2328 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2329 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2332 // reassign y when hitting bottom
2333 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2334 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2337 // but the list may align on trigger left or trigger top... should it be a properity?
2339 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2344 this.fireEvent("show", this);
2350 this.doFocus.defer(50, this);
2354 doFocus : function(){
2356 this.focusEl.focus();
2361 * Hides this menu and optionally all parent menus
2362 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2364 hide : function(deep)
2366 if (false === this.fireEvent("beforehide", this)) {
2367 Roo.log("hide canceled");
2370 this.hideMenuItems();
2371 if(this.el && this.isVisible()){
2373 if(this.activeItem){
2374 this.activeItem.deactivate();
2375 this.activeItem = null;
2377 this.triggerEl.removeClass('open');;
2378 this.el.removeClass('show');
2380 this.fireEvent("hide", this);
2382 if(deep === true && this.parentMenu){
2383 this.parentMenu.hide(true);
2387 onTriggerClick : function(e)
2389 Roo.log('trigger click');
2391 var target = e.getTarget();
2393 Roo.log(target.nodeName.toLowerCase());
2395 if(target.nodeName.toLowerCase() === 'i'){
2401 onTriggerPress : function(e)
2403 Roo.log('trigger press');
2404 //Roo.log(e.getTarget());
2405 // Roo.log(this.triggerEl.dom);
2407 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2408 var pel = Roo.get(e.getTarget());
2409 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2410 Roo.log('is treeview or dropdown?');
2414 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2418 if (this.isVisible()) {
2423 this.show(this.triggerEl, '?', false);
2426 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2433 hideMenuItems : function()
2435 Roo.log("hide Menu Items");
2440 this.el.select('.open',true).each(function(aa) {
2442 aa.removeClass('open');
2446 addxtypeChild : function (tree, cntr) {
2447 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2449 this.menuitems.add(comp);
2461 this.getEl().dom.innerHTML = '';
2462 this.menuitems.clear();
2476 * @class Roo.bootstrap.MenuItem
2477 * @extends Roo.bootstrap.Component
2478 * Bootstrap MenuItem class
2479 * @cfg {String} html the menu label
2480 * @cfg {String} href the link
2481 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2482 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2483 * @cfg {Boolean} active used on sidebars to highlight active itesm
2484 * @cfg {String} fa favicon to show on left of menu item.
2485 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2489 * Create a new MenuItem
2490 * @param {Object} config The config object
2494 Roo.bootstrap.MenuItem = function(config){
2495 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2500 * The raw click event for the entire grid.
2501 * @param {Roo.bootstrap.MenuItem} this
2502 * @param {Roo.EventObject} e
2508 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2512 preventDefault: false,
2513 isContainer : false,
2517 getAutoCreate : function(){
2519 if(this.isContainer){
2522 cls: 'dropdown-menu-item '
2532 cls : 'dropdown-item',
2537 if (this.fa !== false) {
2540 cls : 'fa fa-' + this.fa
2549 cls: 'dropdown-menu-item',
2552 if (this.parent().type == 'treeview') {
2553 cfg.cls = 'treeview-menu';
2556 cfg.cls += ' active';
2561 anc.href = this.href || cfg.cn[0].href ;
2562 ctag.html = this.html || cfg.cn[0].html ;
2566 initEvents: function()
2568 if (this.parent().type == 'treeview') {
2569 this.el.select('a').on('click', this.onClick, this);
2573 this.menu.parentType = this.xtype;
2574 this.menu.triggerEl = this.el;
2575 this.menu = this.addxtype(Roo.apply({}, this.menu));
2579 onClick : function(e)
2581 Roo.log('item on click ');
2583 if(this.preventDefault){
2586 //this.parent().hideMenuItems();
2588 this.fireEvent('click', this, e);
2607 * @class Roo.bootstrap.MenuSeparator
2608 * @extends Roo.bootstrap.Component
2609 * Bootstrap MenuSeparator class
2612 * Create a new MenuItem
2613 * @param {Object} config The config object
2617 Roo.bootstrap.MenuSeparator = function(config){
2618 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2621 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2623 getAutoCreate : function(){
2642 * @class Roo.bootstrap.Modal
2643 * @extends Roo.bootstrap.Component
2644 * Bootstrap Modal class
2645 * @cfg {String} title Title of dialog
2646 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2647 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2648 * @cfg {Boolean} specificTitle default false
2649 * @cfg {Array} buttons Array of buttons or standard button set..
2650 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2651 * @cfg {Boolean} animate default true
2652 * @cfg {Boolean} allow_close default true
2653 * @cfg {Boolean} fitwindow default false
2654 * @cfg {Number} width fixed width - usefull for chrome extension only really.
2655 * @cfg {Number} height fixed height - usefull for chrome extension only really.
2656 * @cfg {String} size (sm|lg) default empty
2657 * @cfg {Number} max_width set the max width of modal
2661 * Create a new Modal Dialog
2662 * @param {Object} config The config object
2665 Roo.bootstrap.Modal = function(config){
2666 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2671 * The raw btnclick event for the button
2672 * @param {Roo.EventObject} e
2677 * Fire when dialog resize
2678 * @param {Roo.bootstrap.Modal} this
2679 * @param {Roo.EventObject} e
2683 this.buttons = this.buttons || [];
2686 this.tmpl = Roo.factory(this.tmpl);
2691 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2693 title : 'test dialog',
2703 specificTitle: false,
2705 buttonPosition: 'right',
2728 onRender : function(ct, position)
2730 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2733 var cfg = Roo.apply({}, this.getAutoCreate());
2736 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2738 //if (!cfg.name.length) {
2742 cfg.cls += ' ' + this.cls;
2745 cfg.style = this.style;
2747 this.el = Roo.get(document.body).createChild(cfg, position);
2749 //var type = this.el.dom.type;
2752 if(this.tabIndex !== undefined){
2753 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2756 this.dialogEl = this.el.select('.modal-dialog',true).first();
2757 this.bodyEl = this.el.select('.modal-body',true).first();
2758 this.closeEl = this.el.select('.modal-header .close', true).first();
2759 this.headerEl = this.el.select('.modal-header',true).first();
2760 this.titleEl = this.el.select('.modal-title',true).first();
2761 this.footerEl = this.el.select('.modal-footer',true).first();
2763 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2765 //this.el.addClass("x-dlg-modal");
2767 if (this.buttons.length) {
2768 Roo.each(this.buttons, function(bb) {
2769 var b = Roo.apply({}, bb);
2770 b.xns = b.xns || Roo.bootstrap;
2771 b.xtype = b.xtype || 'Button';
2772 if (typeof(b.listeners) == 'undefined') {
2773 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2776 var btn = Roo.factory(b);
2778 btn.render(this.getButtonContainer());
2782 // render the children.
2785 if(typeof(this.items) != 'undefined'){
2786 var items = this.items;
2789 for(var i =0;i < items.length;i++) {
2790 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2794 this.items = nitems;
2796 // where are these used - they used to be body/close/footer
2800 //this.el.addClass([this.fieldClass, this.cls]);
2804 getAutoCreate : function()
2806 // we will default to modal-body-overflow - might need to remove or make optional later.
2808 cls : 'modal-body enable-modal-body-overflow ',
2809 html : this.html || ''
2814 cls : 'modal-title',
2818 if(this.specificTitle){
2824 if (this.allow_close && Roo.bootstrap.version == 3) {
2834 if (this.allow_close && Roo.bootstrap.version == 4) {
2844 if(this.size.length){
2845 size = 'modal-' + this.size;
2848 var footer = Roo.bootstrap.version == 3 ?
2850 cls : 'modal-footer',
2854 cls: 'btn-' + this.buttonPosition
2859 { // BS4 uses mr-auto on left buttons....
2860 cls : 'modal-footer'
2871 cls: "modal-dialog " + size,
2874 cls : "modal-content",
2877 cls : 'modal-header',
2892 modal.cls += ' fade';
2898 getChildContainer : function() {
2903 getButtonContainer : function() {
2905 return Roo.bootstrap.version == 4 ?
2906 this.el.select('.modal-footer',true).first()
2907 : this.el.select('.modal-footer div',true).first();
2910 initEvents : function()
2912 if (this.allow_close) {
2913 this.closeEl.on('click', this.hide, this);
2915 Roo.EventManager.onWindowResize(this.resize, this, true);
2923 this.maskEl.setSize(
2924 Roo.lib.Dom.getViewWidth(true),
2925 Roo.lib.Dom.getViewHeight(true)
2928 if (this.fitwindow) {
2932 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2933 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
2938 if(this.max_width !== 0) {
2940 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2943 this.setSize(w, this.height);
2947 if(this.max_height) {
2948 this.setSize(w,Math.min(
2950 Roo.lib.Dom.getViewportHeight(true) - 60
2956 if(!this.fit_content) {
2957 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2961 this.setSize(w, Math.min(
2963 this.headerEl.getHeight() +
2964 this.footerEl.getHeight() +
2965 this.getChildHeight(this.bodyEl.dom.childNodes),
2966 Roo.lib.Dom.getViewportHeight(true) - 60)
2972 setSize : function(w,h)
2983 if (!this.rendered) {
2987 //this.el.setStyle('display', 'block');
2988 this.el.removeClass('hideing');
2989 this.el.dom.style.display='block';
2991 Roo.get(document.body).addClass('modal-open');
2993 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2996 this.el.addClass('show');
2997 this.el.addClass('in');
3000 this.el.addClass('show');
3001 this.el.addClass('in');
3004 // not sure how we can show data in here..
3006 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3009 Roo.get(document.body).addClass("x-body-masked");
3011 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3012 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3013 this.maskEl.dom.style.display = 'block';
3014 this.maskEl.addClass('show');
3019 this.fireEvent('show', this);
3021 // set zindex here - otherwise it appears to be ignored...
3022 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3025 this.items.forEach( function(e) {
3026 e.layout ? e.layout() : false;
3034 if(this.fireEvent("beforehide", this) !== false){
3036 this.maskEl.removeClass('show');
3038 this.maskEl.dom.style.display = '';
3039 Roo.get(document.body).removeClass("x-body-masked");
3040 this.el.removeClass('in');
3041 this.el.select('.modal-dialog', true).first().setStyle('transform','');
3043 if(this.animate){ // why
3044 this.el.addClass('hideing');
3045 this.el.removeClass('show');
3047 if (!this.el.hasClass('hideing')) {
3048 return; // it's been shown again...
3051 this.el.dom.style.display='';
3053 Roo.get(document.body).removeClass('modal-open');
3054 this.el.removeClass('hideing');
3058 this.el.removeClass('show');
3059 this.el.dom.style.display='';
3060 Roo.get(document.body).removeClass('modal-open');
3063 this.fireEvent('hide', this);
3066 isVisible : function()
3069 return this.el.hasClass('show') && !this.el.hasClass('hideing');
3073 addButton : function(str, cb)
3077 var b = Roo.apply({}, { html : str } );
3078 b.xns = b.xns || Roo.bootstrap;
3079 b.xtype = b.xtype || 'Button';
3080 if (typeof(b.listeners) == 'undefined') {
3081 b.listeners = { click : cb.createDelegate(this) };
3084 var btn = Roo.factory(b);
3086 btn.render(this.getButtonContainer());
3092 setDefaultButton : function(btn)
3094 //this.el.select('.modal-footer').()
3097 resizeTo: function(w,h)
3099 this.dialogEl.setWidth(w);
3101 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
3103 this.bodyEl.setHeight(h - diff);
3105 this.fireEvent('resize', this);
3108 setContentSize : function(w, h)
3112 onButtonClick: function(btn,e)
3115 this.fireEvent('btnclick', btn.name, e);
3118 * Set the title of the Dialog
3119 * @param {String} str new Title
3121 setTitle: function(str) {
3122 this.titleEl.dom.innerHTML = str;
3125 * Set the body of the Dialog
3126 * @param {String} str new Title
3128 setBody: function(str) {
3129 this.bodyEl.dom.innerHTML = str;
3132 * Set the body of the Dialog using the template
3133 * @param {Obj} data - apply this data to the template and replace the body contents.
3135 applyBody: function(obj)
3138 Roo.log("Error - using apply Body without a template");
3141 this.tmpl.overwrite(this.bodyEl, obj);
3144 getChildHeight : function(child_nodes)
3148 child_nodes.length == 0
3153 var child_height = 0;
3155 for(var i = 0; i < child_nodes.length; i++) {
3158 * for modal with tabs...
3159 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3161 var layout_childs = child_nodes[i].childNodes;
3163 for(var j = 0; j < layout_childs.length; j++) {
3165 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3167 var layout_body_childs = layout_childs[j].childNodes;
3169 for(var k = 0; k < layout_body_childs.length; k++) {
3171 if(layout_body_childs[k].classList.contains('navbar')) {
3172 child_height += layout_body_childs[k].offsetHeight;
3176 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3178 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3180 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3182 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3183 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3198 child_height += child_nodes[i].offsetHeight;
3199 // Roo.log(child_nodes[i].offsetHeight);
3202 return child_height;
3208 Roo.apply(Roo.bootstrap.Modal, {
3210 * Button config that displays a single OK button
3219 * Button config that displays Yes and No buttons
3235 * Button config that displays OK and Cancel buttons
3250 * Button config that displays Yes, No and Cancel buttons
3274 * messagebox - can be used as a replace
3278 * @class Roo.MessageBox
3279 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3283 Roo.Msg.alert('Status', 'Changes saved successfully.');
3285 // Prompt for user data:
3286 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3288 // process text value...
3292 // Show a dialog using config options:
3294 title:'Save Changes?',
3295 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3296 buttons: Roo.Msg.YESNOCANCEL,
3303 Roo.bootstrap.MessageBox = function(){
3304 var dlg, opt, mask, waitTimer;
3305 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3306 var buttons, activeTextEl, bwidth;
3310 var handleButton = function(button){
3312 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3316 var handleHide = function(){
3318 dlg.el.removeClass(opt.cls);
3321 // Roo.TaskMgr.stop(waitTimer);
3322 // waitTimer = null;
3327 var updateButtons = function(b){
3330 buttons["ok"].hide();
3331 buttons["cancel"].hide();
3332 buttons["yes"].hide();
3333 buttons["no"].hide();
3334 dlg.footerEl.hide();
3338 dlg.footerEl.show();
3339 for(var k in buttons){
3340 if(typeof buttons[k] != "function"){
3343 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3344 width += buttons[k].el.getWidth()+15;
3354 var handleEsc = function(d, k, e){
3355 if(opt && opt.closable !== false){
3365 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3366 * @return {Roo.BasicDialog} The BasicDialog element
3368 getDialog : function(){
3370 dlg = new Roo.bootstrap.Modal( {
3373 //constraintoviewport:false,
3375 //collapsible : false,
3380 //buttonAlign:"center",
3381 closeClick : function(){
3382 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3385 handleButton("cancel");
3390 dlg.on("hide", handleHide);
3392 //dlg.addKeyListener(27, handleEsc);
3394 this.buttons = buttons;
3395 var bt = this.buttonText;
3396 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3397 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3398 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3399 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3401 bodyEl = dlg.bodyEl.createChild({
3403 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3404 '<textarea class="roo-mb-textarea"></textarea>' +
3405 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3407 msgEl = bodyEl.dom.firstChild;
3408 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3409 textboxEl.enableDisplayMode();
3410 textboxEl.addKeyListener([10,13], function(){
3411 if(dlg.isVisible() && opt && opt.buttons){
3414 }else if(opt.buttons.yes){
3415 handleButton("yes");
3419 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3420 textareaEl.enableDisplayMode();
3421 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3422 progressEl.enableDisplayMode();
3424 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3425 var pf = progressEl.dom.firstChild;
3427 pp = Roo.get(pf.firstChild);
3428 pp.setHeight(pf.offsetHeight);
3436 * Updates the message box body text
3437 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3438 * the XHTML-compliant non-breaking space character '&#160;')
3439 * @return {Roo.MessageBox} This message box
3441 updateText : function(text)
3443 if(!dlg.isVisible() && !opt.width){
3444 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3445 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3447 msgEl.innerHTML = text || ' ';
3449 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3450 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3452 Math.min(opt.width || cw , this.maxWidth),
3453 Math.max(opt.minWidth || this.minWidth, bwidth)
3456 activeTextEl.setWidth(w);
3458 if(dlg.isVisible()){
3459 dlg.fixedcenter = false;
3461 // to big, make it scroll. = But as usual stupid IE does not support
3464 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3465 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3466 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3468 bodyEl.dom.style.height = '';
3469 bodyEl.dom.style.overflowY = '';
3472 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3474 bodyEl.dom.style.overflowX = '';
3477 dlg.setContentSize(w, bodyEl.getHeight());
3478 if(dlg.isVisible()){
3479 dlg.fixedcenter = true;
3485 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3486 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3487 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3488 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3489 * @return {Roo.MessageBox} This message box
3491 updateProgress : function(value, text){
3493 this.updateText(text);
3496 if (pp) { // weird bug on my firefox - for some reason this is not defined
3497 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3498 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3504 * Returns true if the message box is currently displayed
3505 * @return {Boolean} True if the message box is visible, else false
3507 isVisible : function(){
3508 return dlg && dlg.isVisible();
3512 * Hides the message box if it is displayed
3515 if(this.isVisible()){
3521 * Displays a new message box, or reinitializes an existing message box, based on the config options
3522 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3523 * The following config object properties are supported:
3525 Property Type Description
3526 ---------- --------------- ------------------------------------------------------------------------------------
3527 animEl String/Element An id or Element from which the message box should animate as it opens and
3528 closes (defaults to undefined)
3529 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3530 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3531 closable Boolean False to hide the top-right close button (defaults to true). Note that
3532 progress and wait dialogs will ignore this property and always hide the
3533 close button as they can only be closed programmatically.
3534 cls String A custom CSS class to apply to the message box element
3535 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3536 displayed (defaults to 75)
3537 fn Function A callback function to execute after closing the dialog. The arguments to the
3538 function will be btn (the name of the button that was clicked, if applicable,
3539 e.g. "ok"), and text (the value of the active text field, if applicable).
3540 Progress and wait dialogs will ignore this option since they do not respond to
3541 user actions and can only be closed programmatically, so any required function
3542 should be called by the same code after it closes the dialog.
3543 icon String A CSS class that provides a background image to be used as an icon for
3544 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3545 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3546 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3547 modal Boolean False to allow user interaction with the page while the message box is
3548 displayed (defaults to true)
3549 msg String A string that will replace the existing message box body text (defaults
3550 to the XHTML-compliant non-breaking space character ' ')
3551 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3552 progress Boolean True to display a progress bar (defaults to false)
3553 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3554 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3555 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3556 title String The title text
3557 value String The string value to set into the active textbox element if displayed
3558 wait Boolean True to display a progress bar (defaults to false)
3559 width Number The width of the dialog in pixels
3566 msg: 'Please enter your address:',
3568 buttons: Roo.MessageBox.OKCANCEL,
3571 animEl: 'addAddressBtn'
3574 * @param {Object} config Configuration options
3575 * @return {Roo.MessageBox} This message box
3577 show : function(options)
3580 // this causes nightmares if you show one dialog after another
3581 // especially on callbacks..
3583 if(this.isVisible()){
3586 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3587 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3588 Roo.log("New Dialog Message:" + options.msg )
3589 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3590 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3593 var d = this.getDialog();
3595 d.setTitle(opt.title || " ");
3596 d.closeEl.setDisplayed(opt.closable !== false);
3597 activeTextEl = textboxEl;
3598 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3603 textareaEl.setHeight(typeof opt.multiline == "number" ?
3604 opt.multiline : this.defaultTextHeight);
3605 activeTextEl = textareaEl;
3614 progressEl.setDisplayed(opt.progress === true);
3616 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3618 this.updateProgress(0);
3619 activeTextEl.dom.value = opt.value || "";
3621 dlg.setDefaultButton(activeTextEl);
3623 var bs = opt.buttons;
3627 }else if(bs && bs.yes){
3628 db = buttons["yes"];
3630 dlg.setDefaultButton(db);
3632 bwidth = updateButtons(opt.buttons);
3633 this.updateText(opt.msg);
3635 d.el.addClass(opt.cls);
3637 d.proxyDrag = opt.proxyDrag === true;
3638 d.modal = opt.modal !== false;
3639 d.mask = opt.modal !== false ? mask : false;
3641 // force it to the end of the z-index stack so it gets a cursor in FF
3642 document.body.appendChild(dlg.el.dom);
3643 d.animateTarget = null;
3644 d.show(options.animEl);
3650 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3651 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3652 * and closing the message box when the process is complete.
3653 * @param {String} title The title bar text
3654 * @param {String} msg The message box body text
3655 * @return {Roo.MessageBox} This message box
3657 progress : function(title, msg){
3664 minWidth: this.minProgressWidth,
3671 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3672 * If a callback function is passed it will be called after the user clicks the button, and the
3673 * id of the button that was clicked will be passed as the only parameter to the callback
3674 * (could also be the top-right close button).
3675 * @param {String} title The title bar text
3676 * @param {String} msg The message box body text
3677 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3678 * @param {Object} scope (optional) The scope of the callback function
3679 * @return {Roo.MessageBox} This message box
3681 alert : function(title, msg, fn, scope)
3696 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3697 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3698 * You are responsible for closing the message box when the process is complete.
3699 * @param {String} msg The message box body text
3700 * @param {String} title (optional) The title bar text
3701 * @return {Roo.MessageBox} This message box
3703 wait : function(msg, title){
3714 waitTimer = Roo.TaskMgr.start({
3716 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3724 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3725 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3726 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3727 * @param {String} title The title bar text
3728 * @param {String} msg The message box body text
3729 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3730 * @param {Object} scope (optional) The scope of the callback function
3731 * @return {Roo.MessageBox} This message box
3733 confirm : function(title, msg, fn, scope){
3737 buttons: this.YESNO,
3746 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3747 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3748 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3749 * (could also be the top-right close button) and the text that was entered will be passed as the two
3750 * parameters to the callback.
3751 * @param {String} title The title bar text
3752 * @param {String} msg The message box body text
3753 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3754 * @param {Object} scope (optional) The scope of the callback function
3755 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3756 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3757 * @return {Roo.MessageBox} This message box
3759 prompt : function(title, msg, fn, scope, multiline){
3763 buttons: this.OKCANCEL,
3768 multiline: multiline,
3775 * Button config that displays a single OK button
3780 * Button config that displays Yes and No buttons
3783 YESNO : {yes:true, no:true},
3785 * Button config that displays OK and Cancel buttons
3788 OKCANCEL : {ok:true, cancel:true},
3790 * Button config that displays Yes, No and Cancel buttons
3793 YESNOCANCEL : {yes:true, no:true, cancel:true},
3796 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3799 defaultTextHeight : 75,
3801 * The maximum width in pixels of the message box (defaults to 600)
3806 * The minimum width in pixels of the message box (defaults to 100)
3811 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3812 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3815 minProgressWidth : 250,
3817 * An object containing the default button text strings that can be overriden for localized language support.
3818 * Supported properties are: ok, cancel, yes and no.
3819 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3832 * Shorthand for {@link Roo.MessageBox}
3834 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3835 Roo.Msg = Roo.Msg || Roo.MessageBox;
3844 * @class Roo.bootstrap.Navbar
3845 * @extends Roo.bootstrap.Component
3846 * Bootstrap Navbar class
3849 * Create a new Navbar
3850 * @param {Object} config The config object
3854 Roo.bootstrap.Navbar = function(config){
3855 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3859 * @event beforetoggle
3860 * Fire before toggle the menu
3861 * @param {Roo.EventObject} e
3863 "beforetoggle" : true
3867 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3876 getAutoCreate : function(){
3879 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3883 initEvents :function ()
3885 //Roo.log(this.el.select('.navbar-toggle',true));
3886 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
3893 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3895 var size = this.el.getSize();
3896 this.maskEl.setSize(size.width, size.height);
3897 this.maskEl.enableDisplayMode("block");
3906 getChildContainer : function()
3908 if (this.el && this.el.select('.collapse').getCount()) {
3909 return this.el.select('.collapse',true).first();
3924 onToggle : function()
3927 if(this.fireEvent('beforetoggle', this) === false){
3930 var ce = this.el.select('.navbar-collapse',true).first();
3932 if (!ce.hasClass('show')) {
3942 * Expand the navbar pulldown
3944 expand : function ()
3947 var ce = this.el.select('.navbar-collapse',true).first();
3948 if (ce.hasClass('collapsing')) {
3951 ce.dom.style.height = '';
3953 ce.addClass('in'); // old...
3954 ce.removeClass('collapse');
3955 ce.addClass('show');
3956 var h = ce.getHeight();
3958 ce.removeClass('show');
3959 // at this point we should be able to see it..
3960 ce.addClass('collapsing');
3962 ce.setHeight(0); // resize it ...
3963 ce.on('transitionend', function() {
3964 //Roo.log('done transition');
3965 ce.removeClass('collapsing');
3966 ce.addClass('show');
3967 ce.removeClass('collapse');
3969 ce.dom.style.height = '';
3970 }, this, { single: true} );
3972 ce.dom.scrollTop = 0;
3975 * Collapse the navbar pulldown
3977 collapse : function()
3979 var ce = this.el.select('.navbar-collapse',true).first();
3981 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
3982 // it's collapsed or collapsing..
3985 ce.removeClass('in'); // old...
3986 ce.setHeight(ce.getHeight());
3987 ce.removeClass('show');
3988 ce.addClass('collapsing');
3990 ce.on('transitionend', function() {
3991 ce.dom.style.height = '';
3992 ce.removeClass('collapsing');
3993 ce.addClass('collapse');
3994 }, this, { single: true} );
4014 * @class Roo.bootstrap.NavSimplebar
4015 * @extends Roo.bootstrap.Navbar
4016 * Bootstrap Sidebar class
4018 * @cfg {Boolean} inverse is inverted color
4020 * @cfg {String} type (nav | pills | tabs)
4021 * @cfg {Boolean} arrangement stacked | justified
4022 * @cfg {String} align (left | right) alignment
4024 * @cfg {Boolean} main (true|false) main nav bar? default false
4025 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4027 * @cfg {String} tag (header|footer|nav|div) default is nav
4029 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4033 * Create a new Sidebar
4034 * @param {Object} config The config object
4038 Roo.bootstrap.NavSimplebar = function(config){
4039 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4042 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
4058 getAutoCreate : function(){
4062 tag : this.tag || 'div',
4063 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
4065 if (['light','white'].indexOf(this.weight) > -1) {
4066 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4068 cfg.cls += ' bg-' + this.weight;
4071 cfg.cls += ' navbar-inverse';
4075 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4077 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
4086 cls: 'nav nav-' + this.xtype,
4092 this.type = this.type || 'nav';
4093 if (['tabs','pills'].indexOf(this.type) != -1) {
4094 cfg.cn[0].cls += ' nav-' + this.type
4098 if (this.type!=='nav') {
4099 Roo.log('nav type must be nav/tabs/pills')
4101 cfg.cn[0].cls += ' navbar-nav'
4107 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4108 cfg.cn[0].cls += ' nav-' + this.arrangement;
4112 if (this.align === 'right') {
4113 cfg.cn[0].cls += ' navbar-right';
4138 * navbar-expand-md fixed-top
4142 * @class Roo.bootstrap.NavHeaderbar
4143 * @extends Roo.bootstrap.NavSimplebar
4144 * Bootstrap Sidebar class
4146 * @cfg {String} brand what is brand
4147 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4148 * @cfg {String} brand_href href of the brand
4149 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
4150 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4151 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4152 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4155 * Create a new Sidebar
4156 * @param {Object} config The config object
4160 Roo.bootstrap.NavHeaderbar = function(config){
4161 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4165 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
4172 desktopCenter : false,
4175 getAutoCreate : function(){
4178 tag: this.nav || 'nav',
4179 cls: 'navbar navbar-expand-md',
4185 if (this.desktopCenter) {
4186 cn.push({cls : 'container', cn : []});
4194 cls: 'navbar-toggle navbar-toggler',
4195 'data-toggle': 'collapse',
4200 html: 'Toggle navigation'
4204 cls: 'icon-bar navbar-toggler-icon'
4217 cn.push( Roo.bootstrap.version == 4 ? btn : {
4219 cls: 'navbar-header',
4228 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
4232 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4234 if (['light','white'].indexOf(this.weight) > -1) {
4235 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4237 cfg.cls += ' bg-' + this.weight;
4240 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4241 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4243 // tag can override this..
4245 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4248 if (this.brand !== '') {
4249 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4250 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4252 href: this.brand_href ? this.brand_href : '#',
4253 cls: 'navbar-brand',
4261 cfg.cls += ' main-nav';
4269 getHeaderChildContainer : function()
4271 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4272 return this.el.select('.navbar-header',true).first();
4275 return this.getChildContainer();
4279 initEvents : function()
4281 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4283 if (this.autohide) {
4288 Roo.get(document).on('scroll',function(e) {
4289 var ns = Roo.get(document).getScroll().top;
4290 var os = prevScroll;
4294 ft.removeClass('slideDown');
4295 ft.addClass('slideUp');
4298 ft.removeClass('slideUp');
4299 ft.addClass('slideDown');
4320 * @class Roo.bootstrap.NavSidebar
4321 * @extends Roo.bootstrap.Navbar
4322 * Bootstrap Sidebar class
4325 * Create a new Sidebar
4326 * @param {Object} config The config object
4330 Roo.bootstrap.NavSidebar = function(config){
4331 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4334 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4336 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4338 getAutoCreate : function(){
4343 cls: 'sidebar sidebar-nav'
4365 * @class Roo.bootstrap.NavGroup
4366 * @extends Roo.bootstrap.Component
4367 * Bootstrap NavGroup class
4368 * @cfg {String} align (left|right)
4369 * @cfg {Boolean} inverse
4370 * @cfg {String} type (nav|pills|tab) default nav
4371 * @cfg {String} navId - reference Id for navbar.
4375 * Create a new nav group
4376 * @param {Object} config The config object
4379 Roo.bootstrap.NavGroup = function(config){
4380 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4383 Roo.bootstrap.NavGroup.register(this);
4387 * Fires when the active item changes
4388 * @param {Roo.bootstrap.NavGroup} this
4389 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4390 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4397 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4408 getAutoCreate : function()
4410 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4416 if (Roo.bootstrap.version == 4) {
4417 if (['tabs','pills'].indexOf(this.type) != -1) {
4418 cfg.cls += ' nav-' + this.type;
4420 // trying to remove so header bar can right align top?
4421 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
4422 // do not use on header bar...
4423 cfg.cls += ' navbar-nav';
4428 if (['tabs','pills'].indexOf(this.type) != -1) {
4429 cfg.cls += ' nav-' + this.type
4431 if (this.type !== 'nav') {
4432 Roo.log('nav type must be nav/tabs/pills')
4434 cfg.cls += ' navbar-nav'
4438 if (this.parent() && this.parent().sidebar) {
4441 cls: 'dashboard-menu sidebar-menu'
4447 if (this.form === true) {
4450 cls: 'navbar-form form-inline'
4452 //nav navbar-right ml-md-auto
4453 if (this.align === 'right') {
4454 cfg.cls += ' navbar-right ml-md-auto';
4456 cfg.cls += ' navbar-left';
4460 if (this.align === 'right') {
4461 cfg.cls += ' navbar-right ml-md-auto';
4463 cfg.cls += ' mr-auto';
4467 cfg.cls += ' navbar-inverse';
4475 * sets the active Navigation item
4476 * @param {Roo.bootstrap.NavItem} the new current navitem
4478 setActiveItem : function(item)
4481 Roo.each(this.navItems, function(v){
4486 v.setActive(false, true);
4493 item.setActive(true, true);
4494 this.fireEvent('changed', this, item, prev);
4499 * gets the active Navigation item
4500 * @return {Roo.bootstrap.NavItem} the current navitem
4502 getActive : function()
4506 Roo.each(this.navItems, function(v){
4517 indexOfNav : function()
4521 Roo.each(this.navItems, function(v,i){
4532 * adds a Navigation item
4533 * @param {Roo.bootstrap.NavItem} the navitem to add
4535 addItem : function(cfg)
4537 if (this.form && Roo.bootstrap.version == 4) {
4540 var cn = new Roo.bootstrap.NavItem(cfg);
4542 cn.parentId = this.id;
4543 cn.onRender(this.el, null);
4547 * register a Navigation item
4548 * @param {Roo.bootstrap.NavItem} the navitem to add
4550 register : function(item)
4552 this.navItems.push( item);
4553 item.navId = this.navId;
4558 * clear all the Navigation item
4561 clearAll : function()
4564 this.el.dom.innerHTML = '';
4567 getNavItem: function(tabId)
4570 Roo.each(this.navItems, function(e) {
4571 if (e.tabId == tabId) {
4581 setActiveNext : function()
4583 var i = this.indexOfNav(this.getActive());
4584 if (i > this.navItems.length) {
4587 this.setActiveItem(this.navItems[i+1]);
4589 setActivePrev : function()
4591 var i = this.indexOfNav(this.getActive());
4595 this.setActiveItem(this.navItems[i-1]);
4597 clearWasActive : function(except) {
4598 Roo.each(this.navItems, function(e) {
4599 if (e.tabId != except.tabId && e.was_active) {
4600 e.was_active = false;
4607 getWasActive : function ()
4610 Roo.each(this.navItems, function(e) {
4625 Roo.apply(Roo.bootstrap.NavGroup, {
4629 * register a Navigation Group
4630 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4632 register : function(navgrp)
4634 this.groups[navgrp.navId] = navgrp;
4638 * fetch a Navigation Group based on the navigation ID
4639 * @param {string} the navgroup to add
4640 * @returns {Roo.bootstrap.NavGroup} the navgroup
4642 get: function(navId) {
4643 if (typeof(this.groups[navId]) == 'undefined') {
4645 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4647 return this.groups[navId] ;
4662 * @class Roo.bootstrap.NavItem
4663 * @extends Roo.bootstrap.Component
4664 * Bootstrap Navbar.NavItem class
4665 * @cfg {String} href link to
4666 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4668 * @cfg {String} html content of button
4669 * @cfg {String} badge text inside badge
4670 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4671 * @cfg {String} glyphicon DEPRICATED - use fa
4672 * @cfg {String} icon DEPRICATED - use fa
4673 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4674 * @cfg {Boolean} active Is item active
4675 * @cfg {Boolean} disabled Is item disabled
4677 * @cfg {Boolean} preventDefault (true | false) default false
4678 * @cfg {String} tabId the tab that this item activates.
4679 * @cfg {String} tagtype (a|span) render as a href or span?
4680 * @cfg {Boolean} animateRef (true|false) link to element default false
4683 * Create a new Navbar Item
4684 * @param {Object} config The config object
4686 Roo.bootstrap.NavItem = function(config){
4687 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4692 * The raw click event for the entire grid.
4693 * @param {Roo.EventObject} e
4698 * Fires when the active item active state changes
4699 * @param {Roo.bootstrap.NavItem} this
4700 * @param {boolean} state the new state
4706 * Fires when scroll to element
4707 * @param {Roo.bootstrap.NavItem} this
4708 * @param {Object} options
4709 * @param {Roo.EventObject} e
4717 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4726 preventDefault : false,
4734 button_outline : false,
4738 getAutoCreate : function(){
4746 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4748 if (this.disabled) {
4749 cfg.cls += ' disabled';
4753 if (this.button_weight.length) {
4754 cfg.tag = this.href ? 'a' : 'button';
4755 cfg.html = this.html || '';
4756 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
4758 cfg.href = this.href;
4761 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
4764 // menu .. should add dropdown-menu class - so no need for carat..
4766 if (this.badge !== '') {
4768 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4773 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4777 href : this.href || "#",
4778 html: this.html || ''
4781 if (this.tagtype == 'a') {
4782 cfg.cn[0].cls = 'nav-link';
4785 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
4788 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
4790 if(this.glyphicon) {
4791 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4796 cfg.cn[0].html += " <span class='caret'></span>";
4800 if (this.badge !== '') {
4802 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4810 onRender : function(ct, position)
4812 // Roo.log("Call onRender: " + this.xtype);
4813 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4817 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4818 this.navLink = this.el.select('.nav-link',true).first();
4823 initEvents: function()
4825 if (typeof (this.menu) != 'undefined') {
4826 this.menu.parentType = this.xtype;
4827 this.menu.triggerEl = this.el;
4828 this.menu = this.addxtype(Roo.apply({}, this.menu));
4831 this.el.select('a',true).on('click', this.onClick, this);
4833 if(this.tagtype == 'span'){
4834 this.el.select('span',true).on('click', this.onClick, this);
4837 // at this point parent should be available..
4838 this.parent().register(this);
4841 onClick : function(e)
4843 if (e.getTarget('.dropdown-menu-item')) {
4844 // did you click on a menu itemm.... - then don't trigger onclick..
4849 this.preventDefault ||
4852 Roo.log("NavItem - prevent Default?");
4856 if (this.disabled) {
4860 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4861 if (tg && tg.transition) {
4862 Roo.log("waiting for the transitionend");
4868 //Roo.log("fire event clicked");
4869 if(this.fireEvent('click', this, e) === false){
4873 if(this.tagtype == 'span'){
4877 //Roo.log(this.href);
4878 var ael = this.el.select('a',true).first();
4881 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4882 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4883 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4884 return; // ignore... - it's a 'hash' to another page.
4886 Roo.log("NavItem - prevent Default?");
4888 this.scrollToElement(e);
4892 var p = this.parent();
4894 if (['tabs','pills'].indexOf(p.type)!==-1) {
4895 if (typeof(p.setActiveItem) !== 'undefined') {
4896 p.setActiveItem(this);
4900 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4901 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4902 // remove the collapsed menu expand...
4903 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
4907 isActive: function () {
4910 setActive : function(state, fire, is_was_active)
4912 if (this.active && !state && this.navId) {
4913 this.was_active = true;
4914 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4916 nv.clearWasActive(this);
4920 this.active = state;
4923 this.el.removeClass('active');
4924 this.navLink ? this.navLink.removeClass('active') : false;
4925 } else if (!this.el.hasClass('active')) {
4927 this.el.addClass('active');
4928 if (Roo.bootstrap.version == 4 && this.navLink ) {
4929 this.navLink.addClass('active');
4934 this.fireEvent('changed', this, state);
4937 // show a panel if it's registered and related..
4939 if (!this.navId || !this.tabId || !state || is_was_active) {
4943 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4947 var pan = tg.getPanelByName(this.tabId);
4951 // if we can not flip to new panel - go back to old nav highlight..
4952 if (false == tg.showPanel(pan)) {
4953 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4955 var onav = nv.getWasActive();
4957 onav.setActive(true, false, true);
4966 // this should not be here...
4967 setDisabled : function(state)
4969 this.disabled = state;
4971 this.el.removeClass('disabled');
4972 } else if (!this.el.hasClass('disabled')) {
4973 this.el.addClass('disabled');
4979 * Fetch the element to display the tooltip on.
4980 * @return {Roo.Element} defaults to this.el
4982 tooltipEl : function()
4984 return this.el.select('' + this.tagtype + '', true).first();
4987 scrollToElement : function(e)
4989 var c = document.body;
4992 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4994 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4995 c = document.documentElement;
4998 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
5004 var o = target.calcOffsetsTo(c);
5011 this.fireEvent('scrollto', this, options, e);
5013 Roo.get(c).scrollTo('top', options.value, true);
5026 * <span> icon </span>
5027 * <span> text </span>
5028 * <span>badge </span>
5032 * @class Roo.bootstrap.NavSidebarItem
5033 * @extends Roo.bootstrap.NavItem
5034 * Bootstrap Navbar.NavSidebarItem class
5035 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5036 * {Boolean} open is the menu open
5037 * {Boolean} buttonView use button as the tigger el rather that a (default false)
5038 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5039 * {String} buttonSize (sm|md|lg)the extra classes for the button
5040 * {Boolean} showArrow show arrow next to the text (default true)
5042 * Create a new Navbar Button
5043 * @param {Object} config The config object
5045 Roo.bootstrap.NavSidebarItem = function(config){
5046 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5051 * The raw click event for the entire grid.
5052 * @param {Roo.EventObject} e
5057 * Fires when the active item active state changes
5058 * @param {Roo.bootstrap.NavSidebarItem} this
5059 * @param {boolean} state the new state
5067 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
5069 badgeWeight : 'default',
5075 buttonWeight : 'default',
5081 getAutoCreate : function(){
5086 href : this.href || '#',
5092 if(this.buttonView){
5095 href : this.href || '#',
5096 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5109 cfg.cls += ' active';
5112 if (this.disabled) {
5113 cfg.cls += ' disabled';
5116 cfg.cls += ' open x-open';
5119 if (this.glyphicon || this.icon) {
5120 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
5121 a.cn.push({ tag : 'i', cls : c }) ;
5124 if(!this.buttonView){
5127 html : this.html || ''
5134 if (this.badge !== '') {
5135 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
5141 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5144 a.cls += ' dropdown-toggle treeview' ;
5150 initEvents : function()
5152 if (typeof (this.menu) != 'undefined') {
5153 this.menu.parentType = this.xtype;
5154 this.menu.triggerEl = this.el;
5155 this.menu = this.addxtype(Roo.apply({}, this.menu));
5158 this.el.on('click', this.onClick, this);
5160 if(this.badge !== ''){
5161 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5166 onClick : function(e)
5173 if(this.preventDefault){
5177 this.fireEvent('click', this, e);
5180 disable : function()
5182 this.setDisabled(true);
5187 this.setDisabled(false);
5190 setDisabled : function(state)
5192 if(this.disabled == state){
5196 this.disabled = state;
5199 this.el.addClass('disabled');
5203 this.el.removeClass('disabled');
5208 setActive : function(state)
5210 if(this.active == state){
5214 this.active = state;
5217 this.el.addClass('active');
5221 this.el.removeClass('active');
5226 isActive: function ()
5231 setBadge : function(str)
5237 this.badgeEl.dom.innerHTML = str;
5254 * @class Roo.bootstrap.Row
5255 * @extends Roo.bootstrap.Component
5256 * Bootstrap Row class (contains columns...)
5260 * @param {Object} config The config object
5263 Roo.bootstrap.Row = function(config){
5264 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5267 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5269 getAutoCreate : function(){
5288 * @class Roo.bootstrap.Element
5289 * @extends Roo.bootstrap.Component
5290 * Bootstrap Element class
5291 * @cfg {String} html contents of the element
5292 * @cfg {String} tag tag of the element
5293 * @cfg {String} cls class of the element
5294 * @cfg {Boolean} preventDefault (true|false) default false
5295 * @cfg {Boolean} clickable (true|false) default false
5298 * Create a new Element
5299 * @param {Object} config The config object
5302 Roo.bootstrap.Element = function(config){
5303 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5309 * When a element is chick
5310 * @param {Roo.bootstrap.Element} this
5311 * @param {Roo.EventObject} e
5317 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5322 preventDefault: false,
5325 getAutoCreate : function(){
5329 // cls: this.cls, double assign in parent class Component.js :: onRender
5336 initEvents: function()
5338 Roo.bootstrap.Element.superclass.initEvents.call(this);
5341 this.el.on('click', this.onClick, this);
5346 onClick : function(e)
5348 if(this.preventDefault){
5352 this.fireEvent('click', this, e);
5355 getValue : function()
5357 return this.el.dom.innerHTML;
5360 setValue : function(value)
5362 this.el.dom.innerHTML = value;
5377 * @class Roo.bootstrap.Pagination
5378 * @extends Roo.bootstrap.Component
5379 * Bootstrap Pagination class
5380 * @cfg {String} size xs | sm | md | lg
5381 * @cfg {Boolean} inverse false | true
5384 * Create a new Pagination
5385 * @param {Object} config The config object
5388 Roo.bootstrap.Pagination = function(config){
5389 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5392 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5398 getAutoCreate : function(){
5404 cfg.cls += ' inverse';
5410 cfg.cls += " " + this.cls;
5428 * @class Roo.bootstrap.PaginationItem
5429 * @extends Roo.bootstrap.Component
5430 * Bootstrap PaginationItem class
5431 * @cfg {String} html text
5432 * @cfg {String} href the link
5433 * @cfg {Boolean} preventDefault (true | false) default true
5434 * @cfg {Boolean} active (true | false) default false
5435 * @cfg {Boolean} disabled default false
5439 * Create a new PaginationItem
5440 * @param {Object} config The config object
5444 Roo.bootstrap.PaginationItem = function(config){
5445 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5450 * The raw click event for the entire grid.
5451 * @param {Roo.EventObject} e
5457 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5461 preventDefault: true,
5466 getAutoCreate : function(){
5472 href : this.href ? this.href : '#',
5473 html : this.html ? this.html : ''
5483 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5487 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5493 initEvents: function() {
5495 this.el.on('click', this.onClick, this);
5498 onClick : function(e)
5500 Roo.log('PaginationItem on click ');
5501 if(this.preventDefault){
5509 this.fireEvent('click', this, e);
5525 * @class Roo.bootstrap.Slider
5526 * @extends Roo.bootstrap.Component
5527 * Bootstrap Slider class
5530 * Create a new Slider
5531 * @param {Object} config The config object
5534 Roo.bootstrap.Slider = function(config){
5535 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5538 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5540 getAutoCreate : function(){
5544 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5548 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5560 * Ext JS Library 1.1.1
5561 * Copyright(c) 2006-2007, Ext JS, LLC.
5563 * Originally Released Under LGPL - original licence link has changed is not relivant.
5566 * <script type="text/javascript">
5571 * @class Roo.grid.ColumnModel
5572 * @extends Roo.util.Observable
5573 * This is the default implementation of a ColumnModel used by the Grid. It defines
5574 * the columns in the grid.
5577 var colModel = new Roo.grid.ColumnModel([
5578 {header: "Ticker", width: 60, sortable: true, locked: true},
5579 {header: "Company Name", width: 150, sortable: true},
5580 {header: "Market Cap.", width: 100, sortable: true},
5581 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5582 {header: "Employees", width: 100, sortable: true, resizable: false}
5587 * The config options listed for this class are options which may appear in each
5588 * individual column definition.
5589 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5591 * @param {Object} config An Array of column config objects. See this class's
5592 * config objects for details.
5594 Roo.grid.ColumnModel = function(config){
5596 * The config passed into the constructor
5598 this.config = config;
5601 // if no id, create one
5602 // if the column does not have a dataIndex mapping,
5603 // map it to the order it is in the config
5604 for(var i = 0, len = config.length; i < len; i++){
5606 if(typeof c.dataIndex == "undefined"){
5609 if(typeof c.renderer == "string"){
5610 c.renderer = Roo.util.Format[c.renderer];
5612 if(typeof c.id == "undefined"){
5615 if(c.editor && c.editor.xtype){
5616 c.editor = Roo.factory(c.editor, Roo.grid);
5618 if(c.editor && c.editor.isFormField){
5619 c.editor = new Roo.grid.GridEditor(c.editor);
5621 this.lookup[c.id] = c;
5625 * The width of columns which have no width specified (defaults to 100)
5628 this.defaultWidth = 100;
5631 * Default sortable of columns which have no sortable specified (defaults to false)
5634 this.defaultSortable = false;
5638 * @event widthchange
5639 * Fires when the width of a column changes.
5640 * @param {ColumnModel} this
5641 * @param {Number} columnIndex The column index
5642 * @param {Number} newWidth The new width
5644 "widthchange": true,
5646 * @event headerchange
5647 * Fires when the text of a header changes.
5648 * @param {ColumnModel} this
5649 * @param {Number} columnIndex The column index
5650 * @param {Number} newText The new header text
5652 "headerchange": true,
5654 * @event hiddenchange
5655 * Fires when a column is hidden or "unhidden".
5656 * @param {ColumnModel} this
5657 * @param {Number} columnIndex The column index
5658 * @param {Boolean} hidden true if hidden, false otherwise
5660 "hiddenchange": true,
5662 * @event columnmoved
5663 * Fires when a column is moved.
5664 * @param {ColumnModel} this
5665 * @param {Number} oldIndex
5666 * @param {Number} newIndex
5668 "columnmoved" : true,
5670 * @event columlockchange
5671 * Fires when a column's locked state is changed
5672 * @param {ColumnModel} this
5673 * @param {Number} colIndex
5674 * @param {Boolean} locked true if locked
5676 "columnlockchange" : true
5678 Roo.grid.ColumnModel.superclass.constructor.call(this);
5680 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5682 * @cfg {String} header The header text to display in the Grid view.
5685 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5686 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5687 * specified, the column's index is used as an index into the Record's data Array.
5690 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5691 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5694 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5695 * Defaults to the value of the {@link #defaultSortable} property.
5696 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5699 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5702 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5705 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5708 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5711 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5712 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5713 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5714 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5717 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5720 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5723 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5726 * @cfg {String} cursor (Optional)
5729 * @cfg {String} tooltip (Optional)
5732 * @cfg {Number} xs (Optional)
5735 * @cfg {Number} sm (Optional)
5738 * @cfg {Number} md (Optional)
5741 * @cfg {Number} lg (Optional)
5744 * Returns the id of the column at the specified index.
5745 * @param {Number} index The column index
5746 * @return {String} the id
5748 getColumnId : function(index){
5749 return this.config[index].id;
5753 * Returns the column for a specified id.
5754 * @param {String} id The column id
5755 * @return {Object} the column
5757 getColumnById : function(id){
5758 return this.lookup[id];
5763 * Returns the column for a specified dataIndex.
5764 * @param {String} dataIndex The column dataIndex
5765 * @return {Object|Boolean} the column or false if not found
5767 getColumnByDataIndex: function(dataIndex){
5768 var index = this.findColumnIndex(dataIndex);
5769 return index > -1 ? this.config[index] : false;
5773 * Returns the index for a specified column id.
5774 * @param {String} id The column id
5775 * @return {Number} the index, or -1 if not found
5777 getIndexById : function(id){
5778 for(var i = 0, len = this.config.length; i < len; i++){
5779 if(this.config[i].id == id){
5787 * Returns the index for a specified column dataIndex.
5788 * @param {String} dataIndex The column dataIndex
5789 * @return {Number} the index, or -1 if not found
5792 findColumnIndex : function(dataIndex){
5793 for(var i = 0, len = this.config.length; i < len; i++){
5794 if(this.config[i].dataIndex == dataIndex){
5802 moveColumn : function(oldIndex, newIndex){
5803 var c = this.config[oldIndex];
5804 this.config.splice(oldIndex, 1);
5805 this.config.splice(newIndex, 0, c);
5806 this.dataMap = null;
5807 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5810 isLocked : function(colIndex){
5811 return this.config[colIndex].locked === true;
5814 setLocked : function(colIndex, value, suppressEvent){
5815 if(this.isLocked(colIndex) == value){
5818 this.config[colIndex].locked = value;
5820 this.fireEvent("columnlockchange", this, colIndex, value);
5824 getTotalLockedWidth : function(){
5826 for(var i = 0; i < this.config.length; i++){
5827 if(this.isLocked(i) && !this.isHidden(i)){
5828 this.totalWidth += this.getColumnWidth(i);
5834 getLockedCount : function(){
5835 for(var i = 0, len = this.config.length; i < len; i++){
5836 if(!this.isLocked(i)){
5841 return this.config.length;
5845 * Returns the number of columns.
5848 getColumnCount : function(visibleOnly){
5849 if(visibleOnly === true){
5851 for(var i = 0, len = this.config.length; i < len; i++){
5852 if(!this.isHidden(i)){
5858 return this.config.length;
5862 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5863 * @param {Function} fn
5864 * @param {Object} scope (optional)
5865 * @return {Array} result
5867 getColumnsBy : function(fn, scope){
5869 for(var i = 0, len = this.config.length; i < len; i++){
5870 var c = this.config[i];
5871 if(fn.call(scope||this, c, i) === true){
5879 * Returns true if the specified column is sortable.
5880 * @param {Number} col The column index
5883 isSortable : function(col){
5884 if(typeof this.config[col].sortable == "undefined"){
5885 return this.defaultSortable;
5887 return this.config[col].sortable;
5891 * Returns the rendering (formatting) function defined for the column.
5892 * @param {Number} col The column index.
5893 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5895 getRenderer : function(col){
5896 if(!this.config[col].renderer){
5897 return Roo.grid.ColumnModel.defaultRenderer;
5899 return this.config[col].renderer;
5903 * Sets the rendering (formatting) function for a column.
5904 * @param {Number} col The column index
5905 * @param {Function} fn The function to use to process the cell's raw data
5906 * to return HTML markup for the grid view. The render function is called with
5907 * the following parameters:<ul>
5908 * <li>Data value.</li>
5909 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5910 * <li>css A CSS style string to apply to the table cell.</li>
5911 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5912 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5913 * <li>Row index</li>
5914 * <li>Column index</li>
5915 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5917 setRenderer : function(col, fn){
5918 this.config[col].renderer = fn;
5922 * Returns the width for the specified column.
5923 * @param {Number} col The column index
5926 getColumnWidth : function(col){
5927 return this.config[col].width * 1 || this.defaultWidth;
5931 * Sets the width for a column.
5932 * @param {Number} col The column index
5933 * @param {Number} width The new width
5935 setColumnWidth : function(col, width, suppressEvent){
5936 this.config[col].width = width;
5937 this.totalWidth = null;
5939 this.fireEvent("widthchange", this, col, width);
5944 * Returns the total width of all columns.
5945 * @param {Boolean} includeHidden True to include hidden column widths
5948 getTotalWidth : function(includeHidden){
5949 if(!this.totalWidth){
5950 this.totalWidth = 0;
5951 for(var i = 0, len = this.config.length; i < len; i++){
5952 if(includeHidden || !this.isHidden(i)){
5953 this.totalWidth += this.getColumnWidth(i);
5957 return this.totalWidth;
5961 * Returns the header for the specified column.
5962 * @param {Number} col The column index
5965 getColumnHeader : function(col){
5966 return this.config[col].header;
5970 * Sets the header for a column.
5971 * @param {Number} col The column index
5972 * @param {String} header The new header
5974 setColumnHeader : function(col, header){
5975 this.config[col].header = header;
5976 this.fireEvent("headerchange", this, col, header);
5980 * Returns the tooltip for the specified column.
5981 * @param {Number} col The column index
5984 getColumnTooltip : function(col){
5985 return this.config[col].tooltip;
5988 * Sets the tooltip for a column.
5989 * @param {Number} col The column index
5990 * @param {String} tooltip The new tooltip
5992 setColumnTooltip : function(col, tooltip){
5993 this.config[col].tooltip = tooltip;
5997 * Returns the dataIndex for the specified column.
5998 * @param {Number} col The column index
6001 getDataIndex : function(col){
6002 return this.config[col].dataIndex;
6006 * Sets the dataIndex for a column.
6007 * @param {Number} col The column index
6008 * @param {Number} dataIndex The new dataIndex
6010 setDataIndex : function(col, dataIndex){
6011 this.config[col].dataIndex = dataIndex;
6017 * Returns true if the cell is editable.
6018 * @param {Number} colIndex The column index
6019 * @param {Number} rowIndex The row index - this is nto actually used..?
6022 isCellEditable : function(colIndex, rowIndex){
6023 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6027 * Returns the editor defined for the cell/column.
6028 * return false or null to disable editing.
6029 * @param {Number} colIndex The column index
6030 * @param {Number} rowIndex The row index
6033 getCellEditor : function(colIndex, rowIndex){
6034 return this.config[colIndex].editor;
6038 * Sets if a column is editable.
6039 * @param {Number} col The column index
6040 * @param {Boolean} editable True if the column is editable
6042 setEditable : function(col, editable){
6043 this.config[col].editable = editable;
6048 * Returns true if the column is hidden.
6049 * @param {Number} colIndex The column index
6052 isHidden : function(colIndex){
6053 return this.config[colIndex].hidden;
6058 * Returns true if the column width cannot be changed
6060 isFixed : function(colIndex){
6061 return this.config[colIndex].fixed;
6065 * Returns true if the column can be resized
6068 isResizable : function(colIndex){
6069 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6072 * Sets if a column is hidden.
6073 * @param {Number} colIndex The column index
6074 * @param {Boolean} hidden True if the column is hidden
6076 setHidden : function(colIndex, hidden){
6077 this.config[colIndex].hidden = hidden;
6078 this.totalWidth = null;
6079 this.fireEvent("hiddenchange", this, colIndex, hidden);
6083 * Sets the editor for a column.
6084 * @param {Number} col The column index
6085 * @param {Object} editor The editor object
6087 setEditor : function(col, editor){
6088 this.config[col].editor = editor;
6092 Roo.grid.ColumnModel.defaultRenderer = function(value)
6094 if(typeof value == "object") {
6097 if(typeof value == "string" && value.length < 1){
6101 return String.format("{0}", value);
6104 // Alias for backwards compatibility
6105 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6108 * Ext JS Library 1.1.1
6109 * Copyright(c) 2006-2007, Ext JS, LLC.
6111 * Originally Released Under LGPL - original licence link has changed is not relivant.
6114 * <script type="text/javascript">
6118 * @class Roo.LoadMask
6119 * A simple utility class for generically masking elements while loading data. If the element being masked has
6120 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6121 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
6122 * element's UpdateManager load indicator and will be destroyed after the initial load.
6124 * Create a new LoadMask
6125 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6126 * @param {Object} config The config object
6128 Roo.LoadMask = function(el, config){
6129 this.el = Roo.get(el);
6130 Roo.apply(this, config);
6132 this.store.on('beforeload', this.onBeforeLoad, this);
6133 this.store.on('load', this.onLoad, this);
6134 this.store.on('loadexception', this.onLoadException, this);
6135 this.removeMask = false;
6137 var um = this.el.getUpdateManager();
6138 um.showLoadIndicator = false; // disable the default indicator
6139 um.on('beforeupdate', this.onBeforeLoad, this);
6140 um.on('update', this.onLoad, this);
6141 um.on('failure', this.onLoad, this);
6142 this.removeMask = true;
6146 Roo.LoadMask.prototype = {
6148 * @cfg {Boolean} removeMask
6149 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6150 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
6154 * The text to display in a centered loading message box (defaults to 'Loading...')
6158 * @cfg {String} msgCls
6159 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6161 msgCls : 'x-mask-loading',
6164 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6170 * Disables the mask to prevent it from being displayed
6172 disable : function(){
6173 this.disabled = true;
6177 * Enables the mask so that it can be displayed
6179 enable : function(){
6180 this.disabled = false;
6183 onLoadException : function()
6187 if (typeof(arguments[3]) != 'undefined') {
6188 Roo.MessageBox.alert("Error loading",arguments[3]);
6192 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6193 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6200 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6205 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6209 onBeforeLoad : function(){
6211 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6216 destroy : function(){
6218 this.store.un('beforeload', this.onBeforeLoad, this);
6219 this.store.un('load', this.onLoad, this);
6220 this.store.un('loadexception', this.onLoadException, this);
6222 var um = this.el.getUpdateManager();
6223 um.un('beforeupdate', this.onBeforeLoad, this);
6224 um.un('update', this.onLoad, this);
6225 um.un('failure', this.onLoad, this);
6236 * @class Roo.bootstrap.Table
6237 * @extends Roo.bootstrap.Component
6238 * Bootstrap Table class
6239 * @cfg {String} cls table class
6240 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6241 * @cfg {String} bgcolor Specifies the background color for a table
6242 * @cfg {Number} border Specifies whether the table cells should have borders or not
6243 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6244 * @cfg {Number} cellspacing Specifies the space between cells
6245 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6246 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6247 * @cfg {String} sortable Specifies that the table should be sortable
6248 * @cfg {String} summary Specifies a summary of the content of a table
6249 * @cfg {Number} width Specifies the width of a table
6250 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6252 * @cfg {boolean} striped Should the rows be alternative striped
6253 * @cfg {boolean} bordered Add borders to the table
6254 * @cfg {boolean} hover Add hover highlighting
6255 * @cfg {boolean} condensed Format condensed
6256 * @cfg {boolean} responsive Format condensed
6257 * @cfg {Boolean} loadMask (true|false) default false
6258 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6259 * @cfg {Boolean} headerShow (true|false) generate thead, default true
6260 * @cfg {Boolean} rowSelection (true|false) default false
6261 * @cfg {Boolean} cellSelection (true|false) default false
6262 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6263 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
6264 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
6265 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6269 * Create a new Table
6270 * @param {Object} config The config object
6273 Roo.bootstrap.Table = function(config){
6274 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6279 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6280 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6281 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6282 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6284 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6286 this.sm.grid = this;
6287 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6288 this.sm = this.selModel;
6289 this.sm.xmodule = this.xmodule || false;
6292 if (this.cm && typeof(this.cm.config) == 'undefined') {
6293 this.colModel = new Roo.grid.ColumnModel(this.cm);
6294 this.cm = this.colModel;
6295 this.cm.xmodule = this.xmodule || false;
6298 this.store= Roo.factory(this.store, Roo.data);
6299 this.ds = this.store;
6300 this.ds.xmodule = this.xmodule || false;
6303 if (this.footer && this.store) {
6304 this.footer.dataSource = this.ds;
6305 this.footer = Roo.factory(this.footer);
6312 * Fires when a cell is clicked
6313 * @param {Roo.bootstrap.Table} this
6314 * @param {Roo.Element} el
6315 * @param {Number} rowIndex
6316 * @param {Number} columnIndex
6317 * @param {Roo.EventObject} e
6321 * @event celldblclick
6322 * Fires when a cell is double clicked
6323 * @param {Roo.bootstrap.Table} this
6324 * @param {Roo.Element} el
6325 * @param {Number} rowIndex
6326 * @param {Number} columnIndex
6327 * @param {Roo.EventObject} e
6329 "celldblclick" : true,
6332 * Fires when a row is clicked
6333 * @param {Roo.bootstrap.Table} this
6334 * @param {Roo.Element} el
6335 * @param {Number} rowIndex
6336 * @param {Roo.EventObject} e
6340 * @event rowdblclick
6341 * Fires when a row is double clicked
6342 * @param {Roo.bootstrap.Table} this
6343 * @param {Roo.Element} el
6344 * @param {Number} rowIndex
6345 * @param {Roo.EventObject} e
6347 "rowdblclick" : true,
6350 * Fires when a mouseover occur
6351 * @param {Roo.bootstrap.Table} this
6352 * @param {Roo.Element} el
6353 * @param {Number} rowIndex
6354 * @param {Number} columnIndex
6355 * @param {Roo.EventObject} e
6360 * Fires when a mouseout occur
6361 * @param {Roo.bootstrap.Table} this
6362 * @param {Roo.Element} el
6363 * @param {Number} rowIndex
6364 * @param {Number} columnIndex
6365 * @param {Roo.EventObject} e
6370 * Fires when a row is rendered, so you can change add a style to it.
6371 * @param {Roo.bootstrap.Table} this
6372 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6376 * @event rowsrendered
6377 * Fires when all the rows have been rendered
6378 * @param {Roo.bootstrap.Table} this
6380 'rowsrendered' : true,
6382 * @event contextmenu
6383 * The raw contextmenu event for the entire grid.
6384 * @param {Roo.EventObject} e
6386 "contextmenu" : true,
6388 * @event rowcontextmenu
6389 * Fires when a row is right clicked
6390 * @param {Roo.bootstrap.Table} this
6391 * @param {Number} rowIndex
6392 * @param {Roo.EventObject} e
6394 "rowcontextmenu" : true,
6396 * @event cellcontextmenu
6397 * Fires when a cell is right clicked
6398 * @param {Roo.bootstrap.Table} this
6399 * @param {Number} rowIndex
6400 * @param {Number} cellIndex
6401 * @param {Roo.EventObject} e
6403 "cellcontextmenu" : true,
6405 * @event headercontextmenu
6406 * Fires when a header is right clicked
6407 * @param {Roo.bootstrap.Table} this
6408 * @param {Number} columnIndex
6409 * @param {Roo.EventObject} e
6411 "headercontextmenu" : true
6415 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6441 rowSelection : false,
6442 cellSelection : false,
6445 // Roo.Element - the tbody
6447 // Roo.Element - thead element
6450 container: false, // used by gridpanel...
6456 auto_hide_footer : false,
6458 getAutoCreate : function()
6460 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6467 if (this.scrollBody) {
6468 cfg.cls += ' table-body-fixed';
6471 cfg.cls += ' table-striped';
6475 cfg.cls += ' table-hover';
6477 if (this.bordered) {
6478 cfg.cls += ' table-bordered';
6480 if (this.condensed) {
6481 cfg.cls += ' table-condensed';
6483 if (this.responsive) {
6484 cfg.cls += ' table-responsive';
6488 cfg.cls+= ' ' +this.cls;
6491 // this lot should be simplifed...
6504 ].forEach(function(k) {
6512 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6515 if(this.store || this.cm){
6516 if(this.headerShow){
6517 cfg.cn.push(this.renderHeader());
6520 cfg.cn.push(this.renderBody());
6522 if(this.footerShow){
6523 cfg.cn.push(this.renderFooter());
6525 // where does this come from?
6526 //cfg.cls+= ' TableGrid';
6529 return { cn : [ cfg ] };
6532 initEvents : function()
6534 if(!this.store || !this.cm){
6537 if (this.selModel) {
6538 this.selModel.initEvents();
6542 //Roo.log('initEvents with ds!!!!');
6544 this.mainBody = this.el.select('tbody', true).first();
6545 this.mainHead = this.el.select('thead', true).first();
6546 this.mainFoot = this.el.select('tfoot', true).first();
6552 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6553 e.on('click', _this.sort, _this);
6556 this.mainBody.on("click", this.onClick, this);
6557 this.mainBody.on("dblclick", this.onDblClick, this);
6559 // why is this done????? = it breaks dialogs??
6560 //this.parent().el.setStyle('position', 'relative');
6564 this.footer.parentId = this.id;
6565 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6568 this.el.select('tfoot tr td').first().addClass('hide');
6573 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6576 this.store.on('load', this.onLoad, this);
6577 this.store.on('beforeload', this.onBeforeLoad, this);
6578 this.store.on('update', this.onUpdate, this);
6579 this.store.on('add', this.onAdd, this);
6580 this.store.on("clear", this.clear, this);
6582 this.el.on("contextmenu", this.onContextMenu, this);
6584 this.mainBody.on('scroll', this.onBodyScroll, this);
6586 this.cm.on("headerchange", this.onHeaderChange, this);
6588 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6592 onContextMenu : function(e, t)
6594 this.processEvent("contextmenu", e);
6597 processEvent : function(name, e)
6599 if (name != 'touchstart' ) {
6600 this.fireEvent(name, e);
6603 var t = e.getTarget();
6605 var cell = Roo.get(t);
6611 if(cell.findParent('tfoot', false, true)){
6615 if(cell.findParent('thead', false, true)){
6617 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6618 cell = Roo.get(t).findParent('th', false, true);
6620 Roo.log("failed to find th in thead?");
6621 Roo.log(e.getTarget());
6626 var cellIndex = cell.dom.cellIndex;
6628 var ename = name == 'touchstart' ? 'click' : name;
6629 this.fireEvent("header" + ename, this, cellIndex, e);
6634 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6635 cell = Roo.get(t).findParent('td', false, true);
6637 Roo.log("failed to find th in tbody?");
6638 Roo.log(e.getTarget());
6643 var row = cell.findParent('tr', false, true);
6644 var cellIndex = cell.dom.cellIndex;
6645 var rowIndex = row.dom.rowIndex - 1;
6649 this.fireEvent("row" + name, this, rowIndex, e);
6653 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6659 onMouseover : function(e, el)
6661 var cell = Roo.get(el);
6667 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6668 cell = cell.findParent('td', false, true);
6671 var row = cell.findParent('tr', false, true);
6672 var cellIndex = cell.dom.cellIndex;
6673 var rowIndex = row.dom.rowIndex - 1; // start from 0
6675 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6679 onMouseout : function(e, el)
6681 var cell = Roo.get(el);
6687 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6688 cell = cell.findParent('td', false, true);
6691 var row = cell.findParent('tr', false, true);
6692 var cellIndex = cell.dom.cellIndex;
6693 var rowIndex = row.dom.rowIndex - 1; // start from 0
6695 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6699 onClick : function(e, el)
6701 var cell = Roo.get(el);
6703 if(!cell || (!this.cellSelection && !this.rowSelection)){
6707 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6708 cell = cell.findParent('td', false, true);
6711 if(!cell || typeof(cell) == 'undefined'){
6715 var row = cell.findParent('tr', false, true);
6717 if(!row || typeof(row) == 'undefined'){
6721 var cellIndex = cell.dom.cellIndex;
6722 var rowIndex = this.getRowIndex(row);
6724 // why??? - should these not be based on SelectionModel?
6725 if(this.cellSelection){
6726 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6729 if(this.rowSelection){
6730 this.fireEvent('rowclick', this, row, rowIndex, e);
6736 onDblClick : function(e,el)
6738 var cell = Roo.get(el);
6740 if(!cell || (!this.cellSelection && !this.rowSelection)){
6744 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6745 cell = cell.findParent('td', false, true);
6748 if(!cell || typeof(cell) == 'undefined'){
6752 var row = cell.findParent('tr', false, true);
6754 if(!row || typeof(row) == 'undefined'){
6758 var cellIndex = cell.dom.cellIndex;
6759 var rowIndex = this.getRowIndex(row);
6761 if(this.cellSelection){
6762 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6765 if(this.rowSelection){
6766 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6770 sort : function(e,el)
6772 var col = Roo.get(el);
6774 if(!col.hasClass('sortable')){
6778 var sort = col.attr('sort');
6781 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6785 this.store.sortInfo = {field : sort, direction : dir};
6788 Roo.log("calling footer first");
6789 this.footer.onClick('first');
6792 this.store.load({ params : { start : 0 } });
6796 renderHeader : function()
6804 this.totalWidth = 0;
6806 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6808 var config = cm.config[i];
6812 cls : 'x-hcol-' + i,
6814 html: cm.getColumnHeader(i)
6819 if(typeof(config.sortable) != 'undefined' && config.sortable){
6821 c.html = '<i class="glyphicon"></i>' + c.html;
6824 // could use BS4 hidden-..-down
6826 if(typeof(config.lgHeader) != 'undefined'){
6827 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
6830 if(typeof(config.mdHeader) != 'undefined'){
6831 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6834 if(typeof(config.smHeader) != 'undefined'){
6835 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6838 if(typeof(config.xsHeader) != 'undefined'){
6839 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6846 if(typeof(config.tooltip) != 'undefined'){
6847 c.tooltip = config.tooltip;
6850 if(typeof(config.colspan) != 'undefined'){
6851 c.colspan = config.colspan;
6854 if(typeof(config.hidden) != 'undefined' && config.hidden){
6855 c.style += ' display:none;';
6858 if(typeof(config.dataIndex) != 'undefined'){
6859 c.sort = config.dataIndex;
6864 if(typeof(config.align) != 'undefined' && config.align.length){
6865 c.style += ' text-align:' + config.align + ';';
6868 if(typeof(config.width) != 'undefined'){
6869 c.style += ' width:' + config.width + 'px;';
6870 this.totalWidth += config.width;
6872 this.totalWidth += 100; // assume minimum of 100 per column?
6875 if(typeof(config.cls) != 'undefined'){
6876 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6879 ['xs','sm','md','lg'].map(function(size){
6881 if(typeof(config[size]) == 'undefined'){
6885 if (!config[size]) { // 0 = hidden
6886 // BS 4 '0' is treated as hide that column and below.
6887 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
6891 c.cls += ' col-' + size + '-' + config[size] + (
6892 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
6904 renderBody : function()
6914 colspan : this.cm.getColumnCount()
6924 renderFooter : function()
6934 colspan : this.cm.getColumnCount()
6948 // Roo.log('ds onload');
6953 var ds = this.store;
6955 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6956 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6957 if (_this.store.sortInfo) {
6959 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6960 e.select('i', true).addClass(['glyphicon-arrow-up']);
6963 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6964 e.select('i', true).addClass(['glyphicon-arrow-down']);
6969 var tbody = this.mainBody;
6971 if(ds.getCount() > 0){
6972 ds.data.each(function(d,rowIndex){
6973 var row = this.renderRow(cm, ds, rowIndex);
6975 tbody.createChild(row);
6979 if(row.cellObjects.length){
6980 Roo.each(row.cellObjects, function(r){
6981 _this.renderCellObject(r);
6988 var tfoot = this.el.select('tfoot', true).first();
6990 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6992 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6994 var total = this.ds.getTotalCount();
6996 if(this.footer.pageSize < total){
6997 this.mainFoot.show();
7001 Roo.each(this.el.select('tbody td', true).elements, function(e){
7002 e.on('mouseover', _this.onMouseover, _this);
7005 Roo.each(this.el.select('tbody td', true).elements, function(e){
7006 e.on('mouseout', _this.onMouseout, _this);
7008 this.fireEvent('rowsrendered', this);
7014 onUpdate : function(ds,record)
7016 this.refreshRow(record);
7020 onRemove : function(ds, record, index, isUpdate){
7021 if(isUpdate !== true){
7022 this.fireEvent("beforerowremoved", this, index, record);
7024 var bt = this.mainBody.dom;
7026 var rows = this.el.select('tbody > tr', true).elements;
7028 if(typeof(rows[index]) != 'undefined'){
7029 bt.removeChild(rows[index].dom);
7032 // if(bt.rows[index]){
7033 // bt.removeChild(bt.rows[index]);
7036 if(isUpdate !== true){
7037 //this.stripeRows(index);
7038 //this.syncRowHeights(index, index);
7040 this.fireEvent("rowremoved", this, index, record);
7044 onAdd : function(ds, records, rowIndex)
7046 //Roo.log('on Add called');
7047 // - note this does not handle multiple adding very well..
7048 var bt = this.mainBody.dom;
7049 for (var i =0 ; i < records.length;i++) {
7050 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7051 //Roo.log(records[i]);
7052 //Roo.log(this.store.getAt(rowIndex+i));
7053 this.insertRow(this.store, rowIndex + i, false);
7060 refreshRow : function(record){
7061 var ds = this.store, index;
7062 if(typeof record == 'number'){
7064 record = ds.getAt(index);
7066 index = ds.indexOf(record);
7068 this.insertRow(ds, index, true);
7070 this.onRemove(ds, record, index+1, true);
7072 //this.syncRowHeights(index, index);
7074 this.fireEvent("rowupdated", this, index, record);
7077 insertRow : function(dm, rowIndex, isUpdate){
7080 this.fireEvent("beforerowsinserted", this, rowIndex);
7082 //var s = this.getScrollState();
7083 var row = this.renderRow(this.cm, this.store, rowIndex);
7084 // insert before rowIndex..
7085 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7089 if(row.cellObjects.length){
7090 Roo.each(row.cellObjects, function(r){
7091 _this.renderCellObject(r);
7096 this.fireEvent("rowsinserted", this, rowIndex);
7097 //this.syncRowHeights(firstRow, lastRow);
7098 //this.stripeRows(firstRow);
7105 getRowDom : function(rowIndex)
7107 var rows = this.el.select('tbody > tr', true).elements;
7109 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7112 // returns the object tree for a tr..
7115 renderRow : function(cm, ds, rowIndex)
7117 var d = ds.getAt(rowIndex);
7121 cls : 'x-row-' + rowIndex,
7125 var cellObjects = [];
7127 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7128 var config = cm.config[i];
7130 var renderer = cm.getRenderer(i);
7134 if(typeof(renderer) !== 'undefined'){
7135 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7137 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7138 // and are rendered into the cells after the row is rendered - using the id for the element.
7140 if(typeof(value) === 'object'){
7150 rowIndex : rowIndex,
7155 this.fireEvent('rowclass', this, rowcfg);
7159 cls : rowcfg.rowClass + ' x-col-' + i,
7161 html: (typeof(value) === 'object') ? '' : value
7168 if(typeof(config.colspan) != 'undefined'){
7169 td.colspan = config.colspan;
7172 if(typeof(config.hidden) != 'undefined' && config.hidden){
7173 td.style += ' display:none;';
7176 if(typeof(config.align) != 'undefined' && config.align.length){
7177 td.style += ' text-align:' + config.align + ';';
7179 if(typeof(config.valign) != 'undefined' && config.valign.length){
7180 td.style += ' vertical-align:' + config.valign + ';';
7183 if(typeof(config.width) != 'undefined'){
7184 td.style += ' width:' + config.width + 'px;';
7187 if(typeof(config.cursor) != 'undefined'){
7188 td.style += ' cursor:' + config.cursor + ';';
7191 if(typeof(config.cls) != 'undefined'){
7192 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7195 ['xs','sm','md','lg'].map(function(size){
7197 if(typeof(config[size]) == 'undefined'){
7203 if (!config[size]) { // 0 = hidden
7204 // BS 4 '0' is treated as hide that column and below.
7205 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
7209 td.cls += ' col-' + size + '-' + config[size] + (
7210 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7220 row.cellObjects = cellObjects;
7228 onBeforeLoad : function()
7237 this.el.select('tbody', true).first().dom.innerHTML = '';
7240 * Show or hide a row.
7241 * @param {Number} rowIndex to show or hide
7242 * @param {Boolean} state hide
7244 setRowVisibility : function(rowIndex, state)
7246 var bt = this.mainBody.dom;
7248 var rows = this.el.select('tbody > tr', true).elements;
7250 if(typeof(rows[rowIndex]) == 'undefined'){
7253 rows[rowIndex].dom.style.display = state ? '' : 'none';
7257 getSelectionModel : function(){
7259 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7261 return this.selModel;
7264 * Render the Roo.bootstrap object from renderder
7266 renderCellObject : function(r)
7270 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7272 var t = r.cfg.render(r.container);
7275 Roo.each(r.cfg.cn, function(c){
7277 container: t.getChildContainer(),
7280 _this.renderCellObject(child);
7285 getRowIndex : function(row)
7289 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7300 * Returns the grid's underlying element = used by panel.Grid
7301 * @return {Element} The element
7303 getGridEl : function(){
7307 * Forces a resize - used by panel.Grid
7308 * @return {Element} The element
7310 autoSize : function()
7312 //var ctr = Roo.get(this.container.dom.parentElement);
7313 var ctr = Roo.get(this.el.dom);
7315 var thd = this.getGridEl().select('thead',true).first();
7316 var tbd = this.getGridEl().select('tbody', true).first();
7317 var tfd = this.getGridEl().select('tfoot', true).first();
7319 var cw = ctr.getWidth();
7323 tbd.setSize(ctr.getWidth(),
7324 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7326 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7329 cw = Math.max(cw, this.totalWidth);
7330 this.getGridEl().select('tr',true).setWidth(cw);
7331 // resize 'expandable coloumn?
7333 return; // we doe not have a view in this design..
7336 onBodyScroll: function()
7338 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7340 this.mainHead.setStyle({
7341 'position' : 'relative',
7342 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7348 var scrollHeight = this.mainBody.dom.scrollHeight;
7350 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7352 var height = this.mainBody.getHeight();
7354 if(scrollHeight - height == scrollTop) {
7356 var total = this.ds.getTotalCount();
7358 if(this.footer.cursor + this.footer.pageSize < total){
7360 this.footer.ds.load({
7362 start : this.footer.cursor + this.footer.pageSize,
7363 limit : this.footer.pageSize
7373 onHeaderChange : function()
7375 var header = this.renderHeader();
7376 var table = this.el.select('table', true).first();
7378 this.mainHead.remove();
7379 this.mainHead = table.createChild(header, this.mainBody, false);
7382 onHiddenChange : function(colModel, colIndex, hidden)
7384 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7385 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7387 this.CSS.updateRule(thSelector, "display", "");
7388 this.CSS.updateRule(tdSelector, "display", "");
7391 this.CSS.updateRule(thSelector, "display", "none");
7392 this.CSS.updateRule(tdSelector, "display", "none");
7395 this.onHeaderChange();
7399 setColumnWidth: function(col_index, width)
7401 // width = "md-2 xs-2..."
7402 if(!this.colModel.config[col_index]) {
7406 var w = width.split(" ");
7408 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7410 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7413 for(var j = 0; j < w.length; j++) {
7419 var size_cls = w[j].split("-");
7421 if(!Number.isInteger(size_cls[1] * 1)) {
7425 if(!this.colModel.config[col_index][size_cls[0]]) {
7429 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7433 h_row[0].classList.replace(
7434 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7435 "col-"+size_cls[0]+"-"+size_cls[1]
7438 for(var i = 0; i < rows.length; i++) {
7440 var size_cls = w[j].split("-");
7442 if(!Number.isInteger(size_cls[1] * 1)) {
7446 if(!this.colModel.config[col_index][size_cls[0]]) {
7450 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7454 rows[i].classList.replace(
7455 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7456 "col-"+size_cls[0]+"-"+size_cls[1]
7460 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7475 * @class Roo.bootstrap.TableCell
7476 * @extends Roo.bootstrap.Component
7477 * Bootstrap TableCell class
7478 * @cfg {String} html cell contain text
7479 * @cfg {String} cls cell class
7480 * @cfg {String} tag cell tag (td|th) default td
7481 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7482 * @cfg {String} align Aligns the content in a cell
7483 * @cfg {String} axis Categorizes cells
7484 * @cfg {String} bgcolor Specifies the background color of a cell
7485 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7486 * @cfg {Number} colspan Specifies the number of columns a cell should span
7487 * @cfg {String} headers Specifies one or more header cells a cell is related to
7488 * @cfg {Number} height Sets the height of a cell
7489 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7490 * @cfg {Number} rowspan Sets the number of rows a cell should span
7491 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7492 * @cfg {String} valign Vertical aligns the content in a cell
7493 * @cfg {Number} width Specifies the width of a cell
7496 * Create a new TableCell
7497 * @param {Object} config The config object
7500 Roo.bootstrap.TableCell = function(config){
7501 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7504 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7524 getAutoCreate : function(){
7525 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7545 cfg.align=this.align
7551 cfg.bgcolor=this.bgcolor
7554 cfg.charoff=this.charoff
7557 cfg.colspan=this.colspan
7560 cfg.headers=this.headers
7563 cfg.height=this.height
7566 cfg.nowrap=this.nowrap
7569 cfg.rowspan=this.rowspan
7572 cfg.scope=this.scope
7575 cfg.valign=this.valign
7578 cfg.width=this.width
7597 * @class Roo.bootstrap.TableRow
7598 * @extends Roo.bootstrap.Component
7599 * Bootstrap TableRow class
7600 * @cfg {String} cls row class
7601 * @cfg {String} align Aligns the content in a table row
7602 * @cfg {String} bgcolor Specifies a background color for a table row
7603 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7604 * @cfg {String} valign Vertical aligns the content in a table row
7607 * Create a new TableRow
7608 * @param {Object} config The config object
7611 Roo.bootstrap.TableRow = function(config){
7612 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7615 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7623 getAutoCreate : function(){
7624 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7634 cfg.align = this.align;
7637 cfg.bgcolor = this.bgcolor;
7640 cfg.charoff = this.charoff;
7643 cfg.valign = this.valign;
7661 * @class Roo.bootstrap.TableBody
7662 * @extends Roo.bootstrap.Component
7663 * Bootstrap TableBody class
7664 * @cfg {String} cls element class
7665 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7666 * @cfg {String} align Aligns the content inside the element
7667 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7668 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7671 * Create a new TableBody
7672 * @param {Object} config The config object
7675 Roo.bootstrap.TableBody = function(config){
7676 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7679 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7687 getAutoCreate : function(){
7688 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7702 cfg.align = this.align;
7705 cfg.charoff = this.charoff;
7708 cfg.valign = this.valign;
7715 // initEvents : function()
7722 // this.store = Roo.factory(this.store, Roo.data);
7723 // this.store.on('load', this.onLoad, this);
7725 // this.store.load();
7729 // onLoad: function ()
7731 // this.fireEvent('load', this);
7741 * Ext JS Library 1.1.1
7742 * Copyright(c) 2006-2007, Ext JS, LLC.
7744 * Originally Released Under LGPL - original licence link has changed is not relivant.
7747 * <script type="text/javascript">
7750 // as we use this in bootstrap.
7751 Roo.namespace('Roo.form');
7753 * @class Roo.form.Action
7754 * Internal Class used to handle form actions
7756 * @param {Roo.form.BasicForm} el The form element or its id
7757 * @param {Object} config Configuration options
7762 // define the action interface
7763 Roo.form.Action = function(form, options){
7765 this.options = options || {};
7768 * Client Validation Failed
7771 Roo.form.Action.CLIENT_INVALID = 'client';
7773 * Server Validation Failed
7776 Roo.form.Action.SERVER_INVALID = 'server';
7778 * Connect to Server Failed
7781 Roo.form.Action.CONNECT_FAILURE = 'connect';
7783 * Reading Data from Server Failed
7786 Roo.form.Action.LOAD_FAILURE = 'load';
7788 Roo.form.Action.prototype = {
7790 failureType : undefined,
7791 response : undefined,
7795 run : function(options){
7800 success : function(response){
7805 handleResponse : function(response){
7809 // default connection failure
7810 failure : function(response){
7812 this.response = response;
7813 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7814 this.form.afterAction(this, false);
7817 processResponse : function(response){
7818 this.response = response;
7819 if(!response.responseText){
7822 this.result = this.handleResponse(response);
7826 // utility functions used internally
7827 getUrl : function(appendParams){
7828 var url = this.options.url || this.form.url || this.form.el.dom.action;
7830 var p = this.getParams();
7832 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7838 getMethod : function(){
7839 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7842 getParams : function(){
7843 var bp = this.form.baseParams;
7844 var p = this.options.params;
7846 if(typeof p == "object"){
7847 p = Roo.urlEncode(Roo.applyIf(p, bp));
7848 }else if(typeof p == 'string' && bp){
7849 p += '&' + Roo.urlEncode(bp);
7852 p = Roo.urlEncode(bp);
7857 createCallback : function(){
7859 success: this.success,
7860 failure: this.failure,
7862 timeout: (this.form.timeout*1000),
7863 upload: this.form.fileUpload ? this.success : undefined
7868 Roo.form.Action.Submit = function(form, options){
7869 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7872 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7875 haveProgress : false,
7876 uploadComplete : false,
7878 // uploadProgress indicator.
7879 uploadProgress : function()
7881 if (!this.form.progressUrl) {
7885 if (!this.haveProgress) {
7886 Roo.MessageBox.progress("Uploading", "Uploading");
7888 if (this.uploadComplete) {
7889 Roo.MessageBox.hide();
7893 this.haveProgress = true;
7895 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7897 var c = new Roo.data.Connection();
7899 url : this.form.progressUrl,
7904 success : function(req){
7905 //console.log(data);
7909 rdata = Roo.decode(req.responseText)
7911 Roo.log("Invalid data from server..");
7915 if (!rdata || !rdata.success) {
7917 Roo.MessageBox.alert(Roo.encode(rdata));
7920 var data = rdata.data;
7922 if (this.uploadComplete) {
7923 Roo.MessageBox.hide();
7928 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7929 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7932 this.uploadProgress.defer(2000,this);
7935 failure: function(data) {
7936 Roo.log('progress url failed ');
7947 // run get Values on the form, so it syncs any secondary forms.
7948 this.form.getValues();
7950 var o = this.options;
7951 var method = this.getMethod();
7952 var isPost = method == 'POST';
7953 if(o.clientValidation === false || this.form.isValid()){
7955 if (this.form.progressUrl) {
7956 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7957 (new Date() * 1) + '' + Math.random());
7962 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7963 form:this.form.el.dom,
7964 url:this.getUrl(!isPost),
7966 params:isPost ? this.getParams() : null,
7967 isUpload: this.form.fileUpload,
7968 formData : this.form.formData
7971 this.uploadProgress();
7973 }else if (o.clientValidation !== false){ // client validation failed
7974 this.failureType = Roo.form.Action.CLIENT_INVALID;
7975 this.form.afterAction(this, false);
7979 success : function(response)
7981 this.uploadComplete= true;
7982 if (this.haveProgress) {
7983 Roo.MessageBox.hide();
7987 var result = this.processResponse(response);
7988 if(result === true || result.success){
7989 this.form.afterAction(this, true);
7993 this.form.markInvalid(result.errors);
7994 this.failureType = Roo.form.Action.SERVER_INVALID;
7996 this.form.afterAction(this, false);
7998 failure : function(response)
8000 this.uploadComplete= true;
8001 if (this.haveProgress) {
8002 Roo.MessageBox.hide();
8005 this.response = response;
8006 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8007 this.form.afterAction(this, false);
8010 handleResponse : function(response){
8011 if(this.form.errorReader){
8012 var rs = this.form.errorReader.read(response);
8015 for(var i = 0, len = rs.records.length; i < len; i++) {
8016 var r = rs.records[i];
8020 if(errors.length < 1){
8024 success : rs.success,
8030 ret = Roo.decode(response.responseText);
8034 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8044 Roo.form.Action.Load = function(form, options){
8045 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8046 this.reader = this.form.reader;
8049 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8054 Roo.Ajax.request(Roo.apply(
8055 this.createCallback(), {
8056 method:this.getMethod(),
8057 url:this.getUrl(false),
8058 params:this.getParams()
8062 success : function(response){
8064 var result = this.processResponse(response);
8065 if(result === true || !result.success || !result.data){
8066 this.failureType = Roo.form.Action.LOAD_FAILURE;
8067 this.form.afterAction(this, false);
8070 this.form.clearInvalid();
8071 this.form.setValues(result.data);
8072 this.form.afterAction(this, true);
8075 handleResponse : function(response){
8076 if(this.form.reader){
8077 var rs = this.form.reader.read(response);
8078 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8080 success : rs.success,
8084 return Roo.decode(response.responseText);
8088 Roo.form.Action.ACTION_TYPES = {
8089 'load' : Roo.form.Action.Load,
8090 'submit' : Roo.form.Action.Submit
8099 * @class Roo.bootstrap.Form
8100 * @extends Roo.bootstrap.Component
8101 * Bootstrap Form class
8102 * @cfg {String} method GET | POST (default POST)
8103 * @cfg {String} labelAlign top | left (default top)
8104 * @cfg {String} align left | right - for navbars
8105 * @cfg {Boolean} loadMask load mask when submit (default true)
8110 * @param {Object} config The config object
8114 Roo.bootstrap.Form = function(config){
8116 Roo.bootstrap.Form.superclass.constructor.call(this, config);
8118 Roo.bootstrap.Form.popover.apply();
8122 * @event clientvalidation
8123 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8124 * @param {Form} this
8125 * @param {Boolean} valid true if the form has passed client-side validation
8127 clientvalidation: true,
8129 * @event beforeaction
8130 * Fires before any action is performed. Return false to cancel the action.
8131 * @param {Form} this
8132 * @param {Action} action The action to be performed
8136 * @event actionfailed
8137 * Fires when an action fails.
8138 * @param {Form} this
8139 * @param {Action} action The action that failed
8141 actionfailed : true,
8143 * @event actioncomplete
8144 * Fires when an action is completed.
8145 * @param {Form} this
8146 * @param {Action} action The action that completed
8148 actioncomplete : true
8152 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
8155 * @cfg {String} method
8156 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8161 * The URL to use for form actions if one isn't supplied in the action options.
8164 * @cfg {Boolean} fileUpload
8165 * Set to true if this form is a file upload.
8169 * @cfg {Object} baseParams
8170 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8174 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8178 * @cfg {Sting} align (left|right) for navbar forms
8183 activeAction : null,
8186 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8187 * element by passing it or its id or mask the form itself by passing in true.
8190 waitMsgTarget : false,
8195 * @cfg {Boolean} errorMask (true|false) default false
8200 * @cfg {Number} maskOffset Default 100
8205 * @cfg {Boolean} maskBody
8209 getAutoCreate : function(){
8213 method : this.method || 'POST',
8214 id : this.id || Roo.id(),
8217 if (this.parent().xtype.match(/^Nav/)) {
8218 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8222 if (this.labelAlign == 'left' ) {
8223 cfg.cls += ' form-horizontal';
8229 initEvents : function()
8231 this.el.on('submit', this.onSubmit, this);
8232 // this was added as random key presses on the form where triggering form submit.
8233 this.el.on('keypress', function(e) {
8234 if (e.getCharCode() != 13) {
8237 // we might need to allow it for textareas.. and some other items.
8238 // check e.getTarget().
8240 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8244 Roo.log("keypress blocked");
8252 onSubmit : function(e){
8257 * Returns true if client-side validation on the form is successful.
8260 isValid : function(){
8261 var items = this.getItems();
8265 items.each(function(f){
8271 Roo.log('invalid field: ' + f.name);
8275 if(!target && f.el.isVisible(true)){
8281 if(this.errorMask && !valid){
8282 Roo.bootstrap.Form.popover.mask(this, target);
8289 * Returns true if any fields in this form have changed since their original load.
8292 isDirty : function(){
8294 var items = this.getItems();
8295 items.each(function(f){
8305 * Performs a predefined action (submit or load) or custom actions you define on this form.
8306 * @param {String} actionName The name of the action type
8307 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8308 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8309 * accept other config options):
8311 Property Type Description
8312 ---------------- --------------- ----------------------------------------------------------------------------------
8313 url String The url for the action (defaults to the form's url)
8314 method String The form method to use (defaults to the form's method, or POST if not defined)
8315 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8316 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8317 validate the form on the client (defaults to false)
8319 * @return {BasicForm} this
8321 doAction : function(action, options){
8322 if(typeof action == 'string'){
8323 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8325 if(this.fireEvent('beforeaction', this, action) !== false){
8326 this.beforeAction(action);
8327 action.run.defer(100, action);
8333 beforeAction : function(action){
8334 var o = action.options;
8339 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8341 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8344 // not really supported yet.. ??
8346 //if(this.waitMsgTarget === true){
8347 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8348 //}else if(this.waitMsgTarget){
8349 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8350 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8352 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8358 afterAction : function(action, success){
8359 this.activeAction = null;
8360 var o = action.options;
8365 Roo.get(document.body).unmask();
8371 //if(this.waitMsgTarget === true){
8372 // this.el.unmask();
8373 //}else if(this.waitMsgTarget){
8374 // this.waitMsgTarget.unmask();
8376 // Roo.MessageBox.updateProgress(1);
8377 // Roo.MessageBox.hide();
8384 Roo.callback(o.success, o.scope, [this, action]);
8385 this.fireEvent('actioncomplete', this, action);
8389 // failure condition..
8390 // we have a scenario where updates need confirming.
8391 // eg. if a locking scenario exists..
8392 // we look for { errors : { needs_confirm : true }} in the response.
8394 (typeof(action.result) != 'undefined') &&
8395 (typeof(action.result.errors) != 'undefined') &&
8396 (typeof(action.result.errors.needs_confirm) != 'undefined')
8399 Roo.log("not supported yet");
8402 Roo.MessageBox.confirm(
8403 "Change requires confirmation",
8404 action.result.errorMsg,
8409 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8419 Roo.callback(o.failure, o.scope, [this, action]);
8420 // show an error message if no failed handler is set..
8421 if (!this.hasListener('actionfailed')) {
8422 Roo.log("need to add dialog support");
8424 Roo.MessageBox.alert("Error",
8425 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8426 action.result.errorMsg :
8427 "Saving Failed, please check your entries or try again"
8432 this.fireEvent('actionfailed', this, action);
8437 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8438 * @param {String} id The value to search for
8441 findField : function(id){
8442 var items = this.getItems();
8443 var field = items.get(id);
8445 items.each(function(f){
8446 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8453 return field || null;
8456 * Mark fields in this form invalid in bulk.
8457 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8458 * @return {BasicForm} this
8460 markInvalid : function(errors){
8461 if(errors instanceof Array){
8462 for(var i = 0, len = errors.length; i < len; i++){
8463 var fieldError = errors[i];
8464 var f = this.findField(fieldError.id);
8466 f.markInvalid(fieldError.msg);
8472 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8473 field.markInvalid(errors[id]);
8477 //Roo.each(this.childForms || [], function (f) {
8478 // f.markInvalid(errors);
8485 * Set values for fields in this form in bulk.
8486 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8487 * @return {BasicForm} this
8489 setValues : function(values){
8490 if(values instanceof Array){ // array of objects
8491 for(var i = 0, len = values.length; i < len; i++){
8493 var f = this.findField(v.id);
8495 f.setValue(v.value);
8496 if(this.trackResetOnLoad){
8497 f.originalValue = f.getValue();
8501 }else{ // object hash
8504 if(typeof values[id] != 'function' && (field = this.findField(id))){
8506 if (field.setFromData &&
8508 field.displayField &&
8509 // combos' with local stores can
8510 // be queried via setValue()
8511 // to set their value..
8512 (field.store && !field.store.isLocal)
8516 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8517 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8518 field.setFromData(sd);
8520 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8522 field.setFromData(values);
8525 field.setValue(values[id]);
8529 if(this.trackResetOnLoad){
8530 field.originalValue = field.getValue();
8536 //Roo.each(this.childForms || [], function (f) {
8537 // f.setValues(values);
8544 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8545 * they are returned as an array.
8546 * @param {Boolean} asString
8549 getValues : function(asString){
8550 //if (this.childForms) {
8551 // copy values from the child forms
8552 // Roo.each(this.childForms, function (f) {
8553 // this.setValues(f.getValues());
8559 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8560 if(asString === true){
8563 return Roo.urlDecode(fs);
8567 * Returns the fields in this form as an object with key/value pairs.
8568 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8571 getFieldValues : function(with_hidden)
8573 var items = this.getItems();
8575 items.each(function(f){
8581 var v = f.getValue();
8583 if (f.inputType =='radio') {
8584 if (typeof(ret[f.getName()]) == 'undefined') {
8585 ret[f.getName()] = ''; // empty..
8588 if (!f.el.dom.checked) {
8596 if(f.xtype == 'MoneyField'){
8597 ret[f.currencyName] = f.getCurrency();
8600 // not sure if this supported any more..
8601 if ((typeof(v) == 'object') && f.getRawValue) {
8602 v = f.getRawValue() ; // dates..
8604 // combo boxes where name != hiddenName...
8605 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8606 ret[f.name] = f.getRawValue();
8608 ret[f.getName()] = v;
8615 * Clears all invalid messages in this form.
8616 * @return {BasicForm} this
8618 clearInvalid : function(){
8619 var items = this.getItems();
8621 items.each(function(f){
8630 * @return {BasicForm} this
8633 var items = this.getItems();
8634 items.each(function(f){
8638 Roo.each(this.childForms || [], function (f) {
8646 getItems : function()
8648 var r=new Roo.util.MixedCollection(false, function(o){
8649 return o.id || (o.id = Roo.id());
8651 var iter = function(el) {
8658 Roo.each(el.items,function(e) {
8667 hideFields : function(items)
8669 Roo.each(items, function(i){
8671 var f = this.findField(i);
8682 showFields : function(items)
8684 Roo.each(items, function(i){
8686 var f = this.findField(i);
8699 Roo.apply(Roo.bootstrap.Form, {
8726 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8727 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8728 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8729 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8732 this.maskEl.top.enableDisplayMode("block");
8733 this.maskEl.left.enableDisplayMode("block");
8734 this.maskEl.bottom.enableDisplayMode("block");
8735 this.maskEl.right.enableDisplayMode("block");
8737 this.toolTip = new Roo.bootstrap.Tooltip({
8738 cls : 'roo-form-error-popover',
8740 'left' : ['r-l', [-2,0], 'right'],
8741 'right' : ['l-r', [2,0], 'left'],
8742 'bottom' : ['tl-bl', [0,2], 'top'],
8743 'top' : [ 'bl-tl', [0,-2], 'bottom']
8747 this.toolTip.render(Roo.get(document.body));
8749 this.toolTip.el.enableDisplayMode("block");
8751 Roo.get(document.body).on('click', function(){
8755 Roo.get(document.body).on('touchstart', function(){
8759 this.isApplied = true
8762 mask : function(form, target)
8766 this.target = target;
8768 if(!this.form.errorMask || !target.el){
8772 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8774 Roo.log(scrollable);
8776 var ot = this.target.el.calcOffsetsTo(scrollable);
8778 var scrollTo = ot[1] - this.form.maskOffset;
8780 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8782 scrollable.scrollTo('top', scrollTo);
8784 var box = this.target.el.getBox();
8786 var zIndex = Roo.bootstrap.Modal.zIndex++;
8789 this.maskEl.top.setStyle('position', 'absolute');
8790 this.maskEl.top.setStyle('z-index', zIndex);
8791 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8792 this.maskEl.top.setLeft(0);
8793 this.maskEl.top.setTop(0);
8794 this.maskEl.top.show();
8796 this.maskEl.left.setStyle('position', 'absolute');
8797 this.maskEl.left.setStyle('z-index', zIndex);
8798 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8799 this.maskEl.left.setLeft(0);
8800 this.maskEl.left.setTop(box.y - this.padding);
8801 this.maskEl.left.show();
8803 this.maskEl.bottom.setStyle('position', 'absolute');
8804 this.maskEl.bottom.setStyle('z-index', zIndex);
8805 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8806 this.maskEl.bottom.setLeft(0);
8807 this.maskEl.bottom.setTop(box.bottom + this.padding);
8808 this.maskEl.bottom.show();
8810 this.maskEl.right.setStyle('position', 'absolute');
8811 this.maskEl.right.setStyle('z-index', zIndex);
8812 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8813 this.maskEl.right.setLeft(box.right + this.padding);
8814 this.maskEl.right.setTop(box.y - this.padding);
8815 this.maskEl.right.show();
8817 this.toolTip.bindEl = this.target.el;
8819 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8821 var tip = this.target.blankText;
8823 if(this.target.getValue() !== '' ) {
8825 if (this.target.invalidText.length) {
8826 tip = this.target.invalidText;
8827 } else if (this.target.regexText.length){
8828 tip = this.target.regexText;
8832 this.toolTip.show(tip);
8834 this.intervalID = window.setInterval(function() {
8835 Roo.bootstrap.Form.popover.unmask();
8838 window.onwheel = function(){ return false;};
8840 (function(){ this.isMasked = true; }).defer(500, this);
8846 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8850 this.maskEl.top.setStyle('position', 'absolute');
8851 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8852 this.maskEl.top.hide();
8854 this.maskEl.left.setStyle('position', 'absolute');
8855 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8856 this.maskEl.left.hide();
8858 this.maskEl.bottom.setStyle('position', 'absolute');
8859 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8860 this.maskEl.bottom.hide();
8862 this.maskEl.right.setStyle('position', 'absolute');
8863 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8864 this.maskEl.right.hide();
8866 this.toolTip.hide();
8868 this.toolTip.el.hide();
8870 window.onwheel = function(){ return true;};
8872 if(this.intervalID){
8873 window.clearInterval(this.intervalID);
8874 this.intervalID = false;
8877 this.isMasked = false;
8887 * Ext JS Library 1.1.1
8888 * Copyright(c) 2006-2007, Ext JS, LLC.
8890 * Originally Released Under LGPL - original licence link has changed is not relivant.
8893 * <script type="text/javascript">
8896 * @class Roo.form.VTypes
8897 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8900 Roo.form.VTypes = function(){
8901 // closure these in so they are only created once.
8902 var alpha = /^[a-zA-Z_]+$/;
8903 var alphanum = /^[a-zA-Z0-9_]+$/;
8904 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8905 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8907 // All these messages and functions are configurable
8910 * The function used to validate email addresses
8911 * @param {String} value The email address
8913 'email' : function(v){
8914 return email.test(v);
8917 * The error text to display when the email validation function returns false
8920 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8922 * The keystroke filter mask to be applied on email input
8925 'emailMask' : /[a-z0-9_\.\-@]/i,
8928 * The function used to validate URLs
8929 * @param {String} value The URL
8931 'url' : function(v){
8935 * The error text to display when the url validation function returns false
8938 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8941 * The function used to validate alpha values
8942 * @param {String} value The value
8944 'alpha' : function(v){
8945 return alpha.test(v);
8948 * The error text to display when the alpha validation function returns false
8951 'alphaText' : 'This field should only contain letters and _',
8953 * The keystroke filter mask to be applied on alpha input
8956 'alphaMask' : /[a-z_]/i,
8959 * The function used to validate alphanumeric values
8960 * @param {String} value The value
8962 'alphanum' : function(v){
8963 return alphanum.test(v);
8966 * The error text to display when the alphanumeric validation function returns false
8969 'alphanumText' : 'This field should only contain letters, numbers and _',
8971 * The keystroke filter mask to be applied on alphanumeric input
8974 'alphanumMask' : /[a-z0-9_]/i
8984 * @class Roo.bootstrap.Input
8985 * @extends Roo.bootstrap.Component
8986 * Bootstrap Input class
8987 * @cfg {Boolean} disabled is it disabled
8988 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8989 * @cfg {String} name name of the input
8990 * @cfg {string} fieldLabel - the label associated
8991 * @cfg {string} placeholder - placeholder to put in text.
8992 * @cfg {string} before - input group add on before
8993 * @cfg {string} after - input group add on after
8994 * @cfg {string} size - (lg|sm) or leave empty..
8995 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8996 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8997 * @cfg {Number} md colspan out of 12 for computer-sized screens
8998 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8999 * @cfg {string} value default value of the input
9000 * @cfg {Number} labelWidth set the width of label
9001 * @cfg {Number} labellg set the width of label (1-12)
9002 * @cfg {Number} labelmd set the width of label (1-12)
9003 * @cfg {Number} labelsm set the width of label (1-12)
9004 * @cfg {Number} labelxs set the width of label (1-12)
9005 * @cfg {String} labelAlign (top|left)
9006 * @cfg {Boolean} readOnly Specifies that the field should be read-only
9007 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9008 * @cfg {String} indicatorpos (left|right) default left
9009 * @cfg {String} capture (user|camera) use for file input only. (default empty)
9010 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9012 * @cfg {String} align (left|center|right) Default left
9013 * @cfg {Boolean} forceFeedback (true|false) Default false
9016 * Create a new Input
9017 * @param {Object} config The config object
9020 Roo.bootstrap.Input = function(config){
9022 Roo.bootstrap.Input.superclass.constructor.call(this, config);
9027 * Fires when this field receives input focus.
9028 * @param {Roo.form.Field} this
9033 * Fires when this field loses input focus.
9034 * @param {Roo.form.Field} this
9039 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
9040 * {@link Roo.EventObject#getKey} to determine which key was pressed.
9041 * @param {Roo.form.Field} this
9042 * @param {Roo.EventObject} e The event object
9047 * Fires just before the field blurs if the field value has changed.
9048 * @param {Roo.form.Field} this
9049 * @param {Mixed} newValue The new value
9050 * @param {Mixed} oldValue The original value
9055 * Fires after the field has been marked as invalid.
9056 * @param {Roo.form.Field} this
9057 * @param {String} msg The validation message
9062 * Fires after the field has been validated with no errors.
9063 * @param {Roo.form.Field} this
9068 * Fires after the key up
9069 * @param {Roo.form.Field} this
9070 * @param {Roo.EventObject} e The event Object
9076 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
9078 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9079 automatic validation (defaults to "keyup").
9081 validationEvent : "keyup",
9083 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9085 validateOnBlur : true,
9087 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9089 validationDelay : 250,
9091 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9093 focusClass : "x-form-focus", // not needed???
9097 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9099 invalidClass : "has-warning",
9102 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9104 validClass : "has-success",
9107 * @cfg {Boolean} hasFeedback (true|false) default true
9112 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9114 invalidFeedbackClass : "glyphicon-warning-sign",
9117 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9119 validFeedbackClass : "glyphicon-ok",
9122 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9124 selectOnFocus : false,
9127 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9131 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9136 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9138 disableKeyFilter : false,
9141 * @cfg {Boolean} disabled True to disable the field (defaults to false).
9145 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9149 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9151 blankText : "Please complete this mandatory field",
9154 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9158 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9160 maxLength : Number.MAX_VALUE,
9162 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9164 minLengthText : "The minimum length for this field is {0}",
9166 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9168 maxLengthText : "The maximum length for this field is {0}",
9172 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9173 * If available, this function will be called only after the basic validators all return true, and will be passed the
9174 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9178 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9179 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9180 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
9184 * @cfg {String} regexText -- Depricated - use Invalid Text
9189 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9195 autocomplete: false,
9214 formatedValue : false,
9215 forceFeedback : false,
9217 indicatorpos : 'left',
9227 parentLabelAlign : function()
9230 while (parent.parent()) {
9231 parent = parent.parent();
9232 if (typeof(parent.labelAlign) !='undefined') {
9233 return parent.labelAlign;
9240 getAutoCreate : function()
9242 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9248 if(this.inputType != 'hidden'){
9249 cfg.cls = 'form-group' //input-group
9255 type : this.inputType,
9257 cls : 'form-control',
9258 placeholder : this.placeholder || '',
9259 autocomplete : this.autocomplete || 'new-password'
9262 if(this.capture.length){
9263 input.capture = this.capture;
9266 if(this.accept.length){
9267 input.accept = this.accept + "/*";
9271 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9274 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9275 input.maxLength = this.maxLength;
9278 if (this.disabled) {
9279 input.disabled=true;
9282 if (this.readOnly) {
9283 input.readonly=true;
9287 input.name = this.name;
9291 input.cls += ' input-' + this.size;
9295 ['xs','sm','md','lg'].map(function(size){
9296 if (settings[size]) {
9297 cfg.cls += ' col-' + size + '-' + settings[size];
9301 var inputblock = input;
9305 cls: 'glyphicon form-control-feedback'
9308 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9311 cls : 'has-feedback',
9319 if (this.before || this.after) {
9322 cls : 'input-group',
9326 if (this.before && typeof(this.before) == 'string') {
9328 inputblock.cn.push({
9330 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9334 if (this.before && typeof(this.before) == 'object') {
9335 this.before = Roo.factory(this.before);
9337 inputblock.cn.push({
9339 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9340 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9344 inputblock.cn.push(input);
9346 if (this.after && typeof(this.after) == 'string') {
9347 inputblock.cn.push({
9349 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9353 if (this.after && typeof(this.after) == 'object') {
9354 this.after = Roo.factory(this.after);
9356 inputblock.cn.push({
9358 cls : 'roo-input-after input-group-append input-group-text input-group-' +
9359 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9363 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9364 inputblock.cls += ' has-feedback';
9365 inputblock.cn.push(feedback);
9370 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9371 tooltip : 'This field is required'
9373 if (Roo.bootstrap.version == 4) {
9376 style : 'display-none'
9379 if (align ==='left' && this.fieldLabel.length) {
9381 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
9388 cls : 'control-label col-form-label',
9389 html : this.fieldLabel
9400 var labelCfg = cfg.cn[1];
9401 var contentCfg = cfg.cn[2];
9403 if(this.indicatorpos == 'right'){
9408 cls : 'control-label col-form-label',
9412 html : this.fieldLabel
9426 labelCfg = cfg.cn[0];
9427 contentCfg = cfg.cn[1];
9431 if(this.labelWidth > 12){
9432 labelCfg.style = "width: " + this.labelWidth + 'px';
9435 if(this.labelWidth < 13 && this.labelmd == 0){
9436 this.labelmd = this.labelWidth;
9439 if(this.labellg > 0){
9440 labelCfg.cls += ' col-lg-' + this.labellg;
9441 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9444 if(this.labelmd > 0){
9445 labelCfg.cls += ' col-md-' + this.labelmd;
9446 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9449 if(this.labelsm > 0){
9450 labelCfg.cls += ' col-sm-' + this.labelsm;
9451 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9454 if(this.labelxs > 0){
9455 labelCfg.cls += ' col-xs-' + this.labelxs;
9456 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9460 } else if ( this.fieldLabel.length) {
9465 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9466 tooltip : 'This field is required'
9470 //cls : 'input-group-addon',
9471 html : this.fieldLabel
9479 if(this.indicatorpos == 'right'){
9484 //cls : 'input-group-addon',
9485 html : this.fieldLabel
9490 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9491 tooltip : 'This field is required'
9511 if (this.parentType === 'Navbar' && this.parent().bar) {
9512 cfg.cls += ' navbar-form';
9515 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9516 // on BS4 we do this only if not form
9517 cfg.cls += ' navbar-form';
9525 * return the real input element.
9527 inputEl: function ()
9529 return this.el.select('input.form-control',true).first();
9532 tooltipEl : function()
9534 return this.inputEl();
9537 indicatorEl : function()
9539 if (Roo.bootstrap.version == 4) {
9540 return false; // not enabled in v4 yet.
9543 var indicator = this.el.select('i.roo-required-indicator',true).first();
9553 setDisabled : function(v)
9555 var i = this.inputEl().dom;
9557 i.removeAttribute('disabled');
9561 i.setAttribute('disabled','true');
9563 initEvents : function()
9566 this.inputEl().on("keydown" , this.fireKey, this);
9567 this.inputEl().on("focus", this.onFocus, this);
9568 this.inputEl().on("blur", this.onBlur, this);
9570 this.inputEl().relayEvent('keyup', this);
9572 this.indicator = this.indicatorEl();
9575 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9578 // reference to original value for reset
9579 this.originalValue = this.getValue();
9580 //Roo.form.TextField.superclass.initEvents.call(this);
9581 if(this.validationEvent == 'keyup'){
9582 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9583 this.inputEl().on('keyup', this.filterValidation, this);
9585 else if(this.validationEvent !== false){
9586 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9589 if(this.selectOnFocus){
9590 this.on("focus", this.preFocus, this);
9593 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9594 this.inputEl().on("keypress", this.filterKeys, this);
9596 this.inputEl().relayEvent('keypress', this);
9599 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9600 this.el.on("click", this.autoSize, this);
9603 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9604 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9607 if (typeof(this.before) == 'object') {
9608 this.before.render(this.el.select('.roo-input-before',true).first());
9610 if (typeof(this.after) == 'object') {
9611 this.after.render(this.el.select('.roo-input-after',true).first());
9614 this.inputEl().on('change', this.onChange, this);
9617 filterValidation : function(e){
9618 if(!e.isNavKeyPress()){
9619 this.validationTask.delay(this.validationDelay);
9623 * Validates the field value
9624 * @return {Boolean} True if the value is valid, else false
9626 validate : function(){
9627 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9628 if(this.disabled || this.validateValue(this.getRawValue())){
9639 * Validates a value according to the field's validation rules and marks the field as invalid
9640 * if the validation fails
9641 * @param {Mixed} value The value to validate
9642 * @return {Boolean} True if the value is valid, else false
9644 validateValue : function(value)
9646 if(this.getVisibilityEl().hasClass('hidden')){
9650 if(value.length < 1) { // if it's blank
9651 if(this.allowBlank){
9657 if(value.length < this.minLength){
9660 if(value.length > this.maxLength){
9664 var vt = Roo.form.VTypes;
9665 if(!vt[this.vtype](value, this)){
9669 if(typeof this.validator == "function"){
9670 var msg = this.validator(value);
9674 if (typeof(msg) == 'string') {
9675 this.invalidText = msg;
9679 if(this.regex && !this.regex.test(value)){
9687 fireKey : function(e){
9688 //Roo.log('field ' + e.getKey());
9689 if(e.isNavKeyPress()){
9690 this.fireEvent("specialkey", this, e);
9693 focus : function (selectText){
9695 this.inputEl().focus();
9696 if(selectText === true){
9697 this.inputEl().dom.select();
9703 onFocus : function(){
9704 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9705 // this.el.addClass(this.focusClass);
9708 this.hasFocus = true;
9709 this.startValue = this.getValue();
9710 this.fireEvent("focus", this);
9714 beforeBlur : Roo.emptyFn,
9718 onBlur : function(){
9720 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9721 //this.el.removeClass(this.focusClass);
9723 this.hasFocus = false;
9724 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9727 var v = this.getValue();
9728 if(String(v) !== String(this.startValue)){
9729 this.fireEvent('change', this, v, this.startValue);
9731 this.fireEvent("blur", this);
9734 onChange : function(e)
9736 var v = this.getValue();
9737 if(String(v) !== String(this.startValue)){
9738 this.fireEvent('change', this, v, this.startValue);
9744 * Resets the current field value to the originally loaded value and clears any validation messages
9747 this.setValue(this.originalValue);
9751 * Returns the name of the field
9752 * @return {Mixed} name The name field
9754 getName: function(){
9758 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9759 * @return {Mixed} value The field value
9761 getValue : function(){
9763 var v = this.inputEl().getValue();
9768 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9769 * @return {Mixed} value The field value
9771 getRawValue : function(){
9772 var v = this.inputEl().getValue();
9778 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9779 * @param {Mixed} value The value to set
9781 setRawValue : function(v){
9782 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9785 selectText : function(start, end){
9786 var v = this.getRawValue();
9788 start = start === undefined ? 0 : start;
9789 end = end === undefined ? v.length : end;
9790 var d = this.inputEl().dom;
9791 if(d.setSelectionRange){
9792 d.setSelectionRange(start, end);
9793 }else if(d.createTextRange){
9794 var range = d.createTextRange();
9795 range.moveStart("character", start);
9796 range.moveEnd("character", v.length-end);
9803 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9804 * @param {Mixed} value The value to set
9806 setValue : function(v){
9809 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9815 processValue : function(value){
9816 if(this.stripCharsRe){
9817 var newValue = value.replace(this.stripCharsRe, '');
9818 if(newValue !== value){
9819 this.setRawValue(newValue);
9826 preFocus : function(){
9828 if(this.selectOnFocus){
9829 this.inputEl().dom.select();
9832 filterKeys : function(e){
9834 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9837 var c = e.getCharCode(), cc = String.fromCharCode(c);
9838 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9841 if(!this.maskRe.test(cc)){
9846 * Clear any invalid styles/messages for this field
9848 clearInvalid : function(){
9850 if(!this.el || this.preventMark){ // not rendered
9855 this.el.removeClass([this.invalidClass, 'is-invalid']);
9857 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9859 var feedback = this.el.select('.form-control-feedback', true).first();
9862 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9868 this.indicator.removeClass('visible');
9869 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9872 this.fireEvent('valid', this);
9876 * Mark this field as valid
9878 markValid : function()
9880 if(!this.el || this.preventMark){ // not rendered...
9884 this.el.removeClass([this.invalidClass, this.validClass]);
9885 this.inputEl().removeClass(['is-valid', 'is-invalid']);
9887 var feedback = this.el.select('.form-control-feedback', true).first();
9890 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9894 this.indicator.removeClass('visible');
9895 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9902 if(this.allowBlank && !this.getRawValue().length){
9905 if (Roo.bootstrap.version == 3) {
9906 this.el.addClass(this.validClass);
9908 this.inputEl().addClass('is-valid');
9911 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9913 var feedback = this.el.select('.form-control-feedback', true).first();
9916 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9917 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9922 this.fireEvent('valid', this);
9926 * Mark this field as invalid
9927 * @param {String} msg The validation message
9929 markInvalid : function(msg)
9931 if(!this.el || this.preventMark){ // not rendered
9935 this.el.removeClass([this.invalidClass, this.validClass]);
9936 this.inputEl().removeClass(['is-valid', 'is-invalid']);
9938 var feedback = this.el.select('.form-control-feedback', true).first();
9941 this.el.select('.form-control-feedback', true).first().removeClass(
9942 [this.invalidFeedbackClass, this.validFeedbackClass]);
9949 if(this.allowBlank && !this.getRawValue().length){
9954 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9955 this.indicator.addClass('visible');
9957 if (Roo.bootstrap.version == 3) {
9958 this.el.addClass(this.invalidClass);
9960 this.inputEl().addClass('is-invalid');
9965 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9967 var feedback = this.el.select('.form-control-feedback', true).first();
9970 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9972 if(this.getValue().length || this.forceFeedback){
9973 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9980 this.fireEvent('invalid', this, msg);
9983 SafariOnKeyDown : function(event)
9985 // this is a workaround for a password hang bug on chrome/ webkit.
9986 if (this.inputEl().dom.type != 'password') {
9990 var isSelectAll = false;
9992 if(this.inputEl().dom.selectionEnd > 0){
9993 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9995 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9996 event.preventDefault();
10001 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10003 event.preventDefault();
10004 // this is very hacky as keydown always get's upper case.
10006 var cc = String.fromCharCode(event.getCharCode());
10007 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
10011 adjustWidth : function(tag, w){
10012 tag = tag.toLowerCase();
10013 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10014 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10015 if(tag == 'input'){
10018 if(tag == 'textarea'){
10021 }else if(Roo.isOpera){
10022 if(tag == 'input'){
10025 if(tag == 'textarea'){
10033 setFieldLabel : function(v)
10035 if(!this.rendered){
10039 if(this.indicatorEl()){
10040 var ar = this.el.select('label > span',true);
10042 if (ar.elements.length) {
10043 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10044 this.fieldLabel = v;
10048 var br = this.el.select('label',true);
10050 if(br.elements.length) {
10051 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10052 this.fieldLabel = v;
10056 Roo.log('Cannot Found any of label > span || label in input');
10060 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10061 this.fieldLabel = v;
10076 * @class Roo.bootstrap.TextArea
10077 * @extends Roo.bootstrap.Input
10078 * Bootstrap TextArea class
10079 * @cfg {Number} cols Specifies the visible width of a text area
10080 * @cfg {Number} rows Specifies the visible number of lines in a text area
10081 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10082 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10083 * @cfg {string} html text
10086 * Create a new TextArea
10087 * @param {Object} config The config object
10090 Roo.bootstrap.TextArea = function(config){
10091 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10095 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
10105 getAutoCreate : function(){
10107 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10113 if(this.inputType != 'hidden'){
10114 cfg.cls = 'form-group' //input-group
10122 value : this.value || '',
10123 html: this.html || '',
10124 cls : 'form-control',
10125 placeholder : this.placeholder || ''
10129 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10130 input.maxLength = this.maxLength;
10134 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10138 input.cols = this.cols;
10141 if (this.readOnly) {
10142 input.readonly = true;
10146 input.name = this.name;
10150 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10154 ['xs','sm','md','lg'].map(function(size){
10155 if (settings[size]) {
10156 cfg.cls += ' col-' + size + '-' + settings[size];
10160 var inputblock = input;
10162 if(this.hasFeedback && !this.allowBlank){
10166 cls: 'glyphicon form-control-feedback'
10170 cls : 'has-feedback',
10179 if (this.before || this.after) {
10182 cls : 'input-group',
10186 inputblock.cn.push({
10188 cls : 'input-group-addon',
10193 inputblock.cn.push(input);
10195 if(this.hasFeedback && !this.allowBlank){
10196 inputblock.cls += ' has-feedback';
10197 inputblock.cn.push(feedback);
10201 inputblock.cn.push({
10203 cls : 'input-group-addon',
10210 if (align ==='left' && this.fieldLabel.length) {
10215 cls : 'control-label',
10216 html : this.fieldLabel
10227 if(this.labelWidth > 12){
10228 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10231 if(this.labelWidth < 13 && this.labelmd == 0){
10232 this.labelmd = this.labelWidth;
10235 if(this.labellg > 0){
10236 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10237 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10240 if(this.labelmd > 0){
10241 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10242 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10245 if(this.labelsm > 0){
10246 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10247 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10250 if(this.labelxs > 0){
10251 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10252 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10255 } else if ( this.fieldLabel.length) {
10260 //cls : 'input-group-addon',
10261 html : this.fieldLabel
10279 if (this.disabled) {
10280 input.disabled=true;
10287 * return the real textarea element.
10289 inputEl: function ()
10291 return this.el.select('textarea.form-control',true).first();
10295 * Clear any invalid styles/messages for this field
10297 clearInvalid : function()
10300 if(!this.el || this.preventMark){ // not rendered
10304 var label = this.el.select('label', true).first();
10305 var icon = this.el.select('i.fa-star', true).first();
10310 this.el.removeClass( this.validClass);
10311 this.inputEl().removeClass('is-invalid');
10313 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10315 var feedback = this.el.select('.form-control-feedback', true).first();
10318 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10323 this.fireEvent('valid', this);
10327 * Mark this field as valid
10329 markValid : function()
10331 if(!this.el || this.preventMark){ // not rendered
10335 this.el.removeClass([this.invalidClass, this.validClass]);
10336 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10338 var feedback = this.el.select('.form-control-feedback', true).first();
10341 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10344 if(this.disabled || this.allowBlank){
10348 var label = this.el.select('label', true).first();
10349 var icon = this.el.select('i.fa-star', true).first();
10354 if (Roo.bootstrap.version == 3) {
10355 this.el.addClass(this.validClass);
10357 this.inputEl().addClass('is-valid');
10361 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10363 var feedback = this.el.select('.form-control-feedback', true).first();
10366 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10367 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10372 this.fireEvent('valid', this);
10376 * Mark this field as invalid
10377 * @param {String} msg The validation message
10379 markInvalid : function(msg)
10381 if(!this.el || this.preventMark){ // not rendered
10385 this.el.removeClass([this.invalidClass, this.validClass]);
10386 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10388 var feedback = this.el.select('.form-control-feedback', true).first();
10391 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10394 if(this.disabled || this.allowBlank){
10398 var label = this.el.select('label', true).first();
10399 var icon = this.el.select('i.fa-star', true).first();
10401 if(!this.getValue().length && label && !icon){
10402 this.el.createChild({
10404 cls : 'text-danger fa fa-lg fa-star',
10405 tooltip : 'This field is required',
10406 style : 'margin-right:5px;'
10410 if (Roo.bootstrap.version == 3) {
10411 this.el.addClass(this.invalidClass);
10413 this.inputEl().addClass('is-invalid');
10416 // fixme ... this may be depricated need to test..
10417 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10419 var feedback = this.el.select('.form-control-feedback', true).first();
10422 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10424 if(this.getValue().length || this.forceFeedback){
10425 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10432 this.fireEvent('invalid', this, msg);
10440 * trigger field - base class for combo..
10445 * @class Roo.bootstrap.TriggerField
10446 * @extends Roo.bootstrap.Input
10447 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10448 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10449 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10450 * for which you can provide a custom implementation. For example:
10452 var trigger = new Roo.bootstrap.TriggerField();
10453 trigger.onTriggerClick = myTriggerFn;
10454 trigger.applyTo('my-field');
10457 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10458 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10459 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10460 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10461 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
10464 * Create a new TriggerField.
10465 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10466 * to the base TextField)
10468 Roo.bootstrap.TriggerField = function(config){
10469 this.mimicing = false;
10470 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10473 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10475 * @cfg {String} triggerClass A CSS class to apply to the trigger
10478 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10483 * @cfg {Boolean} removable (true|false) special filter default false
10487 /** @cfg {Boolean} grow @hide */
10488 /** @cfg {Number} growMin @hide */
10489 /** @cfg {Number} growMax @hide */
10495 autoSize: Roo.emptyFn,
10499 deferHeight : true,
10502 actionMode : 'wrap',
10507 getAutoCreate : function(){
10509 var align = this.labelAlign || this.parentLabelAlign();
10514 cls: 'form-group' //input-group
10521 type : this.inputType,
10522 cls : 'form-control',
10523 autocomplete: 'new-password',
10524 placeholder : this.placeholder || ''
10528 input.name = this.name;
10531 input.cls += ' input-' + this.size;
10534 if (this.disabled) {
10535 input.disabled=true;
10538 var inputblock = input;
10540 if(this.hasFeedback && !this.allowBlank){
10544 cls: 'glyphicon form-control-feedback'
10547 if(this.removable && !this.editable && !this.tickable){
10549 cls : 'has-feedback',
10555 cls : 'roo-combo-removable-btn close'
10562 cls : 'has-feedback',
10571 if(this.removable && !this.editable && !this.tickable){
10573 cls : 'roo-removable',
10579 cls : 'roo-combo-removable-btn close'
10586 if (this.before || this.after) {
10589 cls : 'input-group',
10593 inputblock.cn.push({
10595 cls : 'input-group-addon input-group-prepend input-group-text',
10600 inputblock.cn.push(input);
10602 if(this.hasFeedback && !this.allowBlank){
10603 inputblock.cls += ' has-feedback';
10604 inputblock.cn.push(feedback);
10608 inputblock.cn.push({
10610 cls : 'input-group-addon input-group-append input-group-text',
10619 var ibwrap = inputblock;
10624 cls: 'roo-select2-choices',
10628 cls: 'roo-select2-search-field',
10640 cls: 'roo-select2-container input-group',
10645 cls: 'form-hidden-field'
10651 if(!this.multiple && this.showToggleBtn){
10657 if (this.caret != false) {
10660 cls: 'fa fa-' + this.caret
10667 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10669 Roo.bootstrap.version == 3 ? caret : '',
10672 cls: 'combobox-clear',
10686 combobox.cls += ' roo-select2-container-multi';
10690 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10691 tooltip : 'This field is required'
10693 if (Roo.bootstrap.version == 4) {
10696 style : 'display:none'
10701 if (align ==='left' && this.fieldLabel.length) {
10703 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10710 cls : 'control-label',
10711 html : this.fieldLabel
10723 var labelCfg = cfg.cn[1];
10724 var contentCfg = cfg.cn[2];
10726 if(this.indicatorpos == 'right'){
10731 cls : 'control-label',
10735 html : this.fieldLabel
10749 labelCfg = cfg.cn[0];
10750 contentCfg = cfg.cn[1];
10753 if(this.labelWidth > 12){
10754 labelCfg.style = "width: " + this.labelWidth + 'px';
10757 if(this.labelWidth < 13 && this.labelmd == 0){
10758 this.labelmd = this.labelWidth;
10761 if(this.labellg > 0){
10762 labelCfg.cls += ' col-lg-' + this.labellg;
10763 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10766 if(this.labelmd > 0){
10767 labelCfg.cls += ' col-md-' + this.labelmd;
10768 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10771 if(this.labelsm > 0){
10772 labelCfg.cls += ' col-sm-' + this.labelsm;
10773 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10776 if(this.labelxs > 0){
10777 labelCfg.cls += ' col-xs-' + this.labelxs;
10778 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10781 } else if ( this.fieldLabel.length) {
10782 // Roo.log(" label");
10787 //cls : 'input-group-addon',
10788 html : this.fieldLabel
10796 if(this.indicatorpos == 'right'){
10804 html : this.fieldLabel
10818 // Roo.log(" no label && no align");
10825 ['xs','sm','md','lg'].map(function(size){
10826 if (settings[size]) {
10827 cfg.cls += ' col-' + size + '-' + settings[size];
10838 onResize : function(w, h){
10839 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10840 // if(typeof w == 'number'){
10841 // var x = w - this.trigger.getWidth();
10842 // this.inputEl().setWidth(this.adjustWidth('input', x));
10843 // this.trigger.setStyle('left', x+'px');
10848 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10851 getResizeEl : function(){
10852 return this.inputEl();
10856 getPositionEl : function(){
10857 return this.inputEl();
10861 alignErrorIcon : function(){
10862 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10866 initEvents : function(){
10870 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10871 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10872 if(!this.multiple && this.showToggleBtn){
10873 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10874 if(this.hideTrigger){
10875 this.trigger.setDisplayed(false);
10877 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10881 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10884 if(this.removable && !this.editable && !this.tickable){
10885 var close = this.closeTriggerEl();
10888 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10889 close.on('click', this.removeBtnClick, this, close);
10893 //this.trigger.addClassOnOver('x-form-trigger-over');
10894 //this.trigger.addClassOnClick('x-form-trigger-click');
10897 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10901 closeTriggerEl : function()
10903 var close = this.el.select('.roo-combo-removable-btn', true).first();
10904 return close ? close : false;
10907 removeBtnClick : function(e, h, el)
10909 e.preventDefault();
10911 if(this.fireEvent("remove", this) !== false){
10913 this.fireEvent("afterremove", this)
10917 createList : function()
10919 this.list = Roo.get(document.body).createChild({
10920 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10921 cls: 'typeahead typeahead-long dropdown-menu',
10922 style: 'display:none'
10925 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10930 initTrigger : function(){
10935 onDestroy : function(){
10937 this.trigger.removeAllListeners();
10938 // this.trigger.remove();
10941 // this.wrap.remove();
10943 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10947 onFocus : function(){
10948 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10950 if(!this.mimicing){
10951 this.wrap.addClass('x-trigger-wrap-focus');
10952 this.mimicing = true;
10953 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10954 if(this.monitorTab){
10955 this.el.on("keydown", this.checkTab, this);
10962 checkTab : function(e){
10963 if(e.getKey() == e.TAB){
10964 this.triggerBlur();
10969 onBlur : function(){
10974 mimicBlur : function(e, t){
10976 if(!this.wrap.contains(t) && this.validateBlur()){
10977 this.triggerBlur();
10983 triggerBlur : function(){
10984 this.mimicing = false;
10985 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10986 if(this.monitorTab){
10987 this.el.un("keydown", this.checkTab, this);
10989 //this.wrap.removeClass('x-trigger-wrap-focus');
10990 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10994 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10995 validateBlur : function(e, t){
11000 onDisable : function(){
11001 this.inputEl().dom.disabled = true;
11002 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11004 // this.wrap.addClass('x-item-disabled');
11009 onEnable : function(){
11010 this.inputEl().dom.disabled = false;
11011 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11013 // this.el.removeClass('x-item-disabled');
11018 onShow : function(){
11019 var ae = this.getActionEl();
11022 ae.dom.style.display = '';
11023 ae.dom.style.visibility = 'visible';
11029 onHide : function(){
11030 var ae = this.getActionEl();
11031 ae.dom.style.display = 'none';
11035 * The function that should handle the trigger's click event. This method does nothing by default until overridden
11036 * by an implementing function.
11038 * @param {EventObject} e
11040 onTriggerClick : Roo.emptyFn
11044 * Ext JS Library 1.1.1
11045 * Copyright(c) 2006-2007, Ext JS, LLC.
11047 * Originally Released Under LGPL - original licence link has changed is not relivant.
11050 * <script type="text/javascript">
11055 * @class Roo.data.SortTypes
11057 * Defines the default sorting (casting?) comparison functions used when sorting data.
11059 Roo.data.SortTypes = {
11061 * Default sort that does nothing
11062 * @param {Mixed} s The value being converted
11063 * @return {Mixed} The comparison value
11065 none : function(s){
11070 * The regular expression used to strip tags
11074 stripTagsRE : /<\/?[^>]+>/gi,
11077 * Strips all HTML tags to sort on text only
11078 * @param {Mixed} s The value being converted
11079 * @return {String} The comparison value
11081 asText : function(s){
11082 return String(s).replace(this.stripTagsRE, "");
11086 * Strips all HTML tags to sort on text only - Case insensitive
11087 * @param {Mixed} s The value being converted
11088 * @return {String} The comparison value
11090 asUCText : function(s){
11091 return String(s).toUpperCase().replace(this.stripTagsRE, "");
11095 * Case insensitive string
11096 * @param {Mixed} s The value being converted
11097 * @return {String} The comparison value
11099 asUCString : function(s) {
11100 return String(s).toUpperCase();
11105 * @param {Mixed} s The value being converted
11106 * @return {Number} The comparison value
11108 asDate : function(s) {
11112 if(s instanceof Date){
11113 return s.getTime();
11115 return Date.parse(String(s));
11120 * @param {Mixed} s The value being converted
11121 * @return {Float} The comparison value
11123 asFloat : function(s) {
11124 var val = parseFloat(String(s).replace(/,/g, ""));
11133 * @param {Mixed} s The value being converted
11134 * @return {Number} The comparison value
11136 asInt : function(s) {
11137 var val = parseInt(String(s).replace(/,/g, ""));
11145 * Ext JS Library 1.1.1
11146 * Copyright(c) 2006-2007, Ext JS, LLC.
11148 * Originally Released Under LGPL - original licence link has changed is not relivant.
11151 * <script type="text/javascript">
11155 * @class Roo.data.Record
11156 * Instances of this class encapsulate both record <em>definition</em> information, and record
11157 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11158 * to access Records cached in an {@link Roo.data.Store} object.<br>
11160 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11161 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11164 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11166 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11167 * {@link #create}. The parameters are the same.
11168 * @param {Array} data An associative Array of data values keyed by the field name.
11169 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11170 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11171 * not specified an integer id is generated.
11173 Roo.data.Record = function(data, id){
11174 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11179 * Generate a constructor for a specific record layout.
11180 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11181 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11182 * Each field definition object may contain the following properties: <ul>
11183 * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
11184 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11185 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11186 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11187 * is being used, then this is a string containing the javascript expression to reference the data relative to
11188 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11189 * to the data item relative to the record element. If the mapping expression is the same as the field name,
11190 * this may be omitted.</p></li>
11191 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11192 * <ul><li>auto (Default, implies no conversion)</li>
11197 * <li>date</li></ul></p></li>
11198 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11199 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11200 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11201 * by the Reader into an object that will be stored in the Record. It is passed the
11202 * following parameters:<ul>
11203 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11205 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11207 * <br>usage:<br><pre><code>
11208 var TopicRecord = Roo.data.Record.create(
11209 {name: 'title', mapping: 'topic_title'},
11210 {name: 'author', mapping: 'username'},
11211 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11212 {name: 'lastPost', mapping: 'post_time', type: 'date'},
11213 {name: 'lastPoster', mapping: 'user2'},
11214 {name: 'excerpt', mapping: 'post_text'}
11217 var myNewRecord = new TopicRecord({
11218 title: 'Do my job please',
11221 lastPost: new Date(),
11222 lastPoster: 'Animal',
11223 excerpt: 'No way dude!'
11225 myStore.add(myNewRecord);
11230 Roo.data.Record.create = function(o){
11231 var f = function(){
11232 f.superclass.constructor.apply(this, arguments);
11234 Roo.extend(f, Roo.data.Record);
11235 var p = f.prototype;
11236 p.fields = new Roo.util.MixedCollection(false, function(field){
11239 for(var i = 0, len = o.length; i < len; i++){
11240 p.fields.add(new Roo.data.Field(o[i]));
11242 f.getField = function(name){
11243 return p.fields.get(name);
11248 Roo.data.Record.AUTO_ID = 1000;
11249 Roo.data.Record.EDIT = 'edit';
11250 Roo.data.Record.REJECT = 'reject';
11251 Roo.data.Record.COMMIT = 'commit';
11253 Roo.data.Record.prototype = {
11255 * Readonly flag - true if this record has been modified.
11264 join : function(store){
11265 this.store = store;
11269 * Set the named field to the specified value.
11270 * @param {String} name The name of the field to set.
11271 * @param {Object} value The value to set the field to.
11273 set : function(name, value){
11274 if(this.data[name] == value){
11278 if(!this.modified){
11279 this.modified = {};
11281 if(typeof this.modified[name] == 'undefined'){
11282 this.modified[name] = this.data[name];
11284 this.data[name] = value;
11285 if(!this.editing && this.store){
11286 this.store.afterEdit(this);
11291 * Get the value of the named field.
11292 * @param {String} name The name of the field to get the value of.
11293 * @return {Object} The value of the field.
11295 get : function(name){
11296 return this.data[name];
11300 beginEdit : function(){
11301 this.editing = true;
11302 this.modified = {};
11306 cancelEdit : function(){
11307 this.editing = false;
11308 delete this.modified;
11312 endEdit : function(){
11313 this.editing = false;
11314 if(this.dirty && this.store){
11315 this.store.afterEdit(this);
11320 * Usually called by the {@link Roo.data.Store} which owns the Record.
11321 * Rejects all changes made to the Record since either creation, or the last commit operation.
11322 * Modified fields are reverted to their original values.
11324 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11325 * of reject operations.
11327 reject : function(){
11328 var m = this.modified;
11330 if(typeof m[n] != "function"){
11331 this.data[n] = m[n];
11334 this.dirty = false;
11335 delete this.modified;
11336 this.editing = false;
11338 this.store.afterReject(this);
11343 * Usually called by the {@link Roo.data.Store} which owns the Record.
11344 * Commits all changes made to the Record since either creation, or the last commit operation.
11346 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11347 * of commit operations.
11349 commit : function(){
11350 this.dirty = false;
11351 delete this.modified;
11352 this.editing = false;
11354 this.store.afterCommit(this);
11359 hasError : function(){
11360 return this.error != null;
11364 clearError : function(){
11369 * Creates a copy of this record.
11370 * @param {String} id (optional) A new record id if you don't want to use this record's id
11373 copy : function(newId) {
11374 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11378 * Ext JS Library 1.1.1
11379 * Copyright(c) 2006-2007, Ext JS, LLC.
11381 * Originally Released Under LGPL - original licence link has changed is not relivant.
11384 * <script type="text/javascript">
11390 * @class Roo.data.Store
11391 * @extends Roo.util.Observable
11392 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11393 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11395 * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
11396 * has no knowledge of the format of the data returned by the Proxy.<br>
11398 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11399 * instances from the data object. These records are cached and made available through accessor functions.
11401 * Creates a new Store.
11402 * @param {Object} config A config object containing the objects needed for the Store to access data,
11403 * and read the data into Records.
11405 Roo.data.Store = function(config){
11406 this.data = new Roo.util.MixedCollection(false);
11407 this.data.getKey = function(o){
11410 this.baseParams = {};
11412 this.paramNames = {
11417 "multisort" : "_multisort"
11420 if(config && config.data){
11421 this.inlineData = config.data;
11422 delete config.data;
11425 Roo.apply(this, config);
11427 if(this.reader){ // reader passed
11428 this.reader = Roo.factory(this.reader, Roo.data);
11429 this.reader.xmodule = this.xmodule || false;
11430 if(!this.recordType){
11431 this.recordType = this.reader.recordType;
11433 if(this.reader.onMetaChange){
11434 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11438 if(this.recordType){
11439 this.fields = this.recordType.prototype.fields;
11441 this.modified = [];
11445 * @event datachanged
11446 * Fires when the data cache has changed, and a widget which is using this Store
11447 * as a Record cache should refresh its view.
11448 * @param {Store} this
11450 datachanged : true,
11452 * @event metachange
11453 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11454 * @param {Store} this
11455 * @param {Object} meta The JSON metadata
11460 * Fires when Records have been added to the Store
11461 * @param {Store} this
11462 * @param {Roo.data.Record[]} records The array of Records added
11463 * @param {Number} index The index at which the record(s) were added
11468 * Fires when a Record has been removed from the Store
11469 * @param {Store} this
11470 * @param {Roo.data.Record} record The Record that was removed
11471 * @param {Number} index The index at which the record was removed
11476 * Fires when a Record has been updated
11477 * @param {Store} this
11478 * @param {Roo.data.Record} record The Record that was updated
11479 * @param {String} operation The update operation being performed. Value may be one of:
11481 Roo.data.Record.EDIT
11482 Roo.data.Record.REJECT
11483 Roo.data.Record.COMMIT
11489 * Fires when the data cache has been cleared.
11490 * @param {Store} this
11494 * @event beforeload
11495 * Fires before a request is made for a new data object. If the beforeload handler returns false
11496 * the load action will be canceled.
11497 * @param {Store} this
11498 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11502 * @event beforeloadadd
11503 * Fires after a new set of Records has been loaded.
11504 * @param {Store} this
11505 * @param {Roo.data.Record[]} records The Records that were loaded
11506 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11508 beforeloadadd : true,
11511 * Fires after a new set of Records has been loaded, before they are added to the store.
11512 * @param {Store} this
11513 * @param {Roo.data.Record[]} records The Records that were loaded
11514 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11515 * @params {Object} return from reader
11519 * @event loadexception
11520 * Fires if an exception occurs in the Proxy during loading.
11521 * Called with the signature of the Proxy's "loadexception" event.
11522 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11525 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11526 * @param {Object} load options
11527 * @param {Object} jsonData from your request (normally this contains the Exception)
11529 loadexception : true
11533 this.proxy = Roo.factory(this.proxy, Roo.data);
11534 this.proxy.xmodule = this.xmodule || false;
11535 this.relayEvents(this.proxy, ["loadexception"]);
11537 this.sortToggle = {};
11538 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11540 Roo.data.Store.superclass.constructor.call(this);
11542 if(this.inlineData){
11543 this.loadData(this.inlineData);
11544 delete this.inlineData;
11548 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11550 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11551 * without a remote query - used by combo/forms at present.
11555 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11558 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11561 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11562 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11565 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11566 * on any HTTP request
11569 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11572 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11576 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11577 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11579 remoteSort : false,
11582 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11583 * loaded or when a record is removed. (defaults to false).
11585 pruneModifiedRecords : false,
11588 lastOptions : null,
11591 * Add Records to the Store and fires the add event.
11592 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11594 add : function(records){
11595 records = [].concat(records);
11596 for(var i = 0, len = records.length; i < len; i++){
11597 records[i].join(this);
11599 var index = this.data.length;
11600 this.data.addAll(records);
11601 this.fireEvent("add", this, records, index);
11605 * Remove a Record from the Store and fires the remove event.
11606 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11608 remove : function(record){
11609 var index = this.data.indexOf(record);
11610 this.data.removeAt(index);
11612 if(this.pruneModifiedRecords){
11613 this.modified.remove(record);
11615 this.fireEvent("remove", this, record, index);
11619 * Remove all Records from the Store and fires the clear event.
11621 removeAll : function(){
11623 if(this.pruneModifiedRecords){
11624 this.modified = [];
11626 this.fireEvent("clear", this);
11630 * Inserts Records to the Store at the given index and fires the add event.
11631 * @param {Number} index The start index at which to insert the passed Records.
11632 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11634 insert : function(index, records){
11635 records = [].concat(records);
11636 for(var i = 0, len = records.length; i < len; i++){
11637 this.data.insert(index, records[i]);
11638 records[i].join(this);
11640 this.fireEvent("add", this, records, index);
11644 * Get the index within the cache of the passed Record.
11645 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11646 * @return {Number} The index of the passed Record. Returns -1 if not found.
11648 indexOf : function(record){
11649 return this.data.indexOf(record);
11653 * Get the index within the cache of the Record with the passed id.
11654 * @param {String} id The id of the Record to find.
11655 * @return {Number} The index of the Record. Returns -1 if not found.
11657 indexOfId : function(id){
11658 return this.data.indexOfKey(id);
11662 * Get the Record with the specified id.
11663 * @param {String} id The id of the Record to find.
11664 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11666 getById : function(id){
11667 return this.data.key(id);
11671 * Get the Record at the specified index.
11672 * @param {Number} index The index of the Record to find.
11673 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11675 getAt : function(index){
11676 return this.data.itemAt(index);
11680 * Returns a range of Records between specified indices.
11681 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11682 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11683 * @return {Roo.data.Record[]} An array of Records
11685 getRange : function(start, end){
11686 return this.data.getRange(start, end);
11690 storeOptions : function(o){
11691 o = Roo.apply({}, o);
11694 this.lastOptions = o;
11698 * Loads the Record cache from the configured Proxy using the configured Reader.
11700 * If using remote paging, then the first load call must specify the <em>start</em>
11701 * and <em>limit</em> properties in the options.params property to establish the initial
11702 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11704 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11705 * and this call will return before the new data has been loaded. Perform any post-processing
11706 * in a callback function, or in a "load" event handler.</strong>
11708 * @param {Object} options An object containing properties which control loading options:<ul>
11709 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11710 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11711 * passed the following arguments:<ul>
11712 * <li>r : Roo.data.Record[]</li>
11713 * <li>options: Options object from the load call</li>
11714 * <li>success: Boolean success indicator</li></ul></li>
11715 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11716 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11719 load : function(options){
11720 options = options || {};
11721 if(this.fireEvent("beforeload", this, options) !== false){
11722 this.storeOptions(options);
11723 var p = Roo.apply(options.params || {}, this.baseParams);
11724 // if meta was not loaded from remote source.. try requesting it.
11725 if (!this.reader.metaFromRemote) {
11726 p._requestMeta = 1;
11728 if(this.sortInfo && this.remoteSort){
11729 var pn = this.paramNames;
11730 p[pn["sort"]] = this.sortInfo.field;
11731 p[pn["dir"]] = this.sortInfo.direction;
11733 if (this.multiSort) {
11734 var pn = this.paramNames;
11735 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11738 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11743 * Reloads the Record cache from the configured Proxy using the configured Reader and
11744 * the options from the last load operation performed.
11745 * @param {Object} options (optional) An object containing properties which may override the options
11746 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11747 * the most recently used options are reused).
11749 reload : function(options){
11750 this.load(Roo.applyIf(options||{}, this.lastOptions));
11754 // Called as a callback by the Reader during a load operation.
11755 loadRecords : function(o, options, success){
11756 if(!o || success === false){
11757 if(success !== false){
11758 this.fireEvent("load", this, [], options, o);
11760 if(options.callback){
11761 options.callback.call(options.scope || this, [], options, false);
11765 // if data returned failure - throw an exception.
11766 if (o.success === false) {
11767 // show a message if no listener is registered.
11768 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11769 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11771 // loadmask wil be hooked into this..
11772 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11775 var r = o.records, t = o.totalRecords || r.length;
11777 this.fireEvent("beforeloadadd", this, r, options, o);
11779 if(!options || options.add !== true){
11780 if(this.pruneModifiedRecords){
11781 this.modified = [];
11783 for(var i = 0, len = r.length; i < len; i++){
11787 this.data = this.snapshot;
11788 delete this.snapshot;
11791 this.data.addAll(r);
11792 this.totalLength = t;
11794 this.fireEvent("datachanged", this);
11796 this.totalLength = Math.max(t, this.data.length+r.length);
11800 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11802 var e = new Roo.data.Record({});
11804 e.set(this.parent.displayField, this.parent.emptyTitle);
11805 e.set(this.parent.valueField, '');
11810 this.fireEvent("load", this, r, options, o);
11811 if(options.callback){
11812 options.callback.call(options.scope || this, r, options, true);
11818 * Loads data from a passed data block. A Reader which understands the format of the data
11819 * must have been configured in the constructor.
11820 * @param {Object} data The data block from which to read the Records. The format of the data expected
11821 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11822 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11824 loadData : function(o, append){
11825 var r = this.reader.readRecords(o);
11826 this.loadRecords(r, {add: append}, true);
11830 * using 'cn' the nested child reader read the child array into it's child stores.
11831 * @param {Object} rec The record with a 'children array
11833 loadDataFromChildren : function(rec)
11840 * Gets the number of cached records.
11842 * <em>If using paging, this may not be the total size of the dataset. If the data object
11843 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11844 * the data set size</em>
11846 getCount : function(){
11847 return this.data.length || 0;
11851 * Gets the total number of records in the dataset as returned by the server.
11853 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11854 * the dataset size</em>
11856 getTotalCount : function(){
11857 return this.totalLength || 0;
11861 * Returns the sort state of the Store as an object with two properties:
11863 field {String} The name of the field by which the Records are sorted
11864 direction {String} The sort order, "ASC" or "DESC"
11867 getSortState : function(){
11868 return this.sortInfo;
11872 applySort : function(){
11873 if(this.sortInfo && !this.remoteSort){
11874 var s = this.sortInfo, f = s.field;
11875 var st = this.fields.get(f).sortType;
11876 var fn = function(r1, r2){
11877 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11878 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11880 this.data.sort(s.direction, fn);
11881 if(this.snapshot && this.snapshot != this.data){
11882 this.snapshot.sort(s.direction, fn);
11888 * Sets the default sort column and order to be used by the next load operation.
11889 * @param {String} fieldName The name of the field to sort by.
11890 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11892 setDefaultSort : function(field, dir){
11893 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11897 * Sort the Records.
11898 * If remote sorting is used, the sort is performed on the server, and the cache is
11899 * reloaded. If local sorting is used, the cache is sorted internally.
11900 * @param {String} fieldName The name of the field to sort by.
11901 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11903 sort : function(fieldName, dir){
11904 var f = this.fields.get(fieldName);
11906 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11908 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11909 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11914 this.sortToggle[f.name] = dir;
11915 this.sortInfo = {field: f.name, direction: dir};
11916 if(!this.remoteSort){
11918 this.fireEvent("datachanged", this);
11920 this.load(this.lastOptions);
11925 * Calls the specified function for each of the Records in the cache.
11926 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11927 * Returning <em>false</em> aborts and exits the iteration.
11928 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11930 each : function(fn, scope){
11931 this.data.each(fn, scope);
11935 * Gets all records modified since the last commit. Modified records are persisted across load operations
11936 * (e.g., during paging).
11937 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11939 getModifiedRecords : function(){
11940 return this.modified;
11944 createFilterFn : function(property, value, anyMatch){
11945 if(!value.exec){ // not a regex
11946 value = String(value);
11947 if(value.length == 0){
11950 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11952 return function(r){
11953 return value.test(r.data[property]);
11958 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11959 * @param {String} property A field on your records
11960 * @param {Number} start The record index to start at (defaults to 0)
11961 * @param {Number} end The last record index to include (defaults to length - 1)
11962 * @return {Number} The sum
11964 sum : function(property, start, end){
11965 var rs = this.data.items, v = 0;
11966 start = start || 0;
11967 end = (end || end === 0) ? end : rs.length-1;
11969 for(var i = start; i <= end; i++){
11970 v += (rs[i].data[property] || 0);
11976 * Filter the records by a specified property.
11977 * @param {String} field A field on your records
11978 * @param {String/RegExp} value Either a string that the field
11979 * should start with or a RegExp to test against the field
11980 * @param {Boolean} anyMatch True to match any part not just the beginning
11982 filter : function(property, value, anyMatch){
11983 var fn = this.createFilterFn(property, value, anyMatch);
11984 return fn ? this.filterBy(fn) : this.clearFilter();
11988 * Filter by a function. The specified function will be called with each
11989 * record in this data source. If the function returns true the record is included,
11990 * otherwise it is filtered.
11991 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11992 * @param {Object} scope (optional) The scope of the function (defaults to this)
11994 filterBy : function(fn, scope){
11995 this.snapshot = this.snapshot || this.data;
11996 this.data = this.queryBy(fn, scope||this);
11997 this.fireEvent("datachanged", this);
12001 * Query the records by a specified property.
12002 * @param {String} field A field on your records
12003 * @param {String/RegExp} value Either a string that the field
12004 * should start with or a RegExp to test against the field
12005 * @param {Boolean} anyMatch True to match any part not just the beginning
12006 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12008 query : function(property, value, anyMatch){
12009 var fn = this.createFilterFn(property, value, anyMatch);
12010 return fn ? this.queryBy(fn) : this.data.clone();
12014 * Query by a function. The specified function will be called with each
12015 * record in this data source. If the function returns true the record is included
12017 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12018 * @param {Object} scope (optional) The scope of the function (defaults to this)
12019 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12021 queryBy : function(fn, scope){
12022 var data = this.snapshot || this.data;
12023 return data.filterBy(fn, scope||this);
12027 * Collects unique values for a particular dataIndex from this store.
12028 * @param {String} dataIndex The property to collect
12029 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12030 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12031 * @return {Array} An array of the unique values
12033 collect : function(dataIndex, allowNull, bypassFilter){
12034 var d = (bypassFilter === true && this.snapshot) ?
12035 this.snapshot.items : this.data.items;
12036 var v, sv, r = [], l = {};
12037 for(var i = 0, len = d.length; i < len; i++){
12038 v = d[i].data[dataIndex];
12040 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12049 * Revert to a view of the Record cache with no filtering applied.
12050 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12052 clearFilter : function(suppressEvent){
12053 if(this.snapshot && this.snapshot != this.data){
12054 this.data = this.snapshot;
12055 delete this.snapshot;
12056 if(suppressEvent !== true){
12057 this.fireEvent("datachanged", this);
12063 afterEdit : function(record){
12064 if(this.modified.indexOf(record) == -1){
12065 this.modified.push(record);
12067 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12071 afterReject : function(record){
12072 this.modified.remove(record);
12073 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12077 afterCommit : function(record){
12078 this.modified.remove(record);
12079 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12083 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12084 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12086 commitChanges : function(){
12087 var m = this.modified.slice(0);
12088 this.modified = [];
12089 for(var i = 0, len = m.length; i < len; i++){
12095 * Cancel outstanding changes on all changed records.
12097 rejectChanges : function(){
12098 var m = this.modified.slice(0);
12099 this.modified = [];
12100 for(var i = 0, len = m.length; i < len; i++){
12105 onMetaChange : function(meta, rtype, o){
12106 this.recordType = rtype;
12107 this.fields = rtype.prototype.fields;
12108 delete this.snapshot;
12109 this.sortInfo = meta.sortInfo || this.sortInfo;
12110 this.modified = [];
12111 this.fireEvent('metachange', this, this.reader.meta);
12114 moveIndex : function(data, type)
12116 var index = this.indexOf(data);
12118 var newIndex = index + type;
12122 this.insert(newIndex, data);
12127 * Ext JS Library 1.1.1
12128 * Copyright(c) 2006-2007, Ext JS, LLC.
12130 * Originally Released Under LGPL - original licence link has changed is not relivant.
12133 * <script type="text/javascript">
12137 * @class Roo.data.SimpleStore
12138 * @extends Roo.data.Store
12139 * Small helper class to make creating Stores from Array data easier.
12140 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12141 * @cfg {Array} fields An array of field definition objects, or field name strings.
12142 * @cfg {Object} an existing reader (eg. copied from another store)
12143 * @cfg {Array} data The multi-dimensional array of data
12145 * @param {Object} config
12147 Roo.data.SimpleStore = function(config)
12149 Roo.data.SimpleStore.superclass.constructor.call(this, {
12151 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
12154 Roo.data.Record.create(config.fields)
12156 proxy : new Roo.data.MemoryProxy(config.data)
12160 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12162 * Ext JS Library 1.1.1
12163 * Copyright(c) 2006-2007, Ext JS, LLC.
12165 * Originally Released Under LGPL - original licence link has changed is not relivant.
12168 * <script type="text/javascript">
12173 * @extends Roo.data.Store
12174 * @class Roo.data.JsonStore
12175 * Small helper class to make creating Stores for JSON data easier. <br/>
12177 var store = new Roo.data.JsonStore({
12178 url: 'get-images.php',
12180 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12183 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12184 * JsonReader and HttpProxy (unless inline data is provided).</b>
12185 * @cfg {Array} fields An array of field definition objects, or field name strings.
12187 * @param {Object} config
12189 Roo.data.JsonStore = function(c){
12190 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12191 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12192 reader: new Roo.data.JsonReader(c, c.fields)
12195 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12197 * Ext JS Library 1.1.1
12198 * Copyright(c) 2006-2007, Ext JS, LLC.
12200 * Originally Released Under LGPL - original licence link has changed is not relivant.
12203 * <script type="text/javascript">
12207 Roo.data.Field = function(config){
12208 if(typeof config == "string"){
12209 config = {name: config};
12211 Roo.apply(this, config);
12214 this.type = "auto";
12217 var st = Roo.data.SortTypes;
12218 // named sortTypes are supported, here we look them up
12219 if(typeof this.sortType == "string"){
12220 this.sortType = st[this.sortType];
12223 // set default sortType for strings and dates
12224 if(!this.sortType){
12227 this.sortType = st.asUCString;
12230 this.sortType = st.asDate;
12233 this.sortType = st.none;
12238 var stripRe = /[\$,%]/g;
12240 // prebuilt conversion function for this field, instead of
12241 // switching every time we're reading a value
12243 var cv, dateFormat = this.dateFormat;
12248 cv = function(v){ return v; };
12251 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12255 return v !== undefined && v !== null && v !== '' ?
12256 parseInt(String(v).replace(stripRe, ""), 10) : '';
12261 return v !== undefined && v !== null && v !== '' ?
12262 parseFloat(String(v).replace(stripRe, ""), 10) : '';
12267 cv = function(v){ return v === true || v === "true" || v == 1; };
12274 if(v instanceof Date){
12278 if(dateFormat == "timestamp"){
12279 return new Date(v*1000);
12281 return Date.parseDate(v, dateFormat);
12283 var parsed = Date.parse(v);
12284 return parsed ? new Date(parsed) : null;
12293 Roo.data.Field.prototype = {
12301 * Ext JS Library 1.1.1
12302 * Copyright(c) 2006-2007, Ext JS, LLC.
12304 * Originally Released Under LGPL - original licence link has changed is not relivant.
12307 * <script type="text/javascript">
12310 // Base class for reading structured data from a data source. This class is intended to be
12311 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12314 * @class Roo.data.DataReader
12315 * Base class for reading structured data from a data source. This class is intended to be
12316 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12319 Roo.data.DataReader = function(meta, recordType){
12323 this.recordType = recordType instanceof Array ?
12324 Roo.data.Record.create(recordType) : recordType;
12327 Roo.data.DataReader.prototype = {
12330 readerType : 'Data',
12332 * Create an empty record
12333 * @param {Object} data (optional) - overlay some values
12334 * @return {Roo.data.Record} record created.
12336 newRow : function(d) {
12338 this.recordType.prototype.fields.each(function(c) {
12340 case 'int' : da[c.name] = 0; break;
12341 case 'date' : da[c.name] = new Date(); break;
12342 case 'float' : da[c.name] = 0.0; break;
12343 case 'boolean' : da[c.name] = false; break;
12344 default : da[c.name] = ""; break;
12348 return new this.recordType(Roo.apply(da, d));
12354 * Ext JS Library 1.1.1
12355 * Copyright(c) 2006-2007, Ext JS, LLC.
12357 * Originally Released Under LGPL - original licence link has changed is not relivant.
12360 * <script type="text/javascript">
12364 * @class Roo.data.DataProxy
12365 * @extends Roo.data.Observable
12366 * This class is an abstract base class for implementations which provide retrieval of
12367 * unformatted data objects.<br>
12369 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12370 * (of the appropriate type which knows how to parse the data object) to provide a block of
12371 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12373 * Custom implementations must implement the load method as described in
12374 * {@link Roo.data.HttpProxy#load}.
12376 Roo.data.DataProxy = function(){
12379 * @event beforeload
12380 * Fires before a network request is made to retrieve a data object.
12381 * @param {Object} This DataProxy object.
12382 * @param {Object} params The params parameter to the load function.
12387 * Fires before the load method's callback is called.
12388 * @param {Object} This DataProxy object.
12389 * @param {Object} o The data object.
12390 * @param {Object} arg The callback argument object passed to the load function.
12394 * @event loadexception
12395 * Fires if an Exception occurs during data retrieval.
12396 * @param {Object} This DataProxy object.
12397 * @param {Object} o The data object.
12398 * @param {Object} arg The callback argument object passed to the load function.
12399 * @param {Object} e The Exception.
12401 loadexception : true
12403 Roo.data.DataProxy.superclass.constructor.call(this);
12406 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12409 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12413 * Ext JS Library 1.1.1
12414 * Copyright(c) 2006-2007, Ext JS, LLC.
12416 * Originally Released Under LGPL - original licence link has changed is not relivant.
12419 * <script type="text/javascript">
12422 * @class Roo.data.MemoryProxy
12423 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12424 * to the Reader when its load method is called.
12426 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12428 Roo.data.MemoryProxy = function(data){
12432 Roo.data.MemoryProxy.superclass.constructor.call(this);
12436 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12439 * Load data from the requested source (in this case an in-memory
12440 * data object passed to the constructor), read the data object into
12441 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12442 * process that block using the passed callback.
12443 * @param {Object} params This parameter is not used by the MemoryProxy class.
12444 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12445 * object into a block of Roo.data.Records.
12446 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12447 * The function must be passed <ul>
12448 * <li>The Record block object</li>
12449 * <li>The "arg" argument from the load function</li>
12450 * <li>A boolean success indicator</li>
12452 * @param {Object} scope The scope in which to call the callback
12453 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12455 load : function(params, reader, callback, scope, arg){
12456 params = params || {};
12459 result = reader.readRecords(params.data ? params.data :this.data);
12461 this.fireEvent("loadexception", this, arg, null, e);
12462 callback.call(scope, null, arg, false);
12465 callback.call(scope, result, arg, true);
12469 update : function(params, records){
12474 * Ext JS Library 1.1.1
12475 * Copyright(c) 2006-2007, Ext JS, LLC.
12477 * Originally Released Under LGPL - original licence link has changed is not relivant.
12480 * <script type="text/javascript">
12483 * @class Roo.data.HttpProxy
12484 * @extends Roo.data.DataProxy
12485 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12486 * configured to reference a certain URL.<br><br>
12488 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12489 * from which the running page was served.<br><br>
12491 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12493 * Be aware that to enable the browser to parse an XML document, the server must set
12494 * the Content-Type header in the HTTP response to "text/xml".
12496 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12497 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12498 * will be used to make the request.
12500 Roo.data.HttpProxy = function(conn){
12501 Roo.data.HttpProxy.superclass.constructor.call(this);
12502 // is conn a conn config or a real conn?
12504 this.useAjax = !conn || !conn.events;
12508 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12509 // thse are take from connection...
12512 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12515 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12516 * extra parameters to each request made by this object. (defaults to undefined)
12519 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12520 * to each request made by this object. (defaults to undefined)
12523 * @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)
12526 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12529 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12535 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12539 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12540 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12541 * a finer-grained basis than the DataProxy events.
12543 getConnection : function(){
12544 return this.useAjax ? Roo.Ajax : this.conn;
12548 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12549 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12550 * process that block using the passed callback.
12551 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12552 * for the request to the remote server.
12553 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12554 * object into a block of Roo.data.Records.
12555 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12556 * The function must be passed <ul>
12557 * <li>The Record block object</li>
12558 * <li>The "arg" argument from the load function</li>
12559 * <li>A boolean success indicator</li>
12561 * @param {Object} scope The scope in which to call the callback
12562 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12564 load : function(params, reader, callback, scope, arg){
12565 if(this.fireEvent("beforeload", this, params) !== false){
12567 params : params || {},
12569 callback : callback,
12574 callback : this.loadResponse,
12578 Roo.applyIf(o, this.conn);
12579 if(this.activeRequest){
12580 Roo.Ajax.abort(this.activeRequest);
12582 this.activeRequest = Roo.Ajax.request(o);
12584 this.conn.request(o);
12587 callback.call(scope||this, null, arg, false);
12592 loadResponse : function(o, success, response){
12593 delete this.activeRequest;
12595 this.fireEvent("loadexception", this, o, response);
12596 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12601 result = o.reader.read(response);
12603 this.fireEvent("loadexception", this, o, response, e);
12604 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12608 this.fireEvent("load", this, o, o.request.arg);
12609 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12613 update : function(dataSet){
12618 updateResponse : function(dataSet){
12623 * Ext JS Library 1.1.1
12624 * Copyright(c) 2006-2007, Ext JS, LLC.
12626 * Originally Released Under LGPL - original licence link has changed is not relivant.
12629 * <script type="text/javascript">
12633 * @class Roo.data.ScriptTagProxy
12634 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12635 * other than the originating domain of the running page.<br><br>
12637 * <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
12638 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12640 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12641 * source code that is used as the source inside a <script> tag.<br><br>
12643 * In order for the browser to process the returned data, the server must wrap the data object
12644 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12645 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12646 * depending on whether the callback name was passed:
12649 boolean scriptTag = false;
12650 String cb = request.getParameter("callback");
12653 response.setContentType("text/javascript");
12655 response.setContentType("application/x-json");
12657 Writer out = response.getWriter();
12659 out.write(cb + "(");
12661 out.print(dataBlock.toJsonString());
12668 * @param {Object} config A configuration object.
12670 Roo.data.ScriptTagProxy = function(config){
12671 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12672 Roo.apply(this, config);
12673 this.head = document.getElementsByTagName("head")[0];
12676 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12678 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12680 * @cfg {String} url The URL from which to request the data object.
12683 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12687 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12688 * the server the name of the callback function set up by the load call to process the returned data object.
12689 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12690 * javascript output which calls this named function passing the data object as its only parameter.
12692 callbackParam : "callback",
12694 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12695 * name to the request.
12700 * Load data from the configured URL, read the data object into
12701 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12702 * process that block using the passed callback.
12703 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12704 * for the request to the remote server.
12705 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12706 * object into a block of Roo.data.Records.
12707 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12708 * The function must be passed <ul>
12709 * <li>The Record block object</li>
12710 * <li>The "arg" argument from the load function</li>
12711 * <li>A boolean success indicator</li>
12713 * @param {Object} scope The scope in which to call the callback
12714 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12716 load : function(params, reader, callback, scope, arg){
12717 if(this.fireEvent("beforeload", this, params) !== false){
12719 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12721 var url = this.url;
12722 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12724 url += "&_dc=" + (new Date().getTime());
12726 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12729 cb : "stcCallback"+transId,
12730 scriptId : "stcScript"+transId,
12734 callback : callback,
12740 window[trans.cb] = function(o){
12741 conn.handleResponse(o, trans);
12744 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12746 if(this.autoAbort !== false){
12750 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12752 var script = document.createElement("script");
12753 script.setAttribute("src", url);
12754 script.setAttribute("type", "text/javascript");
12755 script.setAttribute("id", trans.scriptId);
12756 this.head.appendChild(script);
12758 this.trans = trans;
12760 callback.call(scope||this, null, arg, false);
12765 isLoading : function(){
12766 return this.trans ? true : false;
12770 * Abort the current server request.
12772 abort : function(){
12773 if(this.isLoading()){
12774 this.destroyTrans(this.trans);
12779 destroyTrans : function(trans, isLoaded){
12780 this.head.removeChild(document.getElementById(trans.scriptId));
12781 clearTimeout(trans.timeoutId);
12783 window[trans.cb] = undefined;
12785 delete window[trans.cb];
12788 // if hasn't been loaded, wait for load to remove it to prevent script error
12789 window[trans.cb] = function(){
12790 window[trans.cb] = undefined;
12792 delete window[trans.cb];
12799 handleResponse : function(o, trans){
12800 this.trans = false;
12801 this.destroyTrans(trans, true);
12804 result = trans.reader.readRecords(o);
12806 this.fireEvent("loadexception", this, o, trans.arg, e);
12807 trans.callback.call(trans.scope||window, null, trans.arg, false);
12810 this.fireEvent("load", this, o, trans.arg);
12811 trans.callback.call(trans.scope||window, result, trans.arg, true);
12815 handleFailure : function(trans){
12816 this.trans = false;
12817 this.destroyTrans(trans, false);
12818 this.fireEvent("loadexception", this, null, trans.arg);
12819 trans.callback.call(trans.scope||window, null, trans.arg, false);
12823 * Ext JS Library 1.1.1
12824 * Copyright(c) 2006-2007, Ext JS, LLC.
12826 * Originally Released Under LGPL - original licence link has changed is not relivant.
12829 * <script type="text/javascript">
12833 * @class Roo.data.JsonReader
12834 * @extends Roo.data.DataReader
12835 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12836 * based on mappings in a provided Roo.data.Record constructor.
12838 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12839 * in the reply previously.
12844 var RecordDef = Roo.data.Record.create([
12845 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12846 {name: 'occupation'} // This field will use "occupation" as the mapping.
12848 var myReader = new Roo.data.JsonReader({
12849 totalProperty: "results", // The property which contains the total dataset size (optional)
12850 root: "rows", // The property which contains an Array of row objects
12851 id: "id" // The property within each row object that provides an ID for the record (optional)
12855 * This would consume a JSON file like this:
12857 { 'results': 2, 'rows': [
12858 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12859 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12862 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12863 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12864 * paged from the remote server.
12865 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12866 * @cfg {String} root name of the property which contains the Array of row objects.
12867 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12868 * @cfg {Array} fields Array of field definition objects
12870 * Create a new JsonReader
12871 * @param {Object} meta Metadata configuration options
12872 * @param {Object} recordType Either an Array of field definition objects,
12873 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12875 Roo.data.JsonReader = function(meta, recordType){
12878 // set some defaults:
12879 Roo.applyIf(meta, {
12880 totalProperty: 'total',
12881 successProperty : 'success',
12886 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12888 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12890 readerType : 'Json',
12893 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12894 * Used by Store query builder to append _requestMeta to params.
12897 metaFromRemote : false,
12899 * This method is only used by a DataProxy which has retrieved data from a remote server.
12900 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12901 * @return {Object} data A data block which is used by an Roo.data.Store object as
12902 * a cache of Roo.data.Records.
12904 read : function(response){
12905 var json = response.responseText;
12907 var o = /* eval:var:o */ eval("("+json+")");
12909 throw {message: "JsonReader.read: Json object not found"};
12915 this.metaFromRemote = true;
12916 this.meta = o.metaData;
12917 this.recordType = Roo.data.Record.create(o.metaData.fields);
12918 this.onMetaChange(this.meta, this.recordType, o);
12920 return this.readRecords(o);
12923 // private function a store will implement
12924 onMetaChange : function(meta, recordType, o){
12931 simpleAccess: function(obj, subsc) {
12938 getJsonAccessor: function(){
12940 return function(expr) {
12942 return(re.test(expr))
12943 ? new Function("obj", "return obj." + expr)
12948 return Roo.emptyFn;
12953 * Create a data block containing Roo.data.Records from an XML document.
12954 * @param {Object} o An object which contains an Array of row objects in the property specified
12955 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12956 * which contains the total size of the dataset.
12957 * @return {Object} data A data block which is used by an Roo.data.Store object as
12958 * a cache of Roo.data.Records.
12960 readRecords : function(o){
12962 * After any data loads, the raw JSON data is available for further custom processing.
12966 var s = this.meta, Record = this.recordType,
12967 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12969 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12971 if(s.totalProperty) {
12972 this.getTotal = this.getJsonAccessor(s.totalProperty);
12974 if(s.successProperty) {
12975 this.getSuccess = this.getJsonAccessor(s.successProperty);
12977 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12979 var g = this.getJsonAccessor(s.id);
12980 this.getId = function(rec) {
12982 return (r === undefined || r === "") ? null : r;
12985 this.getId = function(){return null;};
12988 for(var jj = 0; jj < fl; jj++){
12990 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12991 this.ef[jj] = this.getJsonAccessor(map);
12995 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12996 if(s.totalProperty){
12997 var vt = parseInt(this.getTotal(o), 10);
13002 if(s.successProperty){
13003 var vs = this.getSuccess(o);
13004 if(vs === false || vs === 'false'){
13009 for(var i = 0; i < c; i++){
13012 var id = this.getId(n);
13013 for(var j = 0; j < fl; j++){
13015 var v = this.ef[j](n);
13017 Roo.log('missing convert for ' + f.name);
13021 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13023 var record = new Record(values, id);
13025 records[i] = record;
13031 totalRecords : totalRecords
13035 * using 'cn' the nested child reader read the child array into it's child stores.
13036 * @param {Object} rec The record with a 'children array
13038 loadDataFromChildren: 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 this.loadData({ 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
13133 * using 'cn' the nested child reader read the child array into it's child stores.
13134 * @param {Object} rec The record with a 'children array
13136 loadDataFromChildren: function(rec)
13138 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13139 return this.loadData(typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn);
13150 * @class Roo.bootstrap.ComboBox
13151 * @extends Roo.bootstrap.TriggerField
13152 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13153 * @cfg {Boolean} append (true|false) default false
13154 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13155 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13156 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13157 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13158 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13159 * @cfg {Boolean} animate default true
13160 * @cfg {Boolean} emptyResultText only for touch device
13161 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13162 * @cfg {String} emptyTitle default ''
13164 * Create a new ComboBox.
13165 * @param {Object} config Configuration options
13167 Roo.bootstrap.ComboBox = function(config){
13168 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13172 * Fires when the dropdown list is expanded
13173 * @param {Roo.bootstrap.ComboBox} combo This combo box
13178 * Fires when the dropdown list is collapsed
13179 * @param {Roo.bootstrap.ComboBox} combo This combo box
13183 * @event beforeselect
13184 * Fires before a list item is selected. Return false to cancel the selection.
13185 * @param {Roo.bootstrap.ComboBox} combo This combo box
13186 * @param {Roo.data.Record} record The data record returned from the underlying store
13187 * @param {Number} index The index of the selected item in the dropdown list
13189 'beforeselect' : true,
13192 * Fires when a list item is selected
13193 * @param {Roo.bootstrap.ComboBox} combo This combo box
13194 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13195 * @param {Number} index The index of the selected item in the dropdown list
13199 * @event beforequery
13200 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13201 * The event object passed has these properties:
13202 * @param {Roo.bootstrap.ComboBox} combo This combo box
13203 * @param {String} query The query
13204 * @param {Boolean} forceAll true to force "all" query
13205 * @param {Boolean} cancel true to cancel the query
13206 * @param {Object} e The query event object
13208 'beforequery': true,
13211 * Fires when the 'add' icon is pressed (add a listener to enable add button)
13212 * @param {Roo.bootstrap.ComboBox} combo This combo box
13217 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13218 * @param {Roo.bootstrap.ComboBox} combo This combo box
13219 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13224 * Fires when the remove value from the combobox array
13225 * @param {Roo.bootstrap.ComboBox} combo This combo box
13229 * @event afterremove
13230 * Fires when the remove value from the combobox array
13231 * @param {Roo.bootstrap.ComboBox} combo This combo box
13233 'afterremove' : true,
13235 * @event specialfilter
13236 * Fires when specialfilter
13237 * @param {Roo.bootstrap.ComboBox} combo This combo box
13239 'specialfilter' : true,
13242 * Fires when tick the element
13243 * @param {Roo.bootstrap.ComboBox} combo This combo box
13247 * @event touchviewdisplay
13248 * Fires when touch view require special display (default is using displayField)
13249 * @param {Roo.bootstrap.ComboBox} combo This combo box
13250 * @param {Object} cfg set html .
13252 'touchviewdisplay' : true
13257 this.tickItems = [];
13259 this.selectedIndex = -1;
13260 if(this.mode == 'local'){
13261 if(config.queryDelay === undefined){
13262 this.queryDelay = 10;
13264 if(config.minChars === undefined){
13270 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13273 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13274 * rendering into an Roo.Editor, defaults to false)
13277 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13278 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13281 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13284 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13285 * the dropdown list (defaults to undefined, with no header element)
13289 * @cfg {String/Roo.Template} tpl The template to use to render the output
13293 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13295 listWidth: undefined,
13297 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13298 * mode = 'remote' or 'text' if mode = 'local')
13300 displayField: undefined,
13303 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13304 * mode = 'remote' or 'value' if mode = 'local').
13305 * Note: use of a valueField requires the user make a selection
13306 * in order for a value to be mapped.
13308 valueField: undefined,
13310 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13315 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13316 * field's data value (defaults to the underlying DOM element's name)
13318 hiddenName: undefined,
13320 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13324 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13326 selectedClass: 'active',
13329 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13333 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13334 * anchor positions (defaults to 'tl-bl')
13336 listAlign: 'tl-bl?',
13338 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13342 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13343 * query specified by the allQuery config option (defaults to 'query')
13345 triggerAction: 'query',
13347 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13348 * (defaults to 4, does not apply if editable = false)
13352 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13353 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13357 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13358 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13362 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13363 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13367 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13368 * when editable = true (defaults to false)
13370 selectOnFocus:false,
13372 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13374 queryParam: 'query',
13376 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13377 * when mode = 'remote' (defaults to 'Loading...')
13379 loadingText: 'Loading...',
13381 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13385 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13389 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13390 * traditional select (defaults to true)
13394 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13398 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13402 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13403 * listWidth has a higher value)
13407 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13408 * allow the user to set arbitrary text into the field (defaults to false)
13410 forceSelection:false,
13412 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13413 * if typeAhead = true (defaults to 250)
13415 typeAheadDelay : 250,
13417 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13418 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13420 valueNotFoundText : undefined,
13422 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13424 blockFocus : false,
13427 * @cfg {Boolean} disableClear Disable showing of clear button.
13429 disableClear : false,
13431 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13433 alwaysQuery : false,
13436 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13441 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13443 invalidClass : "has-warning",
13446 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13448 validClass : "has-success",
13451 * @cfg {Boolean} specialFilter (true|false) special filter default false
13453 specialFilter : false,
13456 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13458 mobileTouchView : true,
13461 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13463 useNativeIOS : false,
13466 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13468 mobile_restrict_height : false,
13470 ios_options : false,
13482 btnPosition : 'right',
13483 triggerList : true,
13484 showToggleBtn : true,
13486 emptyResultText: 'Empty',
13487 triggerText : 'Select',
13490 // element that contains real text value.. (when hidden is used..)
13492 getAutoCreate : function()
13497 * Render classic select for iso
13500 if(Roo.isIOS && this.useNativeIOS){
13501 cfg = this.getAutoCreateNativeIOS();
13509 if(Roo.isTouch && this.mobileTouchView){
13510 cfg = this.getAutoCreateTouchView();
13517 if(!this.tickable){
13518 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13523 * ComboBox with tickable selections
13526 var align = this.labelAlign || this.parentLabelAlign();
13529 cls : 'form-group roo-combobox-tickable' //input-group
13532 var btn_text_select = '';
13533 var btn_text_done = '';
13534 var btn_text_cancel = '';
13536 if (this.btn_text_show) {
13537 btn_text_select = 'Select';
13538 btn_text_done = 'Done';
13539 btn_text_cancel = 'Cancel';
13544 cls : 'tickable-buttons',
13549 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13550 //html : this.triggerText
13551 html: btn_text_select
13557 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13559 html: btn_text_done
13565 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13567 html: btn_text_cancel
13573 buttons.cn.unshift({
13575 cls: 'roo-select2-search-field-input'
13581 Roo.each(buttons.cn, function(c){
13583 c.cls += ' btn-' + _this.size;
13586 if (_this.disabled) {
13593 style : 'display: contents',
13598 cls: 'form-hidden-field'
13602 cls: 'roo-select2-choices',
13606 cls: 'roo-select2-search-field',
13617 cls: 'roo-select2-container input-group roo-select2-container-multi',
13623 // cls: 'typeahead typeahead-long dropdown-menu',
13624 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13629 if(this.hasFeedback && !this.allowBlank){
13633 cls: 'glyphicon form-control-feedback'
13636 combobox.cn.push(feedback);
13641 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13642 tooltip : 'This field is required'
13644 if (Roo.bootstrap.version == 4) {
13647 style : 'display:none'
13650 if (align ==='left' && this.fieldLabel.length) {
13652 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13659 cls : 'control-label col-form-label',
13660 html : this.fieldLabel
13672 var labelCfg = cfg.cn[1];
13673 var contentCfg = cfg.cn[2];
13676 if(this.indicatorpos == 'right'){
13682 cls : 'control-label col-form-label',
13686 html : this.fieldLabel
13702 labelCfg = cfg.cn[0];
13703 contentCfg = cfg.cn[1];
13707 if(this.labelWidth > 12){
13708 labelCfg.style = "width: " + this.labelWidth + 'px';
13711 if(this.labelWidth < 13 && this.labelmd == 0){
13712 this.labelmd = this.labelWidth;
13715 if(this.labellg > 0){
13716 labelCfg.cls += ' col-lg-' + this.labellg;
13717 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13720 if(this.labelmd > 0){
13721 labelCfg.cls += ' col-md-' + this.labelmd;
13722 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13725 if(this.labelsm > 0){
13726 labelCfg.cls += ' col-sm-' + this.labelsm;
13727 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13730 if(this.labelxs > 0){
13731 labelCfg.cls += ' col-xs-' + this.labelxs;
13732 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13736 } else if ( this.fieldLabel.length) {
13737 // Roo.log(" label");
13742 //cls : 'input-group-addon',
13743 html : this.fieldLabel
13748 if(this.indicatorpos == 'right'){
13752 //cls : 'input-group-addon',
13753 html : this.fieldLabel
13763 // Roo.log(" no label && no align");
13770 ['xs','sm','md','lg'].map(function(size){
13771 if (settings[size]) {
13772 cfg.cls += ' col-' + size + '-' + settings[size];
13780 _initEventsCalled : false,
13783 initEvents: function()
13785 if (this._initEventsCalled) { // as we call render... prevent looping...
13788 this._initEventsCalled = true;
13791 throw "can not find store for combo";
13794 this.indicator = this.indicatorEl();
13796 this.store = Roo.factory(this.store, Roo.data);
13797 this.store.parent = this;
13799 // if we are building from html. then this element is so complex, that we can not really
13800 // use the rendered HTML.
13801 // so we have to trash and replace the previous code.
13802 if (Roo.XComponent.build_from_html) {
13803 // remove this element....
13804 var e = this.el.dom, k=0;
13805 while (e ) { e = e.previousSibling; ++k;}
13810 this.rendered = false;
13812 this.render(this.parent().getChildContainer(true), k);
13815 if(Roo.isIOS && this.useNativeIOS){
13816 this.initIOSView();
13824 if(Roo.isTouch && this.mobileTouchView){
13825 this.initTouchView();
13830 this.initTickableEvents();
13834 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13836 if(this.hiddenName){
13838 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13840 this.hiddenField.dom.value =
13841 this.hiddenValue !== undefined ? this.hiddenValue :
13842 this.value !== undefined ? this.value : '';
13844 // prevent input submission
13845 this.el.dom.removeAttribute('name');
13846 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13851 // this.el.dom.setAttribute('autocomplete', 'off');
13854 var cls = 'x-combo-list';
13856 //this.list = new Roo.Layer({
13857 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13863 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13864 _this.list.setWidth(lw);
13867 this.list.on('mouseover', this.onViewOver, this);
13868 this.list.on('mousemove', this.onViewMove, this);
13869 this.list.on('scroll', this.onViewScroll, this);
13872 this.list.swallowEvent('mousewheel');
13873 this.assetHeight = 0;
13876 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13877 this.assetHeight += this.header.getHeight();
13880 this.innerList = this.list.createChild({cls:cls+'-inner'});
13881 this.innerList.on('mouseover', this.onViewOver, this);
13882 this.innerList.on('mousemove', this.onViewMove, this);
13883 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13885 if(this.allowBlank && !this.pageSize && !this.disableClear){
13886 this.footer = this.list.createChild({cls:cls+'-ft'});
13887 this.pageTb = new Roo.Toolbar(this.footer);
13891 this.footer = this.list.createChild({cls:cls+'-ft'});
13892 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13893 {pageSize: this.pageSize});
13897 if (this.pageTb && this.allowBlank && !this.disableClear) {
13899 this.pageTb.add(new Roo.Toolbar.Fill(), {
13900 cls: 'x-btn-icon x-btn-clear',
13902 handler: function()
13905 _this.clearValue();
13906 _this.onSelect(false, -1);
13911 this.assetHeight += this.footer.getHeight();
13916 this.tpl = Roo.bootstrap.version == 4 ?
13917 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
13918 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13921 this.view = new Roo.View(this.list, this.tpl, {
13922 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13924 //this.view.wrapEl.setDisplayed(false);
13925 this.view.on('click', this.onViewClick, this);
13928 this.store.on('beforeload', this.onBeforeLoad, this);
13929 this.store.on('load', this.onLoad, this);
13930 this.store.on('loadexception', this.onLoadException, this);
13932 if(this.resizable){
13933 this.resizer = new Roo.Resizable(this.list, {
13934 pinned:true, handles:'se'
13936 this.resizer.on('resize', function(r, w, h){
13937 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13938 this.listWidth = w;
13939 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13940 this.restrictHeight();
13942 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13945 if(!this.editable){
13946 this.editable = true;
13947 this.setEditable(false);
13952 if (typeof(this.events.add.listeners) != 'undefined') {
13954 this.addicon = this.wrap.createChild(
13955 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13957 this.addicon.on('click', function(e) {
13958 this.fireEvent('add', this);
13961 if (typeof(this.events.edit.listeners) != 'undefined') {
13963 this.editicon = this.wrap.createChild(
13964 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13965 if (this.addicon) {
13966 this.editicon.setStyle('margin-left', '40px');
13968 this.editicon.on('click', function(e) {
13970 // we fire even if inothing is selected..
13971 this.fireEvent('edit', this, this.lastData );
13977 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13978 "up" : function(e){
13979 this.inKeyMode = true;
13983 "down" : function(e){
13984 if(!this.isExpanded()){
13985 this.onTriggerClick();
13987 this.inKeyMode = true;
13992 "enter" : function(e){
13993 // this.onViewClick();
13997 if(this.fireEvent("specialkey", this, e)){
13998 this.onViewClick(false);
14004 "esc" : function(e){
14008 "tab" : function(e){
14011 if(this.fireEvent("specialkey", this, e)){
14012 this.onViewClick(false);
14020 doRelay : function(foo, bar, hname){
14021 if(hname == 'down' || this.scope.isExpanded()){
14022 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14031 this.queryDelay = Math.max(this.queryDelay || 10,
14032 this.mode == 'local' ? 10 : 250);
14035 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14037 if(this.typeAhead){
14038 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14040 if(this.editable !== false){
14041 this.inputEl().on("keyup", this.onKeyUp, this);
14043 if(this.forceSelection){
14044 this.inputEl().on('blur', this.doForce, this);
14048 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14049 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14053 initTickableEvents: function()
14057 if(this.hiddenName){
14059 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14061 this.hiddenField.dom.value =
14062 this.hiddenValue !== undefined ? this.hiddenValue :
14063 this.value !== undefined ? this.value : '';
14065 // prevent input submission
14066 this.el.dom.removeAttribute('name');
14067 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14072 // this.list = this.el.select('ul.dropdown-menu',true).first();
14074 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14075 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14076 if(this.triggerList){
14077 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14080 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14081 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14083 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14084 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14086 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14087 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14089 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14090 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14091 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14094 this.cancelBtn.hide();
14099 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14100 _this.list.setWidth(lw);
14103 this.list.on('mouseover', this.onViewOver, this);
14104 this.list.on('mousemove', this.onViewMove, this);
14106 this.list.on('scroll', this.onViewScroll, this);
14109 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
14110 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14113 this.view = new Roo.View(this.list, this.tpl, {
14118 selectedClass: this.selectedClass
14121 //this.view.wrapEl.setDisplayed(false);
14122 this.view.on('click', this.onViewClick, this);
14126 this.store.on('beforeload', this.onBeforeLoad, this);
14127 this.store.on('load', this.onLoad, this);
14128 this.store.on('loadexception', this.onLoadException, this);
14131 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14132 "up" : function(e){
14133 this.inKeyMode = true;
14137 "down" : function(e){
14138 this.inKeyMode = true;
14142 "enter" : function(e){
14143 if(this.fireEvent("specialkey", this, e)){
14144 this.onViewClick(false);
14150 "esc" : function(e){
14151 this.onTickableFooterButtonClick(e, false, false);
14154 "tab" : function(e){
14155 this.fireEvent("specialkey", this, e);
14157 this.onTickableFooterButtonClick(e, false, false);
14164 doRelay : function(e, fn, key){
14165 if(this.scope.isExpanded()){
14166 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14175 this.queryDelay = Math.max(this.queryDelay || 10,
14176 this.mode == 'local' ? 10 : 250);
14179 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14181 if(this.typeAhead){
14182 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14185 if(this.editable !== false){
14186 this.tickableInputEl().on("keyup", this.onKeyUp, this);
14189 this.indicator = this.indicatorEl();
14191 if(this.indicator){
14192 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14193 this.indicator.hide();
14198 onDestroy : function(){
14200 this.view.setStore(null);
14201 this.view.el.removeAllListeners();
14202 this.view.el.remove();
14203 this.view.purgeListeners();
14206 this.list.dom.innerHTML = '';
14210 this.store.un('beforeload', this.onBeforeLoad, this);
14211 this.store.un('load', this.onLoad, this);
14212 this.store.un('loadexception', this.onLoadException, this);
14214 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14218 fireKey : function(e){
14219 if(e.isNavKeyPress() && !this.list.isVisible()){
14220 this.fireEvent("specialkey", this, e);
14225 onResize: function(w, h){
14226 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14228 // if(typeof w != 'number'){
14229 // // we do not handle it!?!?
14232 // var tw = this.trigger.getWidth();
14233 // // tw += this.addicon ? this.addicon.getWidth() : 0;
14234 // // tw += this.editicon ? this.editicon.getWidth() : 0;
14236 // this.inputEl().setWidth( this.adjustWidth('input', x));
14238 // //this.trigger.setStyle('left', x+'px');
14240 // if(this.list && this.listWidth === undefined){
14241 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14242 // this.list.setWidth(lw);
14243 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14251 * Allow or prevent the user from directly editing the field text. If false is passed,
14252 * the user will only be able to select from the items defined in the dropdown list. This method
14253 * is the runtime equivalent of setting the 'editable' config option at config time.
14254 * @param {Boolean} value True to allow the user to directly edit the field text
14256 setEditable : function(value){
14257 if(value == this.editable){
14260 this.editable = value;
14262 this.inputEl().dom.setAttribute('readOnly', true);
14263 this.inputEl().on('mousedown', this.onTriggerClick, this);
14264 this.inputEl().addClass('x-combo-noedit');
14266 this.inputEl().dom.setAttribute('readOnly', false);
14267 this.inputEl().un('mousedown', this.onTriggerClick, this);
14268 this.inputEl().removeClass('x-combo-noedit');
14274 onBeforeLoad : function(combo,opts){
14275 if(!this.hasFocus){
14279 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14281 this.restrictHeight();
14282 this.selectedIndex = -1;
14286 onLoad : function(){
14288 this.hasQuery = false;
14290 if(!this.hasFocus){
14294 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14295 this.loading.hide();
14298 if(this.store.getCount() > 0){
14301 this.restrictHeight();
14302 if(this.lastQuery == this.allQuery){
14303 if(this.editable && !this.tickable){
14304 this.inputEl().dom.select();
14308 !this.selectByValue(this.value, true) &&
14311 !this.store.lastOptions ||
14312 typeof(this.store.lastOptions.add) == 'undefined' ||
14313 this.store.lastOptions.add != true
14316 this.select(0, true);
14319 if(this.autoFocus){
14322 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14323 this.taTask.delay(this.typeAheadDelay);
14327 this.onEmptyResults();
14333 onLoadException : function()
14335 this.hasQuery = false;
14337 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14338 this.loading.hide();
14341 if(this.tickable && this.editable){
14346 // only causes errors at present
14347 //Roo.log(this.store.reader.jsonData);
14348 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14350 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14356 onTypeAhead : function(){
14357 if(this.store.getCount() > 0){
14358 var r = this.store.getAt(0);
14359 var newValue = r.data[this.displayField];
14360 var len = newValue.length;
14361 var selStart = this.getRawValue().length;
14363 if(selStart != len){
14364 this.setRawValue(newValue);
14365 this.selectText(selStart, newValue.length);
14371 onSelect : function(record, index){
14373 if(this.fireEvent('beforeselect', this, record, index) !== false){
14375 this.setFromData(index > -1 ? record.data : false);
14378 this.fireEvent('select', this, record, index);
14383 * Returns the currently selected field value or empty string if no value is set.
14384 * @return {String} value The selected value
14386 getValue : function()
14388 if(Roo.isIOS && this.useNativeIOS){
14389 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14393 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14396 if(this.valueField){
14397 return typeof this.value != 'undefined' ? this.value : '';
14399 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14403 getRawValue : function()
14405 if(Roo.isIOS && this.useNativeIOS){
14406 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14409 var v = this.inputEl().getValue();
14415 * Clears any text/value currently set in the field
14417 clearValue : function(){
14419 if(this.hiddenField){
14420 this.hiddenField.dom.value = '';
14423 this.setRawValue('');
14424 this.lastSelectionText = '';
14425 this.lastData = false;
14427 var close = this.closeTriggerEl();
14438 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14439 * will be displayed in the field. If the value does not match the data value of an existing item,
14440 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14441 * Otherwise the field will be blank (although the value will still be set).
14442 * @param {String} value The value to match
14444 setValue : function(v)
14446 if(Roo.isIOS && this.useNativeIOS){
14447 this.setIOSValue(v);
14457 if(this.valueField){
14458 var r = this.findRecord(this.valueField, v);
14460 text = r.data[this.displayField];
14461 }else if(this.valueNotFoundText !== undefined){
14462 text = this.valueNotFoundText;
14465 this.lastSelectionText = text;
14466 if(this.hiddenField){
14467 this.hiddenField.dom.value = v;
14469 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14472 var close = this.closeTriggerEl();
14475 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14481 * @property {Object} the last set data for the element
14486 * Sets the value of the field based on a object which is related to the record format for the store.
14487 * @param {Object} value the value to set as. or false on reset?
14489 setFromData : function(o){
14496 var dv = ''; // display value
14497 var vv = ''; // value value..
14499 if (this.displayField) {
14500 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14502 // this is an error condition!!!
14503 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14506 if(this.valueField){
14507 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14510 var close = this.closeTriggerEl();
14513 if(dv.length || vv * 1 > 0){
14515 this.blockFocus=true;
14521 if(this.hiddenField){
14522 this.hiddenField.dom.value = vv;
14524 this.lastSelectionText = dv;
14525 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14529 // no hidden field.. - we store the value in 'value', but still display
14530 // display field!!!!
14531 this.lastSelectionText = dv;
14532 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14539 reset : function(){
14540 // overridden so that last data is reset..
14547 this.setValue(this.originalValue);
14548 //this.clearInvalid();
14549 this.lastData = false;
14551 this.view.clearSelections();
14557 findRecord : function(prop, value){
14559 if(this.store.getCount() > 0){
14560 this.store.each(function(r){
14561 if(r.data[prop] == value){
14571 getName: function()
14573 // returns hidden if it's set..
14574 if (!this.rendered) {return ''};
14575 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14579 onViewMove : function(e, t){
14580 this.inKeyMode = false;
14584 onViewOver : function(e, t){
14585 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14588 var item = this.view.findItemFromChild(t);
14591 var index = this.view.indexOf(item);
14592 this.select(index, false);
14597 onViewClick : function(view, doFocus, el, e)
14599 var index = this.view.getSelectedIndexes()[0];
14601 var r = this.store.getAt(index);
14605 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14612 Roo.each(this.tickItems, function(v,k){
14614 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14616 _this.tickItems.splice(k, 1);
14618 if(typeof(e) == 'undefined' && view == false){
14619 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14631 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14632 this.tickItems.push(r.data);
14635 if(typeof(e) == 'undefined' && view == false){
14636 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14643 this.onSelect(r, index);
14645 if(doFocus !== false && !this.blockFocus){
14646 this.inputEl().focus();
14651 restrictHeight : function(){
14652 //this.innerList.dom.style.height = '';
14653 //var inner = this.innerList.dom;
14654 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14655 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14656 //this.list.beginUpdate();
14657 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14658 this.list.alignTo(this.inputEl(), this.listAlign);
14659 this.list.alignTo(this.inputEl(), this.listAlign);
14660 //this.list.endUpdate();
14664 onEmptyResults : function(){
14666 if(this.tickable && this.editable){
14667 this.hasFocus = false;
14668 this.restrictHeight();
14676 * Returns true if the dropdown list is expanded, else false.
14678 isExpanded : function(){
14679 return this.list.isVisible();
14683 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14684 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14685 * @param {String} value The data value of the item to select
14686 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14687 * selected item if it is not currently in view (defaults to true)
14688 * @return {Boolean} True if the value matched an item in the list, else false
14690 selectByValue : function(v, scrollIntoView){
14691 if(v !== undefined && v !== null){
14692 var r = this.findRecord(this.valueField || this.displayField, v);
14694 this.select(this.store.indexOf(r), scrollIntoView);
14702 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14703 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14704 * @param {Number} index The zero-based index of the list item to select
14705 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14706 * selected item if it is not currently in view (defaults to true)
14708 select : function(index, scrollIntoView){
14709 this.selectedIndex = index;
14710 this.view.select(index);
14711 if(scrollIntoView !== false){
14712 var el = this.view.getNode(index);
14714 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14717 this.list.scrollChildIntoView(el, false);
14723 selectNext : function(){
14724 var ct = this.store.getCount();
14726 if(this.selectedIndex == -1){
14728 }else if(this.selectedIndex < ct-1){
14729 this.select(this.selectedIndex+1);
14735 selectPrev : function(){
14736 var ct = this.store.getCount();
14738 if(this.selectedIndex == -1){
14740 }else if(this.selectedIndex != 0){
14741 this.select(this.selectedIndex-1);
14747 onKeyUp : function(e){
14748 if(this.editable !== false && !e.isSpecialKey()){
14749 this.lastKey = e.getKey();
14750 this.dqTask.delay(this.queryDelay);
14755 validateBlur : function(){
14756 return !this.list || !this.list.isVisible();
14760 initQuery : function(){
14762 var v = this.getRawValue();
14764 if(this.tickable && this.editable){
14765 v = this.tickableInputEl().getValue();
14772 doForce : function(){
14773 if(this.inputEl().dom.value.length > 0){
14774 this.inputEl().dom.value =
14775 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14781 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14782 * query allowing the query action to be canceled if needed.
14783 * @param {String} query The SQL query to execute
14784 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14785 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14786 * saved in the current store (defaults to false)
14788 doQuery : function(q, forceAll){
14790 if(q === undefined || q === null){
14795 forceAll: forceAll,
14799 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14804 forceAll = qe.forceAll;
14805 if(forceAll === true || (q.length >= this.minChars)){
14807 this.hasQuery = true;
14809 if(this.lastQuery != q || this.alwaysQuery){
14810 this.lastQuery = q;
14811 if(this.mode == 'local'){
14812 this.selectedIndex = -1;
14814 this.store.clearFilter();
14817 if(this.specialFilter){
14818 this.fireEvent('specialfilter', this);
14823 this.store.filter(this.displayField, q);
14826 this.store.fireEvent("datachanged", this.store);
14833 this.store.baseParams[this.queryParam] = q;
14835 var options = {params : this.getParams(q)};
14838 options.add = true;
14839 options.params.start = this.page * this.pageSize;
14842 this.store.load(options);
14845 * this code will make the page width larger, at the beginning, the list not align correctly,
14846 * we should expand the list on onLoad
14847 * so command out it
14852 this.selectedIndex = -1;
14857 this.loadNext = false;
14861 getParams : function(q){
14863 //p[this.queryParam] = q;
14867 p.limit = this.pageSize;
14873 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14875 collapse : function(){
14876 if(!this.isExpanded()){
14882 this.hasFocus = false;
14886 this.cancelBtn.hide();
14887 this.trigger.show();
14890 this.tickableInputEl().dom.value = '';
14891 this.tickableInputEl().blur();
14896 Roo.get(document).un('mousedown', this.collapseIf, this);
14897 Roo.get(document).un('mousewheel', this.collapseIf, this);
14898 if (!this.editable) {
14899 Roo.get(document).un('keydown', this.listKeyPress, this);
14901 this.fireEvent('collapse', this);
14907 collapseIf : function(e){
14908 var in_combo = e.within(this.el);
14909 var in_list = e.within(this.list);
14910 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14912 if (in_combo || in_list || is_list) {
14913 //e.stopPropagation();
14918 this.onTickableFooterButtonClick(e, false, false);
14926 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14928 expand : function(){
14930 if(this.isExpanded() || !this.hasFocus){
14934 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14935 this.list.setWidth(lw);
14941 this.restrictHeight();
14945 this.tickItems = Roo.apply([], this.item);
14948 this.cancelBtn.show();
14949 this.trigger.hide();
14952 this.tickableInputEl().focus();
14957 Roo.get(document).on('mousedown', this.collapseIf, this);
14958 Roo.get(document).on('mousewheel', this.collapseIf, this);
14959 if (!this.editable) {
14960 Roo.get(document).on('keydown', this.listKeyPress, this);
14963 this.fireEvent('expand', this);
14967 // Implements the default empty TriggerField.onTriggerClick function
14968 onTriggerClick : function(e)
14970 Roo.log('trigger click');
14972 if(this.disabled || !this.triggerList){
14977 this.loadNext = false;
14979 if(this.isExpanded()){
14981 if (!this.blockFocus) {
14982 this.inputEl().focus();
14986 this.hasFocus = true;
14987 if(this.triggerAction == 'all') {
14988 this.doQuery(this.allQuery, true);
14990 this.doQuery(this.getRawValue());
14992 if (!this.blockFocus) {
14993 this.inputEl().focus();
14998 onTickableTriggerClick : function(e)
15005 this.loadNext = false;
15006 this.hasFocus = true;
15008 if(this.triggerAction == 'all') {
15009 this.doQuery(this.allQuery, true);
15011 this.doQuery(this.getRawValue());
15015 onSearchFieldClick : function(e)
15017 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
15018 this.onTickableFooterButtonClick(e, false, false);
15022 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
15027 this.loadNext = false;
15028 this.hasFocus = true;
15030 if(this.triggerAction == 'all') {
15031 this.doQuery(this.allQuery, true);
15033 this.doQuery(this.getRawValue());
15037 listKeyPress : function(e)
15039 //Roo.log('listkeypress');
15040 // scroll to first matching element based on key pres..
15041 if (e.isSpecialKey()) {
15044 var k = String.fromCharCode(e.getKey()).toUpperCase();
15047 var csel = this.view.getSelectedNodes();
15048 var cselitem = false;
15050 var ix = this.view.indexOf(csel[0]);
15051 cselitem = this.store.getAt(ix);
15052 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15058 this.store.each(function(v) {
15060 // start at existing selection.
15061 if (cselitem.id == v.id) {
15067 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15068 match = this.store.indexOf(v);
15074 if (match === false) {
15075 return true; // no more action?
15078 this.view.select(match);
15079 var sn = Roo.get(this.view.getSelectedNodes()[0]);
15080 sn.scrollIntoView(sn.dom.parentNode, false);
15083 onViewScroll : function(e, t){
15085 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){
15089 this.hasQuery = true;
15091 this.loading = this.list.select('.loading', true).first();
15093 if(this.loading === null){
15094 this.list.createChild({
15096 cls: 'loading roo-select2-more-results roo-select2-active',
15097 html: 'Loading more results...'
15100 this.loading = this.list.select('.loading', true).first();
15102 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15104 this.loading.hide();
15107 this.loading.show();
15112 this.loadNext = true;
15114 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15119 addItem : function(o)
15121 var dv = ''; // display value
15123 if (this.displayField) {
15124 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15126 // this is an error condition!!!
15127 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
15134 var choice = this.choices.createChild({
15136 cls: 'roo-select2-search-choice',
15145 cls: 'roo-select2-search-choice-close fa fa-times',
15150 }, this.searchField);
15152 var close = choice.select('a.roo-select2-search-choice-close', true).first();
15154 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15162 this.inputEl().dom.value = '';
15167 onRemoveItem : function(e, _self, o)
15169 e.preventDefault();
15171 this.lastItem = Roo.apply([], this.item);
15173 var index = this.item.indexOf(o.data) * 1;
15176 Roo.log('not this item?!');
15180 this.item.splice(index, 1);
15185 this.fireEvent('remove', this, e);
15191 syncValue : function()
15193 if(!this.item.length){
15200 Roo.each(this.item, function(i){
15201 if(_this.valueField){
15202 value.push(i[_this.valueField]);
15209 this.value = value.join(',');
15211 if(this.hiddenField){
15212 this.hiddenField.dom.value = this.value;
15215 this.store.fireEvent("datachanged", this.store);
15220 clearItem : function()
15222 if(!this.multiple){
15228 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15236 if(this.tickable && !Roo.isTouch){
15237 this.view.refresh();
15241 inputEl: function ()
15243 if(Roo.isIOS && this.useNativeIOS){
15244 return this.el.select('select.roo-ios-select', true).first();
15247 if(Roo.isTouch && this.mobileTouchView){
15248 return this.el.select('input.form-control',true).first();
15252 return this.searchField;
15255 return this.el.select('input.form-control',true).first();
15258 onTickableFooterButtonClick : function(e, btn, el)
15260 e.preventDefault();
15262 this.lastItem = Roo.apply([], this.item);
15264 if(btn && btn.name == 'cancel'){
15265 this.tickItems = Roo.apply([], this.item);
15274 Roo.each(this.tickItems, function(o){
15282 validate : function()
15284 if(this.getVisibilityEl().hasClass('hidden')){
15288 var v = this.getRawValue();
15291 v = this.getValue();
15294 if(this.disabled || this.allowBlank || v.length){
15299 this.markInvalid();
15303 tickableInputEl : function()
15305 if(!this.tickable || !this.editable){
15306 return this.inputEl();
15309 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15313 getAutoCreateTouchView : function()
15318 cls: 'form-group' //input-group
15324 type : this.inputType,
15325 cls : 'form-control x-combo-noedit',
15326 autocomplete: 'new-password',
15327 placeholder : this.placeholder || '',
15332 input.name = this.name;
15336 input.cls += ' input-' + this.size;
15339 if (this.disabled) {
15340 input.disabled = true;
15351 inputblock.cls += ' input-group';
15353 inputblock.cn.unshift({
15355 cls : 'input-group-addon input-group-prepend input-group-text',
15360 if(this.removable && !this.multiple){
15361 inputblock.cls += ' roo-removable';
15363 inputblock.cn.push({
15366 cls : 'roo-combo-removable-btn close'
15370 if(this.hasFeedback && !this.allowBlank){
15372 inputblock.cls += ' has-feedback';
15374 inputblock.cn.push({
15376 cls: 'glyphicon form-control-feedback'
15383 inputblock.cls += (this.before) ? '' : ' input-group';
15385 inputblock.cn.push({
15387 cls : 'input-group-addon input-group-append input-group-text',
15393 var ibwrap = inputblock;
15398 cls: 'roo-select2-choices',
15402 cls: 'roo-select2-search-field',
15415 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15420 cls: 'form-hidden-field'
15426 if(!this.multiple && this.showToggleBtn){
15432 if (this.caret != false) {
15435 cls: 'fa fa-' + this.caret
15442 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15444 Roo.bootstrap.version == 3 ? caret : '',
15447 cls: 'combobox-clear',
15461 combobox.cls += ' roo-select2-container-multi';
15464 var align = this.labelAlign || this.parentLabelAlign();
15466 if (align ==='left' && this.fieldLabel.length) {
15471 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15472 tooltip : 'This field is required'
15476 cls : 'control-label col-form-label',
15477 html : this.fieldLabel
15488 var labelCfg = cfg.cn[1];
15489 var contentCfg = cfg.cn[2];
15492 if(this.indicatorpos == 'right'){
15497 cls : 'control-label col-form-label',
15501 html : this.fieldLabel
15505 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15506 tooltip : 'This field is required'
15519 labelCfg = cfg.cn[0];
15520 contentCfg = cfg.cn[1];
15525 if(this.labelWidth > 12){
15526 labelCfg.style = "width: " + this.labelWidth + 'px';
15529 if(this.labelWidth < 13 && this.labelmd == 0){
15530 this.labelmd = this.labelWidth;
15533 if(this.labellg > 0){
15534 labelCfg.cls += ' col-lg-' + this.labellg;
15535 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15538 if(this.labelmd > 0){
15539 labelCfg.cls += ' col-md-' + this.labelmd;
15540 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15543 if(this.labelsm > 0){
15544 labelCfg.cls += ' col-sm-' + this.labelsm;
15545 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15548 if(this.labelxs > 0){
15549 labelCfg.cls += ' col-xs-' + this.labelxs;
15550 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15554 } else if ( this.fieldLabel.length) {
15558 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15559 tooltip : 'This field is required'
15563 cls : 'control-label',
15564 html : this.fieldLabel
15575 if(this.indicatorpos == 'right'){
15579 cls : 'control-label',
15580 html : this.fieldLabel,
15584 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15585 tooltip : 'This field is required'
15602 var settings = this;
15604 ['xs','sm','md','lg'].map(function(size){
15605 if (settings[size]) {
15606 cfg.cls += ' col-' + size + '-' + settings[size];
15613 initTouchView : function()
15615 this.renderTouchView();
15617 this.touchViewEl.on('scroll', function(){
15618 this.el.dom.scrollTop = 0;
15621 this.originalValue = this.getValue();
15623 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15625 this.inputEl().on("click", this.showTouchView, this);
15626 if (this.triggerEl) {
15627 this.triggerEl.on("click", this.showTouchView, this);
15631 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15632 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15634 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15636 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15637 this.store.on('load', this.onTouchViewLoad, this);
15638 this.store.on('loadexception', this.onTouchViewLoadException, this);
15640 if(this.hiddenName){
15642 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15644 this.hiddenField.dom.value =
15645 this.hiddenValue !== undefined ? this.hiddenValue :
15646 this.value !== undefined ? this.value : '';
15648 this.el.dom.removeAttribute('name');
15649 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15653 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15654 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15657 if(this.removable && !this.multiple){
15658 var close = this.closeTriggerEl();
15660 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15661 close.on('click', this.removeBtnClick, this, close);
15665 * fix the bug in Safari iOS8
15667 this.inputEl().on("focus", function(e){
15668 document.activeElement.blur();
15671 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15678 renderTouchView : function()
15680 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15681 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15683 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15684 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15686 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15687 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15688 this.touchViewBodyEl.setStyle('overflow', 'auto');
15690 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15691 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15693 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15694 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15698 showTouchView : function()
15704 this.touchViewHeaderEl.hide();
15706 if(this.modalTitle.length){
15707 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15708 this.touchViewHeaderEl.show();
15711 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15712 this.touchViewEl.show();
15714 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15716 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15717 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15719 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15721 if(this.modalTitle.length){
15722 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15725 this.touchViewBodyEl.setHeight(bodyHeight);
15729 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15731 this.touchViewEl.addClass('in');
15734 if(this._touchViewMask){
15735 Roo.get(document.body).addClass("x-body-masked");
15736 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15737 this._touchViewMask.setStyle('z-index', 10000);
15738 this._touchViewMask.addClass('show');
15741 this.doTouchViewQuery();
15745 hideTouchView : function()
15747 this.touchViewEl.removeClass('in');
15751 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15753 this.touchViewEl.setStyle('display', 'none');
15756 if(this._touchViewMask){
15757 this._touchViewMask.removeClass('show');
15758 Roo.get(document.body).removeClass("x-body-masked");
15762 setTouchViewValue : function()
15769 Roo.each(this.tickItems, function(o){
15774 this.hideTouchView();
15777 doTouchViewQuery : function()
15786 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15790 if(!this.alwaysQuery || this.mode == 'local'){
15791 this.onTouchViewLoad();
15798 onTouchViewBeforeLoad : function(combo,opts)
15804 onTouchViewLoad : function()
15806 if(this.store.getCount() < 1){
15807 this.onTouchViewEmptyResults();
15811 this.clearTouchView();
15813 var rawValue = this.getRawValue();
15815 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15817 this.tickItems = [];
15819 this.store.data.each(function(d, rowIndex){
15820 var row = this.touchViewListGroup.createChild(template);
15822 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15823 row.addClass(d.data.cls);
15826 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15829 html : d.data[this.displayField]
15832 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15833 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15836 row.removeClass('selected');
15837 if(!this.multiple && this.valueField &&
15838 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15841 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15842 row.addClass('selected');
15845 if(this.multiple && this.valueField &&
15846 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15850 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15851 this.tickItems.push(d.data);
15854 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15858 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15860 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15862 if(this.modalTitle.length){
15863 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15866 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15868 if(this.mobile_restrict_height && listHeight < bodyHeight){
15869 this.touchViewBodyEl.setHeight(listHeight);
15874 if(firstChecked && listHeight > bodyHeight){
15875 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15880 onTouchViewLoadException : function()
15882 this.hideTouchView();
15885 onTouchViewEmptyResults : function()
15887 this.clearTouchView();
15889 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15891 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15895 clearTouchView : function()
15897 this.touchViewListGroup.dom.innerHTML = '';
15900 onTouchViewClick : function(e, el, o)
15902 e.preventDefault();
15905 var rowIndex = o.rowIndex;
15907 var r = this.store.getAt(rowIndex);
15909 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15911 if(!this.multiple){
15912 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15913 c.dom.removeAttribute('checked');
15916 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15918 this.setFromData(r.data);
15920 var close = this.closeTriggerEl();
15926 this.hideTouchView();
15928 this.fireEvent('select', this, r, rowIndex);
15933 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15934 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15935 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15939 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15940 this.addItem(r.data);
15941 this.tickItems.push(r.data);
15945 getAutoCreateNativeIOS : function()
15948 cls: 'form-group' //input-group,
15953 cls : 'roo-ios-select'
15957 combobox.name = this.name;
15960 if (this.disabled) {
15961 combobox.disabled = true;
15964 var settings = this;
15966 ['xs','sm','md','lg'].map(function(size){
15967 if (settings[size]) {
15968 cfg.cls += ' col-' + size + '-' + settings[size];
15978 initIOSView : function()
15980 this.store.on('load', this.onIOSViewLoad, this);
15985 onIOSViewLoad : function()
15987 if(this.store.getCount() < 1){
15991 this.clearIOSView();
15993 if(this.allowBlank) {
15995 var default_text = '-- SELECT --';
15997 if(this.placeholder.length){
15998 default_text = this.placeholder;
16001 if(this.emptyTitle.length){
16002 default_text += ' - ' + this.emptyTitle + ' -';
16005 var opt = this.inputEl().createChild({
16008 html : default_text
16012 o[this.valueField] = 0;
16013 o[this.displayField] = default_text;
16015 this.ios_options.push({
16022 this.store.data.each(function(d, rowIndex){
16026 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16027 html = d.data[this.displayField];
16032 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16033 value = d.data[this.valueField];
16042 if(this.value == d.data[this.valueField]){
16043 option['selected'] = true;
16046 var opt = this.inputEl().createChild(option);
16048 this.ios_options.push({
16055 this.inputEl().on('change', function(){
16056 this.fireEvent('select', this);
16061 clearIOSView: function()
16063 this.inputEl().dom.innerHTML = '';
16065 this.ios_options = [];
16068 setIOSValue: function(v)
16072 if(!this.ios_options){
16076 Roo.each(this.ios_options, function(opts){
16078 opts.el.dom.removeAttribute('selected');
16080 if(opts.data[this.valueField] != v){
16084 opts.el.dom.setAttribute('selected', true);
16090 * @cfg {Boolean} grow
16094 * @cfg {Number} growMin
16098 * @cfg {Number} growMax
16107 Roo.apply(Roo.bootstrap.ComboBox, {
16111 cls: 'modal-header',
16133 cls: 'list-group-item',
16137 cls: 'roo-combobox-list-group-item-value'
16141 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16155 listItemCheckbox : {
16157 cls: 'list-group-item',
16161 cls: 'roo-combobox-list-group-item-value'
16165 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16181 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16186 cls: 'modal-footer',
16194 cls: 'col-xs-6 text-left',
16197 cls: 'btn btn-danger roo-touch-view-cancel',
16203 cls: 'col-xs-6 text-right',
16206 cls: 'btn btn-success roo-touch-view-ok',
16217 Roo.apply(Roo.bootstrap.ComboBox, {
16219 touchViewTemplate : {
16221 cls: 'modal fade roo-combobox-touch-view',
16225 cls: 'modal-dialog',
16226 style : 'position:fixed', // we have to fix position....
16230 cls: 'modal-content',
16232 Roo.bootstrap.ComboBox.header,
16233 Roo.bootstrap.ComboBox.body,
16234 Roo.bootstrap.ComboBox.footer
16243 * Ext JS Library 1.1.1
16244 * Copyright(c) 2006-2007, Ext JS, LLC.
16246 * Originally Released Under LGPL - original licence link has changed is not relivant.
16249 * <script type="text/javascript">
16254 * @extends Roo.util.Observable
16255 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
16256 * This class also supports single and multi selection modes. <br>
16257 * Create a data model bound view:
16259 var store = new Roo.data.Store(...);
16261 var view = new Roo.View({
16263 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
16265 singleSelect: true,
16266 selectedClass: "ydataview-selected",
16270 // listen for node click?
16271 view.on("click", function(vw, index, node, e){
16272 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16276 dataModel.load("foobar.xml");
16278 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16280 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16281 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16283 * Note: old style constructor is still suported (container, template, config)
16286 * Create a new View
16287 * @param {Object} config The config object
16290 Roo.View = function(config, depreciated_tpl, depreciated_config){
16292 this.parent = false;
16294 if (typeof(depreciated_tpl) == 'undefined') {
16295 // new way.. - universal constructor.
16296 Roo.apply(this, config);
16297 this.el = Roo.get(this.el);
16300 this.el = Roo.get(config);
16301 this.tpl = depreciated_tpl;
16302 Roo.apply(this, depreciated_config);
16304 this.wrapEl = this.el.wrap().wrap();
16305 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16308 if(typeof(this.tpl) == "string"){
16309 this.tpl = new Roo.Template(this.tpl);
16311 // support xtype ctors..
16312 this.tpl = new Roo.factory(this.tpl, Roo);
16316 this.tpl.compile();
16321 * @event beforeclick
16322 * Fires before a click is processed. Returns false to cancel the default action.
16323 * @param {Roo.View} this
16324 * @param {Number} index The index of the target node
16325 * @param {HTMLElement} node The target node
16326 * @param {Roo.EventObject} e The raw event object
16328 "beforeclick" : true,
16331 * Fires when a template node is clicked.
16332 * @param {Roo.View} this
16333 * @param {Number} index The index of the target node
16334 * @param {HTMLElement} node The target node
16335 * @param {Roo.EventObject} e The raw event object
16340 * Fires when a template node is double clicked.
16341 * @param {Roo.View} this
16342 * @param {Number} index The index of the target node
16343 * @param {HTMLElement} node The target node
16344 * @param {Roo.EventObject} e The raw event object
16348 * @event contextmenu
16349 * Fires when a template node is right clicked.
16350 * @param {Roo.View} this
16351 * @param {Number} index The index of the target node
16352 * @param {HTMLElement} node The target node
16353 * @param {Roo.EventObject} e The raw event object
16355 "contextmenu" : true,
16357 * @event selectionchange
16358 * Fires when the selected nodes change.
16359 * @param {Roo.View} this
16360 * @param {Array} selections Array of the selected nodes
16362 "selectionchange" : true,
16365 * @event beforeselect
16366 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16367 * @param {Roo.View} this
16368 * @param {HTMLElement} node The node to be selected
16369 * @param {Array} selections Array of currently selected nodes
16371 "beforeselect" : true,
16373 * @event preparedata
16374 * Fires on every row to render, to allow you to change the data.
16375 * @param {Roo.View} this
16376 * @param {Object} data to be rendered (change this)
16378 "preparedata" : true
16386 "click": this.onClick,
16387 "dblclick": this.onDblClick,
16388 "contextmenu": this.onContextMenu,
16392 this.selections = [];
16394 this.cmp = new Roo.CompositeElementLite([]);
16396 this.store = Roo.factory(this.store, Roo.data);
16397 this.setStore(this.store, true);
16400 if ( this.footer && this.footer.xtype) {
16402 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16404 this.footer.dataSource = this.store;
16405 this.footer.container = fctr;
16406 this.footer = Roo.factory(this.footer, Roo);
16407 fctr.insertFirst(this.el);
16409 // this is a bit insane - as the paging toolbar seems to detach the el..
16410 // dom.parentNode.parentNode.parentNode
16411 // they get detached?
16415 Roo.View.superclass.constructor.call(this);
16420 Roo.extend(Roo.View, Roo.util.Observable, {
16423 * @cfg {Roo.data.Store} store Data store to load data from.
16428 * @cfg {String|Roo.Element} el The container element.
16433 * @cfg {String|Roo.Template} tpl The template used by this View
16437 * @cfg {String} dataName the named area of the template to use as the data area
16438 * Works with domtemplates roo-name="name"
16442 * @cfg {String} selectedClass The css class to add to selected nodes
16444 selectedClass : "x-view-selected",
16446 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16451 * @cfg {String} text to display on mask (default Loading)
16455 * @cfg {Boolean} multiSelect Allow multiple selection
16457 multiSelect : false,
16459 * @cfg {Boolean} singleSelect Allow single selection
16461 singleSelect: false,
16464 * @cfg {Boolean} toggleSelect - selecting
16466 toggleSelect : false,
16469 * @cfg {Boolean} tickable - selecting
16474 * Returns the element this view is bound to.
16475 * @return {Roo.Element}
16477 getEl : function(){
16478 return this.wrapEl;
16484 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16486 refresh : function(){
16487 //Roo.log('refresh');
16490 // if we are using something like 'domtemplate', then
16491 // the what gets used is:
16492 // t.applySubtemplate(NAME, data, wrapping data..)
16493 // the outer template then get' applied with
16494 // the store 'extra data'
16495 // and the body get's added to the
16496 // roo-name="data" node?
16497 // <span class='roo-tpl-{name}'></span> ?????
16501 this.clearSelections();
16502 this.el.update("");
16504 var records = this.store.getRange();
16505 if(records.length < 1) {
16507 // is this valid?? = should it render a template??
16509 this.el.update(this.emptyText);
16513 if (this.dataName) {
16514 this.el.update(t.apply(this.store.meta)); //????
16515 el = this.el.child('.roo-tpl-' + this.dataName);
16518 for(var i = 0, len = records.length; i < len; i++){
16519 var data = this.prepareData(records[i].data, i, records[i]);
16520 this.fireEvent("preparedata", this, data, i, records[i]);
16522 var d = Roo.apply({}, data);
16525 Roo.apply(d, {'roo-id' : Roo.id()});
16529 Roo.each(this.parent.item, function(item){
16530 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16533 Roo.apply(d, {'roo-data-checked' : 'checked'});
16537 html[html.length] = Roo.util.Format.trim(
16539 t.applySubtemplate(this.dataName, d, this.store.meta) :
16546 el.update(html.join(""));
16547 this.nodes = el.dom.childNodes;
16548 this.updateIndexes(0);
16553 * Function to override to reformat the data that is sent to
16554 * the template for each node.
16555 * DEPRICATED - use the preparedata event handler.
16556 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16557 * a JSON object for an UpdateManager bound view).
16559 prepareData : function(data, index, record)
16561 this.fireEvent("preparedata", this, data, index, record);
16565 onUpdate : function(ds, record){
16566 // Roo.log('on update');
16567 this.clearSelections();
16568 var index = this.store.indexOf(record);
16569 var n = this.nodes[index];
16570 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16571 n.parentNode.removeChild(n);
16572 this.updateIndexes(index, index);
16578 onAdd : function(ds, records, index)
16580 //Roo.log(['on Add', ds, records, index] );
16581 this.clearSelections();
16582 if(this.nodes.length == 0){
16586 var n = this.nodes[index];
16587 for(var i = 0, len = records.length; i < len; i++){
16588 var d = this.prepareData(records[i].data, i, records[i]);
16590 this.tpl.insertBefore(n, d);
16593 this.tpl.append(this.el, d);
16596 this.updateIndexes(index);
16599 onRemove : function(ds, record, index){
16600 // Roo.log('onRemove');
16601 this.clearSelections();
16602 var el = this.dataName ?
16603 this.el.child('.roo-tpl-' + this.dataName) :
16606 el.dom.removeChild(this.nodes[index]);
16607 this.updateIndexes(index);
16611 * Refresh an individual node.
16612 * @param {Number} index
16614 refreshNode : function(index){
16615 this.onUpdate(this.store, this.store.getAt(index));
16618 updateIndexes : function(startIndex, endIndex){
16619 var ns = this.nodes;
16620 startIndex = startIndex || 0;
16621 endIndex = endIndex || ns.length - 1;
16622 for(var i = startIndex; i <= endIndex; i++){
16623 ns[i].nodeIndex = i;
16628 * Changes the data store this view uses and refresh the view.
16629 * @param {Store} store
16631 setStore : function(store, initial){
16632 if(!initial && this.store){
16633 this.store.un("datachanged", this.refresh);
16634 this.store.un("add", this.onAdd);
16635 this.store.un("remove", this.onRemove);
16636 this.store.un("update", this.onUpdate);
16637 this.store.un("clear", this.refresh);
16638 this.store.un("beforeload", this.onBeforeLoad);
16639 this.store.un("load", this.onLoad);
16640 this.store.un("loadexception", this.onLoad);
16644 store.on("datachanged", this.refresh, this);
16645 store.on("add", this.onAdd, this);
16646 store.on("remove", this.onRemove, this);
16647 store.on("update", this.onUpdate, this);
16648 store.on("clear", this.refresh, this);
16649 store.on("beforeload", this.onBeforeLoad, this);
16650 store.on("load", this.onLoad, this);
16651 store.on("loadexception", this.onLoad, this);
16659 * onbeforeLoad - masks the loading area.
16662 onBeforeLoad : function(store,opts)
16664 //Roo.log('onBeforeLoad');
16666 this.el.update("");
16668 this.el.mask(this.mask ? this.mask : "Loading" );
16670 onLoad : function ()
16677 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16678 * @param {HTMLElement} node
16679 * @return {HTMLElement} The template node
16681 findItemFromChild : function(node){
16682 var el = this.dataName ?
16683 this.el.child('.roo-tpl-' + this.dataName,true) :
16686 if(!node || node.parentNode == el){
16689 var p = node.parentNode;
16690 while(p && p != el){
16691 if(p.parentNode == el){
16700 onClick : function(e){
16701 var item = this.findItemFromChild(e.getTarget());
16703 var index = this.indexOf(item);
16704 if(this.onItemClick(item, index, e) !== false){
16705 this.fireEvent("click", this, index, item, e);
16708 this.clearSelections();
16713 onContextMenu : function(e){
16714 var item = this.findItemFromChild(e.getTarget());
16716 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16721 onDblClick : function(e){
16722 var item = this.findItemFromChild(e.getTarget());
16724 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16728 onItemClick : function(item, index, e)
16730 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16733 if (this.toggleSelect) {
16734 var m = this.isSelected(item) ? 'unselect' : 'select';
16737 _t[m](item, true, false);
16740 if(this.multiSelect || this.singleSelect){
16741 if(this.multiSelect && e.shiftKey && this.lastSelection){
16742 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16744 this.select(item, this.multiSelect && e.ctrlKey);
16745 this.lastSelection = item;
16748 if(!this.tickable){
16749 e.preventDefault();
16757 * Get the number of selected nodes.
16760 getSelectionCount : function(){
16761 return this.selections.length;
16765 * Get the currently selected nodes.
16766 * @return {Array} An array of HTMLElements
16768 getSelectedNodes : function(){
16769 return this.selections;
16773 * Get the indexes of the selected nodes.
16776 getSelectedIndexes : function(){
16777 var indexes = [], s = this.selections;
16778 for(var i = 0, len = s.length; i < len; i++){
16779 indexes.push(s[i].nodeIndex);
16785 * Clear all selections
16786 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16788 clearSelections : function(suppressEvent){
16789 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16790 this.cmp.elements = this.selections;
16791 this.cmp.removeClass(this.selectedClass);
16792 this.selections = [];
16793 if(!suppressEvent){
16794 this.fireEvent("selectionchange", this, this.selections);
16800 * Returns true if the passed node is selected
16801 * @param {HTMLElement/Number} node The node or node index
16802 * @return {Boolean}
16804 isSelected : function(node){
16805 var s = this.selections;
16809 node = this.getNode(node);
16810 return s.indexOf(node) !== -1;
16815 * @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
16816 * @param {Boolean} keepExisting (optional) true to keep existing selections
16817 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16819 select : function(nodeInfo, keepExisting, suppressEvent){
16820 if(nodeInfo instanceof Array){
16822 this.clearSelections(true);
16824 for(var i = 0, len = nodeInfo.length; i < len; i++){
16825 this.select(nodeInfo[i], true, true);
16829 var node = this.getNode(nodeInfo);
16830 if(!node || this.isSelected(node)){
16831 return; // already selected.
16834 this.clearSelections(true);
16837 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16838 Roo.fly(node).addClass(this.selectedClass);
16839 this.selections.push(node);
16840 if(!suppressEvent){
16841 this.fireEvent("selectionchange", this, this.selections);
16849 * @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
16850 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16851 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16853 unselect : function(nodeInfo, keepExisting, suppressEvent)
16855 if(nodeInfo instanceof Array){
16856 Roo.each(this.selections, function(s) {
16857 this.unselect(s, nodeInfo);
16861 var node = this.getNode(nodeInfo);
16862 if(!node || !this.isSelected(node)){
16863 //Roo.log("not selected");
16864 return; // not selected.
16868 Roo.each(this.selections, function(s) {
16870 Roo.fly(node).removeClass(this.selectedClass);
16877 this.selections= ns;
16878 this.fireEvent("selectionchange", this, this.selections);
16882 * Gets a template node.
16883 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16884 * @return {HTMLElement} The node or null if it wasn't found
16886 getNode : function(nodeInfo){
16887 if(typeof nodeInfo == "string"){
16888 return document.getElementById(nodeInfo);
16889 }else if(typeof nodeInfo == "number"){
16890 return this.nodes[nodeInfo];
16896 * Gets a range template nodes.
16897 * @param {Number} startIndex
16898 * @param {Number} endIndex
16899 * @return {Array} An array of nodes
16901 getNodes : function(start, end){
16902 var ns = this.nodes;
16903 start = start || 0;
16904 end = typeof end == "undefined" ? ns.length - 1 : end;
16907 for(var i = start; i <= end; i++){
16911 for(var i = start; i >= end; i--){
16919 * Finds the index of the passed node
16920 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16921 * @return {Number} The index of the node or -1
16923 indexOf : function(node){
16924 node = this.getNode(node);
16925 if(typeof node.nodeIndex == "number"){
16926 return node.nodeIndex;
16928 var ns = this.nodes;
16929 for(var i = 0, len = ns.length; i < len; i++){
16940 * based on jquery fullcalendar
16944 Roo.bootstrap = Roo.bootstrap || {};
16946 * @class Roo.bootstrap.Calendar
16947 * @extends Roo.bootstrap.Component
16948 * Bootstrap Calendar class
16949 * @cfg {Boolean} loadMask (true|false) default false
16950 * @cfg {Object} header generate the user specific header of the calendar, default false
16953 * Create a new Container
16954 * @param {Object} config The config object
16959 Roo.bootstrap.Calendar = function(config){
16960 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16964 * Fires when a date is selected
16965 * @param {DatePicker} this
16966 * @param {Date} date The selected date
16970 * @event monthchange
16971 * Fires when the displayed month changes
16972 * @param {DatePicker} this
16973 * @param {Date} date The selected month
16975 'monthchange': true,
16977 * @event evententer
16978 * Fires when mouse over an event
16979 * @param {Calendar} this
16980 * @param {event} Event
16982 'evententer': true,
16984 * @event eventleave
16985 * Fires when the mouse leaves an
16986 * @param {Calendar} this
16989 'eventleave': true,
16991 * @event eventclick
16992 * Fires when the mouse click an
16993 * @param {Calendar} this
17002 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
17005 * @cfg {Number} startDay
17006 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
17014 getAutoCreate : function(){
17017 var fc_button = function(name, corner, style, content ) {
17018 return Roo.apply({},{
17020 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
17022 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
17025 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
17036 style : 'width:100%',
17043 cls : 'fc-header-left',
17045 fc_button('prev', 'left', 'arrow', '‹' ),
17046 fc_button('next', 'right', 'arrow', '›' ),
17047 { tag: 'span', cls: 'fc-header-space' },
17048 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
17056 cls : 'fc-header-center',
17060 cls: 'fc-header-title',
17063 html : 'month / year'
17071 cls : 'fc-header-right',
17073 /* fc_button('month', 'left', '', 'month' ),
17074 fc_button('week', '', '', 'week' ),
17075 fc_button('day', 'right', '', 'day' )
17087 header = this.header;
17090 var cal_heads = function() {
17092 // fixme - handle this.
17094 for (var i =0; i < Date.dayNames.length; i++) {
17095 var d = Date.dayNames[i];
17098 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17099 html : d.substring(0,3)
17103 ret[0].cls += ' fc-first';
17104 ret[6].cls += ' fc-last';
17107 var cal_cell = function(n) {
17110 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17115 cls: 'fc-day-number',
17119 cls: 'fc-day-content',
17123 style: 'position: relative;' // height: 17px;
17135 var cal_rows = function() {
17138 for (var r = 0; r < 6; r++) {
17145 for (var i =0; i < Date.dayNames.length; i++) {
17146 var d = Date.dayNames[i];
17147 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17150 row.cn[0].cls+=' fc-first';
17151 row.cn[0].cn[0].style = 'min-height:90px';
17152 row.cn[6].cls+=' fc-last';
17156 ret[0].cls += ' fc-first';
17157 ret[4].cls += ' fc-prev-last';
17158 ret[5].cls += ' fc-last';
17165 cls: 'fc-border-separate',
17166 style : 'width:100%',
17174 cls : 'fc-first fc-last',
17192 cls : 'fc-content',
17193 style : "position: relative;",
17196 cls : 'fc-view fc-view-month fc-grid',
17197 style : 'position: relative',
17198 unselectable : 'on',
17201 cls : 'fc-event-container',
17202 style : 'position:absolute;z-index:8;top:0;left:0;'
17220 initEvents : function()
17223 throw "can not find store for calendar";
17229 style: "text-align:center",
17233 style: "background-color:white;width:50%;margin:250 auto",
17237 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
17248 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17250 var size = this.el.select('.fc-content', true).first().getSize();
17251 this.maskEl.setSize(size.width, size.height);
17252 this.maskEl.enableDisplayMode("block");
17253 if(!this.loadMask){
17254 this.maskEl.hide();
17257 this.store = Roo.factory(this.store, Roo.data);
17258 this.store.on('load', this.onLoad, this);
17259 this.store.on('beforeload', this.onBeforeLoad, this);
17263 this.cells = this.el.select('.fc-day',true);
17264 //Roo.log(this.cells);
17265 this.textNodes = this.el.query('.fc-day-number');
17266 this.cells.addClassOnOver('fc-state-hover');
17268 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17269 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17270 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17271 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17273 this.on('monthchange', this.onMonthChange, this);
17275 this.update(new Date().clearTime());
17278 resize : function() {
17279 var sz = this.el.getSize();
17281 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17282 this.el.select('.fc-day-content div',true).setHeight(34);
17287 showPrevMonth : function(e){
17288 this.update(this.activeDate.add("mo", -1));
17290 showToday : function(e){
17291 this.update(new Date().clearTime());
17294 showNextMonth : function(e){
17295 this.update(this.activeDate.add("mo", 1));
17299 showPrevYear : function(){
17300 this.update(this.activeDate.add("y", -1));
17304 showNextYear : function(){
17305 this.update(this.activeDate.add("y", 1));
17310 update : function(date)
17312 var vd = this.activeDate;
17313 this.activeDate = date;
17314 // if(vd && this.el){
17315 // var t = date.getTime();
17316 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17317 // Roo.log('using add remove');
17319 // this.fireEvent('monthchange', this, date);
17321 // this.cells.removeClass("fc-state-highlight");
17322 // this.cells.each(function(c){
17323 // if(c.dateValue == t){
17324 // c.addClass("fc-state-highlight");
17325 // setTimeout(function(){
17326 // try{c.dom.firstChild.focus();}catch(e){}
17336 var days = date.getDaysInMonth();
17338 var firstOfMonth = date.getFirstDateOfMonth();
17339 var startingPos = firstOfMonth.getDay()-this.startDay;
17341 if(startingPos < this.startDay){
17345 var pm = date.add(Date.MONTH, -1);
17346 var prevStart = pm.getDaysInMonth()-startingPos;
17348 this.cells = this.el.select('.fc-day',true);
17349 this.textNodes = this.el.query('.fc-day-number');
17350 this.cells.addClassOnOver('fc-state-hover');
17352 var cells = this.cells.elements;
17353 var textEls = this.textNodes;
17355 Roo.each(cells, function(cell){
17356 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17359 days += startingPos;
17361 // convert everything to numbers so it's fast
17362 var day = 86400000;
17363 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17366 //Roo.log(prevStart);
17368 var today = new Date().clearTime().getTime();
17369 var sel = date.clearTime().getTime();
17370 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17371 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17372 var ddMatch = this.disabledDatesRE;
17373 var ddText = this.disabledDatesText;
17374 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17375 var ddaysText = this.disabledDaysText;
17376 var format = this.format;
17378 var setCellClass = function(cal, cell){
17382 //Roo.log('set Cell Class');
17384 var t = d.getTime();
17388 cell.dateValue = t;
17390 cell.className += " fc-today";
17391 cell.className += " fc-state-highlight";
17392 cell.title = cal.todayText;
17395 // disable highlight in other month..
17396 //cell.className += " fc-state-highlight";
17401 cell.className = " fc-state-disabled";
17402 cell.title = cal.minText;
17406 cell.className = " fc-state-disabled";
17407 cell.title = cal.maxText;
17411 if(ddays.indexOf(d.getDay()) != -1){
17412 cell.title = ddaysText;
17413 cell.className = " fc-state-disabled";
17416 if(ddMatch && format){
17417 var fvalue = d.dateFormat(format);
17418 if(ddMatch.test(fvalue)){
17419 cell.title = ddText.replace("%0", fvalue);
17420 cell.className = " fc-state-disabled";
17424 if (!cell.initialClassName) {
17425 cell.initialClassName = cell.dom.className;
17428 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17433 for(; i < startingPos; i++) {
17434 textEls[i].innerHTML = (++prevStart);
17435 d.setDate(d.getDate()+1);
17437 cells[i].className = "fc-past fc-other-month";
17438 setCellClass(this, cells[i]);
17443 for(; i < days; i++){
17444 intDay = i - startingPos + 1;
17445 textEls[i].innerHTML = (intDay);
17446 d.setDate(d.getDate()+1);
17448 cells[i].className = ''; // "x-date-active";
17449 setCellClass(this, cells[i]);
17453 for(; i < 42; i++) {
17454 textEls[i].innerHTML = (++extraDays);
17455 d.setDate(d.getDate()+1);
17457 cells[i].className = "fc-future fc-other-month";
17458 setCellClass(this, cells[i]);
17461 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17463 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17465 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17466 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17468 if(totalRows != 6){
17469 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17470 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17473 this.fireEvent('monthchange', this, date);
17477 if(!this.internalRender){
17478 var main = this.el.dom.firstChild;
17479 var w = main.offsetWidth;
17480 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17481 Roo.fly(main).setWidth(w);
17482 this.internalRender = true;
17483 // opera does not respect the auto grow header center column
17484 // then, after it gets a width opera refuses to recalculate
17485 // without a second pass
17486 if(Roo.isOpera && !this.secondPass){
17487 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17488 this.secondPass = true;
17489 this.update.defer(10, this, [date]);
17496 findCell : function(dt) {
17497 dt = dt.clearTime().getTime();
17499 this.cells.each(function(c){
17500 //Roo.log("check " +c.dateValue + '?=' + dt);
17501 if(c.dateValue == dt){
17511 findCells : function(ev) {
17512 var s = ev.start.clone().clearTime().getTime();
17514 var e= ev.end.clone().clearTime().getTime();
17517 this.cells.each(function(c){
17518 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17520 if(c.dateValue > e){
17523 if(c.dateValue < s){
17532 // findBestRow: function(cells)
17536 // for (var i =0 ; i < cells.length;i++) {
17537 // ret = Math.max(cells[i].rows || 0,ret);
17544 addItem : function(ev)
17546 // look for vertical location slot in
17547 var cells = this.findCells(ev);
17549 // ev.row = this.findBestRow(cells);
17551 // work out the location.
17555 for(var i =0; i < cells.length; i++) {
17557 cells[i].row = cells[0].row;
17560 cells[i].row = cells[i].row + 1;
17570 if (crow.start.getY() == cells[i].getY()) {
17572 crow.end = cells[i];
17589 cells[0].events.push(ev);
17591 this.calevents.push(ev);
17594 clearEvents: function() {
17596 if(!this.calevents){
17600 Roo.each(this.cells.elements, function(c){
17606 Roo.each(this.calevents, function(e) {
17607 Roo.each(e.els, function(el) {
17608 el.un('mouseenter' ,this.onEventEnter, this);
17609 el.un('mouseleave' ,this.onEventLeave, this);
17614 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17620 renderEvents: function()
17624 this.cells.each(function(c) {
17633 if(c.row != c.events.length){
17634 r = 4 - (4 - (c.row - c.events.length));
17637 c.events = ev.slice(0, r);
17638 c.more = ev.slice(r);
17640 if(c.more.length && c.more.length == 1){
17641 c.events.push(c.more.pop());
17644 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17648 this.cells.each(function(c) {
17650 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17653 for (var e = 0; e < c.events.length; e++){
17654 var ev = c.events[e];
17655 var rows = ev.rows;
17657 for(var i = 0; i < rows.length; i++) {
17659 // how many rows should it span..
17662 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17663 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17665 unselectable : "on",
17668 cls: 'fc-event-inner',
17672 // cls: 'fc-event-time',
17673 // html : cells.length > 1 ? '' : ev.time
17677 cls: 'fc-event-title',
17678 html : String.format('{0}', ev.title)
17685 cls: 'ui-resizable-handle ui-resizable-e',
17686 html : '  '
17693 cfg.cls += ' fc-event-start';
17695 if ((i+1) == rows.length) {
17696 cfg.cls += ' fc-event-end';
17699 var ctr = _this.el.select('.fc-event-container',true).first();
17700 var cg = ctr.createChild(cfg);
17702 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17703 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17705 var r = (c.more.length) ? 1 : 0;
17706 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17707 cg.setWidth(ebox.right - sbox.x -2);
17709 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17710 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17711 cg.on('click', _this.onEventClick, _this, ev);
17722 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17723 style : 'position: absolute',
17724 unselectable : "on",
17727 cls: 'fc-event-inner',
17731 cls: 'fc-event-title',
17739 cls: 'ui-resizable-handle ui-resizable-e',
17740 html : '  '
17746 var ctr = _this.el.select('.fc-event-container',true).first();
17747 var cg = ctr.createChild(cfg);
17749 var sbox = c.select('.fc-day-content',true).first().getBox();
17750 var ebox = c.select('.fc-day-content',true).first().getBox();
17752 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17753 cg.setWidth(ebox.right - sbox.x -2);
17755 cg.on('click', _this.onMoreEventClick, _this, c.more);
17765 onEventEnter: function (e, el,event,d) {
17766 this.fireEvent('evententer', this, el, event);
17769 onEventLeave: function (e, el,event,d) {
17770 this.fireEvent('eventleave', this, el, event);
17773 onEventClick: function (e, el,event,d) {
17774 this.fireEvent('eventclick', this, el, event);
17777 onMonthChange: function () {
17781 onMoreEventClick: function(e, el, more)
17785 this.calpopover.placement = 'right';
17786 this.calpopover.setTitle('More');
17788 this.calpopover.setContent('');
17790 var ctr = this.calpopover.el.select('.popover-content', true).first();
17792 Roo.each(more, function(m){
17794 cls : 'fc-event-hori fc-event-draggable',
17797 var cg = ctr.createChild(cfg);
17799 cg.on('click', _this.onEventClick, _this, m);
17802 this.calpopover.show(el);
17807 onLoad: function ()
17809 this.calevents = [];
17812 if(this.store.getCount() > 0){
17813 this.store.data.each(function(d){
17816 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17817 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17818 time : d.data.start_time,
17819 title : d.data.title,
17820 description : d.data.description,
17821 venue : d.data.venue
17826 this.renderEvents();
17828 if(this.calevents.length && this.loadMask){
17829 this.maskEl.hide();
17833 onBeforeLoad: function()
17835 this.clearEvents();
17837 this.maskEl.show();
17851 * @class Roo.bootstrap.Popover
17852 * @extends Roo.bootstrap.Component
17853 * Bootstrap Popover class
17854 * @cfg {String} html contents of the popover (or false to use children..)
17855 * @cfg {String} title of popover (or false to hide)
17856 * @cfg {String} placement how it is placed
17857 * @cfg {String} trigger click || hover (or false to trigger manually)
17858 * @cfg {String} over what (parent or false to trigger manually.)
17859 * @cfg {Number} delay - delay before showing
17862 * Create a new Popover
17863 * @param {Object} config The config object
17866 Roo.bootstrap.Popover = function(config){
17867 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17873 * After the popover show
17875 * @param {Roo.bootstrap.Popover} this
17880 * After the popover hide
17882 * @param {Roo.bootstrap.Popover} this
17888 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17890 title: 'Fill in a title',
17893 placement : 'right',
17894 trigger : 'hover', // hover
17900 can_build_overlaid : false,
17902 getChildContainer : function()
17904 return this.el.select('.popover-content',true).first();
17907 getAutoCreate : function(){
17910 cls : 'popover roo-dynamic',
17911 style: 'display:block',
17917 cls : 'popover-inner',
17921 cls: 'popover-title popover-header',
17925 cls : 'popover-content popover-body',
17936 setTitle: function(str)
17939 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17941 setContent: function(str)
17944 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17946 // as it get's added to the bottom of the page.
17947 onRender : function(ct, position)
17949 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17951 var cfg = Roo.apply({}, this.getAutoCreate());
17955 cfg.cls += ' ' + this.cls;
17958 cfg.style = this.style;
17960 //Roo.log("adding to ");
17961 this.el = Roo.get(document.body).createChild(cfg, position);
17962 // Roo.log(this.el);
17967 initEvents : function()
17969 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17970 this.el.enableDisplayMode('block');
17972 if (this.over === false) {
17975 if (this.triggers === false) {
17978 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17979 var triggers = this.trigger ? this.trigger.split(' ') : [];
17980 Roo.each(triggers, function(trigger) {
17982 if (trigger == 'click') {
17983 on_el.on('click', this.toggle, this);
17984 } else if (trigger != 'manual') {
17985 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17986 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17988 on_el.on(eventIn ,this.enter, this);
17989 on_el.on(eventOut, this.leave, this);
18000 toggle : function () {
18001 this.hoverState == 'in' ? this.leave() : this.enter();
18004 enter : function () {
18006 clearTimeout(this.timeout);
18008 this.hoverState = 'in';
18010 if (!this.delay || !this.delay.show) {
18015 this.timeout = setTimeout(function () {
18016 if (_t.hoverState == 'in') {
18019 }, this.delay.show)
18022 leave : function() {
18023 clearTimeout(this.timeout);
18025 this.hoverState = 'out';
18027 if (!this.delay || !this.delay.hide) {
18032 this.timeout = setTimeout(function () {
18033 if (_t.hoverState == 'out') {
18036 }, this.delay.hide)
18039 show : function (on_el)
18042 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18046 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18047 if (this.html !== false) {
18048 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18050 this.el.removeClass([
18051 'fade','top','bottom', 'left', 'right','in',
18052 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18054 if (!this.title.length) {
18055 this.el.select('.popover-title',true).hide();
18058 var placement = typeof this.placement == 'function' ?
18059 this.placement.call(this, this.el, on_el) :
18062 var autoToken = /\s?auto?\s?/i;
18063 var autoPlace = autoToken.test(placement);
18065 placement = placement.replace(autoToken, '') || 'top';
18069 //this.el.setXY([0,0]);
18071 this.el.dom.style.display='block';
18072 this.el.addClass(placement);
18074 //this.el.appendTo(on_el);
18076 var p = this.getPosition();
18077 var box = this.el.getBox();
18082 var align = Roo.bootstrap.Popover.alignment[placement];
18085 this.el.alignTo(on_el, align[0],align[1]);
18086 //var arrow = this.el.select('.arrow',true).first();
18087 //arrow.set(align[2],
18089 this.el.addClass('in');
18092 if (this.el.hasClass('fade')) {
18096 this.hoverState = 'in';
18098 this.fireEvent('show', this);
18103 this.el.setXY([0,0]);
18104 this.el.removeClass('in');
18106 this.hoverState = null;
18108 this.fireEvent('hide', this);
18113 Roo.bootstrap.Popover.alignment = {
18114 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18115 'right' : ['l-r', [10,0], 'left bs-popover-left'],
18116 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18117 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18128 * @class Roo.bootstrap.Progress
18129 * @extends Roo.bootstrap.Component
18130 * Bootstrap Progress class
18131 * @cfg {Boolean} striped striped of the progress bar
18132 * @cfg {Boolean} active animated of the progress bar
18136 * Create a new Progress
18137 * @param {Object} config The config object
18140 Roo.bootstrap.Progress = function(config){
18141 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18144 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
18149 getAutoCreate : function(){
18157 cfg.cls += ' progress-striped';
18161 cfg.cls += ' active';
18180 * @class Roo.bootstrap.ProgressBar
18181 * @extends Roo.bootstrap.Component
18182 * Bootstrap ProgressBar class
18183 * @cfg {Number} aria_valuenow aria-value now
18184 * @cfg {Number} aria_valuemin aria-value min
18185 * @cfg {Number} aria_valuemax aria-value max
18186 * @cfg {String} label label for the progress bar
18187 * @cfg {String} panel (success | info | warning | danger )
18188 * @cfg {String} role role of the progress bar
18189 * @cfg {String} sr_only text
18193 * Create a new ProgressBar
18194 * @param {Object} config The config object
18197 Roo.bootstrap.ProgressBar = function(config){
18198 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18201 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
18205 aria_valuemax : 100,
18211 getAutoCreate : function()
18216 cls: 'progress-bar',
18217 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18229 cfg.role = this.role;
18232 if(this.aria_valuenow){
18233 cfg['aria-valuenow'] = this.aria_valuenow;
18236 if(this.aria_valuemin){
18237 cfg['aria-valuemin'] = this.aria_valuemin;
18240 if(this.aria_valuemax){
18241 cfg['aria-valuemax'] = this.aria_valuemax;
18244 if(this.label && !this.sr_only){
18245 cfg.html = this.label;
18249 cfg.cls += ' progress-bar-' + this.panel;
18255 update : function(aria_valuenow)
18257 this.aria_valuenow = aria_valuenow;
18259 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18274 * @class Roo.bootstrap.TabGroup
18275 * @extends Roo.bootstrap.Column
18276 * Bootstrap Column class
18277 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18278 * @cfg {Boolean} carousel true to make the group behave like a carousel
18279 * @cfg {Boolean} bullets show bullets for the panels
18280 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18281 * @cfg {Number} timer auto slide timer .. default 0 millisecond
18282 * @cfg {Boolean} showarrow (true|false) show arrow default true
18285 * Create a new TabGroup
18286 * @param {Object} config The config object
18289 Roo.bootstrap.TabGroup = function(config){
18290 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18292 this.navId = Roo.id();
18295 Roo.bootstrap.TabGroup.register(this);
18299 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18302 transition : false,
18307 slideOnTouch : false,
18310 getAutoCreate : function()
18312 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18314 cfg.cls += ' tab-content';
18316 if (this.carousel) {
18317 cfg.cls += ' carousel slide';
18320 cls : 'carousel-inner',
18324 if(this.bullets && !Roo.isTouch){
18327 cls : 'carousel-bullets',
18331 if(this.bullets_cls){
18332 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18339 cfg.cn[0].cn.push(bullets);
18342 if(this.showarrow){
18343 cfg.cn[0].cn.push({
18345 class : 'carousel-arrow',
18349 class : 'carousel-prev',
18353 class : 'fa fa-chevron-left'
18359 class : 'carousel-next',
18363 class : 'fa fa-chevron-right'
18376 initEvents: function()
18378 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18379 // this.el.on("touchstart", this.onTouchStart, this);
18382 if(this.autoslide){
18385 this.slideFn = window.setInterval(function() {
18386 _this.showPanelNext();
18390 if(this.showarrow){
18391 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18392 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18398 // onTouchStart : function(e, el, o)
18400 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18404 // this.showPanelNext();
18408 getChildContainer : function()
18410 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18414 * register a Navigation item
18415 * @param {Roo.bootstrap.NavItem} the navitem to add
18417 register : function(item)
18419 this.tabs.push( item);
18420 item.navId = this.navId; // not really needed..
18425 getActivePanel : function()
18428 Roo.each(this.tabs, function(t) {
18438 getPanelByName : function(n)
18441 Roo.each(this.tabs, function(t) {
18442 if (t.tabId == n) {
18450 indexOfPanel : function(p)
18453 Roo.each(this.tabs, function(t,i) {
18454 if (t.tabId == p.tabId) {
18463 * show a specific panel
18464 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18465 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18467 showPanel : function (pan)
18469 if(this.transition || typeof(pan) == 'undefined'){
18470 Roo.log("waiting for the transitionend");
18474 if (typeof(pan) == 'number') {
18475 pan = this.tabs[pan];
18478 if (typeof(pan) == 'string') {
18479 pan = this.getPanelByName(pan);
18482 var cur = this.getActivePanel();
18485 Roo.log('pan or acitve pan is undefined');
18489 if (pan.tabId == this.getActivePanel().tabId) {
18493 if (false === cur.fireEvent('beforedeactivate')) {
18497 if(this.bullets > 0 && !Roo.isTouch){
18498 this.setActiveBullet(this.indexOfPanel(pan));
18501 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18503 //class="carousel-item carousel-item-next carousel-item-left"
18505 this.transition = true;
18506 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18507 var lr = dir == 'next' ? 'left' : 'right';
18508 pan.el.addClass(dir); // or prev
18509 pan.el.addClass('carousel-item-' + dir); // or prev
18510 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18511 cur.el.addClass(lr); // or right
18512 pan.el.addClass(lr);
18513 cur.el.addClass('carousel-item-' +lr); // or right
18514 pan.el.addClass('carousel-item-' +lr);
18518 cur.el.on('transitionend', function() {
18519 Roo.log("trans end?");
18521 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18522 pan.setActive(true);
18524 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18525 cur.setActive(false);
18527 _this.transition = false;
18529 }, this, { single: true } );
18534 cur.setActive(false);
18535 pan.setActive(true);
18540 showPanelNext : function()
18542 var i = this.indexOfPanel(this.getActivePanel());
18544 if (i >= this.tabs.length - 1 && !this.autoslide) {
18548 if (i >= this.tabs.length - 1 && this.autoslide) {
18552 this.showPanel(this.tabs[i+1]);
18555 showPanelPrev : function()
18557 var i = this.indexOfPanel(this.getActivePanel());
18559 if (i < 1 && !this.autoslide) {
18563 if (i < 1 && this.autoslide) {
18564 i = this.tabs.length;
18567 this.showPanel(this.tabs[i-1]);
18571 addBullet: function()
18573 if(!this.bullets || Roo.isTouch){
18576 var ctr = this.el.select('.carousel-bullets',true).first();
18577 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18578 var bullet = ctr.createChild({
18579 cls : 'bullet bullet-' + i
18580 },ctr.dom.lastChild);
18585 bullet.on('click', (function(e, el, o, ii, t){
18587 e.preventDefault();
18589 this.showPanel(ii);
18591 if(this.autoslide && this.slideFn){
18592 clearInterval(this.slideFn);
18593 this.slideFn = window.setInterval(function() {
18594 _this.showPanelNext();
18598 }).createDelegate(this, [i, bullet], true));
18603 setActiveBullet : function(i)
18609 Roo.each(this.el.select('.bullet', true).elements, function(el){
18610 el.removeClass('selected');
18613 var bullet = this.el.select('.bullet-' + i, true).first();
18619 bullet.addClass('selected');
18630 Roo.apply(Roo.bootstrap.TabGroup, {
18634 * register a Navigation Group
18635 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18637 register : function(navgrp)
18639 this.groups[navgrp.navId] = navgrp;
18643 * fetch a Navigation Group based on the navigation ID
18644 * if one does not exist , it will get created.
18645 * @param {string} the navgroup to add
18646 * @returns {Roo.bootstrap.NavGroup} the navgroup
18648 get: function(navId) {
18649 if (typeof(this.groups[navId]) == 'undefined') {
18650 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18652 return this.groups[navId] ;
18667 * @class Roo.bootstrap.TabPanel
18668 * @extends Roo.bootstrap.Component
18669 * Bootstrap TabPanel class
18670 * @cfg {Boolean} active panel active
18671 * @cfg {String} html panel content
18672 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18673 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18674 * @cfg {String} href click to link..
18678 * Create a new TabPanel
18679 * @param {Object} config The config object
18682 Roo.bootstrap.TabPanel = function(config){
18683 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18687 * Fires when the active status changes
18688 * @param {Roo.bootstrap.TabPanel} this
18689 * @param {Boolean} state the new state
18694 * @event beforedeactivate
18695 * Fires before a tab is de-activated - can be used to do validation on a form.
18696 * @param {Roo.bootstrap.TabPanel} this
18697 * @return {Boolean} false if there is an error
18700 'beforedeactivate': true
18703 this.tabId = this.tabId || Roo.id();
18707 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18715 getAutoCreate : function(){
18720 // item is needed for carousel - not sure if it has any effect otherwise
18721 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18722 html: this.html || ''
18726 cfg.cls += ' active';
18730 cfg.tabId = this.tabId;
18738 initEvents: function()
18740 var p = this.parent();
18742 this.navId = this.navId || p.navId;
18744 if (typeof(this.navId) != 'undefined') {
18745 // not really needed.. but just in case.. parent should be a NavGroup.
18746 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18750 var i = tg.tabs.length - 1;
18752 if(this.active && tg.bullets > 0 && i < tg.bullets){
18753 tg.setActiveBullet(i);
18757 this.el.on('click', this.onClick, this);
18760 this.el.on("touchstart", this.onTouchStart, this);
18761 this.el.on("touchmove", this.onTouchMove, this);
18762 this.el.on("touchend", this.onTouchEnd, this);
18767 onRender : function(ct, position)
18769 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18772 setActive : function(state)
18774 Roo.log("panel - set active " + this.tabId + "=" + state);
18776 this.active = state;
18778 this.el.removeClass('active');
18780 } else if (!this.el.hasClass('active')) {
18781 this.el.addClass('active');
18784 this.fireEvent('changed', this, state);
18787 onClick : function(e)
18789 e.preventDefault();
18791 if(!this.href.length){
18795 window.location.href = this.href;
18804 onTouchStart : function(e)
18806 this.swiping = false;
18808 this.startX = e.browserEvent.touches[0].clientX;
18809 this.startY = e.browserEvent.touches[0].clientY;
18812 onTouchMove : function(e)
18814 this.swiping = true;
18816 this.endX = e.browserEvent.touches[0].clientX;
18817 this.endY = e.browserEvent.touches[0].clientY;
18820 onTouchEnd : function(e)
18827 var tabGroup = this.parent();
18829 if(this.endX > this.startX){ // swiping right
18830 tabGroup.showPanelPrev();
18834 if(this.startX > this.endX){ // swiping left
18835 tabGroup.showPanelNext();
18854 * @class Roo.bootstrap.DateField
18855 * @extends Roo.bootstrap.Input
18856 * Bootstrap DateField class
18857 * @cfg {Number} weekStart default 0
18858 * @cfg {String} viewMode default empty, (months|years)
18859 * @cfg {String} minViewMode default empty, (months|years)
18860 * @cfg {Number} startDate default -Infinity
18861 * @cfg {Number} endDate default Infinity
18862 * @cfg {Boolean} todayHighlight default false
18863 * @cfg {Boolean} todayBtn default false
18864 * @cfg {Boolean} calendarWeeks default false
18865 * @cfg {Object} daysOfWeekDisabled default empty
18866 * @cfg {Boolean} singleMode default false (true | false)
18868 * @cfg {Boolean} keyboardNavigation default true
18869 * @cfg {String} language default en
18872 * Create a new DateField
18873 * @param {Object} config The config object
18876 Roo.bootstrap.DateField = function(config){
18877 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18881 * Fires when this field show.
18882 * @param {Roo.bootstrap.DateField} this
18883 * @param {Mixed} date The date value
18888 * Fires when this field hide.
18889 * @param {Roo.bootstrap.DateField} this
18890 * @param {Mixed} date The date value
18895 * Fires when select a date.
18896 * @param {Roo.bootstrap.DateField} this
18897 * @param {Mixed} date The date value
18901 * @event beforeselect
18902 * Fires when before select a date.
18903 * @param {Roo.bootstrap.DateField} this
18904 * @param {Mixed} date The date value
18906 beforeselect : true
18910 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18913 * @cfg {String} format
18914 * The default date format string which can be overriden for localization support. The format must be
18915 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18919 * @cfg {String} altFormats
18920 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18921 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18923 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18931 todayHighlight : false,
18937 keyboardNavigation: true,
18939 calendarWeeks: false,
18941 startDate: -Infinity,
18945 daysOfWeekDisabled: [],
18949 singleMode : false,
18951 UTCDate: function()
18953 return new Date(Date.UTC.apply(Date, arguments));
18956 UTCToday: function()
18958 var today = new Date();
18959 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18962 getDate: function() {
18963 var d = this.getUTCDate();
18964 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18967 getUTCDate: function() {
18971 setDate: function(d) {
18972 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18975 setUTCDate: function(d) {
18977 this.setValue(this.formatDate(this.date));
18980 onRender: function(ct, position)
18983 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18985 this.language = this.language || 'en';
18986 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18987 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18989 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18990 this.format = this.format || 'm/d/y';
18991 this.isInline = false;
18992 this.isInput = true;
18993 this.component = this.el.select('.add-on', true).first() || false;
18994 this.component = (this.component && this.component.length === 0) ? false : this.component;
18995 this.hasInput = this.component && this.inputEl().length;
18997 if (typeof(this.minViewMode === 'string')) {
18998 switch (this.minViewMode) {
19000 this.minViewMode = 1;
19003 this.minViewMode = 2;
19006 this.minViewMode = 0;
19011 if (typeof(this.viewMode === 'string')) {
19012 switch (this.viewMode) {
19025 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
19027 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
19029 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19031 this.picker().on('mousedown', this.onMousedown, this);
19032 this.picker().on('click', this.onClick, this);
19034 this.picker().addClass('datepicker-dropdown');
19036 this.startViewMode = this.viewMode;
19038 if(this.singleMode){
19039 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19040 v.setVisibilityMode(Roo.Element.DISPLAY);
19044 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19045 v.setStyle('width', '189px');
19049 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19050 if(!this.calendarWeeks){
19055 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19056 v.attr('colspan', function(i, val){
19057 return parseInt(val) + 1;
19062 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19064 this.setStartDate(this.startDate);
19065 this.setEndDate(this.endDate);
19067 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19074 if(this.isInline) {
19079 picker : function()
19081 return this.pickerEl;
19082 // return this.el.select('.datepicker', true).first();
19085 fillDow: function()
19087 var dowCnt = this.weekStart;
19096 if(this.calendarWeeks){
19104 while (dowCnt < this.weekStart + 7) {
19108 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19112 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19115 fillMonths: function()
19118 var months = this.picker().select('>.datepicker-months td', true).first();
19120 months.dom.innerHTML = '';
19126 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19129 months.createChild(month);
19136 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;
19138 if (this.date < this.startDate) {
19139 this.viewDate = new Date(this.startDate);
19140 } else if (this.date > this.endDate) {
19141 this.viewDate = new Date(this.endDate);
19143 this.viewDate = new Date(this.date);
19151 var d = new Date(this.viewDate),
19152 year = d.getUTCFullYear(),
19153 month = d.getUTCMonth(),
19154 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19155 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19156 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19157 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19158 currentDate = this.date && this.date.valueOf(),
19159 today = this.UTCToday();
19161 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19163 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19165 // this.picker.select('>tfoot th.today').
19166 // .text(dates[this.language].today)
19167 // .toggle(this.todayBtn !== false);
19169 this.updateNavArrows();
19172 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19174 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19176 prevMonth.setUTCDate(day);
19178 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19180 var nextMonth = new Date(prevMonth);
19182 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19184 nextMonth = nextMonth.valueOf();
19186 var fillMonths = false;
19188 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19190 while(prevMonth.valueOf() <= nextMonth) {
19193 if (prevMonth.getUTCDay() === this.weekStart) {
19195 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19203 if(this.calendarWeeks){
19204 // ISO 8601: First week contains first thursday.
19205 // ISO also states week starts on Monday, but we can be more abstract here.
19207 // Start of current week: based on weekstart/current date
19208 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19209 // Thursday of this week
19210 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19211 // First Thursday of year, year from thursday
19212 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19213 // Calendar week: ms between thursdays, div ms per day, div 7 days
19214 calWeek = (th - yth) / 864e5 / 7 + 1;
19216 fillMonths.cn.push({
19224 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19226 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19229 if (this.todayHighlight &&
19230 prevMonth.getUTCFullYear() == today.getFullYear() &&
19231 prevMonth.getUTCMonth() == today.getMonth() &&
19232 prevMonth.getUTCDate() == today.getDate()) {
19233 clsName += ' today';
19236 if (currentDate && prevMonth.valueOf() === currentDate) {
19237 clsName += ' active';
19240 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19241 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19242 clsName += ' disabled';
19245 fillMonths.cn.push({
19247 cls: 'day ' + clsName,
19248 html: prevMonth.getDate()
19251 prevMonth.setDate(prevMonth.getDate()+1);
19254 var currentYear = this.date && this.date.getUTCFullYear();
19255 var currentMonth = this.date && this.date.getUTCMonth();
19257 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19259 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19260 v.removeClass('active');
19262 if(currentYear === year && k === currentMonth){
19263 v.addClass('active');
19266 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19267 v.addClass('disabled');
19273 year = parseInt(year/10, 10) * 10;
19275 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19277 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19280 for (var i = -1; i < 11; i++) {
19281 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19283 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19291 showMode: function(dir)
19294 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19297 Roo.each(this.picker().select('>div',true).elements, function(v){
19298 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19301 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19306 if(this.isInline) {
19310 this.picker().removeClass(['bottom', 'top']);
19312 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19314 * place to the top of element!
19318 this.picker().addClass('top');
19319 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19324 this.picker().addClass('bottom');
19326 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19329 parseDate : function(value)
19331 if(!value || value instanceof Date){
19334 var v = Date.parseDate(value, this.format);
19335 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19336 v = Date.parseDate(value, 'Y-m-d');
19338 if(!v && this.altFormats){
19339 if(!this.altFormatsArray){
19340 this.altFormatsArray = this.altFormats.split("|");
19342 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19343 v = Date.parseDate(value, this.altFormatsArray[i]);
19349 formatDate : function(date, fmt)
19351 return (!date || !(date instanceof Date)) ?
19352 date : date.dateFormat(fmt || this.format);
19355 onFocus : function()
19357 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19361 onBlur : function()
19363 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19365 var d = this.inputEl().getValue();
19372 showPopup : function()
19374 this.picker().show();
19378 this.fireEvent('showpopup', this, this.date);
19381 hidePopup : function()
19383 if(this.isInline) {
19386 this.picker().hide();
19387 this.viewMode = this.startViewMode;
19390 this.fireEvent('hidepopup', this, this.date);
19394 onMousedown: function(e)
19396 e.stopPropagation();
19397 e.preventDefault();
19402 Roo.bootstrap.DateField.superclass.keyup.call(this);
19406 setValue: function(v)
19408 if(this.fireEvent('beforeselect', this, v) !== false){
19409 var d = new Date(this.parseDate(v) ).clearTime();
19411 if(isNaN(d.getTime())){
19412 this.date = this.viewDate = '';
19413 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19417 v = this.formatDate(d);
19419 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19421 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19425 this.fireEvent('select', this, this.date);
19429 getValue: function()
19431 return this.formatDate(this.date);
19434 fireKey: function(e)
19436 if (!this.picker().isVisible()){
19437 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19443 var dateChanged = false,
19445 newDate, newViewDate;
19450 e.preventDefault();
19454 if (!this.keyboardNavigation) {
19457 dir = e.keyCode == 37 ? -1 : 1;
19460 newDate = this.moveYear(this.date, dir);
19461 newViewDate = this.moveYear(this.viewDate, dir);
19462 } else if (e.shiftKey){
19463 newDate = this.moveMonth(this.date, dir);
19464 newViewDate = this.moveMonth(this.viewDate, dir);
19466 newDate = new Date(this.date);
19467 newDate.setUTCDate(this.date.getUTCDate() + dir);
19468 newViewDate = new Date(this.viewDate);
19469 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19471 if (this.dateWithinRange(newDate)){
19472 this.date = newDate;
19473 this.viewDate = newViewDate;
19474 this.setValue(this.formatDate(this.date));
19476 e.preventDefault();
19477 dateChanged = true;
19482 if (!this.keyboardNavigation) {
19485 dir = e.keyCode == 38 ? -1 : 1;
19487 newDate = this.moveYear(this.date, dir);
19488 newViewDate = this.moveYear(this.viewDate, dir);
19489 } else if (e.shiftKey){
19490 newDate = this.moveMonth(this.date, dir);
19491 newViewDate = this.moveMonth(this.viewDate, dir);
19493 newDate = new Date(this.date);
19494 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19495 newViewDate = new Date(this.viewDate);
19496 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19498 if (this.dateWithinRange(newDate)){
19499 this.date = newDate;
19500 this.viewDate = newViewDate;
19501 this.setValue(this.formatDate(this.date));
19503 e.preventDefault();
19504 dateChanged = true;
19508 this.setValue(this.formatDate(this.date));
19510 e.preventDefault();
19513 this.setValue(this.formatDate(this.date));
19527 onClick: function(e)
19529 e.stopPropagation();
19530 e.preventDefault();
19532 var target = e.getTarget();
19534 if(target.nodeName.toLowerCase() === 'i'){
19535 target = Roo.get(target).dom.parentNode;
19538 var nodeName = target.nodeName;
19539 var className = target.className;
19540 var html = target.innerHTML;
19541 //Roo.log(nodeName);
19543 switch(nodeName.toLowerCase()) {
19545 switch(className) {
19551 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19552 switch(this.viewMode){
19554 this.viewDate = this.moveMonth(this.viewDate, dir);
19558 this.viewDate = this.moveYear(this.viewDate, dir);
19564 var date = new Date();
19565 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19567 this.setValue(this.formatDate(this.date));
19574 if (className.indexOf('disabled') < 0) {
19575 this.viewDate.setUTCDate(1);
19576 if (className.indexOf('month') > -1) {
19577 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19579 var year = parseInt(html, 10) || 0;
19580 this.viewDate.setUTCFullYear(year);
19584 if(this.singleMode){
19585 this.setValue(this.formatDate(this.viewDate));
19596 //Roo.log(className);
19597 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19598 var day = parseInt(html, 10) || 1;
19599 var year = this.viewDate.getUTCFullYear(),
19600 month = this.viewDate.getUTCMonth();
19602 if (className.indexOf('old') > -1) {
19609 } else if (className.indexOf('new') > -1) {
19617 //Roo.log([year,month,day]);
19618 this.date = this.UTCDate(year, month, day,0,0,0,0);
19619 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19621 //Roo.log(this.formatDate(this.date));
19622 this.setValue(this.formatDate(this.date));
19629 setStartDate: function(startDate)
19631 this.startDate = startDate || -Infinity;
19632 if (this.startDate !== -Infinity) {
19633 this.startDate = this.parseDate(this.startDate);
19636 this.updateNavArrows();
19639 setEndDate: function(endDate)
19641 this.endDate = endDate || Infinity;
19642 if (this.endDate !== Infinity) {
19643 this.endDate = this.parseDate(this.endDate);
19646 this.updateNavArrows();
19649 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19651 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19652 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19653 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19655 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19656 return parseInt(d, 10);
19659 this.updateNavArrows();
19662 updateNavArrows: function()
19664 if(this.singleMode){
19668 var d = new Date(this.viewDate),
19669 year = d.getUTCFullYear(),
19670 month = d.getUTCMonth();
19672 Roo.each(this.picker().select('.prev', true).elements, function(v){
19674 switch (this.viewMode) {
19677 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19683 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19690 Roo.each(this.picker().select('.next', true).elements, function(v){
19692 switch (this.viewMode) {
19695 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19701 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19709 moveMonth: function(date, dir)
19714 var new_date = new Date(date.valueOf()),
19715 day = new_date.getUTCDate(),
19716 month = new_date.getUTCMonth(),
19717 mag = Math.abs(dir),
19719 dir = dir > 0 ? 1 : -1;
19722 // If going back one month, make sure month is not current month
19723 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19725 return new_date.getUTCMonth() == month;
19727 // If going forward one month, make sure month is as expected
19728 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19730 return new_date.getUTCMonth() != new_month;
19732 new_month = month + dir;
19733 new_date.setUTCMonth(new_month);
19734 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19735 if (new_month < 0 || new_month > 11) {
19736 new_month = (new_month + 12) % 12;
19739 // For magnitudes >1, move one month at a time...
19740 for (var i=0; i<mag; i++) {
19741 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19742 new_date = this.moveMonth(new_date, dir);
19744 // ...then reset the day, keeping it in the new month
19745 new_month = new_date.getUTCMonth();
19746 new_date.setUTCDate(day);
19748 return new_month != new_date.getUTCMonth();
19751 // Common date-resetting loop -- if date is beyond end of month, make it
19754 new_date.setUTCDate(--day);
19755 new_date.setUTCMonth(new_month);
19760 moveYear: function(date, dir)
19762 return this.moveMonth(date, dir*12);
19765 dateWithinRange: function(date)
19767 return date >= this.startDate && date <= this.endDate;
19773 this.picker().remove();
19776 validateValue : function(value)
19778 if(this.getVisibilityEl().hasClass('hidden')){
19782 if(value.length < 1) {
19783 if(this.allowBlank){
19789 if(value.length < this.minLength){
19792 if(value.length > this.maxLength){
19796 var vt = Roo.form.VTypes;
19797 if(!vt[this.vtype](value, this)){
19801 if(typeof this.validator == "function"){
19802 var msg = this.validator(value);
19808 if(this.regex && !this.regex.test(value)){
19812 if(typeof(this.parseDate(value)) == 'undefined'){
19816 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19820 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19830 this.date = this.viewDate = '';
19832 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19837 Roo.apply(Roo.bootstrap.DateField, {
19848 html: '<i class="fa fa-arrow-left"/>'
19858 html: '<i class="fa fa-arrow-right"/>'
19900 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19901 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19902 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19903 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19904 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19917 navFnc: 'FullYear',
19922 navFnc: 'FullYear',
19927 Roo.apply(Roo.bootstrap.DateField, {
19931 cls: 'datepicker dropdown-menu roo-dynamic',
19935 cls: 'datepicker-days',
19939 cls: 'table-condensed',
19941 Roo.bootstrap.DateField.head,
19945 Roo.bootstrap.DateField.footer
19952 cls: 'datepicker-months',
19956 cls: 'table-condensed',
19958 Roo.bootstrap.DateField.head,
19959 Roo.bootstrap.DateField.content,
19960 Roo.bootstrap.DateField.footer
19967 cls: 'datepicker-years',
19971 cls: 'table-condensed',
19973 Roo.bootstrap.DateField.head,
19974 Roo.bootstrap.DateField.content,
19975 Roo.bootstrap.DateField.footer
19994 * @class Roo.bootstrap.TimeField
19995 * @extends Roo.bootstrap.Input
19996 * Bootstrap DateField class
20000 * Create a new TimeField
20001 * @param {Object} config The config object
20004 Roo.bootstrap.TimeField = function(config){
20005 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
20009 * Fires when this field show.
20010 * @param {Roo.bootstrap.DateField} thisthis
20011 * @param {Mixed} date The date value
20016 * Fires when this field hide.
20017 * @param {Roo.bootstrap.DateField} this
20018 * @param {Mixed} date The date value
20023 * Fires when select a date.
20024 * @param {Roo.bootstrap.DateField} this
20025 * @param {Mixed} date The date value
20031 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
20034 * @cfg {String} format
20035 * The default time format string which can be overriden for localization support. The format must be
20036 * valid according to {@link Date#parseDate} (defaults to 'H:i').
20040 onRender: function(ct, position)
20043 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20045 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20047 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20049 this.pop = this.picker().select('>.datepicker-time',true).first();
20050 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20052 this.picker().on('mousedown', this.onMousedown, this);
20053 this.picker().on('click', this.onClick, this);
20055 this.picker().addClass('datepicker-dropdown');
20060 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20061 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20062 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20063 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20064 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20065 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20069 fireKey: function(e){
20070 if (!this.picker().isVisible()){
20071 if (e.keyCode == 27) { // allow escape to hide and re-show picker
20077 e.preventDefault();
20085 this.onTogglePeriod();
20088 this.onIncrementMinutes();
20091 this.onDecrementMinutes();
20100 onClick: function(e) {
20101 e.stopPropagation();
20102 e.preventDefault();
20105 picker : function()
20107 return this.el.select('.datepicker', true).first();
20110 fillTime: function()
20112 var time = this.pop.select('tbody', true).first();
20114 time.dom.innerHTML = '';
20129 cls: 'hours-up glyphicon glyphicon-chevron-up'
20149 cls: 'minutes-up glyphicon glyphicon-chevron-up'
20170 cls: 'timepicker-hour',
20185 cls: 'timepicker-minute',
20200 cls: 'btn btn-primary period',
20222 cls: 'hours-down glyphicon glyphicon-chevron-down'
20242 cls: 'minutes-down glyphicon glyphicon-chevron-down'
20260 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20267 var hours = this.time.getHours();
20268 var minutes = this.time.getMinutes();
20281 hours = hours - 12;
20285 hours = '0' + hours;
20289 minutes = '0' + minutes;
20292 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20293 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20294 this.pop.select('button', true).first().dom.innerHTML = period;
20300 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20302 var cls = ['bottom'];
20304 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20311 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20316 this.picker().addClass(cls.join('-'));
20320 Roo.each(cls, function(c){
20322 _this.picker().setTop(_this.inputEl().getHeight());
20326 _this.picker().setTop(0 - _this.picker().getHeight());
20331 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20335 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20342 onFocus : function()
20344 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20348 onBlur : function()
20350 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20356 this.picker().show();
20361 this.fireEvent('show', this, this.date);
20366 this.picker().hide();
20369 this.fireEvent('hide', this, this.date);
20372 setTime : function()
20375 this.setValue(this.time.format(this.format));
20377 this.fireEvent('select', this, this.date);
20382 onMousedown: function(e){
20383 e.stopPropagation();
20384 e.preventDefault();
20387 onIncrementHours: function()
20389 Roo.log('onIncrementHours');
20390 this.time = this.time.add(Date.HOUR, 1);
20395 onDecrementHours: function()
20397 Roo.log('onDecrementHours');
20398 this.time = this.time.add(Date.HOUR, -1);
20402 onIncrementMinutes: function()
20404 Roo.log('onIncrementMinutes');
20405 this.time = this.time.add(Date.MINUTE, 1);
20409 onDecrementMinutes: function()
20411 Roo.log('onDecrementMinutes');
20412 this.time = this.time.add(Date.MINUTE, -1);
20416 onTogglePeriod: function()
20418 Roo.log('onTogglePeriod');
20419 this.time = this.time.add(Date.HOUR, 12);
20426 Roo.apply(Roo.bootstrap.TimeField, {
20456 cls: 'btn btn-info ok',
20468 Roo.apply(Roo.bootstrap.TimeField, {
20472 cls: 'datepicker dropdown-menu',
20476 cls: 'datepicker-time',
20480 cls: 'table-condensed',
20482 Roo.bootstrap.TimeField.content,
20483 Roo.bootstrap.TimeField.footer
20502 * @class Roo.bootstrap.MonthField
20503 * @extends Roo.bootstrap.Input
20504 * Bootstrap MonthField class
20506 * @cfg {String} language default en
20509 * Create a new MonthField
20510 * @param {Object} config The config object
20513 Roo.bootstrap.MonthField = function(config){
20514 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20519 * Fires when this field show.
20520 * @param {Roo.bootstrap.MonthField} this
20521 * @param {Mixed} date The date value
20526 * Fires when this field hide.
20527 * @param {Roo.bootstrap.MonthField} this
20528 * @param {Mixed} date The date value
20533 * Fires when select a date.
20534 * @param {Roo.bootstrap.MonthField} this
20535 * @param {String} oldvalue The old value
20536 * @param {String} newvalue The new value
20542 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20544 onRender: function(ct, position)
20547 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20549 this.language = this.language || 'en';
20550 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20551 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20553 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20554 this.isInline = false;
20555 this.isInput = true;
20556 this.component = this.el.select('.add-on', true).first() || false;
20557 this.component = (this.component && this.component.length === 0) ? false : this.component;
20558 this.hasInput = this.component && this.inputEL().length;
20560 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20562 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20564 this.picker().on('mousedown', this.onMousedown, this);
20565 this.picker().on('click', this.onClick, this);
20567 this.picker().addClass('datepicker-dropdown');
20569 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20570 v.setStyle('width', '189px');
20577 if(this.isInline) {
20583 setValue: function(v, suppressEvent)
20585 var o = this.getValue();
20587 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20591 if(suppressEvent !== true){
20592 this.fireEvent('select', this, o, v);
20597 getValue: function()
20602 onClick: function(e)
20604 e.stopPropagation();
20605 e.preventDefault();
20607 var target = e.getTarget();
20609 if(target.nodeName.toLowerCase() === 'i'){
20610 target = Roo.get(target).dom.parentNode;
20613 var nodeName = target.nodeName;
20614 var className = target.className;
20615 var html = target.innerHTML;
20617 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20621 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20623 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20629 picker : function()
20631 return this.pickerEl;
20634 fillMonths: function()
20637 var months = this.picker().select('>.datepicker-months td', true).first();
20639 months.dom.innerHTML = '';
20645 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20648 months.createChild(month);
20657 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20658 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20661 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20662 e.removeClass('active');
20664 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20665 e.addClass('active');
20672 if(this.isInline) {
20676 this.picker().removeClass(['bottom', 'top']);
20678 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20680 * place to the top of element!
20684 this.picker().addClass('top');
20685 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20690 this.picker().addClass('bottom');
20692 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20695 onFocus : function()
20697 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20701 onBlur : function()
20703 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20705 var d = this.inputEl().getValue();
20714 this.picker().show();
20715 this.picker().select('>.datepicker-months', true).first().show();
20719 this.fireEvent('show', this, this.date);
20724 if(this.isInline) {
20727 this.picker().hide();
20728 this.fireEvent('hide', this, this.date);
20732 onMousedown: function(e)
20734 e.stopPropagation();
20735 e.preventDefault();
20740 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20744 fireKey: function(e)
20746 if (!this.picker().isVisible()){
20747 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20758 e.preventDefault();
20762 dir = e.keyCode == 37 ? -1 : 1;
20764 this.vIndex = this.vIndex + dir;
20766 if(this.vIndex < 0){
20770 if(this.vIndex > 11){
20774 if(isNaN(this.vIndex)){
20778 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20784 dir = e.keyCode == 38 ? -1 : 1;
20786 this.vIndex = this.vIndex + dir * 4;
20788 if(this.vIndex < 0){
20792 if(this.vIndex > 11){
20796 if(isNaN(this.vIndex)){
20800 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20805 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20806 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20810 e.preventDefault();
20813 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20814 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20830 this.picker().remove();
20835 Roo.apply(Roo.bootstrap.MonthField, {
20854 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20855 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20860 Roo.apply(Roo.bootstrap.MonthField, {
20864 cls: 'datepicker dropdown-menu roo-dynamic',
20868 cls: 'datepicker-months',
20872 cls: 'table-condensed',
20874 Roo.bootstrap.DateField.content
20894 * @class Roo.bootstrap.CheckBox
20895 * @extends Roo.bootstrap.Input
20896 * Bootstrap CheckBox class
20898 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20899 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20900 * @cfg {String} boxLabel The text that appears beside the checkbox
20901 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20902 * @cfg {Boolean} checked initnal the element
20903 * @cfg {Boolean} inline inline the element (default false)
20904 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20905 * @cfg {String} tooltip label tooltip
20908 * Create a new CheckBox
20909 * @param {Object} config The config object
20912 Roo.bootstrap.CheckBox = function(config){
20913 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20918 * Fires when the element is checked or unchecked.
20919 * @param {Roo.bootstrap.CheckBox} this This input
20920 * @param {Boolean} checked The new checked value
20925 * Fires when the element is click.
20926 * @param {Roo.bootstrap.CheckBox} this This input
20933 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20935 inputType: 'checkbox',
20944 // checkbox success does not make any sense really..
20949 getAutoCreate : function()
20951 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20957 cfg.cls = 'form-group ' + this.inputType; //input-group
20960 cfg.cls += ' ' + this.inputType + '-inline';
20966 type : this.inputType,
20967 value : this.inputValue,
20968 cls : 'roo-' + this.inputType, //'form-box',
20969 placeholder : this.placeholder || ''
20973 if(this.inputType != 'radio'){
20977 cls : 'roo-hidden-value',
20978 value : this.checked ? this.inputValue : this.valueOff
20983 if (this.weight) { // Validity check?
20984 cfg.cls += " " + this.inputType + "-" + this.weight;
20987 if (this.disabled) {
20988 input.disabled=true;
20992 input.checked = this.checked;
20997 input.name = this.name;
20999 if(this.inputType != 'radio'){
21000 hidden.name = this.name;
21001 input.name = '_hidden_' + this.name;
21006 input.cls += ' input-' + this.size;
21011 ['xs','sm','md','lg'].map(function(size){
21012 if (settings[size]) {
21013 cfg.cls += ' col-' + size + '-' + settings[size];
21017 var inputblock = input;
21019 if (this.before || this.after) {
21022 cls : 'input-group',
21027 inputblock.cn.push({
21029 cls : 'input-group-addon',
21034 inputblock.cn.push(input);
21036 if(this.inputType != 'radio'){
21037 inputblock.cn.push(hidden);
21041 inputblock.cn.push({
21043 cls : 'input-group-addon',
21049 var boxLabelCfg = false;
21055 //'for': id, // box label is handled by onclick - so no for...
21057 html: this.boxLabel
21060 boxLabelCfg.tooltip = this.tooltip;
21066 if (align ==='left' && this.fieldLabel.length) {
21067 // Roo.log("left and has label");
21072 cls : 'control-label',
21073 html : this.fieldLabel
21084 cfg.cn[1].cn.push(boxLabelCfg);
21087 if(this.labelWidth > 12){
21088 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21091 if(this.labelWidth < 13 && this.labelmd == 0){
21092 this.labelmd = this.labelWidth;
21095 if(this.labellg > 0){
21096 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21097 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21100 if(this.labelmd > 0){
21101 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21102 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21105 if(this.labelsm > 0){
21106 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21107 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21110 if(this.labelxs > 0){
21111 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21112 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21115 } else if ( this.fieldLabel.length) {
21116 // Roo.log(" label");
21120 tag: this.boxLabel ? 'span' : 'label',
21122 cls: 'control-label box-input-label',
21123 //cls : 'input-group-addon',
21124 html : this.fieldLabel
21131 cfg.cn.push(boxLabelCfg);
21136 // Roo.log(" no label && no align");
21137 cfg.cn = [ inputblock ] ;
21139 cfg.cn.push(boxLabelCfg);
21147 if(this.inputType != 'radio'){
21148 cfg.cn.push(hidden);
21156 * return the real input element.
21158 inputEl: function ()
21160 return this.el.select('input.roo-' + this.inputType,true).first();
21162 hiddenEl: function ()
21164 return this.el.select('input.roo-hidden-value',true).first();
21167 labelEl: function()
21169 return this.el.select('label.control-label',true).first();
21171 /* depricated... */
21175 return this.labelEl();
21178 boxLabelEl: function()
21180 return this.el.select('label.box-label',true).first();
21183 initEvents : function()
21185 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21187 this.inputEl().on('click', this.onClick, this);
21189 if (this.boxLabel) {
21190 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
21193 this.startValue = this.getValue();
21196 Roo.bootstrap.CheckBox.register(this);
21200 onClick : function(e)
21202 if(this.fireEvent('click', this, e) !== false){
21203 this.setChecked(!this.checked);
21208 setChecked : function(state,suppressEvent)
21210 this.startValue = this.getValue();
21212 if(this.inputType == 'radio'){
21214 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21215 e.dom.checked = false;
21218 this.inputEl().dom.checked = true;
21220 this.inputEl().dom.value = this.inputValue;
21222 if(suppressEvent !== true){
21223 this.fireEvent('check', this, true);
21231 this.checked = state;
21233 this.inputEl().dom.checked = state;
21236 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21238 if(suppressEvent !== true){
21239 this.fireEvent('check', this, state);
21245 getValue : function()
21247 if(this.inputType == 'radio'){
21248 return this.getGroupValue();
21251 return this.hiddenEl().dom.value;
21255 getGroupValue : function()
21257 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21261 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21264 setValue : function(v,suppressEvent)
21266 if(this.inputType == 'radio'){
21267 this.setGroupValue(v, suppressEvent);
21271 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21276 setGroupValue : function(v, suppressEvent)
21278 this.startValue = this.getValue();
21280 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21281 e.dom.checked = false;
21283 if(e.dom.value == v){
21284 e.dom.checked = true;
21288 if(suppressEvent !== true){
21289 this.fireEvent('check', this, true);
21297 validate : function()
21299 if(this.getVisibilityEl().hasClass('hidden')){
21305 (this.inputType == 'radio' && this.validateRadio()) ||
21306 (this.inputType == 'checkbox' && this.validateCheckbox())
21312 this.markInvalid();
21316 validateRadio : function()
21318 if(this.getVisibilityEl().hasClass('hidden')){
21322 if(this.allowBlank){
21328 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21329 if(!e.dom.checked){
21341 validateCheckbox : function()
21344 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21345 //return (this.getValue() == this.inputValue) ? true : false;
21348 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21356 for(var i in group){
21357 if(group[i].el.isVisible(true)){
21365 for(var i in group){
21370 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21377 * Mark this field as valid
21379 markValid : function()
21383 this.fireEvent('valid', this);
21385 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21388 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21395 if(this.inputType == 'radio'){
21396 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21397 var fg = e.findParent('.form-group', false, true);
21398 if (Roo.bootstrap.version == 3) {
21399 fg.removeClass([_this.invalidClass, _this.validClass]);
21400 fg.addClass(_this.validClass);
21402 fg.removeClass(['is-valid', 'is-invalid']);
21403 fg.addClass('is-valid');
21411 var fg = this.el.findParent('.form-group', false, true);
21412 if (Roo.bootstrap.version == 3) {
21413 fg.removeClass([this.invalidClass, this.validClass]);
21414 fg.addClass(this.validClass);
21416 fg.removeClass(['is-valid', 'is-invalid']);
21417 fg.addClass('is-valid');
21422 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21428 for(var i in group){
21429 var fg = group[i].el.findParent('.form-group', false, true);
21430 if (Roo.bootstrap.version == 3) {
21431 fg.removeClass([this.invalidClass, this.validClass]);
21432 fg.addClass(this.validClass);
21434 fg.removeClass(['is-valid', 'is-invalid']);
21435 fg.addClass('is-valid');
21441 * Mark this field as invalid
21442 * @param {String} msg The validation message
21444 markInvalid : function(msg)
21446 if(this.allowBlank){
21452 this.fireEvent('invalid', this, msg);
21454 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21457 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21461 label.markInvalid();
21464 if(this.inputType == 'radio'){
21466 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21467 var fg = e.findParent('.form-group', false, true);
21468 if (Roo.bootstrap.version == 3) {
21469 fg.removeClass([_this.invalidClass, _this.validClass]);
21470 fg.addClass(_this.invalidClass);
21472 fg.removeClass(['is-invalid', 'is-valid']);
21473 fg.addClass('is-invalid');
21481 var fg = this.el.findParent('.form-group', false, true);
21482 if (Roo.bootstrap.version == 3) {
21483 fg.removeClass([_this.invalidClass, _this.validClass]);
21484 fg.addClass(_this.invalidClass);
21486 fg.removeClass(['is-invalid', 'is-valid']);
21487 fg.addClass('is-invalid');
21492 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21498 for(var i in group){
21499 var fg = group[i].el.findParent('.form-group', false, true);
21500 if (Roo.bootstrap.version == 3) {
21501 fg.removeClass([_this.invalidClass, _this.validClass]);
21502 fg.addClass(_this.invalidClass);
21504 fg.removeClass(['is-invalid', 'is-valid']);
21505 fg.addClass('is-invalid');
21511 clearInvalid : function()
21513 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21515 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21517 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21519 if (label && label.iconEl) {
21520 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21521 label.iconEl.removeClass(['is-invalid', 'is-valid']);
21525 disable : function()
21527 if(this.inputType != 'radio'){
21528 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21535 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21536 _this.getActionEl().addClass(this.disabledClass);
21537 e.dom.disabled = true;
21541 this.disabled = true;
21542 this.fireEvent("disable", this);
21546 enable : function()
21548 if(this.inputType != 'radio'){
21549 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21556 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21557 _this.getActionEl().removeClass(this.disabledClass);
21558 e.dom.disabled = false;
21562 this.disabled = false;
21563 this.fireEvent("enable", this);
21567 setBoxLabel : function(v)
21572 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21578 Roo.apply(Roo.bootstrap.CheckBox, {
21583 * register a CheckBox Group
21584 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21586 register : function(checkbox)
21588 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21589 this.groups[checkbox.groupId] = {};
21592 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21596 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21600 * fetch a CheckBox Group based on the group ID
21601 * @param {string} the group ID
21602 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21604 get: function(groupId) {
21605 if (typeof(this.groups[groupId]) == 'undefined') {
21609 return this.groups[groupId] ;
21622 * @class Roo.bootstrap.Radio
21623 * @extends Roo.bootstrap.Component
21624 * Bootstrap Radio class
21625 * @cfg {String} boxLabel - the label associated
21626 * @cfg {String} value - the value of radio
21629 * Create a new Radio
21630 * @param {Object} config The config object
21632 Roo.bootstrap.Radio = function(config){
21633 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21637 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21643 getAutoCreate : function()
21647 cls : 'form-group radio',
21652 html : this.boxLabel
21660 initEvents : function()
21662 this.parent().register(this);
21664 this.el.on('click', this.onClick, this);
21668 onClick : function(e)
21670 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21671 this.setChecked(true);
21675 setChecked : function(state, suppressEvent)
21677 this.parent().setValue(this.value, suppressEvent);
21681 setBoxLabel : function(v)
21686 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21701 * @class Roo.bootstrap.SecurePass
21702 * @extends Roo.bootstrap.Input
21703 * Bootstrap SecurePass class
21707 * Create a new SecurePass
21708 * @param {Object} config The config object
21711 Roo.bootstrap.SecurePass = function (config) {
21712 // these go here, so the translation tool can replace them..
21714 PwdEmpty: "Please type a password, and then retype it to confirm.",
21715 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21716 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21717 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21718 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21719 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21720 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21721 TooWeak: "Your password is Too Weak."
21723 this.meterLabel = "Password strength:";
21724 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21725 this.meterClass = [
21726 "roo-password-meter-tooweak",
21727 "roo-password-meter-weak",
21728 "roo-password-meter-medium",
21729 "roo-password-meter-strong",
21730 "roo-password-meter-grey"
21735 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21738 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21740 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21742 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21743 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21744 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21745 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21746 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21747 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21748 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21758 * @cfg {String/Object} Label for the strength meter (defaults to
21759 * 'Password strength:')
21764 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21765 * ['Weak', 'Medium', 'Strong'])
21768 pwdStrengths: false,
21781 initEvents: function ()
21783 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21785 if (this.el.is('input[type=password]') && Roo.isSafari) {
21786 this.el.on('keydown', this.SafariOnKeyDown, this);
21789 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21792 onRender: function (ct, position)
21794 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21795 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21796 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21798 this.trigger.createChild({
21803 cls: 'roo-password-meter-grey col-xs-12',
21806 //width: this.meterWidth + 'px'
21810 cls: 'roo-password-meter-text'
21816 if (this.hideTrigger) {
21817 this.trigger.setDisplayed(false);
21819 this.setSize(this.width || '', this.height || '');
21822 onDestroy: function ()
21824 if (this.trigger) {
21825 this.trigger.removeAllListeners();
21826 this.trigger.remove();
21829 this.wrap.remove();
21831 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21834 checkStrength: function ()
21836 var pwd = this.inputEl().getValue();
21837 if (pwd == this._lastPwd) {
21842 if (this.ClientSideStrongPassword(pwd)) {
21844 } else if (this.ClientSideMediumPassword(pwd)) {
21846 } else if (this.ClientSideWeakPassword(pwd)) {
21852 Roo.log('strength1: ' + strength);
21854 //var pm = this.trigger.child('div/div/div').dom;
21855 var pm = this.trigger.child('div/div');
21856 pm.removeClass(this.meterClass);
21857 pm.addClass(this.meterClass[strength]);
21860 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21862 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21864 this._lastPwd = pwd;
21868 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21870 this._lastPwd = '';
21872 var pm = this.trigger.child('div/div');
21873 pm.removeClass(this.meterClass);
21874 pm.addClass('roo-password-meter-grey');
21877 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21880 this.inputEl().dom.type='password';
21883 validateValue: function (value)
21886 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21889 if (value.length == 0) {
21890 if (this.allowBlank) {
21891 this.clearInvalid();
21895 this.markInvalid(this.errors.PwdEmpty);
21896 this.errorMsg = this.errors.PwdEmpty;
21904 if ('[\x21-\x7e]*'.match(value)) {
21905 this.markInvalid(this.errors.PwdBadChar);
21906 this.errorMsg = this.errors.PwdBadChar;
21909 if (value.length < 6) {
21910 this.markInvalid(this.errors.PwdShort);
21911 this.errorMsg = this.errors.PwdShort;
21914 if (value.length > 16) {
21915 this.markInvalid(this.errors.PwdLong);
21916 this.errorMsg = this.errors.PwdLong;
21920 if (this.ClientSideStrongPassword(value)) {
21922 } else if (this.ClientSideMediumPassword(value)) {
21924 } else if (this.ClientSideWeakPassword(value)) {
21931 if (strength < 2) {
21932 //this.markInvalid(this.errors.TooWeak);
21933 this.errorMsg = this.errors.TooWeak;
21938 console.log('strength2: ' + strength);
21940 //var pm = this.trigger.child('div/div/div').dom;
21942 var pm = this.trigger.child('div/div');
21943 pm.removeClass(this.meterClass);
21944 pm.addClass(this.meterClass[strength]);
21946 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21948 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21950 this.errorMsg = '';
21954 CharacterSetChecks: function (type)
21957 this.fResult = false;
21960 isctype: function (character, type)
21963 case this.kCapitalLetter:
21964 if (character >= 'A' && character <= 'Z') {
21969 case this.kSmallLetter:
21970 if (character >= 'a' && character <= 'z') {
21976 if (character >= '0' && character <= '9') {
21981 case this.kPunctuation:
21982 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21993 IsLongEnough: function (pwd, size)
21995 return !(pwd == null || isNaN(size) || pwd.length < size);
21998 SpansEnoughCharacterSets: function (word, nb)
22000 if (!this.IsLongEnough(word, nb))
22005 var characterSetChecks = new Array(
22006 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
22007 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
22010 for (var index = 0; index < word.length; ++index) {
22011 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22012 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
22013 characterSetChecks[nCharSet].fResult = true;
22020 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22021 if (characterSetChecks[nCharSet].fResult) {
22026 if (nCharSets < nb) {
22032 ClientSideStrongPassword: function (pwd)
22034 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22037 ClientSideMediumPassword: function (pwd)
22039 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22042 ClientSideWeakPassword: function (pwd)
22044 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22047 })//<script type="text/javascript">
22050 * Based Ext JS Library 1.1.1
22051 * Copyright(c) 2006-2007, Ext JS, LLC.
22057 * @class Roo.HtmlEditorCore
22058 * @extends Roo.Component
22059 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22061 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22064 Roo.HtmlEditorCore = function(config){
22067 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22072 * @event initialize
22073 * Fires when the editor is fully initialized (including the iframe)
22074 * @param {Roo.HtmlEditorCore} this
22079 * Fires when the editor is first receives the focus. Any insertion must wait
22080 * until after this event.
22081 * @param {Roo.HtmlEditorCore} this
22085 * @event beforesync
22086 * Fires before the textarea is updated with content from the editor iframe. Return false
22087 * to cancel the sync.
22088 * @param {Roo.HtmlEditorCore} this
22089 * @param {String} html
22093 * @event beforepush
22094 * Fires before the iframe editor is updated with content from the textarea. Return false
22095 * to cancel the push.
22096 * @param {Roo.HtmlEditorCore} this
22097 * @param {String} html
22102 * Fires when the textarea is updated with content from the editor iframe.
22103 * @param {Roo.HtmlEditorCore} this
22104 * @param {String} html
22109 * Fires when the iframe editor is updated with content from the textarea.
22110 * @param {Roo.HtmlEditorCore} this
22111 * @param {String} html
22116 * @event editorevent
22117 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22118 * @param {Roo.HtmlEditorCore} this
22124 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22126 // defaults : white / black...
22127 this.applyBlacklists();
22134 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
22138 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
22144 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22149 * @cfg {Number} height (in pixels)
22153 * @cfg {Number} width (in pixels)
22158 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22161 stylesheets: false,
22166 // private properties
22167 validationEvent : false,
22169 initialized : false,
22171 sourceEditMode : false,
22172 onFocus : Roo.emptyFn,
22174 hideMode:'offsets',
22178 // blacklist + whitelisted elements..
22185 * Protected method that will not generally be called directly. It
22186 * is called when the editor initializes the iframe with HTML contents. Override this method if you
22187 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22189 getDocMarkup : function(){
22193 // inherit styels from page...??
22194 if (this.stylesheets === false) {
22196 Roo.get(document.head).select('style').each(function(node) {
22197 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22200 Roo.get(document.head).select('link').each(function(node) {
22201 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22204 } else if (!this.stylesheets.length) {
22206 st = '<style type="text/css">' +
22207 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22210 st = '<style type="text/css">' +
22215 st += '<style type="text/css">' +
22216 'IMG { cursor: pointer } ' +
22219 var cls = 'roo-htmleditor-body';
22221 if(this.bodyCls.length){
22222 cls += ' ' + this.bodyCls;
22225 return '<html><head>' + st +
22226 //<style type="text/css">' +
22227 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22229 ' </head><body class="' + cls + '"></body></html>';
22233 onRender : function(ct, position)
22236 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22237 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22240 this.el.dom.style.border = '0 none';
22241 this.el.dom.setAttribute('tabIndex', -1);
22242 this.el.addClass('x-hidden hide');
22246 if(Roo.isIE){ // fix IE 1px bogus margin
22247 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22251 this.frameId = Roo.id();
22255 var iframe = this.owner.wrap.createChild({
22257 cls: 'form-control', // bootstrap..
22259 name: this.frameId,
22260 frameBorder : 'no',
22261 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
22266 this.iframe = iframe.dom;
22268 this.assignDocWin();
22270 this.doc.designMode = 'on';
22273 this.doc.write(this.getDocMarkup());
22277 var task = { // must defer to wait for browser to be ready
22279 //console.log("run task?" + this.doc.readyState);
22280 this.assignDocWin();
22281 if(this.doc.body || this.doc.readyState == 'complete'){
22283 this.doc.designMode="on";
22287 Roo.TaskMgr.stop(task);
22288 this.initEditor.defer(10, this);
22295 Roo.TaskMgr.start(task);
22300 onResize : function(w, h)
22302 Roo.log('resize: ' +w + ',' + h );
22303 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22307 if(typeof w == 'number'){
22309 this.iframe.style.width = w + 'px';
22311 if(typeof h == 'number'){
22313 this.iframe.style.height = h + 'px';
22315 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22322 * Toggles the editor between standard and source edit mode.
22323 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22325 toggleSourceEdit : function(sourceEditMode){
22327 this.sourceEditMode = sourceEditMode === true;
22329 if(this.sourceEditMode){
22331 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
22334 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22335 //this.iframe.className = '';
22338 //this.setSize(this.owner.wrap.getSize());
22339 //this.fireEvent('editmodechange', this, this.sourceEditMode);
22346 * Protected method that will not generally be called directly. If you need/want
22347 * custom HTML cleanup, this is the method you should override.
22348 * @param {String} html The HTML to be cleaned
22349 * return {String} The cleaned HTML
22351 cleanHtml : function(html){
22352 html = String(html);
22353 if(html.length > 5){
22354 if(Roo.isSafari){ // strip safari nonsense
22355 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22358 if(html == ' '){
22365 * HTML Editor -> Textarea
22366 * Protected method that will not generally be called directly. Syncs the contents
22367 * of the editor iframe with the textarea.
22369 syncValue : function(){
22370 if(this.initialized){
22371 var bd = (this.doc.body || this.doc.documentElement);
22372 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22373 var html = bd.innerHTML;
22375 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22376 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22378 html = '<div style="'+m[0]+'">' + html + '</div>';
22381 html = this.cleanHtml(html);
22382 // fix up the special chars.. normaly like back quotes in word...
22383 // however we do not want to do this with chinese..
22384 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
22386 var cc = match.charCodeAt();
22388 // Get the character value, handling surrogate pairs
22389 if (match.length == 2) {
22390 // It's a surrogate pair, calculate the Unicode code point
22391 var high = match.charCodeAt(0) - 0xD800;
22392 var low = match.charCodeAt(1) - 0xDC00;
22393 cc = (high * 0x400) + low + 0x10000;
22395 (cc >= 0x4E00 && cc < 0xA000 ) ||
22396 (cc >= 0x3400 && cc < 0x4E00 ) ||
22397 (cc >= 0xf900 && cc < 0xfb00 )
22402 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
22403 return "&#" + cc + ";";
22410 if(this.owner.fireEvent('beforesync', this, html) !== false){
22411 this.el.dom.value = html;
22412 this.owner.fireEvent('sync', this, html);
22418 * Protected method that will not generally be called directly. Pushes the value of the textarea
22419 * into the iframe editor.
22421 pushValue : function(){
22422 if(this.initialized){
22423 var v = this.el.dom.value.trim();
22425 // if(v.length < 1){
22429 if(this.owner.fireEvent('beforepush', this, v) !== false){
22430 var d = (this.doc.body || this.doc.documentElement);
22432 this.cleanUpPaste();
22433 this.el.dom.value = d.innerHTML;
22434 this.owner.fireEvent('push', this, v);
22440 deferFocus : function(){
22441 this.focus.defer(10, this);
22445 focus : function(){
22446 if(this.win && !this.sourceEditMode){
22453 assignDocWin: function()
22455 var iframe = this.iframe;
22458 this.doc = iframe.contentWindow.document;
22459 this.win = iframe.contentWindow;
22461 // if (!Roo.get(this.frameId)) {
22464 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22465 // this.win = Roo.get(this.frameId).dom.contentWindow;
22467 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22471 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22472 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22477 initEditor : function(){
22478 //console.log("INIT EDITOR");
22479 this.assignDocWin();
22483 this.doc.designMode="on";
22485 this.doc.write(this.getDocMarkup());
22488 var dbody = (this.doc.body || this.doc.documentElement);
22489 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22490 // this copies styles from the containing element into thsi one..
22491 // not sure why we need all of this..
22492 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22494 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22495 //ss['background-attachment'] = 'fixed'; // w3c
22496 dbody.bgProperties = 'fixed'; // ie
22497 //Roo.DomHelper.applyStyles(dbody, ss);
22498 Roo.EventManager.on(this.doc, {
22499 //'mousedown': this.onEditorEvent,
22500 'mouseup': this.onEditorEvent,
22501 'dblclick': this.onEditorEvent,
22502 'click': this.onEditorEvent,
22503 'keyup': this.onEditorEvent,
22508 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22510 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22511 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22513 this.initialized = true;
22515 this.owner.fireEvent('initialize', this);
22520 onDestroy : function(){
22526 //for (var i =0; i < this.toolbars.length;i++) {
22527 // // fixme - ask toolbars for heights?
22528 // this.toolbars[i].onDestroy();
22531 //this.wrap.dom.innerHTML = '';
22532 //this.wrap.remove();
22537 onFirstFocus : function(){
22539 this.assignDocWin();
22542 this.activated = true;
22545 if(Roo.isGecko){ // prevent silly gecko errors
22547 var s = this.win.getSelection();
22548 if(!s.focusNode || s.focusNode.nodeType != 3){
22549 var r = s.getRangeAt(0);
22550 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22555 this.execCmd('useCSS', true);
22556 this.execCmd('styleWithCSS', false);
22559 this.owner.fireEvent('activate', this);
22563 adjustFont: function(btn){
22564 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22565 //if(Roo.isSafari){ // safari
22568 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22569 if(Roo.isSafari){ // safari
22570 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22571 v = (v < 10) ? 10 : v;
22572 v = (v > 48) ? 48 : v;
22573 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22578 v = Math.max(1, v+adjust);
22580 this.execCmd('FontSize', v );
22583 onEditorEvent : function(e)
22585 this.owner.fireEvent('editorevent', this, e);
22586 // this.updateToolbar();
22587 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22590 insertTag : function(tg)
22592 // could be a bit smarter... -> wrap the current selected tRoo..
22593 if (tg.toLowerCase() == 'span' ||
22594 tg.toLowerCase() == 'code' ||
22595 tg.toLowerCase() == 'sup' ||
22596 tg.toLowerCase() == 'sub'
22599 range = this.createRange(this.getSelection());
22600 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22601 wrappingNode.appendChild(range.extractContents());
22602 range.insertNode(wrappingNode);
22609 this.execCmd("formatblock", tg);
22613 insertText : function(txt)
22617 var range = this.createRange();
22618 range.deleteContents();
22619 //alert(Sender.getAttribute('label'));
22621 range.insertNode(this.doc.createTextNode(txt));
22627 * Executes a Midas editor command on the editor document and performs necessary focus and
22628 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22629 * @param {String} cmd The Midas command
22630 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22632 relayCmd : function(cmd, value){
22634 this.execCmd(cmd, value);
22635 this.owner.fireEvent('editorevent', this);
22636 //this.updateToolbar();
22637 this.owner.deferFocus();
22641 * Executes a Midas editor command directly on the editor document.
22642 * For visual commands, you should use {@link #relayCmd} instead.
22643 * <b>This should only be called after the editor is initialized.</b>
22644 * @param {String} cmd The Midas command
22645 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22647 execCmd : function(cmd, value){
22648 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22655 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22657 * @param {String} text | dom node..
22659 insertAtCursor : function(text)
22662 if(!this.activated){
22668 var r = this.doc.selection.createRange();
22679 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22683 // from jquery ui (MIT licenced)
22685 var win = this.win;
22687 if (win.getSelection && win.getSelection().getRangeAt) {
22688 range = win.getSelection().getRangeAt(0);
22689 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22690 range.insertNode(node);
22691 } else if (win.document.selection && win.document.selection.createRange) {
22692 // no firefox support
22693 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22694 win.document.selection.createRange().pasteHTML(txt);
22696 // no firefox support
22697 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22698 this.execCmd('InsertHTML', txt);
22707 mozKeyPress : function(e){
22709 var c = e.getCharCode(), cmd;
22712 c = String.fromCharCode(c).toLowerCase();
22726 this.cleanUpPaste.defer(100, this);
22734 e.preventDefault();
22742 fixKeys : function(){ // load time branching for fastest keydown performance
22744 return function(e){
22745 var k = e.getKey(), r;
22748 r = this.doc.selection.createRange();
22751 r.pasteHTML('    ');
22758 r = this.doc.selection.createRange();
22760 var target = r.parentElement();
22761 if(!target || target.tagName.toLowerCase() != 'li'){
22763 r.pasteHTML('<br />');
22769 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22770 this.cleanUpPaste.defer(100, this);
22776 }else if(Roo.isOpera){
22777 return function(e){
22778 var k = e.getKey();
22782 this.execCmd('InsertHTML','    ');
22785 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22786 this.cleanUpPaste.defer(100, this);
22791 }else if(Roo.isSafari){
22792 return function(e){
22793 var k = e.getKey();
22797 this.execCmd('InsertText','\t');
22801 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22802 this.cleanUpPaste.defer(100, this);
22810 getAllAncestors: function()
22812 var p = this.getSelectedNode();
22815 a.push(p); // push blank onto stack..
22816 p = this.getParentElement();
22820 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22824 a.push(this.doc.body);
22828 lastSelNode : false,
22831 getSelection : function()
22833 this.assignDocWin();
22834 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22837 getSelectedNode: function()
22839 // this may only work on Gecko!!!
22841 // should we cache this!!!!
22846 var range = this.createRange(this.getSelection()).cloneRange();
22849 var parent = range.parentElement();
22851 var testRange = range.duplicate();
22852 testRange.moveToElementText(parent);
22853 if (testRange.inRange(range)) {
22856 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22859 parent = parent.parentElement;
22864 // is ancestor a text element.
22865 var ac = range.commonAncestorContainer;
22866 if (ac.nodeType == 3) {
22867 ac = ac.parentNode;
22870 var ar = ac.childNodes;
22873 var other_nodes = [];
22874 var has_other_nodes = false;
22875 for (var i=0;i<ar.length;i++) {
22876 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22879 // fullly contained node.
22881 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22886 // probably selected..
22887 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22888 other_nodes.push(ar[i]);
22892 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22897 has_other_nodes = true;
22899 if (!nodes.length && other_nodes.length) {
22900 nodes= other_nodes;
22902 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22908 createRange: function(sel)
22910 // this has strange effects when using with
22911 // top toolbar - not sure if it's a great idea.
22912 //this.editor.contentWindow.focus();
22913 if (typeof sel != "undefined") {
22915 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22917 return this.doc.createRange();
22920 return this.doc.createRange();
22923 getParentElement: function()
22926 this.assignDocWin();
22927 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22929 var range = this.createRange(sel);
22932 var p = range.commonAncestorContainer;
22933 while (p.nodeType == 3) { // text node
22944 * Range intersection.. the hard stuff...
22948 * [ -- selected range --- ]
22952 * if end is before start or hits it. fail.
22953 * if start is after end or hits it fail.
22955 * if either hits (but other is outside. - then it's not
22961 // @see http://www.thismuchiknow.co.uk/?p=64.
22962 rangeIntersectsNode : function(range, node)
22964 var nodeRange = node.ownerDocument.createRange();
22966 nodeRange.selectNode(node);
22968 nodeRange.selectNodeContents(node);
22971 var rangeStartRange = range.cloneRange();
22972 rangeStartRange.collapse(true);
22974 var rangeEndRange = range.cloneRange();
22975 rangeEndRange.collapse(false);
22977 var nodeStartRange = nodeRange.cloneRange();
22978 nodeStartRange.collapse(true);
22980 var nodeEndRange = nodeRange.cloneRange();
22981 nodeEndRange.collapse(false);
22983 return rangeStartRange.compareBoundaryPoints(
22984 Range.START_TO_START, nodeEndRange) == -1 &&
22985 rangeEndRange.compareBoundaryPoints(
22986 Range.START_TO_START, nodeStartRange) == 1;
22990 rangeCompareNode : function(range, node)
22992 var nodeRange = node.ownerDocument.createRange();
22994 nodeRange.selectNode(node);
22996 nodeRange.selectNodeContents(node);
23000 range.collapse(true);
23002 nodeRange.collapse(true);
23004 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23005 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
23007 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23009 var nodeIsBefore = ss == 1;
23010 var nodeIsAfter = ee == -1;
23012 if (nodeIsBefore && nodeIsAfter) {
23015 if (!nodeIsBefore && nodeIsAfter) {
23016 return 1; //right trailed.
23019 if (nodeIsBefore && !nodeIsAfter) {
23020 return 2; // left trailed.
23026 // private? - in a new class?
23027 cleanUpPaste : function()
23029 // cleans up the whole document..
23030 Roo.log('cleanuppaste');
23032 this.cleanUpChildren(this.doc.body);
23033 var clean = this.cleanWordChars(this.doc.body.innerHTML);
23034 if (clean != this.doc.body.innerHTML) {
23035 this.doc.body.innerHTML = clean;
23040 cleanWordChars : function(input) {// change the chars to hex code
23041 var he = Roo.HtmlEditorCore;
23043 var output = input;
23044 Roo.each(he.swapCodes, function(sw) {
23045 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23047 output = output.replace(swapper, sw[1]);
23054 cleanUpChildren : function (n)
23056 if (!n.childNodes.length) {
23059 for (var i = n.childNodes.length-1; i > -1 ; i--) {
23060 this.cleanUpChild(n.childNodes[i]);
23067 cleanUpChild : function (node)
23070 //console.log(node);
23071 if (node.nodeName == "#text") {
23072 // clean up silly Windows -- stuff?
23075 if (node.nodeName == "#comment") {
23076 node.parentNode.removeChild(node);
23077 // clean up silly Windows -- stuff?
23080 var lcname = node.tagName.toLowerCase();
23081 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23082 // whitelist of tags..
23084 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23086 node.parentNode.removeChild(node);
23091 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23093 // spans with no attributes - just remove them..
23094 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
23095 remove_keep_children = true;
23098 // remove <a name=....> as rendering on yahoo mailer is borked with this.
23099 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23101 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23102 // remove_keep_children = true;
23105 if (remove_keep_children) {
23106 this.cleanUpChildren(node);
23107 // inserts everything just before this node...
23108 while (node.childNodes.length) {
23109 var cn = node.childNodes[0];
23110 node.removeChild(cn);
23111 node.parentNode.insertBefore(cn, node);
23113 node.parentNode.removeChild(node);
23117 if (!node.attributes || !node.attributes.length) {
23122 this.cleanUpChildren(node);
23126 function cleanAttr(n,v)
23129 if (v.match(/^\./) || v.match(/^\//)) {
23132 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23135 if (v.match(/^#/)) {
23138 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23139 node.removeAttribute(n);
23143 var cwhite = this.cwhite;
23144 var cblack = this.cblack;
23146 function cleanStyle(n,v)
23148 if (v.match(/expression/)) { //XSS?? should we even bother..
23149 node.removeAttribute(n);
23153 var parts = v.split(/;/);
23156 Roo.each(parts, function(p) {
23157 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23161 var l = p.split(':').shift().replace(/\s+/g,'');
23162 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23164 if ( cwhite.length && cblack.indexOf(l) > -1) {
23165 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23166 //node.removeAttribute(n);
23170 // only allow 'c whitelisted system attributes'
23171 if ( cwhite.length && cwhite.indexOf(l) < 0) {
23172 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23173 //node.removeAttribute(n);
23183 if (clean.length) {
23184 node.setAttribute(n, clean.join(';'));
23186 node.removeAttribute(n);
23192 for (var i = node.attributes.length-1; i > -1 ; i--) {
23193 var a = node.attributes[i];
23196 if (a.name.toLowerCase().substr(0,2)=='on') {
23197 node.removeAttribute(a.name);
23200 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23201 node.removeAttribute(a.name);
23204 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23205 cleanAttr(a.name,a.value); // fixme..
23208 if (a.name == 'style') {
23209 cleanStyle(a.name,a.value);
23212 /// clean up MS crap..
23213 // tecnically this should be a list of valid class'es..
23216 if (a.name == 'class') {
23217 if (a.value.match(/^Mso/)) {
23218 node.removeAttribute('class');
23221 if (a.value.match(/^body$/)) {
23222 node.removeAttribute('class');
23233 this.cleanUpChildren(node);
23239 * Clean up MS wordisms...
23241 cleanWord : function(node)
23244 this.cleanWord(this.doc.body);
23249 node.nodeName == 'SPAN' &&
23250 !node.hasAttributes() &&
23251 node.childNodes.length == 1 &&
23252 node.firstChild.nodeName == "#text"
23254 var textNode = node.firstChild;
23255 node.removeChild(textNode);
23256 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
23257 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
23259 node.parentNode.insertBefore(textNode, node);
23260 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
23261 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
23263 node.parentNode.removeChild(node);
23266 if (node.nodeName == "#text") {
23267 // clean up silly Windows -- stuff?
23270 if (node.nodeName == "#comment") {
23271 node.parentNode.removeChild(node);
23272 // clean up silly Windows -- stuff?
23276 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23277 node.parentNode.removeChild(node);
23280 //Roo.log(node.tagName);
23281 // remove - but keep children..
23282 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
23283 //Roo.log('-- removed');
23284 while (node.childNodes.length) {
23285 var cn = node.childNodes[0];
23286 node.removeChild(cn);
23287 node.parentNode.insertBefore(cn, node);
23288 // move node to parent - and clean it..
23289 this.cleanWord(cn);
23291 node.parentNode.removeChild(node);
23292 /// no need to iterate chidlren = it's got none..
23293 //this.iterateChildren(node, this.cleanWord);
23297 if (node.className.length) {
23299 var cn = node.className.split(/\W+/);
23301 Roo.each(cn, function(cls) {
23302 if (cls.match(/Mso[a-zA-Z]+/)) {
23307 node.className = cna.length ? cna.join(' ') : '';
23309 node.removeAttribute("class");
23313 if (node.hasAttribute("lang")) {
23314 node.removeAttribute("lang");
23317 if (node.hasAttribute("style")) {
23319 var styles = node.getAttribute("style").split(";");
23321 Roo.each(styles, function(s) {
23322 if (!s.match(/:/)) {
23325 var kv = s.split(":");
23326 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23329 // what ever is left... we allow.
23332 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23333 if (!nstyle.length) {
23334 node.removeAttribute('style');
23337 this.iterateChildren(node, this.cleanWord);
23343 * iterateChildren of a Node, calling fn each time, using this as the scole..
23344 * @param {DomNode} node node to iterate children of.
23345 * @param {Function} fn method of this class to call on each item.
23347 iterateChildren : function(node, fn)
23349 if (!node.childNodes.length) {
23352 for (var i = node.childNodes.length-1; i > -1 ; i--) {
23353 fn.call(this, node.childNodes[i])
23359 * cleanTableWidths.
23361 * Quite often pasting from word etc.. results in tables with column and widths.
23362 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23365 cleanTableWidths : function(node)
23370 this.cleanTableWidths(this.doc.body);
23375 if (node.nodeName == "#text" || node.nodeName == "#comment") {
23378 Roo.log(node.tagName);
23379 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23380 this.iterateChildren(node, this.cleanTableWidths);
23383 if (node.hasAttribute('width')) {
23384 node.removeAttribute('width');
23388 if (node.hasAttribute("style")) {
23391 var styles = node.getAttribute("style").split(";");
23393 Roo.each(styles, function(s) {
23394 if (!s.match(/:/)) {
23397 var kv = s.split(":");
23398 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23401 // what ever is left... we allow.
23404 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23405 if (!nstyle.length) {
23406 node.removeAttribute('style');
23410 this.iterateChildren(node, this.cleanTableWidths);
23418 domToHTML : function(currentElement, depth, nopadtext) {
23420 depth = depth || 0;
23421 nopadtext = nopadtext || false;
23423 if (!currentElement) {
23424 return this.domToHTML(this.doc.body);
23427 //Roo.log(currentElement);
23429 var allText = false;
23430 var nodeName = currentElement.nodeName;
23431 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23433 if (nodeName == '#text') {
23435 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23440 if (nodeName != 'BODY') {
23443 // Prints the node tagName, such as <A>, <IMG>, etc
23446 for(i = 0; i < currentElement.attributes.length;i++) {
23448 var aname = currentElement.attributes.item(i).name;
23449 if (!currentElement.attributes.item(i).value.length) {
23452 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23455 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23464 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23467 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23472 // Traverse the tree
23474 var currentElementChild = currentElement.childNodes.item(i);
23475 var allText = true;
23476 var innerHTML = '';
23478 while (currentElementChild) {
23479 // Formatting code (indent the tree so it looks nice on the screen)
23480 var nopad = nopadtext;
23481 if (lastnode == 'SPAN') {
23485 if (currentElementChild.nodeName == '#text') {
23486 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23487 toadd = nopadtext ? toadd : toadd.trim();
23488 if (!nopad && toadd.length > 80) {
23489 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23491 innerHTML += toadd;
23494 currentElementChild = currentElement.childNodes.item(i);
23500 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23502 // Recursively traverse the tree structure of the child node
23503 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23504 lastnode = currentElementChild.nodeName;
23506 currentElementChild=currentElement.childNodes.item(i);
23512 // The remaining code is mostly for formatting the tree
23513 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23518 ret+= "</"+tagName+">";
23524 applyBlacklists : function()
23526 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23527 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23531 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23532 if (b.indexOf(tag) > -1) {
23535 this.white.push(tag);
23539 Roo.each(w, function(tag) {
23540 if (b.indexOf(tag) > -1) {
23543 if (this.white.indexOf(tag) > -1) {
23546 this.white.push(tag);
23551 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23552 if (w.indexOf(tag) > -1) {
23555 this.black.push(tag);
23559 Roo.each(b, function(tag) {
23560 if (w.indexOf(tag) > -1) {
23563 if (this.black.indexOf(tag) > -1) {
23566 this.black.push(tag);
23571 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23572 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23576 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23577 if (b.indexOf(tag) > -1) {
23580 this.cwhite.push(tag);
23584 Roo.each(w, function(tag) {
23585 if (b.indexOf(tag) > -1) {
23588 if (this.cwhite.indexOf(tag) > -1) {
23591 this.cwhite.push(tag);
23596 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23597 if (w.indexOf(tag) > -1) {
23600 this.cblack.push(tag);
23604 Roo.each(b, function(tag) {
23605 if (w.indexOf(tag) > -1) {
23608 if (this.cblack.indexOf(tag) > -1) {
23611 this.cblack.push(tag);
23616 setStylesheets : function(stylesheets)
23618 if(typeof(stylesheets) == 'string'){
23619 Roo.get(this.iframe.contentDocument.head).createChild({
23621 rel : 'stylesheet',
23630 Roo.each(stylesheets, function(s) {
23635 Roo.get(_this.iframe.contentDocument.head).createChild({
23637 rel : 'stylesheet',
23646 removeStylesheets : function()
23650 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23655 setStyle : function(style)
23657 Roo.get(this.iframe.contentDocument.head).createChild({
23666 // hide stuff that is not compatible
23680 * @event specialkey
23684 * @cfg {String} fieldClass @hide
23687 * @cfg {String} focusClass @hide
23690 * @cfg {String} autoCreate @hide
23693 * @cfg {String} inputType @hide
23696 * @cfg {String} invalidClass @hide
23699 * @cfg {String} invalidText @hide
23702 * @cfg {String} msgFx @hide
23705 * @cfg {String} validateOnBlur @hide
23709 Roo.HtmlEditorCore.white = [
23710 'area', 'br', 'img', 'input', 'hr', 'wbr',
23712 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23713 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23714 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23715 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23716 'table', 'ul', 'xmp',
23718 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23721 'dir', 'menu', 'ol', 'ul', 'dl',
23727 Roo.HtmlEditorCore.black = [
23728 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23730 'base', 'basefont', 'bgsound', 'blink', 'body',
23731 'frame', 'frameset', 'head', 'html', 'ilayer',
23732 'iframe', 'layer', 'link', 'meta', 'object',
23733 'script', 'style' ,'title', 'xml' // clean later..
23735 Roo.HtmlEditorCore.clean = [
23736 'script', 'style', 'title', 'xml'
23738 Roo.HtmlEditorCore.remove = [
23743 Roo.HtmlEditorCore.ablack = [
23747 Roo.HtmlEditorCore.aclean = [
23748 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23752 Roo.HtmlEditorCore.pwhite= [
23753 'http', 'https', 'mailto'
23756 // white listed style attributes.
23757 Roo.HtmlEditorCore.cwhite= [
23758 // 'text-align', /// default is to allow most things..
23764 // black listed style attributes.
23765 Roo.HtmlEditorCore.cblack= [
23766 // 'font-size' -- this can be set by the project
23770 Roo.HtmlEditorCore.swapCodes =[
23789 * @class Roo.bootstrap.HtmlEditor
23790 * @extends Roo.bootstrap.TextArea
23791 * Bootstrap HtmlEditor class
23794 * Create a new HtmlEditor
23795 * @param {Object} config The config object
23798 Roo.bootstrap.HtmlEditor = function(config){
23799 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23800 if (!this.toolbars) {
23801 this.toolbars = [];
23804 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23807 * @event initialize
23808 * Fires when the editor is fully initialized (including the iframe)
23809 * @param {HtmlEditor} this
23814 * Fires when the editor is first receives the focus. Any insertion must wait
23815 * until after this event.
23816 * @param {HtmlEditor} this
23820 * @event beforesync
23821 * Fires before the textarea is updated with content from the editor iframe. Return false
23822 * to cancel the sync.
23823 * @param {HtmlEditor} this
23824 * @param {String} html
23828 * @event beforepush
23829 * Fires before the iframe editor is updated with content from the textarea. Return false
23830 * to cancel the push.
23831 * @param {HtmlEditor} this
23832 * @param {String} html
23837 * Fires when the textarea is updated with content from the editor iframe.
23838 * @param {HtmlEditor} this
23839 * @param {String} html
23844 * Fires when the iframe editor is updated with content from the textarea.
23845 * @param {HtmlEditor} this
23846 * @param {String} html
23850 * @event editmodechange
23851 * Fires when the editor switches edit modes
23852 * @param {HtmlEditor} this
23853 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23855 editmodechange: true,
23857 * @event editorevent
23858 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23859 * @param {HtmlEditor} this
23863 * @event firstfocus
23864 * Fires when on first focus - needed by toolbars..
23865 * @param {HtmlEditor} this
23870 * Auto save the htmlEditor value as a file into Events
23871 * @param {HtmlEditor} this
23875 * @event savedpreview
23876 * preview the saved version of htmlEditor
23877 * @param {HtmlEditor} this
23884 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23888 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23893 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23898 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23903 * @cfg {Number} height (in pixels)
23907 * @cfg {Number} width (in pixels)
23912 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23915 stylesheets: false,
23920 // private properties
23921 validationEvent : false,
23923 initialized : false,
23926 onFocus : Roo.emptyFn,
23928 hideMode:'offsets',
23930 tbContainer : false,
23934 toolbarContainer :function() {
23935 return this.wrap.select('.x-html-editor-tb',true).first();
23939 * Protected method that will not generally be called directly. It
23940 * is called when the editor creates its toolbar. Override this method if you need to
23941 * add custom toolbar buttons.
23942 * @param {HtmlEditor} editor
23944 createToolbar : function(){
23945 Roo.log('renewing');
23946 Roo.log("create toolbars");
23948 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23949 this.toolbars[0].render(this.toolbarContainer());
23953 // if (!editor.toolbars || !editor.toolbars.length) {
23954 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23957 // for (var i =0 ; i < editor.toolbars.length;i++) {
23958 // editor.toolbars[i] = Roo.factory(
23959 // typeof(editor.toolbars[i]) == 'string' ?
23960 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23961 // Roo.bootstrap.HtmlEditor);
23962 // editor.toolbars[i].init(editor);
23968 onRender : function(ct, position)
23970 // Roo.log("Call onRender: " + this.xtype);
23972 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23974 this.wrap = this.inputEl().wrap({
23975 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23978 this.editorcore.onRender(ct, position);
23980 if (this.resizable) {
23981 this.resizeEl = new Roo.Resizable(this.wrap, {
23985 minHeight : this.height,
23986 height: this.height,
23987 handles : this.resizable,
23990 resize : function(r, w, h) {
23991 _t.onResize(w,h); // -something
23997 this.createToolbar(this);
24000 if(!this.width && this.resizable){
24001 this.setSize(this.wrap.getSize());
24003 if (this.resizeEl) {
24004 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24005 // should trigger onReize..
24011 onResize : function(w, h)
24013 Roo.log('resize: ' +w + ',' + h );
24014 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
24018 if(this.inputEl() ){
24019 if(typeof w == 'number'){
24020 var aw = w - this.wrap.getFrameWidth('lr');
24021 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
24024 if(typeof h == 'number'){
24025 var tbh = -11; // fixme it needs to tool bar size!
24026 for (var i =0; i < this.toolbars.length;i++) {
24027 // fixme - ask toolbars for heights?
24028 tbh += this.toolbars[i].el.getHeight();
24029 //if (this.toolbars[i].footer) {
24030 // tbh += this.toolbars[i].footer.el.getHeight();
24038 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24039 ah -= 5; // knock a few pixes off for look..
24040 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24044 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24045 this.editorcore.onResize(ew,eh);
24050 * Toggles the editor between standard and source edit mode.
24051 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24053 toggleSourceEdit : function(sourceEditMode)
24055 this.editorcore.toggleSourceEdit(sourceEditMode);
24057 if(this.editorcore.sourceEditMode){
24058 Roo.log('editor - showing textarea');
24061 // Roo.log(this.syncValue());
24063 this.inputEl().removeClass(['hide', 'x-hidden']);
24064 this.inputEl().dom.removeAttribute('tabIndex');
24065 this.inputEl().focus();
24067 Roo.log('editor - hiding textarea');
24069 // Roo.log(this.pushValue());
24072 this.inputEl().addClass(['hide', 'x-hidden']);
24073 this.inputEl().dom.setAttribute('tabIndex', -1);
24074 //this.deferFocus();
24077 if(this.resizable){
24078 this.setSize(this.wrap.getSize());
24081 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24084 // private (for BoxComponent)
24085 adjustSize : Roo.BoxComponent.prototype.adjustSize,
24087 // private (for BoxComponent)
24088 getResizeEl : function(){
24092 // private (for BoxComponent)
24093 getPositionEl : function(){
24098 initEvents : function(){
24099 this.originalValue = this.getValue();
24103 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24106 // markInvalid : Roo.emptyFn,
24108 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24111 // clearInvalid : Roo.emptyFn,
24113 setValue : function(v){
24114 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24115 this.editorcore.pushValue();
24120 deferFocus : function(){
24121 this.focus.defer(10, this);
24125 focus : function(){
24126 this.editorcore.focus();
24132 onDestroy : function(){
24138 for (var i =0; i < this.toolbars.length;i++) {
24139 // fixme - ask toolbars for heights?
24140 this.toolbars[i].onDestroy();
24143 this.wrap.dom.innerHTML = '';
24144 this.wrap.remove();
24149 onFirstFocus : function(){
24150 //Roo.log("onFirstFocus");
24151 this.editorcore.onFirstFocus();
24152 for (var i =0; i < this.toolbars.length;i++) {
24153 this.toolbars[i].onFirstFocus();
24159 syncValue : function()
24161 this.editorcore.syncValue();
24164 pushValue : function()
24166 this.editorcore.pushValue();
24170 // hide stuff that is not compatible
24184 * @event specialkey
24188 * @cfg {String} fieldClass @hide
24191 * @cfg {String} focusClass @hide
24194 * @cfg {String} autoCreate @hide
24197 * @cfg {String} inputType @hide
24201 * @cfg {String} invalidText @hide
24204 * @cfg {String} msgFx @hide
24207 * @cfg {String} validateOnBlur @hide
24216 Roo.namespace('Roo.bootstrap.htmleditor');
24218 * @class Roo.bootstrap.HtmlEditorToolbar1
24224 new Roo.bootstrap.HtmlEditor({
24227 new Roo.bootstrap.HtmlEditorToolbar1({
24228 disable : { fonts: 1 , format: 1, ..., ... , ...],
24234 * @cfg {Object} disable List of elements to disable..
24235 * @cfg {Array} btns List of additional buttons.
24239 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24242 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24245 Roo.apply(this, config);
24247 // default disabled, based on 'good practice'..
24248 this.disable = this.disable || {};
24249 Roo.applyIf(this.disable, {
24252 specialElements : true
24254 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24256 this.editor = config.editor;
24257 this.editorcore = config.editor.editorcore;
24259 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24261 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24262 // dont call parent... till later.
24264 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
24269 editorcore : false,
24274 "h1","h2","h3","h4","h5","h6",
24276 "abbr", "acronym", "address", "cite", "samp", "var",
24280 onRender : function(ct, position)
24282 // Roo.log("Call onRender: " + this.xtype);
24284 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24286 this.el.dom.style.marginBottom = '0';
24288 var editorcore = this.editorcore;
24289 var editor= this.editor;
24292 var btn = function(id,cmd , toggle, handler, html){
24294 var event = toggle ? 'toggle' : 'click';
24299 xns: Roo.bootstrap,
24303 enableToggle:toggle !== false,
24305 pressed : toggle ? false : null,
24308 a.listeners[toggle ? 'toggle' : 'click'] = function() {
24309 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
24315 // var cb_box = function...
24320 xns: Roo.bootstrap,
24325 xns: Roo.bootstrap,
24329 Roo.each(this.formats, function(f) {
24330 style.menu.items.push({
24332 xns: Roo.bootstrap,
24333 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24338 editorcore.insertTag(this.tagname);
24345 children.push(style);
24347 btn('bold',false,true);
24348 btn('italic',false,true);
24349 btn('align-left', 'justifyleft',true);
24350 btn('align-center', 'justifycenter',true);
24351 btn('align-right' , 'justifyright',true);
24352 btn('link', false, false, function(btn) {
24353 //Roo.log("create link?");
24354 var url = prompt(this.createLinkText, this.defaultLinkValue);
24355 if(url && url != 'http:/'+'/'){
24356 this.editorcore.relayCmd('createlink', url);
24359 btn('list','insertunorderedlist',true);
24360 btn('pencil', false,true, function(btn){
24362 this.toggleSourceEdit(btn.pressed);
24365 if (this.editor.btns.length > 0) {
24366 for (var i = 0; i<this.editor.btns.length; i++) {
24367 children.push(this.editor.btns[i]);
24375 xns: Roo.bootstrap,
24380 xns: Roo.bootstrap,
24385 cog.menu.items.push({
24387 xns: Roo.bootstrap,
24388 html : Clean styles,
24393 editorcore.insertTag(this.tagname);
24402 this.xtype = 'NavSimplebar';
24404 for(var i=0;i< children.length;i++) {
24406 this.buttons.add(this.addxtypeChild(children[i]));
24410 editor.on('editorevent', this.updateToolbar, this);
24412 onBtnClick : function(id)
24414 this.editorcore.relayCmd(id);
24415 this.editorcore.focus();
24419 * Protected method that will not generally be called directly. It triggers
24420 * a toolbar update by reading the markup state of the current selection in the editor.
24422 updateToolbar: function(){
24424 if(!this.editorcore.activated){
24425 this.editor.onFirstFocus(); // is this neeed?
24429 var btns = this.buttons;
24430 var doc = this.editorcore.doc;
24431 btns.get('bold').setActive(doc.queryCommandState('bold'));
24432 btns.get('italic').setActive(doc.queryCommandState('italic'));
24433 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24435 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24436 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24437 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24439 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24440 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24443 var ans = this.editorcore.getAllAncestors();
24444 if (this.formatCombo) {
24447 var store = this.formatCombo.store;
24448 this.formatCombo.setValue("");
24449 for (var i =0; i < ans.length;i++) {
24450 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24452 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24460 // hides menus... - so this cant be on a menu...
24461 Roo.bootstrap.MenuMgr.hideAll();
24463 Roo.bootstrap.MenuMgr.hideAll();
24464 //this.editorsyncValue();
24466 onFirstFocus: function() {
24467 this.buttons.each(function(item){
24471 toggleSourceEdit : function(sourceEditMode){
24474 if(sourceEditMode){
24475 Roo.log("disabling buttons");
24476 this.buttons.each( function(item){
24477 if(item.cmd != 'pencil'){
24483 Roo.log("enabling buttons");
24484 if(this.editorcore.initialized){
24485 this.buttons.each( function(item){
24491 Roo.log("calling toggole on editor");
24492 // tell the editor that it's been pressed..
24493 this.editor.toggleSourceEdit(sourceEditMode);
24503 * @class Roo.bootstrap.Table.AbstractSelectionModel
24504 * @extends Roo.util.Observable
24505 * Abstract base class for grid SelectionModels. It provides the interface that should be
24506 * implemented by descendant classes. This class should not be directly instantiated.
24509 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24510 this.locked = false;
24511 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24515 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24516 /** @ignore Called by the grid automatically. Do not call directly. */
24517 init : function(grid){
24523 * Locks the selections.
24526 this.locked = true;
24530 * Unlocks the selections.
24532 unlock : function(){
24533 this.locked = false;
24537 * Returns true if the selections are locked.
24538 * @return {Boolean}
24540 isLocked : function(){
24541 return this.locked;
24545 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24546 * @class Roo.bootstrap.Table.RowSelectionModel
24547 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24548 * It supports multiple selections and keyboard selection/navigation.
24550 * @param {Object} config
24553 Roo.bootstrap.Table.RowSelectionModel = function(config){
24554 Roo.apply(this, config);
24555 this.selections = new Roo.util.MixedCollection(false, function(o){
24560 this.lastActive = false;
24564 * @event selectionchange
24565 * Fires when the selection changes
24566 * @param {SelectionModel} this
24568 "selectionchange" : true,
24570 * @event afterselectionchange
24571 * Fires after the selection changes (eg. by key press or clicking)
24572 * @param {SelectionModel} this
24574 "afterselectionchange" : true,
24576 * @event beforerowselect
24577 * Fires when a row is selected being selected, return false to cancel.
24578 * @param {SelectionModel} this
24579 * @param {Number} rowIndex The selected index
24580 * @param {Boolean} keepExisting False if other selections will be cleared
24582 "beforerowselect" : true,
24585 * Fires when a row is selected.
24586 * @param {SelectionModel} this
24587 * @param {Number} rowIndex The selected index
24588 * @param {Roo.data.Record} r The record
24590 "rowselect" : true,
24592 * @event rowdeselect
24593 * Fires when a row is deselected.
24594 * @param {SelectionModel} this
24595 * @param {Number} rowIndex The selected index
24597 "rowdeselect" : true
24599 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24600 this.locked = false;
24603 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24605 * @cfg {Boolean} singleSelect
24606 * True to allow selection of only one row at a time (defaults to false)
24608 singleSelect : false,
24611 initEvents : function()
24614 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24615 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24616 //}else{ // allow click to work like normal
24617 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24619 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24620 this.grid.on("rowclick", this.handleMouseDown, this);
24622 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24623 "up" : function(e){
24625 this.selectPrevious(e.shiftKey);
24626 }else if(this.last !== false && this.lastActive !== false){
24627 var last = this.last;
24628 this.selectRange(this.last, this.lastActive-1);
24629 this.grid.getView().focusRow(this.lastActive);
24630 if(last !== false){
24634 this.selectFirstRow();
24636 this.fireEvent("afterselectionchange", this);
24638 "down" : function(e){
24640 this.selectNext(e.shiftKey);
24641 }else if(this.last !== false && this.lastActive !== false){
24642 var last = this.last;
24643 this.selectRange(this.last, this.lastActive+1);
24644 this.grid.getView().focusRow(this.lastActive);
24645 if(last !== false){
24649 this.selectFirstRow();
24651 this.fireEvent("afterselectionchange", this);
24655 this.grid.store.on('load', function(){
24656 this.selections.clear();
24659 var view = this.grid.view;
24660 view.on("refresh", this.onRefresh, this);
24661 view.on("rowupdated", this.onRowUpdated, this);
24662 view.on("rowremoved", this.onRemove, this);
24667 onRefresh : function()
24669 var ds = this.grid.store, i, v = this.grid.view;
24670 var s = this.selections;
24671 s.each(function(r){
24672 if((i = ds.indexOfId(r.id)) != -1){
24681 onRemove : function(v, index, r){
24682 this.selections.remove(r);
24686 onRowUpdated : function(v, index, r){
24687 if(this.isSelected(r)){
24688 v.onRowSelect(index);
24694 * @param {Array} records The records to select
24695 * @param {Boolean} keepExisting (optional) True to keep existing selections
24697 selectRecords : function(records, keepExisting)
24700 this.clearSelections();
24702 var ds = this.grid.store;
24703 for(var i = 0, len = records.length; i < len; i++){
24704 this.selectRow(ds.indexOf(records[i]), true);
24709 * Gets the number of selected rows.
24712 getCount : function(){
24713 return this.selections.length;
24717 * Selects the first row in the grid.
24719 selectFirstRow : function(){
24724 * Select the last row.
24725 * @param {Boolean} keepExisting (optional) True to keep existing selections
24727 selectLastRow : function(keepExisting){
24728 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24729 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24733 * Selects the row immediately following the last selected row.
24734 * @param {Boolean} keepExisting (optional) True to keep existing selections
24736 selectNext : function(keepExisting)
24738 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24739 this.selectRow(this.last+1, keepExisting);
24740 this.grid.getView().focusRow(this.last);
24745 * Selects the row that precedes the last selected row.
24746 * @param {Boolean} keepExisting (optional) True to keep existing selections
24748 selectPrevious : function(keepExisting){
24750 this.selectRow(this.last-1, keepExisting);
24751 this.grid.getView().focusRow(this.last);
24756 * Returns the selected records
24757 * @return {Array} Array of selected records
24759 getSelections : function(){
24760 return [].concat(this.selections.items);
24764 * Returns the first selected record.
24767 getSelected : function(){
24768 return this.selections.itemAt(0);
24773 * Clears all selections.
24775 clearSelections : function(fast)
24781 var ds = this.grid.store;
24782 var s = this.selections;
24783 s.each(function(r){
24784 this.deselectRow(ds.indexOfId(r.id));
24788 this.selections.clear();
24795 * Selects all rows.
24797 selectAll : function(){
24801 this.selections.clear();
24802 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24803 this.selectRow(i, true);
24808 * Returns True if there is a selection.
24809 * @return {Boolean}
24811 hasSelection : function(){
24812 return this.selections.length > 0;
24816 * Returns True if the specified row is selected.
24817 * @param {Number/Record} record The record or index of the record to check
24818 * @return {Boolean}
24820 isSelected : function(index){
24821 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24822 return (r && this.selections.key(r.id) ? true : false);
24826 * Returns True if the specified record id is selected.
24827 * @param {String} id The id of record to check
24828 * @return {Boolean}
24830 isIdSelected : function(id){
24831 return (this.selections.key(id) ? true : false);
24836 handleMouseDBClick : function(e, t){
24840 handleMouseDown : function(e, t)
24842 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24843 if(this.isLocked() || rowIndex < 0 ){
24846 if(e.shiftKey && this.last !== false){
24847 var last = this.last;
24848 this.selectRange(last, rowIndex, e.ctrlKey);
24849 this.last = last; // reset the last
24853 var isSelected = this.isSelected(rowIndex);
24854 //Roo.log("select row:" + rowIndex);
24856 this.deselectRow(rowIndex);
24858 this.selectRow(rowIndex, true);
24862 if(e.button !== 0 && isSelected){
24863 alert('rowIndex 2: ' + rowIndex);
24864 view.focusRow(rowIndex);
24865 }else if(e.ctrlKey && isSelected){
24866 this.deselectRow(rowIndex);
24867 }else if(!isSelected){
24868 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24869 view.focusRow(rowIndex);
24873 this.fireEvent("afterselectionchange", this);
24876 handleDragableRowClick : function(grid, rowIndex, e)
24878 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24879 this.selectRow(rowIndex, false);
24880 grid.view.focusRow(rowIndex);
24881 this.fireEvent("afterselectionchange", this);
24886 * Selects multiple rows.
24887 * @param {Array} rows Array of the indexes of the row to select
24888 * @param {Boolean} keepExisting (optional) True to keep existing selections
24890 selectRows : function(rows, keepExisting){
24892 this.clearSelections();
24894 for(var i = 0, len = rows.length; i < len; i++){
24895 this.selectRow(rows[i], true);
24900 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24901 * @param {Number} startRow The index of the first row in the range
24902 * @param {Number} endRow The index of the last row in the range
24903 * @param {Boolean} keepExisting (optional) True to retain existing selections
24905 selectRange : function(startRow, endRow, keepExisting){
24910 this.clearSelections();
24912 if(startRow <= endRow){
24913 for(var i = startRow; i <= endRow; i++){
24914 this.selectRow(i, true);
24917 for(var i = startRow; i >= endRow; i--){
24918 this.selectRow(i, true);
24924 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24925 * @param {Number} startRow The index of the first row in the range
24926 * @param {Number} endRow The index of the last row in the range
24928 deselectRange : function(startRow, endRow, preventViewNotify){
24932 for(var i = startRow; i <= endRow; i++){
24933 this.deselectRow(i, preventViewNotify);
24939 * @param {Number} row The index of the row to select
24940 * @param {Boolean} keepExisting (optional) True to keep existing selections
24942 selectRow : function(index, keepExisting, preventViewNotify)
24944 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24947 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24948 if(!keepExisting || this.singleSelect){
24949 this.clearSelections();
24952 var r = this.grid.store.getAt(index);
24953 //console.log('selectRow - record id :' + r.id);
24955 this.selections.add(r);
24956 this.last = this.lastActive = index;
24957 if(!preventViewNotify){
24958 var proxy = new Roo.Element(
24959 this.grid.getRowDom(index)
24961 proxy.addClass('bg-info info');
24963 this.fireEvent("rowselect", this, index, r);
24964 this.fireEvent("selectionchange", this);
24970 * @param {Number} row The index of the row to deselect
24972 deselectRow : function(index, preventViewNotify)
24977 if(this.last == index){
24980 if(this.lastActive == index){
24981 this.lastActive = false;
24984 var r = this.grid.store.getAt(index);
24989 this.selections.remove(r);
24990 //.console.log('deselectRow - record id :' + r.id);
24991 if(!preventViewNotify){
24993 var proxy = new Roo.Element(
24994 this.grid.getRowDom(index)
24996 proxy.removeClass('bg-info info');
24998 this.fireEvent("rowdeselect", this, index);
24999 this.fireEvent("selectionchange", this);
25003 restoreLast : function(){
25005 this.last = this._last;
25010 acceptsNav : function(row, col, cm){
25011 return !cm.isHidden(col) && cm.isCellEditable(col, row);
25015 onEditorKey : function(field, e){
25016 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
25021 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
25023 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
25025 }else if(k == e.ENTER && !e.ctrlKey){
25029 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
25031 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25033 }else if(k == e.ESC){
25037 g.startEditing(newCell[0], newCell[1]);
25043 * Ext JS Library 1.1.1
25044 * Copyright(c) 2006-2007, Ext JS, LLC.
25046 * Originally Released Under LGPL - original licence link has changed is not relivant.
25049 * <script type="text/javascript">
25053 * @class Roo.bootstrap.PagingToolbar
25054 * @extends Roo.bootstrap.NavSimplebar
25055 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
25057 * Create a new PagingToolbar
25058 * @param {Object} config The config object
25059 * @param {Roo.data.Store} store
25061 Roo.bootstrap.PagingToolbar = function(config)
25063 // old args format still supported... - xtype is prefered..
25064 // created from xtype...
25066 this.ds = config.dataSource;
25068 if (config.store && !this.ds) {
25069 this.store= Roo.factory(config.store, Roo.data);
25070 this.ds = this.store;
25071 this.ds.xmodule = this.xmodule || false;
25074 this.toolbarItems = [];
25075 if (config.items) {
25076 this.toolbarItems = config.items;
25079 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
25084 this.bind(this.ds);
25087 if (Roo.bootstrap.version == 4) {
25088 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
25090 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
25095 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
25097 * @cfg {Roo.data.Store} dataSource
25098 * The underlying data store providing the paged data
25101 * @cfg {String/HTMLElement/Element} container
25102 * container The id or element that will contain the toolbar
25105 * @cfg {Boolean} displayInfo
25106 * True to display the displayMsg (defaults to false)
25109 * @cfg {Number} pageSize
25110 * The number of records to display per page (defaults to 20)
25114 * @cfg {String} displayMsg
25115 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25117 displayMsg : 'Displaying {0} - {1} of {2}',
25119 * @cfg {String} emptyMsg
25120 * The message to display when no records are found (defaults to "No data to display")
25122 emptyMsg : 'No data to display',
25124 * Customizable piece of the default paging text (defaults to "Page")
25127 beforePageText : "Page",
25129 * Customizable piece of the default paging text (defaults to "of %0")
25132 afterPageText : "of {0}",
25134 * Customizable piece of the default paging text (defaults to "First Page")
25137 firstText : "First Page",
25139 * Customizable piece of the default paging text (defaults to "Previous Page")
25142 prevText : "Previous Page",
25144 * Customizable piece of the default paging text (defaults to "Next Page")
25147 nextText : "Next Page",
25149 * Customizable piece of the default paging text (defaults to "Last Page")
25152 lastText : "Last Page",
25154 * Customizable piece of the default paging text (defaults to "Refresh")
25157 refreshText : "Refresh",
25161 onRender : function(ct, position)
25163 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25164 this.navgroup.parentId = this.id;
25165 this.navgroup.onRender(this.el, null);
25166 // add the buttons to the navgroup
25168 if(this.displayInfo){
25169 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25170 this.displayEl = this.el.select('.x-paging-info', true).first();
25171 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25172 // this.displayEl = navel.el.select('span',true).first();
25178 Roo.each(_this.buttons, function(e){ // this might need to use render????
25179 Roo.factory(e).render(_this.el);
25183 Roo.each(_this.toolbarItems, function(e) {
25184 _this.navgroup.addItem(e);
25188 this.first = this.navgroup.addItem({
25189 tooltip: this.firstText,
25190 cls: "prev btn-outline-secondary",
25191 html : ' <i class="fa fa-step-backward"></i>',
25193 preventDefault: true,
25194 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25197 this.prev = this.navgroup.addItem({
25198 tooltip: this.prevText,
25199 cls: "prev btn-outline-secondary",
25200 html : ' <i class="fa fa-backward"></i>',
25202 preventDefault: true,
25203 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
25205 //this.addSeparator();
25208 var field = this.navgroup.addItem( {
25210 cls : 'x-paging-position btn-outline-secondary',
25212 html : this.beforePageText +
25213 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25214 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
25217 this.field = field.el.select('input', true).first();
25218 this.field.on("keydown", this.onPagingKeydown, this);
25219 this.field.on("focus", function(){this.dom.select();});
25222 this.afterTextEl = field.el.select('.x-paging-after',true).first();
25223 //this.field.setHeight(18);
25224 //this.addSeparator();
25225 this.next = this.navgroup.addItem({
25226 tooltip: this.nextText,
25227 cls: "next btn-outline-secondary",
25228 html : ' <i class="fa fa-forward"></i>',
25230 preventDefault: true,
25231 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
25233 this.last = this.navgroup.addItem({
25234 tooltip: this.lastText,
25235 html : ' <i class="fa fa-step-forward"></i>',
25236 cls: "next btn-outline-secondary",
25238 preventDefault: true,
25239 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
25241 //this.addSeparator();
25242 this.loading = this.navgroup.addItem({
25243 tooltip: this.refreshText,
25244 cls: "btn-outline-secondary",
25245 html : ' <i class="fa fa-refresh"></i>',
25246 preventDefault: true,
25247 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25253 updateInfo : function(){
25254 if(this.displayEl){
25255 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25256 var msg = count == 0 ?
25260 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
25262 this.displayEl.update(msg);
25267 onLoad : function(ds, r, o)
25269 this.cursor = o.params.start ? o.params.start : 0;
25271 var d = this.getPageData(),
25276 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25277 this.field.dom.value = ap;
25278 this.first.setDisabled(ap == 1);
25279 this.prev.setDisabled(ap == 1);
25280 this.next.setDisabled(ap == ps);
25281 this.last.setDisabled(ap == ps);
25282 this.loading.enable();
25287 getPageData : function(){
25288 var total = this.ds.getTotalCount();
25291 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25292 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25297 onLoadError : function(){
25298 this.loading.enable();
25302 onPagingKeydown : function(e){
25303 var k = e.getKey();
25304 var d = this.getPageData();
25306 var v = this.field.dom.value, pageNum;
25307 if(!v || isNaN(pageNum = parseInt(v, 10))){
25308 this.field.dom.value = d.activePage;
25311 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25312 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25315 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))
25317 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25318 this.field.dom.value = pageNum;
25319 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25322 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25324 var v = this.field.dom.value, pageNum;
25325 var increment = (e.shiftKey) ? 10 : 1;
25326 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25329 if(!v || isNaN(pageNum = parseInt(v, 10))) {
25330 this.field.dom.value = d.activePage;
25333 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25335 this.field.dom.value = parseInt(v, 10) + increment;
25336 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25337 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25344 beforeLoad : function(){
25346 this.loading.disable();
25351 onClick : function(which){
25360 ds.load({params:{start: 0, limit: this.pageSize}});
25363 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25366 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25369 var total = ds.getTotalCount();
25370 var extra = total % this.pageSize;
25371 var lastStart = extra ? (total - extra) : total-this.pageSize;
25372 ds.load({params:{start: lastStart, limit: this.pageSize}});
25375 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25381 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25382 * @param {Roo.data.Store} store The data store to unbind
25384 unbind : function(ds){
25385 ds.un("beforeload", this.beforeLoad, this);
25386 ds.un("load", this.onLoad, this);
25387 ds.un("loadexception", this.onLoadError, this);
25388 ds.un("remove", this.updateInfo, this);
25389 ds.un("add", this.updateInfo, this);
25390 this.ds = undefined;
25394 * Binds the paging toolbar to the specified {@link Roo.data.Store}
25395 * @param {Roo.data.Store} store The data store to bind
25397 bind : function(ds){
25398 ds.on("beforeload", this.beforeLoad, this);
25399 ds.on("load", this.onLoad, this);
25400 ds.on("loadexception", this.onLoadError, this);
25401 ds.on("remove", this.updateInfo, this);
25402 ds.on("add", this.updateInfo, this);
25413 * @class Roo.bootstrap.MessageBar
25414 * @extends Roo.bootstrap.Component
25415 * Bootstrap MessageBar class
25416 * @cfg {String} html contents of the MessageBar
25417 * @cfg {String} weight (info | success | warning | danger) default info
25418 * @cfg {String} beforeClass insert the bar before the given class
25419 * @cfg {Boolean} closable (true | false) default false
25420 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25423 * Create a new Element
25424 * @param {Object} config The config object
25427 Roo.bootstrap.MessageBar = function(config){
25428 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25431 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25437 beforeClass: 'bootstrap-sticky-wrap',
25439 getAutoCreate : function(){
25443 cls: 'alert alert-dismissable alert-' + this.weight,
25448 html: this.html || ''
25454 cfg.cls += ' alert-messages-fixed';
25468 onRender : function(ct, position)
25470 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25473 var cfg = Roo.apply({}, this.getAutoCreate());
25477 cfg.cls += ' ' + this.cls;
25480 cfg.style = this.style;
25482 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25484 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25487 this.el.select('>button.close').on('click', this.hide, this);
25493 if (!this.rendered) {
25499 this.fireEvent('show', this);
25505 if (!this.rendered) {
25511 this.fireEvent('hide', this);
25514 update : function()
25516 // var e = this.el.dom.firstChild;
25518 // if(this.closable){
25519 // e = e.nextSibling;
25522 // e.data = this.html || '';
25524 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25540 * @class Roo.bootstrap.Graph
25541 * @extends Roo.bootstrap.Component
25542 * Bootstrap Graph class
25546 @cfg {String} graphtype bar | vbar | pie
25547 @cfg {number} g_x coodinator | centre x (pie)
25548 @cfg {number} g_y coodinator | centre y (pie)
25549 @cfg {number} g_r radius (pie)
25550 @cfg {number} g_height height of the chart (respected by all elements in the set)
25551 @cfg {number} g_width width of the chart (respected by all elements in the set)
25552 @cfg {Object} title The title of the chart
25555 -opts (object) options for the chart
25557 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25558 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25560 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.
25561 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25563 o stretch (boolean)
25565 -opts (object) options for the pie
25568 o startAngle (number)
25569 o endAngle (number)
25573 * Create a new Input
25574 * @param {Object} config The config object
25577 Roo.bootstrap.Graph = function(config){
25578 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25584 * The img click event for the img.
25585 * @param {Roo.EventObject} e
25591 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25602 //g_colors: this.colors,
25609 getAutoCreate : function(){
25620 onRender : function(ct,position){
25623 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25625 if (typeof(Raphael) == 'undefined') {
25626 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25630 this.raphael = Raphael(this.el.dom);
25632 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25633 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25634 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25635 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25637 r.text(160, 10, "Single Series Chart").attr(txtattr);
25638 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25639 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25640 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25642 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25643 r.barchart(330, 10, 300, 220, data1);
25644 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25645 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25648 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25649 // r.barchart(30, 30, 560, 250, xdata, {
25650 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25651 // axis : "0 0 1 1",
25652 // axisxlabels : xdata
25653 // //yvalues : cols,
25656 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25658 // this.load(null,xdata,{
25659 // axis : "0 0 1 1",
25660 // axisxlabels : xdata
25665 load : function(graphtype,xdata,opts)
25667 this.raphael.clear();
25669 graphtype = this.graphtype;
25674 var r = this.raphael,
25675 fin = function () {
25676 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25678 fout = function () {
25679 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25681 pfin = function() {
25682 this.sector.stop();
25683 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25686 this.label[0].stop();
25687 this.label[0].attr({ r: 7.5 });
25688 this.label[1].attr({ "font-weight": 800 });
25691 pfout = function() {
25692 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25695 this.label[0].animate({ r: 5 }, 500, "bounce");
25696 this.label[1].attr({ "font-weight": 400 });
25702 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25705 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25708 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25709 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25711 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25718 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25723 setTitle: function(o)
25728 initEvents: function() {
25731 this.el.on('click', this.onClick, this);
25735 onClick : function(e)
25737 Roo.log('img onclick');
25738 this.fireEvent('click', this, e);
25750 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25753 * @class Roo.bootstrap.dash.NumberBox
25754 * @extends Roo.bootstrap.Component
25755 * Bootstrap NumberBox class
25756 * @cfg {String} headline Box headline
25757 * @cfg {String} content Box content
25758 * @cfg {String} icon Box icon
25759 * @cfg {String} footer Footer text
25760 * @cfg {String} fhref Footer href
25763 * Create a new NumberBox
25764 * @param {Object} config The config object
25768 Roo.bootstrap.dash.NumberBox = function(config){
25769 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25773 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25782 getAutoCreate : function(){
25786 cls : 'small-box ',
25794 cls : 'roo-headline',
25795 html : this.headline
25799 cls : 'roo-content',
25800 html : this.content
25814 cls : 'ion ' + this.icon
25823 cls : 'small-box-footer',
25824 href : this.fhref || '#',
25828 cfg.cn.push(footer);
25835 onRender : function(ct,position){
25836 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25843 setHeadline: function (value)
25845 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25848 setFooter: function (value, href)
25850 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25853 this.el.select('a.small-box-footer',true).first().attr('href', href);
25858 setContent: function (value)
25860 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25863 initEvents: function()
25877 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25880 * @class Roo.bootstrap.dash.TabBox
25881 * @extends Roo.bootstrap.Component
25882 * Bootstrap TabBox class
25883 * @cfg {String} title Title of the TabBox
25884 * @cfg {String} icon Icon of the TabBox
25885 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25886 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25889 * Create a new TabBox
25890 * @param {Object} config The config object
25894 Roo.bootstrap.dash.TabBox = function(config){
25895 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25900 * When a pane is added
25901 * @param {Roo.bootstrap.dash.TabPane} pane
25905 * @event activatepane
25906 * When a pane is activated
25907 * @param {Roo.bootstrap.dash.TabPane} pane
25909 "activatepane" : true
25917 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25922 tabScrollable : false,
25924 getChildContainer : function()
25926 return this.el.select('.tab-content', true).first();
25929 getAutoCreate : function(){
25933 cls: 'pull-left header',
25941 cls: 'fa ' + this.icon
25947 cls: 'nav nav-tabs pull-right',
25953 if(this.tabScrollable){
25960 cls: 'nav nav-tabs pull-right',
25971 cls: 'nav-tabs-custom',
25976 cls: 'tab-content no-padding',
25984 initEvents : function()
25986 //Roo.log('add add pane handler');
25987 this.on('addpane', this.onAddPane, this);
25990 * Updates the box title
25991 * @param {String} html to set the title to.
25993 setTitle : function(value)
25995 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25997 onAddPane : function(pane)
25999 this.panes.push(pane);
26000 //Roo.log('addpane');
26002 // tabs are rendere left to right..
26003 if(!this.showtabs){
26007 var ctr = this.el.select('.nav-tabs', true).first();
26010 var existing = ctr.select('.nav-tab',true);
26011 var qty = existing.getCount();;
26014 var tab = ctr.createChild({
26016 cls : 'nav-tab' + (qty ? '' : ' active'),
26024 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
26027 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
26029 pane.el.addClass('active');
26034 onTabClick : function(ev,un,ob,pane)
26036 //Roo.log('tab - prev default');
26037 ev.preventDefault();
26040 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26041 pane.tab.addClass('active');
26042 //Roo.log(pane.title);
26043 this.getChildContainer().select('.tab-pane',true).removeClass('active');
26044 // technically we should have a deactivate event.. but maybe add later.
26045 // and it should not de-activate the selected tab...
26046 this.fireEvent('activatepane', pane);
26047 pane.el.addClass('active');
26048 pane.fireEvent('activate');
26053 getActivePane : function()
26056 Roo.each(this.panes, function(p) {
26057 if(p.el.hasClass('active')){
26078 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26080 * @class Roo.bootstrap.TabPane
26081 * @extends Roo.bootstrap.Component
26082 * Bootstrap TabPane class
26083 * @cfg {Boolean} active (false | true) Default false
26084 * @cfg {String} title title of panel
26088 * Create a new TabPane
26089 * @param {Object} config The config object
26092 Roo.bootstrap.dash.TabPane = function(config){
26093 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
26099 * When a pane is activated
26100 * @param {Roo.bootstrap.dash.TabPane} pane
26107 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
26112 // the tabBox that this is attached to.
26115 getAutoCreate : function()
26123 cfg.cls += ' active';
26128 initEvents : function()
26130 //Roo.log('trigger add pane handler');
26131 this.parent().fireEvent('addpane', this)
26135 * Updates the tab title
26136 * @param {String} html to set the title to.
26138 setTitle: function(str)
26144 this.tab.select('a', true).first().dom.innerHTML = str;
26161 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26164 * @class Roo.bootstrap.menu.Menu
26165 * @extends Roo.bootstrap.Component
26166 * Bootstrap Menu class - container for Menu
26167 * @cfg {String} html Text of the menu
26168 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26169 * @cfg {String} icon Font awesome icon
26170 * @cfg {String} pos Menu align to (top | bottom) default bottom
26174 * Create a new Menu
26175 * @param {Object} config The config object
26179 Roo.bootstrap.menu.Menu = function(config){
26180 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26184 * @event beforeshow
26185 * Fires before this menu is displayed
26186 * @param {Roo.bootstrap.menu.Menu} this
26190 * @event beforehide
26191 * Fires before this menu is hidden
26192 * @param {Roo.bootstrap.menu.Menu} this
26197 * Fires after this menu is displayed
26198 * @param {Roo.bootstrap.menu.Menu} this
26203 * Fires after this menu is hidden
26204 * @param {Roo.bootstrap.menu.Menu} this
26209 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26210 * @param {Roo.bootstrap.menu.Menu} this
26211 * @param {Roo.EventObject} e
26218 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
26222 weight : 'default',
26227 getChildContainer : function() {
26228 if(this.isSubMenu){
26232 return this.el.select('ul.dropdown-menu', true).first();
26235 getAutoCreate : function()
26240 cls : 'roo-menu-text',
26248 cls : 'fa ' + this.icon
26259 cls : 'dropdown-button btn btn-' + this.weight,
26264 cls : 'dropdown-toggle btn btn-' + this.weight,
26274 cls : 'dropdown-menu'
26280 if(this.pos == 'top'){
26281 cfg.cls += ' dropup';
26284 if(this.isSubMenu){
26287 cls : 'dropdown-menu'
26294 onRender : function(ct, position)
26296 this.isSubMenu = ct.hasClass('dropdown-submenu');
26298 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26301 initEvents : function()
26303 if(this.isSubMenu){
26307 this.hidden = true;
26309 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26310 this.triggerEl.on('click', this.onTriggerPress, this);
26312 this.buttonEl = this.el.select('button.dropdown-button', true).first();
26313 this.buttonEl.on('click', this.onClick, this);
26319 if(this.isSubMenu){
26323 return this.el.select('ul.dropdown-menu', true).first();
26326 onClick : function(e)
26328 this.fireEvent("click", this, e);
26331 onTriggerPress : function(e)
26333 if (this.isVisible()) {
26340 isVisible : function(){
26341 return !this.hidden;
26346 this.fireEvent("beforeshow", this);
26348 this.hidden = false;
26349 this.el.addClass('open');
26351 Roo.get(document).on("mouseup", this.onMouseUp, this);
26353 this.fireEvent("show", this);
26360 this.fireEvent("beforehide", this);
26362 this.hidden = true;
26363 this.el.removeClass('open');
26365 Roo.get(document).un("mouseup", this.onMouseUp);
26367 this.fireEvent("hide", this);
26370 onMouseUp : function()
26384 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26387 * @class Roo.bootstrap.menu.Item
26388 * @extends Roo.bootstrap.Component
26389 * Bootstrap MenuItem class
26390 * @cfg {Boolean} submenu (true | false) default false
26391 * @cfg {String} html text of the item
26392 * @cfg {String} href the link
26393 * @cfg {Boolean} disable (true | false) default false
26394 * @cfg {Boolean} preventDefault (true | false) default true
26395 * @cfg {String} icon Font awesome icon
26396 * @cfg {String} pos Submenu align to (left | right) default right
26400 * Create a new Item
26401 * @param {Object} config The config object
26405 Roo.bootstrap.menu.Item = function(config){
26406 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26410 * Fires when the mouse is hovering over this menu
26411 * @param {Roo.bootstrap.menu.Item} this
26412 * @param {Roo.EventObject} e
26417 * Fires when the mouse exits this menu
26418 * @param {Roo.bootstrap.menu.Item} this
26419 * @param {Roo.EventObject} e
26425 * The raw click event for the entire grid.
26426 * @param {Roo.EventObject} e
26432 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26437 preventDefault: true,
26442 getAutoCreate : function()
26447 cls : 'roo-menu-item-text',
26455 cls : 'fa ' + this.icon
26464 href : this.href || '#',
26471 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26475 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26477 if(this.pos == 'left'){
26478 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26485 initEvents : function()
26487 this.el.on('mouseover', this.onMouseOver, this);
26488 this.el.on('mouseout', this.onMouseOut, this);
26490 this.el.select('a', true).first().on('click', this.onClick, this);
26494 onClick : function(e)
26496 if(this.preventDefault){
26497 e.preventDefault();
26500 this.fireEvent("click", this, e);
26503 onMouseOver : function(e)
26505 if(this.submenu && this.pos == 'left'){
26506 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26509 this.fireEvent("mouseover", this, e);
26512 onMouseOut : function(e)
26514 this.fireEvent("mouseout", this, e);
26526 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26529 * @class Roo.bootstrap.menu.Separator
26530 * @extends Roo.bootstrap.Component
26531 * Bootstrap Separator class
26534 * Create a new Separator
26535 * @param {Object} config The config object
26539 Roo.bootstrap.menu.Separator = function(config){
26540 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26543 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26545 getAutoCreate : function(){
26566 * @class Roo.bootstrap.Tooltip
26567 * Bootstrap Tooltip class
26568 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26569 * to determine which dom element triggers the tooltip.
26571 * It needs to add support for additional attributes like tooltip-position
26574 * Create a new Toolti
26575 * @param {Object} config The config object
26578 Roo.bootstrap.Tooltip = function(config){
26579 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26581 this.alignment = Roo.bootstrap.Tooltip.alignment;
26583 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26584 this.alignment = config.alignment;
26589 Roo.apply(Roo.bootstrap.Tooltip, {
26591 * @function init initialize tooltip monitoring.
26595 currentTip : false,
26596 currentRegion : false,
26602 Roo.get(document).on('mouseover', this.enter ,this);
26603 Roo.get(document).on('mouseout', this.leave, this);
26606 this.currentTip = new Roo.bootstrap.Tooltip();
26609 enter : function(ev)
26611 var dom = ev.getTarget();
26613 //Roo.log(['enter',dom]);
26614 var el = Roo.fly(dom);
26615 if (this.currentEl) {
26617 //Roo.log(this.currentEl);
26618 //Roo.log(this.currentEl.contains(dom));
26619 if (this.currentEl == el) {
26622 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26628 if (this.currentTip.el) {
26629 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26633 if(!el || el.dom == document){
26639 // you can not look for children, as if el is the body.. then everythign is the child..
26640 if (!el.attr('tooltip')) { //
26641 if (!el.select("[tooltip]").elements.length) {
26644 // is the mouse over this child...?
26645 bindEl = el.select("[tooltip]").first();
26646 var xy = ev.getXY();
26647 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26648 //Roo.log("not in region.");
26651 //Roo.log("child element over..");
26654 this.currentEl = bindEl;
26655 this.currentTip.bind(bindEl);
26656 this.currentRegion = Roo.lib.Region.getRegion(dom);
26657 this.currentTip.enter();
26660 leave : function(ev)
26662 var dom = ev.getTarget();
26663 //Roo.log(['leave',dom]);
26664 if (!this.currentEl) {
26669 if (dom != this.currentEl.dom) {
26672 var xy = ev.getXY();
26673 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26676 // only activate leave if mouse cursor is outside... bounding box..
26681 if (this.currentTip) {
26682 this.currentTip.leave();
26684 //Roo.log('clear currentEl');
26685 this.currentEl = false;
26690 'left' : ['r-l', [-2,0], 'right'],
26691 'right' : ['l-r', [2,0], 'left'],
26692 'bottom' : ['t-b', [0,2], 'top'],
26693 'top' : [ 'b-t', [0,-2], 'bottom']
26699 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26704 delay : null, // can be { show : 300 , hide: 500}
26708 hoverState : null, //???
26710 placement : 'bottom',
26714 getAutoCreate : function(){
26721 cls : 'tooltip-arrow'
26724 cls : 'tooltip-inner'
26731 bind : function(el)
26737 enter : function () {
26739 if (this.timeout != null) {
26740 clearTimeout(this.timeout);
26743 this.hoverState = 'in';
26744 //Roo.log("enter - show");
26745 if (!this.delay || !this.delay.show) {
26750 this.timeout = setTimeout(function () {
26751 if (_t.hoverState == 'in') {
26754 }, this.delay.show);
26758 clearTimeout(this.timeout);
26760 this.hoverState = 'out';
26761 if (!this.delay || !this.delay.hide) {
26767 this.timeout = setTimeout(function () {
26768 //Roo.log("leave - timeout");
26770 if (_t.hoverState == 'out') {
26772 Roo.bootstrap.Tooltip.currentEl = false;
26777 show : function (msg)
26780 this.render(document.body);
26783 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26785 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26787 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26789 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26791 var placement = typeof this.placement == 'function' ?
26792 this.placement.call(this, this.el, on_el) :
26795 var autoToken = /\s?auto?\s?/i;
26796 var autoPlace = autoToken.test(placement);
26798 placement = placement.replace(autoToken, '') || 'top';
26802 //this.el.setXY([0,0]);
26804 //this.el.dom.style.display='block';
26806 //this.el.appendTo(on_el);
26808 var p = this.getPosition();
26809 var box = this.el.getBox();
26815 var align = this.alignment[placement];
26817 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26819 if(placement == 'top' || placement == 'bottom'){
26821 placement = 'right';
26824 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26825 placement = 'left';
26828 var scroll = Roo.select('body', true).first().getScroll();
26830 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26834 align = this.alignment[placement];
26837 this.el.alignTo(this.bindEl, align[0],align[1]);
26838 //var arrow = this.el.select('.arrow',true).first();
26839 //arrow.set(align[2],
26841 this.el.addClass(placement);
26843 this.el.addClass('in fade');
26845 this.hoverState = null;
26847 if (this.el.hasClass('fade')) {
26858 //this.el.setXY([0,0]);
26859 this.el.removeClass('in');
26875 * @class Roo.bootstrap.LocationPicker
26876 * @extends Roo.bootstrap.Component
26877 * Bootstrap LocationPicker class
26878 * @cfg {Number} latitude Position when init default 0
26879 * @cfg {Number} longitude Position when init default 0
26880 * @cfg {Number} zoom default 15
26881 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26882 * @cfg {Boolean} mapTypeControl default false
26883 * @cfg {Boolean} disableDoubleClickZoom default false
26884 * @cfg {Boolean} scrollwheel default true
26885 * @cfg {Boolean} streetViewControl default false
26886 * @cfg {Number} radius default 0
26887 * @cfg {String} locationName
26888 * @cfg {Boolean} draggable default true
26889 * @cfg {Boolean} enableAutocomplete default false
26890 * @cfg {Boolean} enableReverseGeocode default true
26891 * @cfg {String} markerTitle
26894 * Create a new LocationPicker
26895 * @param {Object} config The config object
26899 Roo.bootstrap.LocationPicker = function(config){
26901 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26906 * Fires when the picker initialized.
26907 * @param {Roo.bootstrap.LocationPicker} this
26908 * @param {Google Location} location
26912 * @event positionchanged
26913 * Fires when the picker position changed.
26914 * @param {Roo.bootstrap.LocationPicker} this
26915 * @param {Google Location} location
26917 positionchanged : true,
26920 * Fires when the map resize.
26921 * @param {Roo.bootstrap.LocationPicker} this
26926 * Fires when the map show.
26927 * @param {Roo.bootstrap.LocationPicker} this
26932 * Fires when the map hide.
26933 * @param {Roo.bootstrap.LocationPicker} this
26938 * Fires when click the map.
26939 * @param {Roo.bootstrap.LocationPicker} this
26940 * @param {Map event} e
26944 * @event mapRightClick
26945 * Fires when right click the map.
26946 * @param {Roo.bootstrap.LocationPicker} this
26947 * @param {Map event} e
26949 mapRightClick : true,
26951 * @event markerClick
26952 * Fires when click the marker.
26953 * @param {Roo.bootstrap.LocationPicker} this
26954 * @param {Map event} e
26956 markerClick : true,
26958 * @event markerRightClick
26959 * Fires when right click the marker.
26960 * @param {Roo.bootstrap.LocationPicker} this
26961 * @param {Map event} e
26963 markerRightClick : true,
26965 * @event OverlayViewDraw
26966 * Fires when OverlayView Draw
26967 * @param {Roo.bootstrap.LocationPicker} this
26969 OverlayViewDraw : true,
26971 * @event OverlayViewOnAdd
26972 * Fires when OverlayView Draw
26973 * @param {Roo.bootstrap.LocationPicker} this
26975 OverlayViewOnAdd : true,
26977 * @event OverlayViewOnRemove
26978 * Fires when OverlayView Draw
26979 * @param {Roo.bootstrap.LocationPicker} this
26981 OverlayViewOnRemove : true,
26983 * @event OverlayViewShow
26984 * Fires when OverlayView Draw
26985 * @param {Roo.bootstrap.LocationPicker} this
26986 * @param {Pixel} cpx
26988 OverlayViewShow : true,
26990 * @event OverlayViewHide
26991 * Fires when OverlayView Draw
26992 * @param {Roo.bootstrap.LocationPicker} this
26994 OverlayViewHide : true,
26996 * @event loadexception
26997 * Fires when load google lib failed.
26998 * @param {Roo.bootstrap.LocationPicker} this
27000 loadexception : true
27005 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
27007 gMapContext: false,
27013 mapTypeControl: false,
27014 disableDoubleClickZoom: false,
27016 streetViewControl: false,
27020 enableAutocomplete: false,
27021 enableReverseGeocode: true,
27024 getAutoCreate: function()
27029 cls: 'roo-location-picker'
27035 initEvents: function(ct, position)
27037 if(!this.el.getWidth() || this.isApplied()){
27041 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27046 initial: function()
27048 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27049 this.fireEvent('loadexception', this);
27053 if(!this.mapTypeId){
27054 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
27057 this.gMapContext = this.GMapContext();
27059 this.initOverlayView();
27061 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
27065 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
27066 _this.setPosition(_this.gMapContext.marker.position);
27069 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
27070 _this.fireEvent('mapClick', this, event);
27074 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
27075 _this.fireEvent('mapRightClick', this, event);
27079 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
27080 _this.fireEvent('markerClick', this, event);
27084 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
27085 _this.fireEvent('markerRightClick', this, event);
27089 this.setPosition(this.gMapContext.location);
27091 this.fireEvent('initial', this, this.gMapContext.location);
27094 initOverlayView: function()
27098 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
27102 _this.fireEvent('OverlayViewDraw', _this);
27107 _this.fireEvent('OverlayViewOnAdd', _this);
27110 onRemove: function()
27112 _this.fireEvent('OverlayViewOnRemove', _this);
27115 show: function(cpx)
27117 _this.fireEvent('OverlayViewShow', _this, cpx);
27122 _this.fireEvent('OverlayViewHide', _this);
27128 fromLatLngToContainerPixel: function(event)
27130 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27133 isApplied: function()
27135 return this.getGmapContext() == false ? false : true;
27138 getGmapContext: function()
27140 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27143 GMapContext: function()
27145 var position = new google.maps.LatLng(this.latitude, this.longitude);
27147 var _map = new google.maps.Map(this.el.dom, {
27150 mapTypeId: this.mapTypeId,
27151 mapTypeControl: this.mapTypeControl,
27152 disableDoubleClickZoom: this.disableDoubleClickZoom,
27153 scrollwheel: this.scrollwheel,
27154 streetViewControl: this.streetViewControl,
27155 locationName: this.locationName,
27156 draggable: this.draggable,
27157 enableAutocomplete: this.enableAutocomplete,
27158 enableReverseGeocode: this.enableReverseGeocode
27161 var _marker = new google.maps.Marker({
27162 position: position,
27164 title: this.markerTitle,
27165 draggable: this.draggable
27172 location: position,
27173 radius: this.radius,
27174 locationName: this.locationName,
27175 addressComponents: {
27176 formatted_address: null,
27177 addressLine1: null,
27178 addressLine2: null,
27180 streetNumber: null,
27184 stateOrProvince: null
27187 domContainer: this.el.dom,
27188 geodecoder: new google.maps.Geocoder()
27192 drawCircle: function(center, radius, options)
27194 if (this.gMapContext.circle != null) {
27195 this.gMapContext.circle.setMap(null);
27199 options = Roo.apply({}, options, {
27200 strokeColor: "#0000FF",
27201 strokeOpacity: .35,
27203 fillColor: "#0000FF",
27207 options.map = this.gMapContext.map;
27208 options.radius = radius;
27209 options.center = center;
27210 this.gMapContext.circle = new google.maps.Circle(options);
27211 return this.gMapContext.circle;
27217 setPosition: function(location)
27219 this.gMapContext.location = location;
27220 this.gMapContext.marker.setPosition(location);
27221 this.gMapContext.map.panTo(location);
27222 this.drawCircle(location, this.gMapContext.radius, {});
27226 if (this.gMapContext.settings.enableReverseGeocode) {
27227 this.gMapContext.geodecoder.geocode({
27228 latLng: this.gMapContext.location
27229 }, function(results, status) {
27231 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27232 _this.gMapContext.locationName = results[0].formatted_address;
27233 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27235 _this.fireEvent('positionchanged', this, location);
27242 this.fireEvent('positionchanged', this, location);
27247 google.maps.event.trigger(this.gMapContext.map, "resize");
27249 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27251 this.fireEvent('resize', this);
27254 setPositionByLatLng: function(latitude, longitude)
27256 this.setPosition(new google.maps.LatLng(latitude, longitude));
27259 getCurrentPosition: function()
27262 latitude: this.gMapContext.location.lat(),
27263 longitude: this.gMapContext.location.lng()
27267 getAddressName: function()
27269 return this.gMapContext.locationName;
27272 getAddressComponents: function()
27274 return this.gMapContext.addressComponents;
27277 address_component_from_google_geocode: function(address_components)
27281 for (var i = 0; i < address_components.length; i++) {
27282 var component = address_components[i];
27283 if (component.types.indexOf("postal_code") >= 0) {
27284 result.postalCode = component.short_name;
27285 } else if (component.types.indexOf("street_number") >= 0) {
27286 result.streetNumber = component.short_name;
27287 } else if (component.types.indexOf("route") >= 0) {
27288 result.streetName = component.short_name;
27289 } else if (component.types.indexOf("neighborhood") >= 0) {
27290 result.city = component.short_name;
27291 } else if (component.types.indexOf("locality") >= 0) {
27292 result.city = component.short_name;
27293 } else if (component.types.indexOf("sublocality") >= 0) {
27294 result.district = component.short_name;
27295 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27296 result.stateOrProvince = component.short_name;
27297 } else if (component.types.indexOf("country") >= 0) {
27298 result.country = component.short_name;
27302 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27303 result.addressLine2 = "";
27307 setZoomLevel: function(zoom)
27309 this.gMapContext.map.setZoom(zoom);
27322 this.fireEvent('show', this);
27333 this.fireEvent('hide', this);
27338 Roo.apply(Roo.bootstrap.LocationPicker, {
27340 OverlayView : function(map, options)
27342 options = options || {};
27349 * @class Roo.bootstrap.Alert
27350 * @extends Roo.bootstrap.Component
27351 * Bootstrap Alert class - shows an alert area box
27353 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27354 Enter a valid email address
27357 * @cfg {String} title The title of alert
27358 * @cfg {String} html The content of alert
27359 * @cfg {String} weight ( success | info | warning | danger )
27360 * @cfg {String} faicon font-awesomeicon
27363 * Create a new alert
27364 * @param {Object} config The config object
27368 Roo.bootstrap.Alert = function(config){
27369 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27373 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
27380 getAutoCreate : function()
27389 cls : 'roo-alert-icon'
27394 cls : 'roo-alert-title',
27399 cls : 'roo-alert-text',
27406 cfg.cn[0].cls += ' fa ' + this.faicon;
27410 cfg.cls += ' alert-' + this.weight;
27416 initEvents: function()
27418 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27421 setTitle : function(str)
27423 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27426 setText : function(str)
27428 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27431 setWeight : function(weight)
27434 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27437 this.weight = weight;
27439 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27442 setIcon : function(icon)
27445 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27448 this.faicon = icon;
27450 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27471 * @class Roo.bootstrap.UploadCropbox
27472 * @extends Roo.bootstrap.Component
27473 * Bootstrap UploadCropbox class
27474 * @cfg {String} emptyText show when image has been loaded
27475 * @cfg {String} rotateNotify show when image too small to rotate
27476 * @cfg {Number} errorTimeout default 3000
27477 * @cfg {Number} minWidth default 300
27478 * @cfg {Number} minHeight default 300
27479 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27480 * @cfg {Boolean} isDocument (true|false) default false
27481 * @cfg {String} url action url
27482 * @cfg {String} paramName default 'imageUpload'
27483 * @cfg {String} method default POST
27484 * @cfg {Boolean} loadMask (true|false) default true
27485 * @cfg {Boolean} loadingText default 'Loading...'
27488 * Create a new UploadCropbox
27489 * @param {Object} config The config object
27492 Roo.bootstrap.UploadCropbox = function(config){
27493 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27497 * @event beforeselectfile
27498 * Fire before select file
27499 * @param {Roo.bootstrap.UploadCropbox} this
27501 "beforeselectfile" : true,
27504 * Fire after initEvent
27505 * @param {Roo.bootstrap.UploadCropbox} this
27510 * Fire after initEvent
27511 * @param {Roo.bootstrap.UploadCropbox} this
27512 * @param {String} data
27517 * Fire when preparing the file data
27518 * @param {Roo.bootstrap.UploadCropbox} this
27519 * @param {Object} file
27524 * Fire when get exception
27525 * @param {Roo.bootstrap.UploadCropbox} this
27526 * @param {XMLHttpRequest} xhr
27528 "exception" : true,
27530 * @event beforeloadcanvas
27531 * Fire before load the canvas
27532 * @param {Roo.bootstrap.UploadCropbox} this
27533 * @param {String} src
27535 "beforeloadcanvas" : true,
27538 * Fire when trash image
27539 * @param {Roo.bootstrap.UploadCropbox} this
27544 * Fire when download the image
27545 * @param {Roo.bootstrap.UploadCropbox} this
27549 * @event footerbuttonclick
27550 * Fire when footerbuttonclick
27551 * @param {Roo.bootstrap.UploadCropbox} this
27552 * @param {String} type
27554 "footerbuttonclick" : true,
27558 * @param {Roo.bootstrap.UploadCropbox} this
27563 * Fire when rotate the image
27564 * @param {Roo.bootstrap.UploadCropbox} this
27565 * @param {String} pos
27570 * Fire when inspect the file
27571 * @param {Roo.bootstrap.UploadCropbox} this
27572 * @param {Object} file
27577 * Fire when xhr upload the file
27578 * @param {Roo.bootstrap.UploadCropbox} this
27579 * @param {Object} data
27584 * Fire when arrange the file data
27585 * @param {Roo.bootstrap.UploadCropbox} this
27586 * @param {Object} formData
27591 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27594 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27596 emptyText : 'Click to upload image',
27597 rotateNotify : 'Image is too small to rotate',
27598 errorTimeout : 3000,
27612 cropType : 'image/jpeg',
27614 canvasLoaded : false,
27615 isDocument : false,
27617 paramName : 'imageUpload',
27619 loadingText : 'Loading...',
27622 getAutoCreate : function()
27626 cls : 'roo-upload-cropbox',
27630 cls : 'roo-upload-cropbox-selector',
27635 cls : 'roo-upload-cropbox-body',
27636 style : 'cursor:pointer',
27640 cls : 'roo-upload-cropbox-preview'
27644 cls : 'roo-upload-cropbox-thumb'
27648 cls : 'roo-upload-cropbox-empty-notify',
27649 html : this.emptyText
27653 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27654 html : this.rotateNotify
27660 cls : 'roo-upload-cropbox-footer',
27663 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27673 onRender : function(ct, position)
27675 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27677 if (this.buttons.length) {
27679 Roo.each(this.buttons, function(bb) {
27681 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27683 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27689 this.maskEl = this.el;
27693 initEvents : function()
27695 this.urlAPI = (window.createObjectURL && window) ||
27696 (window.URL && URL.revokeObjectURL && URL) ||
27697 (window.webkitURL && webkitURL);
27699 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27700 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27702 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27703 this.selectorEl.hide();
27705 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27706 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27708 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27709 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27710 this.thumbEl.hide();
27712 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27713 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27715 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27716 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27717 this.errorEl.hide();
27719 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27720 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27721 this.footerEl.hide();
27723 this.setThumbBoxSize();
27729 this.fireEvent('initial', this);
27736 window.addEventListener("resize", function() { _this.resize(); } );
27738 this.bodyEl.on('click', this.beforeSelectFile, this);
27741 this.bodyEl.on('touchstart', this.onTouchStart, this);
27742 this.bodyEl.on('touchmove', this.onTouchMove, this);
27743 this.bodyEl.on('touchend', this.onTouchEnd, this);
27747 this.bodyEl.on('mousedown', this.onMouseDown, this);
27748 this.bodyEl.on('mousemove', this.onMouseMove, this);
27749 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27750 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27751 Roo.get(document).on('mouseup', this.onMouseUp, this);
27754 this.selectorEl.on('change', this.onFileSelected, this);
27760 this.baseScale = 1;
27762 this.baseRotate = 1;
27763 this.dragable = false;
27764 this.pinching = false;
27767 this.cropData = false;
27768 this.notifyEl.dom.innerHTML = this.emptyText;
27770 this.selectorEl.dom.value = '';
27774 resize : function()
27776 if(this.fireEvent('resize', this) != false){
27777 this.setThumbBoxPosition();
27778 this.setCanvasPosition();
27782 onFooterButtonClick : function(e, el, o, type)
27785 case 'rotate-left' :
27786 this.onRotateLeft(e);
27788 case 'rotate-right' :
27789 this.onRotateRight(e);
27792 this.beforeSelectFile(e);
27807 this.fireEvent('footerbuttonclick', this, type);
27810 beforeSelectFile : function(e)
27812 e.preventDefault();
27814 if(this.fireEvent('beforeselectfile', this) != false){
27815 this.selectorEl.dom.click();
27819 onFileSelected : function(e)
27821 e.preventDefault();
27823 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27827 var file = this.selectorEl.dom.files[0];
27829 if(this.fireEvent('inspect', this, file) != false){
27830 this.prepare(file);
27835 trash : function(e)
27837 this.fireEvent('trash', this);
27840 download : function(e)
27842 this.fireEvent('download', this);
27845 loadCanvas : function(src)
27847 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27851 this.imageEl = document.createElement('img');
27855 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27857 this.imageEl.src = src;
27861 onLoadCanvas : function()
27863 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27864 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27866 this.bodyEl.un('click', this.beforeSelectFile, this);
27868 this.notifyEl.hide();
27869 this.thumbEl.show();
27870 this.footerEl.show();
27872 this.baseRotateLevel();
27874 if(this.isDocument){
27875 this.setThumbBoxSize();
27878 this.setThumbBoxPosition();
27880 this.baseScaleLevel();
27886 this.canvasLoaded = true;
27889 this.maskEl.unmask();
27894 setCanvasPosition : function()
27896 if(!this.canvasEl){
27900 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27901 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27903 this.previewEl.setLeft(pw);
27904 this.previewEl.setTop(ph);
27908 onMouseDown : function(e)
27912 this.dragable = true;
27913 this.pinching = false;
27915 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27916 this.dragable = false;
27920 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27921 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27925 onMouseMove : function(e)
27929 if(!this.canvasLoaded){
27933 if (!this.dragable){
27937 var minX = Math.ceil(this.thumbEl.getLeft(true));
27938 var minY = Math.ceil(this.thumbEl.getTop(true));
27940 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27941 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27943 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27944 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27946 x = x - this.mouseX;
27947 y = y - this.mouseY;
27949 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27950 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27952 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27953 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27955 this.previewEl.setLeft(bgX);
27956 this.previewEl.setTop(bgY);
27958 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27959 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27962 onMouseUp : function(e)
27966 this.dragable = false;
27969 onMouseWheel : function(e)
27973 this.startScale = this.scale;
27975 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27977 if(!this.zoomable()){
27978 this.scale = this.startScale;
27987 zoomable : function()
27989 var minScale = this.thumbEl.getWidth() / this.minWidth;
27991 if(this.minWidth < this.minHeight){
27992 minScale = this.thumbEl.getHeight() / this.minHeight;
27995 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27996 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
28000 (this.rotate == 0 || this.rotate == 180) &&
28002 width > this.imageEl.OriginWidth ||
28003 height > this.imageEl.OriginHeight ||
28004 (width < this.minWidth && height < this.minHeight)
28012 (this.rotate == 90 || this.rotate == 270) &&
28014 width > this.imageEl.OriginWidth ||
28015 height > this.imageEl.OriginHeight ||
28016 (width < this.minHeight && height < this.minWidth)
28023 !this.isDocument &&
28024 (this.rotate == 0 || this.rotate == 180) &&
28026 width < this.minWidth ||
28027 width > this.imageEl.OriginWidth ||
28028 height < this.minHeight ||
28029 height > this.imageEl.OriginHeight
28036 !this.isDocument &&
28037 (this.rotate == 90 || this.rotate == 270) &&
28039 width < this.minHeight ||
28040 width > this.imageEl.OriginWidth ||
28041 height < this.minWidth ||
28042 height > this.imageEl.OriginHeight
28052 onRotateLeft : function(e)
28054 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28056 var minScale = this.thumbEl.getWidth() / this.minWidth;
28058 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28059 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28061 this.startScale = this.scale;
28063 while (this.getScaleLevel() < minScale){
28065 this.scale = this.scale + 1;
28067 if(!this.zoomable()){
28072 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28073 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28078 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28085 this.scale = this.startScale;
28087 this.onRotateFail();
28092 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28094 if(this.isDocument){
28095 this.setThumbBoxSize();
28096 this.setThumbBoxPosition();
28097 this.setCanvasPosition();
28102 this.fireEvent('rotate', this, 'left');
28106 onRotateRight : function(e)
28108 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28110 var minScale = this.thumbEl.getWidth() / this.minWidth;
28112 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28113 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28115 this.startScale = this.scale;
28117 while (this.getScaleLevel() < minScale){
28119 this.scale = this.scale + 1;
28121 if(!this.zoomable()){
28126 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28127 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28132 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28139 this.scale = this.startScale;
28141 this.onRotateFail();
28146 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28148 if(this.isDocument){
28149 this.setThumbBoxSize();
28150 this.setThumbBoxPosition();
28151 this.setCanvasPosition();
28156 this.fireEvent('rotate', this, 'right');
28159 onRotateFail : function()
28161 this.errorEl.show(true);
28165 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28170 this.previewEl.dom.innerHTML = '';
28172 var canvasEl = document.createElement("canvas");
28174 var contextEl = canvasEl.getContext("2d");
28176 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28177 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28178 var center = this.imageEl.OriginWidth / 2;
28180 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28181 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28182 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28183 center = this.imageEl.OriginHeight / 2;
28186 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28188 contextEl.translate(center, center);
28189 contextEl.rotate(this.rotate * Math.PI / 180);
28191 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28193 this.canvasEl = document.createElement("canvas");
28195 this.contextEl = this.canvasEl.getContext("2d");
28197 switch (this.rotate) {
28200 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28201 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28203 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28208 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28209 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28211 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28212 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);
28216 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28221 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28222 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28224 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28225 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);
28229 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);
28234 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28235 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28237 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28238 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28242 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);
28249 this.previewEl.appendChild(this.canvasEl);
28251 this.setCanvasPosition();
28256 if(!this.canvasLoaded){
28260 var imageCanvas = document.createElement("canvas");
28262 var imageContext = imageCanvas.getContext("2d");
28264 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28265 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28267 var center = imageCanvas.width / 2;
28269 imageContext.translate(center, center);
28271 imageContext.rotate(this.rotate * Math.PI / 180);
28273 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28275 var canvas = document.createElement("canvas");
28277 var context = canvas.getContext("2d");
28279 canvas.width = this.minWidth;
28280 canvas.height = this.minHeight;
28282 switch (this.rotate) {
28285 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28286 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28288 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28289 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28291 var targetWidth = this.minWidth - 2 * x;
28292 var targetHeight = this.minHeight - 2 * y;
28296 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28297 scale = targetWidth / width;
28300 if(x > 0 && y == 0){
28301 scale = targetHeight / height;
28304 if(x > 0 && y > 0){
28305 scale = targetWidth / width;
28307 if(width < height){
28308 scale = targetHeight / height;
28312 context.scale(scale, scale);
28314 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28315 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28317 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28318 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28320 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28325 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28326 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28328 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28329 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28331 var targetWidth = this.minWidth - 2 * x;
28332 var targetHeight = this.minHeight - 2 * y;
28336 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28337 scale = targetWidth / width;
28340 if(x > 0 && y == 0){
28341 scale = targetHeight / height;
28344 if(x > 0 && y > 0){
28345 scale = targetWidth / width;
28347 if(width < height){
28348 scale = targetHeight / height;
28352 context.scale(scale, scale);
28354 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28355 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28357 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28358 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28360 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28362 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28367 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28368 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28370 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28371 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28373 var targetWidth = this.minWidth - 2 * x;
28374 var targetHeight = this.minHeight - 2 * y;
28378 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28379 scale = targetWidth / width;
28382 if(x > 0 && y == 0){
28383 scale = targetHeight / height;
28386 if(x > 0 && y > 0){
28387 scale = targetWidth / width;
28389 if(width < height){
28390 scale = targetHeight / height;
28394 context.scale(scale, scale);
28396 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28397 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28399 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28400 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28402 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28403 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28405 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28410 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28411 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28413 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28414 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28416 var targetWidth = this.minWidth - 2 * x;
28417 var targetHeight = this.minHeight - 2 * y;
28421 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28422 scale = targetWidth / width;
28425 if(x > 0 && y == 0){
28426 scale = targetHeight / height;
28429 if(x > 0 && y > 0){
28430 scale = targetWidth / width;
28432 if(width < height){
28433 scale = targetHeight / height;
28437 context.scale(scale, scale);
28439 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28440 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28442 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28443 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28445 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28447 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28454 this.cropData = canvas.toDataURL(this.cropType);
28456 if(this.fireEvent('crop', this, this.cropData) !== false){
28457 this.process(this.file, this.cropData);
28464 setThumbBoxSize : function()
28468 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28469 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28470 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28472 this.minWidth = width;
28473 this.minHeight = height;
28475 if(this.rotate == 90 || this.rotate == 270){
28476 this.minWidth = height;
28477 this.minHeight = width;
28482 width = Math.ceil(this.minWidth * height / this.minHeight);
28484 if(this.minWidth > this.minHeight){
28486 height = Math.ceil(this.minHeight * width / this.minWidth);
28489 this.thumbEl.setStyle({
28490 width : width + 'px',
28491 height : height + 'px'
28498 setThumbBoxPosition : function()
28500 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28501 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28503 this.thumbEl.setLeft(x);
28504 this.thumbEl.setTop(y);
28508 baseRotateLevel : function()
28510 this.baseRotate = 1;
28513 typeof(this.exif) != 'undefined' &&
28514 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28515 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28517 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28520 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28524 baseScaleLevel : function()
28528 if(this.isDocument){
28530 if(this.baseRotate == 6 || this.baseRotate == 8){
28532 height = this.thumbEl.getHeight();
28533 this.baseScale = height / this.imageEl.OriginWidth;
28535 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28536 width = this.thumbEl.getWidth();
28537 this.baseScale = width / this.imageEl.OriginHeight;
28543 height = this.thumbEl.getHeight();
28544 this.baseScale = height / this.imageEl.OriginHeight;
28546 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28547 width = this.thumbEl.getWidth();
28548 this.baseScale = width / this.imageEl.OriginWidth;
28554 if(this.baseRotate == 6 || this.baseRotate == 8){
28556 width = this.thumbEl.getHeight();
28557 this.baseScale = width / this.imageEl.OriginHeight;
28559 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28560 height = this.thumbEl.getWidth();
28561 this.baseScale = height / this.imageEl.OriginHeight;
28564 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28565 height = this.thumbEl.getWidth();
28566 this.baseScale = height / this.imageEl.OriginHeight;
28568 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28569 width = this.thumbEl.getHeight();
28570 this.baseScale = width / this.imageEl.OriginWidth;
28577 width = this.thumbEl.getWidth();
28578 this.baseScale = width / this.imageEl.OriginWidth;
28580 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28581 height = this.thumbEl.getHeight();
28582 this.baseScale = height / this.imageEl.OriginHeight;
28585 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28587 height = this.thumbEl.getHeight();
28588 this.baseScale = height / this.imageEl.OriginHeight;
28590 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28591 width = this.thumbEl.getWidth();
28592 this.baseScale = width / this.imageEl.OriginWidth;
28600 getScaleLevel : function()
28602 return this.baseScale * Math.pow(1.1, this.scale);
28605 onTouchStart : function(e)
28607 if(!this.canvasLoaded){
28608 this.beforeSelectFile(e);
28612 var touches = e.browserEvent.touches;
28618 if(touches.length == 1){
28619 this.onMouseDown(e);
28623 if(touches.length != 2){
28629 for(var i = 0, finger; finger = touches[i]; i++){
28630 coords.push(finger.pageX, finger.pageY);
28633 var x = Math.pow(coords[0] - coords[2], 2);
28634 var y = Math.pow(coords[1] - coords[3], 2);
28636 this.startDistance = Math.sqrt(x + y);
28638 this.startScale = this.scale;
28640 this.pinching = true;
28641 this.dragable = false;
28645 onTouchMove : function(e)
28647 if(!this.pinching && !this.dragable){
28651 var touches = e.browserEvent.touches;
28658 this.onMouseMove(e);
28664 for(var i = 0, finger; finger = touches[i]; i++){
28665 coords.push(finger.pageX, finger.pageY);
28668 var x = Math.pow(coords[0] - coords[2], 2);
28669 var y = Math.pow(coords[1] - coords[3], 2);
28671 this.endDistance = Math.sqrt(x + y);
28673 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28675 if(!this.zoomable()){
28676 this.scale = this.startScale;
28684 onTouchEnd : function(e)
28686 this.pinching = false;
28687 this.dragable = false;
28691 process : function(file, crop)
28694 this.maskEl.mask(this.loadingText);
28697 this.xhr = new XMLHttpRequest();
28699 file.xhr = this.xhr;
28701 this.xhr.open(this.method, this.url, true);
28704 "Accept": "application/json",
28705 "Cache-Control": "no-cache",
28706 "X-Requested-With": "XMLHttpRequest"
28709 for (var headerName in headers) {
28710 var headerValue = headers[headerName];
28712 this.xhr.setRequestHeader(headerName, headerValue);
28718 this.xhr.onload = function()
28720 _this.xhrOnLoad(_this.xhr);
28723 this.xhr.onerror = function()
28725 _this.xhrOnError(_this.xhr);
28728 var formData = new FormData();
28730 formData.append('returnHTML', 'NO');
28733 formData.append('crop', crop);
28736 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28737 formData.append(this.paramName, file, file.name);
28740 if(typeof(file.filename) != 'undefined'){
28741 formData.append('filename', file.filename);
28744 if(typeof(file.mimetype) != 'undefined'){
28745 formData.append('mimetype', file.mimetype);
28748 if(this.fireEvent('arrange', this, formData) != false){
28749 this.xhr.send(formData);
28753 xhrOnLoad : function(xhr)
28756 this.maskEl.unmask();
28759 if (xhr.readyState !== 4) {
28760 this.fireEvent('exception', this, xhr);
28764 var response = Roo.decode(xhr.responseText);
28766 if(!response.success){
28767 this.fireEvent('exception', this, xhr);
28771 var response = Roo.decode(xhr.responseText);
28773 this.fireEvent('upload', this, response);
28777 xhrOnError : function()
28780 this.maskEl.unmask();
28783 Roo.log('xhr on error');
28785 var response = Roo.decode(xhr.responseText);
28791 prepare : function(file)
28794 this.maskEl.mask(this.loadingText);
28800 if(typeof(file) === 'string'){
28801 this.loadCanvas(file);
28805 if(!file || !this.urlAPI){
28810 this.cropType = file.type;
28814 if(this.fireEvent('prepare', this, this.file) != false){
28816 var reader = new FileReader();
28818 reader.onload = function (e) {
28819 if (e.target.error) {
28820 Roo.log(e.target.error);
28824 var buffer = e.target.result,
28825 dataView = new DataView(buffer),
28827 maxOffset = dataView.byteLength - 4,
28831 if (dataView.getUint16(0) === 0xffd8) {
28832 while (offset < maxOffset) {
28833 markerBytes = dataView.getUint16(offset);
28835 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28836 markerLength = dataView.getUint16(offset + 2) + 2;
28837 if (offset + markerLength > dataView.byteLength) {
28838 Roo.log('Invalid meta data: Invalid segment size.');
28842 if(markerBytes == 0xffe1){
28843 _this.parseExifData(
28850 offset += markerLength;
28860 var url = _this.urlAPI.createObjectURL(_this.file);
28862 _this.loadCanvas(url);
28867 reader.readAsArrayBuffer(this.file);
28873 parseExifData : function(dataView, offset, length)
28875 var tiffOffset = offset + 10,
28879 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28880 // No Exif data, might be XMP data instead
28884 // Check for the ASCII code for "Exif" (0x45786966):
28885 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28886 // No Exif data, might be XMP data instead
28889 if (tiffOffset + 8 > dataView.byteLength) {
28890 Roo.log('Invalid Exif data: Invalid segment size.');
28893 // Check for the two null bytes:
28894 if (dataView.getUint16(offset + 8) !== 0x0000) {
28895 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28898 // Check the byte alignment:
28899 switch (dataView.getUint16(tiffOffset)) {
28901 littleEndian = true;
28904 littleEndian = false;
28907 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28910 // Check for the TIFF tag marker (0x002A):
28911 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28912 Roo.log('Invalid Exif data: Missing TIFF marker.');
28915 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28916 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28918 this.parseExifTags(
28921 tiffOffset + dirOffset,
28926 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28931 if (dirOffset + 6 > dataView.byteLength) {
28932 Roo.log('Invalid Exif data: Invalid directory offset.');
28935 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28936 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28937 if (dirEndOffset + 4 > dataView.byteLength) {
28938 Roo.log('Invalid Exif data: Invalid directory size.');
28941 for (i = 0; i < tagsNumber; i += 1) {
28945 dirOffset + 2 + 12 * i, // tag offset
28949 // Return the offset to the next directory:
28950 return dataView.getUint32(dirEndOffset, littleEndian);
28953 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28955 var tag = dataView.getUint16(offset, littleEndian);
28957 this.exif[tag] = this.getExifValue(
28961 dataView.getUint16(offset + 2, littleEndian), // tag type
28962 dataView.getUint32(offset + 4, littleEndian), // tag length
28967 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28969 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28978 Roo.log('Invalid Exif data: Invalid tag type.');
28982 tagSize = tagType.size * length;
28983 // Determine if the value is contained in the dataOffset bytes,
28984 // or if the value at the dataOffset is a pointer to the actual data:
28985 dataOffset = tagSize > 4 ?
28986 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28987 if (dataOffset + tagSize > dataView.byteLength) {
28988 Roo.log('Invalid Exif data: Invalid data offset.');
28991 if (length === 1) {
28992 return tagType.getValue(dataView, dataOffset, littleEndian);
28995 for (i = 0; i < length; i += 1) {
28996 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28999 if (tagType.ascii) {
29001 // Concatenate the chars:
29002 for (i = 0; i < values.length; i += 1) {
29004 // Ignore the terminating NULL byte(s):
29005 if (c === '\u0000') {
29017 Roo.apply(Roo.bootstrap.UploadCropbox, {
29019 'Orientation': 0x0112
29023 1: 0, //'top-left',
29025 3: 180, //'bottom-right',
29026 // 4: 'bottom-left',
29028 6: 90, //'right-top',
29029 // 7: 'right-bottom',
29030 8: 270 //'left-bottom'
29034 // byte, 8-bit unsigned int:
29036 getValue: function (dataView, dataOffset) {
29037 return dataView.getUint8(dataOffset);
29041 // ascii, 8-bit byte:
29043 getValue: function (dataView, dataOffset) {
29044 return String.fromCharCode(dataView.getUint8(dataOffset));
29049 // short, 16 bit int:
29051 getValue: function (dataView, dataOffset, littleEndian) {
29052 return dataView.getUint16(dataOffset, littleEndian);
29056 // long, 32 bit int:
29058 getValue: function (dataView, dataOffset, littleEndian) {
29059 return dataView.getUint32(dataOffset, littleEndian);
29063 // rational = two long values, first is numerator, second is denominator:
29065 getValue: function (dataView, dataOffset, littleEndian) {
29066 return dataView.getUint32(dataOffset, littleEndian) /
29067 dataView.getUint32(dataOffset + 4, littleEndian);
29071 // slong, 32 bit signed int:
29073 getValue: function (dataView, dataOffset, littleEndian) {
29074 return dataView.getInt32(dataOffset, littleEndian);
29078 // srational, two slongs, first is numerator, second is denominator:
29080 getValue: function (dataView, dataOffset, littleEndian) {
29081 return dataView.getInt32(dataOffset, littleEndian) /
29082 dataView.getInt32(dataOffset + 4, littleEndian);
29092 cls : 'btn-group roo-upload-cropbox-rotate-left',
29093 action : 'rotate-left',
29097 cls : 'btn btn-default',
29098 html : '<i class="fa fa-undo"></i>'
29104 cls : 'btn-group roo-upload-cropbox-picture',
29105 action : 'picture',
29109 cls : 'btn btn-default',
29110 html : '<i class="fa fa-picture-o"></i>'
29116 cls : 'btn-group roo-upload-cropbox-rotate-right',
29117 action : 'rotate-right',
29121 cls : 'btn btn-default',
29122 html : '<i class="fa fa-repeat"></i>'
29130 cls : 'btn-group roo-upload-cropbox-rotate-left',
29131 action : 'rotate-left',
29135 cls : 'btn btn-default',
29136 html : '<i class="fa fa-undo"></i>'
29142 cls : 'btn-group roo-upload-cropbox-download',
29143 action : 'download',
29147 cls : 'btn btn-default',
29148 html : '<i class="fa fa-download"></i>'
29154 cls : 'btn-group roo-upload-cropbox-crop',
29159 cls : 'btn btn-default',
29160 html : '<i class="fa fa-crop"></i>'
29166 cls : 'btn-group roo-upload-cropbox-trash',
29171 cls : 'btn btn-default',
29172 html : '<i class="fa fa-trash"></i>'
29178 cls : 'btn-group roo-upload-cropbox-rotate-right',
29179 action : 'rotate-right',
29183 cls : 'btn btn-default',
29184 html : '<i class="fa fa-repeat"></i>'
29192 cls : 'btn-group roo-upload-cropbox-rotate-left',
29193 action : 'rotate-left',
29197 cls : 'btn btn-default',
29198 html : '<i class="fa fa-undo"></i>'
29204 cls : 'btn-group roo-upload-cropbox-rotate-right',
29205 action : 'rotate-right',
29209 cls : 'btn btn-default',
29210 html : '<i class="fa fa-repeat"></i>'
29223 * @class Roo.bootstrap.DocumentManager
29224 * @extends Roo.bootstrap.Component
29225 * Bootstrap DocumentManager class
29226 * @cfg {String} paramName default 'imageUpload'
29227 * @cfg {String} toolTipName default 'filename'
29228 * @cfg {String} method default POST
29229 * @cfg {String} url action url
29230 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29231 * @cfg {Boolean} multiple multiple upload default true
29232 * @cfg {Number} thumbSize default 300
29233 * @cfg {String} fieldLabel
29234 * @cfg {Number} labelWidth default 4
29235 * @cfg {String} labelAlign (left|top) default left
29236 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29237 * @cfg {Number} labellg set the width of label (1-12)
29238 * @cfg {Number} labelmd set the width of label (1-12)
29239 * @cfg {Number} labelsm set the width of label (1-12)
29240 * @cfg {Number} labelxs set the width of label (1-12)
29243 * Create a new DocumentManager
29244 * @param {Object} config The config object
29247 Roo.bootstrap.DocumentManager = function(config){
29248 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29251 this.delegates = [];
29256 * Fire when initial the DocumentManager
29257 * @param {Roo.bootstrap.DocumentManager} this
29262 * inspect selected file
29263 * @param {Roo.bootstrap.DocumentManager} this
29264 * @param {File} file
29269 * Fire when xhr load exception
29270 * @param {Roo.bootstrap.DocumentManager} this
29271 * @param {XMLHttpRequest} xhr
29273 "exception" : true,
29275 * @event afterupload
29276 * Fire when xhr load exception
29277 * @param {Roo.bootstrap.DocumentManager} this
29278 * @param {XMLHttpRequest} xhr
29280 "afterupload" : true,
29283 * prepare the form data
29284 * @param {Roo.bootstrap.DocumentManager} this
29285 * @param {Object} formData
29290 * Fire when remove the file
29291 * @param {Roo.bootstrap.DocumentManager} this
29292 * @param {Object} file
29297 * Fire after refresh the file
29298 * @param {Roo.bootstrap.DocumentManager} this
29303 * Fire after click the image
29304 * @param {Roo.bootstrap.DocumentManager} this
29305 * @param {Object} file
29310 * Fire when upload a image and editable set to true
29311 * @param {Roo.bootstrap.DocumentManager} this
29312 * @param {Object} file
29316 * @event beforeselectfile
29317 * Fire before select file
29318 * @param {Roo.bootstrap.DocumentManager} this
29320 "beforeselectfile" : true,
29323 * Fire before process file
29324 * @param {Roo.bootstrap.DocumentManager} this
29325 * @param {Object} file
29329 * @event previewrendered
29330 * Fire when preview rendered
29331 * @param {Roo.bootstrap.DocumentManager} this
29332 * @param {Object} file
29334 "previewrendered" : true,
29337 "previewResize" : true
29342 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
29351 paramName : 'imageUpload',
29352 toolTipName : 'filename',
29355 labelAlign : 'left',
29365 getAutoCreate : function()
29367 var managerWidget = {
29369 cls : 'roo-document-manager',
29373 cls : 'roo-document-manager-selector',
29378 cls : 'roo-document-manager-uploader',
29382 cls : 'roo-document-manager-upload-btn',
29383 html : '<i class="fa fa-plus"></i>'
29394 cls : 'column col-md-12',
29399 if(this.fieldLabel.length){
29404 cls : 'column col-md-12',
29405 html : this.fieldLabel
29409 cls : 'column col-md-12',
29414 if(this.labelAlign == 'left'){
29419 html : this.fieldLabel
29428 if(this.labelWidth > 12){
29429 content[0].style = "width: " + this.labelWidth + 'px';
29432 if(this.labelWidth < 13 && this.labelmd == 0){
29433 this.labelmd = this.labelWidth;
29436 if(this.labellg > 0){
29437 content[0].cls += ' col-lg-' + this.labellg;
29438 content[1].cls += ' col-lg-' + (12 - this.labellg);
29441 if(this.labelmd > 0){
29442 content[0].cls += ' col-md-' + this.labelmd;
29443 content[1].cls += ' col-md-' + (12 - this.labelmd);
29446 if(this.labelsm > 0){
29447 content[0].cls += ' col-sm-' + this.labelsm;
29448 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29451 if(this.labelxs > 0){
29452 content[0].cls += ' col-xs-' + this.labelxs;
29453 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29461 cls : 'row clearfix',
29469 initEvents : function()
29471 this.managerEl = this.el.select('.roo-document-manager', true).first();
29472 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29474 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29475 this.selectorEl.hide();
29478 this.selectorEl.attr('multiple', 'multiple');
29481 this.selectorEl.on('change', this.onFileSelected, this);
29483 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29484 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29486 this.uploader.on('click', this.onUploaderClick, this);
29488 this.renderProgressDialog();
29492 window.addEventListener("resize", function() { _this.refresh(); } );
29494 this.fireEvent('initial', this);
29497 renderProgressDialog : function()
29501 this.progressDialog = new Roo.bootstrap.Modal({
29502 cls : 'roo-document-manager-progress-dialog',
29503 allow_close : false,
29514 btnclick : function() {
29515 _this.uploadCancel();
29521 this.progressDialog.render(Roo.get(document.body));
29523 this.progress = new Roo.bootstrap.Progress({
29524 cls : 'roo-document-manager-progress',
29529 this.progress.render(this.progressDialog.getChildContainer());
29531 this.progressBar = new Roo.bootstrap.ProgressBar({
29532 cls : 'roo-document-manager-progress-bar',
29535 aria_valuemax : 12,
29539 this.progressBar.render(this.progress.getChildContainer());
29542 onUploaderClick : function(e)
29544 e.preventDefault();
29546 if(this.fireEvent('beforeselectfile', this) != false){
29547 this.selectorEl.dom.click();
29552 onFileSelected : function(e)
29554 e.preventDefault();
29556 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29560 Roo.each(this.selectorEl.dom.files, function(file){
29561 if(this.fireEvent('inspect', this, file) != false){
29562 this.files.push(file);
29572 this.selectorEl.dom.value = '';
29574 if(!this.files || !this.files.length){
29578 if(this.boxes > 0 && this.files.length > this.boxes){
29579 this.files = this.files.slice(0, this.boxes);
29582 this.uploader.show();
29584 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29585 this.uploader.hide();
29594 Roo.each(this.files, function(file){
29596 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29597 var f = this.renderPreview(file);
29602 if(file.type.indexOf('image') != -1){
29603 this.delegates.push(
29605 _this.process(file);
29606 }).createDelegate(this)
29614 _this.process(file);
29615 }).createDelegate(this)
29620 this.files = files;
29622 this.delegates = this.delegates.concat(docs);
29624 if(!this.delegates.length){
29629 this.progressBar.aria_valuemax = this.delegates.length;
29636 arrange : function()
29638 if(!this.delegates.length){
29639 this.progressDialog.hide();
29644 var delegate = this.delegates.shift();
29646 this.progressDialog.show();
29648 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29650 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29655 refresh : function()
29657 this.uploader.show();
29659 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29660 this.uploader.hide();
29663 Roo.isTouch ? this.closable(false) : this.closable(true);
29665 this.fireEvent('refresh', this);
29668 onRemove : function(e, el, o)
29670 e.preventDefault();
29672 this.fireEvent('remove', this, o);
29676 remove : function(o)
29680 Roo.each(this.files, function(file){
29681 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29690 this.files = files;
29697 Roo.each(this.files, function(file){
29702 file.target.remove();
29711 onClick : function(e, el, o)
29713 e.preventDefault();
29715 this.fireEvent('click', this, o);
29719 closable : function(closable)
29721 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29723 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29735 xhrOnLoad : function(xhr)
29737 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29741 if (xhr.readyState !== 4) {
29743 this.fireEvent('exception', this, xhr);
29747 var response = Roo.decode(xhr.responseText);
29749 if(!response.success){
29751 this.fireEvent('exception', this, xhr);
29755 var file = this.renderPreview(response.data);
29757 this.files.push(file);
29761 this.fireEvent('afterupload', this, xhr);
29765 xhrOnError : function(xhr)
29767 Roo.log('xhr on error');
29769 var response = Roo.decode(xhr.responseText);
29776 process : function(file)
29778 if(this.fireEvent('process', this, file) !== false){
29779 if(this.editable && file.type.indexOf('image') != -1){
29780 this.fireEvent('edit', this, file);
29784 this.uploadStart(file, false);
29791 uploadStart : function(file, crop)
29793 this.xhr = new XMLHttpRequest();
29795 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29800 file.xhr = this.xhr;
29802 this.managerEl.createChild({
29804 cls : 'roo-document-manager-loading',
29808 tooltip : file.name,
29809 cls : 'roo-document-manager-thumb',
29810 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29816 this.xhr.open(this.method, this.url, true);
29819 "Accept": "application/json",
29820 "Cache-Control": "no-cache",
29821 "X-Requested-With": "XMLHttpRequest"
29824 for (var headerName in headers) {
29825 var headerValue = headers[headerName];
29827 this.xhr.setRequestHeader(headerName, headerValue);
29833 this.xhr.onload = function()
29835 _this.xhrOnLoad(_this.xhr);
29838 this.xhr.onerror = function()
29840 _this.xhrOnError(_this.xhr);
29843 var formData = new FormData();
29845 formData.append('returnHTML', 'NO');
29848 formData.append('crop', crop);
29851 formData.append(this.paramName, file, file.name);
29858 if(this.fireEvent('prepare', this, formData, options) != false){
29860 if(options.manually){
29864 this.xhr.send(formData);
29868 this.uploadCancel();
29871 uploadCancel : function()
29877 this.delegates = [];
29879 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29886 renderPreview : function(file)
29888 if(typeof(file.target) != 'undefined' && file.target){
29892 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29894 var previewEl = this.managerEl.createChild({
29896 cls : 'roo-document-manager-preview',
29900 tooltip : file[this.toolTipName],
29901 cls : 'roo-document-manager-thumb',
29902 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29907 html : '<i class="fa fa-times-circle"></i>'
29912 var close = previewEl.select('button.close', true).first();
29914 close.on('click', this.onRemove, this, file);
29916 file.target = previewEl;
29918 var image = previewEl.select('img', true).first();
29922 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29924 image.on('click', this.onClick, this, file);
29926 this.fireEvent('previewrendered', this, file);
29932 onPreviewLoad : function(file, image)
29934 if(typeof(file.target) == 'undefined' || !file.target){
29938 var width = image.dom.naturalWidth || image.dom.width;
29939 var height = image.dom.naturalHeight || image.dom.height;
29941 if(!this.previewResize) {
29945 if(width > height){
29946 file.target.addClass('wide');
29950 file.target.addClass('tall');
29955 uploadFromSource : function(file, crop)
29957 this.xhr = new XMLHttpRequest();
29959 this.managerEl.createChild({
29961 cls : 'roo-document-manager-loading',
29965 tooltip : file.name,
29966 cls : 'roo-document-manager-thumb',
29967 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29973 this.xhr.open(this.method, this.url, true);
29976 "Accept": "application/json",
29977 "Cache-Control": "no-cache",
29978 "X-Requested-With": "XMLHttpRequest"
29981 for (var headerName in headers) {
29982 var headerValue = headers[headerName];
29984 this.xhr.setRequestHeader(headerName, headerValue);
29990 this.xhr.onload = function()
29992 _this.xhrOnLoad(_this.xhr);
29995 this.xhr.onerror = function()
29997 _this.xhrOnError(_this.xhr);
30000 var formData = new FormData();
30002 formData.append('returnHTML', 'NO');
30004 formData.append('crop', crop);
30006 if(typeof(file.filename) != 'undefined'){
30007 formData.append('filename', file.filename);
30010 if(typeof(file.mimetype) != 'undefined'){
30011 formData.append('mimetype', file.mimetype);
30016 if(this.fireEvent('prepare', this, formData) != false){
30017 this.xhr.send(formData);
30027 * @class Roo.bootstrap.DocumentViewer
30028 * @extends Roo.bootstrap.Component
30029 * Bootstrap DocumentViewer class
30030 * @cfg {Boolean} showDownload (true|false) show download button (default true)
30031 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30034 * Create a new DocumentViewer
30035 * @param {Object} config The config object
30038 Roo.bootstrap.DocumentViewer = function(config){
30039 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30044 * Fire after initEvent
30045 * @param {Roo.bootstrap.DocumentViewer} this
30051 * @param {Roo.bootstrap.DocumentViewer} this
30056 * Fire after download button
30057 * @param {Roo.bootstrap.DocumentViewer} this
30062 * Fire after trash button
30063 * @param {Roo.bootstrap.DocumentViewer} this
30070 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
30072 showDownload : true,
30076 getAutoCreate : function()
30080 cls : 'roo-document-viewer',
30084 cls : 'roo-document-viewer-body',
30088 cls : 'roo-document-viewer-thumb',
30092 cls : 'roo-document-viewer-image'
30100 cls : 'roo-document-viewer-footer',
30103 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
30107 cls : 'btn-group roo-document-viewer-download',
30111 cls : 'btn btn-default',
30112 html : '<i class="fa fa-download"></i>'
30118 cls : 'btn-group roo-document-viewer-trash',
30122 cls : 'btn btn-default',
30123 html : '<i class="fa fa-trash"></i>'
30136 initEvents : function()
30138 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30139 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30141 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30142 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30144 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30145 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30147 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30148 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30150 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30151 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30153 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30154 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30156 this.bodyEl.on('click', this.onClick, this);
30157 this.downloadBtn.on('click', this.onDownload, this);
30158 this.trashBtn.on('click', this.onTrash, this);
30160 this.downloadBtn.hide();
30161 this.trashBtn.hide();
30163 if(this.showDownload){
30164 this.downloadBtn.show();
30167 if(this.showTrash){
30168 this.trashBtn.show();
30171 if(!this.showDownload && !this.showTrash) {
30172 this.footerEl.hide();
30177 initial : function()
30179 this.fireEvent('initial', this);
30183 onClick : function(e)
30185 e.preventDefault();
30187 this.fireEvent('click', this);
30190 onDownload : function(e)
30192 e.preventDefault();
30194 this.fireEvent('download', this);
30197 onTrash : function(e)
30199 e.preventDefault();
30201 this.fireEvent('trash', this);
30213 * @class Roo.bootstrap.NavProgressBar
30214 * @extends Roo.bootstrap.Component
30215 * Bootstrap NavProgressBar class
30218 * Create a new nav progress bar
30219 * @param {Object} config The config object
30222 Roo.bootstrap.NavProgressBar = function(config){
30223 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30225 this.bullets = this.bullets || [];
30227 // Roo.bootstrap.NavProgressBar.register(this);
30231 * Fires when the active item changes
30232 * @param {Roo.bootstrap.NavProgressBar} this
30233 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30234 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
30241 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
30246 getAutoCreate : function()
30248 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30252 cls : 'roo-navigation-bar-group',
30256 cls : 'roo-navigation-top-bar'
30260 cls : 'roo-navigation-bullets-bar',
30264 cls : 'roo-navigation-bar'
30271 cls : 'roo-navigation-bottom-bar'
30281 initEvents: function()
30286 onRender : function(ct, position)
30288 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30290 if(this.bullets.length){
30291 Roo.each(this.bullets, function(b){
30300 addItem : function(cfg)
30302 var item = new Roo.bootstrap.NavProgressItem(cfg);
30304 item.parentId = this.id;
30305 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30308 var top = new Roo.bootstrap.Element({
30310 cls : 'roo-navigation-bar-text'
30313 var bottom = new Roo.bootstrap.Element({
30315 cls : 'roo-navigation-bar-text'
30318 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30319 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30321 var topText = new Roo.bootstrap.Element({
30323 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30326 var bottomText = new Roo.bootstrap.Element({
30328 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30331 topText.onRender(top.el, null);
30332 bottomText.onRender(bottom.el, null);
30335 item.bottomEl = bottom;
30338 this.barItems.push(item);
30343 getActive : function()
30345 var active = false;
30347 Roo.each(this.barItems, function(v){
30349 if (!v.isActive()) {
30361 setActiveItem : function(item)
30365 Roo.each(this.barItems, function(v){
30366 if (v.rid == item.rid) {
30370 if (v.isActive()) {
30371 v.setActive(false);
30376 item.setActive(true);
30378 this.fireEvent('changed', this, item, prev);
30381 getBarItem: function(rid)
30385 Roo.each(this.barItems, function(e) {
30386 if (e.rid != rid) {
30397 indexOfItem : function(item)
30401 Roo.each(this.barItems, function(v, i){
30403 if (v.rid != item.rid) {
30414 setActiveNext : function()
30416 var i = this.indexOfItem(this.getActive());
30418 if (i > this.barItems.length) {
30422 this.setActiveItem(this.barItems[i+1]);
30425 setActivePrev : function()
30427 var i = this.indexOfItem(this.getActive());
30433 this.setActiveItem(this.barItems[i-1]);
30436 format : function()
30438 if(!this.barItems.length){
30442 var width = 100 / this.barItems.length;
30444 Roo.each(this.barItems, function(i){
30445 i.el.setStyle('width', width + '%');
30446 i.topEl.el.setStyle('width', width + '%');
30447 i.bottomEl.el.setStyle('width', width + '%');
30456 * Nav Progress Item
30461 * @class Roo.bootstrap.NavProgressItem
30462 * @extends Roo.bootstrap.Component
30463 * Bootstrap NavProgressItem class
30464 * @cfg {String} rid the reference id
30465 * @cfg {Boolean} active (true|false) Is item active default false
30466 * @cfg {Boolean} disabled (true|false) Is item active default false
30467 * @cfg {String} html
30468 * @cfg {String} position (top|bottom) text position default bottom
30469 * @cfg {String} icon show icon instead of number
30472 * Create a new NavProgressItem
30473 * @param {Object} config The config object
30475 Roo.bootstrap.NavProgressItem = function(config){
30476 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30481 * The raw click event for the entire grid.
30482 * @param {Roo.bootstrap.NavProgressItem} this
30483 * @param {Roo.EventObject} e
30490 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30496 position : 'bottom',
30499 getAutoCreate : function()
30501 var iconCls = 'roo-navigation-bar-item-icon';
30503 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30507 cls: 'roo-navigation-bar-item',
30517 cfg.cls += ' active';
30520 cfg.cls += ' disabled';
30526 disable : function()
30528 this.setDisabled(true);
30531 enable : function()
30533 this.setDisabled(false);
30536 initEvents: function()
30538 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30540 this.iconEl.on('click', this.onClick, this);
30543 onClick : function(e)
30545 e.preventDefault();
30551 if(this.fireEvent('click', this, e) === false){
30555 this.parent().setActiveItem(this);
30558 isActive: function ()
30560 return this.active;
30563 setActive : function(state)
30565 if(this.active == state){
30569 this.active = state;
30572 this.el.addClass('active');
30576 this.el.removeClass('active');
30581 setDisabled : function(state)
30583 if(this.disabled == state){
30587 this.disabled = state;
30590 this.el.addClass('disabled');
30594 this.el.removeClass('disabled');
30597 tooltipEl : function()
30599 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30612 * @class Roo.bootstrap.FieldLabel
30613 * @extends Roo.bootstrap.Component
30614 * Bootstrap FieldLabel class
30615 * @cfg {String} html contents of the element
30616 * @cfg {String} tag tag of the element default label
30617 * @cfg {String} cls class of the element
30618 * @cfg {String} target label target
30619 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30620 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30621 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30622 * @cfg {String} iconTooltip default "This field is required"
30623 * @cfg {String} indicatorpos (left|right) default left
30626 * Create a new FieldLabel
30627 * @param {Object} config The config object
30630 Roo.bootstrap.FieldLabel = function(config){
30631 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30636 * Fires after the field has been marked as invalid.
30637 * @param {Roo.form.FieldLabel} this
30638 * @param {String} msg The validation message
30643 * Fires after the field has been validated with no errors.
30644 * @param {Roo.form.FieldLabel} this
30650 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30657 invalidClass : 'has-warning',
30658 validClass : 'has-success',
30659 iconTooltip : 'This field is required',
30660 indicatorpos : 'left',
30662 getAutoCreate : function(){
30665 if (!this.allowBlank) {
30671 cls : 'roo-bootstrap-field-label ' + this.cls,
30676 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30677 tooltip : this.iconTooltip
30686 if(this.indicatorpos == 'right'){
30689 cls : 'roo-bootstrap-field-label ' + this.cls,
30698 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30699 tooltip : this.iconTooltip
30708 initEvents: function()
30710 Roo.bootstrap.Element.superclass.initEvents.call(this);
30712 this.indicator = this.indicatorEl();
30714 if(this.indicator){
30715 this.indicator.removeClass('visible');
30716 this.indicator.addClass('invisible');
30719 Roo.bootstrap.FieldLabel.register(this);
30722 indicatorEl : function()
30724 var indicator = this.el.select('i.roo-required-indicator',true).first();
30735 * Mark this field as valid
30737 markValid : function()
30739 if(this.indicator){
30740 this.indicator.removeClass('visible');
30741 this.indicator.addClass('invisible');
30743 if (Roo.bootstrap.version == 3) {
30744 this.el.removeClass(this.invalidClass);
30745 this.el.addClass(this.validClass);
30747 this.el.removeClass('is-invalid');
30748 this.el.addClass('is-valid');
30752 this.fireEvent('valid', this);
30756 * Mark this field as invalid
30757 * @param {String} msg The validation message
30759 markInvalid : function(msg)
30761 if(this.indicator){
30762 this.indicator.removeClass('invisible');
30763 this.indicator.addClass('visible');
30765 if (Roo.bootstrap.version == 3) {
30766 this.el.removeClass(this.validClass);
30767 this.el.addClass(this.invalidClass);
30769 this.el.removeClass('is-valid');
30770 this.el.addClass('is-invalid');
30774 this.fireEvent('invalid', this, msg);
30780 Roo.apply(Roo.bootstrap.FieldLabel, {
30785 * register a FieldLabel Group
30786 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30788 register : function(label)
30790 if(this.groups.hasOwnProperty(label.target)){
30794 this.groups[label.target] = label;
30798 * fetch a FieldLabel Group based on the target
30799 * @param {string} target
30800 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30802 get: function(target) {
30803 if (typeof(this.groups[target]) == 'undefined') {
30807 return this.groups[target] ;
30816 * page DateSplitField.
30822 * @class Roo.bootstrap.DateSplitField
30823 * @extends Roo.bootstrap.Component
30824 * Bootstrap DateSplitField class
30825 * @cfg {string} fieldLabel - the label associated
30826 * @cfg {Number} labelWidth set the width of label (0-12)
30827 * @cfg {String} labelAlign (top|left)
30828 * @cfg {Boolean} dayAllowBlank (true|false) default false
30829 * @cfg {Boolean} monthAllowBlank (true|false) default false
30830 * @cfg {Boolean} yearAllowBlank (true|false) default false
30831 * @cfg {string} dayPlaceholder
30832 * @cfg {string} monthPlaceholder
30833 * @cfg {string} yearPlaceholder
30834 * @cfg {string} dayFormat default 'd'
30835 * @cfg {string} monthFormat default 'm'
30836 * @cfg {string} yearFormat default 'Y'
30837 * @cfg {Number} labellg set the width of label (1-12)
30838 * @cfg {Number} labelmd set the width of label (1-12)
30839 * @cfg {Number} labelsm set the width of label (1-12)
30840 * @cfg {Number} labelxs set the width of label (1-12)
30844 * Create a new DateSplitField
30845 * @param {Object} config The config object
30848 Roo.bootstrap.DateSplitField = function(config){
30849 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30855 * getting the data of years
30856 * @param {Roo.bootstrap.DateSplitField} this
30857 * @param {Object} years
30862 * getting the data of days
30863 * @param {Roo.bootstrap.DateSplitField} this
30864 * @param {Object} days
30869 * Fires after the field has been marked as invalid.
30870 * @param {Roo.form.Field} this
30871 * @param {String} msg The validation message
30876 * Fires after the field has been validated with no errors.
30877 * @param {Roo.form.Field} this
30883 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30886 labelAlign : 'top',
30888 dayAllowBlank : false,
30889 monthAllowBlank : false,
30890 yearAllowBlank : false,
30891 dayPlaceholder : '',
30892 monthPlaceholder : '',
30893 yearPlaceholder : '',
30897 isFormField : true,
30903 getAutoCreate : function()
30907 cls : 'row roo-date-split-field-group',
30912 cls : 'form-hidden-field roo-date-split-field-group-value',
30918 var labelCls = 'col-md-12';
30919 var contentCls = 'col-md-4';
30921 if(this.fieldLabel){
30925 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30929 html : this.fieldLabel
30934 if(this.labelAlign == 'left'){
30936 if(this.labelWidth > 12){
30937 label.style = "width: " + this.labelWidth + 'px';
30940 if(this.labelWidth < 13 && this.labelmd == 0){
30941 this.labelmd = this.labelWidth;
30944 if(this.labellg > 0){
30945 labelCls = ' col-lg-' + this.labellg;
30946 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30949 if(this.labelmd > 0){
30950 labelCls = ' col-md-' + this.labelmd;
30951 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30954 if(this.labelsm > 0){
30955 labelCls = ' col-sm-' + this.labelsm;
30956 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30959 if(this.labelxs > 0){
30960 labelCls = ' col-xs-' + this.labelxs;
30961 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30965 label.cls += ' ' + labelCls;
30967 cfg.cn.push(label);
30970 Roo.each(['day', 'month', 'year'], function(t){
30973 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30980 inputEl: function ()
30982 return this.el.select('.roo-date-split-field-group-value', true).first();
30985 onRender : function(ct, position)
30989 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30991 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30993 this.dayField = new Roo.bootstrap.ComboBox({
30994 allowBlank : this.dayAllowBlank,
30995 alwaysQuery : true,
30996 displayField : 'value',
30999 forceSelection : true,
31001 placeholder : this.dayPlaceholder,
31002 selectOnFocus : true,
31003 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31004 triggerAction : 'all',
31006 valueField : 'value',
31007 store : new Roo.data.SimpleStore({
31008 data : (function() {
31010 _this.fireEvent('days', _this, days);
31013 fields : [ 'value' ]
31016 select : function (_self, record, index)
31018 _this.setValue(_this.getValue());
31023 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
31025 this.monthField = new Roo.bootstrap.MonthField({
31026 after : '<i class=\"fa fa-calendar\"></i>',
31027 allowBlank : this.monthAllowBlank,
31028 placeholder : this.monthPlaceholder,
31031 render : function (_self)
31033 this.el.select('span.input-group-addon', true).first().on('click', function(e){
31034 e.preventDefault();
31038 select : function (_self, oldvalue, newvalue)
31040 _this.setValue(_this.getValue());
31045 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31047 this.yearField = new Roo.bootstrap.ComboBox({
31048 allowBlank : this.yearAllowBlank,
31049 alwaysQuery : true,
31050 displayField : 'value',
31053 forceSelection : true,
31055 placeholder : this.yearPlaceholder,
31056 selectOnFocus : true,
31057 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31058 triggerAction : 'all',
31060 valueField : 'value',
31061 store : new Roo.data.SimpleStore({
31062 data : (function() {
31064 _this.fireEvent('years', _this, years);
31067 fields : [ 'value' ]
31070 select : function (_self, record, index)
31072 _this.setValue(_this.getValue());
31077 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
31080 setValue : function(v, format)
31082 this.inputEl.dom.value = v;
31084 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
31086 var d = Date.parseDate(v, f);
31093 this.setDay(d.format(this.dayFormat));
31094 this.setMonth(d.format(this.monthFormat));
31095 this.setYear(d.format(this.yearFormat));
31102 setDay : function(v)
31104 this.dayField.setValue(v);
31105 this.inputEl.dom.value = this.getValue();
31110 setMonth : function(v)
31112 this.monthField.setValue(v, true);
31113 this.inputEl.dom.value = this.getValue();
31118 setYear : function(v)
31120 this.yearField.setValue(v);
31121 this.inputEl.dom.value = this.getValue();
31126 getDay : function()
31128 return this.dayField.getValue();
31131 getMonth : function()
31133 return this.monthField.getValue();
31136 getYear : function()
31138 return this.yearField.getValue();
31141 getValue : function()
31143 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31145 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31155 this.inputEl.dom.value = '';
31160 validate : function()
31162 var d = this.dayField.validate();
31163 var m = this.monthField.validate();
31164 var y = this.yearField.validate();
31169 (!this.dayAllowBlank && !d) ||
31170 (!this.monthAllowBlank && !m) ||
31171 (!this.yearAllowBlank && !y)
31176 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31185 this.markInvalid();
31190 markValid : function()
31193 var label = this.el.select('label', true).first();
31194 var icon = this.el.select('i.fa-star', true).first();
31200 this.fireEvent('valid', this);
31204 * Mark this field as invalid
31205 * @param {String} msg The validation message
31207 markInvalid : function(msg)
31210 var label = this.el.select('label', true).first();
31211 var icon = this.el.select('i.fa-star', true).first();
31213 if(label && !icon){
31214 this.el.select('.roo-date-split-field-label', true).createChild({
31216 cls : 'text-danger fa fa-lg fa-star',
31217 tooltip : 'This field is required',
31218 style : 'margin-right:5px;'
31222 this.fireEvent('invalid', this, msg);
31225 clearInvalid : function()
31227 var label = this.el.select('label', true).first();
31228 var icon = this.el.select('i.fa-star', true).first();
31234 this.fireEvent('valid', this);
31237 getName: function()
31247 * http://masonry.desandro.com
31249 * The idea is to render all the bricks based on vertical width...
31251 * The original code extends 'outlayer' - we might need to use that....
31257 * @class Roo.bootstrap.LayoutMasonry
31258 * @extends Roo.bootstrap.Component
31259 * Bootstrap Layout Masonry class
31262 * Create a new Element
31263 * @param {Object} config The config object
31266 Roo.bootstrap.LayoutMasonry = function(config){
31268 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31272 Roo.bootstrap.LayoutMasonry.register(this);
31278 * Fire after layout the items
31279 * @param {Roo.bootstrap.LayoutMasonry} this
31280 * @param {Roo.EventObject} e
31287 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
31290 * @cfg {Boolean} isLayoutInstant = no animation?
31292 isLayoutInstant : false, // needed?
31295 * @cfg {Number} boxWidth width of the columns
31300 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
31305 * @cfg {Number} padWidth padding below box..
31310 * @cfg {Number} gutter gutter width..
31315 * @cfg {Number} maxCols maximum number of columns
31321 * @cfg {Boolean} isAutoInitial defalut true
31323 isAutoInitial : true,
31328 * @cfg {Boolean} isHorizontal defalut false
31330 isHorizontal : false,
31332 currentSize : null,
31338 bricks: null, //CompositeElement
31342 _isLayoutInited : false,
31344 // isAlternative : false, // only use for vertical layout...
31347 * @cfg {Number} alternativePadWidth padding below box..
31349 alternativePadWidth : 50,
31351 selectedBrick : [],
31353 getAutoCreate : function(){
31355 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31359 cls: 'blog-masonary-wrapper ' + this.cls,
31361 cls : 'mas-boxes masonary'
31368 getChildContainer: function( )
31370 if (this.boxesEl) {
31371 return this.boxesEl;
31374 this.boxesEl = this.el.select('.mas-boxes').first();
31376 return this.boxesEl;
31380 initEvents : function()
31384 if(this.isAutoInitial){
31385 Roo.log('hook children rendered');
31386 this.on('childrenrendered', function() {
31387 Roo.log('children rendered');
31393 initial : function()
31395 this.selectedBrick = [];
31397 this.currentSize = this.el.getBox(true);
31399 Roo.EventManager.onWindowResize(this.resize, this);
31401 if(!this.isAutoInitial){
31409 //this.layout.defer(500,this);
31413 resize : function()
31415 var cs = this.el.getBox(true);
31418 this.currentSize.width == cs.width &&
31419 this.currentSize.x == cs.x &&
31420 this.currentSize.height == cs.height &&
31421 this.currentSize.y == cs.y
31423 Roo.log("no change in with or X or Y");
31427 this.currentSize = cs;
31433 layout : function()
31435 this._resetLayout();
31437 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31439 this.layoutItems( isInstant );
31441 this._isLayoutInited = true;
31443 this.fireEvent('layout', this);
31447 _resetLayout : function()
31449 if(this.isHorizontal){
31450 this.horizontalMeasureColumns();
31454 this.verticalMeasureColumns();
31458 verticalMeasureColumns : function()
31460 this.getContainerWidth();
31462 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31463 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31467 var boxWidth = this.boxWidth + this.padWidth;
31469 if(this.containerWidth < this.boxWidth){
31470 boxWidth = this.containerWidth
31473 var containerWidth = this.containerWidth;
31475 var cols = Math.floor(containerWidth / boxWidth);
31477 this.cols = Math.max( cols, 1 );
31479 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31481 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31483 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31485 this.colWidth = boxWidth + avail - this.padWidth;
31487 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31488 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31491 horizontalMeasureColumns : function()
31493 this.getContainerWidth();
31495 var boxWidth = this.boxWidth;
31497 if(this.containerWidth < boxWidth){
31498 boxWidth = this.containerWidth;
31501 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31503 this.el.setHeight(boxWidth);
31507 getContainerWidth : function()
31509 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31512 layoutItems : function( isInstant )
31514 Roo.log(this.bricks);
31516 var items = Roo.apply([], this.bricks);
31518 if(this.isHorizontal){
31519 this._horizontalLayoutItems( items , isInstant );
31523 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31524 // this._verticalAlternativeLayoutItems( items , isInstant );
31528 this._verticalLayoutItems( items , isInstant );
31532 _verticalLayoutItems : function ( items , isInstant)
31534 if ( !items || !items.length ) {
31539 ['xs', 'xs', 'xs', 'tall'],
31540 ['xs', 'xs', 'tall'],
31541 ['xs', 'xs', 'sm'],
31542 ['xs', 'xs', 'xs'],
31548 ['sm', 'xs', 'xs'],
31552 ['tall', 'xs', 'xs', 'xs'],
31553 ['tall', 'xs', 'xs'],
31565 Roo.each(items, function(item, k){
31567 switch (item.size) {
31568 // these layouts take up a full box,
31579 boxes.push([item]);
31602 var filterPattern = function(box, length)
31610 var pattern = box.slice(0, length);
31614 Roo.each(pattern, function(i){
31615 format.push(i.size);
31618 Roo.each(standard, function(s){
31620 if(String(s) != String(format)){
31629 if(!match && length == 1){
31634 filterPattern(box, length - 1);
31638 queue.push(pattern);
31640 box = box.slice(length, box.length);
31642 filterPattern(box, 4);
31648 Roo.each(boxes, function(box, k){
31654 if(box.length == 1){
31659 filterPattern(box, 4);
31663 this._processVerticalLayoutQueue( queue, isInstant );
31667 // _verticalAlternativeLayoutItems : function( items , isInstant )
31669 // if ( !items || !items.length ) {
31673 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31677 _horizontalLayoutItems : function ( items , isInstant)
31679 if ( !items || !items.length || items.length < 3) {
31685 var eItems = items.slice(0, 3);
31687 items = items.slice(3, items.length);
31690 ['xs', 'xs', 'xs', 'wide'],
31691 ['xs', 'xs', 'wide'],
31692 ['xs', 'xs', 'sm'],
31693 ['xs', 'xs', 'xs'],
31699 ['sm', 'xs', 'xs'],
31703 ['wide', 'xs', 'xs', 'xs'],
31704 ['wide', 'xs', 'xs'],
31717 Roo.each(items, function(item, k){
31719 switch (item.size) {
31730 boxes.push([item]);
31754 var filterPattern = function(box, length)
31762 var pattern = box.slice(0, length);
31766 Roo.each(pattern, function(i){
31767 format.push(i.size);
31770 Roo.each(standard, function(s){
31772 if(String(s) != String(format)){
31781 if(!match && length == 1){
31786 filterPattern(box, length - 1);
31790 queue.push(pattern);
31792 box = box.slice(length, box.length);
31794 filterPattern(box, 4);
31800 Roo.each(boxes, function(box, k){
31806 if(box.length == 1){
31811 filterPattern(box, 4);
31818 var pos = this.el.getBox(true);
31822 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31824 var hit_end = false;
31826 Roo.each(queue, function(box){
31830 Roo.each(box, function(b){
31832 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31842 Roo.each(box, function(b){
31844 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31847 mx = Math.max(mx, b.x);
31851 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31855 Roo.each(box, function(b){
31857 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31871 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31874 /** Sets position of item in DOM
31875 * @param {Element} item
31876 * @param {Number} x - horizontal position
31877 * @param {Number} y - vertical position
31878 * @param {Boolean} isInstant - disables transitions
31880 _processVerticalLayoutQueue : function( queue, isInstant )
31882 var pos = this.el.getBox(true);
31887 for (var i = 0; i < this.cols; i++){
31891 Roo.each(queue, function(box, k){
31893 var col = k % this.cols;
31895 Roo.each(box, function(b,kk){
31897 b.el.position('absolute');
31899 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31900 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31902 if(b.size == 'md-left' || b.size == 'md-right'){
31903 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31904 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31907 b.el.setWidth(width);
31908 b.el.setHeight(height);
31910 b.el.select('iframe',true).setSize(width,height);
31914 for (var i = 0; i < this.cols; i++){
31916 if(maxY[i] < maxY[col]){
31921 col = Math.min(col, i);
31925 x = pos.x + col * (this.colWidth + this.padWidth);
31929 var positions = [];
31931 switch (box.length){
31933 positions = this.getVerticalOneBoxColPositions(x, y, box);
31936 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31939 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31942 positions = this.getVerticalFourBoxColPositions(x, y, box);
31948 Roo.each(box, function(b,kk){
31950 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31952 var sz = b.el.getSize();
31954 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31962 for (var i = 0; i < this.cols; i++){
31963 mY = Math.max(mY, maxY[i]);
31966 this.el.setHeight(mY - pos.y);
31970 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31972 // var pos = this.el.getBox(true);
31975 // var maxX = pos.right;
31977 // var maxHeight = 0;
31979 // Roo.each(items, function(item, k){
31983 // item.el.position('absolute');
31985 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31987 // item.el.setWidth(width);
31989 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31991 // item.el.setHeight(height);
31994 // item.el.setXY([x, y], isInstant ? false : true);
31996 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31999 // y = y + height + this.alternativePadWidth;
32001 // maxHeight = maxHeight + height + this.alternativePadWidth;
32005 // this.el.setHeight(maxHeight);
32009 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
32011 var pos = this.el.getBox(true);
32016 var maxX = pos.right;
32018 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
32020 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32022 Roo.each(queue, function(box, k){
32024 Roo.each(box, function(b, kk){
32026 b.el.position('absolute');
32028 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32029 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32031 if(b.size == 'md-left' || b.size == 'md-right'){
32032 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32033 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32036 b.el.setWidth(width);
32037 b.el.setHeight(height);
32045 var positions = [];
32047 switch (box.length){
32049 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32052 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
32055 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
32058 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
32064 Roo.each(box, function(b,kk){
32066 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32068 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
32076 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
32078 Roo.each(eItems, function(b,k){
32080 b.size = (k == 0) ? 'sm' : 'xs';
32081 b.x = (k == 0) ? 2 : 1;
32082 b.y = (k == 0) ? 2 : 1;
32084 b.el.position('absolute');
32086 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32088 b.el.setWidth(width);
32090 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32092 b.el.setHeight(height);
32096 var positions = [];
32099 x : maxX - this.unitWidth * 2 - this.gutter,
32104 x : maxX - this.unitWidth,
32105 y : minY + (this.unitWidth + this.gutter) * 2
32109 x : maxX - this.unitWidth * 3 - this.gutter * 2,
32113 Roo.each(eItems, function(b,k){
32115 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32121 getVerticalOneBoxColPositions : function(x, y, box)
32125 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32127 if(box[0].size == 'md-left'){
32131 if(box[0].size == 'md-right'){
32136 x : x + (this.unitWidth + this.gutter) * rand,
32143 getVerticalTwoBoxColPositions : function(x, y, box)
32147 if(box[0].size == 'xs'){
32151 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32155 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32169 x : x + (this.unitWidth + this.gutter) * 2,
32170 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32177 getVerticalThreeBoxColPositions : function(x, y, box)
32181 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32189 x : x + (this.unitWidth + this.gutter) * 1,
32194 x : x + (this.unitWidth + this.gutter) * 2,
32202 if(box[0].size == 'xs' && box[1].size == 'xs'){
32211 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32215 x : x + (this.unitWidth + this.gutter) * 1,
32229 x : x + (this.unitWidth + this.gutter) * 2,
32234 x : x + (this.unitWidth + this.gutter) * 2,
32235 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32242 getVerticalFourBoxColPositions : function(x, y, box)
32246 if(box[0].size == 'xs'){
32255 y : y + (this.unitHeight + this.gutter) * 1
32260 y : y + (this.unitHeight + this.gutter) * 2
32264 x : x + (this.unitWidth + this.gutter) * 1,
32278 x : x + (this.unitWidth + this.gutter) * 2,
32283 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32284 y : y + (this.unitHeight + this.gutter) * 1
32288 x : x + (this.unitWidth + this.gutter) * 2,
32289 y : y + (this.unitWidth + this.gutter) * 2
32296 getHorizontalOneBoxColPositions : function(maxX, minY, box)
32300 if(box[0].size == 'md-left'){
32302 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32309 if(box[0].size == 'md-right'){
32311 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32312 y : minY + (this.unitWidth + this.gutter) * 1
32318 var rand = Math.floor(Math.random() * (4 - box[0].y));
32321 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32322 y : minY + (this.unitWidth + this.gutter) * rand
32329 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32333 if(box[0].size == 'xs'){
32336 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32341 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32342 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32350 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32355 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32356 y : minY + (this.unitWidth + this.gutter) * 2
32363 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32367 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32370 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32375 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32376 y : minY + (this.unitWidth + this.gutter) * 1
32380 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32381 y : minY + (this.unitWidth + this.gutter) * 2
32388 if(box[0].size == 'xs' && box[1].size == 'xs'){
32391 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32396 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32401 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32402 y : minY + (this.unitWidth + this.gutter) * 1
32410 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32415 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32416 y : minY + (this.unitWidth + this.gutter) * 2
32420 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32421 y : minY + (this.unitWidth + this.gutter) * 2
32428 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32432 if(box[0].size == 'xs'){
32435 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32440 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32445 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),
32450 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32451 y : minY + (this.unitWidth + this.gutter) * 1
32459 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32464 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32465 y : minY + (this.unitWidth + this.gutter) * 2
32469 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32470 y : minY + (this.unitWidth + this.gutter) * 2
32474 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),
32475 y : minY + (this.unitWidth + this.gutter) * 2
32483 * remove a Masonry Brick
32484 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32486 removeBrick : function(brick_id)
32492 for (var i = 0; i<this.bricks.length; i++) {
32493 if (this.bricks[i].id == brick_id) {
32494 this.bricks.splice(i,1);
32495 this.el.dom.removeChild(Roo.get(brick_id).dom);
32502 * adds a Masonry Brick
32503 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32505 addBrick : function(cfg)
32507 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32508 //this.register(cn);
32509 cn.parentId = this.id;
32510 cn.render(this.el);
32515 * register a Masonry Brick
32516 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32519 register : function(brick)
32521 this.bricks.push(brick);
32522 brick.masonryId = this.id;
32526 * clear all the Masonry Brick
32528 clearAll : function()
32531 //this.getChildContainer().dom.innerHTML = "";
32532 this.el.dom.innerHTML = '';
32535 getSelected : function()
32537 if (!this.selectedBrick) {
32541 return this.selectedBrick;
32545 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32549 * register a Masonry Layout
32550 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32553 register : function(layout)
32555 this.groups[layout.id] = layout;
32558 * fetch a Masonry Layout based on the masonry layout ID
32559 * @param {string} the masonry layout to add
32560 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32563 get: function(layout_id) {
32564 if (typeof(this.groups[layout_id]) == 'undefined') {
32567 return this.groups[layout_id] ;
32579 * http://masonry.desandro.com
32581 * The idea is to render all the bricks based on vertical width...
32583 * The original code extends 'outlayer' - we might need to use that....
32589 * @class Roo.bootstrap.LayoutMasonryAuto
32590 * @extends Roo.bootstrap.Component
32591 * Bootstrap Layout Masonry class
32594 * Create a new Element
32595 * @param {Object} config The config object
32598 Roo.bootstrap.LayoutMasonryAuto = function(config){
32599 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32602 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32605 * @cfg {Boolean} isFitWidth - resize the width..
32607 isFitWidth : false, // options..
32609 * @cfg {Boolean} isOriginLeft = left align?
32611 isOriginLeft : true,
32613 * @cfg {Boolean} isOriginTop = top align?
32615 isOriginTop : false,
32617 * @cfg {Boolean} isLayoutInstant = no animation?
32619 isLayoutInstant : false, // needed?
32621 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32623 isResizingContainer : true,
32625 * @cfg {Number} columnWidth width of the columns
32631 * @cfg {Number} maxCols maximum number of columns
32636 * @cfg {Number} padHeight padding below box..
32642 * @cfg {Boolean} isAutoInitial defalut true
32645 isAutoInitial : true,
32651 initialColumnWidth : 0,
32652 currentSize : null,
32654 colYs : null, // array.
32661 bricks: null, //CompositeElement
32662 cols : 0, // array?
32663 // element : null, // wrapped now this.el
32664 _isLayoutInited : null,
32667 getAutoCreate : function(){
32671 cls: 'blog-masonary-wrapper ' + this.cls,
32673 cls : 'mas-boxes masonary'
32680 getChildContainer: function( )
32682 if (this.boxesEl) {
32683 return this.boxesEl;
32686 this.boxesEl = this.el.select('.mas-boxes').first();
32688 return this.boxesEl;
32692 initEvents : function()
32696 if(this.isAutoInitial){
32697 Roo.log('hook children rendered');
32698 this.on('childrenrendered', function() {
32699 Roo.log('children rendered');
32706 initial : function()
32708 this.reloadItems();
32710 this.currentSize = this.el.getBox(true);
32712 /// was window resize... - let's see if this works..
32713 Roo.EventManager.onWindowResize(this.resize, this);
32715 if(!this.isAutoInitial){
32720 this.layout.defer(500,this);
32723 reloadItems: function()
32725 this.bricks = this.el.select('.masonry-brick', true);
32727 this.bricks.each(function(b) {
32728 //Roo.log(b.getSize());
32729 if (!b.attr('originalwidth')) {
32730 b.attr('originalwidth', b.getSize().width);
32735 Roo.log(this.bricks.elements.length);
32738 resize : function()
32741 var cs = this.el.getBox(true);
32743 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32744 Roo.log("no change in with or X");
32747 this.currentSize = cs;
32751 layout : function()
32754 this._resetLayout();
32755 //this._manageStamps();
32757 // don't animate first layout
32758 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32759 this.layoutItems( isInstant );
32761 // flag for initalized
32762 this._isLayoutInited = true;
32765 layoutItems : function( isInstant )
32767 //var items = this._getItemsForLayout( this.items );
32768 // original code supports filtering layout items.. we just ignore it..
32770 this._layoutItems( this.bricks , isInstant );
32772 this._postLayout();
32774 _layoutItems : function ( items , isInstant)
32776 //this.fireEvent( 'layout', this, items );
32779 if ( !items || !items.elements.length ) {
32780 // no items, emit event with empty array
32785 items.each(function(item) {
32786 Roo.log("layout item");
32788 // get x/y object from method
32789 var position = this._getItemLayoutPosition( item );
32791 position.item = item;
32792 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32793 queue.push( position );
32796 this._processLayoutQueue( queue );
32798 /** Sets position of item in DOM
32799 * @param {Element} item
32800 * @param {Number} x - horizontal position
32801 * @param {Number} y - vertical position
32802 * @param {Boolean} isInstant - disables transitions
32804 _processLayoutQueue : function( queue )
32806 for ( var i=0, len = queue.length; i < len; i++ ) {
32807 var obj = queue[i];
32808 obj.item.position('absolute');
32809 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32815 * Any logic you want to do after each layout,
32816 * i.e. size the container
32818 _postLayout : function()
32820 this.resizeContainer();
32823 resizeContainer : function()
32825 if ( !this.isResizingContainer ) {
32828 var size = this._getContainerSize();
32830 this.el.setSize(size.width,size.height);
32831 this.boxesEl.setSize(size.width,size.height);
32837 _resetLayout : function()
32839 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32840 this.colWidth = this.el.getWidth();
32841 //this.gutter = this.el.getWidth();
32843 this.measureColumns();
32849 this.colYs.push( 0 );
32855 measureColumns : function()
32857 this.getContainerWidth();
32858 // if columnWidth is 0, default to outerWidth of first item
32859 if ( !this.columnWidth ) {
32860 var firstItem = this.bricks.first();
32861 Roo.log(firstItem);
32862 this.columnWidth = this.containerWidth;
32863 if (firstItem && firstItem.attr('originalwidth') ) {
32864 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32866 // columnWidth fall back to item of first element
32867 Roo.log("set column width?");
32868 this.initialColumnWidth = this.columnWidth ;
32870 // if first elem has no width, default to size of container
32875 if (this.initialColumnWidth) {
32876 this.columnWidth = this.initialColumnWidth;
32881 // column width is fixed at the top - however if container width get's smaller we should
32884 // this bit calcs how man columns..
32886 var columnWidth = this.columnWidth += this.gutter;
32888 // calculate columns
32889 var containerWidth = this.containerWidth + this.gutter;
32891 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32892 // fix rounding errors, typically with gutters
32893 var excess = columnWidth - containerWidth % columnWidth;
32896 // if overshoot is less than a pixel, round up, otherwise floor it
32897 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32898 cols = Math[ mathMethod ]( cols );
32899 this.cols = Math.max( cols, 1 );
32900 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32902 // padding positioning..
32903 var totalColWidth = this.cols * this.columnWidth;
32904 var padavail = this.containerWidth - totalColWidth;
32905 // so for 2 columns - we need 3 'pads'
32907 var padNeeded = (1+this.cols) * this.padWidth;
32909 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32911 this.columnWidth += padExtra
32912 //this.padWidth = Math.floor(padavail / ( this.cols));
32914 // adjust colum width so that padding is fixed??
32916 // we have 3 columns ... total = width * 3
32917 // we have X left over... that should be used by
32919 //if (this.expandC) {
32927 getContainerWidth : function()
32929 /* // container is parent if fit width
32930 var container = this.isFitWidth ? this.element.parentNode : this.element;
32931 // check that this.size and size are there
32932 // IE8 triggers resize on body size change, so they might not be
32934 var size = getSize( container ); //FIXME
32935 this.containerWidth = size && size.innerWidth; //FIXME
32938 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32942 _getItemLayoutPosition : function( item ) // what is item?
32944 // we resize the item to our columnWidth..
32946 item.setWidth(this.columnWidth);
32947 item.autoBoxAdjust = false;
32949 var sz = item.getSize();
32951 // how many columns does this brick span
32952 var remainder = this.containerWidth % this.columnWidth;
32954 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32955 // round if off by 1 pixel, otherwise use ceil
32956 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32957 colSpan = Math.min( colSpan, this.cols );
32959 // normally this should be '1' as we dont' currently allow multi width columns..
32961 var colGroup = this._getColGroup( colSpan );
32962 // get the minimum Y value from the columns
32963 var minimumY = Math.min.apply( Math, colGroup );
32964 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32966 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32968 // position the brick
32970 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32971 y: this.currentSize.y + minimumY + this.padHeight
32975 // apply setHeight to necessary columns
32976 var setHeight = minimumY + sz.height + this.padHeight;
32977 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32979 var setSpan = this.cols + 1 - colGroup.length;
32980 for ( var i = 0; i < setSpan; i++ ) {
32981 this.colYs[ shortColIndex + i ] = setHeight ;
32988 * @param {Number} colSpan - number of columns the element spans
32989 * @returns {Array} colGroup
32991 _getColGroup : function( colSpan )
32993 if ( colSpan < 2 ) {
32994 // if brick spans only one column, use all the column Ys
32999 // how many different places could this brick fit horizontally
33000 var groupCount = this.cols + 1 - colSpan;
33001 // for each group potential horizontal position
33002 for ( var i = 0; i < groupCount; i++ ) {
33003 // make an array of colY values for that one group
33004 var groupColYs = this.colYs.slice( i, i + colSpan );
33005 // and get the max value of the array
33006 colGroup[i] = Math.max.apply( Math, groupColYs );
33011 _manageStamp : function( stamp )
33013 var stampSize = stamp.getSize();
33014 var offset = stamp.getBox();
33015 // get the columns that this stamp affects
33016 var firstX = this.isOriginLeft ? offset.x : offset.right;
33017 var lastX = firstX + stampSize.width;
33018 var firstCol = Math.floor( firstX / this.columnWidth );
33019 firstCol = Math.max( 0, firstCol );
33021 var lastCol = Math.floor( lastX / this.columnWidth );
33022 // lastCol should not go over if multiple of columnWidth #425
33023 lastCol -= lastX % this.columnWidth ? 0 : 1;
33024 lastCol = Math.min( this.cols - 1, lastCol );
33026 // set colYs to bottom of the stamp
33027 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
33030 for ( var i = firstCol; i <= lastCol; i++ ) {
33031 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33036 _getContainerSize : function()
33038 this.maxY = Math.max.apply( Math, this.colYs );
33043 if ( this.isFitWidth ) {
33044 size.width = this._getContainerFitWidth();
33050 _getContainerFitWidth : function()
33052 var unusedCols = 0;
33053 // count unused columns
33056 if ( this.colYs[i] !== 0 ) {
33061 // fit container to columns that have been used
33062 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
33065 needsResizeLayout : function()
33067 var previousWidth = this.containerWidth;
33068 this.getContainerWidth();
33069 return previousWidth !== this.containerWidth;
33084 * @class Roo.bootstrap.MasonryBrick
33085 * @extends Roo.bootstrap.Component
33086 * Bootstrap MasonryBrick class
33089 * Create a new MasonryBrick
33090 * @param {Object} config The config object
33093 Roo.bootstrap.MasonryBrick = function(config){
33095 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
33097 Roo.bootstrap.MasonryBrick.register(this);
33103 * When a MasonryBrick is clcik
33104 * @param {Roo.bootstrap.MasonryBrick} this
33105 * @param {Roo.EventObject} e
33111 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
33114 * @cfg {String} title
33118 * @cfg {String} html
33122 * @cfg {String} bgimage
33126 * @cfg {String} videourl
33130 * @cfg {String} cls
33134 * @cfg {String} href
33138 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33143 * @cfg {String} placetitle (center|bottom)
33148 * @cfg {Boolean} isFitContainer defalut true
33150 isFitContainer : true,
33153 * @cfg {Boolean} preventDefault defalut false
33155 preventDefault : false,
33158 * @cfg {Boolean} inverse defalut false
33160 maskInverse : false,
33162 getAutoCreate : function()
33164 if(!this.isFitContainer){
33165 return this.getSplitAutoCreate();
33168 var cls = 'masonry-brick masonry-brick-full';
33170 if(this.href.length){
33171 cls += ' masonry-brick-link';
33174 if(this.bgimage.length){
33175 cls += ' masonry-brick-image';
33178 if(this.maskInverse){
33179 cls += ' mask-inverse';
33182 if(!this.html.length && !this.maskInverse && !this.videourl.length){
33183 cls += ' enable-mask';
33187 cls += ' masonry-' + this.size + '-brick';
33190 if(this.placetitle.length){
33192 switch (this.placetitle) {
33194 cls += ' masonry-center-title';
33197 cls += ' masonry-bottom-title';
33204 if(!this.html.length && !this.bgimage.length){
33205 cls += ' masonry-center-title';
33208 if(!this.html.length && this.bgimage.length){
33209 cls += ' masonry-bottom-title';
33214 cls += ' ' + this.cls;
33218 tag: (this.href.length) ? 'a' : 'div',
33223 cls: 'masonry-brick-mask'
33227 cls: 'masonry-brick-paragraph',
33233 if(this.href.length){
33234 cfg.href = this.href;
33237 var cn = cfg.cn[1].cn;
33239 if(this.title.length){
33242 cls: 'masonry-brick-title',
33247 if(this.html.length){
33250 cls: 'masonry-brick-text',
33255 if (!this.title.length && !this.html.length) {
33256 cfg.cn[1].cls += ' hide';
33259 if(this.bgimage.length){
33262 cls: 'masonry-brick-image-view',
33267 if(this.videourl.length){
33268 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33269 // youtube support only?
33272 cls: 'masonry-brick-image-view',
33275 allowfullscreen : true
33283 getSplitAutoCreate : function()
33285 var cls = 'masonry-brick masonry-brick-split';
33287 if(this.href.length){
33288 cls += ' masonry-brick-link';
33291 if(this.bgimage.length){
33292 cls += ' masonry-brick-image';
33296 cls += ' masonry-' + this.size + '-brick';
33299 switch (this.placetitle) {
33301 cls += ' masonry-center-title';
33304 cls += ' masonry-bottom-title';
33307 if(!this.bgimage.length){
33308 cls += ' masonry-center-title';
33311 if(this.bgimage.length){
33312 cls += ' masonry-bottom-title';
33318 cls += ' ' + this.cls;
33322 tag: (this.href.length) ? 'a' : 'div',
33327 cls: 'masonry-brick-split-head',
33331 cls: 'masonry-brick-paragraph',
33338 cls: 'masonry-brick-split-body',
33344 if(this.href.length){
33345 cfg.href = this.href;
33348 if(this.title.length){
33349 cfg.cn[0].cn[0].cn.push({
33351 cls: 'masonry-brick-title',
33356 if(this.html.length){
33357 cfg.cn[1].cn.push({
33359 cls: 'masonry-brick-text',
33364 if(this.bgimage.length){
33365 cfg.cn[0].cn.push({
33367 cls: 'masonry-brick-image-view',
33372 if(this.videourl.length){
33373 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33374 // youtube support only?
33375 cfg.cn[0].cn.cn.push({
33377 cls: 'masonry-brick-image-view',
33380 allowfullscreen : true
33387 initEvents: function()
33389 switch (this.size) {
33422 this.el.on('touchstart', this.onTouchStart, this);
33423 this.el.on('touchmove', this.onTouchMove, this);
33424 this.el.on('touchend', this.onTouchEnd, this);
33425 this.el.on('contextmenu', this.onContextMenu, this);
33427 this.el.on('mouseenter' ,this.enter, this);
33428 this.el.on('mouseleave', this.leave, this);
33429 this.el.on('click', this.onClick, this);
33432 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33433 this.parent().bricks.push(this);
33438 onClick: function(e, el)
33440 var time = this.endTimer - this.startTimer;
33441 // Roo.log(e.preventDefault());
33444 e.preventDefault();
33449 if(!this.preventDefault){
33453 e.preventDefault();
33455 if (this.activeClass != '') {
33456 this.selectBrick();
33459 this.fireEvent('click', this, e);
33462 enter: function(e, el)
33464 e.preventDefault();
33466 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33470 if(this.bgimage.length && this.html.length){
33471 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33475 leave: function(e, el)
33477 e.preventDefault();
33479 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33483 if(this.bgimage.length && this.html.length){
33484 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33488 onTouchStart: function(e, el)
33490 // e.preventDefault();
33492 this.touchmoved = false;
33494 if(!this.isFitContainer){
33498 if(!this.bgimage.length || !this.html.length){
33502 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33504 this.timer = new Date().getTime();
33508 onTouchMove: function(e, el)
33510 this.touchmoved = true;
33513 onContextMenu : function(e,el)
33515 e.preventDefault();
33516 e.stopPropagation();
33520 onTouchEnd: function(e, el)
33522 // e.preventDefault();
33524 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33531 if(!this.bgimage.length || !this.html.length){
33533 if(this.href.length){
33534 window.location.href = this.href;
33540 if(!this.isFitContainer){
33544 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33546 window.location.href = this.href;
33549 //selection on single brick only
33550 selectBrick : function() {
33552 if (!this.parentId) {
33556 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33557 var index = m.selectedBrick.indexOf(this.id);
33560 m.selectedBrick.splice(index,1);
33561 this.el.removeClass(this.activeClass);
33565 for(var i = 0; i < m.selectedBrick.length; i++) {
33566 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33567 b.el.removeClass(b.activeClass);
33570 m.selectedBrick = [];
33572 m.selectedBrick.push(this.id);
33573 this.el.addClass(this.activeClass);
33577 isSelected : function(){
33578 return this.el.hasClass(this.activeClass);
33583 Roo.apply(Roo.bootstrap.MasonryBrick, {
33586 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33588 * register a Masonry Brick
33589 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33592 register : function(brick)
33594 //this.groups[brick.id] = brick;
33595 this.groups.add(brick.id, brick);
33598 * fetch a masonry brick based on the masonry brick ID
33599 * @param {string} the masonry brick to add
33600 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33603 get: function(brick_id)
33605 // if (typeof(this.groups[brick_id]) == 'undefined') {
33608 // return this.groups[brick_id] ;
33610 if(this.groups.key(brick_id)) {
33611 return this.groups.key(brick_id);
33629 * @class Roo.bootstrap.Brick
33630 * @extends Roo.bootstrap.Component
33631 * Bootstrap Brick class
33634 * Create a new Brick
33635 * @param {Object} config The config object
33638 Roo.bootstrap.Brick = function(config){
33639 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33645 * When a Brick is click
33646 * @param {Roo.bootstrap.Brick} this
33647 * @param {Roo.EventObject} e
33653 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33656 * @cfg {String} title
33660 * @cfg {String} html
33664 * @cfg {String} bgimage
33668 * @cfg {String} cls
33672 * @cfg {String} href
33676 * @cfg {String} video
33680 * @cfg {Boolean} square
33684 getAutoCreate : function()
33686 var cls = 'roo-brick';
33688 if(this.href.length){
33689 cls += ' roo-brick-link';
33692 if(this.bgimage.length){
33693 cls += ' roo-brick-image';
33696 if(!this.html.length && !this.bgimage.length){
33697 cls += ' roo-brick-center-title';
33700 if(!this.html.length && this.bgimage.length){
33701 cls += ' roo-brick-bottom-title';
33705 cls += ' ' + this.cls;
33709 tag: (this.href.length) ? 'a' : 'div',
33714 cls: 'roo-brick-paragraph',
33720 if(this.href.length){
33721 cfg.href = this.href;
33724 var cn = cfg.cn[0].cn;
33726 if(this.title.length){
33729 cls: 'roo-brick-title',
33734 if(this.html.length){
33737 cls: 'roo-brick-text',
33744 if(this.bgimage.length){
33747 cls: 'roo-brick-image-view',
33755 initEvents: function()
33757 if(this.title.length || this.html.length){
33758 this.el.on('mouseenter' ,this.enter, this);
33759 this.el.on('mouseleave', this.leave, this);
33762 Roo.EventManager.onWindowResize(this.resize, this);
33764 if(this.bgimage.length){
33765 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33766 this.imageEl.on('load', this.onImageLoad, this);
33773 onImageLoad : function()
33778 resize : function()
33780 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33782 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33784 if(this.bgimage.length){
33785 var image = this.el.select('.roo-brick-image-view', true).first();
33787 image.setWidth(paragraph.getWidth());
33790 image.setHeight(paragraph.getWidth());
33793 this.el.setHeight(image.getHeight());
33794 paragraph.setHeight(image.getHeight());
33800 enter: function(e, el)
33802 e.preventDefault();
33804 if(this.bgimage.length){
33805 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33806 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33810 leave: function(e, el)
33812 e.preventDefault();
33814 if(this.bgimage.length){
33815 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33816 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33831 * @class Roo.bootstrap.NumberField
33832 * @extends Roo.bootstrap.Input
33833 * Bootstrap NumberField class
33839 * Create a new NumberField
33840 * @param {Object} config The config object
33843 Roo.bootstrap.NumberField = function(config){
33844 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33847 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33850 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33852 allowDecimals : true,
33854 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33856 decimalSeparator : ".",
33858 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33860 decimalPrecision : 2,
33862 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33864 allowNegative : true,
33867 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33871 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33873 minValue : Number.NEGATIVE_INFINITY,
33875 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33877 maxValue : Number.MAX_VALUE,
33879 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33881 minText : "The minimum value for this field is {0}",
33883 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33885 maxText : "The maximum value for this field is {0}",
33887 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33888 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33890 nanText : "{0} is not a valid number",
33892 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33894 thousandsDelimiter : false,
33896 * @cfg {String} valueAlign alignment of value
33898 valueAlign : "left",
33900 getAutoCreate : function()
33902 var hiddenInput = {
33906 cls: 'hidden-number-input'
33910 hiddenInput.name = this.name;
33915 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33917 this.name = hiddenInput.name;
33919 if(cfg.cn.length > 0) {
33920 cfg.cn.push(hiddenInput);
33927 initEvents : function()
33929 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33931 var allowed = "0123456789";
33933 if(this.allowDecimals){
33934 allowed += this.decimalSeparator;
33937 if(this.allowNegative){
33941 if(this.thousandsDelimiter) {
33945 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33947 var keyPress = function(e){
33949 var k = e.getKey();
33951 var c = e.getCharCode();
33954 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33955 allowed.indexOf(String.fromCharCode(c)) === -1
33961 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33965 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33970 this.el.on("keypress", keyPress, this);
33973 validateValue : function(value)
33976 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33980 var num = this.parseValue(value);
33983 this.markInvalid(String.format(this.nanText, value));
33987 if(num < this.minValue){
33988 this.markInvalid(String.format(this.minText, this.minValue));
33992 if(num > this.maxValue){
33993 this.markInvalid(String.format(this.maxText, this.maxValue));
34000 getValue : function()
34002 var v = this.hiddenEl().getValue();
34004 return this.fixPrecision(this.parseValue(v));
34007 parseValue : function(value)
34009 if(this.thousandsDelimiter) {
34011 r = new RegExp(",", "g");
34012 value = value.replace(r, "");
34015 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
34016 return isNaN(value) ? '' : value;
34019 fixPrecision : function(value)
34021 if(this.thousandsDelimiter) {
34023 r = new RegExp(",", "g");
34024 value = value.replace(r, "");
34027 var nan = isNaN(value);
34029 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
34030 return nan ? '' : value;
34032 return parseFloat(value).toFixed(this.decimalPrecision);
34035 setValue : function(v)
34037 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34043 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34045 this.inputEl().dom.value = (v == '') ? '' :
34046 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34048 if(!this.allowZero && v === '0') {
34049 this.hiddenEl().dom.value = '';
34050 this.inputEl().dom.value = '';
34057 decimalPrecisionFcn : function(v)
34059 return Math.floor(v);
34062 beforeBlur : function()
34064 var v = this.parseValue(this.getRawValue());
34066 if(v || v === 0 || v === ''){
34071 hiddenEl : function()
34073 return this.el.select('input.hidden-number-input',true).first();
34085 * @class Roo.bootstrap.DocumentSlider
34086 * @extends Roo.bootstrap.Component
34087 * Bootstrap DocumentSlider class
34090 * Create a new DocumentViewer
34091 * @param {Object} config The config object
34094 Roo.bootstrap.DocumentSlider = function(config){
34095 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
34102 * Fire after initEvent
34103 * @param {Roo.bootstrap.DocumentSlider} this
34108 * Fire after update
34109 * @param {Roo.bootstrap.DocumentSlider} this
34115 * @param {Roo.bootstrap.DocumentSlider} this
34121 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
34127 getAutoCreate : function()
34131 cls : 'roo-document-slider',
34135 cls : 'roo-document-slider-header',
34139 cls : 'roo-document-slider-header-title'
34145 cls : 'roo-document-slider-body',
34149 cls : 'roo-document-slider-prev',
34153 cls : 'fa fa-chevron-left'
34159 cls : 'roo-document-slider-thumb',
34163 cls : 'roo-document-slider-image'
34169 cls : 'roo-document-slider-next',
34173 cls : 'fa fa-chevron-right'
34185 initEvents : function()
34187 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34188 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34190 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34191 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34193 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34194 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34196 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34197 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34199 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34200 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34202 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34203 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34205 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34206 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34208 this.thumbEl.on('click', this.onClick, this);
34210 this.prevIndicator.on('click', this.prev, this);
34212 this.nextIndicator.on('click', this.next, this);
34216 initial : function()
34218 if(this.files.length){
34219 this.indicator = 1;
34223 this.fireEvent('initial', this);
34226 update : function()
34228 this.imageEl.attr('src', this.files[this.indicator - 1]);
34230 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34232 this.prevIndicator.show();
34234 if(this.indicator == 1){
34235 this.prevIndicator.hide();
34238 this.nextIndicator.show();
34240 if(this.indicator == this.files.length){
34241 this.nextIndicator.hide();
34244 this.thumbEl.scrollTo('top');
34246 this.fireEvent('update', this);
34249 onClick : function(e)
34251 e.preventDefault();
34253 this.fireEvent('click', this);
34258 e.preventDefault();
34260 this.indicator = Math.max(1, this.indicator - 1);
34267 e.preventDefault();
34269 this.indicator = Math.min(this.files.length, this.indicator + 1);
34283 * @class Roo.bootstrap.RadioSet
34284 * @extends Roo.bootstrap.Input
34285 * Bootstrap RadioSet class
34286 * @cfg {String} indicatorpos (left|right) default left
34287 * @cfg {Boolean} inline (true|false) inline the element (default true)
34288 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34290 * Create a new RadioSet
34291 * @param {Object} config The config object
34294 Roo.bootstrap.RadioSet = function(config){
34296 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34300 Roo.bootstrap.RadioSet.register(this);
34305 * Fires when the element is checked or unchecked.
34306 * @param {Roo.bootstrap.RadioSet} this This radio
34307 * @param {Roo.bootstrap.Radio} item The checked item
34312 * Fires when the element is click.
34313 * @param {Roo.bootstrap.RadioSet} this This radio set
34314 * @param {Roo.bootstrap.Radio} item The checked item
34315 * @param {Roo.EventObject} e The event object
34322 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
34330 indicatorpos : 'left',
34332 getAutoCreate : function()
34336 cls : 'roo-radio-set-label',
34340 html : this.fieldLabel
34344 if (Roo.bootstrap.version == 3) {
34347 if(this.indicatorpos == 'left'){
34350 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34351 tooltip : 'This field is required'
34356 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34357 tooltip : 'This field is required'
34363 cls : 'roo-radio-set-items'
34366 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34368 if (align === 'left' && this.fieldLabel.length) {
34371 cls : "roo-radio-set-right",
34377 if(this.labelWidth > 12){
34378 label.style = "width: " + this.labelWidth + 'px';
34381 if(this.labelWidth < 13 && this.labelmd == 0){
34382 this.labelmd = this.labelWidth;
34385 if(this.labellg > 0){
34386 label.cls += ' col-lg-' + this.labellg;
34387 items.cls += ' col-lg-' + (12 - this.labellg);
34390 if(this.labelmd > 0){
34391 label.cls += ' col-md-' + this.labelmd;
34392 items.cls += ' col-md-' + (12 - this.labelmd);
34395 if(this.labelsm > 0){
34396 label.cls += ' col-sm-' + this.labelsm;
34397 items.cls += ' col-sm-' + (12 - this.labelsm);
34400 if(this.labelxs > 0){
34401 label.cls += ' col-xs-' + this.labelxs;
34402 items.cls += ' col-xs-' + (12 - this.labelxs);
34408 cls : 'roo-radio-set',
34412 cls : 'roo-radio-set-input',
34415 value : this.value ? this.value : ''
34422 if(this.weight.length){
34423 cfg.cls += ' roo-radio-' + this.weight;
34427 cfg.cls += ' roo-radio-set-inline';
34431 ['xs','sm','md','lg'].map(function(size){
34432 if (settings[size]) {
34433 cfg.cls += ' col-' + size + '-' + settings[size];
34441 initEvents : function()
34443 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34444 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34446 if(!this.fieldLabel.length){
34447 this.labelEl.hide();
34450 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34451 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34453 this.indicator = this.indicatorEl();
34455 if(this.indicator){
34456 this.indicator.addClass('invisible');
34459 this.originalValue = this.getValue();
34463 inputEl: function ()
34465 return this.el.select('.roo-radio-set-input', true).first();
34468 getChildContainer : function()
34470 return this.itemsEl;
34473 register : function(item)
34475 this.radioes.push(item);
34479 validate : function()
34481 if(this.getVisibilityEl().hasClass('hidden')){
34487 Roo.each(this.radioes, function(i){
34496 if(this.allowBlank) {
34500 if(this.disabled || valid){
34505 this.markInvalid();
34510 markValid : function()
34512 if(this.labelEl.isVisible(true) && this.indicatorEl()){
34513 this.indicatorEl().removeClass('visible');
34514 this.indicatorEl().addClass('invisible');
34518 if (Roo.bootstrap.version == 3) {
34519 this.el.removeClass([this.invalidClass, this.validClass]);
34520 this.el.addClass(this.validClass);
34522 this.el.removeClass(['is-invalid','is-valid']);
34523 this.el.addClass(['is-valid']);
34525 this.fireEvent('valid', this);
34528 markInvalid : function(msg)
34530 if(this.allowBlank || this.disabled){
34534 if(this.labelEl.isVisible(true) && this.indicatorEl()){
34535 this.indicatorEl().removeClass('invisible');
34536 this.indicatorEl().addClass('visible');
34538 if (Roo.bootstrap.version == 3) {
34539 this.el.removeClass([this.invalidClass, this.validClass]);
34540 this.el.addClass(this.invalidClass);
34542 this.el.removeClass(['is-invalid','is-valid']);
34543 this.el.addClass(['is-invalid']);
34546 this.fireEvent('invalid', this, msg);
34550 setValue : function(v, suppressEvent)
34552 if(this.value === v){
34559 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34562 Roo.each(this.radioes, function(i){
34564 i.el.removeClass('checked');
34567 Roo.each(this.radioes, function(i){
34569 if(i.value === v || i.value.toString() === v.toString()){
34571 i.el.addClass('checked');
34573 if(suppressEvent !== true){
34574 this.fireEvent('check', this, i);
34585 clearInvalid : function(){
34587 if(!this.el || this.preventMark){
34591 this.el.removeClass([this.invalidClass]);
34593 this.fireEvent('valid', this);
34598 Roo.apply(Roo.bootstrap.RadioSet, {
34602 register : function(set)
34604 this.groups[set.name] = set;
34607 get: function(name)
34609 if (typeof(this.groups[name]) == 'undefined') {
34613 return this.groups[name] ;
34619 * Ext JS Library 1.1.1
34620 * Copyright(c) 2006-2007, Ext JS, LLC.
34622 * Originally Released Under LGPL - original licence link has changed is not relivant.
34625 * <script type="text/javascript">
34630 * @class Roo.bootstrap.SplitBar
34631 * @extends Roo.util.Observable
34632 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34636 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34637 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34638 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34639 split.minSize = 100;
34640 split.maxSize = 600;
34641 split.animate = true;
34642 split.on('moved', splitterMoved);
34645 * Create a new SplitBar
34646 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34647 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34648 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34649 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34650 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34651 position of the SplitBar).
34653 Roo.bootstrap.SplitBar = function(cfg){
34658 // dragElement : elm
34659 // resizingElement: el,
34661 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34662 // placement : Roo.bootstrap.SplitBar.LEFT ,
34663 // existingProxy ???
34666 this.el = Roo.get(cfg.dragElement, true);
34667 this.el.dom.unselectable = "on";
34669 this.resizingEl = Roo.get(cfg.resizingElement, true);
34673 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34674 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34677 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34680 * The minimum size of the resizing element. (Defaults to 0)
34686 * The maximum size of the resizing element. (Defaults to 2000)
34689 this.maxSize = 2000;
34692 * Whether to animate the transition to the new size
34695 this.animate = false;
34698 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34701 this.useShim = false;
34706 if(!cfg.existingProxy){
34708 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34710 this.proxy = Roo.get(cfg.existingProxy).dom;
34713 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34716 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34719 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34722 this.dragSpecs = {};
34725 * @private The adapter to use to positon and resize elements
34727 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34728 this.adapter.init(this);
34730 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34732 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34733 this.el.addClass("roo-splitbar-h");
34736 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34737 this.el.addClass("roo-splitbar-v");
34743 * Fires when the splitter is moved (alias for {@link #event-moved})
34744 * @param {Roo.bootstrap.SplitBar} this
34745 * @param {Number} newSize the new width or height
34750 * Fires when the splitter is moved
34751 * @param {Roo.bootstrap.SplitBar} this
34752 * @param {Number} newSize the new width or height
34756 * @event beforeresize
34757 * Fires before the splitter is dragged
34758 * @param {Roo.bootstrap.SplitBar} this
34760 "beforeresize" : true,
34762 "beforeapply" : true
34765 Roo.util.Observable.call(this);
34768 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34769 onStartProxyDrag : function(x, y){
34770 this.fireEvent("beforeresize", this);
34772 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34774 o.enableDisplayMode("block");
34775 // all splitbars share the same overlay
34776 Roo.bootstrap.SplitBar.prototype.overlay = o;
34778 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34779 this.overlay.show();
34780 Roo.get(this.proxy).setDisplayed("block");
34781 var size = this.adapter.getElementSize(this);
34782 this.activeMinSize = this.getMinimumSize();;
34783 this.activeMaxSize = this.getMaximumSize();;
34784 var c1 = size - this.activeMinSize;
34785 var c2 = Math.max(this.activeMaxSize - size, 0);
34786 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34787 this.dd.resetConstraints();
34788 this.dd.setXConstraint(
34789 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34790 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34792 this.dd.setYConstraint(0, 0);
34794 this.dd.resetConstraints();
34795 this.dd.setXConstraint(0, 0);
34796 this.dd.setYConstraint(
34797 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34798 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34801 this.dragSpecs.startSize = size;
34802 this.dragSpecs.startPoint = [x, y];
34803 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34807 * @private Called after the drag operation by the DDProxy
34809 onEndProxyDrag : function(e){
34810 Roo.get(this.proxy).setDisplayed(false);
34811 var endPoint = Roo.lib.Event.getXY(e);
34813 this.overlay.hide();
34816 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34817 newSize = this.dragSpecs.startSize +
34818 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34819 endPoint[0] - this.dragSpecs.startPoint[0] :
34820 this.dragSpecs.startPoint[0] - endPoint[0]
34823 newSize = this.dragSpecs.startSize +
34824 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34825 endPoint[1] - this.dragSpecs.startPoint[1] :
34826 this.dragSpecs.startPoint[1] - endPoint[1]
34829 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34830 if(newSize != this.dragSpecs.startSize){
34831 if(this.fireEvent('beforeapply', this, newSize) !== false){
34832 this.adapter.setElementSize(this, newSize);
34833 this.fireEvent("moved", this, newSize);
34834 this.fireEvent("resize", this, newSize);
34840 * Get the adapter this SplitBar uses
34841 * @return The adapter object
34843 getAdapter : function(){
34844 return this.adapter;
34848 * Set the adapter this SplitBar uses
34849 * @param {Object} adapter A SplitBar adapter object
34851 setAdapter : function(adapter){
34852 this.adapter = adapter;
34853 this.adapter.init(this);
34857 * Gets the minimum size for the resizing element
34858 * @return {Number} The minimum size
34860 getMinimumSize : function(){
34861 return this.minSize;
34865 * Sets the minimum size for the resizing element
34866 * @param {Number} minSize The minimum size
34868 setMinimumSize : function(minSize){
34869 this.minSize = minSize;
34873 * Gets the maximum size for the resizing element
34874 * @return {Number} The maximum size
34876 getMaximumSize : function(){
34877 return this.maxSize;
34881 * Sets the maximum size for the resizing element
34882 * @param {Number} maxSize The maximum size
34884 setMaximumSize : function(maxSize){
34885 this.maxSize = maxSize;
34889 * Sets the initialize size for the resizing element
34890 * @param {Number} size The initial size
34892 setCurrentSize : function(size){
34893 var oldAnimate = this.animate;
34894 this.animate = false;
34895 this.adapter.setElementSize(this, size);
34896 this.animate = oldAnimate;
34900 * Destroy this splitbar.
34901 * @param {Boolean} removeEl True to remove the element
34903 destroy : function(removeEl){
34905 this.shim.remove();
34908 this.proxy.parentNode.removeChild(this.proxy);
34916 * @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.
34918 Roo.bootstrap.SplitBar.createProxy = function(dir){
34919 var proxy = new Roo.Element(document.createElement("div"));
34920 proxy.unselectable();
34921 var cls = 'roo-splitbar-proxy';
34922 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34923 document.body.appendChild(proxy.dom);
34928 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34929 * Default Adapter. It assumes the splitter and resizing element are not positioned
34930 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34932 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34935 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34936 // do nothing for now
34937 init : function(s){
34941 * Called before drag operations to get the current size of the resizing element.
34942 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34944 getElementSize : function(s){
34945 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34946 return s.resizingEl.getWidth();
34948 return s.resizingEl.getHeight();
34953 * Called after drag operations to set the size of the resizing element.
34954 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34955 * @param {Number} newSize The new size to set
34956 * @param {Function} onComplete A function to be invoked when resizing is complete
34958 setElementSize : function(s, newSize, onComplete){
34959 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34961 s.resizingEl.setWidth(newSize);
34963 onComplete(s, newSize);
34966 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34971 s.resizingEl.setHeight(newSize);
34973 onComplete(s, newSize);
34976 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34983 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34984 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34985 * Adapter that moves the splitter element to align with the resized sizing element.
34986 * Used with an absolute positioned SplitBar.
34987 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34988 * document.body, make sure you assign an id to the body element.
34990 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34991 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34992 this.container = Roo.get(container);
34995 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34996 init : function(s){
34997 this.basic.init(s);
35000 getElementSize : function(s){
35001 return this.basic.getElementSize(s);
35004 setElementSize : function(s, newSize, onComplete){
35005 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
35008 moveSplitter : function(s){
35009 var yes = Roo.bootstrap.SplitBar;
35010 switch(s.placement){
35012 s.el.setX(s.resizingEl.getRight());
35015 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
35018 s.el.setY(s.resizingEl.getBottom());
35021 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
35028 * Orientation constant - Create a vertical SplitBar
35032 Roo.bootstrap.SplitBar.VERTICAL = 1;
35035 * Orientation constant - Create a horizontal SplitBar
35039 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35042 * Placement constant - The resizing element is to the left of the splitter element
35046 Roo.bootstrap.SplitBar.LEFT = 1;
35049 * Placement constant - The resizing element is to the right of the splitter element
35053 Roo.bootstrap.SplitBar.RIGHT = 2;
35056 * Placement constant - The resizing element is positioned above the splitter element
35060 Roo.bootstrap.SplitBar.TOP = 3;
35063 * Placement constant - The resizing element is positioned under splitter element
35067 Roo.bootstrap.SplitBar.BOTTOM = 4;
35068 Roo.namespace("Roo.bootstrap.layout");/*
35070 * Ext JS Library 1.1.1
35071 * Copyright(c) 2006-2007, Ext JS, LLC.
35073 * Originally Released Under LGPL - original licence link has changed is not relivant.
35076 * <script type="text/javascript">
35080 * @class Roo.bootstrap.layout.Manager
35081 * @extends Roo.bootstrap.Component
35082 * Base class for layout managers.
35084 Roo.bootstrap.layout.Manager = function(config)
35086 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
35092 /** false to disable window resize monitoring @type Boolean */
35093 this.monitorWindowResize = true;
35098 * Fires when a layout is performed.
35099 * @param {Roo.LayoutManager} this
35103 * @event regionresized
35104 * Fires when the user resizes a region.
35105 * @param {Roo.LayoutRegion} region The resized region
35106 * @param {Number} newSize The new size (width for east/west, height for north/south)
35108 "regionresized" : true,
35110 * @event regioncollapsed
35111 * Fires when a region is collapsed.
35112 * @param {Roo.LayoutRegion} region The collapsed region
35114 "regioncollapsed" : true,
35116 * @event regionexpanded
35117 * Fires when a region is expanded.
35118 * @param {Roo.LayoutRegion} region The expanded region
35120 "regionexpanded" : true
35122 this.updating = false;
35125 this.el = Roo.get(config.el);
35131 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35136 monitorWindowResize : true,
35142 onRender : function(ct, position)
35145 this.el = Roo.get(ct);
35148 //this.fireEvent('render',this);
35152 initEvents: function()
35156 // ie scrollbar fix
35157 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35158 document.body.scroll = "no";
35159 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35160 this.el.position('relative');
35162 this.id = this.el.id;
35163 this.el.addClass("roo-layout-container");
35164 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35165 if(this.el.dom != document.body ) {
35166 this.el.on('resize', this.layout,this);
35167 this.el.on('show', this.layout,this);
35173 * Returns true if this layout is currently being updated
35174 * @return {Boolean}
35176 isUpdating : function(){
35177 return this.updating;
35181 * Suspend the LayoutManager from doing auto-layouts while
35182 * making multiple add or remove calls
35184 beginUpdate : function(){
35185 this.updating = true;
35189 * Restore auto-layouts and optionally disable the manager from performing a layout
35190 * @param {Boolean} noLayout true to disable a layout update
35192 endUpdate : function(noLayout){
35193 this.updating = false;
35199 layout: function(){
35203 onRegionResized : function(region, newSize){
35204 this.fireEvent("regionresized", region, newSize);
35208 onRegionCollapsed : function(region){
35209 this.fireEvent("regioncollapsed", region);
35212 onRegionExpanded : function(region){
35213 this.fireEvent("regionexpanded", region);
35217 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35218 * performs box-model adjustments.
35219 * @return {Object} The size as an object {width: (the width), height: (the height)}
35221 getViewSize : function()
35224 if(this.el.dom != document.body){
35225 size = this.el.getSize();
35227 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35229 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35230 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35235 * Returns the Element this layout is bound to.
35236 * @return {Roo.Element}
35238 getEl : function(){
35243 * Returns the specified region.
35244 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35245 * @return {Roo.LayoutRegion}
35247 getRegion : function(target){
35248 return this.regions[target.toLowerCase()];
35251 onWindowResize : function(){
35252 if(this.monitorWindowResize){
35259 * Ext JS Library 1.1.1
35260 * Copyright(c) 2006-2007, Ext JS, LLC.
35262 * Originally Released Under LGPL - original licence link has changed is not relivant.
35265 * <script type="text/javascript">
35268 * @class Roo.bootstrap.layout.Border
35269 * @extends Roo.bootstrap.layout.Manager
35270 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35271 * please see: examples/bootstrap/nested.html<br><br>
35273 <b>The container the layout is rendered into can be either the body element or any other element.
35274 If it is not the body element, the container needs to either be an absolute positioned element,
35275 or you will need to add "position:relative" to the css of the container. You will also need to specify
35276 the container size if it is not the body element.</b>
35279 * Create a new Border
35280 * @param {Object} config Configuration options
35282 Roo.bootstrap.layout.Border = function(config){
35283 config = config || {};
35284 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35288 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35289 if(config[region]){
35290 config[region].region = region;
35291 this.addRegion(config[region]);
35297 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
35299 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35301 parent : false, // this might point to a 'nest' or a ???
35304 * Creates and adds a new region if it doesn't already exist.
35305 * @param {String} target The target region key (north, south, east, west or center).
35306 * @param {Object} config The regions config object
35307 * @return {BorderLayoutRegion} The new region
35309 addRegion : function(config)
35311 if(!this.regions[config.region]){
35312 var r = this.factory(config);
35313 this.bindRegion(r);
35315 return this.regions[config.region];
35319 bindRegion : function(r){
35320 this.regions[r.config.region] = r;
35322 r.on("visibilitychange", this.layout, this);
35323 r.on("paneladded", this.layout, this);
35324 r.on("panelremoved", this.layout, this);
35325 r.on("invalidated", this.layout, this);
35326 r.on("resized", this.onRegionResized, this);
35327 r.on("collapsed", this.onRegionCollapsed, this);
35328 r.on("expanded", this.onRegionExpanded, this);
35332 * Performs a layout update.
35334 layout : function()
35336 if(this.updating) {
35340 // render all the rebions if they have not been done alreayd?
35341 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35342 if(this.regions[region] && !this.regions[region].bodyEl){
35343 this.regions[region].onRender(this.el)
35347 var size = this.getViewSize();
35348 var w = size.width;
35349 var h = size.height;
35354 //var x = 0, y = 0;
35356 var rs = this.regions;
35357 var north = rs["north"];
35358 var south = rs["south"];
35359 var west = rs["west"];
35360 var east = rs["east"];
35361 var center = rs["center"];
35362 //if(this.hideOnLayout){ // not supported anymore
35363 //c.el.setStyle("display", "none");
35365 if(north && north.isVisible()){
35366 var b = north.getBox();
35367 var m = north.getMargins();
35368 b.width = w - (m.left+m.right);
35371 centerY = b.height + b.y + m.bottom;
35372 centerH -= centerY;
35373 north.updateBox(this.safeBox(b));
35375 if(south && south.isVisible()){
35376 var b = south.getBox();
35377 var m = south.getMargins();
35378 b.width = w - (m.left+m.right);
35380 var totalHeight = (b.height + m.top + m.bottom);
35381 b.y = h - totalHeight + m.top;
35382 centerH -= totalHeight;
35383 south.updateBox(this.safeBox(b));
35385 if(west && west.isVisible()){
35386 var b = west.getBox();
35387 var m = west.getMargins();
35388 b.height = centerH - (m.top+m.bottom);
35390 b.y = centerY + m.top;
35391 var totalWidth = (b.width + m.left + m.right);
35392 centerX += totalWidth;
35393 centerW -= totalWidth;
35394 west.updateBox(this.safeBox(b));
35396 if(east && east.isVisible()){
35397 var b = east.getBox();
35398 var m = east.getMargins();
35399 b.height = centerH - (m.top+m.bottom);
35400 var totalWidth = (b.width + m.left + m.right);
35401 b.x = w - totalWidth + m.left;
35402 b.y = centerY + m.top;
35403 centerW -= totalWidth;
35404 east.updateBox(this.safeBox(b));
35407 var m = center.getMargins();
35409 x: centerX + m.left,
35410 y: centerY + m.top,
35411 width: centerW - (m.left+m.right),
35412 height: centerH - (m.top+m.bottom)
35414 //if(this.hideOnLayout){
35415 //center.el.setStyle("display", "block");
35417 center.updateBox(this.safeBox(centerBox));
35420 this.fireEvent("layout", this);
35424 safeBox : function(box){
35425 box.width = Math.max(0, box.width);
35426 box.height = Math.max(0, box.height);
35431 * Adds a ContentPanel (or subclass) to this layout.
35432 * @param {String} target The target region key (north, south, east, west or center).
35433 * @param {Roo.ContentPanel} panel The panel to add
35434 * @return {Roo.ContentPanel} The added panel
35436 add : function(target, panel){
35438 target = target.toLowerCase();
35439 return this.regions[target].add(panel);
35443 * Remove a ContentPanel (or subclass) to this layout.
35444 * @param {String} target The target region key (north, south, east, west or center).
35445 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35446 * @return {Roo.ContentPanel} The removed panel
35448 remove : function(target, panel){
35449 target = target.toLowerCase();
35450 return this.regions[target].remove(panel);
35454 * Searches all regions for a panel with the specified id
35455 * @param {String} panelId
35456 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35458 findPanel : function(panelId){
35459 var rs = this.regions;
35460 for(var target in rs){
35461 if(typeof rs[target] != "function"){
35462 var p = rs[target].getPanel(panelId);
35472 * Searches all regions for a panel with the specified id and activates (shows) it.
35473 * @param {String/ContentPanel} panelId The panels id or the panel itself
35474 * @return {Roo.ContentPanel} The shown panel or null
35476 showPanel : function(panelId) {
35477 var rs = this.regions;
35478 for(var target in rs){
35479 var r = rs[target];
35480 if(typeof r != "function"){
35481 if(r.hasPanel(panelId)){
35482 return r.showPanel(panelId);
35490 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35491 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35494 restoreState : function(provider){
35496 provider = Roo.state.Manager;
35498 var sm = new Roo.LayoutStateManager();
35499 sm.init(this, provider);
35505 * Adds a xtype elements to the layout.
35509 xtype : 'ContentPanel',
35516 xtype : 'NestedLayoutPanel',
35522 items : [ ... list of content panels or nested layout panels.. ]
35526 * @param {Object} cfg Xtype definition of item to add.
35528 addxtype : function(cfg)
35530 // basically accepts a pannel...
35531 // can accept a layout region..!?!?
35532 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35535 // theory? children can only be panels??
35537 //if (!cfg.xtype.match(/Panel$/)) {
35542 if (typeof(cfg.region) == 'undefined') {
35543 Roo.log("Failed to add Panel, region was not set");
35547 var region = cfg.region;
35553 xitems = cfg.items;
35558 if ( region == 'center') {
35559 Roo.log("Center: " + cfg.title);
35565 case 'Content': // ContentPanel (el, cfg)
35566 case 'Scroll': // ContentPanel (el, cfg)
35568 cfg.autoCreate = cfg.autoCreate || true;
35569 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35571 // var el = this.el.createChild();
35572 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35575 this.add(region, ret);
35579 case 'TreePanel': // our new panel!
35580 cfg.el = this.el.createChild();
35581 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35582 this.add(region, ret);
35587 // create a new Layout (which is a Border Layout...
35589 var clayout = cfg.layout;
35590 clayout.el = this.el.createChild();
35591 clayout.items = clayout.items || [];
35595 // replace this exitems with the clayout ones..
35596 xitems = clayout.items;
35598 // force background off if it's in center...
35599 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35600 cfg.background = false;
35602 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35605 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35606 //console.log('adding nested layout panel ' + cfg.toSource());
35607 this.add(region, ret);
35608 nb = {}; /// find first...
35613 // needs grid and region
35615 //var el = this.getRegion(region).el.createChild();
35617 *var el = this.el.createChild();
35618 // create the grid first...
35619 cfg.grid.container = el;
35620 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35623 if (region == 'center' && this.active ) {
35624 cfg.background = false;
35627 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35629 this.add(region, ret);
35631 if (cfg.background) {
35632 // render grid on panel activation (if panel background)
35633 ret.on('activate', function(gp) {
35634 if (!gp.grid.rendered) {
35635 // gp.grid.render(el);
35639 // cfg.grid.render(el);
35645 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35646 // it was the old xcomponent building that caused this before.
35647 // espeically if border is the top element in the tree.
35657 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35659 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35660 this.add(region, ret);
35664 throw "Can not add '" + cfg.xtype + "' to Border";
35670 this.beginUpdate();
35674 Roo.each(xitems, function(i) {
35675 region = nb && i.region ? i.region : false;
35677 var add = ret.addxtype(i);
35680 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35681 if (!i.background) {
35682 abn[region] = nb[region] ;
35689 // make the last non-background panel active..
35690 //if (nb) { Roo.log(abn); }
35693 for(var r in abn) {
35694 region = this.getRegion(r);
35696 // tried using nb[r], but it does not work..
35698 region.showPanel(abn[r]);
35709 factory : function(cfg)
35712 var validRegions = Roo.bootstrap.layout.Border.regions;
35714 var target = cfg.region;
35717 var r = Roo.bootstrap.layout;
35721 return new r.North(cfg);
35723 return new r.South(cfg);
35725 return new r.East(cfg);
35727 return new r.West(cfg);
35729 return new r.Center(cfg);
35731 throw 'Layout region "'+target+'" not supported.';
35738 * Ext JS Library 1.1.1
35739 * Copyright(c) 2006-2007, Ext JS, LLC.
35741 * Originally Released Under LGPL - original licence link has changed is not relivant.
35744 * <script type="text/javascript">
35748 * @class Roo.bootstrap.layout.Basic
35749 * @extends Roo.util.Observable
35750 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35751 * and does not have a titlebar, tabs or any other features. All it does is size and position
35752 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35753 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35754 * @cfg {string} region the region that it inhabits..
35755 * @cfg {bool} skipConfig skip config?
35759 Roo.bootstrap.layout.Basic = function(config){
35761 this.mgr = config.mgr;
35763 this.position = config.region;
35765 var skipConfig = config.skipConfig;
35769 * @scope Roo.BasicLayoutRegion
35773 * @event beforeremove
35774 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35775 * @param {Roo.LayoutRegion} this
35776 * @param {Roo.ContentPanel} panel The panel
35777 * @param {Object} e The cancel event object
35779 "beforeremove" : true,
35781 * @event invalidated
35782 * Fires when the layout for this region is changed.
35783 * @param {Roo.LayoutRegion} this
35785 "invalidated" : true,
35787 * @event visibilitychange
35788 * Fires when this region is shown or hidden
35789 * @param {Roo.LayoutRegion} this
35790 * @param {Boolean} visibility true or false
35792 "visibilitychange" : true,
35794 * @event paneladded
35795 * Fires when a panel is added.
35796 * @param {Roo.LayoutRegion} this
35797 * @param {Roo.ContentPanel} panel The panel
35799 "paneladded" : true,
35801 * @event panelremoved
35802 * Fires when a panel is removed.
35803 * @param {Roo.LayoutRegion} this
35804 * @param {Roo.ContentPanel} panel The panel
35806 "panelremoved" : true,
35808 * @event beforecollapse
35809 * Fires when this region before collapse.
35810 * @param {Roo.LayoutRegion} this
35812 "beforecollapse" : true,
35815 * Fires when this region is collapsed.
35816 * @param {Roo.LayoutRegion} this
35818 "collapsed" : true,
35821 * Fires when this region is expanded.
35822 * @param {Roo.LayoutRegion} this
35827 * Fires when this region is slid into view.
35828 * @param {Roo.LayoutRegion} this
35830 "slideshow" : true,
35833 * Fires when this region slides out of view.
35834 * @param {Roo.LayoutRegion} this
35836 "slidehide" : true,
35838 * @event panelactivated
35839 * Fires when a panel is activated.
35840 * @param {Roo.LayoutRegion} this
35841 * @param {Roo.ContentPanel} panel The activated panel
35843 "panelactivated" : true,
35846 * Fires when the user resizes this region.
35847 * @param {Roo.LayoutRegion} this
35848 * @param {Number} newSize The new size (width for east/west, height for north/south)
35852 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35853 this.panels = new Roo.util.MixedCollection();
35854 this.panels.getKey = this.getPanelId.createDelegate(this);
35856 this.activePanel = null;
35857 // ensure listeners are added...
35859 if (config.listeners || config.events) {
35860 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35861 listeners : config.listeners || {},
35862 events : config.events || {}
35866 if(skipConfig !== true){
35867 this.applyConfig(config);
35871 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35873 getPanelId : function(p){
35877 applyConfig : function(config){
35878 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35879 this.config = config;
35884 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35885 * the width, for horizontal (north, south) the height.
35886 * @param {Number} newSize The new width or height
35888 resizeTo : function(newSize){
35889 var el = this.el ? this.el :
35890 (this.activePanel ? this.activePanel.getEl() : null);
35892 switch(this.position){
35895 el.setWidth(newSize);
35896 this.fireEvent("resized", this, newSize);
35900 el.setHeight(newSize);
35901 this.fireEvent("resized", this, newSize);
35907 getBox : function(){
35908 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35911 getMargins : function(){
35912 return this.margins;
35915 updateBox : function(box){
35917 var el = this.activePanel.getEl();
35918 el.dom.style.left = box.x + "px";
35919 el.dom.style.top = box.y + "px";
35920 this.activePanel.setSize(box.width, box.height);
35924 * Returns the container element for this region.
35925 * @return {Roo.Element}
35927 getEl : function(){
35928 return this.activePanel;
35932 * Returns true if this region is currently visible.
35933 * @return {Boolean}
35935 isVisible : function(){
35936 return this.activePanel ? true : false;
35939 setActivePanel : function(panel){
35940 panel = this.getPanel(panel);
35941 if(this.activePanel && this.activePanel != panel){
35942 this.activePanel.setActiveState(false);
35943 this.activePanel.getEl().setLeftTop(-10000,-10000);
35945 this.activePanel = panel;
35946 panel.setActiveState(true);
35948 panel.setSize(this.box.width, this.box.height);
35950 this.fireEvent("panelactivated", this, panel);
35951 this.fireEvent("invalidated");
35955 * Show the specified panel.
35956 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35957 * @return {Roo.ContentPanel} The shown panel or null
35959 showPanel : function(panel){
35960 panel = this.getPanel(panel);
35962 this.setActivePanel(panel);
35968 * Get the active panel for this region.
35969 * @return {Roo.ContentPanel} The active panel or null
35971 getActivePanel : function(){
35972 return this.activePanel;
35976 * Add the passed ContentPanel(s)
35977 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35978 * @return {Roo.ContentPanel} The panel added (if only one was added)
35980 add : function(panel){
35981 if(arguments.length > 1){
35982 for(var i = 0, len = arguments.length; i < len; i++) {
35983 this.add(arguments[i]);
35987 if(this.hasPanel(panel)){
35988 this.showPanel(panel);
35991 var el = panel.getEl();
35992 if(el.dom.parentNode != this.mgr.el.dom){
35993 this.mgr.el.dom.appendChild(el.dom);
35995 if(panel.setRegion){
35996 panel.setRegion(this);
35998 this.panels.add(panel);
35999 el.setStyle("position", "absolute");
36000 if(!panel.background){
36001 this.setActivePanel(panel);
36002 if(this.config.initialSize && this.panels.getCount()==1){
36003 this.resizeTo(this.config.initialSize);
36006 this.fireEvent("paneladded", this, panel);
36011 * Returns true if the panel is in this region.
36012 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36013 * @return {Boolean}
36015 hasPanel : function(panel){
36016 if(typeof panel == "object"){ // must be panel obj
36017 panel = panel.getId();
36019 return this.getPanel(panel) ? true : false;
36023 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36024 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36025 * @param {Boolean} preservePanel Overrides the config preservePanel option
36026 * @return {Roo.ContentPanel} The panel that was removed
36028 remove : function(panel, preservePanel){
36029 panel = this.getPanel(panel);
36034 this.fireEvent("beforeremove", this, panel, e);
36035 if(e.cancel === true){
36038 var panelId = panel.getId();
36039 this.panels.removeKey(panelId);
36044 * Returns the panel specified or null if it's not in this region.
36045 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36046 * @return {Roo.ContentPanel}
36048 getPanel : function(id){
36049 if(typeof id == "object"){ // must be panel obj
36052 return this.panels.get(id);
36056 * Returns this regions position (north/south/east/west/center).
36059 getPosition: function(){
36060 return this.position;
36064 * Ext JS Library 1.1.1
36065 * Copyright(c) 2006-2007, Ext JS, LLC.
36067 * Originally Released Under LGPL - original licence link has changed is not relivant.
36070 * <script type="text/javascript">
36074 * @class Roo.bootstrap.layout.Region
36075 * @extends Roo.bootstrap.layout.Basic
36076 * This class represents a region in a layout manager.
36078 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
36079 * @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})
36080 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
36081 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
36082 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
36083 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
36084 * @cfg {String} title The title for the region (overrides panel titles)
36085 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
36086 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36087 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
36088 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
36089 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
36090 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
36091 * the space available, similar to FireFox 1.5 tabs (defaults to false)
36092 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
36093 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
36094 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
36096 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
36097 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
36098 * @cfg {Boolean} disableTabTips True to disable tab tooltips
36099 * @cfg {Number} width For East/West panels
36100 * @cfg {Number} height For North/South panels
36101 * @cfg {Boolean} split To show the splitter
36102 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
36104 * @cfg {string} cls Extra CSS classes to add to region
36106 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
36107 * @cfg {string} region the region that it inhabits..
36110 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
36111 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
36113 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
36114 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
36115 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
36117 Roo.bootstrap.layout.Region = function(config)
36119 this.applyConfig(config);
36121 var mgr = config.mgr;
36122 var pos = config.region;
36123 config.skipConfig = true;
36124 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36127 this.onRender(mgr.el);
36130 this.visible = true;
36131 this.collapsed = false;
36132 this.unrendered_panels = [];
36135 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36137 position: '', // set by wrapper (eg. north/south etc..)
36138 unrendered_panels : null, // unrendered panels.
36140 tabPosition : false,
36142 mgr: false, // points to 'Border'
36145 createBody : function(){
36146 /** This region's body element
36147 * @type Roo.Element */
36148 this.bodyEl = this.el.createChild({
36150 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36154 onRender: function(ctr, pos)
36156 var dh = Roo.DomHelper;
36157 /** This region's container element
36158 * @type Roo.Element */
36159 this.el = dh.append(ctr.dom, {
36161 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36163 /** This region's title element
36164 * @type Roo.Element */
36166 this.titleEl = dh.append(this.el.dom, {
36168 unselectable: "on",
36169 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36171 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
36172 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36176 this.titleEl.enableDisplayMode();
36177 /** This region's title text element
36178 * @type HTMLElement */
36179 this.titleTextEl = this.titleEl.dom.firstChild;
36180 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36182 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36183 this.closeBtn.enableDisplayMode();
36184 this.closeBtn.on("click", this.closeClicked, this);
36185 this.closeBtn.hide();
36187 this.createBody(this.config);
36188 if(this.config.hideWhenEmpty){
36190 this.on("paneladded", this.validateVisibility, this);
36191 this.on("panelremoved", this.validateVisibility, this);
36193 if(this.autoScroll){
36194 this.bodyEl.setStyle("overflow", "auto");
36196 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36198 //if(c.titlebar !== false){
36199 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36200 this.titleEl.hide();
36202 this.titleEl.show();
36203 if(this.config.title){
36204 this.titleTextEl.innerHTML = this.config.title;
36208 if(this.config.collapsed){
36209 this.collapse(true);
36211 if(this.config.hidden){
36215 if (this.unrendered_panels && this.unrendered_panels.length) {
36216 for (var i =0;i< this.unrendered_panels.length; i++) {
36217 this.add(this.unrendered_panels[i]);
36219 this.unrendered_panels = null;
36225 applyConfig : function(c)
36228 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36229 var dh = Roo.DomHelper;
36230 if(c.titlebar !== false){
36231 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36232 this.collapseBtn.on("click", this.collapse, this);
36233 this.collapseBtn.enableDisplayMode();
36235 if(c.showPin === true || this.showPin){
36236 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36237 this.stickBtn.enableDisplayMode();
36238 this.stickBtn.on("click", this.expand, this);
36239 this.stickBtn.hide();
36244 /** This region's collapsed element
36245 * @type Roo.Element */
36248 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36249 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36252 if(c.floatable !== false){
36253 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36254 this.collapsedEl.on("click", this.collapseClick, this);
36257 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36258 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36259 id: "message", unselectable: "on", style:{"float":"left"}});
36260 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36262 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36263 this.expandBtn.on("click", this.expand, this);
36267 if(this.collapseBtn){
36268 this.collapseBtn.setVisible(c.collapsible == true);
36271 this.cmargins = c.cmargins || this.cmargins ||
36272 (this.position == "west" || this.position == "east" ?
36273 {top: 0, left: 2, right:2, bottom: 0} :
36274 {top: 2, left: 0, right:0, bottom: 2});
36276 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36279 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36281 this.autoScroll = c.autoScroll || false;
36286 this.duration = c.duration || .30;
36287 this.slideDuration = c.slideDuration || .45;
36292 * Returns true if this region is currently visible.
36293 * @return {Boolean}
36295 isVisible : function(){
36296 return this.visible;
36300 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36301 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
36303 //setCollapsedTitle : function(title){
36304 // title = title || " ";
36305 // if(this.collapsedTitleTextEl){
36306 // this.collapsedTitleTextEl.innerHTML = title;
36310 getBox : function(){
36312 // if(!this.collapsed){
36313 b = this.el.getBox(false, true);
36315 // b = this.collapsedEl.getBox(false, true);
36320 getMargins : function(){
36321 return this.margins;
36322 //return this.collapsed ? this.cmargins : this.margins;
36325 highlight : function(){
36326 this.el.addClass("x-layout-panel-dragover");
36329 unhighlight : function(){
36330 this.el.removeClass("x-layout-panel-dragover");
36333 updateBox : function(box)
36335 if (!this.bodyEl) {
36336 return; // not rendered yet..
36340 if(!this.collapsed){
36341 this.el.dom.style.left = box.x + "px";
36342 this.el.dom.style.top = box.y + "px";
36343 this.updateBody(box.width, box.height);
36345 this.collapsedEl.dom.style.left = box.x + "px";
36346 this.collapsedEl.dom.style.top = box.y + "px";
36347 this.collapsedEl.setSize(box.width, box.height);
36350 this.tabs.autoSizeTabs();
36354 updateBody : function(w, h)
36357 this.el.setWidth(w);
36358 w -= this.el.getBorderWidth("rl");
36359 if(this.config.adjustments){
36360 w += this.config.adjustments[0];
36363 if(h !== null && h > 0){
36364 this.el.setHeight(h);
36365 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36366 h -= this.el.getBorderWidth("tb");
36367 if(this.config.adjustments){
36368 h += this.config.adjustments[1];
36370 this.bodyEl.setHeight(h);
36372 h = this.tabs.syncHeight(h);
36375 if(this.panelSize){
36376 w = w !== null ? w : this.panelSize.width;
36377 h = h !== null ? h : this.panelSize.height;
36379 if(this.activePanel){
36380 var el = this.activePanel.getEl();
36381 w = w !== null ? w : el.getWidth();
36382 h = h !== null ? h : el.getHeight();
36383 this.panelSize = {width: w, height: h};
36384 this.activePanel.setSize(w, h);
36386 if(Roo.isIE && this.tabs){
36387 this.tabs.el.repaint();
36392 * Returns the container element for this region.
36393 * @return {Roo.Element}
36395 getEl : function(){
36400 * Hides this region.
36403 //if(!this.collapsed){
36404 this.el.dom.style.left = "-2000px";
36407 // this.collapsedEl.dom.style.left = "-2000px";
36408 // this.collapsedEl.hide();
36410 this.visible = false;
36411 this.fireEvent("visibilitychange", this, false);
36415 * Shows this region if it was previously hidden.
36418 //if(!this.collapsed){
36421 // this.collapsedEl.show();
36423 this.visible = true;
36424 this.fireEvent("visibilitychange", this, true);
36427 closeClicked : function(){
36428 if(this.activePanel){
36429 this.remove(this.activePanel);
36433 collapseClick : function(e){
36435 e.stopPropagation();
36438 e.stopPropagation();
36444 * Collapses this region.
36445 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36448 collapse : function(skipAnim, skipCheck = false){
36449 if(this.collapsed) {
36453 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36455 this.collapsed = true;
36457 this.split.el.hide();
36459 if(this.config.animate && skipAnim !== true){
36460 this.fireEvent("invalidated", this);
36461 this.animateCollapse();
36463 this.el.setLocation(-20000,-20000);
36465 this.collapsedEl.show();
36466 this.fireEvent("collapsed", this);
36467 this.fireEvent("invalidated", this);
36473 animateCollapse : function(){
36478 * Expands this region if it was previously collapsed.
36479 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36480 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36483 expand : function(e, skipAnim){
36485 e.stopPropagation();
36487 if(!this.collapsed || this.el.hasActiveFx()) {
36491 this.afterSlideIn();
36494 this.collapsed = false;
36495 if(this.config.animate && skipAnim !== true){
36496 this.animateExpand();
36500 this.split.el.show();
36502 this.collapsedEl.setLocation(-2000,-2000);
36503 this.collapsedEl.hide();
36504 this.fireEvent("invalidated", this);
36505 this.fireEvent("expanded", this);
36509 animateExpand : function(){
36513 initTabs : function()
36515 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36517 var ts = new Roo.bootstrap.panel.Tabs({
36518 el: this.bodyEl.dom,
36520 tabPosition: this.tabPosition ? this.tabPosition : 'top',
36521 disableTooltips: this.config.disableTabTips,
36522 toolbar : this.config.toolbar
36525 if(this.config.hideTabs){
36526 ts.stripWrap.setDisplayed(false);
36529 ts.resizeTabs = this.config.resizeTabs === true;
36530 ts.minTabWidth = this.config.minTabWidth || 40;
36531 ts.maxTabWidth = this.config.maxTabWidth || 250;
36532 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36533 ts.monitorResize = false;
36534 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36535 ts.bodyEl.addClass('roo-layout-tabs-body');
36536 this.panels.each(this.initPanelAsTab, this);
36539 initPanelAsTab : function(panel){
36540 var ti = this.tabs.addTab(
36544 this.config.closeOnTab && panel.isClosable(),
36547 if(panel.tabTip !== undefined){
36548 ti.setTooltip(panel.tabTip);
36550 ti.on("activate", function(){
36551 this.setActivePanel(panel);
36554 if(this.config.closeOnTab){
36555 ti.on("beforeclose", function(t, e){
36557 this.remove(panel);
36561 panel.tabItem = ti;
36566 updatePanelTitle : function(panel, title)
36568 if(this.activePanel == panel){
36569 this.updateTitle(title);
36572 var ti = this.tabs.getTab(panel.getEl().id);
36574 if(panel.tabTip !== undefined){
36575 ti.setTooltip(panel.tabTip);
36580 updateTitle : function(title){
36581 if(this.titleTextEl && !this.config.title){
36582 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36586 setActivePanel : function(panel)
36588 panel = this.getPanel(panel);
36589 if(this.activePanel && this.activePanel != panel){
36590 if(this.activePanel.setActiveState(false) === false){
36594 this.activePanel = panel;
36595 panel.setActiveState(true);
36596 if(this.panelSize){
36597 panel.setSize(this.panelSize.width, this.panelSize.height);
36600 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36602 this.updateTitle(panel.getTitle());
36604 this.fireEvent("invalidated", this);
36606 this.fireEvent("panelactivated", this, panel);
36610 * Shows the specified panel.
36611 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36612 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36614 showPanel : function(panel)
36616 panel = this.getPanel(panel);
36619 var tab = this.tabs.getTab(panel.getEl().id);
36620 if(tab.isHidden()){
36621 this.tabs.unhideTab(tab.id);
36625 this.setActivePanel(panel);
36632 * Get the active panel for this region.
36633 * @return {Roo.ContentPanel} The active panel or null
36635 getActivePanel : function(){
36636 return this.activePanel;
36639 validateVisibility : function(){
36640 if(this.panels.getCount() < 1){
36641 this.updateTitle(" ");
36642 this.closeBtn.hide();
36645 if(!this.isVisible()){
36652 * Adds the passed ContentPanel(s) to this region.
36653 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36654 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36656 add : function(panel)
36658 if(arguments.length > 1){
36659 for(var i = 0, len = arguments.length; i < len; i++) {
36660 this.add(arguments[i]);
36665 // if we have not been rendered yet, then we can not really do much of this..
36666 if (!this.bodyEl) {
36667 this.unrendered_panels.push(panel);
36674 if(this.hasPanel(panel)){
36675 this.showPanel(panel);
36678 panel.setRegion(this);
36679 this.panels.add(panel);
36680 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36681 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36682 // and hide them... ???
36683 this.bodyEl.dom.appendChild(panel.getEl().dom);
36684 if(panel.background !== true){
36685 this.setActivePanel(panel);
36687 this.fireEvent("paneladded", this, panel);
36694 this.initPanelAsTab(panel);
36698 if(panel.background !== true){
36699 this.tabs.activate(panel.getEl().id);
36701 this.fireEvent("paneladded", this, panel);
36706 * Hides the tab for the specified panel.
36707 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36709 hidePanel : function(panel){
36710 if(this.tabs && (panel = this.getPanel(panel))){
36711 this.tabs.hideTab(panel.getEl().id);
36716 * Unhides the tab for a previously hidden panel.
36717 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36719 unhidePanel : function(panel){
36720 if(this.tabs && (panel = this.getPanel(panel))){
36721 this.tabs.unhideTab(panel.getEl().id);
36725 clearPanels : function(){
36726 while(this.panels.getCount() > 0){
36727 this.remove(this.panels.first());
36732 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36733 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36734 * @param {Boolean} preservePanel Overrides the config preservePanel option
36735 * @return {Roo.ContentPanel} The panel that was removed
36737 remove : function(panel, preservePanel)
36739 panel = this.getPanel(panel);
36744 this.fireEvent("beforeremove", this, panel, e);
36745 if(e.cancel === true){
36748 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36749 var panelId = panel.getId();
36750 this.panels.removeKey(panelId);
36752 document.body.appendChild(panel.getEl().dom);
36755 this.tabs.removeTab(panel.getEl().id);
36756 }else if (!preservePanel){
36757 this.bodyEl.dom.removeChild(panel.getEl().dom);
36759 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36760 var p = this.panels.first();
36761 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36762 tempEl.appendChild(p.getEl().dom);
36763 this.bodyEl.update("");
36764 this.bodyEl.dom.appendChild(p.getEl().dom);
36766 this.updateTitle(p.getTitle());
36768 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36769 this.setActivePanel(p);
36771 panel.setRegion(null);
36772 if(this.activePanel == panel){
36773 this.activePanel = null;
36775 if(this.config.autoDestroy !== false && preservePanel !== true){
36776 try{panel.destroy();}catch(e){}
36778 this.fireEvent("panelremoved", this, panel);
36783 * Returns the TabPanel component used by this region
36784 * @return {Roo.TabPanel}
36786 getTabs : function(){
36790 createTool : function(parentEl, className){
36791 var btn = Roo.DomHelper.append(parentEl, {
36793 cls: "x-layout-tools-button",
36796 cls: "roo-layout-tools-button-inner " + className,
36800 btn.addClassOnOver("roo-layout-tools-button-over");
36805 * Ext JS Library 1.1.1
36806 * Copyright(c) 2006-2007, Ext JS, LLC.
36808 * Originally Released Under LGPL - original licence link has changed is not relivant.
36811 * <script type="text/javascript">
36817 * @class Roo.SplitLayoutRegion
36818 * @extends Roo.LayoutRegion
36819 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36821 Roo.bootstrap.layout.Split = function(config){
36822 this.cursor = config.cursor;
36823 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36826 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36828 splitTip : "Drag to resize.",
36829 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36830 useSplitTips : false,
36832 applyConfig : function(config){
36833 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36836 onRender : function(ctr,pos) {
36838 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36839 if(!this.config.split){
36844 var splitEl = Roo.DomHelper.append(ctr.dom, {
36846 id: this.el.id + "-split",
36847 cls: "roo-layout-split roo-layout-split-"+this.position,
36850 /** The SplitBar for this region
36851 * @type Roo.SplitBar */
36852 // does not exist yet...
36853 Roo.log([this.position, this.orientation]);
36855 this.split = new Roo.bootstrap.SplitBar({
36856 dragElement : splitEl,
36857 resizingElement: this.el,
36858 orientation : this.orientation
36861 this.split.on("moved", this.onSplitMove, this);
36862 this.split.useShim = this.config.useShim === true;
36863 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36864 if(this.useSplitTips){
36865 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36867 //if(config.collapsible){
36868 // this.split.el.on("dblclick", this.collapse, this);
36871 if(typeof this.config.minSize != "undefined"){
36872 this.split.minSize = this.config.minSize;
36874 if(typeof this.config.maxSize != "undefined"){
36875 this.split.maxSize = this.config.maxSize;
36877 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36878 this.hideSplitter();
36883 getHMaxSize : function(){
36884 var cmax = this.config.maxSize || 10000;
36885 var center = this.mgr.getRegion("center");
36886 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36889 getVMaxSize : function(){
36890 var cmax = this.config.maxSize || 10000;
36891 var center = this.mgr.getRegion("center");
36892 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36895 onSplitMove : function(split, newSize){
36896 this.fireEvent("resized", this, newSize);
36900 * Returns the {@link Roo.SplitBar} for this region.
36901 * @return {Roo.SplitBar}
36903 getSplitBar : function(){
36908 this.hideSplitter();
36909 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36912 hideSplitter : function(){
36914 this.split.el.setLocation(-2000,-2000);
36915 this.split.el.hide();
36921 this.split.el.show();
36923 Roo.bootstrap.layout.Split.superclass.show.call(this);
36926 beforeSlide: function(){
36927 if(Roo.isGecko){// firefox overflow auto bug workaround
36928 this.bodyEl.clip();
36930 this.tabs.bodyEl.clip();
36932 if(this.activePanel){
36933 this.activePanel.getEl().clip();
36935 if(this.activePanel.beforeSlide){
36936 this.activePanel.beforeSlide();
36942 afterSlide : function(){
36943 if(Roo.isGecko){// firefox overflow auto bug workaround
36944 this.bodyEl.unclip();
36946 this.tabs.bodyEl.unclip();
36948 if(this.activePanel){
36949 this.activePanel.getEl().unclip();
36950 if(this.activePanel.afterSlide){
36951 this.activePanel.afterSlide();
36957 initAutoHide : function(){
36958 if(this.autoHide !== false){
36959 if(!this.autoHideHd){
36960 var st = new Roo.util.DelayedTask(this.slideIn, this);
36961 this.autoHideHd = {
36962 "mouseout": function(e){
36963 if(!e.within(this.el, true)){
36967 "mouseover" : function(e){
36973 this.el.on(this.autoHideHd);
36977 clearAutoHide : function(){
36978 if(this.autoHide !== false){
36979 this.el.un("mouseout", this.autoHideHd.mouseout);
36980 this.el.un("mouseover", this.autoHideHd.mouseover);
36984 clearMonitor : function(){
36985 Roo.get(document).un("click", this.slideInIf, this);
36988 // these names are backwards but not changed for compat
36989 slideOut : function(){
36990 if(this.isSlid || this.el.hasActiveFx()){
36993 this.isSlid = true;
36994 if(this.collapseBtn){
36995 this.collapseBtn.hide();
36997 this.closeBtnState = this.closeBtn.getStyle('display');
36998 this.closeBtn.hide();
37000 this.stickBtn.show();
37003 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
37004 this.beforeSlide();
37005 this.el.setStyle("z-index", 10001);
37006 this.el.slideIn(this.getSlideAnchor(), {
37007 callback: function(){
37009 this.initAutoHide();
37010 Roo.get(document).on("click", this.slideInIf, this);
37011 this.fireEvent("slideshow", this);
37018 afterSlideIn : function(){
37019 this.clearAutoHide();
37020 this.isSlid = false;
37021 this.clearMonitor();
37022 this.el.setStyle("z-index", "");
37023 if(this.collapseBtn){
37024 this.collapseBtn.show();
37026 this.closeBtn.setStyle('display', this.closeBtnState);
37028 this.stickBtn.hide();
37030 this.fireEvent("slidehide", this);
37033 slideIn : function(cb){
37034 if(!this.isSlid || this.el.hasActiveFx()){
37038 this.isSlid = false;
37039 this.beforeSlide();
37040 this.el.slideOut(this.getSlideAnchor(), {
37041 callback: function(){
37042 this.el.setLeftTop(-10000, -10000);
37044 this.afterSlideIn();
37052 slideInIf : function(e){
37053 if(!e.within(this.el)){
37058 animateCollapse : function(){
37059 this.beforeSlide();
37060 this.el.setStyle("z-index", 20000);
37061 var anchor = this.getSlideAnchor();
37062 this.el.slideOut(anchor, {
37063 callback : function(){
37064 this.el.setStyle("z-index", "");
37065 this.collapsedEl.slideIn(anchor, {duration:.3});
37067 this.el.setLocation(-10000,-10000);
37069 this.fireEvent("collapsed", this);
37076 animateExpand : function(){
37077 this.beforeSlide();
37078 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
37079 this.el.setStyle("z-index", 20000);
37080 this.collapsedEl.hide({
37083 this.el.slideIn(this.getSlideAnchor(), {
37084 callback : function(){
37085 this.el.setStyle("z-index", "");
37088 this.split.el.show();
37090 this.fireEvent("invalidated", this);
37091 this.fireEvent("expanded", this);
37119 getAnchor : function(){
37120 return this.anchors[this.position];
37123 getCollapseAnchor : function(){
37124 return this.canchors[this.position];
37127 getSlideAnchor : function(){
37128 return this.sanchors[this.position];
37131 getAlignAdj : function(){
37132 var cm = this.cmargins;
37133 switch(this.position){
37149 getExpandAdj : function(){
37150 var c = this.collapsedEl, cm = this.cmargins;
37151 switch(this.position){
37153 return [-(cm.right+c.getWidth()+cm.left), 0];
37156 return [cm.right+c.getWidth()+cm.left, 0];
37159 return [0, -(cm.top+cm.bottom+c.getHeight())];
37162 return [0, cm.top+cm.bottom+c.getHeight()];
37168 * Ext JS Library 1.1.1
37169 * Copyright(c) 2006-2007, Ext JS, LLC.
37171 * Originally Released Under LGPL - original licence link has changed is not relivant.
37174 * <script type="text/javascript">
37177 * These classes are private internal classes
37179 Roo.bootstrap.layout.Center = function(config){
37180 config.region = "center";
37181 Roo.bootstrap.layout.Region.call(this, config);
37182 this.visible = true;
37183 this.minWidth = config.minWidth || 20;
37184 this.minHeight = config.minHeight || 20;
37187 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37189 // center panel can't be hidden
37193 // center panel can't be hidden
37196 getMinWidth: function(){
37197 return this.minWidth;
37200 getMinHeight: function(){
37201 return this.minHeight;
37215 Roo.bootstrap.layout.North = function(config)
37217 config.region = 'north';
37218 config.cursor = 'n-resize';
37220 Roo.bootstrap.layout.Split.call(this, config);
37224 this.split.placement = Roo.bootstrap.SplitBar.TOP;
37225 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37226 this.split.el.addClass("roo-layout-split-v");
37228 var size = config.initialSize || config.height;
37229 if(typeof size != "undefined"){
37230 this.el.setHeight(size);
37233 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37235 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37239 getBox : function(){
37240 if(this.collapsed){
37241 return this.collapsedEl.getBox();
37243 var box = this.el.getBox();
37245 box.height += this.split.el.getHeight();
37250 updateBox : function(box){
37251 if(this.split && !this.collapsed){
37252 box.height -= this.split.el.getHeight();
37253 this.split.el.setLeft(box.x);
37254 this.split.el.setTop(box.y+box.height);
37255 this.split.el.setWidth(box.width);
37257 if(this.collapsed){
37258 this.updateBody(box.width, null);
37260 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37268 Roo.bootstrap.layout.South = function(config){
37269 config.region = 'south';
37270 config.cursor = 's-resize';
37271 Roo.bootstrap.layout.Split.call(this, config);
37273 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37274 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37275 this.split.el.addClass("roo-layout-split-v");
37277 var size = config.initialSize || config.height;
37278 if(typeof size != "undefined"){
37279 this.el.setHeight(size);
37283 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37284 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37285 getBox : function(){
37286 if(this.collapsed){
37287 return this.collapsedEl.getBox();
37289 var box = this.el.getBox();
37291 var sh = this.split.el.getHeight();
37298 updateBox : function(box){
37299 if(this.split && !this.collapsed){
37300 var sh = this.split.el.getHeight();
37303 this.split.el.setLeft(box.x);
37304 this.split.el.setTop(box.y-sh);
37305 this.split.el.setWidth(box.width);
37307 if(this.collapsed){
37308 this.updateBody(box.width, null);
37310 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37314 Roo.bootstrap.layout.East = function(config){
37315 config.region = "east";
37316 config.cursor = "e-resize";
37317 Roo.bootstrap.layout.Split.call(this, config);
37319 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37320 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37321 this.split.el.addClass("roo-layout-split-h");
37323 var size = config.initialSize || config.width;
37324 if(typeof size != "undefined"){
37325 this.el.setWidth(size);
37328 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37329 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37330 getBox : function(){
37331 if(this.collapsed){
37332 return this.collapsedEl.getBox();
37334 var box = this.el.getBox();
37336 var sw = this.split.el.getWidth();
37343 updateBox : function(box){
37344 if(this.split && !this.collapsed){
37345 var sw = this.split.el.getWidth();
37347 this.split.el.setLeft(box.x);
37348 this.split.el.setTop(box.y);
37349 this.split.el.setHeight(box.height);
37352 if(this.collapsed){
37353 this.updateBody(null, box.height);
37355 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37359 Roo.bootstrap.layout.West = function(config){
37360 config.region = "west";
37361 config.cursor = "w-resize";
37363 Roo.bootstrap.layout.Split.call(this, config);
37365 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37366 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37367 this.split.el.addClass("roo-layout-split-h");
37371 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37372 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37374 onRender: function(ctr, pos)
37376 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37377 var size = this.config.initialSize || this.config.width;
37378 if(typeof size != "undefined"){
37379 this.el.setWidth(size);
37383 getBox : function(){
37384 if(this.collapsed){
37385 return this.collapsedEl.getBox();
37387 var box = this.el.getBox();
37389 box.width += this.split.el.getWidth();
37394 updateBox : function(box){
37395 if(this.split && !this.collapsed){
37396 var sw = this.split.el.getWidth();
37398 this.split.el.setLeft(box.x+box.width);
37399 this.split.el.setTop(box.y);
37400 this.split.el.setHeight(box.height);
37402 if(this.collapsed){
37403 this.updateBody(null, box.height);
37405 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37407 });Roo.namespace("Roo.bootstrap.panel");/*
37409 * Ext JS Library 1.1.1
37410 * Copyright(c) 2006-2007, Ext JS, LLC.
37412 * Originally Released Under LGPL - original licence link has changed is not relivant.
37415 * <script type="text/javascript">
37418 * @class Roo.ContentPanel
37419 * @extends Roo.util.Observable
37420 * A basic ContentPanel element.
37421 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
37422 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
37423 * @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
37424 * @cfg {Boolean} closable True if the panel can be closed/removed
37425 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
37426 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37427 * @cfg {Toolbar} toolbar A toolbar for this panel
37428 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37429 * @cfg {String} title The title for this panel
37430 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37431 * @cfg {String} url Calls {@link #setUrl} with this value
37432 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37433 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37434 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37435 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37436 * @cfg {Boolean} badges render the badges
37439 * Create a new ContentPanel.
37440 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37441 * @param {String/Object} config A string to set only the title or a config object
37442 * @param {String} content (optional) Set the HTML content for this panel
37443 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37445 Roo.bootstrap.panel.Content = function( config){
37447 this.tpl = config.tpl || false;
37449 var el = config.el;
37450 var content = config.content;
37452 if(config.autoCreate){ // xtype is available if this is called from factory
37455 this.el = Roo.get(el);
37456 if(!this.el && config && config.autoCreate){
37457 if(typeof config.autoCreate == "object"){
37458 if(!config.autoCreate.id){
37459 config.autoCreate.id = config.id||el;
37461 this.el = Roo.DomHelper.append(document.body,
37462 config.autoCreate, true);
37464 var elcfg = { tag: "div",
37465 cls: "roo-layout-inactive-content",
37469 elcfg.html = config.html;
37473 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37476 this.closable = false;
37477 this.loaded = false;
37478 this.active = false;
37481 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37483 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37485 this.wrapEl = this.el; //this.el.wrap();
37487 if (config.toolbar.items) {
37488 ti = config.toolbar.items ;
37489 delete config.toolbar.items ;
37493 this.toolbar.render(this.wrapEl, 'before');
37494 for(var i =0;i < ti.length;i++) {
37495 // Roo.log(['add child', items[i]]);
37496 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37498 this.toolbar.items = nitems;
37499 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37500 delete config.toolbar;
37504 // xtype created footer. - not sure if will work as we normally have to render first..
37505 if (this.footer && !this.footer.el && this.footer.xtype) {
37506 if (!this.wrapEl) {
37507 this.wrapEl = this.el.wrap();
37510 this.footer.container = this.wrapEl.createChild();
37512 this.footer = Roo.factory(this.footer, Roo);
37517 if(typeof config == "string"){
37518 this.title = config;
37520 Roo.apply(this, config);
37524 this.resizeEl = Roo.get(this.resizeEl, true);
37526 this.resizeEl = this.el;
37528 // handle view.xtype
37536 * Fires when this panel is activated.
37537 * @param {Roo.ContentPanel} this
37541 * @event deactivate
37542 * Fires when this panel is activated.
37543 * @param {Roo.ContentPanel} this
37545 "deactivate" : true,
37549 * Fires when this panel is resized if fitToFrame is true.
37550 * @param {Roo.ContentPanel} this
37551 * @param {Number} width The width after any component adjustments
37552 * @param {Number} height The height after any component adjustments
37558 * Fires when this tab is created
37559 * @param {Roo.ContentPanel} this
37570 if(this.autoScroll){
37571 this.resizeEl.setStyle("overflow", "auto");
37573 // fix randome scrolling
37574 //this.el.on('scroll', function() {
37575 // Roo.log('fix random scolling');
37576 // this.scrollTo('top',0);
37579 content = content || this.content;
37581 this.setContent(content);
37583 if(config && config.url){
37584 this.setUrl(this.url, this.params, this.loadOnce);
37589 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37591 if (this.view && typeof(this.view.xtype) != 'undefined') {
37592 this.view.el = this.el.appendChild(document.createElement("div"));
37593 this.view = Roo.factory(this.view);
37594 this.view.render && this.view.render(false, '');
37598 this.fireEvent('render', this);
37601 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37605 setRegion : function(region){
37606 this.region = region;
37607 this.setActiveClass(region && !this.background);
37611 setActiveClass: function(state)
37614 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37615 this.el.setStyle('position','relative');
37617 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37618 this.el.setStyle('position', 'absolute');
37623 * Returns the toolbar for this Panel if one was configured.
37624 * @return {Roo.Toolbar}
37626 getToolbar : function(){
37627 return this.toolbar;
37630 setActiveState : function(active)
37632 this.active = active;
37633 this.setActiveClass(active);
37635 if(this.fireEvent("deactivate", this) === false){
37640 this.fireEvent("activate", this);
37644 * Updates this panel's element
37645 * @param {String} content The new content
37646 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37648 setContent : function(content, loadScripts){
37649 this.el.update(content, loadScripts);
37652 ignoreResize : function(w, h){
37653 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37656 this.lastSize = {width: w, height: h};
37661 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37662 * @return {Roo.UpdateManager} The UpdateManager
37664 getUpdateManager : function(){
37665 return this.el.getUpdateManager();
37668 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37669 * @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:
37672 url: "your-url.php",
37673 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37674 callback: yourFunction,
37675 scope: yourObject, //(optional scope)
37678 text: "Loading...",
37683 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37684 * 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.
37685 * @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}
37686 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37687 * @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.
37688 * @return {Roo.ContentPanel} this
37691 var um = this.el.getUpdateManager();
37692 um.update.apply(um, arguments);
37698 * 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.
37699 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37700 * @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)
37701 * @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)
37702 * @return {Roo.UpdateManager} The UpdateManager
37704 setUrl : function(url, params, loadOnce){
37705 if(this.refreshDelegate){
37706 this.removeListener("activate", this.refreshDelegate);
37708 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37709 this.on("activate", this.refreshDelegate);
37710 return this.el.getUpdateManager();
37713 _handleRefresh : function(url, params, loadOnce){
37714 if(!loadOnce || !this.loaded){
37715 var updater = this.el.getUpdateManager();
37716 updater.update(url, params, this._setLoaded.createDelegate(this));
37720 _setLoaded : function(){
37721 this.loaded = true;
37725 * Returns this panel's id
37728 getId : function(){
37733 * Returns this panel's element - used by regiosn to add.
37734 * @return {Roo.Element}
37736 getEl : function(){
37737 return this.wrapEl || this.el;
37742 adjustForComponents : function(width, height)
37744 //Roo.log('adjustForComponents ');
37745 if(this.resizeEl != this.el){
37746 width -= this.el.getFrameWidth('lr');
37747 height -= this.el.getFrameWidth('tb');
37750 var te = this.toolbar.getEl();
37751 te.setWidth(width);
37752 height -= te.getHeight();
37755 var te = this.footer.getEl();
37756 te.setWidth(width);
37757 height -= te.getHeight();
37761 if(this.adjustments){
37762 width += this.adjustments[0];
37763 height += this.adjustments[1];
37765 return {"width": width, "height": height};
37768 setSize : function(width, height){
37769 if(this.fitToFrame && !this.ignoreResize(width, height)){
37770 if(this.fitContainer && this.resizeEl != this.el){
37771 this.el.setSize(width, height);
37773 var size = this.adjustForComponents(width, height);
37774 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37775 this.fireEvent('resize', this, size.width, size.height);
37780 * Returns this panel's title
37783 getTitle : function(){
37785 if (typeof(this.title) != 'object') {
37790 for (var k in this.title) {
37791 if (!this.title.hasOwnProperty(k)) {
37795 if (k.indexOf('-') >= 0) {
37796 var s = k.split('-');
37797 for (var i = 0; i<s.length; i++) {
37798 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37801 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37808 * Set this panel's title
37809 * @param {String} title
37811 setTitle : function(title){
37812 this.title = title;
37814 this.region.updatePanelTitle(this, title);
37819 * Returns true is this panel was configured to be closable
37820 * @return {Boolean}
37822 isClosable : function(){
37823 return this.closable;
37826 beforeSlide : function(){
37828 this.resizeEl.clip();
37831 afterSlide : function(){
37833 this.resizeEl.unclip();
37837 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37838 * Will fail silently if the {@link #setUrl} method has not been called.
37839 * This does not activate the panel, just updates its content.
37841 refresh : function(){
37842 if(this.refreshDelegate){
37843 this.loaded = false;
37844 this.refreshDelegate();
37849 * Destroys this panel
37851 destroy : function(){
37852 this.el.removeAllListeners();
37853 var tempEl = document.createElement("span");
37854 tempEl.appendChild(this.el.dom);
37855 tempEl.innerHTML = "";
37861 * form - if the content panel contains a form - this is a reference to it.
37862 * @type {Roo.form.Form}
37866 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37867 * This contains a reference to it.
37873 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37883 * @param {Object} cfg Xtype definition of item to add.
37887 getChildContainer: function () {
37888 return this.getEl();
37893 var ret = new Roo.factory(cfg);
37898 if (cfg.xtype.match(/^Form$/)) {
37901 //if (this.footer) {
37902 // el = this.footer.container.insertSibling(false, 'before');
37904 el = this.el.createChild();
37907 this.form = new Roo.form.Form(cfg);
37910 if ( this.form.allItems.length) {
37911 this.form.render(el.dom);
37915 // should only have one of theses..
37916 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37917 // views.. should not be just added - used named prop 'view''
37919 cfg.el = this.el.appendChild(document.createElement("div"));
37922 var ret = new Roo.factory(cfg);
37924 ret.render && ret.render(false, ''); // render blank..
37934 * @class Roo.bootstrap.panel.Grid
37935 * @extends Roo.bootstrap.panel.Content
37937 * Create a new GridPanel.
37938 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37939 * @param {Object} config A the config object
37945 Roo.bootstrap.panel.Grid = function(config)
37949 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37950 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37952 config.el = this.wrapper;
37953 //this.el = this.wrapper;
37955 if (config.container) {
37956 // ctor'ed from a Border/panel.grid
37959 this.wrapper.setStyle("overflow", "hidden");
37960 this.wrapper.addClass('roo-grid-container');
37965 if(config.toolbar){
37966 var tool_el = this.wrapper.createChild();
37967 this.toolbar = Roo.factory(config.toolbar);
37969 if (config.toolbar.items) {
37970 ti = config.toolbar.items ;
37971 delete config.toolbar.items ;
37975 this.toolbar.render(tool_el);
37976 for(var i =0;i < ti.length;i++) {
37977 // Roo.log(['add child', items[i]]);
37978 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37980 this.toolbar.items = nitems;
37982 delete config.toolbar;
37985 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37986 config.grid.scrollBody = true;;
37987 config.grid.monitorWindowResize = false; // turn off autosizing
37988 config.grid.autoHeight = false;
37989 config.grid.autoWidth = false;
37991 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37993 if (config.background) {
37994 // render grid on panel activation (if panel background)
37995 this.on('activate', function(gp) {
37996 if (!gp.grid.rendered) {
37997 gp.grid.render(this.wrapper);
37998 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
38003 this.grid.render(this.wrapper);
38004 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
38007 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
38008 // ??? needed ??? config.el = this.wrapper;
38013 // xtype created footer. - not sure if will work as we normally have to render first..
38014 if (this.footer && !this.footer.el && this.footer.xtype) {
38016 var ctr = this.grid.getView().getFooterPanel(true);
38017 this.footer.dataSource = this.grid.dataSource;
38018 this.footer = Roo.factory(this.footer, Roo);
38019 this.footer.render(ctr);
38029 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
38030 getId : function(){
38031 return this.grid.id;
38035 * Returns the grid for this panel
38036 * @return {Roo.bootstrap.Table}
38038 getGrid : function(){
38042 setSize : function(width, height){
38043 if(!this.ignoreResize(width, height)){
38044 var grid = this.grid;
38045 var size = this.adjustForComponents(width, height);
38046 var gridel = grid.getGridEl();
38047 gridel.setSize(size.width, size.height);
38049 var thd = grid.getGridEl().select('thead',true).first();
38050 var tbd = grid.getGridEl().select('tbody', true).first();
38052 tbd.setSize(width, height - thd.getHeight());
38061 beforeSlide : function(){
38062 this.grid.getView().scroller.clip();
38065 afterSlide : function(){
38066 this.grid.getView().scroller.unclip();
38069 destroy : function(){
38070 this.grid.destroy();
38072 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
38077 * @class Roo.bootstrap.panel.Nest
38078 * @extends Roo.bootstrap.panel.Content
38080 * Create a new Panel, that can contain a layout.Border.
38083 * @param {Roo.BorderLayout} layout The layout for this panel
38084 * @param {String/Object} config A string to set only the title or a config object
38086 Roo.bootstrap.panel.Nest = function(config)
38088 // construct with only one argument..
38089 /* FIXME - implement nicer consturctors
38090 if (layout.layout) {
38092 layout = config.layout;
38093 delete config.layout;
38095 if (layout.xtype && !layout.getEl) {
38096 // then layout needs constructing..
38097 layout = Roo.factory(layout, Roo);
38101 config.el = config.layout.getEl();
38103 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
38105 config.layout.monitorWindowResize = false; // turn off autosizing
38106 this.layout = config.layout;
38107 this.layout.getEl().addClass("roo-layout-nested-layout");
38108 this.layout.parent = this;
38115 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38117 setSize : function(width, height){
38118 if(!this.ignoreResize(width, height)){
38119 var size = this.adjustForComponents(width, height);
38120 var el = this.layout.getEl();
38121 if (size.height < 1) {
38122 el.setWidth(size.width);
38124 el.setSize(size.width, size.height);
38126 var touch = el.dom.offsetWidth;
38127 this.layout.layout();
38128 // ie requires a double layout on the first pass
38129 if(Roo.isIE && !this.initialized){
38130 this.initialized = true;
38131 this.layout.layout();
38136 // activate all subpanels if not currently active..
38138 setActiveState : function(active){
38139 this.active = active;
38140 this.setActiveClass(active);
38143 this.fireEvent("deactivate", this);
38147 this.fireEvent("activate", this);
38148 // not sure if this should happen before or after..
38149 if (!this.layout) {
38150 return; // should not happen..
38153 for (var r in this.layout.regions) {
38154 reg = this.layout.getRegion(r);
38155 if (reg.getActivePanel()) {
38156 //reg.showPanel(reg.getActivePanel()); // force it to activate..
38157 reg.setActivePanel(reg.getActivePanel());
38160 if (!reg.panels.length) {
38163 reg.showPanel(reg.getPanel(0));
38172 * Returns the nested BorderLayout for this panel
38173 * @return {Roo.BorderLayout}
38175 getLayout : function(){
38176 return this.layout;
38180 * Adds a xtype elements to the layout of the nested panel
38184 xtype : 'ContentPanel',
38191 xtype : 'NestedLayoutPanel',
38197 items : [ ... list of content panels or nested layout panels.. ]
38201 * @param {Object} cfg Xtype definition of item to add.
38203 addxtype : function(cfg) {
38204 return this.layout.addxtype(cfg);
38209 * Ext JS Library 1.1.1
38210 * Copyright(c) 2006-2007, Ext JS, LLC.
38212 * Originally Released Under LGPL - original licence link has changed is not relivant.
38215 * <script type="text/javascript">
38218 * @class Roo.TabPanel
38219 * @extends Roo.util.Observable
38220 * A lightweight tab container.
38224 // basic tabs 1, built from existing content
38225 var tabs = new Roo.TabPanel("tabs1");
38226 tabs.addTab("script", "View Script");
38227 tabs.addTab("markup", "View Markup");
38228 tabs.activate("script");
38230 // more advanced tabs, built from javascript
38231 var jtabs = new Roo.TabPanel("jtabs");
38232 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38234 // set up the UpdateManager
38235 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38236 var updater = tab2.getUpdateManager();
38237 updater.setDefaultUrl("ajax1.htm");
38238 tab2.on('activate', updater.refresh, updater, true);
38240 // Use setUrl for Ajax loading
38241 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38242 tab3.setUrl("ajax2.htm", null, true);
38245 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38248 jtabs.activate("jtabs-1");
38251 * Create a new TabPanel.
38252 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38253 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38255 Roo.bootstrap.panel.Tabs = function(config){
38257 * The container element for this TabPanel.
38258 * @type Roo.Element
38260 this.el = Roo.get(config.el);
38263 if(typeof config == "boolean"){
38264 this.tabPosition = config ? "bottom" : "top";
38266 Roo.apply(this, config);
38270 if(this.tabPosition == "bottom"){
38271 // if tabs are at the bottom = create the body first.
38272 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38273 this.el.addClass("roo-tabs-bottom");
38275 // next create the tabs holders
38277 if (this.tabPosition == "west"){
38279 var reg = this.region; // fake it..
38281 if (!reg.mgr.parent) {
38284 reg = reg.mgr.parent.region;
38286 Roo.log("got nest?");
38288 if (reg.mgr.getRegion('west')) {
38289 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38290 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38291 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38292 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38293 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38301 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38302 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38303 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38304 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38309 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38312 // finally - if tabs are at the top, then create the body last..
38313 if(this.tabPosition != "bottom"){
38314 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38315 * @type Roo.Element
38317 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38318 this.el.addClass("roo-tabs-top");
38322 this.bodyEl.setStyle("position", "relative");
38324 this.active = null;
38325 this.activateDelegate = this.activate.createDelegate(this);
38330 * Fires when the active tab changes
38331 * @param {Roo.TabPanel} this
38332 * @param {Roo.TabPanelItem} activePanel The new active tab
38336 * @event beforetabchange
38337 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38338 * @param {Roo.TabPanel} this
38339 * @param {Object} e Set cancel to true on this object to cancel the tab change
38340 * @param {Roo.TabPanelItem} tab The tab being changed to
38342 "beforetabchange" : true
38345 Roo.EventManager.onWindowResize(this.onResize, this);
38346 this.cpad = this.el.getPadding("lr");
38347 this.hiddenCount = 0;
38350 // toolbar on the tabbar support...
38351 if (this.toolbar) {
38352 alert("no toolbar support yet");
38353 this.toolbar = false;
38355 var tcfg = this.toolbar;
38356 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
38357 this.toolbar = new Roo.Toolbar(tcfg);
38358 if (Roo.isSafari) {
38359 var tbl = tcfg.container.child('table', true);
38360 tbl.setAttribute('width', '100%');
38368 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38371 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38373 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38375 tabPosition : "top",
38377 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38379 currentTabWidth : 0,
38381 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38385 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38389 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38391 preferredTabWidth : 175,
38393 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38395 resizeTabs : false,
38397 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38399 monitorResize : true,
38401 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
38403 toolbar : false, // set by caller..
38405 region : false, /// set by caller
38407 disableTooltips : true, // not used yet...
38410 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38411 * @param {String} id The id of the div to use <b>or create</b>
38412 * @param {String} text The text for the tab
38413 * @param {String} content (optional) Content to put in the TabPanelItem body
38414 * @param {Boolean} closable (optional) True to create a close icon on the tab
38415 * @return {Roo.TabPanelItem} The created TabPanelItem
38417 addTab : function(id, text, content, closable, tpl)
38419 var item = new Roo.bootstrap.panel.TabItem({
38423 closable : closable,
38426 this.addTabItem(item);
38428 item.setContent(content);
38434 * Returns the {@link Roo.TabPanelItem} with the specified id/index
38435 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38436 * @return {Roo.TabPanelItem}
38438 getTab : function(id){
38439 return this.items[id];
38443 * Hides the {@link Roo.TabPanelItem} with the specified id/index
38444 * @param {String/Number} id The id or index of the TabPanelItem to hide.
38446 hideTab : function(id){
38447 var t = this.items[id];
38450 this.hiddenCount++;
38451 this.autoSizeTabs();
38456 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38457 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38459 unhideTab : function(id){
38460 var t = this.items[id];
38462 t.setHidden(false);
38463 this.hiddenCount--;
38464 this.autoSizeTabs();
38469 * Adds an existing {@link Roo.TabPanelItem}.
38470 * @param {Roo.TabPanelItem} item The TabPanelItem to add
38472 addTabItem : function(item)
38474 this.items[item.id] = item;
38475 this.items.push(item);
38476 this.autoSizeTabs();
38477 // if(this.resizeTabs){
38478 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38479 // this.autoSizeTabs();
38481 // item.autoSize();
38486 * Removes a {@link Roo.TabPanelItem}.
38487 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38489 removeTab : function(id){
38490 var items = this.items;
38491 var tab = items[id];
38492 if(!tab) { return; }
38493 var index = items.indexOf(tab);
38494 if(this.active == tab && items.length > 1){
38495 var newTab = this.getNextAvailable(index);
38500 this.stripEl.dom.removeChild(tab.pnode.dom);
38501 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38502 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38504 items.splice(index, 1);
38505 delete this.items[tab.id];
38506 tab.fireEvent("close", tab);
38507 tab.purgeListeners();
38508 this.autoSizeTabs();
38511 getNextAvailable : function(start){
38512 var items = this.items;
38514 // look for a next tab that will slide over to
38515 // replace the one being removed
38516 while(index < items.length){
38517 var item = items[++index];
38518 if(item && !item.isHidden()){
38522 // if one isn't found select the previous tab (on the left)
38525 var item = items[--index];
38526 if(item && !item.isHidden()){
38534 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38535 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38537 disableTab : function(id){
38538 var tab = this.items[id];
38539 if(tab && this.active != tab){
38545 * Enables a {@link Roo.TabPanelItem} that is disabled.
38546 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38548 enableTab : function(id){
38549 var tab = this.items[id];
38554 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38555 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38556 * @return {Roo.TabPanelItem} The TabPanelItem.
38558 activate : function(id)
38560 //Roo.log('activite:' + id);
38562 var tab = this.items[id];
38566 if(tab == this.active || tab.disabled){
38570 this.fireEvent("beforetabchange", this, e, tab);
38571 if(e.cancel !== true && !tab.disabled){
38573 this.active.hide();
38575 this.active = this.items[id];
38576 this.active.show();
38577 this.fireEvent("tabchange", this, this.active);
38583 * Gets the active {@link Roo.TabPanelItem}.
38584 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38586 getActiveTab : function(){
38587 return this.active;
38591 * Updates the tab body element to fit the height of the container element
38592 * for overflow scrolling
38593 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38595 syncHeight : function(targetHeight){
38596 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38597 var bm = this.bodyEl.getMargins();
38598 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38599 this.bodyEl.setHeight(newHeight);
38603 onResize : function(){
38604 if(this.monitorResize){
38605 this.autoSizeTabs();
38610 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38612 beginUpdate : function(){
38613 this.updating = true;
38617 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38619 endUpdate : function(){
38620 this.updating = false;
38621 this.autoSizeTabs();
38625 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38627 autoSizeTabs : function()
38629 var count = this.items.length;
38630 var vcount = count - this.hiddenCount;
38633 this.stripEl.hide();
38635 this.stripEl.show();
38638 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38643 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38644 var availWidth = Math.floor(w / vcount);
38645 var b = this.stripBody;
38646 if(b.getWidth() > w){
38647 var tabs = this.items;
38648 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38649 if(availWidth < this.minTabWidth){
38650 /*if(!this.sleft){ // incomplete scrolling code
38651 this.createScrollButtons();
38654 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38657 if(this.currentTabWidth < this.preferredTabWidth){
38658 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38664 * Returns the number of tabs in this TabPanel.
38667 getCount : function(){
38668 return this.items.length;
38672 * Resizes all the tabs to the passed width
38673 * @param {Number} The new width
38675 setTabWidth : function(width){
38676 this.currentTabWidth = width;
38677 for(var i = 0, len = this.items.length; i < len; i++) {
38678 if(!this.items[i].isHidden()) {
38679 this.items[i].setWidth(width);
38685 * Destroys this TabPanel
38686 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38688 destroy : function(removeEl){
38689 Roo.EventManager.removeResizeListener(this.onResize, this);
38690 for(var i = 0, len = this.items.length; i < len; i++){
38691 this.items[i].purgeListeners();
38693 if(removeEl === true){
38694 this.el.update("");
38699 createStrip : function(container)
38701 var strip = document.createElement("nav");
38702 strip.className = Roo.bootstrap.version == 4 ?
38703 "navbar-light bg-light" :
38704 "navbar navbar-default"; //"x-tabs-wrap";
38705 container.appendChild(strip);
38709 createStripList : function(strip)
38711 // div wrapper for retard IE
38712 // returns the "tr" element.
38713 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38714 //'<div class="x-tabs-strip-wrap">'+
38715 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38716 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38717 return strip.firstChild; //.firstChild.firstChild.firstChild;
38719 createBody : function(container)
38721 var body = document.createElement("div");
38722 Roo.id(body, "tab-body");
38723 //Roo.fly(body).addClass("x-tabs-body");
38724 Roo.fly(body).addClass("tab-content");
38725 container.appendChild(body);
38728 createItemBody :function(bodyEl, id){
38729 var body = Roo.getDom(id);
38731 body = document.createElement("div");
38734 //Roo.fly(body).addClass("x-tabs-item-body");
38735 Roo.fly(body).addClass("tab-pane");
38736 bodyEl.insertBefore(body, bodyEl.firstChild);
38740 createStripElements : function(stripEl, text, closable, tpl)
38742 var td = document.createElement("li"); // was td..
38743 td.className = 'nav-item';
38745 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38748 stripEl.appendChild(td);
38750 td.className = "x-tabs-closable";
38751 if(!this.closeTpl){
38752 this.closeTpl = new Roo.Template(
38753 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38754 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38755 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38758 var el = this.closeTpl.overwrite(td, {"text": text});
38759 var close = el.getElementsByTagName("div")[0];
38760 var inner = el.getElementsByTagName("em")[0];
38761 return {"el": el, "close": close, "inner": inner};
38764 // not sure what this is..
38765 // if(!this.tabTpl){
38766 //this.tabTpl = new Roo.Template(
38767 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38768 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38770 // this.tabTpl = new Roo.Template(
38771 // '<a href="#">' +
38772 // '<span unselectable="on"' +
38773 // (this.disableTooltips ? '' : ' title="{text}"') +
38774 // ' >{text}</span></a>'
38780 var template = tpl || this.tabTpl || false;
38783 template = new Roo.Template(
38784 Roo.bootstrap.version == 4 ?
38786 '<a class="nav-link" href="#" unselectable="on"' +
38787 (this.disableTooltips ? '' : ' title="{text}"') +
38790 '<a class="nav-link" href="#">' +
38791 '<span unselectable="on"' +
38792 (this.disableTooltips ? '' : ' title="{text}"') +
38793 ' >{text}</span></a>'
38798 switch (typeof(template)) {
38802 template = new Roo.Template(template);
38808 var el = template.overwrite(td, {"text": text});
38810 var inner = el.getElementsByTagName("span")[0];
38812 return {"el": el, "inner": inner};
38820 * @class Roo.TabPanelItem
38821 * @extends Roo.util.Observable
38822 * Represents an individual item (tab plus body) in a TabPanel.
38823 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38824 * @param {String} id The id of this TabPanelItem
38825 * @param {String} text The text for the tab of this TabPanelItem
38826 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38828 Roo.bootstrap.panel.TabItem = function(config){
38830 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38831 * @type Roo.TabPanel
38833 this.tabPanel = config.panel;
38835 * The id for this TabPanelItem
38838 this.id = config.id;
38840 this.disabled = false;
38842 this.text = config.text;
38844 this.loaded = false;
38845 this.closable = config.closable;
38848 * The body element for this TabPanelItem.
38849 * @type Roo.Element
38851 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38852 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38853 this.bodyEl.setStyle("display", "block");
38854 this.bodyEl.setStyle("zoom", "1");
38855 //this.hideAction();
38857 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38859 this.el = Roo.get(els.el);
38860 this.inner = Roo.get(els.inner, true);
38861 this.textEl = Roo.bootstrap.version == 4 ?
38862 this.el : Roo.get(this.el.dom.firstChild, true);
38864 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38865 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38868 // this.el.on("mousedown", this.onTabMouseDown, this);
38869 this.el.on("click", this.onTabClick, this);
38871 if(config.closable){
38872 var c = Roo.get(els.close, true);
38873 c.dom.title = this.closeText;
38874 c.addClassOnOver("close-over");
38875 c.on("click", this.closeClick, this);
38881 * Fires when this tab becomes the active tab.
38882 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38883 * @param {Roo.TabPanelItem} this
38887 * @event beforeclose
38888 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38889 * @param {Roo.TabPanelItem} this
38890 * @param {Object} e Set cancel to true on this object to cancel the close.
38892 "beforeclose": true,
38895 * Fires when this tab is closed.
38896 * @param {Roo.TabPanelItem} this
38900 * @event deactivate
38901 * Fires when this tab is no longer the active tab.
38902 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38903 * @param {Roo.TabPanelItem} this
38905 "deactivate" : true
38907 this.hidden = false;
38909 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38912 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38914 purgeListeners : function(){
38915 Roo.util.Observable.prototype.purgeListeners.call(this);
38916 this.el.removeAllListeners();
38919 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38922 this.status_node.addClass("active");
38925 this.tabPanel.stripWrap.repaint();
38927 this.fireEvent("activate", this.tabPanel, this);
38931 * Returns true if this tab is the active tab.
38932 * @return {Boolean}
38934 isActive : function(){
38935 return this.tabPanel.getActiveTab() == this;
38939 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38942 this.status_node.removeClass("active");
38944 this.fireEvent("deactivate", this.tabPanel, this);
38947 hideAction : function(){
38948 this.bodyEl.hide();
38949 this.bodyEl.setStyle("position", "absolute");
38950 this.bodyEl.setLeft("-20000px");
38951 this.bodyEl.setTop("-20000px");
38954 showAction : function(){
38955 this.bodyEl.setStyle("position", "relative");
38956 this.bodyEl.setTop("");
38957 this.bodyEl.setLeft("");
38958 this.bodyEl.show();
38962 * Set the tooltip for the tab.
38963 * @param {String} tooltip The tab's tooltip
38965 setTooltip : function(text){
38966 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38967 this.textEl.dom.qtip = text;
38968 this.textEl.dom.removeAttribute('title');
38970 this.textEl.dom.title = text;
38974 onTabClick : function(e){
38975 e.preventDefault();
38976 this.tabPanel.activate(this.id);
38979 onTabMouseDown : function(e){
38980 e.preventDefault();
38981 this.tabPanel.activate(this.id);
38984 getWidth : function(){
38985 return this.inner.getWidth();
38988 setWidth : function(width){
38989 var iwidth = width - this.linode.getPadding("lr");
38990 this.inner.setWidth(iwidth);
38991 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38992 this.linode.setWidth(width);
38996 * Show or hide the tab
38997 * @param {Boolean} hidden True to hide or false to show.
38999 setHidden : function(hidden){
39000 this.hidden = hidden;
39001 this.linode.setStyle("display", hidden ? "none" : "");
39005 * Returns true if this tab is "hidden"
39006 * @return {Boolean}
39008 isHidden : function(){
39009 return this.hidden;
39013 * Returns the text for this tab
39016 getText : function(){
39020 autoSize : function(){
39021 //this.el.beginMeasure();
39022 this.textEl.setWidth(1);
39024 * #2804 [new] Tabs in Roojs
39025 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
39027 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
39028 //this.el.endMeasure();
39032 * Sets the text for the tab (Note: this also sets the tooltip text)
39033 * @param {String} text The tab's text and tooltip
39035 setText : function(text){
39037 this.textEl.update(text);
39038 this.setTooltip(text);
39039 //if(!this.tabPanel.resizeTabs){
39040 // this.autoSize();
39044 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39046 activate : function(){
39047 this.tabPanel.activate(this.id);
39051 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
39053 disable : function(){
39054 if(this.tabPanel.active != this){
39055 this.disabled = true;
39056 this.status_node.addClass("disabled");
39061 * Enables this TabPanelItem if it was previously disabled.
39063 enable : function(){
39064 this.disabled = false;
39065 this.status_node.removeClass("disabled");
39069 * Sets the content for this TabPanelItem.
39070 * @param {String} content The content
39071 * @param {Boolean} loadScripts true to look for and load scripts
39073 setContent : function(content, loadScripts){
39074 this.bodyEl.update(content, loadScripts);
39078 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
39079 * @return {Roo.UpdateManager} The UpdateManager
39081 getUpdateManager : function(){
39082 return this.bodyEl.getUpdateManager();
39086 * Set a URL to be used to load the content for this TabPanelItem.
39087 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
39088 * @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)
39089 * @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)
39090 * @return {Roo.UpdateManager} The UpdateManager
39092 setUrl : function(url, params, loadOnce){
39093 if(this.refreshDelegate){
39094 this.un('activate', this.refreshDelegate);
39096 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39097 this.on("activate", this.refreshDelegate);
39098 return this.bodyEl.getUpdateManager();
39102 _handleRefresh : function(url, params, loadOnce){
39103 if(!loadOnce || !this.loaded){
39104 var updater = this.bodyEl.getUpdateManager();
39105 updater.update(url, params, this._setLoaded.createDelegate(this));
39110 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
39111 * Will fail silently if the setUrl method has not been called.
39112 * This does not activate the panel, just updates its content.
39114 refresh : function(){
39115 if(this.refreshDelegate){
39116 this.loaded = false;
39117 this.refreshDelegate();
39122 _setLoaded : function(){
39123 this.loaded = true;
39127 closeClick : function(e){
39130 this.fireEvent("beforeclose", this, o);
39131 if(o.cancel !== true){
39132 this.tabPanel.removeTab(this.id);
39136 * The text displayed in the tooltip for the close icon.
39139 closeText : "Close this tab"
39142 * This script refer to:
39143 * Title: International Telephone Input
39144 * Author: Jack O'Connor
39145 * Code version: v12.1.12
39146 * Availability: https://github.com/jackocnr/intl-tel-input.git
39149 Roo.bootstrap.PhoneInputData = function() {
39152 "Afghanistan (افغانستان)",
39157 "Albania (Shqipëri)",
39162 "Algeria (الجزائر)",
39187 "Antigua and Barbuda",
39197 "Armenia (Հայաստան)",
39213 "Austria (Österreich)",
39218 "Azerbaijan (Azərbaycan)",
39228 "Bahrain (البحرين)",
39233 "Bangladesh (বাংলাদেশ)",
39243 "Belarus (Беларусь)",
39248 "Belgium (België)",
39278 "Bosnia and Herzegovina (Босна и Херцеговина)",
39293 "British Indian Ocean Territory",
39298 "British Virgin Islands",
39308 "Bulgaria (България)",
39318 "Burundi (Uburundi)",
39323 "Cambodia (កម្ពុជា)",
39328 "Cameroon (Cameroun)",
39337 ["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"]
39340 "Cape Verde (Kabu Verdi)",
39345 "Caribbean Netherlands",
39356 "Central African Republic (République centrafricaine)",
39376 "Christmas Island",
39382 "Cocos (Keeling) Islands",
39393 "Comoros (جزر القمر)",
39398 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39403 "Congo (Republic) (Congo-Brazzaville)",
39423 "Croatia (Hrvatska)",
39444 "Czech Republic (Česká republika)",
39449 "Denmark (Danmark)",
39464 "Dominican Republic (República Dominicana)",
39468 ["809", "829", "849"]
39486 "Equatorial Guinea (Guinea Ecuatorial)",
39506 "Falkland Islands (Islas Malvinas)",
39511 "Faroe Islands (Føroyar)",
39532 "French Guiana (Guyane française)",
39537 "French Polynesia (Polynésie française)",
39552 "Georgia (საქართველო)",
39557 "Germany (Deutschland)",
39577 "Greenland (Kalaallit Nunaat)",
39614 "Guinea-Bissau (Guiné Bissau)",
39639 "Hungary (Magyarország)",
39644 "Iceland (Ísland)",
39664 "Iraq (العراق)",
39680 "Israel (ישראל)",
39707 "Jordan (الأردن)",
39712 "Kazakhstan (Казахстан)",
39733 "Kuwait (الكويت)",
39738 "Kyrgyzstan (Кыргызстан)",
39748 "Latvia (Latvija)",
39753 "Lebanon (لبنان)",
39768 "Libya (ليبيا)",
39778 "Lithuania (Lietuva)",
39793 "Macedonia (FYROM) (Македонија)",
39798 "Madagascar (Madagasikara)",
39828 "Marshall Islands",
39838 "Mauritania (موريتانيا)",
39843 "Mauritius (Moris)",
39864 "Moldova (Republica Moldova)",
39874 "Mongolia (Монгол)",
39879 "Montenegro (Crna Gora)",
39889 "Morocco (المغرب)",
39895 "Mozambique (Moçambique)",
39900 "Myanmar (Burma) (မြန်မာ)",
39905 "Namibia (Namibië)",
39920 "Netherlands (Nederland)",
39925 "New Caledonia (Nouvelle-Calédonie)",
39960 "North Korea (조선 민주주의 인민 공화국)",
39965 "Northern Mariana Islands",
39981 "Pakistan (پاکستان)",
39991 "Palestine (فلسطين)",
40001 "Papua New Guinea",
40043 "Réunion (La Réunion)",
40049 "Romania (România)",
40065 "Saint Barthélemy",
40076 "Saint Kitts and Nevis",
40086 "Saint Martin (Saint-Martin (partie française))",
40092 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
40097 "Saint Vincent and the Grenadines",
40112 "São Tomé and Príncipe (São Tomé e Príncipe)",
40117 "Saudi Arabia (المملكة العربية السعودية)",
40122 "Senegal (Sénégal)",
40152 "Slovakia (Slovensko)",
40157 "Slovenia (Slovenija)",
40167 "Somalia (Soomaaliya)",
40177 "South Korea (대한민국)",
40182 "South Sudan (جنوب السودان)",
40192 "Sri Lanka (ශ්රී ලංකාව)",
40197 "Sudan (السودان)",
40207 "Svalbard and Jan Mayen",
40218 "Sweden (Sverige)",
40223 "Switzerland (Schweiz)",
40228 "Syria (سوريا)",
40273 "Trinidad and Tobago",
40278 "Tunisia (تونس)",
40283 "Turkey (Türkiye)",
40293 "Turks and Caicos Islands",
40303 "U.S. Virgin Islands",
40313 "Ukraine (Україна)",
40318 "United Arab Emirates (الإمارات العربية المتحدة)",
40340 "Uzbekistan (Oʻzbekiston)",
40350 "Vatican City (Città del Vaticano)",
40361 "Vietnam (Việt Nam)",
40366 "Wallis and Futuna (Wallis-et-Futuna)",
40371 "Western Sahara (الصحراء الغربية)",
40377 "Yemen (اليمن)",
40401 * This script refer to:
40402 * Title: International Telephone Input
40403 * Author: Jack O'Connor
40404 * Code version: v12.1.12
40405 * Availability: https://github.com/jackocnr/intl-tel-input.git
40409 * @class Roo.bootstrap.PhoneInput
40410 * @extends Roo.bootstrap.TriggerField
40411 * An input with International dial-code selection
40413 * @cfg {String} defaultDialCode default '+852'
40414 * @cfg {Array} preferedCountries default []
40417 * Create a new PhoneInput.
40418 * @param {Object} config Configuration options
40421 Roo.bootstrap.PhoneInput = function(config) {
40422 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40425 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40427 listWidth: undefined,
40429 selectedClass: 'active',
40431 invalidClass : "has-warning",
40433 validClass: 'has-success',
40435 allowed: '0123456789',
40440 * @cfg {String} defaultDialCode The default dial code when initializing the input
40442 defaultDialCode: '+852',
40445 * @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
40447 preferedCountries: false,
40449 getAutoCreate : function()
40451 var data = Roo.bootstrap.PhoneInputData();
40452 var align = this.labelAlign || this.parentLabelAlign();
40455 this.allCountries = [];
40456 this.dialCodeMapping = [];
40458 for (var i = 0; i < data.length; i++) {
40460 this.allCountries[i] = {
40464 priority: c[3] || 0,
40465 areaCodes: c[4] || null
40467 this.dialCodeMapping[c[2]] = {
40470 priority: c[3] || 0,
40471 areaCodes: c[4] || null
40483 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40484 maxlength: this.max_length,
40485 cls : 'form-control tel-input',
40486 autocomplete: 'new-password'
40489 var hiddenInput = {
40492 cls: 'hidden-tel-input'
40496 hiddenInput.name = this.name;
40499 if (this.disabled) {
40500 input.disabled = true;
40503 var flag_container = {
40520 cls: this.hasFeedback ? 'has-feedback' : '',
40526 cls: 'dial-code-holder',
40533 cls: 'roo-select2-container input-group',
40540 if (this.fieldLabel.length) {
40543 tooltip: 'This field is required'
40549 cls: 'control-label',
40555 html: this.fieldLabel
40558 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40564 if(this.indicatorpos == 'right') {
40565 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40572 if(align == 'left') {
40580 if(this.labelWidth > 12){
40581 label.style = "width: " + this.labelWidth + 'px';
40583 if(this.labelWidth < 13 && this.labelmd == 0){
40584 this.labelmd = this.labelWidth;
40586 if(this.labellg > 0){
40587 label.cls += ' col-lg-' + this.labellg;
40588 input.cls += ' col-lg-' + (12 - this.labellg);
40590 if(this.labelmd > 0){
40591 label.cls += ' col-md-' + this.labelmd;
40592 container.cls += ' col-md-' + (12 - this.labelmd);
40594 if(this.labelsm > 0){
40595 label.cls += ' col-sm-' + this.labelsm;
40596 container.cls += ' col-sm-' + (12 - this.labelsm);
40598 if(this.labelxs > 0){
40599 label.cls += ' col-xs-' + this.labelxs;
40600 container.cls += ' col-xs-' + (12 - this.labelxs);
40610 var settings = this;
40612 ['xs','sm','md','lg'].map(function(size){
40613 if (settings[size]) {
40614 cfg.cls += ' col-' + size + '-' + settings[size];
40618 this.store = new Roo.data.Store({
40619 proxy : new Roo.data.MemoryProxy({}),
40620 reader : new Roo.data.JsonReader({
40631 'name' : 'dialCode',
40635 'name' : 'priority',
40639 'name' : 'areaCodes',
40646 if(!this.preferedCountries) {
40647 this.preferedCountries = [
40654 var p = this.preferedCountries.reverse();
40657 for (var i = 0; i < p.length; i++) {
40658 for (var j = 0; j < this.allCountries.length; j++) {
40659 if(this.allCountries[j].iso2 == p[i]) {
40660 var t = this.allCountries[j];
40661 this.allCountries.splice(j,1);
40662 this.allCountries.unshift(t);
40668 this.store.proxy.data = {
40670 data: this.allCountries
40676 initEvents : function()
40679 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40681 this.indicator = this.indicatorEl();
40682 this.flag = this.flagEl();
40683 this.dialCodeHolder = this.dialCodeHolderEl();
40685 this.trigger = this.el.select('div.flag-box',true).first();
40686 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40691 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40692 _this.list.setWidth(lw);
40695 this.list.on('mouseover', this.onViewOver, this);
40696 this.list.on('mousemove', this.onViewMove, this);
40697 this.inputEl().on("keyup", this.onKeyUp, this);
40698 this.inputEl().on("keypress", this.onKeyPress, this);
40700 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40702 this.view = new Roo.View(this.list, this.tpl, {
40703 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40706 this.view.on('click', this.onViewClick, this);
40707 this.setValue(this.defaultDialCode);
40710 onTriggerClick : function(e)
40712 Roo.log('trigger click');
40717 if(this.isExpanded()){
40719 this.hasFocus = false;
40721 this.store.load({});
40722 this.hasFocus = true;
40727 isExpanded : function()
40729 return this.list.isVisible();
40732 collapse : function()
40734 if(!this.isExpanded()){
40738 Roo.get(document).un('mousedown', this.collapseIf, this);
40739 Roo.get(document).un('mousewheel', this.collapseIf, this);
40740 this.fireEvent('collapse', this);
40744 expand : function()
40748 if(this.isExpanded() || !this.hasFocus){
40752 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40753 this.list.setWidth(lw);
40756 this.restrictHeight();
40758 Roo.get(document).on('mousedown', this.collapseIf, this);
40759 Roo.get(document).on('mousewheel', this.collapseIf, this);
40761 this.fireEvent('expand', this);
40764 restrictHeight : function()
40766 this.list.alignTo(this.inputEl(), this.listAlign);
40767 this.list.alignTo(this.inputEl(), this.listAlign);
40770 onViewOver : function(e, t)
40772 if(this.inKeyMode){
40775 var item = this.view.findItemFromChild(t);
40778 var index = this.view.indexOf(item);
40779 this.select(index, false);
40784 onViewClick : function(view, doFocus, el, e)
40786 var index = this.view.getSelectedIndexes()[0];
40788 var r = this.store.getAt(index);
40791 this.onSelect(r, index);
40793 if(doFocus !== false && !this.blockFocus){
40794 this.inputEl().focus();
40798 onViewMove : function(e, t)
40800 this.inKeyMode = false;
40803 select : function(index, scrollIntoView)
40805 this.selectedIndex = index;
40806 this.view.select(index);
40807 if(scrollIntoView !== false){
40808 var el = this.view.getNode(index);
40810 this.list.scrollChildIntoView(el, false);
40815 createList : function()
40817 this.list = Roo.get(document.body).createChild({
40819 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40820 style: 'display:none'
40823 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40826 collapseIf : function(e)
40828 var in_combo = e.within(this.el);
40829 var in_list = e.within(this.list);
40830 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40832 if (in_combo || in_list || is_list) {
40838 onSelect : function(record, index)
40840 if(this.fireEvent('beforeselect', this, record, index) !== false){
40842 this.setFlagClass(record.data.iso2);
40843 this.setDialCode(record.data.dialCode);
40844 this.hasFocus = false;
40846 this.fireEvent('select', this, record, index);
40850 flagEl : function()
40852 var flag = this.el.select('div.flag',true).first();
40859 dialCodeHolderEl : function()
40861 var d = this.el.select('input.dial-code-holder',true).first();
40868 setDialCode : function(v)
40870 this.dialCodeHolder.dom.value = '+'+v;
40873 setFlagClass : function(n)
40875 this.flag.dom.className = 'flag '+n;
40878 getValue : function()
40880 var v = this.inputEl().getValue();
40881 if(this.dialCodeHolder) {
40882 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40887 setValue : function(v)
40889 var d = this.getDialCode(v);
40891 //invalid dial code
40892 if(v.length == 0 || !d || d.length == 0) {
40894 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40895 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40901 this.setFlagClass(this.dialCodeMapping[d].iso2);
40902 this.setDialCode(d);
40903 this.inputEl().dom.value = v.replace('+'+d,'');
40904 this.hiddenEl().dom.value = this.getValue();
40909 getDialCode : function(v)
40913 if (v.length == 0) {
40914 return this.dialCodeHolder.dom.value;
40918 if (v.charAt(0) != "+") {
40921 var numericChars = "";
40922 for (var i = 1; i < v.length; i++) {
40923 var c = v.charAt(i);
40926 if (this.dialCodeMapping[numericChars]) {
40927 dialCode = v.substr(1, i);
40929 if (numericChars.length == 4) {
40939 this.setValue(this.defaultDialCode);
40943 hiddenEl : function()
40945 return this.el.select('input.hidden-tel-input',true).first();
40948 // after setting val
40949 onKeyUp : function(e){
40950 this.setValue(this.getValue());
40953 onKeyPress : function(e){
40954 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40961 * @class Roo.bootstrap.MoneyField
40962 * @extends Roo.bootstrap.ComboBox
40963 * Bootstrap MoneyField class
40966 * Create a new MoneyField.
40967 * @param {Object} config Configuration options
40970 Roo.bootstrap.MoneyField = function(config) {
40972 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40976 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40979 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40981 allowDecimals : true,
40983 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40985 decimalSeparator : ".",
40987 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40989 decimalPrecision : 0,
40991 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40993 allowNegative : true,
40995 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40999 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41001 minValue : Number.NEGATIVE_INFINITY,
41003 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41005 maxValue : Number.MAX_VALUE,
41007 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41009 minText : "The minimum value for this field is {0}",
41011 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41013 maxText : "The maximum value for this field is {0}",
41015 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
41016 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41018 nanText : "{0} is not a valid number",
41020 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
41024 * @cfg {String} defaults currency of the MoneyField
41025 * value should be in lkey
41027 defaultCurrency : false,
41029 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41031 thousandsDelimiter : false,
41033 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41044 getAutoCreate : function()
41046 var align = this.labelAlign || this.parentLabelAlign();
41058 cls : 'form-control roo-money-amount-input',
41059 autocomplete: 'new-password'
41062 var hiddenInput = {
41066 cls: 'hidden-number-input'
41069 if(this.max_length) {
41070 input.maxlength = this.max_length;
41074 hiddenInput.name = this.name;
41077 if (this.disabled) {
41078 input.disabled = true;
41081 var clg = 12 - this.inputlg;
41082 var cmd = 12 - this.inputmd;
41083 var csm = 12 - this.inputsm;
41084 var cxs = 12 - this.inputxs;
41088 cls : 'row roo-money-field',
41092 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
41096 cls: 'roo-select2-container input-group',
41100 cls : 'form-control roo-money-currency-input',
41101 autocomplete: 'new-password',
41103 name : this.currencyName
41107 cls : 'input-group-addon',
41121 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41125 cls: this.hasFeedback ? 'has-feedback' : '',
41136 if (this.fieldLabel.length) {
41139 tooltip: 'This field is required'
41145 cls: 'control-label',
41151 html: this.fieldLabel
41154 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41160 if(this.indicatorpos == 'right') {
41161 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41168 if(align == 'left') {
41176 if(this.labelWidth > 12){
41177 label.style = "width: " + this.labelWidth + 'px';
41179 if(this.labelWidth < 13 && this.labelmd == 0){
41180 this.labelmd = this.labelWidth;
41182 if(this.labellg > 0){
41183 label.cls += ' col-lg-' + this.labellg;
41184 input.cls += ' col-lg-' + (12 - this.labellg);
41186 if(this.labelmd > 0){
41187 label.cls += ' col-md-' + this.labelmd;
41188 container.cls += ' col-md-' + (12 - this.labelmd);
41190 if(this.labelsm > 0){
41191 label.cls += ' col-sm-' + this.labelsm;
41192 container.cls += ' col-sm-' + (12 - this.labelsm);
41194 if(this.labelxs > 0){
41195 label.cls += ' col-xs-' + this.labelxs;
41196 container.cls += ' col-xs-' + (12 - this.labelxs);
41207 var settings = this;
41209 ['xs','sm','md','lg'].map(function(size){
41210 if (settings[size]) {
41211 cfg.cls += ' col-' + size + '-' + settings[size];
41218 initEvents : function()
41220 this.indicator = this.indicatorEl();
41222 this.initCurrencyEvent();
41224 this.initNumberEvent();
41227 initCurrencyEvent : function()
41230 throw "can not find store for combo";
41233 this.store = Roo.factory(this.store, Roo.data);
41234 this.store.parent = this;
41238 this.triggerEl = this.el.select('.input-group-addon', true).first();
41240 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41245 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41246 _this.list.setWidth(lw);
41249 this.list.on('mouseover', this.onViewOver, this);
41250 this.list.on('mousemove', this.onViewMove, this);
41251 this.list.on('scroll', this.onViewScroll, this);
41254 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41257 this.view = new Roo.View(this.list, this.tpl, {
41258 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41261 this.view.on('click', this.onViewClick, this);
41263 this.store.on('beforeload', this.onBeforeLoad, this);
41264 this.store.on('load', this.onLoad, this);
41265 this.store.on('loadexception', this.onLoadException, this);
41267 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41268 "up" : function(e){
41269 this.inKeyMode = true;
41273 "down" : function(e){
41274 if(!this.isExpanded()){
41275 this.onTriggerClick();
41277 this.inKeyMode = true;
41282 "enter" : function(e){
41285 if(this.fireEvent("specialkey", this, e)){
41286 this.onViewClick(false);
41292 "esc" : function(e){
41296 "tab" : function(e){
41299 if(this.fireEvent("specialkey", this, e)){
41300 this.onViewClick(false);
41308 doRelay : function(foo, bar, hname){
41309 if(hname == 'down' || this.scope.isExpanded()){
41310 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41318 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41322 initNumberEvent : function(e)
41324 this.inputEl().on("keydown" , this.fireKey, this);
41325 this.inputEl().on("focus", this.onFocus, this);
41326 this.inputEl().on("blur", this.onBlur, this);
41328 this.inputEl().relayEvent('keyup', this);
41330 if(this.indicator){
41331 this.indicator.addClass('invisible');
41334 this.originalValue = this.getValue();
41336 if(this.validationEvent == 'keyup'){
41337 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41338 this.inputEl().on('keyup', this.filterValidation, this);
41340 else if(this.validationEvent !== false){
41341 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41344 if(this.selectOnFocus){
41345 this.on("focus", this.preFocus, this);
41348 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41349 this.inputEl().on("keypress", this.filterKeys, this);
41351 this.inputEl().relayEvent('keypress', this);
41354 var allowed = "0123456789";
41356 if(this.allowDecimals){
41357 allowed += this.decimalSeparator;
41360 if(this.allowNegative){
41364 if(this.thousandsDelimiter) {
41368 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41370 var keyPress = function(e){
41372 var k = e.getKey();
41374 var c = e.getCharCode();
41377 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41378 allowed.indexOf(String.fromCharCode(c)) === -1
41384 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41388 if(allowed.indexOf(String.fromCharCode(c)) === -1){
41393 this.inputEl().on("keypress", keyPress, this);
41397 onTriggerClick : function(e)
41404 this.loadNext = false;
41406 if(this.isExpanded()){
41411 this.hasFocus = true;
41413 if(this.triggerAction == 'all') {
41414 this.doQuery(this.allQuery, true);
41418 this.doQuery(this.getRawValue());
41421 getCurrency : function()
41423 var v = this.currencyEl().getValue();
41428 restrictHeight : function()
41430 this.list.alignTo(this.currencyEl(), this.listAlign);
41431 this.list.alignTo(this.currencyEl(), this.listAlign);
41434 onViewClick : function(view, doFocus, el, e)
41436 var index = this.view.getSelectedIndexes()[0];
41438 var r = this.store.getAt(index);
41441 this.onSelect(r, index);
41445 onSelect : function(record, index){
41447 if(this.fireEvent('beforeselect', this, record, index) !== false){
41449 this.setFromCurrencyData(index > -1 ? record.data : false);
41453 this.fireEvent('select', this, record, index);
41457 setFromCurrencyData : function(o)
41461 this.lastCurrency = o;
41463 if (this.currencyField) {
41464 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41466 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
41469 this.lastSelectionText = currency;
41471 //setting default currency
41472 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41473 this.setCurrency(this.defaultCurrency);
41477 this.setCurrency(currency);
41480 setFromData : function(o)
41484 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41486 this.setFromCurrencyData(c);
41491 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41493 Roo.log('no value set for '+ (this.name ? this.name : this.id));
41496 this.setValue(value);
41500 setCurrency : function(v)
41502 this.currencyValue = v;
41505 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41510 setValue : function(v)
41512 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41518 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41520 this.inputEl().dom.value = (v == '') ? '' :
41521 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41523 if(!this.allowZero && v === '0') {
41524 this.hiddenEl().dom.value = '';
41525 this.inputEl().dom.value = '';
41532 getRawValue : function()
41534 var v = this.inputEl().getValue();
41539 getValue : function()
41541 return this.fixPrecision(this.parseValue(this.getRawValue()));
41544 parseValue : function(value)
41546 if(this.thousandsDelimiter) {
41548 r = new RegExp(",", "g");
41549 value = value.replace(r, "");
41552 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41553 return isNaN(value) ? '' : value;
41557 fixPrecision : function(value)
41559 if(this.thousandsDelimiter) {
41561 r = new RegExp(",", "g");
41562 value = value.replace(r, "");
41565 var nan = isNaN(value);
41567 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41568 return nan ? '' : value;
41570 return parseFloat(value).toFixed(this.decimalPrecision);
41573 decimalPrecisionFcn : function(v)
41575 return Math.floor(v);
41578 validateValue : function(value)
41580 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41584 var num = this.parseValue(value);
41587 this.markInvalid(String.format(this.nanText, value));
41591 if(num < this.minValue){
41592 this.markInvalid(String.format(this.minText, this.minValue));
41596 if(num > this.maxValue){
41597 this.markInvalid(String.format(this.maxText, this.maxValue));
41604 validate : function()
41606 if(this.disabled || this.allowBlank){
41611 var currency = this.getCurrency();
41613 if(this.validateValue(this.getRawValue()) && currency.length){
41618 this.markInvalid();
41622 getName: function()
41627 beforeBlur : function()
41633 var v = this.parseValue(this.getRawValue());
41640 onBlur : function()
41644 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41645 //this.el.removeClass(this.focusClass);
41648 this.hasFocus = false;
41650 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41654 var v = this.getValue();
41656 if(String(v) !== String(this.startValue)){
41657 this.fireEvent('change', this, v, this.startValue);
41660 this.fireEvent("blur", this);
41663 inputEl : function()
41665 return this.el.select('.roo-money-amount-input', true).first();
41668 currencyEl : function()
41670 return this.el.select('.roo-money-currency-input', true).first();
41673 hiddenEl : function()
41675 return this.el.select('input.hidden-number-input',true).first();
41679 * @class Roo.bootstrap.BezierSignature
41680 * @extends Roo.bootstrap.Component
41681 * Bootstrap BezierSignature class
41682 * This script refer to:
41683 * Title: Signature Pad
41685 * Availability: https://github.com/szimek/signature_pad
41688 * Create a new BezierSignature
41689 * @param {Object} config The config object
41692 Roo.bootstrap.BezierSignature = function(config){
41693 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41699 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
41706 mouse_btn_down: true,
41709 * @cfg {int} canvas height
41711 canvas_height: '200px',
41714 * @cfg {float|function} Radius of a single dot.
41719 * @cfg {float} Minimum width of a line. Defaults to 0.5.
41724 * @cfg {float} Maximum width of a line. Defaults to 2.5.
41729 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41734 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41739 * @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.
41741 bg_color: 'rgba(0, 0, 0, 0)',
41744 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41746 dot_color: 'black',
41749 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41751 velocity_filter_weight: 0.7,
41754 * @cfg {function} Callback when stroke begin.
41759 * @cfg {function} Callback when stroke end.
41763 getAutoCreate : function()
41765 var cls = 'roo-signature column';
41768 cls += ' ' + this.cls;
41778 for(var i = 0; i < col_sizes.length; i++) {
41779 if(this[col_sizes[i]]) {
41780 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
41790 cls: 'roo-signature-body',
41794 cls: 'roo-signature-body-canvas',
41795 height: this.canvas_height,
41796 width: this.canvas_width
41803 style: 'display: none'
41811 initEvents: function()
41813 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
41815 var canvas = this.canvasEl();
41817 // mouse && touch event swapping...
41818 canvas.dom.style.touchAction = 'none';
41819 canvas.dom.style.msTouchAction = 'none';
41821 this.mouse_btn_down = false;
41822 canvas.on('mousedown', this._handleMouseDown, this);
41823 canvas.on('mousemove', this._handleMouseMove, this);
41824 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
41826 if (window.PointerEvent) {
41827 canvas.on('pointerdown', this._handleMouseDown, this);
41828 canvas.on('pointermove', this._handleMouseMove, this);
41829 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
41832 if ('ontouchstart' in window) {
41833 canvas.on('touchstart', this._handleTouchStart, this);
41834 canvas.on('touchmove', this._handleTouchMove, this);
41835 canvas.on('touchend', this._handleTouchEnd, this);
41838 Roo.EventManager.onWindowResize(this.resize, this, true);
41840 // file input event
41841 this.fileEl().on('change', this.uploadImage, this);
41848 resize: function(){
41850 var canvas = this.canvasEl().dom;
41851 var ctx = this.canvasElCtx();
41852 var img_data = false;
41854 if(canvas.width > 0) {
41855 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
41857 // setting canvas width will clean img data
41860 var style = window.getComputedStyle ?
41861 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
41863 var padding_left = parseInt(style.paddingLeft) || 0;
41864 var padding_right = parseInt(style.paddingRight) || 0;
41866 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
41869 ctx.putImageData(img_data, 0, 0);
41873 _handleMouseDown: function(e)
41875 if (e.browserEvent.which === 1) {
41876 this.mouse_btn_down = true;
41877 this.strokeBegin(e);
41881 _handleMouseMove: function (e)
41883 if (this.mouse_btn_down) {
41884 this.strokeMoveUpdate(e);
41888 _handleMouseUp: function (e)
41890 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
41891 this.mouse_btn_down = false;
41896 _handleTouchStart: function (e) {
41898 e.preventDefault();
41899 if (e.browserEvent.targetTouches.length === 1) {
41900 // var touch = e.browserEvent.changedTouches[0];
41901 // this.strokeBegin(touch);
41903 this.strokeBegin(e); // assume e catching the correct xy...
41907 _handleTouchMove: function (e) {
41908 e.preventDefault();
41909 // var touch = event.targetTouches[0];
41910 // _this._strokeMoveUpdate(touch);
41911 this.strokeMoveUpdate(e);
41914 _handleTouchEnd: function (e) {
41915 var wasCanvasTouched = e.target === this.canvasEl().dom;
41916 if (wasCanvasTouched) {
41917 e.preventDefault();
41918 // var touch = event.changedTouches[0];
41919 // _this._strokeEnd(touch);
41924 reset: function () {
41925 this._lastPoints = [];
41926 this._lastVelocity = 0;
41927 this._lastWidth = (this.min_width + this.max_width) / 2;
41928 this.canvasElCtx().fillStyle = this.dot_color;
41931 strokeMoveUpdate: function(e)
41933 this.strokeUpdate(e);
41935 if (this.throttle) {
41936 this.throttleStroke(this.strokeUpdate, this.throttle);
41939 this.strokeUpdate(e);
41943 strokeBegin: function(e)
41945 var newPointGroup = {
41946 color: this.dot_color,
41950 if (typeof this.onBegin === 'function') {
41954 this.curve_data.push(newPointGroup);
41956 this.strokeUpdate(e);
41959 strokeUpdate: function(e)
41961 var rect = this.canvasEl().dom.getBoundingClientRect();
41962 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
41963 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
41964 var lastPoints = lastPointGroup.points;
41965 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
41966 var isLastPointTooClose = lastPoint
41967 ? point.distanceTo(lastPoint) <= this.min_distance
41969 var color = lastPointGroup.color;
41970 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
41971 var curve = this.addPoint(point);
41973 this.drawDot({color: color, point: point});
41976 this.drawCurve({color: color, curve: curve});
41986 strokeEnd: function(e)
41988 this.strokeUpdate(e);
41989 if (typeof this.onEnd === 'function') {
41994 addPoint: function (point) {
41995 var _lastPoints = this._lastPoints;
41996 _lastPoints.push(point);
41997 if (_lastPoints.length > 2) {
41998 if (_lastPoints.length === 3) {
41999 _lastPoints.unshift(_lastPoints[0]);
42001 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
42002 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
42003 _lastPoints.shift();
42009 calculateCurveWidths: function (startPoint, endPoint) {
42010 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
42011 (1 - this.velocity_filter_weight) * this._lastVelocity;
42013 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
42016 start: this._lastWidth
42019 this._lastVelocity = velocity;
42020 this._lastWidth = newWidth;
42024 drawDot: function (_a) {
42025 var color = _a.color, point = _a.point;
42026 var ctx = this.canvasElCtx();
42027 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
42029 this.drawCurveSegment(point.x, point.y, width);
42031 ctx.fillStyle = color;
42035 drawCurve: function (_a) {
42036 var color = _a.color, curve = _a.curve;
42037 var ctx = this.canvasElCtx();
42038 var widthDelta = curve.endWidth - curve.startWidth;
42039 var drawSteps = Math.floor(curve.length()) * 2;
42041 ctx.fillStyle = color;
42042 for (var i = 0; i < drawSteps; i += 1) {
42043 var t = i / drawSteps;
42049 var x = uuu * curve.startPoint.x;
42050 x += 3 * uu * t * curve.control1.x;
42051 x += 3 * u * tt * curve.control2.x;
42052 x += ttt * curve.endPoint.x;
42053 var y = uuu * curve.startPoint.y;
42054 y += 3 * uu * t * curve.control1.y;
42055 y += 3 * u * tt * curve.control2.y;
42056 y += ttt * curve.endPoint.y;
42057 var width = curve.startWidth + ttt * widthDelta;
42058 this.drawCurveSegment(x, y, width);
42064 drawCurveSegment: function (x, y, width) {
42065 var ctx = this.canvasElCtx();
42067 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
42068 this.is_empty = false;
42073 var ctx = this.canvasElCtx();
42074 var canvas = this.canvasEl().dom;
42075 ctx.fillStyle = this.bg_color;
42076 ctx.clearRect(0, 0, canvas.width, canvas.height);
42077 ctx.fillRect(0, 0, canvas.width, canvas.height);
42078 this.curve_data = [];
42080 this.is_empty = true;
42085 return this.el.select('input',true).first();
42088 canvasEl: function()
42090 return this.el.select('canvas',true).first();
42093 canvasElCtx: function()
42095 return this.el.select('canvas',true).first().dom.getContext('2d');
42098 getImage: function(type)
42100 if(this.is_empty) {
42105 return this.canvasEl().dom.toDataURL('image/'+type, 1);
42108 drawFromImage: function(img_src)
42110 var img = new Image();
42112 img.onload = function(){
42113 this.canvasElCtx().drawImage(img, 0, 0);
42118 this.is_empty = false;
42121 selectImage: function()
42123 this.fileEl().dom.click();
42126 uploadImage: function(e)
42128 var reader = new FileReader();
42130 reader.onload = function(e){
42131 var img = new Image();
42132 img.onload = function(){
42134 this.canvasElCtx().drawImage(img, 0, 0);
42136 img.src = e.target.result;
42139 reader.readAsDataURL(e.target.files[0]);
42142 // Bezier Point Constructor
42143 Point: (function () {
42144 function Point(x, y, time) {
42147 this.time = time || Date.now();
42149 Point.prototype.distanceTo = function (start) {
42150 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42152 Point.prototype.equals = function (other) {
42153 return this.x === other.x && this.y === other.y && this.time === other.time;
42155 Point.prototype.velocityFrom = function (start) {
42156 return this.time !== start.time
42157 ? this.distanceTo(start) / (this.time - start.time)
42164 // Bezier Constructor
42165 Bezier: (function () {
42166 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42167 this.startPoint = startPoint;
42168 this.control2 = control2;
42169 this.control1 = control1;
42170 this.endPoint = endPoint;
42171 this.startWidth = startWidth;
42172 this.endWidth = endWidth;
42174 Bezier.fromPoints = function (points, widths, scope) {
42175 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42176 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42177 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42179 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42180 var dx1 = s1.x - s2.x;
42181 var dy1 = s1.y - s2.y;
42182 var dx2 = s2.x - s3.x;
42183 var dy2 = s2.y - s3.y;
42184 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42185 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42186 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42187 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42188 var dxm = m1.x - m2.x;
42189 var dym = m1.y - m2.y;
42190 var k = l2 / (l1 + l2);
42191 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42192 var tx = s2.x - cm.x;
42193 var ty = s2.y - cm.y;
42195 c1: new scope.Point(m1.x + tx, m1.y + ty),
42196 c2: new scope.Point(m2.x + tx, m2.y + ty)
42199 Bezier.prototype.length = function () {
42204 for (var i = 0; i <= steps; i += 1) {
42206 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42207 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42209 var xdiff = cx - px;
42210 var ydiff = cy - py;
42211 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42218 Bezier.prototype.point = function (t, start, c1, c2, end) {
42219 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42220 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42221 + (3.0 * c2 * (1.0 - t) * t * t)
42222 + (end * t * t * t);
42227 throttleStroke: function(fn, wait) {
42228 if (wait === void 0) { wait = 250; }
42230 var timeout = null;
42234 var later = function () {
42235 previous = Date.now();
42237 result = fn.apply(storedContext, storedArgs);
42239 storedContext = null;
42243 return function wrapper() {
42245 for (var _i = 0; _i < arguments.length; _i++) {
42246 args[_i] = arguments[_i];
42248 var now = Date.now();
42249 var remaining = wait - (now - previous);
42250 storedContext = this;
42252 if (remaining <= 0 || remaining > wait) {
42254 clearTimeout(timeout);
42258 result = fn.apply(storedContext, storedArgs);
42260 storedContext = null;
42264 else if (!timeout) {
42265 timeout = window.setTimeout(later, remaining);