2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = (
9 Roo.each(document.styleSheets, function(s) {
10 if ( s.href && s.href.match(/css-bootstrap4/)) {
18 * base class for bootstrap elements.
22 Roo.bootstrap = Roo.bootstrap || {};
24 * @class Roo.bootstrap.Component
25 * @extends Roo.Component
26 * Bootstrap Component base class
27 * @cfg {String} cls css class
28 * @cfg {String} style any extra css
29 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
31 * @cfg {string} dataId cutomer id
32 * @cfg {string} name Specifies name attribute
33 * @cfg {string} tooltip Text for the tooltip
34 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
35 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
38 * Do not use directly - it does not do anything..
39 * @param {Object} config The config object
44 Roo.bootstrap.Component = function(config){
45 Roo.bootstrap.Component.superclass.constructor.call(this, config);
49 * @event childrenrendered
50 * Fires when the children have been rendered..
51 * @param {Roo.bootstrap.Component} this
53 "childrenrendered" : true
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
65 allowDomMove : false, // to stop relocations in parent onRender...
75 * Initialize Events for the element
77 initEvents : function() { },
83 can_build_overlaid : true,
85 container_method : false,
92 // returns the parent component..
93 return Roo.ComponentMgr.get(this.parentId)
99 onRender : function(ct, position)
101 // Roo.log("Call onRender: " + this.xtype);
103 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
106 if (this.el.attr('xtype')) {
107 this.el.attr('xtypex', this.el.attr('xtype'));
108 this.el.dom.removeAttribute('xtype');
118 var cfg = Roo.apply({}, this.getAutoCreate());
120 cfg.id = this.id || Roo.id();
122 // fill in the extra attributes
123 if (this.xattr && typeof(this.xattr) =='object') {
124 for (var i in this.xattr) {
125 cfg[i] = this.xattr[i];
130 cfg.dataId = this.dataId;
134 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
137 if (this.style) { // fixme needs to support more complex style data.
138 cfg.style = this.style;
142 cfg.name = this.name;
145 this.el = ct.createChild(cfg, position);
148 this.tooltipEl().attr('tooltip', this.tooltip);
151 if(this.tabIndex !== undefined){
152 this.el.dom.setAttribute('tabIndex', this.tabIndex);
159 * Fetch the element to add children to
160 * @return {Roo.Element} defaults to this.el
162 getChildContainer : function()
167 * Fetch the element to display the tooltip on.
168 * @return {Roo.Element} defaults to this.el
170 tooltipEl : function()
175 addxtype : function(tree,cntr)
179 cn = Roo.factory(tree);
180 //Roo.log(['addxtype', cn]);
182 cn.parentType = this.xtype; //??
183 cn.parentId = this.id;
185 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186 if (typeof(cn.container_method) == 'string') {
187 cntr = cn.container_method;
191 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
193 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
195 var build_from_html = Roo.XComponent.build_from_html;
197 var is_body = (tree.xtype == 'Body') ;
199 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
201 var self_cntr_el = Roo.get(this[cntr](false));
203 // do not try and build conditional elements
204 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
208 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210 return this.addxtypeChild(tree,cntr, is_body);
213 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
216 return this.addxtypeChild(Roo.apply({}, tree),cntr);
219 Roo.log('skipping render');
225 if (!build_from_html) {
229 // this i think handles overlaying multiple children of the same type
230 // with the sam eelement.. - which might be buggy..
232 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
238 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
242 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
249 addxtypeChild : function (tree, cntr, is_body)
251 Roo.debug && Roo.log('addxtypeChild:' + cntr);
253 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
256 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257 (typeof(tree['flexy:foreach']) != 'undefined');
261 skip_children = false;
262 // render the element if it's not BODY.
265 // if parent was disabled, then do not try and create the children..
266 if(!this[cntr](true)){
271 cn = Roo.factory(tree);
273 cn.parentType = this.xtype; //??
274 cn.parentId = this.id;
276 var build_from_html = Roo.XComponent.build_from_html;
279 // does the container contain child eleemnts with 'xtype' attributes.
280 // that match this xtype..
281 // note - when we render we create these as well..
282 // so we should check to see if body has xtype set.
283 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
285 var self_cntr_el = Roo.get(this[cntr](false));
286 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
288 //Roo.log(Roo.XComponent.build_from_html);
289 //Roo.log("got echild:");
292 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293 // and are not displayed -this causes this to use up the wrong element when matching.
294 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
297 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
304 //echild.dom.removeAttribute('xtype');
306 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307 Roo.debug && Roo.log(self_cntr_el);
308 Roo.debug && Roo.log(echild);
309 Roo.debug && Roo.log(cn);
315 // if object has flexy:if - then it may or may not be rendered.
316 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
317 // skip a flexy if element.
318 Roo.debug && Roo.log('skipping render');
319 Roo.debug && Roo.log(tree);
321 Roo.debug && Roo.log('skipping all children');
322 skip_children = true;
327 // actually if flexy:foreach is found, we really want to create
328 // multiple copies here...
330 //Roo.log(this[cntr]());
331 // some elements do not have render methods.. like the layouts...
333 if(this[cntr](true) === false){
338 cn.render && cn.render(this[cntr](true));
341 // then add the element..
348 if (typeof (tree.menu) != 'undefined') {
349 tree.menu.parentType = cn.xtype;
350 tree.menu.triggerEl = cn.el;
351 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
355 if (!tree.items || !tree.items.length) {
357 //Roo.log(["no children", this]);
362 var items = tree.items;
365 //Roo.log(items.length);
367 if (!skip_children) {
368 for(var i =0;i < items.length;i++) {
369 // Roo.log(['add child', items[i]]);
370 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
376 //Roo.log("fire childrenrendered");
378 cn.fireEvent('childrenrendered', this);
384 * Set the element that will be used to show or hide
386 setVisibilityEl : function(el)
388 this.visibilityEl = el;
392 * Get the element that will be used to show or hide
394 getVisibilityEl : function()
396 if (typeof(this.visibilityEl) == 'object') {
397 return this.visibilityEl;
400 if (typeof(this.visibilityEl) == 'string') {
401 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
408 * Show a component - removes 'hidden' class
412 if(!this.getVisibilityEl()){
416 this.getVisibilityEl().removeClass(['hidden','d-none']);
418 this.fireEvent('show', this);
423 * Hide a component - adds 'hidden' class
427 if(!this.getVisibilityEl()){
431 this.getVisibilityEl().addClass(['hidden','d-none']);
433 this.fireEvent('hide', this);
446 * @class Roo.bootstrap.Body
447 * @extends Roo.bootstrap.Component
448 * Bootstrap Body class
452 * @param {Object} config The config object
455 Roo.bootstrap.Body = function(config){
457 config = config || {};
459 Roo.bootstrap.Body.superclass.constructor.call(this, config);
460 this.el = Roo.get(config.el ? config.el : document.body );
461 if (this.cls && this.cls.length) {
462 Roo.get(document.body).addClass(this.cls);
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
468 is_body : true,// just to make sure it's constructed?
473 onRender : function(ct, position)
475 /* Roo.log("Roo.bootstrap.Body - onRender");
476 if (this.cls && this.cls.length) {
477 Roo.get(document.body).addClass(this.cls);
496 * @class Roo.bootstrap.ButtonGroup
497 * @extends Roo.bootstrap.Component
498 * Bootstrap ButtonGroup class
499 * @cfg {String} size lg | sm | xs (default empty normal)
500 * @cfg {String} align vertical | justified (default none)
501 * @cfg {String} direction up | down (default down)
502 * @cfg {Boolean} toolbar false | true
503 * @cfg {Boolean} btn true | false
508 * @param {Object} config The config object
511 Roo.bootstrap.ButtonGroup = function(config){
512 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
523 getAutoCreate : function(){
529 cfg.html = this.html || cfg.html;
540 if (['vertical','justified'].indexOf(this.align)!==-1) {
541 cfg.cls = 'btn-group-' + this.align;
543 if (this.align == 'justified') {
544 console.log(this.items);
548 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549 cfg.cls += ' btn-group-' + this.size;
552 if (this.direction == 'up') {
553 cfg.cls += ' dropup' ;
559 * Add a button to the group (similar to NavItem API.)
561 addItem : function(cfg)
563 var cn = new Roo.bootstrap.Button(cfg);
565 cn.parentId = this.id;
566 cn.onRender(this.el, null);
580 * @class Roo.bootstrap.Button
581 * @extends Roo.bootstrap.Component
582 * Bootstrap Button class
583 * @cfg {String} html The button content
584 * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585 * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587 * @cfg {String} size ( lg | sm | xs)
588 * @cfg {String} tag ( a | input | submit)
589 * @cfg {String} href empty or href
590 * @cfg {Boolean} disabled default false;
591 * @cfg {Boolean} isClose default false;
592 * @cfg {String} glyphicon depricated - use fa
593 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
594 * @cfg {String} badge text for badge
595 * @cfg {String} theme (default|glow)
596 * @cfg {Boolean} inverse dark themed version
597 * @cfg {Boolean} toggle is it a slidy toggle button
598 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
599 * @cfg {String} ontext text for on slidy toggle state
600 * @cfg {String} offtext text for off slidy toggle state
601 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
602 * @cfg {Boolean} removeClass remove the standard class..
603 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
606 * Create a new button
607 * @param {Object} config The config object
611 Roo.bootstrap.Button = function(config){
612 Roo.bootstrap.Button.superclass.constructor.call(this, config);
613 this.weightClass = ["btn-default btn-outline-secondary",
625 * When a butotn is pressed
626 * @param {Roo.bootstrap.Button} btn
627 * @param {Roo.EventObject} e
632 * After the button has been toggles
633 * @param {Roo.bootstrap.Button} btn
634 * @param {Roo.EventObject} e
635 * @param {boolean} pressed (also available as button.pressed)
641 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
662 preventDefault: true,
670 getAutoCreate : function(){
678 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
679 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
684 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
686 if (this.toggle == true) {
689 cls: 'slider-frame roo-button',
694 'data-off-text':'OFF',
695 cls: 'slider-button',
701 if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
702 cfg.cls += ' '+this.weight;
711 cfg["aria-hidden"] = true;
713 cfg.html = "×";
719 if (this.theme==='default') {
720 cfg.cls = 'btn roo-button';
722 //if (this.parentType != 'Navbar') {
723 this.weight = this.weight.length ? this.weight : 'default';
725 if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
727 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
728 var weight = this.weight == 'default' ? 'secondary' : this.weight;
729 cfg.cls += ' btn-' + outline + weight;
730 if (this.weight == 'default') {
732 cfg.cls += ' btn-' + this.weight;
735 } else if (this.theme==='glow') {
738 cfg.cls = 'btn-glow roo-button';
740 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
742 cfg.cls += ' ' + this.weight;
748 this.cls += ' inverse';
752 if (this.active || this.pressed === true) {
753 cfg.cls += ' active';
757 cfg.disabled = 'disabled';
761 Roo.log('changing to ul' );
763 this.glyphicon = 'caret';
764 if (Roo.bootstrap.version == 4) {
765 this.fa = 'caret-down';
770 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
772 //gsRoo.log(this.parentType);
773 if (this.parentType === 'Navbar' && !this.parent().bar) {
774 Roo.log('changing to li?');
783 href : this.href || '#'
786 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
787 cfg.cls += ' dropdown';
794 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
796 if (this.glyphicon) {
797 cfg.html = ' ' + cfg.html;
802 cls: 'glyphicon glyphicon-' + this.glyphicon
807 cfg.html = ' ' + cfg.html;
812 cls: 'fa fas fa-' + this.fa
822 // cfg.cls='btn roo-button';
826 var value = cfg.html;
831 cls: 'glyphicon glyphicon-' + this.glyphicon,
838 cls: 'fa fas fa-' + this.fa,
843 var bw = this.badge_weight.length ? this.badge_weight :
844 (this.weight.length ? this.weight : 'secondary');
845 bw = bw == 'default' ? 'secondary' : bw;
851 cls: 'badge badge-' + bw,
860 cfg.cls += ' dropdown';
861 cfg.html = typeof(cfg.html) != 'undefined' ?
862 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
865 if (cfg.tag !== 'a' && this.href !== '') {
866 throw "Tag must be a to set href.";
867 } else if (this.href.length > 0) {
868 cfg.href = this.href;
871 if(this.removeClass){
876 cfg.target = this.target;
881 initEvents: function() {
882 // Roo.log('init events?');
883 // Roo.log(this.el.dom);
886 if (typeof (this.menu) != 'undefined') {
887 this.menu.parentType = this.xtype;
888 this.menu.triggerEl = this.el;
889 this.addxtype(Roo.apply({}, this.menu));
893 if (this.el.hasClass('roo-button')) {
894 this.el.on('click', this.onClick, this);
896 this.el.select('.roo-button').on('click', this.onClick, this);
899 if(this.removeClass){
900 this.el.on('click', this.onClick, this);
903 this.el.enableDisplayMode();
906 onClick : function(e)
912 Roo.log('button on click ');
913 if(this.preventDefault){
917 if (this.pressed === true || this.pressed === false) {
918 this.toggleActive(e);
922 this.fireEvent('click', this, e);
926 * Enables this button
930 this.disabled = false;
931 this.el.removeClass('disabled');
935 * Disable this button
939 this.disabled = true;
940 this.el.addClass('disabled');
943 * sets the active state on/off,
944 * @param {Boolean} state (optional) Force a particular state
946 setActive : function(v) {
948 this.el[v ? 'addClass' : 'removeClass']('active');
952 * toggles the current active state
954 toggleActive : function(e)
956 this.setActive(!this.pressed);
957 this.fireEvent('toggle', this, e, !this.pressed);
960 * get the current active state
961 * @return {boolean} true if it's active
963 isActive : function()
965 return this.el.hasClass('active');
968 * set the text of the first selected button
970 setText : function(str)
972 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
975 * get the text of the first selected button
979 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
982 setWeight : function(str)
984 this.el.removeClass(this.weightClass);
986 var outline = this.outline ? 'outline-' : '';
987 if (str == 'default') {
988 this.el.addClass('btn-default btn-outline-secondary');
991 this.el.addClass('btn-' + outline + str);
1005 * @class Roo.bootstrap.Column
1006 * @extends Roo.bootstrap.Component
1007 * Bootstrap Column class
1008 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1009 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1010 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1011 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1012 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1013 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1014 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1015 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1018 * @cfg {Boolean} hidden (true|false) hide the element
1019 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1020 * @cfg {String} fa (ban|check|...) font awesome icon
1021 * @cfg {Number} fasize (1|2|....) font awsome size
1023 * @cfg {String} icon (info-sign|check|...) glyphicon name
1025 * @cfg {String} html content of column.
1028 * Create a new Column
1029 * @param {Object} config The config object
1032 Roo.bootstrap.Column = function(config){
1033 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1036 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1054 getAutoCreate : function(){
1055 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1063 ['xs','sm','md','lg'].map(function(size){
1064 //Roo.log( size + ':' + settings[size]);
1066 if (settings[size+'off'] !== false) {
1067 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1070 if (settings[size] === false) {
1074 if (!settings[size]) { // 0 = hidden
1075 cfg.cls += ' hidden-' + size + ' hidden' + size + '-down';;
1078 cfg.cls += ' col-' + size + '-' + settings[size] + (
1079 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1085 cfg.cls += ' hidden';
1088 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1089 cfg.cls +=' alert alert-' + this.alert;
1093 if (this.html.length) {
1094 cfg.html = this.html;
1098 if (this.fasize > 1) {
1099 fasize = ' fa-' + this.fasize + 'x';
1101 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1106 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1125 * @class Roo.bootstrap.Container
1126 * @extends Roo.bootstrap.Component
1127 * Bootstrap Container class
1128 * @cfg {Boolean} jumbotron is it a jumbotron element
1129 * @cfg {String} html content of element
1130 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1131 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1132 * @cfg {String} header content of header (for panel)
1133 * @cfg {String} footer content of footer (for panel)
1134 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1135 * @cfg {String} tag (header|aside|section) type of HTML tag.
1136 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1137 * @cfg {String} fa font awesome icon
1138 * @cfg {String} icon (info-sign|check|...) glyphicon name
1139 * @cfg {Boolean} hidden (true|false) hide the element
1140 * @cfg {Boolean} expandable (true|false) default false
1141 * @cfg {Boolean} expanded (true|false) default true
1142 * @cfg {String} rheader contet on the right of header
1143 * @cfg {Boolean} clickable (true|false) default false
1147 * Create a new Container
1148 * @param {Object} config The config object
1151 Roo.bootstrap.Container = function(config){
1152 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1158 * After the panel has been expand
1160 * @param {Roo.bootstrap.Container} this
1165 * After the panel has been collapsed
1167 * @param {Roo.bootstrap.Container} this
1172 * When a element is chick
1173 * @param {Roo.bootstrap.Container} this
1174 * @param {Roo.EventObject} e
1180 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1198 getChildContainer : function() {
1204 if (this.panel.length) {
1205 return this.el.select('.panel-body',true).first();
1212 getAutoCreate : function(){
1215 tag : this.tag || 'div',
1219 if (this.jumbotron) {
1220 cfg.cls = 'jumbotron';
1225 // - this is applied by the parent..
1227 // cfg.cls = this.cls + '';
1230 if (this.sticky.length) {
1232 var bd = Roo.get(document.body);
1233 if (!bd.hasClass('bootstrap-sticky')) {
1234 bd.addClass('bootstrap-sticky');
1235 Roo.select('html',true).setStyle('height', '100%');
1238 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1242 if (this.well.length) {
1243 switch (this.well) {
1246 cfg.cls +=' well well-' +this.well;
1255 cfg.cls += ' hidden';
1259 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1260 cfg.cls +=' alert alert-' + this.alert;
1265 if (this.panel.length) {
1266 cfg.cls += ' panel panel-' + this.panel;
1268 if (this.header.length) {
1272 if(this.expandable){
1274 cfg.cls = cfg.cls + ' expandable';
1278 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1286 cls : 'panel-title',
1287 html : (this.expandable ? ' ' : '') + this.header
1291 cls: 'panel-header-right',
1297 cls : 'panel-heading',
1298 style : this.expandable ? 'cursor: pointer' : '',
1306 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1311 if (this.footer.length) {
1313 cls : 'panel-footer',
1322 body.html = this.html || cfg.html;
1323 // prefix with the icons..
1325 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1328 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1333 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1334 cfg.cls = 'container';
1340 initEvents: function()
1342 if(this.expandable){
1343 var headerEl = this.headerEl();
1346 headerEl.on('click', this.onToggleClick, this);
1351 this.el.on('click', this.onClick, this);
1356 onToggleClick : function()
1358 var headerEl = this.headerEl();
1374 if(this.fireEvent('expand', this)) {
1376 this.expanded = true;
1378 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1380 this.el.select('.panel-body',true).first().removeClass('hide');
1382 var toggleEl = this.toggleEl();
1388 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1393 collapse : function()
1395 if(this.fireEvent('collapse', this)) {
1397 this.expanded = false;
1399 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1400 this.el.select('.panel-body',true).first().addClass('hide');
1402 var toggleEl = this.toggleEl();
1408 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1412 toggleEl : function()
1414 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1418 return this.el.select('.panel-heading .fa',true).first();
1421 headerEl : function()
1423 if(!this.el || !this.panel.length || !this.header.length){
1427 return this.el.select('.panel-heading',true).first()
1432 if(!this.el || !this.panel.length){
1436 return this.el.select('.panel-body',true).first()
1439 titleEl : function()
1441 if(!this.el || !this.panel.length || !this.header.length){
1445 return this.el.select('.panel-title',true).first();
1448 setTitle : function(v)
1450 var titleEl = this.titleEl();
1456 titleEl.dom.innerHTML = v;
1459 getTitle : function()
1462 var titleEl = this.titleEl();
1468 return titleEl.dom.innerHTML;
1471 setRightTitle : function(v)
1473 var t = this.el.select('.panel-header-right',true).first();
1479 t.dom.innerHTML = v;
1482 onClick : function(e)
1486 this.fireEvent('click', this, e);
1499 * @class Roo.bootstrap.Img
1500 * @extends Roo.bootstrap.Component
1501 * Bootstrap Img class
1502 * @cfg {Boolean} imgResponsive false | true
1503 * @cfg {String} border rounded | circle | thumbnail
1504 * @cfg {String} src image source
1505 * @cfg {String} alt image alternative text
1506 * @cfg {String} href a tag href
1507 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1508 * @cfg {String} xsUrl xs image source
1509 * @cfg {String} smUrl sm image source
1510 * @cfg {String} mdUrl md image source
1511 * @cfg {String} lgUrl lg image source
1514 * Create a new Input
1515 * @param {Object} config The config object
1518 Roo.bootstrap.Img = function(config){
1519 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1525 * The img click event for the img.
1526 * @param {Roo.EventObject} e
1532 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1534 imgResponsive: true,
1544 getAutoCreate : function()
1546 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1547 return this.createSingleImg();
1552 cls: 'roo-image-responsive-group',
1557 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1559 if(!_this[size + 'Url']){
1565 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1566 html: _this.html || cfg.html,
1567 src: _this[size + 'Url']
1570 img.cls += ' roo-image-responsive-' + size;
1572 var s = ['xs', 'sm', 'md', 'lg'];
1574 s.splice(s.indexOf(size), 1);
1576 Roo.each(s, function(ss){
1577 img.cls += ' hidden-' + ss;
1580 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1581 cfg.cls += ' img-' + _this.border;
1585 cfg.alt = _this.alt;
1598 a.target = _this.target;
1602 cfg.cn.push((_this.href) ? a : img);
1609 createSingleImg : function()
1613 cls: (this.imgResponsive) ? 'img-responsive' : '',
1615 src : 'about:blank' // just incase src get's set to undefined?!?
1618 cfg.html = this.html || cfg.html;
1620 cfg.src = this.src || cfg.src;
1622 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1623 cfg.cls += ' img-' + this.border;
1640 a.target = this.target;
1645 return (this.href) ? a : cfg;
1648 initEvents: function()
1651 this.el.on('click', this.onClick, this);
1656 onClick : function(e)
1658 Roo.log('img onclick');
1659 this.fireEvent('click', this, e);
1662 * Sets the url of the image - used to update it
1663 * @param {String} url the url of the image
1666 setSrc : function(url)
1670 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1671 this.el.dom.src = url;
1675 this.el.select('img', true).first().dom.src = url;
1691 * @class Roo.bootstrap.Link
1692 * @extends Roo.bootstrap.Component
1693 * Bootstrap Link Class
1694 * @cfg {String} alt image alternative text
1695 * @cfg {String} href a tag href
1696 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1697 * @cfg {String} html the content of the link.
1698 * @cfg {String} anchor name for the anchor link
1699 * @cfg {String} fa - favicon
1701 * @cfg {Boolean} preventDefault (true | false) default false
1705 * Create a new Input
1706 * @param {Object} config The config object
1709 Roo.bootstrap.Link = function(config){
1710 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1716 * The img click event for the img.
1717 * @param {Roo.EventObject} e
1723 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1727 preventDefault: false,
1733 getAutoCreate : function()
1735 var html = this.html || '';
1737 if (this.fa !== false) {
1738 html = '<i class="fa fa-' + this.fa + '"></i>';
1743 // anchor's do not require html/href...
1744 if (this.anchor === false) {
1746 cfg.href = this.href || '#';
1748 cfg.name = this.anchor;
1749 if (this.html !== false || this.fa !== false) {
1752 if (this.href !== false) {
1753 cfg.href = this.href;
1757 if(this.alt !== false){
1762 if(this.target !== false) {
1763 cfg.target = this.target;
1769 initEvents: function() {
1771 if(!this.href || this.preventDefault){
1772 this.el.on('click', this.onClick, this);
1776 onClick : function(e)
1778 if(this.preventDefault){
1781 //Roo.log('img onclick');
1782 this.fireEvent('click', this, e);
1795 * @class Roo.bootstrap.Header
1796 * @extends Roo.bootstrap.Component
1797 * Bootstrap Header class
1798 * @cfg {String} html content of header
1799 * @cfg {Number} level (1|2|3|4|5|6) default 1
1802 * Create a new Header
1803 * @param {Object} config The config object
1807 Roo.bootstrap.Header = function(config){
1808 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1811 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1819 getAutoCreate : function(){
1824 tag: 'h' + (1 *this.level),
1825 html: this.html || ''
1837 * Ext JS Library 1.1.1
1838 * Copyright(c) 2006-2007, Ext JS, LLC.
1840 * Originally Released Under LGPL - original licence link has changed is not relivant.
1843 * <script type="text/javascript">
1847 * @class Roo.bootstrap.MenuMgr
1848 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1851 Roo.bootstrap.MenuMgr = function(){
1852 var menus, active, groups = {}, attached = false, lastShow = new Date();
1854 // private - called when first menu is created
1857 active = new Roo.util.MixedCollection();
1858 Roo.get(document).addKeyListener(27, function(){
1859 if(active.length > 0){
1867 if(active && active.length > 0){
1868 var c = active.clone();
1878 if(active.length < 1){
1879 Roo.get(document).un("mouseup", onMouseDown);
1887 var last = active.last();
1888 lastShow = new Date();
1891 Roo.get(document).on("mouseup", onMouseDown);
1896 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1897 m.parentMenu.activeChild = m;
1898 }else if(last && last.isVisible()){
1899 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1904 function onBeforeHide(m){
1906 m.activeChild.hide();
1908 if(m.autoHideTimer){
1909 clearTimeout(m.autoHideTimer);
1910 delete m.autoHideTimer;
1915 function onBeforeShow(m){
1916 var pm = m.parentMenu;
1917 if(!pm && !m.allowOtherMenus){
1919 }else if(pm && pm.activeChild && active != m){
1920 pm.activeChild.hide();
1924 // private this should really trigger on mouseup..
1925 function onMouseDown(e){
1926 Roo.log("on Mouse Up");
1928 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1929 Roo.log("MenuManager hideAll");
1938 function onBeforeCheck(mi, state){
1940 var g = groups[mi.group];
1941 for(var i = 0, l = g.length; i < l; i++){
1943 g[i].setChecked(false);
1952 * Hides all menus that are currently visible
1954 hideAll : function(){
1959 register : function(menu){
1963 menus[menu.id] = menu;
1964 menu.on("beforehide", onBeforeHide);
1965 menu.on("hide", onHide);
1966 menu.on("beforeshow", onBeforeShow);
1967 menu.on("show", onShow);
1969 if(g && menu.events["checkchange"]){
1973 groups[g].push(menu);
1974 menu.on("checkchange", onCheck);
1979 * Returns a {@link Roo.menu.Menu} object
1980 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1981 * be used to generate and return a new Menu instance.
1983 get : function(menu){
1984 if(typeof menu == "string"){ // menu id
1986 }else if(menu.events){ // menu instance
1989 /*else if(typeof menu.length == 'number'){ // array of menu items?
1990 return new Roo.bootstrap.Menu({items:menu});
1991 }else{ // otherwise, must be a config
1992 return new Roo.bootstrap.Menu(menu);
1999 unregister : function(menu){
2000 delete menus[menu.id];
2001 menu.un("beforehide", onBeforeHide);
2002 menu.un("hide", onHide);
2003 menu.un("beforeshow", onBeforeShow);
2004 menu.un("show", onShow);
2006 if(g && menu.events["checkchange"]){
2007 groups[g].remove(menu);
2008 menu.un("checkchange", onCheck);
2013 registerCheckable : function(menuItem){
2014 var g = menuItem.group;
2019 groups[g].push(menuItem);
2020 menuItem.on("beforecheckchange", onBeforeCheck);
2025 unregisterCheckable : function(menuItem){
2026 var g = menuItem.group;
2028 groups[g].remove(menuItem);
2029 menuItem.un("beforecheckchange", onBeforeCheck);
2041 * @class Roo.bootstrap.Menu
2042 * @extends Roo.bootstrap.Component
2043 * Bootstrap Menu class - container for MenuItems
2044 * @cfg {String} type (dropdown|treeview|submenu) type of menu
2045 * @cfg {bool} hidden if the menu should be hidden when rendered.
2046 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
2047 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
2051 * @param {Object} config The config object
2055 Roo.bootstrap.Menu = function(config){
2056 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2057 if (this.registerMenu && this.type != 'treeview') {
2058 Roo.bootstrap.MenuMgr.register(this);
2065 * Fires before this menu is displayed (return false to block)
2066 * @param {Roo.menu.Menu} this
2071 * Fires before this menu is hidden (return false to block)
2072 * @param {Roo.menu.Menu} this
2077 * Fires after this menu is displayed
2078 * @param {Roo.menu.Menu} this
2083 * Fires after this menu is hidden
2084 * @param {Roo.menu.Menu} this
2089 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2090 * @param {Roo.menu.Menu} this
2091 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2092 * @param {Roo.EventObject} e
2097 * Fires when the mouse is hovering over this menu
2098 * @param {Roo.menu.Menu} this
2099 * @param {Roo.EventObject} e
2100 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2105 * Fires when the mouse exits this menu
2106 * @param {Roo.menu.Menu} this
2107 * @param {Roo.EventObject} e
2108 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2113 * Fires when a menu item contained in this menu is clicked
2114 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2115 * @param {Roo.EventObject} e
2119 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2122 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2126 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2129 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2131 registerMenu : true,
2133 menuItems :false, // stores the menu items..
2143 getChildContainer : function() {
2147 getAutoCreate : function(){
2149 //if (['right'].indexOf(this.align)!==-1) {
2150 // cfg.cn[1].cls += ' pull-right'
2156 cls : 'dropdown-menu' ,
2157 style : 'z-index:1000'
2161 if (this.type === 'submenu') {
2162 cfg.cls = 'submenu active';
2164 if (this.type === 'treeview') {
2165 cfg.cls = 'treeview-menu';
2170 initEvents : function() {
2172 // Roo.log("ADD event");
2173 // Roo.log(this.triggerEl.dom);
2175 this.triggerEl.on('click', this.onTriggerClick, this);
2177 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2180 if (this.triggerEl.hasClass('nav-item')) {
2181 // dropdown toggle on the 'a' in BS4?
2182 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2184 this.triggerEl.addClass('dropdown-toggle');
2187 this.el.on('touchstart' , this.onTouch, this);
2189 this.el.on('click' , this.onClick, this);
2191 this.el.on("mouseover", this.onMouseOver, this);
2192 this.el.on("mouseout", this.onMouseOut, this);
2196 findTargetItem : function(e)
2198 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2202 //Roo.log(t); Roo.log(t.id);
2204 //Roo.log(this.menuitems);
2205 return this.menuitems.get(t.id);
2207 //return this.items.get(t.menuItemId);
2213 onTouch : function(e)
2215 Roo.log("menu.onTouch");
2216 //e.stopEvent(); this make the user popdown broken
2220 onClick : function(e)
2222 Roo.log("menu.onClick");
2224 var t = this.findTargetItem(e);
2225 if(!t || t.isContainer){
2230 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2231 if(t == this.activeItem && t.shouldDeactivate(e)){
2232 this.activeItem.deactivate();
2233 delete this.activeItem;
2237 this.setActiveItem(t, true);
2245 Roo.log('pass click event');
2249 this.fireEvent("click", this, t, e);
2253 if(!t.href.length || t.href == '#'){
2254 (function() { _this.hide(); }).defer(100);
2259 onMouseOver : function(e){
2260 var t = this.findTargetItem(e);
2263 // if(t.canActivate && !t.disabled){
2264 // this.setActiveItem(t, true);
2268 this.fireEvent("mouseover", this, e, t);
2270 isVisible : function(){
2271 return !this.hidden;
2273 onMouseOut : function(e){
2274 var t = this.findTargetItem(e);
2277 // if(t == this.activeItem && t.shouldDeactivate(e)){
2278 // this.activeItem.deactivate();
2279 // delete this.activeItem;
2282 this.fireEvent("mouseout", this, e, t);
2287 * Displays this menu relative to another element
2288 * @param {String/HTMLElement/Roo.Element} element The element to align to
2289 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2290 * the element (defaults to this.defaultAlign)
2291 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2293 show : function(el, pos, parentMenu)
2295 if (false === this.fireEvent("beforeshow", this)) {
2296 Roo.log("show canceled");
2299 this.parentMenu = parentMenu;
2304 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2307 * Displays this menu at a specific xy position
2308 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2309 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2311 showAt : function(xy, parentMenu, /* private: */_e){
2312 this.parentMenu = parentMenu;
2317 this.fireEvent("beforeshow", this);
2318 //xy = this.el.adjustForConstraints(xy);
2322 this.hideMenuItems();
2323 this.hidden = false;
2324 this.triggerEl.addClass('open');
2325 this.el.addClass('show');
2327 // reassign x when hitting right
2328 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2329 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2332 // reassign y when hitting bottom
2333 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2334 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2337 // but the list may align on trigger left or trigger top... should it be a properity?
2339 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2344 this.fireEvent("show", this);
2350 this.doFocus.defer(50, this);
2354 doFocus : function(){
2356 this.focusEl.focus();
2361 * Hides this menu and optionally all parent menus
2362 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2364 hide : function(deep)
2366 if (false === this.fireEvent("beforehide", this)) {
2367 Roo.log("hide canceled");
2370 this.hideMenuItems();
2371 if(this.el && this.isVisible()){
2373 if(this.activeItem){
2374 this.activeItem.deactivate();
2375 this.activeItem = null;
2377 this.triggerEl.removeClass('open');;
2378 this.el.removeClass('show');
2380 this.fireEvent("hide", this);
2382 if(deep === true && this.parentMenu){
2383 this.parentMenu.hide(true);
2387 onTriggerClick : function(e)
2389 Roo.log('trigger click');
2391 var target = e.getTarget();
2393 Roo.log(target.nodeName.toLowerCase());
2395 if(target.nodeName.toLowerCase() === 'i'){
2401 onTriggerPress : function(e)
2403 Roo.log('trigger press');
2404 //Roo.log(e.getTarget());
2405 // Roo.log(this.triggerEl.dom);
2407 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2408 var pel = Roo.get(e.getTarget());
2409 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2410 Roo.log('is treeview or dropdown?');
2414 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2418 if (this.isVisible()) {
2423 this.show(this.triggerEl, '?', false);
2426 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2433 hideMenuItems : function()
2435 Roo.log("hide Menu Items");
2440 this.el.select('.open',true).each(function(aa) {
2442 aa.removeClass('open');
2446 addxtypeChild : function (tree, cntr) {
2447 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2449 this.menuitems.add(comp);
2461 this.getEl().dom.innerHTML = '';
2462 this.menuitems.clear();
2476 * @class Roo.bootstrap.MenuItem
2477 * @extends Roo.bootstrap.Component
2478 * Bootstrap MenuItem class
2479 * @cfg {String} html the menu label
2480 * @cfg {String} href the link
2481 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2482 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2483 * @cfg {Boolean} active used on sidebars to highlight active itesm
2484 * @cfg {String} fa favicon to show on left of menu item.
2485 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2489 * Create a new MenuItem
2490 * @param {Object} config The config object
2494 Roo.bootstrap.MenuItem = function(config){
2495 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2500 * The raw click event for the entire grid.
2501 * @param {Roo.bootstrap.MenuItem} this
2502 * @param {Roo.EventObject} e
2508 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2512 preventDefault: false,
2513 isContainer : false,
2517 getAutoCreate : function(){
2519 if(this.isContainer){
2522 cls: 'dropdown-menu-item '
2532 cls : 'dropdown-item',
2537 if (this.fa !== false) {
2540 cls : 'fa fa-' + this.fa
2549 cls: 'dropdown-menu-item',
2552 if (this.parent().type == 'treeview') {
2553 cfg.cls = 'treeview-menu';
2556 cfg.cls += ' active';
2561 anc.href = this.href || cfg.cn[0].href ;
2562 ctag.html = this.html || cfg.cn[0].html ;
2566 initEvents: function()
2568 if (this.parent().type == 'treeview') {
2569 this.el.select('a').on('click', this.onClick, this);
2573 this.menu.parentType = this.xtype;
2574 this.menu.triggerEl = this.el;
2575 this.menu = this.addxtype(Roo.apply({}, this.menu));
2579 onClick : function(e)
2581 Roo.log('item on click ');
2583 if(this.preventDefault){
2586 //this.parent().hideMenuItems();
2588 this.fireEvent('click', this, e);
2607 * @class Roo.bootstrap.MenuSeparator
2608 * @extends Roo.bootstrap.Component
2609 * Bootstrap MenuSeparator class
2612 * Create a new MenuItem
2613 * @param {Object} config The config object
2617 Roo.bootstrap.MenuSeparator = function(config){
2618 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2621 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2623 getAutoCreate : function(){
2642 * @class Roo.bootstrap.Modal
2643 * @extends Roo.bootstrap.Component
2644 * Bootstrap Modal class
2645 * @cfg {String} title Title of dialog
2646 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2647 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2648 * @cfg {Boolean} specificTitle default false
2649 * @cfg {Array} buttons Array of buttons or standard button set..
2650 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2651 * @cfg {Boolean} animate default true
2652 * @cfg {Boolean} allow_close default true
2653 * @cfg {Boolean} fitwindow default false
2654 * @cfg {Number} width fixed width - usefull for chrome extension only really.
2655 * @cfg {Number} height fixed height - usefull for chrome extension only really.
2656 * @cfg {String} size (sm|lg) default empty
2657 * @cfg {Number} max_width set the max width of modal
2661 * Create a new Modal Dialog
2662 * @param {Object} config The config object
2665 Roo.bootstrap.Modal = function(config){
2666 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2671 * The raw btnclick event for the button
2672 * @param {Roo.EventObject} e
2677 * Fire when dialog resize
2678 * @param {Roo.bootstrap.Modal} this
2679 * @param {Roo.EventObject} e
2683 this.buttons = this.buttons || [];
2686 this.tmpl = Roo.factory(this.tmpl);
2691 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2693 title : 'test dialog',
2703 specificTitle: false,
2705 buttonPosition: 'right',
2728 onRender : function(ct, position)
2730 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2733 var cfg = Roo.apply({}, this.getAutoCreate());
2736 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2738 //if (!cfg.name.length) {
2742 cfg.cls += ' ' + this.cls;
2745 cfg.style = this.style;
2747 this.el = Roo.get(document.body).createChild(cfg, position);
2749 //var type = this.el.dom.type;
2752 if(this.tabIndex !== undefined){
2753 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2756 this.dialogEl = this.el.select('.modal-dialog',true).first();
2757 this.bodyEl = this.el.select('.modal-body',true).first();
2758 this.closeEl = this.el.select('.modal-header .close', true).first();
2759 this.headerEl = this.el.select('.modal-header',true).first();
2760 this.titleEl = this.el.select('.modal-title',true).first();
2761 this.footerEl = this.el.select('.modal-footer',true).first();
2763 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2765 //this.el.addClass("x-dlg-modal");
2767 if (this.buttons.length) {
2768 Roo.each(this.buttons, function(bb) {
2769 var b = Roo.apply({}, bb);
2770 b.xns = b.xns || Roo.bootstrap;
2771 b.xtype = b.xtype || 'Button';
2772 if (typeof(b.listeners) == 'undefined') {
2773 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2776 var btn = Roo.factory(b);
2778 btn.render(this.getButtonContainer());
2782 // render the children.
2785 if(typeof(this.items) != 'undefined'){
2786 var items = this.items;
2789 for(var i =0;i < items.length;i++) {
2790 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2794 this.items = nitems;
2796 // where are these used - they used to be body/close/footer
2800 //this.el.addClass([this.fieldClass, this.cls]);
2804 getAutoCreate : function()
2806 // we will default to modal-body-overflow - might need to remove or make optional later.
2808 cls : 'modal-body enable-modal-body-overflow ',
2809 html : this.html || ''
2814 cls : 'modal-title',
2818 if(this.specificTitle){
2824 if (this.allow_close && Roo.bootstrap.version == 3) {
2834 if (this.allow_close && Roo.bootstrap.version == 4) {
2844 if(this.size.length){
2845 size = 'modal-' + this.size;
2848 var footer = Roo.bootstrap.version == 3 ?
2850 cls : 'modal-footer',
2854 cls: 'btn-' + this.buttonPosition
2859 { // BS4 uses mr-auto on left buttons....
2860 cls : 'modal-footer'
2871 cls: "modal-dialog " + size,
2874 cls : "modal-content",
2877 cls : 'modal-header',
2892 modal.cls += ' fade';
2898 getChildContainer : function() {
2903 getButtonContainer : function() {
2905 return Roo.bootstrap.version == 4 ?
2906 this.el.select('.modal-footer',true).first()
2907 : this.el.select('.modal-footer div',true).first();
2910 initEvents : function()
2912 if (this.allow_close) {
2913 this.closeEl.on('click', this.hide, this);
2915 Roo.EventManager.onWindowResize(this.resize, this, true);
2923 this.maskEl.setSize(
2924 Roo.lib.Dom.getViewWidth(true),
2925 Roo.lib.Dom.getViewHeight(true)
2928 if (this.fitwindow) {
2932 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2933 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
2938 if(this.max_width !== 0) {
2940 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2943 this.setSize(w, this.height);
2947 if(this.max_height) {
2948 this.setSize(w,Math.min(
2950 Roo.lib.Dom.getViewportHeight(true) - 60
2956 if(!this.fit_content) {
2957 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2961 this.setSize(w, Math.min(
2963 this.headerEl.getHeight() +
2964 this.footerEl.getHeight() +
2965 this.getChildHeight(this.bodyEl.dom.childNodes),
2966 Roo.lib.Dom.getViewportHeight(true) - 60)
2972 setSize : function(w,h)
2983 if (!this.rendered) {
2987 //this.el.setStyle('display', 'block');
2988 this.el.removeClass('hideing');
2989 this.el.dom.style.display='block';
2991 Roo.get(document.body).addClass('modal-open');
2993 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2996 this.el.addClass('show');
2997 this.el.addClass('in');
3000 this.el.addClass('show');
3001 this.el.addClass('in');
3004 // not sure how we can show data in here..
3006 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3009 Roo.get(document.body).addClass("x-body-masked");
3011 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3012 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3013 this.maskEl.dom.style.display = 'block';
3014 this.maskEl.addClass('show');
3019 this.fireEvent('show', this);
3021 // set zindex here - otherwise it appears to be ignored...
3022 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3025 this.items.forEach( function(e) {
3026 e.layout ? e.layout() : false;
3034 if(this.fireEvent("beforehide", this) !== false){
3036 this.maskEl.removeClass('show');
3038 this.maskEl.dom.style.display = '';
3039 Roo.get(document.body).removeClass("x-body-masked");
3040 this.el.removeClass('in');
3041 this.el.select('.modal-dialog', true).first().setStyle('transform','');
3043 if(this.animate){ // why
3044 this.el.addClass('hideing');
3045 this.el.removeClass('show');
3047 if (!this.el.hasClass('hideing')) {
3048 return; // it's been shown again...
3051 this.el.dom.style.display='';
3053 Roo.get(document.body).removeClass('modal-open');
3054 this.el.removeClass('hideing');
3058 this.el.removeClass('show');
3059 this.el.dom.style.display='';
3060 Roo.get(document.body).removeClass('modal-open');
3063 this.fireEvent('hide', this);
3066 isVisible : function()
3069 return this.el.hasClass('show') && !this.el.hasClass('hideing');
3073 addButton : function(str, cb)
3077 var b = Roo.apply({}, { html : str } );
3078 b.xns = b.xns || Roo.bootstrap;
3079 b.xtype = b.xtype || 'Button';
3080 if (typeof(b.listeners) == 'undefined') {
3081 b.listeners = { click : cb.createDelegate(this) };
3084 var btn = Roo.factory(b);
3086 btn.render(this.getButtonContainer());
3092 setDefaultButton : function(btn)
3094 //this.el.select('.modal-footer').()
3097 resizeTo: function(w,h)
3099 this.dialogEl.setWidth(w);
3101 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
3103 this.bodyEl.setHeight(h - diff);
3105 this.fireEvent('resize', this);
3108 setContentSize : function(w, h)
3112 onButtonClick: function(btn,e)
3115 this.fireEvent('btnclick', btn.name, e);
3118 * Set the title of the Dialog
3119 * @param {String} str new Title
3121 setTitle: function(str) {
3122 this.titleEl.dom.innerHTML = str;
3125 * Set the body of the Dialog
3126 * @param {String} str new Title
3128 setBody: function(str) {
3129 this.bodyEl.dom.innerHTML = str;
3132 * Set the body of the Dialog using the template
3133 * @param {Obj} data - apply this data to the template and replace the body contents.
3135 applyBody: function(obj)
3138 Roo.log("Error - using apply Body without a template");
3141 this.tmpl.overwrite(this.bodyEl, obj);
3144 getChildHeight : function(child_nodes)
3148 child_nodes.length == 0
3153 var child_height = 0;
3155 for(var i = 0; i < child_nodes.length; i++) {
3158 * for modal with tabs...
3159 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3161 var layout_childs = child_nodes[i].childNodes;
3163 for(var j = 0; j < layout_childs.length; j++) {
3165 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3167 var layout_body_childs = layout_childs[j].childNodes;
3169 for(var k = 0; k < layout_body_childs.length; k++) {
3171 if(layout_body_childs[k].classList.contains('navbar')) {
3172 child_height += layout_body_childs[k].offsetHeight;
3176 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3178 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3180 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3182 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3183 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3198 child_height += child_nodes[i].offsetHeight;
3199 // Roo.log(child_nodes[i].offsetHeight);
3202 return child_height;
3208 Roo.apply(Roo.bootstrap.Modal, {
3210 * Button config that displays a single OK button
3219 * Button config that displays Yes and No buttons
3235 * Button config that displays OK and Cancel buttons
3250 * Button config that displays Yes, No and Cancel buttons
3274 * messagebox - can be used as a replace
3278 * @class Roo.MessageBox
3279 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3283 Roo.Msg.alert('Status', 'Changes saved successfully.');
3285 // Prompt for user data:
3286 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3288 // process text value...
3292 // Show a dialog using config options:
3294 title:'Save Changes?',
3295 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3296 buttons: Roo.Msg.YESNOCANCEL,
3303 Roo.bootstrap.MessageBox = function(){
3304 var dlg, opt, mask, waitTimer;
3305 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3306 var buttons, activeTextEl, bwidth;
3310 var handleButton = function(button){
3312 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3316 var handleHide = function(){
3318 dlg.el.removeClass(opt.cls);
3321 // Roo.TaskMgr.stop(waitTimer);
3322 // waitTimer = null;
3327 var updateButtons = function(b){
3330 buttons["ok"].hide();
3331 buttons["cancel"].hide();
3332 buttons["yes"].hide();
3333 buttons["no"].hide();
3334 dlg.footerEl.hide();
3338 dlg.footerEl.show();
3339 for(var k in buttons){
3340 if(typeof buttons[k] != "function"){
3343 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3344 width += buttons[k].el.getWidth()+15;
3354 var handleEsc = function(d, k, e){
3355 if(opt && opt.closable !== false){
3365 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3366 * @return {Roo.BasicDialog} The BasicDialog element
3368 getDialog : function(){
3370 dlg = new Roo.bootstrap.Modal( {
3373 //constraintoviewport:false,
3375 //collapsible : false,
3380 //buttonAlign:"center",
3381 closeClick : function(){
3382 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3385 handleButton("cancel");
3390 dlg.on("hide", handleHide);
3392 //dlg.addKeyListener(27, handleEsc);
3394 this.buttons = buttons;
3395 var bt = this.buttonText;
3396 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3397 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3398 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3399 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3401 bodyEl = dlg.bodyEl.createChild({
3403 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3404 '<textarea class="roo-mb-textarea"></textarea>' +
3405 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3407 msgEl = bodyEl.dom.firstChild;
3408 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3409 textboxEl.enableDisplayMode();
3410 textboxEl.addKeyListener([10,13], function(){
3411 if(dlg.isVisible() && opt && opt.buttons){
3414 }else if(opt.buttons.yes){
3415 handleButton("yes");
3419 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3420 textareaEl.enableDisplayMode();
3421 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3422 progressEl.enableDisplayMode();
3424 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3425 var pf = progressEl.dom.firstChild;
3427 pp = Roo.get(pf.firstChild);
3428 pp.setHeight(pf.offsetHeight);
3436 * Updates the message box body text
3437 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3438 * the XHTML-compliant non-breaking space character '&#160;')
3439 * @return {Roo.MessageBox} This message box
3441 updateText : function(text)
3443 if(!dlg.isVisible() && !opt.width){
3444 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3445 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3447 msgEl.innerHTML = text || ' ';
3449 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3450 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3452 Math.min(opt.width || cw , this.maxWidth),
3453 Math.max(opt.minWidth || this.minWidth, bwidth)
3456 activeTextEl.setWidth(w);
3458 if(dlg.isVisible()){
3459 dlg.fixedcenter = false;
3461 // to big, make it scroll. = But as usual stupid IE does not support
3464 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3465 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3466 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3468 bodyEl.dom.style.height = '';
3469 bodyEl.dom.style.overflowY = '';
3472 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3474 bodyEl.dom.style.overflowX = '';
3477 dlg.setContentSize(w, bodyEl.getHeight());
3478 if(dlg.isVisible()){
3479 dlg.fixedcenter = true;
3485 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3486 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3487 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3488 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3489 * @return {Roo.MessageBox} This message box
3491 updateProgress : function(value, text){
3493 this.updateText(text);
3496 if (pp) { // weird bug on my firefox - for some reason this is not defined
3497 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3498 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3504 * Returns true if the message box is currently displayed
3505 * @return {Boolean} True if the message box is visible, else false
3507 isVisible : function(){
3508 return dlg && dlg.isVisible();
3512 * Hides the message box if it is displayed
3515 if(this.isVisible()){
3521 * Displays a new message box, or reinitializes an existing message box, based on the config options
3522 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3523 * The following config object properties are supported:
3525 Property Type Description
3526 ---------- --------------- ------------------------------------------------------------------------------------
3527 animEl String/Element An id or Element from which the message box should animate as it opens and
3528 closes (defaults to undefined)
3529 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3530 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3531 closable Boolean False to hide the top-right close button (defaults to true). Note that
3532 progress and wait dialogs will ignore this property and always hide the
3533 close button as they can only be closed programmatically.
3534 cls String A custom CSS class to apply to the message box element
3535 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3536 displayed (defaults to 75)
3537 fn Function A callback function to execute after closing the dialog. The arguments to the
3538 function will be btn (the name of the button that was clicked, if applicable,
3539 e.g. "ok"), and text (the value of the active text field, if applicable).
3540 Progress and wait dialogs will ignore this option since they do not respond to
3541 user actions and can only be closed programmatically, so any required function
3542 should be called by the same code after it closes the dialog.
3543 icon String A CSS class that provides a background image to be used as an icon for
3544 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3545 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3546 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3547 modal Boolean False to allow user interaction with the page while the message box is
3548 displayed (defaults to true)
3549 msg String A string that will replace the existing message box body text (defaults
3550 to the XHTML-compliant non-breaking space character ' ')
3551 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3552 progress Boolean True to display a progress bar (defaults to false)
3553 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3554 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3555 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3556 title String The title text
3557 value String The string value to set into the active textbox element if displayed
3558 wait Boolean True to display a progress bar (defaults to false)
3559 width Number The width of the dialog in pixels
3566 msg: 'Please enter your address:',
3568 buttons: Roo.MessageBox.OKCANCEL,
3571 animEl: 'addAddressBtn'
3574 * @param {Object} config Configuration options
3575 * @return {Roo.MessageBox} This message box
3577 show : function(options)
3580 // this causes nightmares if you show one dialog after another
3581 // especially on callbacks..
3583 if(this.isVisible()){
3586 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3587 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3588 Roo.log("New Dialog Message:" + options.msg )
3589 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3590 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3593 var d = this.getDialog();
3595 d.setTitle(opt.title || " ");
3596 d.closeEl.setDisplayed(opt.closable !== false);
3597 activeTextEl = textboxEl;
3598 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3603 textareaEl.setHeight(typeof opt.multiline == "number" ?
3604 opt.multiline : this.defaultTextHeight);
3605 activeTextEl = textareaEl;
3614 progressEl.setDisplayed(opt.progress === true);
3616 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3618 this.updateProgress(0);
3619 activeTextEl.dom.value = opt.value || "";
3621 dlg.setDefaultButton(activeTextEl);
3623 var bs = opt.buttons;
3627 }else if(bs && bs.yes){
3628 db = buttons["yes"];
3630 dlg.setDefaultButton(db);
3632 bwidth = updateButtons(opt.buttons);
3633 this.updateText(opt.msg);
3635 d.el.addClass(opt.cls);
3637 d.proxyDrag = opt.proxyDrag === true;
3638 d.modal = opt.modal !== false;
3639 d.mask = opt.modal !== false ? mask : false;
3641 // force it to the end of the z-index stack so it gets a cursor in FF
3642 document.body.appendChild(dlg.el.dom);
3643 d.animateTarget = null;
3644 d.show(options.animEl);
3650 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3651 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3652 * and closing the message box when the process is complete.
3653 * @param {String} title The title bar text
3654 * @param {String} msg The message box body text
3655 * @return {Roo.MessageBox} This message box
3657 progress : function(title, msg){
3664 minWidth: this.minProgressWidth,
3671 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3672 * If a callback function is passed it will be called after the user clicks the button, and the
3673 * id of the button that was clicked will be passed as the only parameter to the callback
3674 * (could also be the top-right close button).
3675 * @param {String} title The title bar text
3676 * @param {String} msg The message box body text
3677 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3678 * @param {Object} scope (optional) The scope of the callback function
3679 * @return {Roo.MessageBox} This message box
3681 alert : function(title, msg, fn, scope)
3696 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3697 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3698 * You are responsible for closing the message box when the process is complete.
3699 * @param {String} msg The message box body text
3700 * @param {String} title (optional) The title bar text
3701 * @return {Roo.MessageBox} This message box
3703 wait : function(msg, title){
3714 waitTimer = Roo.TaskMgr.start({
3716 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3724 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3725 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3726 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3727 * @param {String} title The title bar text
3728 * @param {String} msg The message box body text
3729 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3730 * @param {Object} scope (optional) The scope of the callback function
3731 * @return {Roo.MessageBox} This message box
3733 confirm : function(title, msg, fn, scope){
3737 buttons: this.YESNO,
3746 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3747 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3748 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3749 * (could also be the top-right close button) and the text that was entered will be passed as the two
3750 * parameters to the callback.
3751 * @param {String} title The title bar text
3752 * @param {String} msg The message box body text
3753 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3754 * @param {Object} scope (optional) The scope of the callback function
3755 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3756 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3757 * @return {Roo.MessageBox} This message box
3759 prompt : function(title, msg, fn, scope, multiline){
3763 buttons: this.OKCANCEL,
3768 multiline: multiline,
3775 * Button config that displays a single OK button
3780 * Button config that displays Yes and No buttons
3783 YESNO : {yes:true, no:true},
3785 * Button config that displays OK and Cancel buttons
3788 OKCANCEL : {ok:true, cancel:true},
3790 * Button config that displays Yes, No and Cancel buttons
3793 YESNOCANCEL : {yes:true, no:true, cancel:true},
3796 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3799 defaultTextHeight : 75,
3801 * The maximum width in pixels of the message box (defaults to 600)
3806 * The minimum width in pixels of the message box (defaults to 100)
3811 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3812 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3815 minProgressWidth : 250,
3817 * An object containing the default button text strings that can be overriden for localized language support.
3818 * Supported properties are: ok, cancel, yes and no.
3819 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3832 * Shorthand for {@link Roo.MessageBox}
3834 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3835 Roo.Msg = Roo.Msg || Roo.MessageBox;
3844 * @class Roo.bootstrap.Navbar
3845 * @extends Roo.bootstrap.Component
3846 * Bootstrap Navbar class
3849 * Create a new Navbar
3850 * @param {Object} config The config object
3854 Roo.bootstrap.Navbar = function(config){
3855 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3859 * @event beforetoggle
3860 * Fire before toggle the menu
3861 * @param {Roo.EventObject} e
3863 "beforetoggle" : true
3867 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3876 getAutoCreate : function(){
3879 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3883 initEvents :function ()
3885 //Roo.log(this.el.select('.navbar-toggle',true));
3886 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
3893 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3895 var size = this.el.getSize();
3896 this.maskEl.setSize(size.width, size.height);
3897 this.maskEl.enableDisplayMode("block");
3906 getChildContainer : function()
3908 if (this.el && this.el.select('.collapse').getCount()) {
3909 return this.el.select('.collapse',true).first();
3924 onToggle : function()
3927 if(this.fireEvent('beforetoggle', this) === false){
3930 var ce = this.el.select('.navbar-collapse',true).first();
3932 if (!ce.hasClass('show')) {
3942 * Expand the navbar pulldown
3944 expand : function ()
3947 var ce = this.el.select('.navbar-collapse',true).first();
3948 if (ce.hasClass('collapsing')) {
3951 ce.dom.style.height = '';
3953 ce.addClass('in'); // old...
3954 ce.removeClass('collapse');
3955 ce.addClass('show');
3956 var h = ce.getHeight();
3958 ce.removeClass('show');
3959 // at this point we should be able to see it..
3960 ce.addClass('collapsing');
3962 ce.setHeight(0); // resize it ...
3963 ce.on('transitionend', function() {
3964 //Roo.log('done transition');
3965 ce.removeClass('collapsing');
3966 ce.addClass('show');
3967 ce.removeClass('collapse');
3969 ce.dom.style.height = '';
3970 }, this, { single: true} );
3972 ce.dom.scrollTop = 0;
3975 * Collapse the navbar pulldown
3977 collapse : function()
3979 var ce = this.el.select('.navbar-collapse',true).first();
3981 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
3982 // it's collapsed or collapsing..
3985 ce.removeClass('in'); // old...
3986 ce.setHeight(ce.getHeight());
3987 ce.removeClass('show');
3988 ce.addClass('collapsing');
3990 ce.on('transitionend', function() {
3991 ce.dom.style.height = '';
3992 ce.removeClass('collapsing');
3993 ce.addClass('collapse');
3994 }, this, { single: true} );
4014 * @class Roo.bootstrap.NavSimplebar
4015 * @extends Roo.bootstrap.Navbar
4016 * Bootstrap Sidebar class
4018 * @cfg {Boolean} inverse is inverted color
4020 * @cfg {String} type (nav | pills | tabs)
4021 * @cfg {Boolean} arrangement stacked | justified
4022 * @cfg {String} align (left | right) alignment
4024 * @cfg {Boolean} main (true|false) main nav bar? default false
4025 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4027 * @cfg {String} tag (header|footer|nav|div) default is nav
4029 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4033 * Create a new Sidebar
4034 * @param {Object} config The config object
4038 Roo.bootstrap.NavSimplebar = function(config){
4039 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4042 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
4058 getAutoCreate : function(){
4062 tag : this.tag || 'div',
4063 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
4065 if (['light','white'].indexOf(this.weight) > -1) {
4066 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4068 cfg.cls += ' bg-' + this.weight;
4071 cfg.cls += ' navbar-inverse';
4075 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4077 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
4086 cls: 'nav nav-' + this.xtype,
4092 this.type = this.type || 'nav';
4093 if (['tabs','pills'].indexOf(this.type) != -1) {
4094 cfg.cn[0].cls += ' nav-' + this.type
4098 if (this.type!=='nav') {
4099 Roo.log('nav type must be nav/tabs/pills')
4101 cfg.cn[0].cls += ' navbar-nav'
4107 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4108 cfg.cn[0].cls += ' nav-' + this.arrangement;
4112 if (this.align === 'right') {
4113 cfg.cn[0].cls += ' navbar-right';
4138 * navbar-expand-md fixed-top
4142 * @class Roo.bootstrap.NavHeaderbar
4143 * @extends Roo.bootstrap.NavSimplebar
4144 * Bootstrap Sidebar class
4146 * @cfg {String} brand what is brand
4147 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4148 * @cfg {String} brand_href href of the brand
4149 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
4150 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4151 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4152 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4155 * Create a new Sidebar
4156 * @param {Object} config The config object
4160 Roo.bootstrap.NavHeaderbar = function(config){
4161 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4165 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
4172 desktopCenter : false,
4175 getAutoCreate : function(){
4178 tag: this.nav || 'nav',
4179 cls: 'navbar navbar-expand-md',
4185 if (this.desktopCenter) {
4186 cn.push({cls : 'container', cn : []});
4194 cls: 'navbar-toggle navbar-toggler',
4195 'data-toggle': 'collapse',
4200 html: 'Toggle navigation'
4204 cls: 'icon-bar navbar-toggler-icon'
4217 cn.push( Roo.bootstrap.version == 4 ? btn : {
4219 cls: 'navbar-header',
4228 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
4232 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4234 if (['light','white'].indexOf(this.weight) > -1) {
4235 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4237 cfg.cls += ' bg-' + this.weight;
4240 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4241 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4243 // tag can override this..
4245 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4248 if (this.brand !== '') {
4249 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4250 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4252 href: this.brand_href ? this.brand_href : '#',
4253 cls: 'navbar-brand',
4261 cfg.cls += ' main-nav';
4269 getHeaderChildContainer : function()
4271 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4272 return this.el.select('.navbar-header',true).first();
4275 return this.getChildContainer();
4279 initEvents : function()
4281 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4283 if (this.autohide) {
4288 Roo.get(document).on('scroll',function(e) {
4289 var ns = Roo.get(document).getScroll().top;
4290 var os = prevScroll;
4294 ft.removeClass('slideDown');
4295 ft.addClass('slideUp');
4298 ft.removeClass('slideUp');
4299 ft.addClass('slideDown');
4320 * @class Roo.bootstrap.NavSidebar
4321 * @extends Roo.bootstrap.Navbar
4322 * Bootstrap Sidebar class
4325 * Create a new Sidebar
4326 * @param {Object} config The config object
4330 Roo.bootstrap.NavSidebar = function(config){
4331 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4334 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4336 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4338 getAutoCreate : function(){
4343 cls: 'sidebar sidebar-nav'
4365 * @class Roo.bootstrap.NavGroup
4366 * @extends Roo.bootstrap.Component
4367 * Bootstrap NavGroup class
4368 * @cfg {String} align (left|right)
4369 * @cfg {Boolean} inverse
4370 * @cfg {String} type (nav|pills|tab) default nav
4371 * @cfg {String} navId - reference Id for navbar.
4375 * Create a new nav group
4376 * @param {Object} config The config object
4379 Roo.bootstrap.NavGroup = function(config){
4380 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4383 Roo.bootstrap.NavGroup.register(this);
4387 * Fires when the active item changes
4388 * @param {Roo.bootstrap.NavGroup} this
4389 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4390 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4397 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4408 getAutoCreate : function()
4410 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4416 if (Roo.bootstrap.version == 4) {
4417 if (['tabs','pills'].indexOf(this.type) != -1) {
4418 cfg.cls += ' nav-' + this.type;
4420 // trying to remove so header bar can right align top?
4421 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
4422 // do not use on header bar...
4423 cfg.cls += ' navbar-nav';
4428 if (['tabs','pills'].indexOf(this.type) != -1) {
4429 cfg.cls += ' nav-' + this.type
4431 if (this.type !== 'nav') {
4432 Roo.log('nav type must be nav/tabs/pills')
4434 cfg.cls += ' navbar-nav'
4438 if (this.parent() && this.parent().sidebar) {
4441 cls: 'dashboard-menu sidebar-menu'
4447 if (this.form === true) {
4450 cls: 'navbar-form form-inline'
4452 //nav navbar-right ml-md-auto
4453 if (this.align === 'right') {
4454 cfg.cls += ' navbar-right ml-md-auto';
4456 cfg.cls += ' navbar-left';
4460 if (this.align === 'right') {
4461 cfg.cls += ' navbar-right ml-md-auto';
4463 cfg.cls += ' mr-auto';
4467 cfg.cls += ' navbar-inverse';
4475 * sets the active Navigation item
4476 * @param {Roo.bootstrap.NavItem} the new current navitem
4478 setActiveItem : function(item)
4481 Roo.each(this.navItems, function(v){
4486 v.setActive(false, true);
4493 item.setActive(true, true);
4494 this.fireEvent('changed', this, item, prev);
4499 * gets the active Navigation item
4500 * @return {Roo.bootstrap.NavItem} the current navitem
4502 getActive : function()
4506 Roo.each(this.navItems, function(v){
4517 indexOfNav : function()
4521 Roo.each(this.navItems, function(v,i){
4532 * adds a Navigation item
4533 * @param {Roo.bootstrap.NavItem} the navitem to add
4535 addItem : function(cfg)
4537 if (this.form && Roo.bootstrap.version == 4) {
4540 var cn = new Roo.bootstrap.NavItem(cfg);
4542 cn.parentId = this.id;
4543 cn.onRender(this.el, null);
4547 * register a Navigation item
4548 * @param {Roo.bootstrap.NavItem} the navitem to add
4550 register : function(item)
4552 this.navItems.push( item);
4553 item.navId = this.navId;
4558 * clear all the Navigation item
4561 clearAll : function()
4564 this.el.dom.innerHTML = '';
4567 getNavItem: function(tabId)
4570 Roo.each(this.navItems, function(e) {
4571 if (e.tabId == tabId) {
4581 setActiveNext : function()
4583 var i = this.indexOfNav(this.getActive());
4584 if (i > this.navItems.length) {
4587 this.setActiveItem(this.navItems[i+1]);
4589 setActivePrev : function()
4591 var i = this.indexOfNav(this.getActive());
4595 this.setActiveItem(this.navItems[i-1]);
4597 clearWasActive : function(except) {
4598 Roo.each(this.navItems, function(e) {
4599 if (e.tabId != except.tabId && e.was_active) {
4600 e.was_active = false;
4607 getWasActive : function ()
4610 Roo.each(this.navItems, function(e) {
4625 Roo.apply(Roo.bootstrap.NavGroup, {
4629 * register a Navigation Group
4630 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4632 register : function(navgrp)
4634 this.groups[navgrp.navId] = navgrp;
4638 * fetch a Navigation Group based on the navigation ID
4639 * @param {string} the navgroup to add
4640 * @returns {Roo.bootstrap.NavGroup} the navgroup
4642 get: function(navId) {
4643 if (typeof(this.groups[navId]) == 'undefined') {
4645 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4647 return this.groups[navId] ;
4662 * @class Roo.bootstrap.NavItem
4663 * @extends Roo.bootstrap.Component
4664 * Bootstrap Navbar.NavItem class
4665 * @cfg {String} href link to
4666 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4668 * @cfg {String} html content of button
4669 * @cfg {String} badge text inside badge
4670 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4671 * @cfg {String} glyphicon DEPRICATED - use fa
4672 * @cfg {String} icon DEPRICATED - use fa
4673 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4674 * @cfg {Boolean} active Is item active
4675 * @cfg {Boolean} disabled Is item disabled
4677 * @cfg {Boolean} preventDefault (true | false) default false
4678 * @cfg {String} tabId the tab that this item activates.
4679 * @cfg {String} tagtype (a|span) render as a href or span?
4680 * @cfg {Boolean} animateRef (true|false) link to element default false
4683 * Create a new Navbar Item
4684 * @param {Object} config The config object
4686 Roo.bootstrap.NavItem = function(config){
4687 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4692 * The raw click event for the entire grid.
4693 * @param {Roo.EventObject} e
4698 * Fires when the active item active state changes
4699 * @param {Roo.bootstrap.NavItem} this
4700 * @param {boolean} state the new state
4706 * Fires when scroll to element
4707 * @param {Roo.bootstrap.NavItem} this
4708 * @param {Object} options
4709 * @param {Roo.EventObject} e
4717 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4726 preventDefault : false,
4734 button_outline : false,
4738 getAutoCreate : function(){
4746 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4748 if (this.disabled) {
4749 cfg.cls += ' disabled';
4753 if (this.button_weight.length) {
4754 cfg.tag = this.href ? 'a' : 'button';
4755 cfg.html = this.html || '';
4756 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
4758 cfg.href = this.href;
4761 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
4764 // menu .. should add dropdown-menu class - so no need for carat..
4766 if (this.badge !== '') {
4768 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4773 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4777 href : this.href || "#",
4778 html: this.html || ''
4781 if (this.tagtype == 'a') {
4782 cfg.cn[0].cls = 'nav-link';
4785 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
4788 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
4790 if(this.glyphicon) {
4791 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4796 cfg.cn[0].html += " <span class='caret'></span>";
4800 if (this.badge !== '') {
4802 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4810 onRender : function(ct, position)
4812 // Roo.log("Call onRender: " + this.xtype);
4813 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4817 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4818 this.navLink = this.el.select('.nav-link',true).first();
4823 initEvents: function()
4825 if (typeof (this.menu) != 'undefined') {
4826 this.menu.parentType = this.xtype;
4827 this.menu.triggerEl = this.el;
4828 this.menu = this.addxtype(Roo.apply({}, this.menu));
4831 this.el.select('a',true).on('click', this.onClick, this);
4833 if(this.tagtype == 'span'){
4834 this.el.select('span',true).on('click', this.onClick, this);
4837 // at this point parent should be available..
4838 this.parent().register(this);
4841 onClick : function(e)
4843 if (e.getTarget('.dropdown-menu-item')) {
4844 // did you click on a menu itemm.... - then don't trigger onclick..
4849 this.preventDefault ||
4852 Roo.log("NavItem - prevent Default?");
4856 if (this.disabled) {
4860 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4861 if (tg && tg.transition) {
4862 Roo.log("waiting for the transitionend");
4868 //Roo.log("fire event clicked");
4869 if(this.fireEvent('click', this, e) === false){
4873 if(this.tagtype == 'span'){
4877 //Roo.log(this.href);
4878 var ael = this.el.select('a',true).first();
4881 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4882 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4883 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4884 return; // ignore... - it's a 'hash' to another page.
4886 Roo.log("NavItem - prevent Default?");
4888 this.scrollToElement(e);
4892 var p = this.parent();
4894 if (['tabs','pills'].indexOf(p.type)!==-1) {
4895 if (typeof(p.setActiveItem) !== 'undefined') {
4896 p.setActiveItem(this);
4900 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4901 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4902 // remove the collapsed menu expand...
4903 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
4907 isActive: function () {
4910 setActive : function(state, fire, is_was_active)
4912 if (this.active && !state && this.navId) {
4913 this.was_active = true;
4914 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4916 nv.clearWasActive(this);
4920 this.active = state;
4923 this.el.removeClass('active');
4924 this.navLink ? this.navLink.removeClass('active') : false;
4925 } else if (!this.el.hasClass('active')) {
4927 this.el.addClass('active');
4928 if (Roo.bootstrap.version == 4 && this.navLink ) {
4929 this.navLink.addClass('active');
4934 this.fireEvent('changed', this, state);
4937 // show a panel if it's registered and related..
4939 if (!this.navId || !this.tabId || !state || is_was_active) {
4943 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4947 var pan = tg.getPanelByName(this.tabId);
4951 // if we can not flip to new panel - go back to old nav highlight..
4952 if (false == tg.showPanel(pan)) {
4953 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4955 var onav = nv.getWasActive();
4957 onav.setActive(true, false, true);
4966 // this should not be here...
4967 setDisabled : function(state)
4969 this.disabled = state;
4971 this.el.removeClass('disabled');
4972 } else if (!this.el.hasClass('disabled')) {
4973 this.el.addClass('disabled');
4979 * Fetch the element to display the tooltip on.
4980 * @return {Roo.Element} defaults to this.el
4982 tooltipEl : function()
4984 return this.el.select('' + this.tagtype + '', true).first();
4987 scrollToElement : function(e)
4989 var c = document.body;
4992 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4994 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4995 c = document.documentElement;
4998 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
5004 var o = target.calcOffsetsTo(c);
5011 this.fireEvent('scrollto', this, options, e);
5013 Roo.get(c).scrollTo('top', options.value, true);
5026 * <span> icon </span>
5027 * <span> text </span>
5028 * <span>badge </span>
5032 * @class Roo.bootstrap.NavSidebarItem
5033 * @extends Roo.bootstrap.NavItem
5034 * Bootstrap Navbar.NavSidebarItem class
5035 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5036 * {Boolean} open is the menu open
5037 * {Boolean} buttonView use button as the tigger el rather that a (default false)
5038 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5039 * {String} buttonSize (sm|md|lg)the extra classes for the button
5040 * {Boolean} showArrow show arrow next to the text (default true)
5042 * Create a new Navbar Button
5043 * @param {Object} config The config object
5045 Roo.bootstrap.NavSidebarItem = function(config){
5046 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5051 * The raw click event for the entire grid.
5052 * @param {Roo.EventObject} e
5057 * Fires when the active item active state changes
5058 * @param {Roo.bootstrap.NavSidebarItem} this
5059 * @param {boolean} state the new state
5067 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
5069 badgeWeight : 'default',
5075 buttonWeight : 'default',
5081 getAutoCreate : function(){
5086 href : this.href || '#',
5092 if(this.buttonView){
5095 href : this.href || '#',
5096 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5109 cfg.cls += ' active';
5112 if (this.disabled) {
5113 cfg.cls += ' disabled';
5116 cfg.cls += ' open x-open';
5119 if (this.glyphicon || this.icon) {
5120 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
5121 a.cn.push({ tag : 'i', cls : c }) ;
5124 if(!this.buttonView){
5127 html : this.html || ''
5134 if (this.badge !== '') {
5135 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
5141 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5144 a.cls += ' dropdown-toggle treeview' ;
5150 initEvents : function()
5152 if (typeof (this.menu) != 'undefined') {
5153 this.menu.parentType = this.xtype;
5154 this.menu.triggerEl = this.el;
5155 this.menu = this.addxtype(Roo.apply({}, this.menu));
5158 this.el.on('click', this.onClick, this);
5160 if(this.badge !== ''){
5161 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5166 onClick : function(e)
5173 if(this.preventDefault){
5177 this.fireEvent('click', this, e);
5180 disable : function()
5182 this.setDisabled(true);
5187 this.setDisabled(false);
5190 setDisabled : function(state)
5192 if(this.disabled == state){
5196 this.disabled = state;
5199 this.el.addClass('disabled');
5203 this.el.removeClass('disabled');
5208 setActive : function(state)
5210 if(this.active == state){
5214 this.active = state;
5217 this.el.addClass('active');
5221 this.el.removeClass('active');
5226 isActive: function ()
5231 setBadge : function(str)
5237 this.badgeEl.dom.innerHTML = str;
5254 * @class Roo.bootstrap.Row
5255 * @extends Roo.bootstrap.Component
5256 * Bootstrap Row class (contains columns...)
5260 * @param {Object} config The config object
5263 Roo.bootstrap.Row = function(config){
5264 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5267 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5269 getAutoCreate : function(){
5288 * @class Roo.bootstrap.Element
5289 * @extends Roo.bootstrap.Component
5290 * Bootstrap Element class
5291 * @cfg {String} html contents of the element
5292 * @cfg {String} tag tag of the element
5293 * @cfg {String} cls class of the element
5294 * @cfg {Boolean} preventDefault (true|false) default false
5295 * @cfg {Boolean} clickable (true|false) default false
5298 * Create a new Element
5299 * @param {Object} config The config object
5302 Roo.bootstrap.Element = function(config){
5303 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5309 * When a element is chick
5310 * @param {Roo.bootstrap.Element} this
5311 * @param {Roo.EventObject} e
5317 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5322 preventDefault: false,
5325 getAutoCreate : function(){
5329 // cls: this.cls, double assign in parent class Component.js :: onRender
5336 initEvents: function()
5338 Roo.bootstrap.Element.superclass.initEvents.call(this);
5341 this.el.on('click', this.onClick, this);
5346 onClick : function(e)
5348 if(this.preventDefault){
5352 this.fireEvent('click', this, e);
5355 getValue : function()
5357 return this.el.dom.innerHTML;
5360 setValue : function(value)
5362 this.el.dom.innerHTML = value;
5377 * @class Roo.bootstrap.Pagination
5378 * @extends Roo.bootstrap.Component
5379 * Bootstrap Pagination class
5380 * @cfg {String} size xs | sm | md | lg
5381 * @cfg {Boolean} inverse false | true
5384 * Create a new Pagination
5385 * @param {Object} config The config object
5388 Roo.bootstrap.Pagination = function(config){
5389 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5392 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5398 getAutoCreate : function(){
5404 cfg.cls += ' inverse';
5410 cfg.cls += " " + this.cls;
5428 * @class Roo.bootstrap.PaginationItem
5429 * @extends Roo.bootstrap.Component
5430 * Bootstrap PaginationItem class
5431 * @cfg {String} html text
5432 * @cfg {String} href the link
5433 * @cfg {Boolean} preventDefault (true | false) default true
5434 * @cfg {Boolean} active (true | false) default false
5435 * @cfg {Boolean} disabled default false
5439 * Create a new PaginationItem
5440 * @param {Object} config The config object
5444 Roo.bootstrap.PaginationItem = function(config){
5445 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5450 * The raw click event for the entire grid.
5451 * @param {Roo.EventObject} e
5457 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5461 preventDefault: true,
5466 getAutoCreate : function(){
5472 href : this.href ? this.href : '#',
5473 html : this.html ? this.html : ''
5483 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5487 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5493 initEvents: function() {
5495 this.el.on('click', this.onClick, this);
5498 onClick : function(e)
5500 Roo.log('PaginationItem on click ');
5501 if(this.preventDefault){
5509 this.fireEvent('click', this, e);
5525 * @class Roo.bootstrap.Slider
5526 * @extends Roo.bootstrap.Component
5527 * Bootstrap Slider class
5530 * Create a new Slider
5531 * @param {Object} config The config object
5534 Roo.bootstrap.Slider = function(config){
5535 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5538 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5540 getAutoCreate : function(){
5544 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5548 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5560 * Ext JS Library 1.1.1
5561 * Copyright(c) 2006-2007, Ext JS, LLC.
5563 * Originally Released Under LGPL - original licence link has changed is not relivant.
5566 * <script type="text/javascript">
5571 * @class Roo.grid.ColumnModel
5572 * @extends Roo.util.Observable
5573 * This is the default implementation of a ColumnModel used by the Grid. It defines
5574 * the columns in the grid.
5577 var colModel = new Roo.grid.ColumnModel([
5578 {header: "Ticker", width: 60, sortable: true, locked: true},
5579 {header: "Company Name", width: 150, sortable: true},
5580 {header: "Market Cap.", width: 100, sortable: true},
5581 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5582 {header: "Employees", width: 100, sortable: true, resizable: false}
5587 * The config options listed for this class are options which may appear in each
5588 * individual column definition.
5589 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5591 * @param {Object} config An Array of column config objects. See this class's
5592 * config objects for details.
5594 Roo.grid.ColumnModel = function(config){
5596 * The config passed into the constructor
5598 this.config = config;
5601 // if no id, create one
5602 // if the column does not have a dataIndex mapping,
5603 // map it to the order it is in the config
5604 for(var i = 0, len = config.length; i < len; i++){
5606 if(typeof c.dataIndex == "undefined"){
5609 if(typeof c.renderer == "string"){
5610 c.renderer = Roo.util.Format[c.renderer];
5612 if(typeof c.id == "undefined"){
5615 if(c.editor && c.editor.xtype){
5616 c.editor = Roo.factory(c.editor, Roo.grid);
5618 if(c.editor && c.editor.isFormField){
5619 c.editor = new Roo.grid.GridEditor(c.editor);
5621 this.lookup[c.id] = c;
5625 * The width of columns which have no width specified (defaults to 100)
5628 this.defaultWidth = 100;
5631 * Default sortable of columns which have no sortable specified (defaults to false)
5634 this.defaultSortable = false;
5638 * @event widthchange
5639 * Fires when the width of a column changes.
5640 * @param {ColumnModel} this
5641 * @param {Number} columnIndex The column index
5642 * @param {Number} newWidth The new width
5644 "widthchange": true,
5646 * @event headerchange
5647 * Fires when the text of a header changes.
5648 * @param {ColumnModel} this
5649 * @param {Number} columnIndex The column index
5650 * @param {Number} newText The new header text
5652 "headerchange": true,
5654 * @event hiddenchange
5655 * Fires when a column is hidden or "unhidden".
5656 * @param {ColumnModel} this
5657 * @param {Number} columnIndex The column index
5658 * @param {Boolean} hidden true if hidden, false otherwise
5660 "hiddenchange": true,
5662 * @event columnmoved
5663 * Fires when a column is moved.
5664 * @param {ColumnModel} this
5665 * @param {Number} oldIndex
5666 * @param {Number} newIndex
5668 "columnmoved" : true,
5670 * @event columlockchange
5671 * Fires when a column's locked state is changed
5672 * @param {ColumnModel} this
5673 * @param {Number} colIndex
5674 * @param {Boolean} locked true if locked
5676 "columnlockchange" : true
5678 Roo.grid.ColumnModel.superclass.constructor.call(this);
5680 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5682 * @cfg {String} header The header text to display in the Grid view.
5685 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5686 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5687 * specified, the column's index is used as an index into the Record's data Array.
5690 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5691 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5694 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5695 * Defaults to the value of the {@link #defaultSortable} property.
5696 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5699 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5702 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5705 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5708 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5711 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5712 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5713 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5714 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5717 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5720 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5723 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5726 * @cfg {String} cursor (Optional)
5729 * @cfg {String} tooltip (Optional)
5732 * @cfg {Number} xs (Optional)
5735 * @cfg {Number} sm (Optional)
5738 * @cfg {Number} md (Optional)
5741 * @cfg {Number} lg (Optional)
5744 * Returns the id of the column at the specified index.
5745 * @param {Number} index The column index
5746 * @return {String} the id
5748 getColumnId : function(index){
5749 return this.config[index].id;
5753 * Returns the column for a specified id.
5754 * @param {String} id The column id
5755 * @return {Object} the column
5757 getColumnById : function(id){
5758 return this.lookup[id];
5763 * Returns the column for a specified dataIndex.
5764 * @param {String} dataIndex The column dataIndex
5765 * @return {Object|Boolean} the column or false if not found
5767 getColumnByDataIndex: function(dataIndex){
5768 var index = this.findColumnIndex(dataIndex);
5769 return index > -1 ? this.config[index] : false;
5773 * Returns the index for a specified column id.
5774 * @param {String} id The column id
5775 * @return {Number} the index, or -1 if not found
5777 getIndexById : function(id){
5778 for(var i = 0, len = this.config.length; i < len; i++){
5779 if(this.config[i].id == id){
5787 * Returns the index for a specified column dataIndex.
5788 * @param {String} dataIndex The column dataIndex
5789 * @return {Number} the index, or -1 if not found
5792 findColumnIndex : function(dataIndex){
5793 for(var i = 0, len = this.config.length; i < len; i++){
5794 if(this.config[i].dataIndex == dataIndex){
5802 moveColumn : function(oldIndex, newIndex){
5803 var c = this.config[oldIndex];
5804 this.config.splice(oldIndex, 1);
5805 this.config.splice(newIndex, 0, c);
5806 this.dataMap = null;
5807 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5810 isLocked : function(colIndex){
5811 return this.config[colIndex].locked === true;
5814 setLocked : function(colIndex, value, suppressEvent){
5815 if(this.isLocked(colIndex) == value){
5818 this.config[colIndex].locked = value;
5820 this.fireEvent("columnlockchange", this, colIndex, value);
5824 getTotalLockedWidth : function(){
5826 for(var i = 0; i < this.config.length; i++){
5827 if(this.isLocked(i) && !this.isHidden(i)){
5828 this.totalWidth += this.getColumnWidth(i);
5834 getLockedCount : function(){
5835 for(var i = 0, len = this.config.length; i < len; i++){
5836 if(!this.isLocked(i)){
5841 return this.config.length;
5845 * Returns the number of columns.
5848 getColumnCount : function(visibleOnly){
5849 if(visibleOnly === true){
5851 for(var i = 0, len = this.config.length; i < len; i++){
5852 if(!this.isHidden(i)){
5858 return this.config.length;
5862 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5863 * @param {Function} fn
5864 * @param {Object} scope (optional)
5865 * @return {Array} result
5867 getColumnsBy : function(fn, scope){
5869 for(var i = 0, len = this.config.length; i < len; i++){
5870 var c = this.config[i];
5871 if(fn.call(scope||this, c, i) === true){
5879 * Returns true if the specified column is sortable.
5880 * @param {Number} col The column index
5883 isSortable : function(col){
5884 if(typeof this.config[col].sortable == "undefined"){
5885 return this.defaultSortable;
5887 return this.config[col].sortable;
5891 * Returns the rendering (formatting) function defined for the column.
5892 * @param {Number} col The column index.
5893 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5895 getRenderer : function(col){
5896 if(!this.config[col].renderer){
5897 return Roo.grid.ColumnModel.defaultRenderer;
5899 return this.config[col].renderer;
5903 * Sets the rendering (formatting) function for a column.
5904 * @param {Number} col The column index
5905 * @param {Function} fn The function to use to process the cell's raw data
5906 * to return HTML markup for the grid view. The render function is called with
5907 * the following parameters:<ul>
5908 * <li>Data value.</li>
5909 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5910 * <li>css A CSS style string to apply to the table cell.</li>
5911 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5912 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5913 * <li>Row index</li>
5914 * <li>Column index</li>
5915 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5917 setRenderer : function(col, fn){
5918 this.config[col].renderer = fn;
5922 * Returns the width for the specified column.
5923 * @param {Number} col The column index
5926 getColumnWidth : function(col){
5927 return this.config[col].width * 1 || this.defaultWidth;
5931 * Sets the width for a column.
5932 * @param {Number} col The column index
5933 * @param {Number} width The new width
5935 setColumnWidth : function(col, width, suppressEvent){
5936 this.config[col].width = width;
5937 this.totalWidth = null;
5939 this.fireEvent("widthchange", this, col, width);
5944 * Returns the total width of all columns.
5945 * @param {Boolean} includeHidden True to include hidden column widths
5948 getTotalWidth : function(includeHidden){
5949 if(!this.totalWidth){
5950 this.totalWidth = 0;
5951 for(var i = 0, len = this.config.length; i < len; i++){
5952 if(includeHidden || !this.isHidden(i)){
5953 this.totalWidth += this.getColumnWidth(i);
5957 return this.totalWidth;
5961 * Returns the header for the specified column.
5962 * @param {Number} col The column index
5965 getColumnHeader : function(col){
5966 return this.config[col].header;
5970 * Sets the header for a column.
5971 * @param {Number} col The column index
5972 * @param {String} header The new header
5974 setColumnHeader : function(col, header){
5975 this.config[col].header = header;
5976 this.fireEvent("headerchange", this, col, header);
5980 * Returns the tooltip for the specified column.
5981 * @param {Number} col The column index
5984 getColumnTooltip : function(col){
5985 return this.config[col].tooltip;
5988 * Sets the tooltip for a column.
5989 * @param {Number} col The column index
5990 * @param {String} tooltip The new tooltip
5992 setColumnTooltip : function(col, tooltip){
5993 this.config[col].tooltip = tooltip;
5997 * Returns the dataIndex for the specified column.
5998 * @param {Number} col The column index
6001 getDataIndex : function(col){
6002 return this.config[col].dataIndex;
6006 * Sets the dataIndex for a column.
6007 * @param {Number} col The column index
6008 * @param {Number} dataIndex The new dataIndex
6010 setDataIndex : function(col, dataIndex){
6011 this.config[col].dataIndex = dataIndex;
6017 * Returns true if the cell is editable.
6018 * @param {Number} colIndex The column index
6019 * @param {Number} rowIndex The row index - this is nto actually used..?
6022 isCellEditable : function(colIndex, rowIndex){
6023 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6027 * Returns the editor defined for the cell/column.
6028 * return false or null to disable editing.
6029 * @param {Number} colIndex The column index
6030 * @param {Number} rowIndex The row index
6033 getCellEditor : function(colIndex, rowIndex){
6034 return this.config[colIndex].editor;
6038 * Sets if a column is editable.
6039 * @param {Number} col The column index
6040 * @param {Boolean} editable True if the column is editable
6042 setEditable : function(col, editable){
6043 this.config[col].editable = editable;
6048 * Returns true if the column is hidden.
6049 * @param {Number} colIndex The column index
6052 isHidden : function(colIndex){
6053 return this.config[colIndex].hidden;
6058 * Returns true if the column width cannot be changed
6060 isFixed : function(colIndex){
6061 return this.config[colIndex].fixed;
6065 * Returns true if the column can be resized
6068 isResizable : function(colIndex){
6069 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6072 * Sets if a column is hidden.
6073 * @param {Number} colIndex The column index
6074 * @param {Boolean} hidden True if the column is hidden
6076 setHidden : function(colIndex, hidden){
6077 this.config[colIndex].hidden = hidden;
6078 this.totalWidth = null;
6079 this.fireEvent("hiddenchange", this, colIndex, hidden);
6083 * Sets the editor for a column.
6084 * @param {Number} col The column index
6085 * @param {Object} editor The editor object
6087 setEditor : function(col, editor){
6088 this.config[col].editor = editor;
6092 Roo.grid.ColumnModel.defaultRenderer = function(value)
6094 if(typeof value == "object") {
6097 if(typeof value == "string" && value.length < 1){
6101 return String.format("{0}", value);
6104 // Alias for backwards compatibility
6105 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6108 * Ext JS Library 1.1.1
6109 * Copyright(c) 2006-2007, Ext JS, LLC.
6111 * Originally Released Under LGPL - original licence link has changed is not relivant.
6114 * <script type="text/javascript">
6118 * @class Roo.LoadMask
6119 * A simple utility class for generically masking elements while loading data. If the element being masked has
6120 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6121 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
6122 * element's UpdateManager load indicator and will be destroyed after the initial load.
6124 * Create a new LoadMask
6125 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6126 * @param {Object} config The config object
6128 Roo.LoadMask = function(el, config){
6129 this.el = Roo.get(el);
6130 Roo.apply(this, config);
6132 this.store.on('beforeload', this.onBeforeLoad, this);
6133 this.store.on('load', this.onLoad, this);
6134 this.store.on('loadexception', this.onLoadException, this);
6135 this.removeMask = false;
6137 var um = this.el.getUpdateManager();
6138 um.showLoadIndicator = false; // disable the default indicator
6139 um.on('beforeupdate', this.onBeforeLoad, this);
6140 um.on('update', this.onLoad, this);
6141 um.on('failure', this.onLoad, this);
6142 this.removeMask = true;
6146 Roo.LoadMask.prototype = {
6148 * @cfg {Boolean} removeMask
6149 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6150 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
6154 * The text to display in a centered loading message box (defaults to 'Loading...')
6158 * @cfg {String} msgCls
6159 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6161 msgCls : 'x-mask-loading',
6164 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6170 * Disables the mask to prevent it from being displayed
6172 disable : function(){
6173 this.disabled = true;
6177 * Enables the mask so that it can be displayed
6179 enable : function(){
6180 this.disabled = false;
6183 onLoadException : function()
6187 if (typeof(arguments[3]) != 'undefined') {
6188 Roo.MessageBox.alert("Error loading",arguments[3]);
6192 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6193 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6200 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6205 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6209 onBeforeLoad : function(){
6211 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6216 destroy : function(){
6218 this.store.un('beforeload', this.onBeforeLoad, this);
6219 this.store.un('load', this.onLoad, this);
6220 this.store.un('loadexception', this.onLoadException, this);
6222 var um = this.el.getUpdateManager();
6223 um.un('beforeupdate', this.onBeforeLoad, this);
6224 um.un('update', this.onLoad, this);
6225 um.un('failure', this.onLoad, this);
6236 * @class Roo.bootstrap.Table
6237 * @extends Roo.bootstrap.Component
6238 * Bootstrap Table class
6239 * @cfg {String} cls table class
6240 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6241 * @cfg {String} bgcolor Specifies the background color for a table
6242 * @cfg {Number} border Specifies whether the table cells should have borders or not
6243 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6244 * @cfg {Number} cellspacing Specifies the space between cells
6245 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6246 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6247 * @cfg {String} sortable Specifies that the table should be sortable
6248 * @cfg {String} summary Specifies a summary of the content of a table
6249 * @cfg {Number} width Specifies the width of a table
6250 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6252 * @cfg {boolean} striped Should the rows be alternative striped
6253 * @cfg {boolean} bordered Add borders to the table
6254 * @cfg {boolean} hover Add hover highlighting
6255 * @cfg {boolean} condensed Format condensed
6256 * @cfg {boolean} responsive Format condensed
6257 * @cfg {Boolean} loadMask (true|false) default false
6258 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6259 * @cfg {Boolean} headerShow (true|false) generate thead, default true
6260 * @cfg {Boolean} rowSelection (true|false) default false
6261 * @cfg {Boolean} cellSelection (true|false) default false
6262 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6263 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
6264 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
6265 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6269 * Create a new Table
6270 * @param {Object} config The config object
6273 Roo.bootstrap.Table = function(config){
6274 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6279 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6280 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6281 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6282 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6284 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6286 this.sm.grid = this;
6287 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6288 this.sm = this.selModel;
6289 this.sm.xmodule = this.xmodule || false;
6292 if (this.cm && typeof(this.cm.config) == 'undefined') {
6293 this.colModel = new Roo.grid.ColumnModel(this.cm);
6294 this.cm = this.colModel;
6295 this.cm.xmodule = this.xmodule || false;
6298 this.store= Roo.factory(this.store, Roo.data);
6299 this.ds = this.store;
6300 this.ds.xmodule = this.xmodule || false;
6303 if (this.footer && this.store) {
6304 this.footer.dataSource = this.ds;
6305 this.footer = Roo.factory(this.footer);
6312 * Fires when a cell is clicked
6313 * @param {Roo.bootstrap.Table} this
6314 * @param {Roo.Element} el
6315 * @param {Number} rowIndex
6316 * @param {Number} columnIndex
6317 * @param {Roo.EventObject} e
6321 * @event celldblclick
6322 * Fires when a cell is double clicked
6323 * @param {Roo.bootstrap.Table} this
6324 * @param {Roo.Element} el
6325 * @param {Number} rowIndex
6326 * @param {Number} columnIndex
6327 * @param {Roo.EventObject} e
6329 "celldblclick" : true,
6332 * Fires when a row is clicked
6333 * @param {Roo.bootstrap.Table} this
6334 * @param {Roo.Element} el
6335 * @param {Number} rowIndex
6336 * @param {Roo.EventObject} e
6340 * @event rowdblclick
6341 * Fires when a row is double clicked
6342 * @param {Roo.bootstrap.Table} this
6343 * @param {Roo.Element} el
6344 * @param {Number} rowIndex
6345 * @param {Roo.EventObject} e
6347 "rowdblclick" : true,
6350 * Fires when a mouseover occur
6351 * @param {Roo.bootstrap.Table} this
6352 * @param {Roo.Element} el
6353 * @param {Number} rowIndex
6354 * @param {Number} columnIndex
6355 * @param {Roo.EventObject} e
6360 * Fires when a mouseout occur
6361 * @param {Roo.bootstrap.Table} this
6362 * @param {Roo.Element} el
6363 * @param {Number} rowIndex
6364 * @param {Number} columnIndex
6365 * @param {Roo.EventObject} e
6370 * Fires when a row is rendered, so you can change add a style to it.
6371 * @param {Roo.bootstrap.Table} this
6372 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6376 * @event rowsrendered
6377 * Fires when all the rows have been rendered
6378 * @param {Roo.bootstrap.Table} this
6380 'rowsrendered' : true,
6382 * @event contextmenu
6383 * The raw contextmenu event for the entire grid.
6384 * @param {Roo.EventObject} e
6386 "contextmenu" : true,
6388 * @event rowcontextmenu
6389 * Fires when a row is right clicked
6390 * @param {Roo.bootstrap.Table} this
6391 * @param {Number} rowIndex
6392 * @param {Roo.EventObject} e
6394 "rowcontextmenu" : true,
6396 * @event cellcontextmenu
6397 * Fires when a cell is right clicked
6398 * @param {Roo.bootstrap.Table} this
6399 * @param {Number} rowIndex
6400 * @param {Number} cellIndex
6401 * @param {Roo.EventObject} e
6403 "cellcontextmenu" : true,
6405 * @event headercontextmenu
6406 * Fires when a header is right clicked
6407 * @param {Roo.bootstrap.Table} this
6408 * @param {Number} columnIndex
6409 * @param {Roo.EventObject} e
6411 "headercontextmenu" : true
6415 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6441 rowSelection : false,
6442 cellSelection : false,
6445 // Roo.Element - the tbody
6447 // Roo.Element - thead element
6450 container: false, // used by gridpanel...
6456 auto_hide_footer : false,
6458 getAutoCreate : function()
6460 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6467 if (this.scrollBody) {
6468 cfg.cls += ' table-body-fixed';
6471 cfg.cls += ' table-striped';
6475 cfg.cls += ' table-hover';
6477 if (this.bordered) {
6478 cfg.cls += ' table-bordered';
6480 if (this.condensed) {
6481 cfg.cls += ' table-condensed';
6483 if (this.responsive) {
6484 cfg.cls += ' table-responsive';
6488 cfg.cls+= ' ' +this.cls;
6491 // this lot should be simplifed...
6504 ].forEach(function(k) {
6512 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6515 if(this.store || this.cm){
6516 if(this.headerShow){
6517 cfg.cn.push(this.renderHeader());
6520 cfg.cn.push(this.renderBody());
6522 if(this.footerShow){
6523 cfg.cn.push(this.renderFooter());
6525 // where does this come from?
6526 //cfg.cls+= ' TableGrid';
6529 return { cn : [ cfg ] };
6532 initEvents : function()
6534 if(!this.store || !this.cm){
6537 if (this.selModel) {
6538 this.selModel.initEvents();
6542 //Roo.log('initEvents with ds!!!!');
6544 this.mainBody = this.el.select('tbody', true).first();
6545 this.mainHead = this.el.select('thead', true).first();
6546 this.mainFoot = this.el.select('tfoot', true).first();
6552 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6553 e.on('click', _this.sort, _this);
6556 this.mainBody.on("click", this.onClick, this);
6557 this.mainBody.on("dblclick", this.onDblClick, this);
6559 // why is this done????? = it breaks dialogs??
6560 //this.parent().el.setStyle('position', 'relative');
6564 this.footer.parentId = this.id;
6565 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6568 this.el.select('tfoot tr td').first().addClass('hide');
6573 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6576 this.store.on('load', this.onLoad, this);
6577 this.store.on('beforeload', this.onBeforeLoad, this);
6578 this.store.on('update', this.onUpdate, this);
6579 this.store.on('add', this.onAdd, this);
6580 this.store.on("clear", this.clear, this);
6582 this.el.on("contextmenu", this.onContextMenu, this);
6584 this.mainBody.on('scroll', this.onBodyScroll, this);
6586 this.cm.on("headerchange", this.onHeaderChange, this);
6588 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6592 onContextMenu : function(e, t)
6594 this.processEvent("contextmenu", e);
6597 processEvent : function(name, e)
6599 if (name != 'touchstart' ) {
6600 this.fireEvent(name, e);
6603 var t = e.getTarget();
6605 var cell = Roo.get(t);
6611 if(cell.findParent('tfoot', false, true)){
6615 if(cell.findParent('thead', false, true)){
6617 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6618 cell = Roo.get(t).findParent('th', false, true);
6620 Roo.log("failed to find th in thead?");
6621 Roo.log(e.getTarget());
6626 var cellIndex = cell.dom.cellIndex;
6628 var ename = name == 'touchstart' ? 'click' : name;
6629 this.fireEvent("header" + ename, this, cellIndex, e);
6634 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6635 cell = Roo.get(t).findParent('td', false, true);
6637 Roo.log("failed to find th in tbody?");
6638 Roo.log(e.getTarget());
6643 var row = cell.findParent('tr', false, true);
6644 var cellIndex = cell.dom.cellIndex;
6645 var rowIndex = row.dom.rowIndex - 1;
6649 this.fireEvent("row" + name, this, rowIndex, e);
6653 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6659 onMouseover : function(e, el)
6661 var cell = Roo.get(el);
6667 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6668 cell = cell.findParent('td', false, true);
6671 var row = cell.findParent('tr', false, true);
6672 var cellIndex = cell.dom.cellIndex;
6673 var rowIndex = row.dom.rowIndex - 1; // start from 0
6675 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6679 onMouseout : function(e, el)
6681 var cell = Roo.get(el);
6687 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6688 cell = cell.findParent('td', false, true);
6691 var row = cell.findParent('tr', false, true);
6692 var cellIndex = cell.dom.cellIndex;
6693 var rowIndex = row.dom.rowIndex - 1; // start from 0
6695 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6699 onClick : function(e, el)
6701 var cell = Roo.get(el);
6703 if(!cell || (!this.cellSelection && !this.rowSelection)){
6707 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6708 cell = cell.findParent('td', false, true);
6711 if(!cell || typeof(cell) == 'undefined'){
6715 var row = cell.findParent('tr', false, true);
6717 if(!row || typeof(row) == 'undefined'){
6721 var cellIndex = cell.dom.cellIndex;
6722 var rowIndex = this.getRowIndex(row);
6724 // why??? - should these not be based on SelectionModel?
6725 if(this.cellSelection){
6726 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6729 if(this.rowSelection){
6730 this.fireEvent('rowclick', this, row, rowIndex, e);
6736 onDblClick : function(e,el)
6738 var cell = Roo.get(el);
6740 if(!cell || (!this.cellSelection && !this.rowSelection)){
6744 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6745 cell = cell.findParent('td', false, true);
6748 if(!cell || typeof(cell) == 'undefined'){
6752 var row = cell.findParent('tr', false, true);
6754 if(!row || typeof(row) == 'undefined'){
6758 var cellIndex = cell.dom.cellIndex;
6759 var rowIndex = this.getRowIndex(row);
6761 if(this.cellSelection){
6762 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6765 if(this.rowSelection){
6766 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6770 sort : function(e,el)
6772 var col = Roo.get(el);
6774 if(!col.hasClass('sortable')){
6778 var sort = col.attr('sort');
6781 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6785 this.store.sortInfo = {field : sort, direction : dir};
6788 Roo.log("calling footer first");
6789 this.footer.onClick('first');
6792 this.store.load({ params : { start : 0 } });
6796 renderHeader : function()
6804 this.totalWidth = 0;
6806 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6808 var config = cm.config[i];
6812 cls : 'x-hcol-' + i,
6814 html: cm.getColumnHeader(i)
6819 if(typeof(config.sortable) != 'undefined' && config.sortable){
6821 c.html = '<i class="glyphicon"></i>' + c.html;
6824 // could use BS4 hidden-..-down
6826 if(typeof(config.lgHeader) != 'undefined'){
6827 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
6830 if(typeof(config.mdHeader) != 'undefined'){
6831 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6834 if(typeof(config.smHeader) != 'undefined'){
6835 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6838 if(typeof(config.xsHeader) != 'undefined'){
6839 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6846 if(typeof(config.tooltip) != 'undefined'){
6847 c.tooltip = config.tooltip;
6850 if(typeof(config.colspan) != 'undefined'){
6851 c.colspan = config.colspan;
6854 if(typeof(config.hidden) != 'undefined' && config.hidden){
6855 c.style += ' display:none;';
6858 if(typeof(config.dataIndex) != 'undefined'){
6859 c.sort = config.dataIndex;
6864 if(typeof(config.align) != 'undefined' && config.align.length){
6865 c.style += ' text-align:' + config.align + ';';
6868 if(typeof(config.width) != 'undefined'){
6869 c.style += ' width:' + config.width + 'px;';
6870 this.totalWidth += config.width;
6872 this.totalWidth += 100; // assume minimum of 100 per column?
6875 if(typeof(config.cls) != 'undefined'){
6876 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6879 ['xs','sm','md','lg'].map(function(size){
6881 if(typeof(config[size]) == 'undefined'){
6885 if (!config[size]) { // 0 = hidden
6886 // BS 4 '0' is treated as hide that column and below.
6887 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
6891 c.cls += ' col-' + size + '-' + config[size] + (
6892 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
6904 renderBody : function()
6914 colspan : this.cm.getColumnCount()
6924 renderFooter : function()
6934 colspan : this.cm.getColumnCount()
6948 // Roo.log('ds onload');
6953 var ds = this.store;
6955 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6956 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6957 if (_this.store.sortInfo) {
6959 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6960 e.select('i', true).addClass(['glyphicon-arrow-up']);
6963 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6964 e.select('i', true).addClass(['glyphicon-arrow-down']);
6969 var tbody = this.mainBody;
6971 if(ds.getCount() > 0){
6972 ds.data.each(function(d,rowIndex){
6973 var row = this.renderRow(cm, ds, rowIndex);
6975 tbody.createChild(row);
6979 if(row.cellObjects.length){
6980 Roo.each(row.cellObjects, function(r){
6981 _this.renderCellObject(r);
6988 var tfoot = this.el.select('tfoot', true).first();
6990 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6992 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6994 var total = this.ds.getTotalCount();
6996 if(this.footer.pageSize < total){
6997 this.mainFoot.show();
7001 Roo.each(this.el.select('tbody td', true).elements, function(e){
7002 e.on('mouseover', _this.onMouseover, _this);
7005 Roo.each(this.el.select('tbody td', true).elements, function(e){
7006 e.on('mouseout', _this.onMouseout, _this);
7008 this.fireEvent('rowsrendered', this);
7014 onUpdate : function(ds,record)
7016 this.refreshRow(record);
7020 onRemove : function(ds, record, index, isUpdate){
7021 if(isUpdate !== true){
7022 this.fireEvent("beforerowremoved", this, index, record);
7024 var bt = this.mainBody.dom;
7026 var rows = this.el.select('tbody > tr', true).elements;
7028 if(typeof(rows[index]) != 'undefined'){
7029 bt.removeChild(rows[index].dom);
7032 // if(bt.rows[index]){
7033 // bt.removeChild(bt.rows[index]);
7036 if(isUpdate !== true){
7037 //this.stripeRows(index);
7038 //this.syncRowHeights(index, index);
7040 this.fireEvent("rowremoved", this, index, record);
7044 onAdd : function(ds, records, rowIndex)
7046 //Roo.log('on Add called');
7047 // - note this does not handle multiple adding very well..
7048 var bt = this.mainBody.dom;
7049 for (var i =0 ; i < records.length;i++) {
7050 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7051 //Roo.log(records[i]);
7052 //Roo.log(this.store.getAt(rowIndex+i));
7053 this.insertRow(this.store, rowIndex + i, false);
7060 refreshRow : function(record){
7061 var ds = this.store, index;
7062 if(typeof record == 'number'){
7064 record = ds.getAt(index);
7066 index = ds.indexOf(record);
7068 this.insertRow(ds, index, true);
7070 this.onRemove(ds, record, index+1, true);
7072 //this.syncRowHeights(index, index);
7074 this.fireEvent("rowupdated", this, index, record);
7077 insertRow : function(dm, rowIndex, isUpdate){
7080 this.fireEvent("beforerowsinserted", this, rowIndex);
7082 //var s = this.getScrollState();
7083 var row = this.renderRow(this.cm, this.store, rowIndex);
7084 // insert before rowIndex..
7085 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7089 if(row.cellObjects.length){
7090 Roo.each(row.cellObjects, function(r){
7091 _this.renderCellObject(r);
7096 this.fireEvent("rowsinserted", this, rowIndex);
7097 //this.syncRowHeights(firstRow, lastRow);
7098 //this.stripeRows(firstRow);
7105 getRowDom : function(rowIndex)
7107 var rows = this.el.select('tbody > tr', true).elements;
7109 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7112 // returns the object tree for a tr..
7115 renderRow : function(cm, ds, rowIndex)
7117 var d = ds.getAt(rowIndex);
7121 cls : 'x-row-' + rowIndex,
7125 var cellObjects = [];
7127 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7128 var config = cm.config[i];
7130 var renderer = cm.getRenderer(i);
7134 if(typeof(renderer) !== 'undefined'){
7135 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7137 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7138 // and are rendered into the cells after the row is rendered - using the id for the element.
7140 if(typeof(value) === 'object'){
7150 rowIndex : rowIndex,
7155 this.fireEvent('rowclass', this, rowcfg);
7159 cls : rowcfg.rowClass + ' x-col-' + i,
7161 html: (typeof(value) === 'object') ? '' : value
7168 if(typeof(config.colspan) != 'undefined'){
7169 td.colspan = config.colspan;
7172 if(typeof(config.hidden) != 'undefined' && config.hidden){
7173 td.style += ' display:none;';
7176 if(typeof(config.align) != 'undefined' && config.align.length){
7177 td.style += ' text-align:' + config.align + ';';
7179 if(typeof(config.valign) != 'undefined' && config.valign.length){
7180 td.style += ' vertical-align:' + config.valign + ';';
7183 if(typeof(config.width) != 'undefined'){
7184 td.style += ' width:' + config.width + 'px;';
7187 if(typeof(config.cursor) != 'undefined'){
7188 td.style += ' cursor:' + config.cursor + ';';
7191 if(typeof(config.cls) != 'undefined'){
7192 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7195 ['xs','sm','md','lg'].map(function(size){
7197 if(typeof(config[size]) == 'undefined'){
7203 if (!config[size]) { // 0 = hidden
7204 // BS 4 '0' is treated as hide that column and below.
7205 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
7209 td.cls += ' col-' + size + '-' + config[size] + (
7210 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7220 row.cellObjects = cellObjects;
7228 onBeforeLoad : function()
7237 this.el.select('tbody', true).first().dom.innerHTML = '';
7240 * Show or hide a row.
7241 * @param {Number} rowIndex to show or hide
7242 * @param {Boolean} state hide
7244 setRowVisibility : function(rowIndex, state)
7246 var bt = this.mainBody.dom;
7248 var rows = this.el.select('tbody > tr', true).elements;
7250 if(typeof(rows[rowIndex]) == 'undefined'){
7253 rows[rowIndex].dom.style.display = state ? '' : 'none';
7257 getSelectionModel : function(){
7259 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7261 return this.selModel;
7264 * Render the Roo.bootstrap object from renderder
7266 renderCellObject : function(r)
7270 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7272 var t = r.cfg.render(r.container);
7275 Roo.each(r.cfg.cn, function(c){
7277 container: t.getChildContainer(),
7280 _this.renderCellObject(child);
7285 getRowIndex : function(row)
7289 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7300 * Returns the grid's underlying element = used by panel.Grid
7301 * @return {Element} The element
7303 getGridEl : function(){
7307 * Forces a resize - used by panel.Grid
7308 * @return {Element} The element
7310 autoSize : function()
7312 //var ctr = Roo.get(this.container.dom.parentElement);
7313 var ctr = Roo.get(this.el.dom);
7315 var thd = this.getGridEl().select('thead',true).first();
7316 var tbd = this.getGridEl().select('tbody', true).first();
7317 var tfd = this.getGridEl().select('tfoot', true).first();
7319 var cw = ctr.getWidth();
7323 tbd.setSize(ctr.getWidth(),
7324 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7326 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7329 cw = Math.max(cw, this.totalWidth);
7330 this.getGridEl().select('tr',true).setWidth(cw);
7331 // resize 'expandable coloumn?
7333 return; // we doe not have a view in this design..
7336 onBodyScroll: function()
7338 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7340 this.mainHead.setStyle({
7341 'position' : 'relative',
7342 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7348 var scrollHeight = this.mainBody.dom.scrollHeight;
7350 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7352 var height = this.mainBody.getHeight();
7354 if(scrollHeight - height == scrollTop) {
7356 var total = this.ds.getTotalCount();
7358 if(this.footer.cursor + this.footer.pageSize < total){
7360 this.footer.ds.load({
7362 start : this.footer.cursor + this.footer.pageSize,
7363 limit : this.footer.pageSize
7373 onHeaderChange : function()
7375 var header = this.renderHeader();
7376 var table = this.el.select('table', true).first();
7378 this.mainHead.remove();
7379 this.mainHead = table.createChild(header, this.mainBody, false);
7382 onHiddenChange : function(colModel, colIndex, hidden)
7384 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7385 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7387 this.CSS.updateRule(thSelector, "display", "");
7388 this.CSS.updateRule(tdSelector, "display", "");
7391 this.CSS.updateRule(thSelector, "display", "none");
7392 this.CSS.updateRule(tdSelector, "display", "none");
7395 this.onHeaderChange();
7399 setColumnWidth: function(col_index, width)
7401 // width = "md-2 xs-2..."
7402 if(!this.colModel.config[col_index]) {
7406 var w = width.split(" ");
7408 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7410 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7413 for(var j = 0; j < w.length; j++) {
7419 var size_cls = w[j].split("-");
7421 if(!Number.isInteger(size_cls[1] * 1)) {
7425 if(!this.colModel.config[col_index][size_cls[0]]) {
7429 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7433 h_row[0].classList.replace(
7434 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7435 "col-"+size_cls[0]+"-"+size_cls[1]
7438 for(var i = 0; i < rows.length; i++) {
7440 var size_cls = w[j].split("-");
7442 if(!Number.isInteger(size_cls[1] * 1)) {
7446 if(!this.colModel.config[col_index][size_cls[0]]) {
7450 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7454 rows[i].classList.replace(
7455 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7456 "col-"+size_cls[0]+"-"+size_cls[1]
7460 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7475 * @class Roo.bootstrap.TableCell
7476 * @extends Roo.bootstrap.Component
7477 * Bootstrap TableCell class
7478 * @cfg {String} html cell contain text
7479 * @cfg {String} cls cell class
7480 * @cfg {String} tag cell tag (td|th) default td
7481 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7482 * @cfg {String} align Aligns the content in a cell
7483 * @cfg {String} axis Categorizes cells
7484 * @cfg {String} bgcolor Specifies the background color of a cell
7485 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7486 * @cfg {Number} colspan Specifies the number of columns a cell should span
7487 * @cfg {String} headers Specifies one or more header cells a cell is related to
7488 * @cfg {Number} height Sets the height of a cell
7489 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7490 * @cfg {Number} rowspan Sets the number of rows a cell should span
7491 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7492 * @cfg {String} valign Vertical aligns the content in a cell
7493 * @cfg {Number} width Specifies the width of a cell
7496 * Create a new TableCell
7497 * @param {Object} config The config object
7500 Roo.bootstrap.TableCell = function(config){
7501 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7504 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7524 getAutoCreate : function(){
7525 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7545 cfg.align=this.align
7551 cfg.bgcolor=this.bgcolor
7554 cfg.charoff=this.charoff
7557 cfg.colspan=this.colspan
7560 cfg.headers=this.headers
7563 cfg.height=this.height
7566 cfg.nowrap=this.nowrap
7569 cfg.rowspan=this.rowspan
7572 cfg.scope=this.scope
7575 cfg.valign=this.valign
7578 cfg.width=this.width
7597 * @class Roo.bootstrap.TableRow
7598 * @extends Roo.bootstrap.Component
7599 * Bootstrap TableRow class
7600 * @cfg {String} cls row class
7601 * @cfg {String} align Aligns the content in a table row
7602 * @cfg {String} bgcolor Specifies a background color for a table row
7603 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7604 * @cfg {String} valign Vertical aligns the content in a table row
7607 * Create a new TableRow
7608 * @param {Object} config The config object
7611 Roo.bootstrap.TableRow = function(config){
7612 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7615 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7623 getAutoCreate : function(){
7624 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7634 cfg.align = this.align;
7637 cfg.bgcolor = this.bgcolor;
7640 cfg.charoff = this.charoff;
7643 cfg.valign = this.valign;
7661 * @class Roo.bootstrap.TableBody
7662 * @extends Roo.bootstrap.Component
7663 * Bootstrap TableBody class
7664 * @cfg {String} cls element class
7665 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7666 * @cfg {String} align Aligns the content inside the element
7667 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7668 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7671 * Create a new TableBody
7672 * @param {Object} config The config object
7675 Roo.bootstrap.TableBody = function(config){
7676 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7679 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7687 getAutoCreate : function(){
7688 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7702 cfg.align = this.align;
7705 cfg.charoff = this.charoff;
7708 cfg.valign = this.valign;
7715 // initEvents : function()
7722 // this.store = Roo.factory(this.store, Roo.data);
7723 // this.store.on('load', this.onLoad, this);
7725 // this.store.load();
7729 // onLoad: function ()
7731 // this.fireEvent('load', this);
7741 * Ext JS Library 1.1.1
7742 * Copyright(c) 2006-2007, Ext JS, LLC.
7744 * Originally Released Under LGPL - original licence link has changed is not relivant.
7747 * <script type="text/javascript">
7750 // as we use this in bootstrap.
7751 Roo.namespace('Roo.form');
7753 * @class Roo.form.Action
7754 * Internal Class used to handle form actions
7756 * @param {Roo.form.BasicForm} el The form element or its id
7757 * @param {Object} config Configuration options
7762 // define the action interface
7763 Roo.form.Action = function(form, options){
7765 this.options = options || {};
7768 * Client Validation Failed
7771 Roo.form.Action.CLIENT_INVALID = 'client';
7773 * Server Validation Failed
7776 Roo.form.Action.SERVER_INVALID = 'server';
7778 * Connect to Server Failed
7781 Roo.form.Action.CONNECT_FAILURE = 'connect';
7783 * Reading Data from Server Failed
7786 Roo.form.Action.LOAD_FAILURE = 'load';
7788 Roo.form.Action.prototype = {
7790 failureType : undefined,
7791 response : undefined,
7795 run : function(options){
7800 success : function(response){
7805 handleResponse : function(response){
7809 // default connection failure
7810 failure : function(response){
7812 this.response = response;
7813 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7814 this.form.afterAction(this, false);
7817 processResponse : function(response){
7818 this.response = response;
7819 if(!response.responseText){
7822 this.result = this.handleResponse(response);
7826 // utility functions used internally
7827 getUrl : function(appendParams){
7828 var url = this.options.url || this.form.url || this.form.el.dom.action;
7830 var p = this.getParams();
7832 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7838 getMethod : function(){
7839 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7842 getParams : function(){
7843 var bp = this.form.baseParams;
7844 var p = this.options.params;
7846 if(typeof p == "object"){
7847 p = Roo.urlEncode(Roo.applyIf(p, bp));
7848 }else if(typeof p == 'string' && bp){
7849 p += '&' + Roo.urlEncode(bp);
7852 p = Roo.urlEncode(bp);
7857 createCallback : function(){
7859 success: this.success,
7860 failure: this.failure,
7862 timeout: (this.form.timeout*1000),
7863 upload: this.form.fileUpload ? this.success : undefined
7868 Roo.form.Action.Submit = function(form, options){
7869 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7872 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7875 haveProgress : false,
7876 uploadComplete : false,
7878 // uploadProgress indicator.
7879 uploadProgress : function()
7881 if (!this.form.progressUrl) {
7885 if (!this.haveProgress) {
7886 Roo.MessageBox.progress("Uploading", "Uploading");
7888 if (this.uploadComplete) {
7889 Roo.MessageBox.hide();
7893 this.haveProgress = true;
7895 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7897 var c = new Roo.data.Connection();
7899 url : this.form.progressUrl,
7904 success : function(req){
7905 //console.log(data);
7909 rdata = Roo.decode(req.responseText)
7911 Roo.log("Invalid data from server..");
7915 if (!rdata || !rdata.success) {
7917 Roo.MessageBox.alert(Roo.encode(rdata));
7920 var data = rdata.data;
7922 if (this.uploadComplete) {
7923 Roo.MessageBox.hide();
7928 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7929 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7932 this.uploadProgress.defer(2000,this);
7935 failure: function(data) {
7936 Roo.log('progress url failed ');
7947 // run get Values on the form, so it syncs any secondary forms.
7948 this.form.getValues();
7950 var o = this.options;
7951 var method = this.getMethod();
7952 var isPost = method == 'POST';
7953 if(o.clientValidation === false || this.form.isValid()){
7955 if (this.form.progressUrl) {
7956 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7957 (new Date() * 1) + '' + Math.random());
7962 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7963 form:this.form.el.dom,
7964 url:this.getUrl(!isPost),
7966 params:isPost ? this.getParams() : null,
7967 isUpload: this.form.fileUpload,
7968 formData : this.form.formData
7971 this.uploadProgress();
7973 }else if (o.clientValidation !== false){ // client validation failed
7974 this.failureType = Roo.form.Action.CLIENT_INVALID;
7975 this.form.afterAction(this, false);
7979 success : function(response)
7981 this.uploadComplete= true;
7982 if (this.haveProgress) {
7983 Roo.MessageBox.hide();
7987 var result = this.processResponse(response);
7988 if(result === true || result.success){
7989 this.form.afterAction(this, true);
7993 this.form.markInvalid(result.errors);
7994 this.failureType = Roo.form.Action.SERVER_INVALID;
7996 this.form.afterAction(this, false);
7998 failure : function(response)
8000 this.uploadComplete= true;
8001 if (this.haveProgress) {
8002 Roo.MessageBox.hide();
8005 this.response = response;
8006 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8007 this.form.afterAction(this, false);
8010 handleResponse : function(response){
8011 if(this.form.errorReader){
8012 var rs = this.form.errorReader.read(response);
8015 for(var i = 0, len = rs.records.length; i < len; i++) {
8016 var r = rs.records[i];
8020 if(errors.length < 1){
8024 success : rs.success,
8030 ret = Roo.decode(response.responseText);
8034 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8044 Roo.form.Action.Load = function(form, options){
8045 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8046 this.reader = this.form.reader;
8049 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8054 Roo.Ajax.request(Roo.apply(
8055 this.createCallback(), {
8056 method:this.getMethod(),
8057 url:this.getUrl(false),
8058 params:this.getParams()
8062 success : function(response){
8064 var result = this.processResponse(response);
8065 if(result === true || !result.success || !result.data){
8066 this.failureType = Roo.form.Action.LOAD_FAILURE;
8067 this.form.afterAction(this, false);
8070 this.form.clearInvalid();
8071 this.form.setValues(result.data);
8072 this.form.afterAction(this, true);
8075 handleResponse : function(response){
8076 if(this.form.reader){
8077 var rs = this.form.reader.read(response);
8078 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8080 success : rs.success,
8084 return Roo.decode(response.responseText);
8088 Roo.form.Action.ACTION_TYPES = {
8089 'load' : Roo.form.Action.Load,
8090 'submit' : Roo.form.Action.Submit
8099 * @class Roo.bootstrap.Form
8100 * @extends Roo.bootstrap.Component
8101 * Bootstrap Form class
8102 * @cfg {String} method GET | POST (default POST)
8103 * @cfg {String} labelAlign top | left (default top)
8104 * @cfg {String} align left | right - for navbars
8105 * @cfg {Boolean} loadMask load mask when submit (default true)
8110 * @param {Object} config The config object
8114 Roo.bootstrap.Form = function(config){
8116 Roo.bootstrap.Form.superclass.constructor.call(this, config);
8118 Roo.bootstrap.Form.popover.apply();
8122 * @event clientvalidation
8123 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8124 * @param {Form} this
8125 * @param {Boolean} valid true if the form has passed client-side validation
8127 clientvalidation: true,
8129 * @event beforeaction
8130 * Fires before any action is performed. Return false to cancel the action.
8131 * @param {Form} this
8132 * @param {Action} action The action to be performed
8136 * @event actionfailed
8137 * Fires when an action fails.
8138 * @param {Form} this
8139 * @param {Action} action The action that failed
8141 actionfailed : true,
8143 * @event actioncomplete
8144 * Fires when an action is completed.
8145 * @param {Form} this
8146 * @param {Action} action The action that completed
8148 actioncomplete : true
8152 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
8155 * @cfg {String} method
8156 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8161 * The URL to use for form actions if one isn't supplied in the action options.
8164 * @cfg {Boolean} fileUpload
8165 * Set to true if this form is a file upload.
8169 * @cfg {Object} baseParams
8170 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8174 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8178 * @cfg {Sting} align (left|right) for navbar forms
8183 activeAction : null,
8186 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8187 * element by passing it or its id or mask the form itself by passing in true.
8190 waitMsgTarget : false,
8195 * @cfg {Boolean} errorMask (true|false) default false
8200 * @cfg {Number} maskOffset Default 100
8205 * @cfg {Boolean} maskBody
8209 getAutoCreate : function(){
8213 method : this.method || 'POST',
8214 id : this.id || Roo.id(),
8217 if (this.parent().xtype.match(/^Nav/)) {
8218 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8222 if (this.labelAlign == 'left' ) {
8223 cfg.cls += ' form-horizontal';
8229 initEvents : function()
8231 this.el.on('submit', this.onSubmit, this);
8232 // this was added as random key presses on the form where triggering form submit.
8233 this.el.on('keypress', function(e) {
8234 if (e.getCharCode() != 13) {
8237 // we might need to allow it for textareas.. and some other items.
8238 // check e.getTarget().
8240 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8244 Roo.log("keypress blocked");
8252 onSubmit : function(e){
8257 * Returns true if client-side validation on the form is successful.
8260 isValid : function(){
8261 var items = this.getItems();
8265 items.each(function(f){
8271 Roo.log('invalid field: ' + f.name);
8275 if(!target && f.el.isVisible(true)){
8281 if(this.errorMask && !valid){
8282 Roo.bootstrap.Form.popover.mask(this, target);
8289 * Returns true if any fields in this form have changed since their original load.
8292 isDirty : function(){
8294 var items = this.getItems();
8295 items.each(function(f){
8305 * Performs a predefined action (submit or load) or custom actions you define on this form.
8306 * @param {String} actionName The name of the action type
8307 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8308 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8309 * accept other config options):
8311 Property Type Description
8312 ---------------- --------------- ----------------------------------------------------------------------------------
8313 url String The url for the action (defaults to the form's url)
8314 method String The form method to use (defaults to the form's method, or POST if not defined)
8315 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8316 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8317 validate the form on the client (defaults to false)
8319 * @return {BasicForm} this
8321 doAction : function(action, options){
8322 if(typeof action == 'string'){
8323 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8325 if(this.fireEvent('beforeaction', this, action) !== false){
8326 this.beforeAction(action);
8327 action.run.defer(100, action);
8333 beforeAction : function(action){
8334 var o = action.options;
8339 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8341 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8344 // not really supported yet.. ??
8346 //if(this.waitMsgTarget === true){
8347 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8348 //}else if(this.waitMsgTarget){
8349 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8350 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8352 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8358 afterAction : function(action, success){
8359 this.activeAction = null;
8360 var o = action.options;
8365 Roo.get(document.body).unmask();
8371 //if(this.waitMsgTarget === true){
8372 // this.el.unmask();
8373 //}else if(this.waitMsgTarget){
8374 // this.waitMsgTarget.unmask();
8376 // Roo.MessageBox.updateProgress(1);
8377 // Roo.MessageBox.hide();
8384 Roo.callback(o.success, o.scope, [this, action]);
8385 this.fireEvent('actioncomplete', this, action);
8389 // failure condition..
8390 // we have a scenario where updates need confirming.
8391 // eg. if a locking scenario exists..
8392 // we look for { errors : { needs_confirm : true }} in the response.
8394 (typeof(action.result) != 'undefined') &&
8395 (typeof(action.result.errors) != 'undefined') &&
8396 (typeof(action.result.errors.needs_confirm) != 'undefined')
8399 Roo.log("not supported yet");
8402 Roo.MessageBox.confirm(
8403 "Change requires confirmation",
8404 action.result.errorMsg,
8409 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8419 Roo.callback(o.failure, o.scope, [this, action]);
8420 // show an error message if no failed handler is set..
8421 if (!this.hasListener('actionfailed')) {
8422 Roo.log("need to add dialog support");
8424 Roo.MessageBox.alert("Error",
8425 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8426 action.result.errorMsg :
8427 "Saving Failed, please check your entries or try again"
8432 this.fireEvent('actionfailed', this, action);
8437 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8438 * @param {String} id The value to search for
8441 findField : function(id){
8442 var items = this.getItems();
8443 var field = items.get(id);
8445 items.each(function(f){
8446 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8453 return field || null;
8456 * Mark fields in this form invalid in bulk.
8457 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8458 * @return {BasicForm} this
8460 markInvalid : function(errors){
8461 if(errors instanceof Array){
8462 for(var i = 0, len = errors.length; i < len; i++){
8463 var fieldError = errors[i];
8464 var f = this.findField(fieldError.id);
8466 f.markInvalid(fieldError.msg);
8472 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8473 field.markInvalid(errors[id]);
8477 //Roo.each(this.childForms || [], function (f) {
8478 // f.markInvalid(errors);
8485 * Set values for fields in this form in bulk.
8486 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8487 * @return {BasicForm} this
8489 setValues : function(values){
8490 if(values instanceof Array){ // array of objects
8491 for(var i = 0, len = values.length; i < len; i++){
8493 var f = this.findField(v.id);
8495 f.setValue(v.value);
8496 if(this.trackResetOnLoad){
8497 f.originalValue = f.getValue();
8501 }else{ // object hash
8504 if(typeof values[id] != 'function' && (field = this.findField(id))){
8506 if (field.setFromData &&
8508 field.displayField &&
8509 // combos' with local stores can
8510 // be queried via setValue()
8511 // to set their value..
8512 (field.store && !field.store.isLocal)
8516 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8517 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8518 field.setFromData(sd);
8520 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8522 field.setFromData(values);
8525 field.setValue(values[id]);
8529 if(this.trackResetOnLoad){
8530 field.originalValue = field.getValue();
8536 //Roo.each(this.childForms || [], function (f) {
8537 // f.setValues(values);
8544 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8545 * they are returned as an array.
8546 * @param {Boolean} asString
8549 getValues : function(asString){
8550 //if (this.childForms) {
8551 // copy values from the child forms
8552 // Roo.each(this.childForms, function (f) {
8553 // this.setValues(f.getValues());
8559 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8560 if(asString === true){
8563 return Roo.urlDecode(fs);
8567 * Returns the fields in this form as an object with key/value pairs.
8568 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8571 getFieldValues : function(with_hidden)
8573 var items = this.getItems();
8575 items.each(function(f){
8581 var v = f.getValue();
8583 if (f.inputType =='radio') {
8584 if (typeof(ret[f.getName()]) == 'undefined') {
8585 ret[f.getName()] = ''; // empty..
8588 if (!f.el.dom.checked) {
8596 if(f.xtype == 'MoneyField'){
8597 ret[f.currencyName] = f.getCurrency();
8600 // not sure if this supported any more..
8601 if ((typeof(v) == 'object') && f.getRawValue) {
8602 v = f.getRawValue() ; // dates..
8604 // combo boxes where name != hiddenName...
8605 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8606 ret[f.name] = f.getRawValue();
8608 ret[f.getName()] = v;
8615 * Clears all invalid messages in this form.
8616 * @return {BasicForm} this
8618 clearInvalid : function(){
8619 var items = this.getItems();
8621 items.each(function(f){
8630 * @return {BasicForm} this
8633 var items = this.getItems();
8634 items.each(function(f){
8638 Roo.each(this.childForms || [], function (f) {
8646 getItems : function()
8648 var r=new Roo.util.MixedCollection(false, function(o){
8649 return o.id || (o.id = Roo.id());
8651 var iter = function(el) {
8658 Roo.each(el.items,function(e) {
8667 hideFields : function(items)
8669 Roo.each(items, function(i){
8671 var f = this.findField(i);
8682 showFields : function(items)
8684 Roo.each(items, function(i){
8686 var f = this.findField(i);
8699 Roo.apply(Roo.bootstrap.Form, {
8726 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8727 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8728 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8729 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8732 this.maskEl.top.enableDisplayMode("block");
8733 this.maskEl.left.enableDisplayMode("block");
8734 this.maskEl.bottom.enableDisplayMode("block");
8735 this.maskEl.right.enableDisplayMode("block");
8737 this.toolTip = new Roo.bootstrap.Tooltip({
8738 cls : 'roo-form-error-popover',
8740 'left' : ['r-l', [-2,0], 'right'],
8741 'right' : ['l-r', [2,0], 'left'],
8742 'bottom' : ['tl-bl', [0,2], 'top'],
8743 'top' : [ 'bl-tl', [0,-2], 'bottom']
8747 this.toolTip.render(Roo.get(document.body));
8749 this.toolTip.el.enableDisplayMode("block");
8751 Roo.get(document.body).on('click', function(){
8755 Roo.get(document.body).on('touchstart', function(){
8759 this.isApplied = true
8762 mask : function(form, target)
8766 this.target = target;
8768 if(!this.form.errorMask || !target.el){
8772 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8774 Roo.log(scrollable);
8776 var ot = this.target.el.calcOffsetsTo(scrollable);
8778 var scrollTo = ot[1] - this.form.maskOffset;
8780 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8782 scrollable.scrollTo('top', scrollTo);
8784 var box = this.target.el.getBox();
8786 var zIndex = Roo.bootstrap.Modal.zIndex++;
8789 this.maskEl.top.setStyle('position', 'absolute');
8790 this.maskEl.top.setStyle('z-index', zIndex);
8791 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8792 this.maskEl.top.setLeft(0);
8793 this.maskEl.top.setTop(0);
8794 this.maskEl.top.show();
8796 this.maskEl.left.setStyle('position', 'absolute');
8797 this.maskEl.left.setStyle('z-index', zIndex);
8798 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8799 this.maskEl.left.setLeft(0);
8800 this.maskEl.left.setTop(box.y - this.padding);
8801 this.maskEl.left.show();
8803 this.maskEl.bottom.setStyle('position', 'absolute');
8804 this.maskEl.bottom.setStyle('z-index', zIndex);
8805 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8806 this.maskEl.bottom.setLeft(0);
8807 this.maskEl.bottom.setTop(box.bottom + this.padding);
8808 this.maskEl.bottom.show();
8810 this.maskEl.right.setStyle('position', 'absolute');
8811 this.maskEl.right.setStyle('z-index', zIndex);
8812 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8813 this.maskEl.right.setLeft(box.right + this.padding);
8814 this.maskEl.right.setTop(box.y - this.padding);
8815 this.maskEl.right.show();
8817 this.toolTip.bindEl = this.target.el;
8819 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8821 var tip = this.target.blankText;
8823 if(this.target.getValue() !== '' ) {
8825 if (this.target.invalidText.length) {
8826 tip = this.target.invalidText;
8827 } else if (this.target.regexText.length){
8828 tip = this.target.regexText;
8832 this.toolTip.show(tip);
8834 this.intervalID = window.setInterval(function() {
8835 Roo.bootstrap.Form.popover.unmask();
8838 window.onwheel = function(){ return false;};
8840 (function(){ this.isMasked = true; }).defer(500, this);
8846 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8850 this.maskEl.top.setStyle('position', 'absolute');
8851 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8852 this.maskEl.top.hide();
8854 this.maskEl.left.setStyle('position', 'absolute');
8855 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8856 this.maskEl.left.hide();
8858 this.maskEl.bottom.setStyle('position', 'absolute');
8859 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8860 this.maskEl.bottom.hide();
8862 this.maskEl.right.setStyle('position', 'absolute');
8863 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8864 this.maskEl.right.hide();
8866 this.toolTip.hide();
8868 this.toolTip.el.hide();
8870 window.onwheel = function(){ return true;};
8872 if(this.intervalID){
8873 window.clearInterval(this.intervalID);
8874 this.intervalID = false;
8877 this.isMasked = false;
8887 * Ext JS Library 1.1.1
8888 * Copyright(c) 2006-2007, Ext JS, LLC.
8890 * Originally Released Under LGPL - original licence link has changed is not relivant.
8893 * <script type="text/javascript">
8896 * @class Roo.form.VTypes
8897 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8900 Roo.form.VTypes = function(){
8901 // closure these in so they are only created once.
8902 var alpha = /^[a-zA-Z_]+$/;
8903 var alphanum = /^[a-zA-Z0-9_]+$/;
8904 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8905 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8907 // All these messages and functions are configurable
8910 * The function used to validate email addresses
8911 * @param {String} value The email address
8913 'email' : function(v){
8914 return email.test(v);
8917 * The error text to display when the email validation function returns false
8920 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8922 * The keystroke filter mask to be applied on email input
8925 'emailMask' : /[a-z0-9_\.\-@]/i,
8928 * The function used to validate URLs
8929 * @param {String} value The URL
8931 'url' : function(v){
8935 * The error text to display when the url validation function returns false
8938 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8941 * The function used to validate alpha values
8942 * @param {String} value The value
8944 'alpha' : function(v){
8945 return alpha.test(v);
8948 * The error text to display when the alpha validation function returns false
8951 'alphaText' : 'This field should only contain letters and _',
8953 * The keystroke filter mask to be applied on alpha input
8956 'alphaMask' : /[a-z_]/i,
8959 * The function used to validate alphanumeric values
8960 * @param {String} value The value
8962 'alphanum' : function(v){
8963 return alphanum.test(v);
8966 * The error text to display when the alphanumeric validation function returns false
8969 'alphanumText' : 'This field should only contain letters, numbers and _',
8971 * The keystroke filter mask to be applied on alphanumeric input
8974 'alphanumMask' : /[a-z0-9_]/i
8984 * @class Roo.bootstrap.Input
8985 * @extends Roo.bootstrap.Component
8986 * Bootstrap Input class
8987 * @cfg {Boolean} disabled is it disabled
8988 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8989 * @cfg {String} name name of the input
8990 * @cfg {string} fieldLabel - the label associated
8991 * @cfg {string} placeholder - placeholder to put in text.
8992 * @cfg {string} before - input group add on before
8993 * @cfg {string} after - input group add on after
8994 * @cfg {string} size - (lg|sm) or leave empty..
8995 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8996 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8997 * @cfg {Number} md colspan out of 12 for computer-sized screens
8998 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8999 * @cfg {string} value default value of the input
9000 * @cfg {Number} labelWidth set the width of label
9001 * @cfg {Number} labellg set the width of label (1-12)
9002 * @cfg {Number} labelmd set the width of label (1-12)
9003 * @cfg {Number} labelsm set the width of label (1-12)
9004 * @cfg {Number} labelxs set the width of label (1-12)
9005 * @cfg {String} labelAlign (top|left)
9006 * @cfg {Boolean} readOnly Specifies that the field should be read-only
9007 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9008 * @cfg {String} indicatorpos (left|right) default left
9009 * @cfg {String} capture (user|camera) use for file input only. (default empty)
9010 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9012 * @cfg {String} align (left|center|right) Default left
9013 * @cfg {Boolean} forceFeedback (true|false) Default false
9016 * Create a new Input
9017 * @param {Object} config The config object
9020 Roo.bootstrap.Input = function(config){
9022 Roo.bootstrap.Input.superclass.constructor.call(this, config);
9027 * Fires when this field receives input focus.
9028 * @param {Roo.form.Field} this
9033 * Fires when this field loses input focus.
9034 * @param {Roo.form.Field} this
9039 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
9040 * {@link Roo.EventObject#getKey} to determine which key was pressed.
9041 * @param {Roo.form.Field} this
9042 * @param {Roo.EventObject} e The event object
9047 * Fires just before the field blurs if the field value has changed.
9048 * @param {Roo.form.Field} this
9049 * @param {Mixed} newValue The new value
9050 * @param {Mixed} oldValue The original value
9055 * Fires after the field has been marked as invalid.
9056 * @param {Roo.form.Field} this
9057 * @param {String} msg The validation message
9062 * Fires after the field has been validated with no errors.
9063 * @param {Roo.form.Field} this
9068 * Fires after the key up
9069 * @param {Roo.form.Field} this
9070 * @param {Roo.EventObject} e The event Object
9076 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
9078 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9079 automatic validation (defaults to "keyup").
9081 validationEvent : "keyup",
9083 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9085 validateOnBlur : true,
9087 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9089 validationDelay : 250,
9091 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9093 focusClass : "x-form-focus", // not needed???
9097 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9099 invalidClass : "has-warning",
9102 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9104 validClass : "has-success",
9107 * @cfg {Boolean} hasFeedback (true|false) default true
9112 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9114 invalidFeedbackClass : "glyphicon-warning-sign",
9117 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9119 validFeedbackClass : "glyphicon-ok",
9122 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9124 selectOnFocus : false,
9127 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9131 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9136 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9138 disableKeyFilter : false,
9141 * @cfg {Boolean} disabled True to disable the field (defaults to false).
9145 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9149 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9151 blankText : "Please complete this mandatory field",
9154 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9158 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9160 maxLength : Number.MAX_VALUE,
9162 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9164 minLengthText : "The minimum length for this field is {0}",
9166 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9168 maxLengthText : "The maximum length for this field is {0}",
9172 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9173 * If available, this function will be called only after the basic validators all return true, and will be passed the
9174 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9178 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9179 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9180 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
9184 * @cfg {String} regexText -- Depricated - use Invalid Text
9189 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9195 autocomplete: false,
9214 formatedValue : false,
9215 forceFeedback : false,
9217 indicatorpos : 'left',
9227 parentLabelAlign : function()
9230 while (parent.parent()) {
9231 parent = parent.parent();
9232 if (typeof(parent.labelAlign) !='undefined') {
9233 return parent.labelAlign;
9240 getAutoCreate : function()
9242 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9248 if(this.inputType != 'hidden'){
9249 cfg.cls = 'form-group' //input-group
9255 type : this.inputType,
9257 cls : 'form-control',
9258 placeholder : this.placeholder || '',
9259 autocomplete : this.autocomplete || 'new-password'
9262 if(this.capture.length){
9263 input.capture = this.capture;
9266 if(this.accept.length){
9267 input.accept = this.accept + "/*";
9271 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9274 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9275 input.maxLength = this.maxLength;
9278 if (this.disabled) {
9279 input.disabled=true;
9282 if (this.readOnly) {
9283 input.readonly=true;
9287 input.name = this.name;
9291 input.cls += ' input-' + this.size;
9295 ['xs','sm','md','lg'].map(function(size){
9296 if (settings[size]) {
9297 cfg.cls += ' col-' + size + '-' + settings[size];
9301 var inputblock = input;
9305 cls: 'glyphicon form-control-feedback'
9308 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9311 cls : 'has-feedback',
9319 if (this.before || this.after) {
9322 cls : 'input-group',
9326 if (this.before && typeof(this.before) == 'string') {
9328 inputblock.cn.push({
9330 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9334 if (this.before && typeof(this.before) == 'object') {
9335 this.before = Roo.factory(this.before);
9337 inputblock.cn.push({
9339 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9340 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9344 inputblock.cn.push(input);
9346 if (this.after && typeof(this.after) == 'string') {
9347 inputblock.cn.push({
9349 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9353 if (this.after && typeof(this.after) == 'object') {
9354 this.after = Roo.factory(this.after);
9356 inputblock.cn.push({
9358 cls : 'roo-input-after input-group-append input-group-text input-group-' +
9359 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9363 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9364 inputblock.cls += ' has-feedback';
9365 inputblock.cn.push(feedback);
9370 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9371 tooltip : 'This field is required'
9373 if (Roo.bootstrap.version == 4) {
9376 style : 'display-none'
9379 if (align ==='left' && this.fieldLabel.length) {
9381 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
9388 cls : 'control-label col-form-label',
9389 html : this.fieldLabel
9400 var labelCfg = cfg.cn[1];
9401 var contentCfg = cfg.cn[2];
9403 if(this.indicatorpos == 'right'){
9408 cls : 'control-label col-form-label',
9412 html : this.fieldLabel
9426 labelCfg = cfg.cn[0];
9427 contentCfg = cfg.cn[1];
9431 if(this.labelWidth > 12){
9432 labelCfg.style = "width: " + this.labelWidth + 'px';
9435 if(this.labelWidth < 13 && this.labelmd == 0){
9436 this.labelmd = this.labelWidth;
9439 if(this.labellg > 0){
9440 labelCfg.cls += ' col-lg-' + this.labellg;
9441 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9444 if(this.labelmd > 0){
9445 labelCfg.cls += ' col-md-' + this.labelmd;
9446 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9449 if(this.labelsm > 0){
9450 labelCfg.cls += ' col-sm-' + this.labelsm;
9451 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9454 if(this.labelxs > 0){
9455 labelCfg.cls += ' col-xs-' + this.labelxs;
9456 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9460 } else if ( this.fieldLabel.length) {
9465 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9466 tooltip : 'This field is required'
9470 //cls : 'input-group-addon',
9471 html : this.fieldLabel
9479 if(this.indicatorpos == 'right'){
9484 //cls : 'input-group-addon',
9485 html : this.fieldLabel
9490 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9491 tooltip : 'This field is required'
9511 if (this.parentType === 'Navbar' && this.parent().bar) {
9512 cfg.cls += ' navbar-form';
9515 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9516 // on BS4 we do this only if not form
9517 cfg.cls += ' navbar-form';
9525 * return the real input element.
9527 inputEl: function ()
9529 return this.el.select('input.form-control',true).first();
9532 tooltipEl : function()
9534 return this.inputEl();
9537 indicatorEl : function()
9539 if (Roo.bootstrap.version == 4) {
9540 return false; // not enabled in v4 yet.
9543 var indicator = this.el.select('i.roo-required-indicator',true).first();
9553 setDisabled : function(v)
9555 var i = this.inputEl().dom;
9557 i.removeAttribute('disabled');
9561 i.setAttribute('disabled','true');
9563 initEvents : function()
9566 this.inputEl().on("keydown" , this.fireKey, this);
9567 this.inputEl().on("focus", this.onFocus, this);
9568 this.inputEl().on("blur", this.onBlur, this);
9570 this.inputEl().relayEvent('keyup', this);
9572 this.indicator = this.indicatorEl();
9575 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9578 // reference to original value for reset
9579 this.originalValue = this.getValue();
9580 //Roo.form.TextField.superclass.initEvents.call(this);
9581 if(this.validationEvent == 'keyup'){
9582 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9583 this.inputEl().on('keyup', this.filterValidation, this);
9585 else if(this.validationEvent !== false){
9586 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9589 if(this.selectOnFocus){
9590 this.on("focus", this.preFocus, this);
9593 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9594 this.inputEl().on("keypress", this.filterKeys, this);
9596 this.inputEl().relayEvent('keypress', this);
9599 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9600 this.el.on("click", this.autoSize, this);
9603 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9604 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9607 if (typeof(this.before) == 'object') {
9608 this.before.render(this.el.select('.roo-input-before',true).first());
9610 if (typeof(this.after) == 'object') {
9611 this.after.render(this.el.select('.roo-input-after',true).first());
9614 this.inputEl().on('change', this.onChange, this);
9617 filterValidation : function(e){
9618 if(!e.isNavKeyPress()){
9619 this.validationTask.delay(this.validationDelay);
9623 * Validates the field value
9624 * @return {Boolean} True if the value is valid, else false
9626 validate : function(){
9627 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9628 if(this.disabled || this.validateValue(this.getRawValue())){
9639 * Validates a value according to the field's validation rules and marks the field as invalid
9640 * if the validation fails
9641 * @param {Mixed} value The value to validate
9642 * @return {Boolean} True if the value is valid, else false
9644 validateValue : function(value)
9646 if(this.getVisibilityEl().hasClass('hidden')){
9650 if(value.length < 1) { // if it's blank
9651 if(this.allowBlank){
9657 if(value.length < this.minLength){
9660 if(value.length > this.maxLength){
9664 var vt = Roo.form.VTypes;
9665 if(!vt[this.vtype](value, this)){
9669 if(typeof this.validator == "function"){
9670 var msg = this.validator(value);
9674 if (typeof(msg) == 'string') {
9675 this.invalidText = msg;
9679 if(this.regex && !this.regex.test(value)){
9687 fireKey : function(e){
9688 //Roo.log('field ' + e.getKey());
9689 if(e.isNavKeyPress()){
9690 this.fireEvent("specialkey", this, e);
9693 focus : function (selectText){
9695 this.inputEl().focus();
9696 if(selectText === true){
9697 this.inputEl().dom.select();
9703 onFocus : function(){
9704 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9705 // this.el.addClass(this.focusClass);
9708 this.hasFocus = true;
9709 this.startValue = this.getValue();
9710 this.fireEvent("focus", this);
9714 beforeBlur : Roo.emptyFn,
9718 onBlur : function(){
9720 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9721 //this.el.removeClass(this.focusClass);
9723 this.hasFocus = false;
9724 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9727 var v = this.getValue();
9728 if(String(v) !== String(this.startValue)){
9729 this.fireEvent('change', this, v, this.startValue);
9731 this.fireEvent("blur", this);
9734 onChange : function(e)
9736 var v = this.getValue();
9737 if(String(v) !== String(this.startValue)){
9738 this.fireEvent('change', this, v, this.startValue);
9744 * Resets the current field value to the originally loaded value and clears any validation messages
9747 this.setValue(this.originalValue);
9751 * Returns the name of the field
9752 * @return {Mixed} name The name field
9754 getName: function(){
9758 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9759 * @return {Mixed} value The field value
9761 getValue : function(){
9763 var v = this.inputEl().getValue();
9768 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9769 * @return {Mixed} value The field value
9771 getRawValue : function(){
9772 var v = this.inputEl().getValue();
9778 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9779 * @param {Mixed} value The value to set
9781 setRawValue : function(v){
9782 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9785 selectText : function(start, end){
9786 var v = this.getRawValue();
9788 start = start === undefined ? 0 : start;
9789 end = end === undefined ? v.length : end;
9790 var d = this.inputEl().dom;
9791 if(d.setSelectionRange){
9792 d.setSelectionRange(start, end);
9793 }else if(d.createTextRange){
9794 var range = d.createTextRange();
9795 range.moveStart("character", start);
9796 range.moveEnd("character", v.length-end);
9803 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9804 * @param {Mixed} value The value to set
9806 setValue : function(v){
9809 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9815 processValue : function(value){
9816 if(this.stripCharsRe){
9817 var newValue = value.replace(this.stripCharsRe, '');
9818 if(newValue !== value){
9819 this.setRawValue(newValue);
9826 preFocus : function(){
9828 if(this.selectOnFocus){
9829 this.inputEl().dom.select();
9832 filterKeys : function(e){
9834 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9837 var c = e.getCharCode(), cc = String.fromCharCode(c);
9838 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9841 if(!this.maskRe.test(cc)){
9846 * Clear any invalid styles/messages for this field
9848 clearInvalid : function(){
9850 if(!this.el || this.preventMark){ // not rendered
9855 this.el.removeClass([this.invalidClass, 'is-invalid']);
9857 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9859 var feedback = this.el.select('.form-control-feedback', true).first();
9862 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9868 this.indicator.removeClass('visible');
9869 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9872 this.fireEvent('valid', this);
9876 * Mark this field as valid
9878 markValid : function()
9880 if(!this.el || this.preventMark){ // not rendered...
9884 this.el.removeClass([this.invalidClass, this.validClass]);
9885 this.inputEl().removeClass(['is-valid', 'is-invalid']);
9887 var feedback = this.el.select('.form-control-feedback', true).first();
9890 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9894 this.indicator.removeClass('visible');
9895 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9902 if(this.allowBlank && !this.getRawValue().length){
9905 if (Roo.bootstrap.version == 3) {
9906 this.el.addClass(this.validClass);
9908 this.inputEl().addClass('is-valid');
9911 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9913 var feedback = this.el.select('.form-control-feedback', true).first();
9916 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9917 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9922 this.fireEvent('valid', this);
9926 * Mark this field as invalid
9927 * @param {String} msg The validation message
9929 markInvalid : function(msg)
9931 if(!this.el || this.preventMark){ // not rendered
9935 this.el.removeClass([this.invalidClass, this.validClass]);
9936 this.inputEl().removeClass(['is-valid', 'is-invalid']);
9938 var feedback = this.el.select('.form-control-feedback', true).first();
9941 this.el.select('.form-control-feedback', true).first().removeClass(
9942 [this.invalidFeedbackClass, this.validFeedbackClass]);
9949 if(this.allowBlank && !this.getRawValue().length){
9954 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9955 this.indicator.addClass('visible');
9957 if (Roo.bootstrap.version == 3) {
9958 this.el.addClass(this.invalidClass);
9960 this.inputEl().addClass('is-invalid');
9965 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9967 var feedback = this.el.select('.form-control-feedback', true).first();
9970 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9972 if(this.getValue().length || this.forceFeedback){
9973 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9980 this.fireEvent('invalid', this, msg);
9983 SafariOnKeyDown : function(event)
9985 // this is a workaround for a password hang bug on chrome/ webkit.
9986 if (this.inputEl().dom.type != 'password') {
9990 var isSelectAll = false;
9992 if(this.inputEl().dom.selectionEnd > 0){
9993 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9995 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9996 event.preventDefault();
10001 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10003 event.preventDefault();
10004 // this is very hacky as keydown always get's upper case.
10006 var cc = String.fromCharCode(event.getCharCode());
10007 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
10011 adjustWidth : function(tag, w){
10012 tag = tag.toLowerCase();
10013 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10014 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10015 if(tag == 'input'){
10018 if(tag == 'textarea'){
10021 }else if(Roo.isOpera){
10022 if(tag == 'input'){
10025 if(tag == 'textarea'){
10033 setFieldLabel : function(v)
10035 if(!this.rendered){
10039 if(this.indicatorEl()){
10040 var ar = this.el.select('label > span',true);
10042 if (ar.elements.length) {
10043 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10044 this.fieldLabel = v;
10048 var br = this.el.select('label',true);
10050 if(br.elements.length) {
10051 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10052 this.fieldLabel = v;
10056 Roo.log('Cannot Found any of label > span || label in input');
10060 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10061 this.fieldLabel = v;
10076 * @class Roo.bootstrap.TextArea
10077 * @extends Roo.bootstrap.Input
10078 * Bootstrap TextArea class
10079 * @cfg {Number} cols Specifies the visible width of a text area
10080 * @cfg {Number} rows Specifies the visible number of lines in a text area
10081 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10082 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10083 * @cfg {string} html text
10086 * Create a new TextArea
10087 * @param {Object} config The config object
10090 Roo.bootstrap.TextArea = function(config){
10091 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10095 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
10105 getAutoCreate : function(){
10107 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10113 if(this.inputType != 'hidden'){
10114 cfg.cls = 'form-group' //input-group
10122 value : this.value || '',
10123 html: this.html || '',
10124 cls : 'form-control',
10125 placeholder : this.placeholder || ''
10129 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10130 input.maxLength = this.maxLength;
10134 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10138 input.cols = this.cols;
10141 if (this.readOnly) {
10142 input.readonly = true;
10146 input.name = this.name;
10150 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10154 ['xs','sm','md','lg'].map(function(size){
10155 if (settings[size]) {
10156 cfg.cls += ' col-' + size + '-' + settings[size];
10160 var inputblock = input;
10162 if(this.hasFeedback && !this.allowBlank){
10166 cls: 'glyphicon form-control-feedback'
10170 cls : 'has-feedback',
10179 if (this.before || this.after) {
10182 cls : 'input-group',
10186 inputblock.cn.push({
10188 cls : 'input-group-addon',
10193 inputblock.cn.push(input);
10195 if(this.hasFeedback && !this.allowBlank){
10196 inputblock.cls += ' has-feedback';
10197 inputblock.cn.push(feedback);
10201 inputblock.cn.push({
10203 cls : 'input-group-addon',
10210 if (align ==='left' && this.fieldLabel.length) {
10215 cls : 'control-label',
10216 html : this.fieldLabel
10227 if(this.labelWidth > 12){
10228 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10231 if(this.labelWidth < 13 && this.labelmd == 0){
10232 this.labelmd = this.labelWidth;
10235 if(this.labellg > 0){
10236 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10237 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10240 if(this.labelmd > 0){
10241 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10242 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10245 if(this.labelsm > 0){
10246 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10247 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10250 if(this.labelxs > 0){
10251 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10252 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10255 } else if ( this.fieldLabel.length) {
10260 //cls : 'input-group-addon',
10261 html : this.fieldLabel
10279 if (this.disabled) {
10280 input.disabled=true;
10287 * return the real textarea element.
10289 inputEl: function ()
10291 return this.el.select('textarea.form-control',true).first();
10295 * Clear any invalid styles/messages for this field
10297 clearInvalid : function()
10300 if(!this.el || this.preventMark){ // not rendered
10304 var label = this.el.select('label', true).first();
10305 var icon = this.el.select('i.fa-star', true).first();
10310 this.el.removeClass( this.validClass);
10311 this.inputEl().removeClass('is-invalid');
10313 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10315 var feedback = this.el.select('.form-control-feedback', true).first();
10318 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10323 this.fireEvent('valid', this);
10327 * Mark this field as valid
10329 markValid : function()
10331 if(!this.el || this.preventMark){ // not rendered
10335 this.el.removeClass([this.invalidClass, this.validClass]);
10336 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10338 var feedback = this.el.select('.form-control-feedback', true).first();
10341 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10344 if(this.disabled || this.allowBlank){
10348 var label = this.el.select('label', true).first();
10349 var icon = this.el.select('i.fa-star', true).first();
10354 if (Roo.bootstrap.version == 3) {
10355 this.el.addClass(this.validClass);
10357 this.inputEl().addClass('is-valid');
10361 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10363 var feedback = this.el.select('.form-control-feedback', true).first();
10366 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10367 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10372 this.fireEvent('valid', this);
10376 * Mark this field as invalid
10377 * @param {String} msg The validation message
10379 markInvalid : function(msg)
10381 if(!this.el || this.preventMark){ // not rendered
10385 this.el.removeClass([this.invalidClass, this.validClass]);
10386 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10388 var feedback = this.el.select('.form-control-feedback', true).first();
10391 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10394 if(this.disabled || this.allowBlank){
10398 var label = this.el.select('label', true).first();
10399 var icon = this.el.select('i.fa-star', true).first();
10401 if(!this.getValue().length && label && !icon){
10402 this.el.createChild({
10404 cls : 'text-danger fa fa-lg fa-star',
10405 tooltip : 'This field is required',
10406 style : 'margin-right:5px;'
10410 if (Roo.bootstrap.version == 3) {
10411 this.el.addClass(this.invalidClass);
10413 this.inputEl().addClass('is-invalid');
10416 // fixme ... this may be depricated need to test..
10417 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10419 var feedback = this.el.select('.form-control-feedback', true).first();
10422 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10424 if(this.getValue().length || this.forceFeedback){
10425 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10432 this.fireEvent('invalid', this, msg);
10440 * trigger field - base class for combo..
10445 * @class Roo.bootstrap.TriggerField
10446 * @extends Roo.bootstrap.Input
10447 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10448 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10449 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10450 * for which you can provide a custom implementation. For example:
10452 var trigger = new Roo.bootstrap.TriggerField();
10453 trigger.onTriggerClick = myTriggerFn;
10454 trigger.applyTo('my-field');
10457 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10458 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10459 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10460 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10461 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
10464 * Create a new TriggerField.
10465 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10466 * to the base TextField)
10468 Roo.bootstrap.TriggerField = function(config){
10469 this.mimicing = false;
10470 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10473 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10475 * @cfg {String} triggerClass A CSS class to apply to the trigger
10478 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10483 * @cfg {Boolean} removable (true|false) special filter default false
10487 /** @cfg {Boolean} grow @hide */
10488 /** @cfg {Number} growMin @hide */
10489 /** @cfg {Number} growMax @hide */
10495 autoSize: Roo.emptyFn,
10499 deferHeight : true,
10502 actionMode : 'wrap',
10507 getAutoCreate : function(){
10509 var align = this.labelAlign || this.parentLabelAlign();
10514 cls: 'form-group' //input-group
10521 type : this.inputType,
10522 cls : 'form-control',
10523 autocomplete: 'new-password',
10524 placeholder : this.placeholder || ''
10528 input.name = this.name;
10531 input.cls += ' input-' + this.size;
10534 if (this.disabled) {
10535 input.disabled=true;
10538 var inputblock = input;
10540 if(this.hasFeedback && !this.allowBlank){
10544 cls: 'glyphicon form-control-feedback'
10547 if(this.removable && !this.editable && !this.tickable){
10549 cls : 'has-feedback',
10555 cls : 'roo-combo-removable-btn close'
10562 cls : 'has-feedback',
10571 if(this.removable && !this.editable && !this.tickable){
10573 cls : 'roo-removable',
10579 cls : 'roo-combo-removable-btn close'
10586 if (this.before || this.after) {
10589 cls : 'input-group',
10593 inputblock.cn.push({
10595 cls : 'input-group-addon input-group-prepend input-group-text',
10600 inputblock.cn.push(input);
10602 if(this.hasFeedback && !this.allowBlank){
10603 inputblock.cls += ' has-feedback';
10604 inputblock.cn.push(feedback);
10608 inputblock.cn.push({
10610 cls : 'input-group-addon input-group-append input-group-text',
10619 var ibwrap = inputblock;
10624 cls: 'roo-select2-choices',
10628 cls: 'roo-select2-search-field',
10640 cls: 'roo-select2-container input-group',
10645 cls: 'form-hidden-field'
10651 if(!this.multiple && this.showToggleBtn){
10657 if (this.caret != false) {
10660 cls: 'fa fa-' + this.caret
10667 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10669 Roo.bootstrap.version == 3 ? caret : '',
10672 cls: 'combobox-clear',
10686 combobox.cls += ' roo-select2-container-multi';
10690 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10691 tooltip : 'This field is required'
10693 if (Roo.bootstrap.version == 4) {
10696 style : 'display:none'
10701 if (align ==='left' && this.fieldLabel.length) {
10703 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10710 cls : 'control-label',
10711 html : this.fieldLabel
10723 var labelCfg = cfg.cn[1];
10724 var contentCfg = cfg.cn[2];
10726 if(this.indicatorpos == 'right'){
10731 cls : 'control-label',
10735 html : this.fieldLabel
10749 labelCfg = cfg.cn[0];
10750 contentCfg = cfg.cn[1];
10753 if(this.labelWidth > 12){
10754 labelCfg.style = "width: " + this.labelWidth + 'px';
10757 if(this.labelWidth < 13 && this.labelmd == 0){
10758 this.labelmd = this.labelWidth;
10761 if(this.labellg > 0){
10762 labelCfg.cls += ' col-lg-' + this.labellg;
10763 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10766 if(this.labelmd > 0){
10767 labelCfg.cls += ' col-md-' + this.labelmd;
10768 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10771 if(this.labelsm > 0){
10772 labelCfg.cls += ' col-sm-' + this.labelsm;
10773 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10776 if(this.labelxs > 0){
10777 labelCfg.cls += ' col-xs-' + this.labelxs;
10778 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10781 } else if ( this.fieldLabel.length) {
10782 // Roo.log(" label");
10787 //cls : 'input-group-addon',
10788 html : this.fieldLabel
10796 if(this.indicatorpos == 'right'){
10804 html : this.fieldLabel
10818 // Roo.log(" no label && no align");
10825 ['xs','sm','md','lg'].map(function(size){
10826 if (settings[size]) {
10827 cfg.cls += ' col-' + size + '-' + settings[size];
10838 onResize : function(w, h){
10839 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10840 // if(typeof w == 'number'){
10841 // var x = w - this.trigger.getWidth();
10842 // this.inputEl().setWidth(this.adjustWidth('input', x));
10843 // this.trigger.setStyle('left', x+'px');
10848 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10851 getResizeEl : function(){
10852 return this.inputEl();
10856 getPositionEl : function(){
10857 return this.inputEl();
10861 alignErrorIcon : function(){
10862 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10866 initEvents : function(){
10870 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10871 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10872 if(!this.multiple && this.showToggleBtn){
10873 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10874 if(this.hideTrigger){
10875 this.trigger.setDisplayed(false);
10877 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10881 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10884 if(this.removable && !this.editable && !this.tickable){
10885 var close = this.closeTriggerEl();
10888 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10889 close.on('click', this.removeBtnClick, this, close);
10893 //this.trigger.addClassOnOver('x-form-trigger-over');
10894 //this.trigger.addClassOnClick('x-form-trigger-click');
10897 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10901 closeTriggerEl : function()
10903 var close = this.el.select('.roo-combo-removable-btn', true).first();
10904 return close ? close : false;
10907 removeBtnClick : function(e, h, el)
10909 e.preventDefault();
10911 if(this.fireEvent("remove", this) !== false){
10913 this.fireEvent("afterremove", this)
10917 createList : function()
10919 this.list = Roo.get(document.body).createChild({
10920 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10921 cls: 'typeahead typeahead-long dropdown-menu',
10922 style: 'display:none'
10925 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10930 initTrigger : function(){
10935 onDestroy : function(){
10937 this.trigger.removeAllListeners();
10938 // this.trigger.remove();
10941 // this.wrap.remove();
10943 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10947 onFocus : function(){
10948 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10950 if(!this.mimicing){
10951 this.wrap.addClass('x-trigger-wrap-focus');
10952 this.mimicing = true;
10953 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10954 if(this.monitorTab){
10955 this.el.on("keydown", this.checkTab, this);
10962 checkTab : function(e){
10963 if(e.getKey() == e.TAB){
10964 this.triggerBlur();
10969 onBlur : function(){
10974 mimicBlur : function(e, t){
10976 if(!this.wrap.contains(t) && this.validateBlur()){
10977 this.triggerBlur();
10983 triggerBlur : function(){
10984 this.mimicing = false;
10985 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10986 if(this.monitorTab){
10987 this.el.un("keydown", this.checkTab, this);
10989 //this.wrap.removeClass('x-trigger-wrap-focus');
10990 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10994 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10995 validateBlur : function(e, t){
11000 onDisable : function(){
11001 this.inputEl().dom.disabled = true;
11002 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11004 // this.wrap.addClass('x-item-disabled');
11009 onEnable : function(){
11010 this.inputEl().dom.disabled = false;
11011 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11013 // this.el.removeClass('x-item-disabled');
11018 onShow : function(){
11019 var ae = this.getActionEl();
11022 ae.dom.style.display = '';
11023 ae.dom.style.visibility = 'visible';
11029 onHide : function(){
11030 var ae = this.getActionEl();
11031 ae.dom.style.display = 'none';
11035 * The function that should handle the trigger's click event. This method does nothing by default until overridden
11036 * by an implementing function.
11038 * @param {EventObject} e
11040 onTriggerClick : Roo.emptyFn
11044 * Ext JS Library 1.1.1
11045 * Copyright(c) 2006-2007, Ext JS, LLC.
11047 * Originally Released Under LGPL - original licence link has changed is not relivant.
11050 * <script type="text/javascript">
11055 * @class Roo.data.SortTypes
11057 * Defines the default sorting (casting?) comparison functions used when sorting data.
11059 Roo.data.SortTypes = {
11061 * Default sort that does nothing
11062 * @param {Mixed} s The value being converted
11063 * @return {Mixed} The comparison value
11065 none : function(s){
11070 * The regular expression used to strip tags
11074 stripTagsRE : /<\/?[^>]+>/gi,
11077 * Strips all HTML tags to sort on text only
11078 * @param {Mixed} s The value being converted
11079 * @return {String} The comparison value
11081 asText : function(s){
11082 return String(s).replace(this.stripTagsRE, "");
11086 * Strips all HTML tags to sort on text only - Case insensitive
11087 * @param {Mixed} s The value being converted
11088 * @return {String} The comparison value
11090 asUCText : function(s){
11091 return String(s).toUpperCase().replace(this.stripTagsRE, "");
11095 * Case insensitive string
11096 * @param {Mixed} s The value being converted
11097 * @return {String} The comparison value
11099 asUCString : function(s) {
11100 return String(s).toUpperCase();
11105 * @param {Mixed} s The value being converted
11106 * @return {Number} The comparison value
11108 asDate : function(s) {
11112 if(s instanceof Date){
11113 return s.getTime();
11115 return Date.parse(String(s));
11120 * @param {Mixed} s The value being converted
11121 * @return {Float} The comparison value
11123 asFloat : function(s) {
11124 var val = parseFloat(String(s).replace(/,/g, ""));
11133 * @param {Mixed} s The value being converted
11134 * @return {Number} The comparison value
11136 asInt : function(s) {
11137 var val = parseInt(String(s).replace(/,/g, ""));
11145 * Ext JS Library 1.1.1
11146 * Copyright(c) 2006-2007, Ext JS, LLC.
11148 * Originally Released Under LGPL - original licence link has changed is not relivant.
11151 * <script type="text/javascript">
11155 * @class Roo.data.Record
11156 * Instances of this class encapsulate both record <em>definition</em> information, and record
11157 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11158 * to access Records cached in an {@link Roo.data.Store} object.<br>
11160 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11161 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11164 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11166 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11167 * {@link #create}. The parameters are the same.
11168 * @param {Array} data An associative Array of data values keyed by the field name.
11169 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11170 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11171 * not specified an integer id is generated.
11173 Roo.data.Record = function(data, id){
11174 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11179 * Generate a constructor for a specific record layout.
11180 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11181 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11182 * Each field definition object may contain the following properties: <ul>
11183 * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
11184 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11185 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11186 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11187 * is being used, then this is a string containing the javascript expression to reference the data relative to
11188 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11189 * to the data item relative to the record element. If the mapping expression is the same as the field name,
11190 * this may be omitted.</p></li>
11191 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11192 * <ul><li>auto (Default, implies no conversion)</li>
11197 * <li>date</li></ul></p></li>
11198 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11199 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11200 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11201 * by the Reader into an object that will be stored in the Record. It is passed the
11202 * following parameters:<ul>
11203 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11205 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11207 * <br>usage:<br><pre><code>
11208 var TopicRecord = Roo.data.Record.create(
11209 {name: 'title', mapping: 'topic_title'},
11210 {name: 'author', mapping: 'username'},
11211 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11212 {name: 'lastPost', mapping: 'post_time', type: 'date'},
11213 {name: 'lastPoster', mapping: 'user2'},
11214 {name: 'excerpt', mapping: 'post_text'}
11217 var myNewRecord = new TopicRecord({
11218 title: 'Do my job please',
11221 lastPost: new Date(),
11222 lastPoster: 'Animal',
11223 excerpt: 'No way dude!'
11225 myStore.add(myNewRecord);
11230 Roo.data.Record.create = function(o){
11231 var f = function(){
11232 f.superclass.constructor.apply(this, arguments);
11234 Roo.extend(f, Roo.data.Record);
11235 var p = f.prototype;
11236 p.fields = new Roo.util.MixedCollection(false, function(field){
11239 for(var i = 0, len = o.length; i < len; i++){
11240 p.fields.add(new Roo.data.Field(o[i]));
11242 f.getField = function(name){
11243 return p.fields.get(name);
11248 Roo.data.Record.AUTO_ID = 1000;
11249 Roo.data.Record.EDIT = 'edit';
11250 Roo.data.Record.REJECT = 'reject';
11251 Roo.data.Record.COMMIT = 'commit';
11253 Roo.data.Record.prototype = {
11255 * Readonly flag - true if this record has been modified.
11264 join : function(store){
11265 this.store = store;
11269 * Set the named field to the specified value.
11270 * @param {String} name The name of the field to set.
11271 * @param {Object} value The value to set the field to.
11273 set : function(name, value){
11274 if(this.data[name] == value){
11278 if(!this.modified){
11279 this.modified = {};
11281 if(typeof this.modified[name] == 'undefined'){
11282 this.modified[name] = this.data[name];
11284 this.data[name] = value;
11285 if(!this.editing && this.store){
11286 this.store.afterEdit(this);
11291 * Get the value of the named field.
11292 * @param {String} name The name of the field to get the value of.
11293 * @return {Object} The value of the field.
11295 get : function(name){
11296 return this.data[name];
11300 beginEdit : function(){
11301 this.editing = true;
11302 this.modified = {};
11306 cancelEdit : function(){
11307 this.editing = false;
11308 delete this.modified;
11312 endEdit : function(){
11313 this.editing = false;
11314 if(this.dirty && this.store){
11315 this.store.afterEdit(this);
11320 * Usually called by the {@link Roo.data.Store} which owns the Record.
11321 * Rejects all changes made to the Record since either creation, or the last commit operation.
11322 * Modified fields are reverted to their original values.
11324 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11325 * of reject operations.
11327 reject : function(){
11328 var m = this.modified;
11330 if(typeof m[n] != "function"){
11331 this.data[n] = m[n];
11334 this.dirty = false;
11335 delete this.modified;
11336 this.editing = false;
11338 this.store.afterReject(this);
11343 * Usually called by the {@link Roo.data.Store} which owns the Record.
11344 * Commits all changes made to the Record since either creation, or the last commit operation.
11346 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11347 * of commit operations.
11349 commit : function(){
11350 this.dirty = false;
11351 delete this.modified;
11352 this.editing = false;
11354 this.store.afterCommit(this);
11359 hasError : function(){
11360 return this.error != null;
11364 clearError : function(){
11369 * Creates a copy of this record.
11370 * @param {String} id (optional) A new record id if you don't want to use this record's id
11373 copy : function(newId) {
11374 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11378 * Ext JS Library 1.1.1
11379 * Copyright(c) 2006-2007, Ext JS, LLC.
11381 * Originally Released Under LGPL - original licence link has changed is not relivant.
11384 * <script type="text/javascript">
11390 * @class Roo.data.Store
11391 * @extends Roo.util.Observable
11392 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11393 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11395 * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
11396 * has no knowledge of the format of the data returned by the Proxy.<br>
11398 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11399 * instances from the data object. These records are cached and made available through accessor functions.
11401 * Creates a new Store.
11402 * @param {Object} config A config object containing the objects needed for the Store to access data,
11403 * and read the data into Records.
11405 Roo.data.Store = function(config){
11406 this.data = new Roo.util.MixedCollection(false);
11407 this.data.getKey = function(o){
11410 this.baseParams = {};
11412 this.paramNames = {
11417 "multisort" : "_multisort"
11420 if(config && config.data){
11421 this.inlineData = config.data;
11422 delete config.data;
11425 Roo.apply(this, config);
11427 if(this.reader){ // reader passed
11428 this.reader = Roo.factory(this.reader, Roo.data);
11429 this.reader.xmodule = this.xmodule || false;
11430 if(!this.recordType){
11431 this.recordType = this.reader.recordType;
11433 if(this.reader.onMetaChange){
11434 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11438 if(this.recordType){
11439 this.fields = this.recordType.prototype.fields;
11441 this.modified = [];
11445 * @event datachanged
11446 * Fires when the data cache has changed, and a widget which is using this Store
11447 * as a Record cache should refresh its view.
11448 * @param {Store} this
11450 datachanged : true,
11452 * @event metachange
11453 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11454 * @param {Store} this
11455 * @param {Object} meta The JSON metadata
11460 * Fires when Records have been added to the Store
11461 * @param {Store} this
11462 * @param {Roo.data.Record[]} records The array of Records added
11463 * @param {Number} index The index at which the record(s) were added
11468 * Fires when a Record has been removed from the Store
11469 * @param {Store} this
11470 * @param {Roo.data.Record} record The Record that was removed
11471 * @param {Number} index The index at which the record was removed
11476 * Fires when a Record has been updated
11477 * @param {Store} this
11478 * @param {Roo.data.Record} record The Record that was updated
11479 * @param {String} operation The update operation being performed. Value may be one of:
11481 Roo.data.Record.EDIT
11482 Roo.data.Record.REJECT
11483 Roo.data.Record.COMMIT
11489 * Fires when the data cache has been cleared.
11490 * @param {Store} this
11494 * @event beforeload
11495 * Fires before a request is made for a new data object. If the beforeload handler returns false
11496 * the load action will be canceled.
11497 * @param {Store} this
11498 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11502 * @event beforeloadadd
11503 * Fires after a new set of Records has been loaded.
11504 * @param {Store} this
11505 * @param {Roo.data.Record[]} records The Records that were loaded
11506 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11508 beforeloadadd : true,
11511 * Fires after a new set of Records has been loaded, before they are added to the store.
11512 * @param {Store} this
11513 * @param {Roo.data.Record[]} records The Records that were loaded
11514 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11515 * @params {Object} return from reader
11519 * @event loadexception
11520 * Fires if an exception occurs in the Proxy during loading.
11521 * Called with the signature of the Proxy's "loadexception" event.
11522 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11525 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11526 * @param {Object} load options
11527 * @param {Object} jsonData from your request (normally this contains the Exception)
11529 loadexception : true
11533 this.proxy = Roo.factory(this.proxy, Roo.data);
11534 this.proxy.xmodule = this.xmodule || false;
11535 this.relayEvents(this.proxy, ["loadexception"]);
11537 this.sortToggle = {};
11538 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11540 Roo.data.Store.superclass.constructor.call(this);
11542 if(this.inlineData){
11543 this.loadData(this.inlineData);
11544 delete this.inlineData;
11548 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11550 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11551 * without a remote query - used by combo/forms at present.
11555 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11558 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11561 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11562 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11565 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11566 * on any HTTP request
11569 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11572 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11576 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11577 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11579 remoteSort : false,
11582 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11583 * loaded or when a record is removed. (defaults to false).
11585 pruneModifiedRecords : false,
11588 lastOptions : null,
11591 * Add Records to the Store and fires the add event.
11592 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11594 add : function(records){
11595 records = [].concat(records);
11596 for(var i = 0, len = records.length; i < len; i++){
11597 records[i].join(this);
11599 var index = this.data.length;
11600 this.data.addAll(records);
11601 this.fireEvent("add", this, records, index);
11605 * Remove a Record from the Store and fires the remove event.
11606 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11608 remove : function(record){
11609 var index = this.data.indexOf(record);
11610 this.data.removeAt(index);
11612 if(this.pruneModifiedRecords){
11613 this.modified.remove(record);
11615 this.fireEvent("remove", this, record, index);
11619 * Remove all Records from the Store and fires the clear event.
11621 removeAll : function(){
11623 if(this.pruneModifiedRecords){
11624 this.modified = [];
11626 this.fireEvent("clear", this);
11630 * Inserts Records to the Store at the given index and fires the add event.
11631 * @param {Number} index The start index at which to insert the passed Records.
11632 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11634 insert : function(index, records){
11635 records = [].concat(records);
11636 for(var i = 0, len = records.length; i < len; i++){
11637 this.data.insert(index, records[i]);
11638 records[i].join(this);
11640 this.fireEvent("add", this, records, index);
11644 * Get the index within the cache of the passed Record.
11645 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11646 * @return {Number} The index of the passed Record. Returns -1 if not found.
11648 indexOf : function(record){
11649 return this.data.indexOf(record);
11653 * Get the index within the cache of the Record with the passed id.
11654 * @param {String} id The id of the Record to find.
11655 * @return {Number} The index of the Record. Returns -1 if not found.
11657 indexOfId : function(id){
11658 return this.data.indexOfKey(id);
11662 * Get the Record with the specified id.
11663 * @param {String} id The id of the Record to find.
11664 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11666 getById : function(id){
11667 return this.data.key(id);
11671 * Get the Record at the specified index.
11672 * @param {Number} index The index of the Record to find.
11673 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11675 getAt : function(index){
11676 return this.data.itemAt(index);
11680 * Returns a range of Records between specified indices.
11681 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11682 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11683 * @return {Roo.data.Record[]} An array of Records
11685 getRange : function(start, end){
11686 return this.data.getRange(start, end);
11690 storeOptions : function(o){
11691 o = Roo.apply({}, o);
11694 this.lastOptions = o;
11698 * Loads the Record cache from the configured Proxy using the configured Reader.
11700 * If using remote paging, then the first load call must specify the <em>start</em>
11701 * and <em>limit</em> properties in the options.params property to establish the initial
11702 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11704 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11705 * and this call will return before the new data has been loaded. Perform any post-processing
11706 * in a callback function, or in a "load" event handler.</strong>
11708 * @param {Object} options An object containing properties which control loading options:<ul>
11709 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11710 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11711 * passed the following arguments:<ul>
11712 * <li>r : Roo.data.Record[]</li>
11713 * <li>options: Options object from the load call</li>
11714 * <li>success: Boolean success indicator</li></ul></li>
11715 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11716 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11719 load : function(options){
11720 options = options || {};
11721 if(this.fireEvent("beforeload", this, options) !== false){
11722 this.storeOptions(options);
11723 var p = Roo.apply(options.params || {}, this.baseParams);
11724 // if meta was not loaded from remote source.. try requesting it.
11725 if (!this.reader.metaFromRemote) {
11726 p._requestMeta = 1;
11728 if(this.sortInfo && this.remoteSort){
11729 var pn = this.paramNames;
11730 p[pn["sort"]] = this.sortInfo.field;
11731 p[pn["dir"]] = this.sortInfo.direction;
11733 if (this.multiSort) {
11734 var pn = this.paramNames;
11735 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11738 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11743 * Reloads the Record cache from the configured Proxy using the configured Reader and
11744 * the options from the last load operation performed.
11745 * @param {Object} options (optional) An object containing properties which may override the options
11746 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11747 * the most recently used options are reused).
11749 reload : function(options){
11750 this.load(Roo.applyIf(options||{}, this.lastOptions));
11754 // Called as a callback by the Reader during a load operation.
11755 loadRecords : function(o, options, success){
11756 if(!o || success === false){
11757 if(success !== false){
11758 this.fireEvent("load", this, [], options, o);
11760 if(options.callback){
11761 options.callback.call(options.scope || this, [], options, false);
11765 // if data returned failure - throw an exception.
11766 if (o.success === false) {
11767 // show a message if no listener is registered.
11768 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11769 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11771 // loadmask wil be hooked into this..
11772 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11775 var r = o.records, t = o.totalRecords || r.length;
11777 this.fireEvent("beforeloadadd", this, r, options, o);
11779 if(!options || options.add !== true){
11780 if(this.pruneModifiedRecords){
11781 this.modified = [];
11783 for(var i = 0, len = r.length; i < len; i++){
11787 this.data = this.snapshot;
11788 delete this.snapshot;
11791 this.data.addAll(r);
11792 this.totalLength = t;
11794 this.fireEvent("datachanged", this);
11796 this.totalLength = Math.max(t, this.data.length+r.length);
11800 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11802 var e = new Roo.data.Record({});
11804 e.set(this.parent.displayField, this.parent.emptyTitle);
11805 e.set(this.parent.valueField, '');
11810 this.fireEvent("load", this, r, options, o);
11811 if(options.callback){
11812 options.callback.call(options.scope || this, r, options, true);
11818 * Loads data from a passed data block. A Reader which understands the format of the data
11819 * must have been configured in the constructor.
11820 * @param {Object} data The data block from which to read the Records. The format of the data expected
11821 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11822 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11824 loadData : function(o, append){
11825 var r = this.reader.readRecords(o);
11826 this.loadRecords(r, {add: append}, true);
11830 * Gets the number of cached records.
11832 * <em>If using paging, this may not be the total size of the dataset. If the data object
11833 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11834 * the data set size</em>
11836 getCount : function(){
11837 return this.data.length || 0;
11841 * Gets the total number of records in the dataset as returned by the server.
11843 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11844 * the dataset size</em>
11846 getTotalCount : function(){
11847 return this.totalLength || 0;
11851 * Returns the sort state of the Store as an object with two properties:
11853 field {String} The name of the field by which the Records are sorted
11854 direction {String} The sort order, "ASC" or "DESC"
11857 getSortState : function(){
11858 return this.sortInfo;
11862 applySort : function(){
11863 if(this.sortInfo && !this.remoteSort){
11864 var s = this.sortInfo, f = s.field;
11865 var st = this.fields.get(f).sortType;
11866 var fn = function(r1, r2){
11867 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11868 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11870 this.data.sort(s.direction, fn);
11871 if(this.snapshot && this.snapshot != this.data){
11872 this.snapshot.sort(s.direction, fn);
11878 * Sets the default sort column and order to be used by the next load operation.
11879 * @param {String} fieldName The name of the field to sort by.
11880 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11882 setDefaultSort : function(field, dir){
11883 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11887 * Sort the Records.
11888 * If remote sorting is used, the sort is performed on the server, and the cache is
11889 * reloaded. If local sorting is used, the cache is sorted internally.
11890 * @param {String} fieldName The name of the field to sort by.
11891 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11893 sort : function(fieldName, dir){
11894 var f = this.fields.get(fieldName);
11896 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11898 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11899 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11904 this.sortToggle[f.name] = dir;
11905 this.sortInfo = {field: f.name, direction: dir};
11906 if(!this.remoteSort){
11908 this.fireEvent("datachanged", this);
11910 this.load(this.lastOptions);
11915 * Calls the specified function for each of the Records in the cache.
11916 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11917 * Returning <em>false</em> aborts and exits the iteration.
11918 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11920 each : function(fn, scope){
11921 this.data.each(fn, scope);
11925 * Gets all records modified since the last commit. Modified records are persisted across load operations
11926 * (e.g., during paging).
11927 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11929 getModifiedRecords : function(){
11930 return this.modified;
11934 createFilterFn : function(property, value, anyMatch){
11935 if(!value.exec){ // not a regex
11936 value = String(value);
11937 if(value.length == 0){
11940 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11942 return function(r){
11943 return value.test(r.data[property]);
11948 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11949 * @param {String} property A field on your records
11950 * @param {Number} start The record index to start at (defaults to 0)
11951 * @param {Number} end The last record index to include (defaults to length - 1)
11952 * @return {Number} The sum
11954 sum : function(property, start, end){
11955 var rs = this.data.items, v = 0;
11956 start = start || 0;
11957 end = (end || end === 0) ? end : rs.length-1;
11959 for(var i = start; i <= end; i++){
11960 v += (rs[i].data[property] || 0);
11966 * Filter the records by a specified property.
11967 * @param {String} field A field on your records
11968 * @param {String/RegExp} value Either a string that the field
11969 * should start with or a RegExp to test against the field
11970 * @param {Boolean} anyMatch True to match any part not just the beginning
11972 filter : function(property, value, anyMatch){
11973 var fn = this.createFilterFn(property, value, anyMatch);
11974 return fn ? this.filterBy(fn) : this.clearFilter();
11978 * Filter by a function. The specified function will be called with each
11979 * record in this data source. If the function returns true the record is included,
11980 * otherwise it is filtered.
11981 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11982 * @param {Object} scope (optional) The scope of the function (defaults to this)
11984 filterBy : function(fn, scope){
11985 this.snapshot = this.snapshot || this.data;
11986 this.data = this.queryBy(fn, scope||this);
11987 this.fireEvent("datachanged", this);
11991 * Query the records by a specified property.
11992 * @param {String} field A field on your records
11993 * @param {String/RegExp} value Either a string that the field
11994 * should start with or a RegExp to test against the field
11995 * @param {Boolean} anyMatch True to match any part not just the beginning
11996 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11998 query : function(property, value, anyMatch){
11999 var fn = this.createFilterFn(property, value, anyMatch);
12000 return fn ? this.queryBy(fn) : this.data.clone();
12004 * Query by a function. The specified function will be called with each
12005 * record in this data source. If the function returns true the record is included
12007 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12008 * @param {Object} scope (optional) The scope of the function (defaults to this)
12009 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12011 queryBy : function(fn, scope){
12012 var data = this.snapshot || this.data;
12013 return data.filterBy(fn, scope||this);
12017 * Collects unique values for a particular dataIndex from this store.
12018 * @param {String} dataIndex The property to collect
12019 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12020 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12021 * @return {Array} An array of the unique values
12023 collect : function(dataIndex, allowNull, bypassFilter){
12024 var d = (bypassFilter === true && this.snapshot) ?
12025 this.snapshot.items : this.data.items;
12026 var v, sv, r = [], l = {};
12027 for(var i = 0, len = d.length; i < len; i++){
12028 v = d[i].data[dataIndex];
12030 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12039 * Revert to a view of the Record cache with no filtering applied.
12040 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12042 clearFilter : function(suppressEvent){
12043 if(this.snapshot && this.snapshot != this.data){
12044 this.data = this.snapshot;
12045 delete this.snapshot;
12046 if(suppressEvent !== true){
12047 this.fireEvent("datachanged", this);
12053 afterEdit : function(record){
12054 if(this.modified.indexOf(record) == -1){
12055 this.modified.push(record);
12057 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12061 afterReject : function(record){
12062 this.modified.remove(record);
12063 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12067 afterCommit : function(record){
12068 this.modified.remove(record);
12069 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12073 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12074 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12076 commitChanges : function(){
12077 var m = this.modified.slice(0);
12078 this.modified = [];
12079 for(var i = 0, len = m.length; i < len; i++){
12085 * Cancel outstanding changes on all changed records.
12087 rejectChanges : function(){
12088 var m = this.modified.slice(0);
12089 this.modified = [];
12090 for(var i = 0, len = m.length; i < len; i++){
12095 onMetaChange : function(meta, rtype, o){
12096 this.recordType = rtype;
12097 this.fields = rtype.prototype.fields;
12098 delete this.snapshot;
12099 this.sortInfo = meta.sortInfo || this.sortInfo;
12100 this.modified = [];
12101 this.fireEvent('metachange', this, this.reader.meta);
12104 moveIndex : function(data, type)
12106 var index = this.indexOf(data);
12108 var newIndex = index + type;
12112 this.insert(newIndex, data);
12117 * Ext JS Library 1.1.1
12118 * Copyright(c) 2006-2007, Ext JS, LLC.
12120 * Originally Released Under LGPL - original licence link has changed is not relivant.
12123 * <script type="text/javascript">
12127 * @class Roo.data.SimpleStore
12128 * @extends Roo.data.Store
12129 * Small helper class to make creating Stores from Array data easier.
12130 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12131 * @cfg {Array} fields An array of field definition objects, or field name strings.
12132 * @cfg {Object} an existing reader (eg. copied from another store)
12133 * @cfg {Array} data The multi-dimensional array of data
12135 * @param {Object} config
12137 Roo.data.SimpleStore = function(config)
12139 Roo.data.SimpleStore.superclass.constructor.call(this, {
12141 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
12144 Roo.data.Record.create(config.fields)
12146 proxy : new Roo.data.MemoryProxy(config.data)
12150 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12152 * Ext JS Library 1.1.1
12153 * Copyright(c) 2006-2007, Ext JS, LLC.
12155 * Originally Released Under LGPL - original licence link has changed is not relivant.
12158 * <script type="text/javascript">
12163 * @extends Roo.data.Store
12164 * @class Roo.data.JsonStore
12165 * Small helper class to make creating Stores for JSON data easier. <br/>
12167 var store = new Roo.data.JsonStore({
12168 url: 'get-images.php',
12170 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12173 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12174 * JsonReader and HttpProxy (unless inline data is provided).</b>
12175 * @cfg {Array} fields An array of field definition objects, or field name strings.
12177 * @param {Object} config
12179 Roo.data.JsonStore = function(c){
12180 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12181 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12182 reader: new Roo.data.JsonReader(c, c.fields)
12185 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12187 * Ext JS Library 1.1.1
12188 * Copyright(c) 2006-2007, Ext JS, LLC.
12190 * Originally Released Under LGPL - original licence link has changed is not relivant.
12193 * <script type="text/javascript">
12197 Roo.data.Field = function(config){
12198 if(typeof config == "string"){
12199 config = {name: config};
12201 Roo.apply(this, config);
12204 this.type = "auto";
12207 var st = Roo.data.SortTypes;
12208 // named sortTypes are supported, here we look them up
12209 if(typeof this.sortType == "string"){
12210 this.sortType = st[this.sortType];
12213 // set default sortType for strings and dates
12214 if(!this.sortType){
12217 this.sortType = st.asUCString;
12220 this.sortType = st.asDate;
12223 this.sortType = st.none;
12228 var stripRe = /[\$,%]/g;
12230 // prebuilt conversion function for this field, instead of
12231 // switching every time we're reading a value
12233 var cv, dateFormat = this.dateFormat;
12238 cv = function(v){ return v; };
12241 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12245 return v !== undefined && v !== null && v !== '' ?
12246 parseInt(String(v).replace(stripRe, ""), 10) : '';
12251 return v !== undefined && v !== null && v !== '' ?
12252 parseFloat(String(v).replace(stripRe, ""), 10) : '';
12257 cv = function(v){ return v === true || v === "true" || v == 1; };
12264 if(v instanceof Date){
12268 if(dateFormat == "timestamp"){
12269 return new Date(v*1000);
12271 return Date.parseDate(v, dateFormat);
12273 var parsed = Date.parse(v);
12274 return parsed ? new Date(parsed) : null;
12283 Roo.data.Field.prototype = {
12291 * Ext JS Library 1.1.1
12292 * Copyright(c) 2006-2007, Ext JS, LLC.
12294 * Originally Released Under LGPL - original licence link has changed is not relivant.
12297 * <script type="text/javascript">
12300 // Base class for reading structured data from a data source. This class is intended to be
12301 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12304 * @class Roo.data.DataReader
12305 * Base class for reading structured data from a data source. This class is intended to be
12306 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12309 Roo.data.DataReader = function(meta, recordType){
12313 this.recordType = recordType instanceof Array ?
12314 Roo.data.Record.create(recordType) : recordType;
12317 Roo.data.DataReader.prototype = {
12320 readerType : 'Data',
12322 * Create an empty record
12323 * @param {Object} data (optional) - overlay some values
12324 * @return {Roo.data.Record} record created.
12326 newRow : function(d) {
12328 this.recordType.prototype.fields.each(function(c) {
12330 case 'int' : da[c.name] = 0; break;
12331 case 'date' : da[c.name] = new Date(); break;
12332 case 'float' : da[c.name] = 0.0; break;
12333 case 'boolean' : da[c.name] = false; break;
12334 default : da[c.name] = ""; break;
12338 return new this.recordType(Roo.apply(da, d));
12344 * Ext JS Library 1.1.1
12345 * Copyright(c) 2006-2007, Ext JS, LLC.
12347 * Originally Released Under LGPL - original licence link has changed is not relivant.
12350 * <script type="text/javascript">
12354 * @class Roo.data.DataProxy
12355 * @extends Roo.data.Observable
12356 * This class is an abstract base class for implementations which provide retrieval of
12357 * unformatted data objects.<br>
12359 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12360 * (of the appropriate type which knows how to parse the data object) to provide a block of
12361 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12363 * Custom implementations must implement the load method as described in
12364 * {@link Roo.data.HttpProxy#load}.
12366 Roo.data.DataProxy = function(){
12369 * @event beforeload
12370 * Fires before a network request is made to retrieve a data object.
12371 * @param {Object} This DataProxy object.
12372 * @param {Object} params The params parameter to the load function.
12377 * Fires before the load method's callback is called.
12378 * @param {Object} This DataProxy object.
12379 * @param {Object} o The data object.
12380 * @param {Object} arg The callback argument object passed to the load function.
12384 * @event loadexception
12385 * Fires if an Exception occurs during data retrieval.
12386 * @param {Object} This DataProxy object.
12387 * @param {Object} o The data object.
12388 * @param {Object} arg The callback argument object passed to the load function.
12389 * @param {Object} e The Exception.
12391 loadexception : true
12393 Roo.data.DataProxy.superclass.constructor.call(this);
12396 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12399 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12403 * Ext JS Library 1.1.1
12404 * Copyright(c) 2006-2007, Ext JS, LLC.
12406 * Originally Released Under LGPL - original licence link has changed is not relivant.
12409 * <script type="text/javascript">
12412 * @class Roo.data.MemoryProxy
12413 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12414 * to the Reader when its load method is called.
12416 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12418 Roo.data.MemoryProxy = function(data){
12422 Roo.data.MemoryProxy.superclass.constructor.call(this);
12426 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12429 * Load data from the requested source (in this case an in-memory
12430 * data object passed to the constructor), read the data object into
12431 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12432 * process that block using the passed callback.
12433 * @param {Object} params This parameter is not used by the MemoryProxy class.
12434 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12435 * object into a block of Roo.data.Records.
12436 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12437 * The function must be passed <ul>
12438 * <li>The Record block object</li>
12439 * <li>The "arg" argument from the load function</li>
12440 * <li>A boolean success indicator</li>
12442 * @param {Object} scope The scope in which to call the callback
12443 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12445 load : function(params, reader, callback, scope, arg){
12446 params = params || {};
12449 result = reader.readRecords(params.data ? params.data :this.data);
12451 this.fireEvent("loadexception", this, arg, null, e);
12452 callback.call(scope, null, arg, false);
12455 callback.call(scope, result, arg, true);
12459 update : function(params, records){
12464 * Ext JS Library 1.1.1
12465 * Copyright(c) 2006-2007, Ext JS, LLC.
12467 * Originally Released Under LGPL - original licence link has changed is not relivant.
12470 * <script type="text/javascript">
12473 * @class Roo.data.HttpProxy
12474 * @extends Roo.data.DataProxy
12475 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12476 * configured to reference a certain URL.<br><br>
12478 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12479 * from which the running page was served.<br><br>
12481 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12483 * Be aware that to enable the browser to parse an XML document, the server must set
12484 * the Content-Type header in the HTTP response to "text/xml".
12486 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12487 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12488 * will be used to make the request.
12490 Roo.data.HttpProxy = function(conn){
12491 Roo.data.HttpProxy.superclass.constructor.call(this);
12492 // is conn a conn config or a real conn?
12494 this.useAjax = !conn || !conn.events;
12498 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12499 // thse are take from connection...
12502 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12505 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12506 * extra parameters to each request made by this object. (defaults to undefined)
12509 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12510 * to each request made by this object. (defaults to undefined)
12513 * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
12516 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12519 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12525 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12529 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12530 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12531 * a finer-grained basis than the DataProxy events.
12533 getConnection : function(){
12534 return this.useAjax ? Roo.Ajax : this.conn;
12538 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12539 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12540 * process that block using the passed callback.
12541 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12542 * for the request to the remote server.
12543 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12544 * object into a block of Roo.data.Records.
12545 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12546 * The function must be passed <ul>
12547 * <li>The Record block object</li>
12548 * <li>The "arg" argument from the load function</li>
12549 * <li>A boolean success indicator</li>
12551 * @param {Object} scope The scope in which to call the callback
12552 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12554 load : function(params, reader, callback, scope, arg){
12555 if(this.fireEvent("beforeload", this, params) !== false){
12557 params : params || {},
12559 callback : callback,
12564 callback : this.loadResponse,
12568 Roo.applyIf(o, this.conn);
12569 if(this.activeRequest){
12570 Roo.Ajax.abort(this.activeRequest);
12572 this.activeRequest = Roo.Ajax.request(o);
12574 this.conn.request(o);
12577 callback.call(scope||this, null, arg, false);
12582 loadResponse : function(o, success, response){
12583 delete this.activeRequest;
12585 this.fireEvent("loadexception", this, o, response);
12586 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12591 result = o.reader.read(response);
12593 this.fireEvent("loadexception", this, o, response, e);
12594 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12598 this.fireEvent("load", this, o, o.request.arg);
12599 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12603 update : function(dataSet){
12608 updateResponse : function(dataSet){
12613 * Ext JS Library 1.1.1
12614 * Copyright(c) 2006-2007, Ext JS, LLC.
12616 * Originally Released Under LGPL - original licence link has changed is not relivant.
12619 * <script type="text/javascript">
12623 * @class Roo.data.ScriptTagProxy
12624 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12625 * other than the originating domain of the running page.<br><br>
12627 * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
12628 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12630 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12631 * source code that is used as the source inside a <script> tag.<br><br>
12633 * In order for the browser to process the returned data, the server must wrap the data object
12634 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12635 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12636 * depending on whether the callback name was passed:
12639 boolean scriptTag = false;
12640 String cb = request.getParameter("callback");
12643 response.setContentType("text/javascript");
12645 response.setContentType("application/x-json");
12647 Writer out = response.getWriter();
12649 out.write(cb + "(");
12651 out.print(dataBlock.toJsonString());
12658 * @param {Object} config A configuration object.
12660 Roo.data.ScriptTagProxy = function(config){
12661 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12662 Roo.apply(this, config);
12663 this.head = document.getElementsByTagName("head")[0];
12666 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12668 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12670 * @cfg {String} url The URL from which to request the data object.
12673 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12677 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12678 * the server the name of the callback function set up by the load call to process the returned data object.
12679 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12680 * javascript output which calls this named function passing the data object as its only parameter.
12682 callbackParam : "callback",
12684 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12685 * name to the request.
12690 * Load data from the configured URL, read the data object into
12691 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12692 * process that block using the passed callback.
12693 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12694 * for the request to the remote server.
12695 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12696 * object into a block of Roo.data.Records.
12697 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12698 * The function must be passed <ul>
12699 * <li>The Record block object</li>
12700 * <li>The "arg" argument from the load function</li>
12701 * <li>A boolean success indicator</li>
12703 * @param {Object} scope The scope in which to call the callback
12704 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12706 load : function(params, reader, callback, scope, arg){
12707 if(this.fireEvent("beforeload", this, params) !== false){
12709 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12711 var url = this.url;
12712 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12714 url += "&_dc=" + (new Date().getTime());
12716 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12719 cb : "stcCallback"+transId,
12720 scriptId : "stcScript"+transId,
12724 callback : callback,
12730 window[trans.cb] = function(o){
12731 conn.handleResponse(o, trans);
12734 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12736 if(this.autoAbort !== false){
12740 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12742 var script = document.createElement("script");
12743 script.setAttribute("src", url);
12744 script.setAttribute("type", "text/javascript");
12745 script.setAttribute("id", trans.scriptId);
12746 this.head.appendChild(script);
12748 this.trans = trans;
12750 callback.call(scope||this, null, arg, false);
12755 isLoading : function(){
12756 return this.trans ? true : false;
12760 * Abort the current server request.
12762 abort : function(){
12763 if(this.isLoading()){
12764 this.destroyTrans(this.trans);
12769 destroyTrans : function(trans, isLoaded){
12770 this.head.removeChild(document.getElementById(trans.scriptId));
12771 clearTimeout(trans.timeoutId);
12773 window[trans.cb] = undefined;
12775 delete window[trans.cb];
12778 // if hasn't been loaded, wait for load to remove it to prevent script error
12779 window[trans.cb] = function(){
12780 window[trans.cb] = undefined;
12782 delete window[trans.cb];
12789 handleResponse : function(o, trans){
12790 this.trans = false;
12791 this.destroyTrans(trans, true);
12794 result = trans.reader.readRecords(o);
12796 this.fireEvent("loadexception", this, o, trans.arg, e);
12797 trans.callback.call(trans.scope||window, null, trans.arg, false);
12800 this.fireEvent("load", this, o, trans.arg);
12801 trans.callback.call(trans.scope||window, result, trans.arg, true);
12805 handleFailure : function(trans){
12806 this.trans = false;
12807 this.destroyTrans(trans, false);
12808 this.fireEvent("loadexception", this, null, trans.arg);
12809 trans.callback.call(trans.scope||window, null, trans.arg, false);
12813 * Ext JS Library 1.1.1
12814 * Copyright(c) 2006-2007, Ext JS, LLC.
12816 * Originally Released Under LGPL - original licence link has changed is not relivant.
12819 * <script type="text/javascript">
12823 * @class Roo.data.JsonReader
12824 * @extends Roo.data.DataReader
12825 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12826 * based on mappings in a provided Roo.data.Record constructor.
12828 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12829 * in the reply previously.
12834 var RecordDef = Roo.data.Record.create([
12835 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12836 {name: 'occupation'} // This field will use "occupation" as the mapping.
12838 var myReader = new Roo.data.JsonReader({
12839 totalProperty: "results", // The property which contains the total dataset size (optional)
12840 root: "rows", // The property which contains an Array of row objects
12841 id: "id" // The property within each row object that provides an ID for the record (optional)
12845 * This would consume a JSON file like this:
12847 { 'results': 2, 'rows': [
12848 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12849 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12852 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12853 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12854 * paged from the remote server.
12855 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12856 * @cfg {String} root name of the property which contains the Array of row objects.
12857 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12858 * @cfg {Array} fields Array of field definition objects
12860 * Create a new JsonReader
12861 * @param {Object} meta Metadata configuration options
12862 * @param {Object} recordType Either an Array of field definition objects,
12863 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12865 Roo.data.JsonReader = function(meta, recordType){
12868 // set some defaults:
12869 Roo.applyIf(meta, {
12870 totalProperty: 'total',
12871 successProperty : 'success',
12876 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12878 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12880 readerType : 'Json',
12883 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12884 * Used by Store query builder to append _requestMeta to params.
12887 metaFromRemote : false,
12889 * This method is only used by a DataProxy which has retrieved data from a remote server.
12890 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12891 * @return {Object} data A data block which is used by an Roo.data.Store object as
12892 * a cache of Roo.data.Records.
12894 read : function(response){
12895 var json = response.responseText;
12897 var o = /* eval:var:o */ eval("("+json+")");
12899 throw {message: "JsonReader.read: Json object not found"};
12905 this.metaFromRemote = true;
12906 this.meta = o.metaData;
12907 this.recordType = Roo.data.Record.create(o.metaData.fields);
12908 this.onMetaChange(this.meta, this.recordType, o);
12910 return this.readRecords(o);
12913 // private function a store will implement
12914 onMetaChange : function(meta, recordType, o){
12921 simpleAccess: function(obj, subsc) {
12928 getJsonAccessor: function(){
12930 return function(expr) {
12932 return(re.test(expr))
12933 ? new Function("obj", "return obj." + expr)
12938 return Roo.emptyFn;
12943 * Create a data block containing Roo.data.Records from an XML document.
12944 * @param {Object} o An object which contains an Array of row objects in the property specified
12945 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12946 * which contains the total size of the dataset.
12947 * @return {Object} data A data block which is used by an Roo.data.Store object as
12948 * a cache of Roo.data.Records.
12950 readRecords : function(o){
12952 * After any data loads, the raw JSON data is available for further custom processing.
12956 var s = this.meta, Record = this.recordType,
12957 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12959 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12961 if(s.totalProperty) {
12962 this.getTotal = this.getJsonAccessor(s.totalProperty);
12964 if(s.successProperty) {
12965 this.getSuccess = this.getJsonAccessor(s.successProperty);
12967 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12969 var g = this.getJsonAccessor(s.id);
12970 this.getId = function(rec) {
12972 return (r === undefined || r === "") ? null : r;
12975 this.getId = function(){return null;};
12978 for(var jj = 0; jj < fl; jj++){
12980 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12981 this.ef[jj] = this.getJsonAccessor(map);
12985 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12986 if(s.totalProperty){
12987 var vt = parseInt(this.getTotal(o), 10);
12992 if(s.successProperty){
12993 var vs = this.getSuccess(o);
12994 if(vs === false || vs === 'false'){
12999 for(var i = 0; i < c; i++){
13002 var id = this.getId(n);
13003 for(var j = 0; j < fl; j++){
13005 var v = this.ef[j](n);
13007 Roo.log('missing convert for ' + f.name);
13011 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13013 var record = new Record(values, id);
13015 records[i] = record;
13021 totalRecords : totalRecords
13025 * using 'cn' the nested child reader read the child array into it's child stores.
13026 * @param {Object} rec The record with a 'children array
13028 loadDataFromChildren: function(rec)
13030 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13031 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13032 return this.loadData({ data : data, total : data.length });
13037 * Ext JS Library 1.1.1
13038 * Copyright(c) 2006-2007, Ext JS, LLC.
13040 * Originally Released Under LGPL - original licence link has changed is not relivant.
13043 * <script type="text/javascript">
13047 * @class Roo.data.ArrayReader
13048 * @extends Roo.data.DataReader
13049 * Data reader class to create an Array of Roo.data.Record objects from an Array.
13050 * Each element of that Array represents a row of data fields. The
13051 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13052 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13056 var RecordDef = Roo.data.Record.create([
13057 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
13058 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
13060 var myReader = new Roo.data.ArrayReader({
13061 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
13065 * This would consume an Array like this:
13067 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13071 * Create a new JsonReader
13072 * @param {Object} meta Metadata configuration options.
13073 * @param {Object|Array} recordType Either an Array of field definition objects
13075 * @cfg {Array} fields Array of field definition objects
13076 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13077 * as specified to {@link Roo.data.Record#create},
13078 * or an {@link Roo.data.Record} object
13081 * created using {@link Roo.data.Record#create}.
13083 Roo.data.ArrayReader = function(meta, recordType)
13085 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13088 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13091 * Create a data block containing Roo.data.Records from an XML document.
13092 * @param {Object} o An Array of row objects which represents the dataset.
13093 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13094 * a cache of Roo.data.Records.
13096 readRecords : function(o)
13098 var sid = this.meta ? this.meta.id : null;
13099 var recordType = this.recordType, fields = recordType.prototype.fields;
13102 for(var i = 0; i < root.length; i++){
13105 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13106 for(var j = 0, jlen = fields.length; j < jlen; j++){
13107 var f = fields.items[j];
13108 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13109 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13111 values[f.name] = v;
13113 var record = new recordType(values, id);
13115 records[records.length] = record;
13119 totalRecords : records.length
13123 * using 'cn' the nested child reader read the child array into it's child stores.
13124 * @param {Object} rec The record with a 'children array
13126 loadDataFromChildren: function(rec)
13128 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13129 return this.loadData(typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn);
13140 * @class Roo.bootstrap.ComboBox
13141 * @extends Roo.bootstrap.TriggerField
13142 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13143 * @cfg {Boolean} append (true|false) default false
13144 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13145 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13146 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13147 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13148 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13149 * @cfg {Boolean} animate default true
13150 * @cfg {Boolean} emptyResultText only for touch device
13151 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13152 * @cfg {String} emptyTitle default ''
13154 * Create a new ComboBox.
13155 * @param {Object} config Configuration options
13157 Roo.bootstrap.ComboBox = function(config){
13158 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13162 * Fires when the dropdown list is expanded
13163 * @param {Roo.bootstrap.ComboBox} combo This combo box
13168 * Fires when the dropdown list is collapsed
13169 * @param {Roo.bootstrap.ComboBox} combo This combo box
13173 * @event beforeselect
13174 * Fires before a list item is selected. Return false to cancel the selection.
13175 * @param {Roo.bootstrap.ComboBox} combo This combo box
13176 * @param {Roo.data.Record} record The data record returned from the underlying store
13177 * @param {Number} index The index of the selected item in the dropdown list
13179 'beforeselect' : true,
13182 * Fires when a list item is selected
13183 * @param {Roo.bootstrap.ComboBox} combo This combo box
13184 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13185 * @param {Number} index The index of the selected item in the dropdown list
13189 * @event beforequery
13190 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13191 * The event object passed has these properties:
13192 * @param {Roo.bootstrap.ComboBox} combo This combo box
13193 * @param {String} query The query
13194 * @param {Boolean} forceAll true to force "all" query
13195 * @param {Boolean} cancel true to cancel the query
13196 * @param {Object} e The query event object
13198 'beforequery': true,
13201 * Fires when the 'add' icon is pressed (add a listener to enable add button)
13202 * @param {Roo.bootstrap.ComboBox} combo This combo box
13207 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13208 * @param {Roo.bootstrap.ComboBox} combo This combo box
13209 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13214 * Fires when the remove value from the combobox array
13215 * @param {Roo.bootstrap.ComboBox} combo This combo box
13219 * @event afterremove
13220 * Fires when the remove value from the combobox array
13221 * @param {Roo.bootstrap.ComboBox} combo This combo box
13223 'afterremove' : true,
13225 * @event specialfilter
13226 * Fires when specialfilter
13227 * @param {Roo.bootstrap.ComboBox} combo This combo box
13229 'specialfilter' : true,
13232 * Fires when tick the element
13233 * @param {Roo.bootstrap.ComboBox} combo This combo box
13237 * @event touchviewdisplay
13238 * Fires when touch view require special display (default is using displayField)
13239 * @param {Roo.bootstrap.ComboBox} combo This combo box
13240 * @param {Object} cfg set html .
13242 'touchviewdisplay' : true
13247 this.tickItems = [];
13249 this.selectedIndex = -1;
13250 if(this.mode == 'local'){
13251 if(config.queryDelay === undefined){
13252 this.queryDelay = 10;
13254 if(config.minChars === undefined){
13260 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13263 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13264 * rendering into an Roo.Editor, defaults to false)
13267 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13268 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13271 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13274 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13275 * the dropdown list (defaults to undefined, with no header element)
13279 * @cfg {String/Roo.Template} tpl The template to use to render the output
13283 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13285 listWidth: undefined,
13287 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13288 * mode = 'remote' or 'text' if mode = 'local')
13290 displayField: undefined,
13293 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13294 * mode = 'remote' or 'value' if mode = 'local').
13295 * Note: use of a valueField requires the user make a selection
13296 * in order for a value to be mapped.
13298 valueField: undefined,
13300 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13305 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13306 * field's data value (defaults to the underlying DOM element's name)
13308 hiddenName: undefined,
13310 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13314 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13316 selectedClass: 'active',
13319 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13323 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13324 * anchor positions (defaults to 'tl-bl')
13326 listAlign: 'tl-bl?',
13328 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13332 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13333 * query specified by the allQuery config option (defaults to 'query')
13335 triggerAction: 'query',
13337 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13338 * (defaults to 4, does not apply if editable = false)
13342 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13343 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13347 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13348 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13352 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13353 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13357 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13358 * when editable = true (defaults to false)
13360 selectOnFocus:false,
13362 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13364 queryParam: 'query',
13366 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13367 * when mode = 'remote' (defaults to 'Loading...')
13369 loadingText: 'Loading...',
13371 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13375 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13379 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13380 * traditional select (defaults to true)
13384 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13388 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13392 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13393 * listWidth has a higher value)
13397 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13398 * allow the user to set arbitrary text into the field (defaults to false)
13400 forceSelection:false,
13402 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13403 * if typeAhead = true (defaults to 250)
13405 typeAheadDelay : 250,
13407 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13408 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13410 valueNotFoundText : undefined,
13412 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13414 blockFocus : false,
13417 * @cfg {Boolean} disableClear Disable showing of clear button.
13419 disableClear : false,
13421 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13423 alwaysQuery : false,
13426 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13431 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13433 invalidClass : "has-warning",
13436 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13438 validClass : "has-success",
13441 * @cfg {Boolean} specialFilter (true|false) special filter default false
13443 specialFilter : false,
13446 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13448 mobileTouchView : true,
13451 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13453 useNativeIOS : false,
13456 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13458 mobile_restrict_height : false,
13460 ios_options : false,
13472 btnPosition : 'right',
13473 triggerList : true,
13474 showToggleBtn : true,
13476 emptyResultText: 'Empty',
13477 triggerText : 'Select',
13480 // element that contains real text value.. (when hidden is used..)
13482 getAutoCreate : function()
13487 * Render classic select for iso
13490 if(Roo.isIOS && this.useNativeIOS){
13491 cfg = this.getAutoCreateNativeIOS();
13499 if(Roo.isTouch && this.mobileTouchView){
13500 cfg = this.getAutoCreateTouchView();
13507 if(!this.tickable){
13508 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13513 * ComboBox with tickable selections
13516 var align = this.labelAlign || this.parentLabelAlign();
13519 cls : 'form-group roo-combobox-tickable' //input-group
13522 var btn_text_select = '';
13523 var btn_text_done = '';
13524 var btn_text_cancel = '';
13526 if (this.btn_text_show) {
13527 btn_text_select = 'Select';
13528 btn_text_done = 'Done';
13529 btn_text_cancel = 'Cancel';
13534 cls : 'tickable-buttons',
13539 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13540 //html : this.triggerText
13541 html: btn_text_select
13547 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13549 html: btn_text_done
13555 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13557 html: btn_text_cancel
13563 buttons.cn.unshift({
13565 cls: 'roo-select2-search-field-input'
13571 Roo.each(buttons.cn, function(c){
13573 c.cls += ' btn-' + _this.size;
13576 if (_this.disabled) {
13583 style : 'display: contents',
13588 cls: 'form-hidden-field'
13592 cls: 'roo-select2-choices',
13596 cls: 'roo-select2-search-field',
13607 cls: 'roo-select2-container input-group roo-select2-container-multi',
13613 // cls: 'typeahead typeahead-long dropdown-menu',
13614 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13619 if(this.hasFeedback && !this.allowBlank){
13623 cls: 'glyphicon form-control-feedback'
13626 combobox.cn.push(feedback);
13631 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13632 tooltip : 'This field is required'
13634 if (Roo.bootstrap.version == 4) {
13637 style : 'display:none'
13640 if (align ==='left' && this.fieldLabel.length) {
13642 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13649 cls : 'control-label col-form-label',
13650 html : this.fieldLabel
13662 var labelCfg = cfg.cn[1];
13663 var contentCfg = cfg.cn[2];
13666 if(this.indicatorpos == 'right'){
13672 cls : 'control-label col-form-label',
13676 html : this.fieldLabel
13692 labelCfg = cfg.cn[0];
13693 contentCfg = cfg.cn[1];
13697 if(this.labelWidth > 12){
13698 labelCfg.style = "width: " + this.labelWidth + 'px';
13701 if(this.labelWidth < 13 && this.labelmd == 0){
13702 this.labelmd = this.labelWidth;
13705 if(this.labellg > 0){
13706 labelCfg.cls += ' col-lg-' + this.labellg;
13707 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13710 if(this.labelmd > 0){
13711 labelCfg.cls += ' col-md-' + this.labelmd;
13712 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13715 if(this.labelsm > 0){
13716 labelCfg.cls += ' col-sm-' + this.labelsm;
13717 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13720 if(this.labelxs > 0){
13721 labelCfg.cls += ' col-xs-' + this.labelxs;
13722 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13726 } else if ( this.fieldLabel.length) {
13727 // Roo.log(" label");
13732 //cls : 'input-group-addon',
13733 html : this.fieldLabel
13738 if(this.indicatorpos == 'right'){
13742 //cls : 'input-group-addon',
13743 html : this.fieldLabel
13753 // Roo.log(" no label && no align");
13760 ['xs','sm','md','lg'].map(function(size){
13761 if (settings[size]) {
13762 cfg.cls += ' col-' + size + '-' + settings[size];
13770 _initEventsCalled : false,
13773 initEvents: function()
13775 if (this._initEventsCalled) { // as we call render... prevent looping...
13778 this._initEventsCalled = true;
13781 throw "can not find store for combo";
13784 this.indicator = this.indicatorEl();
13786 this.store = Roo.factory(this.store, Roo.data);
13787 this.store.parent = this;
13789 // if we are building from html. then this element is so complex, that we can not really
13790 // use the rendered HTML.
13791 // so we have to trash and replace the previous code.
13792 if (Roo.XComponent.build_from_html) {
13793 // remove this element....
13794 var e = this.el.dom, k=0;
13795 while (e ) { e = e.previousSibling; ++k;}
13800 this.rendered = false;
13802 this.render(this.parent().getChildContainer(true), k);
13805 if(Roo.isIOS && this.useNativeIOS){
13806 this.initIOSView();
13814 if(Roo.isTouch && this.mobileTouchView){
13815 this.initTouchView();
13820 this.initTickableEvents();
13824 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13826 if(this.hiddenName){
13828 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13830 this.hiddenField.dom.value =
13831 this.hiddenValue !== undefined ? this.hiddenValue :
13832 this.value !== undefined ? this.value : '';
13834 // prevent input submission
13835 this.el.dom.removeAttribute('name');
13836 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13841 // this.el.dom.setAttribute('autocomplete', 'off');
13844 var cls = 'x-combo-list';
13846 //this.list = new Roo.Layer({
13847 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13853 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13854 _this.list.setWidth(lw);
13857 this.list.on('mouseover', this.onViewOver, this);
13858 this.list.on('mousemove', this.onViewMove, this);
13859 this.list.on('scroll', this.onViewScroll, this);
13862 this.list.swallowEvent('mousewheel');
13863 this.assetHeight = 0;
13866 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13867 this.assetHeight += this.header.getHeight();
13870 this.innerList = this.list.createChild({cls:cls+'-inner'});
13871 this.innerList.on('mouseover', this.onViewOver, this);
13872 this.innerList.on('mousemove', this.onViewMove, this);
13873 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13875 if(this.allowBlank && !this.pageSize && !this.disableClear){
13876 this.footer = this.list.createChild({cls:cls+'-ft'});
13877 this.pageTb = new Roo.Toolbar(this.footer);
13881 this.footer = this.list.createChild({cls:cls+'-ft'});
13882 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13883 {pageSize: this.pageSize});
13887 if (this.pageTb && this.allowBlank && !this.disableClear) {
13889 this.pageTb.add(new Roo.Toolbar.Fill(), {
13890 cls: 'x-btn-icon x-btn-clear',
13892 handler: function()
13895 _this.clearValue();
13896 _this.onSelect(false, -1);
13901 this.assetHeight += this.footer.getHeight();
13906 this.tpl = Roo.bootstrap.version == 4 ?
13907 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
13908 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13911 this.view = new Roo.View(this.list, this.tpl, {
13912 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13914 //this.view.wrapEl.setDisplayed(false);
13915 this.view.on('click', this.onViewClick, this);
13918 this.store.on('beforeload', this.onBeforeLoad, this);
13919 this.store.on('load', this.onLoad, this);
13920 this.store.on('loadexception', this.onLoadException, this);
13922 if(this.resizable){
13923 this.resizer = new Roo.Resizable(this.list, {
13924 pinned:true, handles:'se'
13926 this.resizer.on('resize', function(r, w, h){
13927 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13928 this.listWidth = w;
13929 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13930 this.restrictHeight();
13932 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13935 if(!this.editable){
13936 this.editable = true;
13937 this.setEditable(false);
13942 if (typeof(this.events.add.listeners) != 'undefined') {
13944 this.addicon = this.wrap.createChild(
13945 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13947 this.addicon.on('click', function(e) {
13948 this.fireEvent('add', this);
13951 if (typeof(this.events.edit.listeners) != 'undefined') {
13953 this.editicon = this.wrap.createChild(
13954 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13955 if (this.addicon) {
13956 this.editicon.setStyle('margin-left', '40px');
13958 this.editicon.on('click', function(e) {
13960 // we fire even if inothing is selected..
13961 this.fireEvent('edit', this, this.lastData );
13967 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13968 "up" : function(e){
13969 this.inKeyMode = true;
13973 "down" : function(e){
13974 if(!this.isExpanded()){
13975 this.onTriggerClick();
13977 this.inKeyMode = true;
13982 "enter" : function(e){
13983 // this.onViewClick();
13987 if(this.fireEvent("specialkey", this, e)){
13988 this.onViewClick(false);
13994 "esc" : function(e){
13998 "tab" : function(e){
14001 if(this.fireEvent("specialkey", this, e)){
14002 this.onViewClick(false);
14010 doRelay : function(foo, bar, hname){
14011 if(hname == 'down' || this.scope.isExpanded()){
14012 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14021 this.queryDelay = Math.max(this.queryDelay || 10,
14022 this.mode == 'local' ? 10 : 250);
14025 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14027 if(this.typeAhead){
14028 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14030 if(this.editable !== false){
14031 this.inputEl().on("keyup", this.onKeyUp, this);
14033 if(this.forceSelection){
14034 this.inputEl().on('blur', this.doForce, this);
14038 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14039 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14043 initTickableEvents: function()
14047 if(this.hiddenName){
14049 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14051 this.hiddenField.dom.value =
14052 this.hiddenValue !== undefined ? this.hiddenValue :
14053 this.value !== undefined ? this.value : '';
14055 // prevent input submission
14056 this.el.dom.removeAttribute('name');
14057 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14062 // this.list = this.el.select('ul.dropdown-menu',true).first();
14064 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14065 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14066 if(this.triggerList){
14067 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14070 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14071 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14073 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14074 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14076 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14077 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14079 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14080 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14081 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14084 this.cancelBtn.hide();
14089 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14090 _this.list.setWidth(lw);
14093 this.list.on('mouseover', this.onViewOver, this);
14094 this.list.on('mousemove', this.onViewMove, this);
14096 this.list.on('scroll', this.onViewScroll, this);
14099 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
14100 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14103 this.view = new Roo.View(this.list, this.tpl, {
14108 selectedClass: this.selectedClass
14111 //this.view.wrapEl.setDisplayed(false);
14112 this.view.on('click', this.onViewClick, this);
14116 this.store.on('beforeload', this.onBeforeLoad, this);
14117 this.store.on('load', this.onLoad, this);
14118 this.store.on('loadexception', this.onLoadException, this);
14121 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14122 "up" : function(e){
14123 this.inKeyMode = true;
14127 "down" : function(e){
14128 this.inKeyMode = true;
14132 "enter" : function(e){
14133 if(this.fireEvent("specialkey", this, e)){
14134 this.onViewClick(false);
14140 "esc" : function(e){
14141 this.onTickableFooterButtonClick(e, false, false);
14144 "tab" : function(e){
14145 this.fireEvent("specialkey", this, e);
14147 this.onTickableFooterButtonClick(e, false, false);
14154 doRelay : function(e, fn, key){
14155 if(this.scope.isExpanded()){
14156 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14165 this.queryDelay = Math.max(this.queryDelay || 10,
14166 this.mode == 'local' ? 10 : 250);
14169 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14171 if(this.typeAhead){
14172 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14175 if(this.editable !== false){
14176 this.tickableInputEl().on("keyup", this.onKeyUp, this);
14179 this.indicator = this.indicatorEl();
14181 if(this.indicator){
14182 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14183 this.indicator.hide();
14188 onDestroy : function(){
14190 this.view.setStore(null);
14191 this.view.el.removeAllListeners();
14192 this.view.el.remove();
14193 this.view.purgeListeners();
14196 this.list.dom.innerHTML = '';
14200 this.store.un('beforeload', this.onBeforeLoad, this);
14201 this.store.un('load', this.onLoad, this);
14202 this.store.un('loadexception', this.onLoadException, this);
14204 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14208 fireKey : function(e){
14209 if(e.isNavKeyPress() && !this.list.isVisible()){
14210 this.fireEvent("specialkey", this, e);
14215 onResize: function(w, h){
14216 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14218 // if(typeof w != 'number'){
14219 // // we do not handle it!?!?
14222 // var tw = this.trigger.getWidth();
14223 // // tw += this.addicon ? this.addicon.getWidth() : 0;
14224 // // tw += this.editicon ? this.editicon.getWidth() : 0;
14226 // this.inputEl().setWidth( this.adjustWidth('input', x));
14228 // //this.trigger.setStyle('left', x+'px');
14230 // if(this.list && this.listWidth === undefined){
14231 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14232 // this.list.setWidth(lw);
14233 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14241 * Allow or prevent the user from directly editing the field text. If false is passed,
14242 * the user will only be able to select from the items defined in the dropdown list. This method
14243 * is the runtime equivalent of setting the 'editable' config option at config time.
14244 * @param {Boolean} value True to allow the user to directly edit the field text
14246 setEditable : function(value){
14247 if(value == this.editable){
14250 this.editable = value;
14252 this.inputEl().dom.setAttribute('readOnly', true);
14253 this.inputEl().on('mousedown', this.onTriggerClick, this);
14254 this.inputEl().addClass('x-combo-noedit');
14256 this.inputEl().dom.setAttribute('readOnly', false);
14257 this.inputEl().un('mousedown', this.onTriggerClick, this);
14258 this.inputEl().removeClass('x-combo-noedit');
14264 onBeforeLoad : function(combo,opts){
14265 if(!this.hasFocus){
14269 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14271 this.restrictHeight();
14272 this.selectedIndex = -1;
14276 onLoad : function(){
14278 this.hasQuery = false;
14280 if(!this.hasFocus){
14284 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14285 this.loading.hide();
14288 if(this.store.getCount() > 0){
14291 this.restrictHeight();
14292 if(this.lastQuery == this.allQuery){
14293 if(this.editable && !this.tickable){
14294 this.inputEl().dom.select();
14298 !this.selectByValue(this.value, true) &&
14301 !this.store.lastOptions ||
14302 typeof(this.store.lastOptions.add) == 'undefined' ||
14303 this.store.lastOptions.add != true
14306 this.select(0, true);
14309 if(this.autoFocus){
14312 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14313 this.taTask.delay(this.typeAheadDelay);
14317 this.onEmptyResults();
14323 onLoadException : function()
14325 this.hasQuery = false;
14327 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14328 this.loading.hide();
14331 if(this.tickable && this.editable){
14336 // only causes errors at present
14337 //Roo.log(this.store.reader.jsonData);
14338 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14340 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14346 onTypeAhead : function(){
14347 if(this.store.getCount() > 0){
14348 var r = this.store.getAt(0);
14349 var newValue = r.data[this.displayField];
14350 var len = newValue.length;
14351 var selStart = this.getRawValue().length;
14353 if(selStart != len){
14354 this.setRawValue(newValue);
14355 this.selectText(selStart, newValue.length);
14361 onSelect : function(record, index){
14363 if(this.fireEvent('beforeselect', this, record, index) !== false){
14365 this.setFromData(index > -1 ? record.data : false);
14368 this.fireEvent('select', this, record, index);
14373 * Returns the currently selected field value or empty string if no value is set.
14374 * @return {String} value The selected value
14376 getValue : function()
14378 if(Roo.isIOS && this.useNativeIOS){
14379 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14383 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14386 if(this.valueField){
14387 return typeof this.value != 'undefined' ? this.value : '';
14389 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14393 getRawValue : function()
14395 if(Roo.isIOS && this.useNativeIOS){
14396 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14399 var v = this.inputEl().getValue();
14405 * Clears any text/value currently set in the field
14407 clearValue : function(){
14409 if(this.hiddenField){
14410 this.hiddenField.dom.value = '';
14413 this.setRawValue('');
14414 this.lastSelectionText = '';
14415 this.lastData = false;
14417 var close = this.closeTriggerEl();
14428 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14429 * will be displayed in the field. If the value does not match the data value of an existing item,
14430 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14431 * Otherwise the field will be blank (although the value will still be set).
14432 * @param {String} value The value to match
14434 setValue : function(v)
14436 if(Roo.isIOS && this.useNativeIOS){
14437 this.setIOSValue(v);
14447 if(this.valueField){
14448 var r = this.findRecord(this.valueField, v);
14450 text = r.data[this.displayField];
14451 }else if(this.valueNotFoundText !== undefined){
14452 text = this.valueNotFoundText;
14455 this.lastSelectionText = text;
14456 if(this.hiddenField){
14457 this.hiddenField.dom.value = v;
14459 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14462 var close = this.closeTriggerEl();
14465 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14471 * @property {Object} the last set data for the element
14476 * Sets the value of the field based on a object which is related to the record format for the store.
14477 * @param {Object} value the value to set as. or false on reset?
14479 setFromData : function(o){
14486 var dv = ''; // display value
14487 var vv = ''; // value value..
14489 if (this.displayField) {
14490 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14492 // this is an error condition!!!
14493 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14496 if(this.valueField){
14497 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14500 var close = this.closeTriggerEl();
14503 if(dv.length || vv * 1 > 0){
14505 this.blockFocus=true;
14511 if(this.hiddenField){
14512 this.hiddenField.dom.value = vv;
14514 this.lastSelectionText = dv;
14515 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14519 // no hidden field.. - we store the value in 'value', but still display
14520 // display field!!!!
14521 this.lastSelectionText = dv;
14522 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14529 reset : function(){
14530 // overridden so that last data is reset..
14537 this.setValue(this.originalValue);
14538 //this.clearInvalid();
14539 this.lastData = false;
14541 this.view.clearSelections();
14547 findRecord : function(prop, value){
14549 if(this.store.getCount() > 0){
14550 this.store.each(function(r){
14551 if(r.data[prop] == value){
14561 getName: function()
14563 // returns hidden if it's set..
14564 if (!this.rendered) {return ''};
14565 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14569 onViewMove : function(e, t){
14570 this.inKeyMode = false;
14574 onViewOver : function(e, t){
14575 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14578 var item = this.view.findItemFromChild(t);
14581 var index = this.view.indexOf(item);
14582 this.select(index, false);
14587 onViewClick : function(view, doFocus, el, e)
14589 var index = this.view.getSelectedIndexes()[0];
14591 var r = this.store.getAt(index);
14595 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14602 Roo.each(this.tickItems, function(v,k){
14604 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14606 _this.tickItems.splice(k, 1);
14608 if(typeof(e) == 'undefined' && view == false){
14609 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14621 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14622 this.tickItems.push(r.data);
14625 if(typeof(e) == 'undefined' && view == false){
14626 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14633 this.onSelect(r, index);
14635 if(doFocus !== false && !this.blockFocus){
14636 this.inputEl().focus();
14641 restrictHeight : function(){
14642 //this.innerList.dom.style.height = '';
14643 //var inner = this.innerList.dom;
14644 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14645 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14646 //this.list.beginUpdate();
14647 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14648 this.list.alignTo(this.inputEl(), this.listAlign);
14649 this.list.alignTo(this.inputEl(), this.listAlign);
14650 //this.list.endUpdate();
14654 onEmptyResults : function(){
14656 if(this.tickable && this.editable){
14657 this.hasFocus = false;
14658 this.restrictHeight();
14666 * Returns true if the dropdown list is expanded, else false.
14668 isExpanded : function(){
14669 return this.list.isVisible();
14673 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14674 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14675 * @param {String} value The data value of the item to select
14676 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14677 * selected item if it is not currently in view (defaults to true)
14678 * @return {Boolean} True if the value matched an item in the list, else false
14680 selectByValue : function(v, scrollIntoView){
14681 if(v !== undefined && v !== null){
14682 var r = this.findRecord(this.valueField || this.displayField, v);
14684 this.select(this.store.indexOf(r), scrollIntoView);
14692 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14693 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14694 * @param {Number} index The zero-based index of the list item to select
14695 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14696 * selected item if it is not currently in view (defaults to true)
14698 select : function(index, scrollIntoView){
14699 this.selectedIndex = index;
14700 this.view.select(index);
14701 if(scrollIntoView !== false){
14702 var el = this.view.getNode(index);
14704 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14707 this.list.scrollChildIntoView(el, false);
14713 selectNext : function(){
14714 var ct = this.store.getCount();
14716 if(this.selectedIndex == -1){
14718 }else if(this.selectedIndex < ct-1){
14719 this.select(this.selectedIndex+1);
14725 selectPrev : function(){
14726 var ct = this.store.getCount();
14728 if(this.selectedIndex == -1){
14730 }else if(this.selectedIndex != 0){
14731 this.select(this.selectedIndex-1);
14737 onKeyUp : function(e){
14738 if(this.editable !== false && !e.isSpecialKey()){
14739 this.lastKey = e.getKey();
14740 this.dqTask.delay(this.queryDelay);
14745 validateBlur : function(){
14746 return !this.list || !this.list.isVisible();
14750 initQuery : function(){
14752 var v = this.getRawValue();
14754 if(this.tickable && this.editable){
14755 v = this.tickableInputEl().getValue();
14762 doForce : function(){
14763 if(this.inputEl().dom.value.length > 0){
14764 this.inputEl().dom.value =
14765 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14771 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14772 * query allowing the query action to be canceled if needed.
14773 * @param {String} query The SQL query to execute
14774 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14775 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14776 * saved in the current store (defaults to false)
14778 doQuery : function(q, forceAll){
14780 if(q === undefined || q === null){
14785 forceAll: forceAll,
14789 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14794 forceAll = qe.forceAll;
14795 if(forceAll === true || (q.length >= this.minChars)){
14797 this.hasQuery = true;
14799 if(this.lastQuery != q || this.alwaysQuery){
14800 this.lastQuery = q;
14801 if(this.mode == 'local'){
14802 this.selectedIndex = -1;
14804 this.store.clearFilter();
14807 if(this.specialFilter){
14808 this.fireEvent('specialfilter', this);
14813 this.store.filter(this.displayField, q);
14816 this.store.fireEvent("datachanged", this.store);
14823 this.store.baseParams[this.queryParam] = q;
14825 var options = {params : this.getParams(q)};
14828 options.add = true;
14829 options.params.start = this.page * this.pageSize;
14832 this.store.load(options);
14835 * this code will make the page width larger, at the beginning, the list not align correctly,
14836 * we should expand the list on onLoad
14837 * so command out it
14842 this.selectedIndex = -1;
14847 this.loadNext = false;
14851 getParams : function(q){
14853 //p[this.queryParam] = q;
14857 p.limit = this.pageSize;
14863 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14865 collapse : function(){
14866 if(!this.isExpanded()){
14872 this.hasFocus = false;
14876 this.cancelBtn.hide();
14877 this.trigger.show();
14880 this.tickableInputEl().dom.value = '';
14881 this.tickableInputEl().blur();
14886 Roo.get(document).un('mousedown', this.collapseIf, this);
14887 Roo.get(document).un('mousewheel', this.collapseIf, this);
14888 if (!this.editable) {
14889 Roo.get(document).un('keydown', this.listKeyPress, this);
14891 this.fireEvent('collapse', this);
14897 collapseIf : function(e){
14898 var in_combo = e.within(this.el);
14899 var in_list = e.within(this.list);
14900 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14902 if (in_combo || in_list || is_list) {
14903 //e.stopPropagation();
14908 this.onTickableFooterButtonClick(e, false, false);
14916 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14918 expand : function(){
14920 if(this.isExpanded() || !this.hasFocus){
14924 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14925 this.list.setWidth(lw);
14931 this.restrictHeight();
14935 this.tickItems = Roo.apply([], this.item);
14938 this.cancelBtn.show();
14939 this.trigger.hide();
14942 this.tickableInputEl().focus();
14947 Roo.get(document).on('mousedown', this.collapseIf, this);
14948 Roo.get(document).on('mousewheel', this.collapseIf, this);
14949 if (!this.editable) {
14950 Roo.get(document).on('keydown', this.listKeyPress, this);
14953 this.fireEvent('expand', this);
14957 // Implements the default empty TriggerField.onTriggerClick function
14958 onTriggerClick : function(e)
14960 Roo.log('trigger click');
14962 if(this.disabled || !this.triggerList){
14967 this.loadNext = false;
14969 if(this.isExpanded()){
14971 if (!this.blockFocus) {
14972 this.inputEl().focus();
14976 this.hasFocus = true;
14977 if(this.triggerAction == 'all') {
14978 this.doQuery(this.allQuery, true);
14980 this.doQuery(this.getRawValue());
14982 if (!this.blockFocus) {
14983 this.inputEl().focus();
14988 onTickableTriggerClick : function(e)
14995 this.loadNext = false;
14996 this.hasFocus = true;
14998 if(this.triggerAction == 'all') {
14999 this.doQuery(this.allQuery, true);
15001 this.doQuery(this.getRawValue());
15005 onSearchFieldClick : function(e)
15007 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
15008 this.onTickableFooterButtonClick(e, false, false);
15012 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
15017 this.loadNext = false;
15018 this.hasFocus = true;
15020 if(this.triggerAction == 'all') {
15021 this.doQuery(this.allQuery, true);
15023 this.doQuery(this.getRawValue());
15027 listKeyPress : function(e)
15029 //Roo.log('listkeypress');
15030 // scroll to first matching element based on key pres..
15031 if (e.isSpecialKey()) {
15034 var k = String.fromCharCode(e.getKey()).toUpperCase();
15037 var csel = this.view.getSelectedNodes();
15038 var cselitem = false;
15040 var ix = this.view.indexOf(csel[0]);
15041 cselitem = this.store.getAt(ix);
15042 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15048 this.store.each(function(v) {
15050 // start at existing selection.
15051 if (cselitem.id == v.id) {
15057 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15058 match = this.store.indexOf(v);
15064 if (match === false) {
15065 return true; // no more action?
15068 this.view.select(match);
15069 var sn = Roo.get(this.view.getSelectedNodes()[0]);
15070 sn.scrollIntoView(sn.dom.parentNode, false);
15073 onViewScroll : function(e, t){
15075 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){
15079 this.hasQuery = true;
15081 this.loading = this.list.select('.loading', true).first();
15083 if(this.loading === null){
15084 this.list.createChild({
15086 cls: 'loading roo-select2-more-results roo-select2-active',
15087 html: 'Loading more results...'
15090 this.loading = this.list.select('.loading', true).first();
15092 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15094 this.loading.hide();
15097 this.loading.show();
15102 this.loadNext = true;
15104 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15109 addItem : function(o)
15111 var dv = ''; // display value
15113 if (this.displayField) {
15114 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15116 // this is an error condition!!!
15117 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
15124 var choice = this.choices.createChild({
15126 cls: 'roo-select2-search-choice',
15135 cls: 'roo-select2-search-choice-close fa fa-times',
15140 }, this.searchField);
15142 var close = choice.select('a.roo-select2-search-choice-close', true).first();
15144 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15152 this.inputEl().dom.value = '';
15157 onRemoveItem : function(e, _self, o)
15159 e.preventDefault();
15161 this.lastItem = Roo.apply([], this.item);
15163 var index = this.item.indexOf(o.data) * 1;
15166 Roo.log('not this item?!');
15170 this.item.splice(index, 1);
15175 this.fireEvent('remove', this, e);
15181 syncValue : function()
15183 if(!this.item.length){
15190 Roo.each(this.item, function(i){
15191 if(_this.valueField){
15192 value.push(i[_this.valueField]);
15199 this.value = value.join(',');
15201 if(this.hiddenField){
15202 this.hiddenField.dom.value = this.value;
15205 this.store.fireEvent("datachanged", this.store);
15210 clearItem : function()
15212 if(!this.multiple){
15218 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15226 if(this.tickable && !Roo.isTouch){
15227 this.view.refresh();
15231 inputEl: function ()
15233 if(Roo.isIOS && this.useNativeIOS){
15234 return this.el.select('select.roo-ios-select', true).first();
15237 if(Roo.isTouch && this.mobileTouchView){
15238 return this.el.select('input.form-control',true).first();
15242 return this.searchField;
15245 return this.el.select('input.form-control',true).first();
15248 onTickableFooterButtonClick : function(e, btn, el)
15250 e.preventDefault();
15252 this.lastItem = Roo.apply([], this.item);
15254 if(btn && btn.name == 'cancel'){
15255 this.tickItems = Roo.apply([], this.item);
15264 Roo.each(this.tickItems, function(o){
15272 validate : function()
15274 if(this.getVisibilityEl().hasClass('hidden')){
15278 var v = this.getRawValue();
15281 v = this.getValue();
15284 if(this.disabled || this.allowBlank || v.length){
15289 this.markInvalid();
15293 tickableInputEl : function()
15295 if(!this.tickable || !this.editable){
15296 return this.inputEl();
15299 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15303 getAutoCreateTouchView : function()
15308 cls: 'form-group' //input-group
15314 type : this.inputType,
15315 cls : 'form-control x-combo-noedit',
15316 autocomplete: 'new-password',
15317 placeholder : this.placeholder || '',
15322 input.name = this.name;
15326 input.cls += ' input-' + this.size;
15329 if (this.disabled) {
15330 input.disabled = true;
15341 inputblock.cls += ' input-group';
15343 inputblock.cn.unshift({
15345 cls : 'input-group-addon input-group-prepend input-group-text',
15350 if(this.removable && !this.multiple){
15351 inputblock.cls += ' roo-removable';
15353 inputblock.cn.push({
15356 cls : 'roo-combo-removable-btn close'
15360 if(this.hasFeedback && !this.allowBlank){
15362 inputblock.cls += ' has-feedback';
15364 inputblock.cn.push({
15366 cls: 'glyphicon form-control-feedback'
15373 inputblock.cls += (this.before) ? '' : ' input-group';
15375 inputblock.cn.push({
15377 cls : 'input-group-addon input-group-append input-group-text',
15383 var ibwrap = inputblock;
15388 cls: 'roo-select2-choices',
15392 cls: 'roo-select2-search-field',
15405 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15410 cls: 'form-hidden-field'
15416 if(!this.multiple && this.showToggleBtn){
15422 if (this.caret != false) {
15425 cls: 'fa fa-' + this.caret
15432 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15434 Roo.bootstrap.version == 3 ? caret : '',
15437 cls: 'combobox-clear',
15451 combobox.cls += ' roo-select2-container-multi';
15454 var align = this.labelAlign || this.parentLabelAlign();
15456 if (align ==='left' && this.fieldLabel.length) {
15461 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15462 tooltip : 'This field is required'
15466 cls : 'control-label col-form-label',
15467 html : this.fieldLabel
15478 var labelCfg = cfg.cn[1];
15479 var contentCfg = cfg.cn[2];
15482 if(this.indicatorpos == 'right'){
15487 cls : 'control-label col-form-label',
15491 html : this.fieldLabel
15495 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15496 tooltip : 'This field is required'
15509 labelCfg = cfg.cn[0];
15510 contentCfg = cfg.cn[1];
15515 if(this.labelWidth > 12){
15516 labelCfg.style = "width: " + this.labelWidth + 'px';
15519 if(this.labelWidth < 13 && this.labelmd == 0){
15520 this.labelmd = this.labelWidth;
15523 if(this.labellg > 0){
15524 labelCfg.cls += ' col-lg-' + this.labellg;
15525 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15528 if(this.labelmd > 0){
15529 labelCfg.cls += ' col-md-' + this.labelmd;
15530 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15533 if(this.labelsm > 0){
15534 labelCfg.cls += ' col-sm-' + this.labelsm;
15535 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15538 if(this.labelxs > 0){
15539 labelCfg.cls += ' col-xs-' + this.labelxs;
15540 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15544 } else if ( this.fieldLabel.length) {
15548 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15549 tooltip : 'This field is required'
15553 cls : 'control-label',
15554 html : this.fieldLabel
15565 if(this.indicatorpos == 'right'){
15569 cls : 'control-label',
15570 html : this.fieldLabel,
15574 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15575 tooltip : 'This field is required'
15592 var settings = this;
15594 ['xs','sm','md','lg'].map(function(size){
15595 if (settings[size]) {
15596 cfg.cls += ' col-' + size + '-' + settings[size];
15603 initTouchView : function()
15605 this.renderTouchView();
15607 this.touchViewEl.on('scroll', function(){
15608 this.el.dom.scrollTop = 0;
15611 this.originalValue = this.getValue();
15613 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15615 this.inputEl().on("click", this.showTouchView, this);
15616 if (this.triggerEl) {
15617 this.triggerEl.on("click", this.showTouchView, this);
15621 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15622 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15624 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15626 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15627 this.store.on('load', this.onTouchViewLoad, this);
15628 this.store.on('loadexception', this.onTouchViewLoadException, this);
15630 if(this.hiddenName){
15632 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15634 this.hiddenField.dom.value =
15635 this.hiddenValue !== undefined ? this.hiddenValue :
15636 this.value !== undefined ? this.value : '';
15638 this.el.dom.removeAttribute('name');
15639 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15643 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15644 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15647 if(this.removable && !this.multiple){
15648 var close = this.closeTriggerEl();
15650 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15651 close.on('click', this.removeBtnClick, this, close);
15655 * fix the bug in Safari iOS8
15657 this.inputEl().on("focus", function(e){
15658 document.activeElement.blur();
15661 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15668 renderTouchView : function()
15670 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15671 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15673 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15674 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15676 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15677 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15678 this.touchViewBodyEl.setStyle('overflow', 'auto');
15680 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15681 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15683 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15684 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15688 showTouchView : function()
15694 this.touchViewHeaderEl.hide();
15696 if(this.modalTitle.length){
15697 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15698 this.touchViewHeaderEl.show();
15701 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15702 this.touchViewEl.show();
15704 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15706 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15707 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15709 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15711 if(this.modalTitle.length){
15712 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15715 this.touchViewBodyEl.setHeight(bodyHeight);
15719 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15721 this.touchViewEl.addClass('in');
15724 if(this._touchViewMask){
15725 Roo.get(document.body).addClass("x-body-masked");
15726 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15727 this._touchViewMask.setStyle('z-index', 10000);
15728 this._touchViewMask.addClass('show');
15731 this.doTouchViewQuery();
15735 hideTouchView : function()
15737 this.touchViewEl.removeClass('in');
15741 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15743 this.touchViewEl.setStyle('display', 'none');
15746 if(this._touchViewMask){
15747 this._touchViewMask.removeClass('show');
15748 Roo.get(document.body).removeClass("x-body-masked");
15752 setTouchViewValue : function()
15759 Roo.each(this.tickItems, function(o){
15764 this.hideTouchView();
15767 doTouchViewQuery : function()
15776 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15780 if(!this.alwaysQuery || this.mode == 'local'){
15781 this.onTouchViewLoad();
15788 onTouchViewBeforeLoad : function(combo,opts)
15794 onTouchViewLoad : function()
15796 if(this.store.getCount() < 1){
15797 this.onTouchViewEmptyResults();
15801 this.clearTouchView();
15803 var rawValue = this.getRawValue();
15805 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15807 this.tickItems = [];
15809 this.store.data.each(function(d, rowIndex){
15810 var row = this.touchViewListGroup.createChild(template);
15812 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15813 row.addClass(d.data.cls);
15816 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15819 html : d.data[this.displayField]
15822 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15823 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15826 row.removeClass('selected');
15827 if(!this.multiple && this.valueField &&
15828 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15831 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15832 row.addClass('selected');
15835 if(this.multiple && this.valueField &&
15836 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15840 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15841 this.tickItems.push(d.data);
15844 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15848 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15850 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15852 if(this.modalTitle.length){
15853 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15856 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15858 if(this.mobile_restrict_height && listHeight < bodyHeight){
15859 this.touchViewBodyEl.setHeight(listHeight);
15864 if(firstChecked && listHeight > bodyHeight){
15865 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15870 onTouchViewLoadException : function()
15872 this.hideTouchView();
15875 onTouchViewEmptyResults : function()
15877 this.clearTouchView();
15879 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15881 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15885 clearTouchView : function()
15887 this.touchViewListGroup.dom.innerHTML = '';
15890 onTouchViewClick : function(e, el, o)
15892 e.preventDefault();
15895 var rowIndex = o.rowIndex;
15897 var r = this.store.getAt(rowIndex);
15899 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15901 if(!this.multiple){
15902 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15903 c.dom.removeAttribute('checked');
15906 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15908 this.setFromData(r.data);
15910 var close = this.closeTriggerEl();
15916 this.hideTouchView();
15918 this.fireEvent('select', this, r, rowIndex);
15923 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15924 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15925 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15929 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15930 this.addItem(r.data);
15931 this.tickItems.push(r.data);
15935 getAutoCreateNativeIOS : function()
15938 cls: 'form-group' //input-group,
15943 cls : 'roo-ios-select'
15947 combobox.name = this.name;
15950 if (this.disabled) {
15951 combobox.disabled = true;
15954 var settings = this;
15956 ['xs','sm','md','lg'].map(function(size){
15957 if (settings[size]) {
15958 cfg.cls += ' col-' + size + '-' + settings[size];
15968 initIOSView : function()
15970 this.store.on('load', this.onIOSViewLoad, this);
15975 onIOSViewLoad : function()
15977 if(this.store.getCount() < 1){
15981 this.clearIOSView();
15983 if(this.allowBlank) {
15985 var default_text = '-- SELECT --';
15987 if(this.placeholder.length){
15988 default_text = this.placeholder;
15991 if(this.emptyTitle.length){
15992 default_text += ' - ' + this.emptyTitle + ' -';
15995 var opt = this.inputEl().createChild({
15998 html : default_text
16002 o[this.valueField] = 0;
16003 o[this.displayField] = default_text;
16005 this.ios_options.push({
16012 this.store.data.each(function(d, rowIndex){
16016 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16017 html = d.data[this.displayField];
16022 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16023 value = d.data[this.valueField];
16032 if(this.value == d.data[this.valueField]){
16033 option['selected'] = true;
16036 var opt = this.inputEl().createChild(option);
16038 this.ios_options.push({
16045 this.inputEl().on('change', function(){
16046 this.fireEvent('select', this);
16051 clearIOSView: function()
16053 this.inputEl().dom.innerHTML = '';
16055 this.ios_options = [];
16058 setIOSValue: function(v)
16062 if(!this.ios_options){
16066 Roo.each(this.ios_options, function(opts){
16068 opts.el.dom.removeAttribute('selected');
16070 if(opts.data[this.valueField] != v){
16074 opts.el.dom.setAttribute('selected', true);
16080 * @cfg {Boolean} grow
16084 * @cfg {Number} growMin
16088 * @cfg {Number} growMax
16097 Roo.apply(Roo.bootstrap.ComboBox, {
16101 cls: 'modal-header',
16123 cls: 'list-group-item',
16127 cls: 'roo-combobox-list-group-item-value'
16131 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16145 listItemCheckbox : {
16147 cls: 'list-group-item',
16151 cls: 'roo-combobox-list-group-item-value'
16155 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16171 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16176 cls: 'modal-footer',
16184 cls: 'col-xs-6 text-left',
16187 cls: 'btn btn-danger roo-touch-view-cancel',
16193 cls: 'col-xs-6 text-right',
16196 cls: 'btn btn-success roo-touch-view-ok',
16207 Roo.apply(Roo.bootstrap.ComboBox, {
16209 touchViewTemplate : {
16211 cls: 'modal fade roo-combobox-touch-view',
16215 cls: 'modal-dialog',
16216 style : 'position:fixed', // we have to fix position....
16220 cls: 'modal-content',
16222 Roo.bootstrap.ComboBox.header,
16223 Roo.bootstrap.ComboBox.body,
16224 Roo.bootstrap.ComboBox.footer
16233 * Ext JS Library 1.1.1
16234 * Copyright(c) 2006-2007, Ext JS, LLC.
16236 * Originally Released Under LGPL - original licence link has changed is not relivant.
16239 * <script type="text/javascript">
16244 * @extends Roo.util.Observable
16245 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
16246 * This class also supports single and multi selection modes. <br>
16247 * Create a data model bound view:
16249 var store = new Roo.data.Store(...);
16251 var view = new Roo.View({
16253 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
16255 singleSelect: true,
16256 selectedClass: "ydataview-selected",
16260 // listen for node click?
16261 view.on("click", function(vw, index, node, e){
16262 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16266 dataModel.load("foobar.xml");
16268 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16270 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16271 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16273 * Note: old style constructor is still suported (container, template, config)
16276 * Create a new View
16277 * @param {Object} config The config object
16280 Roo.View = function(config, depreciated_tpl, depreciated_config){
16282 this.parent = false;
16284 if (typeof(depreciated_tpl) == 'undefined') {
16285 // new way.. - universal constructor.
16286 Roo.apply(this, config);
16287 this.el = Roo.get(this.el);
16290 this.el = Roo.get(config);
16291 this.tpl = depreciated_tpl;
16292 Roo.apply(this, depreciated_config);
16294 this.wrapEl = this.el.wrap().wrap();
16295 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16298 if(typeof(this.tpl) == "string"){
16299 this.tpl = new Roo.Template(this.tpl);
16301 // support xtype ctors..
16302 this.tpl = new Roo.factory(this.tpl, Roo);
16306 this.tpl.compile();
16311 * @event beforeclick
16312 * Fires before a click is processed. Returns false to cancel the default action.
16313 * @param {Roo.View} this
16314 * @param {Number} index The index of the target node
16315 * @param {HTMLElement} node The target node
16316 * @param {Roo.EventObject} e The raw event object
16318 "beforeclick" : true,
16321 * Fires when a template node is clicked.
16322 * @param {Roo.View} this
16323 * @param {Number} index The index of the target node
16324 * @param {HTMLElement} node The target node
16325 * @param {Roo.EventObject} e The raw event object
16330 * Fires when a template node is double clicked.
16331 * @param {Roo.View} this
16332 * @param {Number} index The index of the target node
16333 * @param {HTMLElement} node The target node
16334 * @param {Roo.EventObject} e The raw event object
16338 * @event contextmenu
16339 * Fires when a template node is right clicked.
16340 * @param {Roo.View} this
16341 * @param {Number} index The index of the target node
16342 * @param {HTMLElement} node The target node
16343 * @param {Roo.EventObject} e The raw event object
16345 "contextmenu" : true,
16347 * @event selectionchange
16348 * Fires when the selected nodes change.
16349 * @param {Roo.View} this
16350 * @param {Array} selections Array of the selected nodes
16352 "selectionchange" : true,
16355 * @event beforeselect
16356 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16357 * @param {Roo.View} this
16358 * @param {HTMLElement} node The node to be selected
16359 * @param {Array} selections Array of currently selected nodes
16361 "beforeselect" : true,
16363 * @event preparedata
16364 * Fires on every row to render, to allow you to change the data.
16365 * @param {Roo.View} this
16366 * @param {Object} data to be rendered (change this)
16368 "preparedata" : true
16376 "click": this.onClick,
16377 "dblclick": this.onDblClick,
16378 "contextmenu": this.onContextMenu,
16382 this.selections = [];
16384 this.cmp = new Roo.CompositeElementLite([]);
16386 this.store = Roo.factory(this.store, Roo.data);
16387 this.setStore(this.store, true);
16390 if ( this.footer && this.footer.xtype) {
16392 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16394 this.footer.dataSource = this.store;
16395 this.footer.container = fctr;
16396 this.footer = Roo.factory(this.footer, Roo);
16397 fctr.insertFirst(this.el);
16399 // this is a bit insane - as the paging toolbar seems to detach the el..
16400 // dom.parentNode.parentNode.parentNode
16401 // they get detached?
16405 Roo.View.superclass.constructor.call(this);
16410 Roo.extend(Roo.View, Roo.util.Observable, {
16413 * @cfg {Roo.data.Store} store Data store to load data from.
16418 * @cfg {String|Roo.Element} el The container element.
16423 * @cfg {String|Roo.Template} tpl The template used by this View
16427 * @cfg {String} dataName the named area of the template to use as the data area
16428 * Works with domtemplates roo-name="name"
16432 * @cfg {String} selectedClass The css class to add to selected nodes
16434 selectedClass : "x-view-selected",
16436 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16441 * @cfg {String} text to display on mask (default Loading)
16445 * @cfg {Boolean} multiSelect Allow multiple selection
16447 multiSelect : false,
16449 * @cfg {Boolean} singleSelect Allow single selection
16451 singleSelect: false,
16454 * @cfg {Boolean} toggleSelect - selecting
16456 toggleSelect : false,
16459 * @cfg {Boolean} tickable - selecting
16464 * Returns the element this view is bound to.
16465 * @return {Roo.Element}
16467 getEl : function(){
16468 return this.wrapEl;
16474 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16476 refresh : function(){
16477 //Roo.log('refresh');
16480 // if we are using something like 'domtemplate', then
16481 // the what gets used is:
16482 // t.applySubtemplate(NAME, data, wrapping data..)
16483 // the outer template then get' applied with
16484 // the store 'extra data'
16485 // and the body get's added to the
16486 // roo-name="data" node?
16487 // <span class='roo-tpl-{name}'></span> ?????
16491 this.clearSelections();
16492 this.el.update("");
16494 var records = this.store.getRange();
16495 if(records.length < 1) {
16497 // is this valid?? = should it render a template??
16499 this.el.update(this.emptyText);
16503 if (this.dataName) {
16504 this.el.update(t.apply(this.store.meta)); //????
16505 el = this.el.child('.roo-tpl-' + this.dataName);
16508 for(var i = 0, len = records.length; i < len; i++){
16509 var data = this.prepareData(records[i].data, i, records[i]);
16510 this.fireEvent("preparedata", this, data, i, records[i]);
16512 var d = Roo.apply({}, data);
16515 Roo.apply(d, {'roo-id' : Roo.id()});
16519 Roo.each(this.parent.item, function(item){
16520 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16523 Roo.apply(d, {'roo-data-checked' : 'checked'});
16527 html[html.length] = Roo.util.Format.trim(
16529 t.applySubtemplate(this.dataName, d, this.store.meta) :
16536 el.update(html.join(""));
16537 this.nodes = el.dom.childNodes;
16538 this.updateIndexes(0);
16543 * Function to override to reformat the data that is sent to
16544 * the template for each node.
16545 * DEPRICATED - use the preparedata event handler.
16546 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16547 * a JSON object for an UpdateManager bound view).
16549 prepareData : function(data, index, record)
16551 this.fireEvent("preparedata", this, data, index, record);
16555 onUpdate : function(ds, record){
16556 // Roo.log('on update');
16557 this.clearSelections();
16558 var index = this.store.indexOf(record);
16559 var n = this.nodes[index];
16560 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16561 n.parentNode.removeChild(n);
16562 this.updateIndexes(index, index);
16568 onAdd : function(ds, records, index)
16570 //Roo.log(['on Add', ds, records, index] );
16571 this.clearSelections();
16572 if(this.nodes.length == 0){
16576 var n = this.nodes[index];
16577 for(var i = 0, len = records.length; i < len; i++){
16578 var d = this.prepareData(records[i].data, i, records[i]);
16580 this.tpl.insertBefore(n, d);
16583 this.tpl.append(this.el, d);
16586 this.updateIndexes(index);
16589 onRemove : function(ds, record, index){
16590 // Roo.log('onRemove');
16591 this.clearSelections();
16592 var el = this.dataName ?
16593 this.el.child('.roo-tpl-' + this.dataName) :
16596 el.dom.removeChild(this.nodes[index]);
16597 this.updateIndexes(index);
16601 * Refresh an individual node.
16602 * @param {Number} index
16604 refreshNode : function(index){
16605 this.onUpdate(this.store, this.store.getAt(index));
16608 updateIndexes : function(startIndex, endIndex){
16609 var ns = this.nodes;
16610 startIndex = startIndex || 0;
16611 endIndex = endIndex || ns.length - 1;
16612 for(var i = startIndex; i <= endIndex; i++){
16613 ns[i].nodeIndex = i;
16618 * Changes the data store this view uses and refresh the view.
16619 * @param {Store} store
16621 setStore : function(store, initial){
16622 if(!initial && this.store){
16623 this.store.un("datachanged", this.refresh);
16624 this.store.un("add", this.onAdd);
16625 this.store.un("remove", this.onRemove);
16626 this.store.un("update", this.onUpdate);
16627 this.store.un("clear", this.refresh);
16628 this.store.un("beforeload", this.onBeforeLoad);
16629 this.store.un("load", this.onLoad);
16630 this.store.un("loadexception", this.onLoad);
16634 store.on("datachanged", this.refresh, this);
16635 store.on("add", this.onAdd, this);
16636 store.on("remove", this.onRemove, this);
16637 store.on("update", this.onUpdate, this);
16638 store.on("clear", this.refresh, this);
16639 store.on("beforeload", this.onBeforeLoad, this);
16640 store.on("load", this.onLoad, this);
16641 store.on("loadexception", this.onLoad, this);
16649 * onbeforeLoad - masks the loading area.
16652 onBeforeLoad : function(store,opts)
16654 //Roo.log('onBeforeLoad');
16656 this.el.update("");
16658 this.el.mask(this.mask ? this.mask : "Loading" );
16660 onLoad : function ()
16667 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16668 * @param {HTMLElement} node
16669 * @return {HTMLElement} The template node
16671 findItemFromChild : function(node){
16672 var el = this.dataName ?
16673 this.el.child('.roo-tpl-' + this.dataName,true) :
16676 if(!node || node.parentNode == el){
16679 var p = node.parentNode;
16680 while(p && p != el){
16681 if(p.parentNode == el){
16690 onClick : function(e){
16691 var item = this.findItemFromChild(e.getTarget());
16693 var index = this.indexOf(item);
16694 if(this.onItemClick(item, index, e) !== false){
16695 this.fireEvent("click", this, index, item, e);
16698 this.clearSelections();
16703 onContextMenu : function(e){
16704 var item = this.findItemFromChild(e.getTarget());
16706 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16711 onDblClick : function(e){
16712 var item = this.findItemFromChild(e.getTarget());
16714 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16718 onItemClick : function(item, index, e)
16720 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16723 if (this.toggleSelect) {
16724 var m = this.isSelected(item) ? 'unselect' : 'select';
16727 _t[m](item, true, false);
16730 if(this.multiSelect || this.singleSelect){
16731 if(this.multiSelect && e.shiftKey && this.lastSelection){
16732 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16734 this.select(item, this.multiSelect && e.ctrlKey);
16735 this.lastSelection = item;
16738 if(!this.tickable){
16739 e.preventDefault();
16747 * Get the number of selected nodes.
16750 getSelectionCount : function(){
16751 return this.selections.length;
16755 * Get the currently selected nodes.
16756 * @return {Array} An array of HTMLElements
16758 getSelectedNodes : function(){
16759 return this.selections;
16763 * Get the indexes of the selected nodes.
16766 getSelectedIndexes : function(){
16767 var indexes = [], s = this.selections;
16768 for(var i = 0, len = s.length; i < len; i++){
16769 indexes.push(s[i].nodeIndex);
16775 * Clear all selections
16776 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16778 clearSelections : function(suppressEvent){
16779 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16780 this.cmp.elements = this.selections;
16781 this.cmp.removeClass(this.selectedClass);
16782 this.selections = [];
16783 if(!suppressEvent){
16784 this.fireEvent("selectionchange", this, this.selections);
16790 * Returns true if the passed node is selected
16791 * @param {HTMLElement/Number} node The node or node index
16792 * @return {Boolean}
16794 isSelected : function(node){
16795 var s = this.selections;
16799 node = this.getNode(node);
16800 return s.indexOf(node) !== -1;
16805 * @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
16806 * @param {Boolean} keepExisting (optional) true to keep existing selections
16807 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16809 select : function(nodeInfo, keepExisting, suppressEvent){
16810 if(nodeInfo instanceof Array){
16812 this.clearSelections(true);
16814 for(var i = 0, len = nodeInfo.length; i < len; i++){
16815 this.select(nodeInfo[i], true, true);
16819 var node = this.getNode(nodeInfo);
16820 if(!node || this.isSelected(node)){
16821 return; // already selected.
16824 this.clearSelections(true);
16827 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16828 Roo.fly(node).addClass(this.selectedClass);
16829 this.selections.push(node);
16830 if(!suppressEvent){
16831 this.fireEvent("selectionchange", this, this.selections);
16839 * @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
16840 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16841 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16843 unselect : function(nodeInfo, keepExisting, suppressEvent)
16845 if(nodeInfo instanceof Array){
16846 Roo.each(this.selections, function(s) {
16847 this.unselect(s, nodeInfo);
16851 var node = this.getNode(nodeInfo);
16852 if(!node || !this.isSelected(node)){
16853 //Roo.log("not selected");
16854 return; // not selected.
16858 Roo.each(this.selections, function(s) {
16860 Roo.fly(node).removeClass(this.selectedClass);
16867 this.selections= ns;
16868 this.fireEvent("selectionchange", this, this.selections);
16872 * Gets a template node.
16873 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16874 * @return {HTMLElement} The node or null if it wasn't found
16876 getNode : function(nodeInfo){
16877 if(typeof nodeInfo == "string"){
16878 return document.getElementById(nodeInfo);
16879 }else if(typeof nodeInfo == "number"){
16880 return this.nodes[nodeInfo];
16886 * Gets a range template nodes.
16887 * @param {Number} startIndex
16888 * @param {Number} endIndex
16889 * @return {Array} An array of nodes
16891 getNodes : function(start, end){
16892 var ns = this.nodes;
16893 start = start || 0;
16894 end = typeof end == "undefined" ? ns.length - 1 : end;
16897 for(var i = start; i <= end; i++){
16901 for(var i = start; i >= end; i--){
16909 * Finds the index of the passed node
16910 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16911 * @return {Number} The index of the node or -1
16913 indexOf : function(node){
16914 node = this.getNode(node);
16915 if(typeof node.nodeIndex == "number"){
16916 return node.nodeIndex;
16918 var ns = this.nodes;
16919 for(var i = 0, len = ns.length; i < len; i++){
16930 * based on jquery fullcalendar
16934 Roo.bootstrap = Roo.bootstrap || {};
16936 * @class Roo.bootstrap.Calendar
16937 * @extends Roo.bootstrap.Component
16938 * Bootstrap Calendar class
16939 * @cfg {Boolean} loadMask (true|false) default false
16940 * @cfg {Object} header generate the user specific header of the calendar, default false
16943 * Create a new Container
16944 * @param {Object} config The config object
16949 Roo.bootstrap.Calendar = function(config){
16950 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16954 * Fires when a date is selected
16955 * @param {DatePicker} this
16956 * @param {Date} date The selected date
16960 * @event monthchange
16961 * Fires when the displayed month changes
16962 * @param {DatePicker} this
16963 * @param {Date} date The selected month
16965 'monthchange': true,
16967 * @event evententer
16968 * Fires when mouse over an event
16969 * @param {Calendar} this
16970 * @param {event} Event
16972 'evententer': true,
16974 * @event eventleave
16975 * Fires when the mouse leaves an
16976 * @param {Calendar} this
16979 'eventleave': true,
16981 * @event eventclick
16982 * Fires when the mouse click an
16983 * @param {Calendar} this
16992 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16995 * @cfg {Number} startDay
16996 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
17004 getAutoCreate : function(){
17007 var fc_button = function(name, corner, style, content ) {
17008 return Roo.apply({},{
17010 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
17012 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
17015 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
17026 style : 'width:100%',
17033 cls : 'fc-header-left',
17035 fc_button('prev', 'left', 'arrow', '‹' ),
17036 fc_button('next', 'right', 'arrow', '›' ),
17037 { tag: 'span', cls: 'fc-header-space' },
17038 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
17046 cls : 'fc-header-center',
17050 cls: 'fc-header-title',
17053 html : 'month / year'
17061 cls : 'fc-header-right',
17063 /* fc_button('month', 'left', '', 'month' ),
17064 fc_button('week', '', '', 'week' ),
17065 fc_button('day', 'right', '', 'day' )
17077 header = this.header;
17080 var cal_heads = function() {
17082 // fixme - handle this.
17084 for (var i =0; i < Date.dayNames.length; i++) {
17085 var d = Date.dayNames[i];
17088 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17089 html : d.substring(0,3)
17093 ret[0].cls += ' fc-first';
17094 ret[6].cls += ' fc-last';
17097 var cal_cell = function(n) {
17100 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17105 cls: 'fc-day-number',
17109 cls: 'fc-day-content',
17113 style: 'position: relative;' // height: 17px;
17125 var cal_rows = function() {
17128 for (var r = 0; r < 6; r++) {
17135 for (var i =0; i < Date.dayNames.length; i++) {
17136 var d = Date.dayNames[i];
17137 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17140 row.cn[0].cls+=' fc-first';
17141 row.cn[0].cn[0].style = 'min-height:90px';
17142 row.cn[6].cls+=' fc-last';
17146 ret[0].cls += ' fc-first';
17147 ret[4].cls += ' fc-prev-last';
17148 ret[5].cls += ' fc-last';
17155 cls: 'fc-border-separate',
17156 style : 'width:100%',
17164 cls : 'fc-first fc-last',
17182 cls : 'fc-content',
17183 style : "position: relative;",
17186 cls : 'fc-view fc-view-month fc-grid',
17187 style : 'position: relative',
17188 unselectable : 'on',
17191 cls : 'fc-event-container',
17192 style : 'position:absolute;z-index:8;top:0;left:0;'
17210 initEvents : function()
17213 throw "can not find store for calendar";
17219 style: "text-align:center",
17223 style: "background-color:white;width:50%;margin:250 auto",
17227 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
17238 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17240 var size = this.el.select('.fc-content', true).first().getSize();
17241 this.maskEl.setSize(size.width, size.height);
17242 this.maskEl.enableDisplayMode("block");
17243 if(!this.loadMask){
17244 this.maskEl.hide();
17247 this.store = Roo.factory(this.store, Roo.data);
17248 this.store.on('load', this.onLoad, this);
17249 this.store.on('beforeload', this.onBeforeLoad, this);
17253 this.cells = this.el.select('.fc-day',true);
17254 //Roo.log(this.cells);
17255 this.textNodes = this.el.query('.fc-day-number');
17256 this.cells.addClassOnOver('fc-state-hover');
17258 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17259 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17260 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17261 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17263 this.on('monthchange', this.onMonthChange, this);
17265 this.update(new Date().clearTime());
17268 resize : function() {
17269 var sz = this.el.getSize();
17271 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17272 this.el.select('.fc-day-content div',true).setHeight(34);
17277 showPrevMonth : function(e){
17278 this.update(this.activeDate.add("mo", -1));
17280 showToday : function(e){
17281 this.update(new Date().clearTime());
17284 showNextMonth : function(e){
17285 this.update(this.activeDate.add("mo", 1));
17289 showPrevYear : function(){
17290 this.update(this.activeDate.add("y", -1));
17294 showNextYear : function(){
17295 this.update(this.activeDate.add("y", 1));
17300 update : function(date)
17302 var vd = this.activeDate;
17303 this.activeDate = date;
17304 // if(vd && this.el){
17305 // var t = date.getTime();
17306 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17307 // Roo.log('using add remove');
17309 // this.fireEvent('monthchange', this, date);
17311 // this.cells.removeClass("fc-state-highlight");
17312 // this.cells.each(function(c){
17313 // if(c.dateValue == t){
17314 // c.addClass("fc-state-highlight");
17315 // setTimeout(function(){
17316 // try{c.dom.firstChild.focus();}catch(e){}
17326 var days = date.getDaysInMonth();
17328 var firstOfMonth = date.getFirstDateOfMonth();
17329 var startingPos = firstOfMonth.getDay()-this.startDay;
17331 if(startingPos < this.startDay){
17335 var pm = date.add(Date.MONTH, -1);
17336 var prevStart = pm.getDaysInMonth()-startingPos;
17338 this.cells = this.el.select('.fc-day',true);
17339 this.textNodes = this.el.query('.fc-day-number');
17340 this.cells.addClassOnOver('fc-state-hover');
17342 var cells = this.cells.elements;
17343 var textEls = this.textNodes;
17345 Roo.each(cells, function(cell){
17346 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17349 days += startingPos;
17351 // convert everything to numbers so it's fast
17352 var day = 86400000;
17353 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17356 //Roo.log(prevStart);
17358 var today = new Date().clearTime().getTime();
17359 var sel = date.clearTime().getTime();
17360 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17361 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17362 var ddMatch = this.disabledDatesRE;
17363 var ddText = this.disabledDatesText;
17364 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17365 var ddaysText = this.disabledDaysText;
17366 var format = this.format;
17368 var setCellClass = function(cal, cell){
17372 //Roo.log('set Cell Class');
17374 var t = d.getTime();
17378 cell.dateValue = t;
17380 cell.className += " fc-today";
17381 cell.className += " fc-state-highlight";
17382 cell.title = cal.todayText;
17385 // disable highlight in other month..
17386 //cell.className += " fc-state-highlight";
17391 cell.className = " fc-state-disabled";
17392 cell.title = cal.minText;
17396 cell.className = " fc-state-disabled";
17397 cell.title = cal.maxText;
17401 if(ddays.indexOf(d.getDay()) != -1){
17402 cell.title = ddaysText;
17403 cell.className = " fc-state-disabled";
17406 if(ddMatch && format){
17407 var fvalue = d.dateFormat(format);
17408 if(ddMatch.test(fvalue)){
17409 cell.title = ddText.replace("%0", fvalue);
17410 cell.className = " fc-state-disabled";
17414 if (!cell.initialClassName) {
17415 cell.initialClassName = cell.dom.className;
17418 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17423 for(; i < startingPos; i++) {
17424 textEls[i].innerHTML = (++prevStart);
17425 d.setDate(d.getDate()+1);
17427 cells[i].className = "fc-past fc-other-month";
17428 setCellClass(this, cells[i]);
17433 for(; i < days; i++){
17434 intDay = i - startingPos + 1;
17435 textEls[i].innerHTML = (intDay);
17436 d.setDate(d.getDate()+1);
17438 cells[i].className = ''; // "x-date-active";
17439 setCellClass(this, cells[i]);
17443 for(; i < 42; i++) {
17444 textEls[i].innerHTML = (++extraDays);
17445 d.setDate(d.getDate()+1);
17447 cells[i].className = "fc-future fc-other-month";
17448 setCellClass(this, cells[i]);
17451 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17453 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17455 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17456 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17458 if(totalRows != 6){
17459 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17460 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17463 this.fireEvent('monthchange', this, date);
17467 if(!this.internalRender){
17468 var main = this.el.dom.firstChild;
17469 var w = main.offsetWidth;
17470 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17471 Roo.fly(main).setWidth(w);
17472 this.internalRender = true;
17473 // opera does not respect the auto grow header center column
17474 // then, after it gets a width opera refuses to recalculate
17475 // without a second pass
17476 if(Roo.isOpera && !this.secondPass){
17477 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17478 this.secondPass = true;
17479 this.update.defer(10, this, [date]);
17486 findCell : function(dt) {
17487 dt = dt.clearTime().getTime();
17489 this.cells.each(function(c){
17490 //Roo.log("check " +c.dateValue + '?=' + dt);
17491 if(c.dateValue == dt){
17501 findCells : function(ev) {
17502 var s = ev.start.clone().clearTime().getTime();
17504 var e= ev.end.clone().clearTime().getTime();
17507 this.cells.each(function(c){
17508 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17510 if(c.dateValue > e){
17513 if(c.dateValue < s){
17522 // findBestRow: function(cells)
17526 // for (var i =0 ; i < cells.length;i++) {
17527 // ret = Math.max(cells[i].rows || 0,ret);
17534 addItem : function(ev)
17536 // look for vertical location slot in
17537 var cells = this.findCells(ev);
17539 // ev.row = this.findBestRow(cells);
17541 // work out the location.
17545 for(var i =0; i < cells.length; i++) {
17547 cells[i].row = cells[0].row;
17550 cells[i].row = cells[i].row + 1;
17560 if (crow.start.getY() == cells[i].getY()) {
17562 crow.end = cells[i];
17579 cells[0].events.push(ev);
17581 this.calevents.push(ev);
17584 clearEvents: function() {
17586 if(!this.calevents){
17590 Roo.each(this.cells.elements, function(c){
17596 Roo.each(this.calevents, function(e) {
17597 Roo.each(e.els, function(el) {
17598 el.un('mouseenter' ,this.onEventEnter, this);
17599 el.un('mouseleave' ,this.onEventLeave, this);
17604 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17610 renderEvents: function()
17614 this.cells.each(function(c) {
17623 if(c.row != c.events.length){
17624 r = 4 - (4 - (c.row - c.events.length));
17627 c.events = ev.slice(0, r);
17628 c.more = ev.slice(r);
17630 if(c.more.length && c.more.length == 1){
17631 c.events.push(c.more.pop());
17634 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17638 this.cells.each(function(c) {
17640 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17643 for (var e = 0; e < c.events.length; e++){
17644 var ev = c.events[e];
17645 var rows = ev.rows;
17647 for(var i = 0; i < rows.length; i++) {
17649 // how many rows should it span..
17652 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17653 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17655 unselectable : "on",
17658 cls: 'fc-event-inner',
17662 // cls: 'fc-event-time',
17663 // html : cells.length > 1 ? '' : ev.time
17667 cls: 'fc-event-title',
17668 html : String.format('{0}', ev.title)
17675 cls: 'ui-resizable-handle ui-resizable-e',
17676 html : '  '
17683 cfg.cls += ' fc-event-start';
17685 if ((i+1) == rows.length) {
17686 cfg.cls += ' fc-event-end';
17689 var ctr = _this.el.select('.fc-event-container',true).first();
17690 var cg = ctr.createChild(cfg);
17692 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17693 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17695 var r = (c.more.length) ? 1 : 0;
17696 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17697 cg.setWidth(ebox.right - sbox.x -2);
17699 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17700 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17701 cg.on('click', _this.onEventClick, _this, ev);
17712 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17713 style : 'position: absolute',
17714 unselectable : "on",
17717 cls: 'fc-event-inner',
17721 cls: 'fc-event-title',
17729 cls: 'ui-resizable-handle ui-resizable-e',
17730 html : '  '
17736 var ctr = _this.el.select('.fc-event-container',true).first();
17737 var cg = ctr.createChild(cfg);
17739 var sbox = c.select('.fc-day-content',true).first().getBox();
17740 var ebox = c.select('.fc-day-content',true).first().getBox();
17742 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17743 cg.setWidth(ebox.right - sbox.x -2);
17745 cg.on('click', _this.onMoreEventClick, _this, c.more);
17755 onEventEnter: function (e, el,event,d) {
17756 this.fireEvent('evententer', this, el, event);
17759 onEventLeave: function (e, el,event,d) {
17760 this.fireEvent('eventleave', this, el, event);
17763 onEventClick: function (e, el,event,d) {
17764 this.fireEvent('eventclick', this, el, event);
17767 onMonthChange: function () {
17771 onMoreEventClick: function(e, el, more)
17775 this.calpopover.placement = 'right';
17776 this.calpopover.setTitle('More');
17778 this.calpopover.setContent('');
17780 var ctr = this.calpopover.el.select('.popover-content', true).first();
17782 Roo.each(more, function(m){
17784 cls : 'fc-event-hori fc-event-draggable',
17787 var cg = ctr.createChild(cfg);
17789 cg.on('click', _this.onEventClick, _this, m);
17792 this.calpopover.show(el);
17797 onLoad: function ()
17799 this.calevents = [];
17802 if(this.store.getCount() > 0){
17803 this.store.data.each(function(d){
17806 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17807 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17808 time : d.data.start_time,
17809 title : d.data.title,
17810 description : d.data.description,
17811 venue : d.data.venue
17816 this.renderEvents();
17818 if(this.calevents.length && this.loadMask){
17819 this.maskEl.hide();
17823 onBeforeLoad: function()
17825 this.clearEvents();
17827 this.maskEl.show();
17841 * @class Roo.bootstrap.Popover
17842 * @extends Roo.bootstrap.Component
17843 * Bootstrap Popover class
17844 * @cfg {String} html contents of the popover (or false to use children..)
17845 * @cfg {String} title of popover (or false to hide)
17846 * @cfg {String} placement how it is placed
17847 * @cfg {String} trigger click || hover (or false to trigger manually)
17848 * @cfg {String} over what (parent or false to trigger manually.)
17849 * @cfg {Number} delay - delay before showing
17852 * Create a new Popover
17853 * @param {Object} config The config object
17856 Roo.bootstrap.Popover = function(config){
17857 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17863 * After the popover show
17865 * @param {Roo.bootstrap.Popover} this
17870 * After the popover hide
17872 * @param {Roo.bootstrap.Popover} this
17878 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17880 title: 'Fill in a title',
17883 placement : 'right',
17884 trigger : 'hover', // hover
17890 can_build_overlaid : false,
17892 getChildContainer : function()
17894 return this.el.select('.popover-content',true).first();
17897 getAutoCreate : function(){
17900 cls : 'popover roo-dynamic',
17901 style: 'display:block',
17907 cls : 'popover-inner',
17911 cls: 'popover-title popover-header',
17915 cls : 'popover-content popover-body',
17926 setTitle: function(str)
17929 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17931 setContent: function(str)
17934 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17936 // as it get's added to the bottom of the page.
17937 onRender : function(ct, position)
17939 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17941 var cfg = Roo.apply({}, this.getAutoCreate());
17945 cfg.cls += ' ' + this.cls;
17948 cfg.style = this.style;
17950 //Roo.log("adding to ");
17951 this.el = Roo.get(document.body).createChild(cfg, position);
17952 // Roo.log(this.el);
17957 initEvents : function()
17959 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17960 this.el.enableDisplayMode('block');
17962 if (this.over === false) {
17965 if (this.triggers === false) {
17968 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17969 var triggers = this.trigger ? this.trigger.split(' ') : [];
17970 Roo.each(triggers, function(trigger) {
17972 if (trigger == 'click') {
17973 on_el.on('click', this.toggle, this);
17974 } else if (trigger != 'manual') {
17975 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17976 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17978 on_el.on(eventIn ,this.enter, this);
17979 on_el.on(eventOut, this.leave, this);
17990 toggle : function () {
17991 this.hoverState == 'in' ? this.leave() : this.enter();
17994 enter : function () {
17996 clearTimeout(this.timeout);
17998 this.hoverState = 'in';
18000 if (!this.delay || !this.delay.show) {
18005 this.timeout = setTimeout(function () {
18006 if (_t.hoverState == 'in') {
18009 }, this.delay.show)
18012 leave : function() {
18013 clearTimeout(this.timeout);
18015 this.hoverState = 'out';
18017 if (!this.delay || !this.delay.hide) {
18022 this.timeout = setTimeout(function () {
18023 if (_t.hoverState == 'out') {
18026 }, this.delay.hide)
18029 show : function (on_el)
18032 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18036 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18037 if (this.html !== false) {
18038 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18040 this.el.removeClass([
18041 'fade','top','bottom', 'left', 'right','in',
18042 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18044 if (!this.title.length) {
18045 this.el.select('.popover-title',true).hide();
18048 var placement = typeof this.placement == 'function' ?
18049 this.placement.call(this, this.el, on_el) :
18052 var autoToken = /\s?auto?\s?/i;
18053 var autoPlace = autoToken.test(placement);
18055 placement = placement.replace(autoToken, '') || 'top';
18059 //this.el.setXY([0,0]);
18061 this.el.dom.style.display='block';
18062 this.el.addClass(placement);
18064 //this.el.appendTo(on_el);
18066 var p = this.getPosition();
18067 var box = this.el.getBox();
18072 var align = Roo.bootstrap.Popover.alignment[placement];
18075 this.el.alignTo(on_el, align[0],align[1]);
18076 //var arrow = this.el.select('.arrow',true).first();
18077 //arrow.set(align[2],
18079 this.el.addClass('in');
18082 if (this.el.hasClass('fade')) {
18086 this.hoverState = 'in';
18088 this.fireEvent('show', this);
18093 this.el.setXY([0,0]);
18094 this.el.removeClass('in');
18096 this.hoverState = null;
18098 this.fireEvent('hide', this);
18103 Roo.bootstrap.Popover.alignment = {
18104 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18105 'right' : ['l-r', [10,0], 'left bs-popover-left'],
18106 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18107 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18118 * @class Roo.bootstrap.Progress
18119 * @extends Roo.bootstrap.Component
18120 * Bootstrap Progress class
18121 * @cfg {Boolean} striped striped of the progress bar
18122 * @cfg {Boolean} active animated of the progress bar
18126 * Create a new Progress
18127 * @param {Object} config The config object
18130 Roo.bootstrap.Progress = function(config){
18131 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18134 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
18139 getAutoCreate : function(){
18147 cfg.cls += ' progress-striped';
18151 cfg.cls += ' active';
18170 * @class Roo.bootstrap.ProgressBar
18171 * @extends Roo.bootstrap.Component
18172 * Bootstrap ProgressBar class
18173 * @cfg {Number} aria_valuenow aria-value now
18174 * @cfg {Number} aria_valuemin aria-value min
18175 * @cfg {Number} aria_valuemax aria-value max
18176 * @cfg {String} label label for the progress bar
18177 * @cfg {String} panel (success | info | warning | danger )
18178 * @cfg {String} role role of the progress bar
18179 * @cfg {String} sr_only text
18183 * Create a new ProgressBar
18184 * @param {Object} config The config object
18187 Roo.bootstrap.ProgressBar = function(config){
18188 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18191 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
18195 aria_valuemax : 100,
18201 getAutoCreate : function()
18206 cls: 'progress-bar',
18207 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18219 cfg.role = this.role;
18222 if(this.aria_valuenow){
18223 cfg['aria-valuenow'] = this.aria_valuenow;
18226 if(this.aria_valuemin){
18227 cfg['aria-valuemin'] = this.aria_valuemin;
18230 if(this.aria_valuemax){
18231 cfg['aria-valuemax'] = this.aria_valuemax;
18234 if(this.label && !this.sr_only){
18235 cfg.html = this.label;
18239 cfg.cls += ' progress-bar-' + this.panel;
18245 update : function(aria_valuenow)
18247 this.aria_valuenow = aria_valuenow;
18249 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18264 * @class Roo.bootstrap.TabGroup
18265 * @extends Roo.bootstrap.Column
18266 * Bootstrap Column class
18267 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18268 * @cfg {Boolean} carousel true to make the group behave like a carousel
18269 * @cfg {Boolean} bullets show bullets for the panels
18270 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18271 * @cfg {Number} timer auto slide timer .. default 0 millisecond
18272 * @cfg {Boolean} showarrow (true|false) show arrow default true
18275 * Create a new TabGroup
18276 * @param {Object} config The config object
18279 Roo.bootstrap.TabGroup = function(config){
18280 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18282 this.navId = Roo.id();
18285 Roo.bootstrap.TabGroup.register(this);
18289 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18292 transition : false,
18297 slideOnTouch : false,
18300 getAutoCreate : function()
18302 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18304 cfg.cls += ' tab-content';
18306 if (this.carousel) {
18307 cfg.cls += ' carousel slide';
18310 cls : 'carousel-inner',
18314 if(this.bullets && !Roo.isTouch){
18317 cls : 'carousel-bullets',
18321 if(this.bullets_cls){
18322 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18329 cfg.cn[0].cn.push(bullets);
18332 if(this.showarrow){
18333 cfg.cn[0].cn.push({
18335 class : 'carousel-arrow',
18339 class : 'carousel-prev',
18343 class : 'fa fa-chevron-left'
18349 class : 'carousel-next',
18353 class : 'fa fa-chevron-right'
18366 initEvents: function()
18368 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18369 // this.el.on("touchstart", this.onTouchStart, this);
18372 if(this.autoslide){
18375 this.slideFn = window.setInterval(function() {
18376 _this.showPanelNext();
18380 if(this.showarrow){
18381 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18382 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18388 // onTouchStart : function(e, el, o)
18390 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18394 // this.showPanelNext();
18398 getChildContainer : function()
18400 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18404 * register a Navigation item
18405 * @param {Roo.bootstrap.NavItem} the navitem to add
18407 register : function(item)
18409 this.tabs.push( item);
18410 item.navId = this.navId; // not really needed..
18415 getActivePanel : function()
18418 Roo.each(this.tabs, function(t) {
18428 getPanelByName : function(n)
18431 Roo.each(this.tabs, function(t) {
18432 if (t.tabId == n) {
18440 indexOfPanel : function(p)
18443 Roo.each(this.tabs, function(t,i) {
18444 if (t.tabId == p.tabId) {
18453 * show a specific panel
18454 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18455 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18457 showPanel : function (pan)
18459 if(this.transition || typeof(pan) == 'undefined'){
18460 Roo.log("waiting for the transitionend");
18464 if (typeof(pan) == 'number') {
18465 pan = this.tabs[pan];
18468 if (typeof(pan) == 'string') {
18469 pan = this.getPanelByName(pan);
18472 var cur = this.getActivePanel();
18475 Roo.log('pan or acitve pan is undefined');
18479 if (pan.tabId == this.getActivePanel().tabId) {
18483 if (false === cur.fireEvent('beforedeactivate')) {
18487 if(this.bullets > 0 && !Roo.isTouch){
18488 this.setActiveBullet(this.indexOfPanel(pan));
18491 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18493 //class="carousel-item carousel-item-next carousel-item-left"
18495 this.transition = true;
18496 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18497 var lr = dir == 'next' ? 'left' : 'right';
18498 pan.el.addClass(dir); // or prev
18499 pan.el.addClass('carousel-item-' + dir); // or prev
18500 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18501 cur.el.addClass(lr); // or right
18502 pan.el.addClass(lr);
18503 cur.el.addClass('carousel-item-' +lr); // or right
18504 pan.el.addClass('carousel-item-' +lr);
18508 cur.el.on('transitionend', function() {
18509 Roo.log("trans end?");
18511 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18512 pan.setActive(true);
18514 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18515 cur.setActive(false);
18517 _this.transition = false;
18519 }, this, { single: true } );
18524 cur.setActive(false);
18525 pan.setActive(true);
18530 showPanelNext : function()
18532 var i = this.indexOfPanel(this.getActivePanel());
18534 if (i >= this.tabs.length - 1 && !this.autoslide) {
18538 if (i >= this.tabs.length - 1 && this.autoslide) {
18542 this.showPanel(this.tabs[i+1]);
18545 showPanelPrev : function()
18547 var i = this.indexOfPanel(this.getActivePanel());
18549 if (i < 1 && !this.autoslide) {
18553 if (i < 1 && this.autoslide) {
18554 i = this.tabs.length;
18557 this.showPanel(this.tabs[i-1]);
18561 addBullet: function()
18563 if(!this.bullets || Roo.isTouch){
18566 var ctr = this.el.select('.carousel-bullets',true).first();
18567 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18568 var bullet = ctr.createChild({
18569 cls : 'bullet bullet-' + i
18570 },ctr.dom.lastChild);
18575 bullet.on('click', (function(e, el, o, ii, t){
18577 e.preventDefault();
18579 this.showPanel(ii);
18581 if(this.autoslide && this.slideFn){
18582 clearInterval(this.slideFn);
18583 this.slideFn = window.setInterval(function() {
18584 _this.showPanelNext();
18588 }).createDelegate(this, [i, bullet], true));
18593 setActiveBullet : function(i)
18599 Roo.each(this.el.select('.bullet', true).elements, function(el){
18600 el.removeClass('selected');
18603 var bullet = this.el.select('.bullet-' + i, true).first();
18609 bullet.addClass('selected');
18620 Roo.apply(Roo.bootstrap.TabGroup, {
18624 * register a Navigation Group
18625 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18627 register : function(navgrp)
18629 this.groups[navgrp.navId] = navgrp;
18633 * fetch a Navigation Group based on the navigation ID
18634 * if one does not exist , it will get created.
18635 * @param {string} the navgroup to add
18636 * @returns {Roo.bootstrap.NavGroup} the navgroup
18638 get: function(navId) {
18639 if (typeof(this.groups[navId]) == 'undefined') {
18640 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18642 return this.groups[navId] ;
18657 * @class Roo.bootstrap.TabPanel
18658 * @extends Roo.bootstrap.Component
18659 * Bootstrap TabPanel class
18660 * @cfg {Boolean} active panel active
18661 * @cfg {String} html panel content
18662 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18663 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18664 * @cfg {String} href click to link..
18668 * Create a new TabPanel
18669 * @param {Object} config The config object
18672 Roo.bootstrap.TabPanel = function(config){
18673 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18677 * Fires when the active status changes
18678 * @param {Roo.bootstrap.TabPanel} this
18679 * @param {Boolean} state the new state
18684 * @event beforedeactivate
18685 * Fires before a tab is de-activated - can be used to do validation on a form.
18686 * @param {Roo.bootstrap.TabPanel} this
18687 * @return {Boolean} false if there is an error
18690 'beforedeactivate': true
18693 this.tabId = this.tabId || Roo.id();
18697 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18705 getAutoCreate : function(){
18710 // item is needed for carousel - not sure if it has any effect otherwise
18711 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18712 html: this.html || ''
18716 cfg.cls += ' active';
18720 cfg.tabId = this.tabId;
18728 initEvents: function()
18730 var p = this.parent();
18732 this.navId = this.navId || p.navId;
18734 if (typeof(this.navId) != 'undefined') {
18735 // not really needed.. but just in case.. parent should be a NavGroup.
18736 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18740 var i = tg.tabs.length - 1;
18742 if(this.active && tg.bullets > 0 && i < tg.bullets){
18743 tg.setActiveBullet(i);
18747 this.el.on('click', this.onClick, this);
18750 this.el.on("touchstart", this.onTouchStart, this);
18751 this.el.on("touchmove", this.onTouchMove, this);
18752 this.el.on("touchend", this.onTouchEnd, this);
18757 onRender : function(ct, position)
18759 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18762 setActive : function(state)
18764 Roo.log("panel - set active " + this.tabId + "=" + state);
18766 this.active = state;
18768 this.el.removeClass('active');
18770 } else if (!this.el.hasClass('active')) {
18771 this.el.addClass('active');
18774 this.fireEvent('changed', this, state);
18777 onClick : function(e)
18779 e.preventDefault();
18781 if(!this.href.length){
18785 window.location.href = this.href;
18794 onTouchStart : function(e)
18796 this.swiping = false;
18798 this.startX = e.browserEvent.touches[0].clientX;
18799 this.startY = e.browserEvent.touches[0].clientY;
18802 onTouchMove : function(e)
18804 this.swiping = true;
18806 this.endX = e.browserEvent.touches[0].clientX;
18807 this.endY = e.browserEvent.touches[0].clientY;
18810 onTouchEnd : function(e)
18817 var tabGroup = this.parent();
18819 if(this.endX > this.startX){ // swiping right
18820 tabGroup.showPanelPrev();
18824 if(this.startX > this.endX){ // swiping left
18825 tabGroup.showPanelNext();
18844 * @class Roo.bootstrap.DateField
18845 * @extends Roo.bootstrap.Input
18846 * Bootstrap DateField class
18847 * @cfg {Number} weekStart default 0
18848 * @cfg {String} viewMode default empty, (months|years)
18849 * @cfg {String} minViewMode default empty, (months|years)
18850 * @cfg {Number} startDate default -Infinity
18851 * @cfg {Number} endDate default Infinity
18852 * @cfg {Boolean} todayHighlight default false
18853 * @cfg {Boolean} todayBtn default false
18854 * @cfg {Boolean} calendarWeeks default false
18855 * @cfg {Object} daysOfWeekDisabled default empty
18856 * @cfg {Boolean} singleMode default false (true | false)
18858 * @cfg {Boolean} keyboardNavigation default true
18859 * @cfg {String} language default en
18862 * Create a new DateField
18863 * @param {Object} config The config object
18866 Roo.bootstrap.DateField = function(config){
18867 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18871 * Fires when this field show.
18872 * @param {Roo.bootstrap.DateField} this
18873 * @param {Mixed} date The date value
18878 * Fires when this field hide.
18879 * @param {Roo.bootstrap.DateField} this
18880 * @param {Mixed} date The date value
18885 * Fires when select a date.
18886 * @param {Roo.bootstrap.DateField} this
18887 * @param {Mixed} date The date value
18891 * @event beforeselect
18892 * Fires when before select a date.
18893 * @param {Roo.bootstrap.DateField} this
18894 * @param {Mixed} date The date value
18896 beforeselect : true
18900 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18903 * @cfg {String} format
18904 * The default date format string which can be overriden for localization support. The format must be
18905 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18909 * @cfg {String} altFormats
18910 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18911 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18913 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18921 todayHighlight : false,
18927 keyboardNavigation: true,
18929 calendarWeeks: false,
18931 startDate: -Infinity,
18935 daysOfWeekDisabled: [],
18939 singleMode : false,
18941 UTCDate: function()
18943 return new Date(Date.UTC.apply(Date, arguments));
18946 UTCToday: function()
18948 var today = new Date();
18949 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18952 getDate: function() {
18953 var d = this.getUTCDate();
18954 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18957 getUTCDate: function() {
18961 setDate: function(d) {
18962 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18965 setUTCDate: function(d) {
18967 this.setValue(this.formatDate(this.date));
18970 onRender: function(ct, position)
18973 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18975 this.language = this.language || 'en';
18976 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18977 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18979 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18980 this.format = this.format || 'm/d/y';
18981 this.isInline = false;
18982 this.isInput = true;
18983 this.component = this.el.select('.add-on', true).first() || false;
18984 this.component = (this.component && this.component.length === 0) ? false : this.component;
18985 this.hasInput = this.component && this.inputEl().length;
18987 if (typeof(this.minViewMode === 'string')) {
18988 switch (this.minViewMode) {
18990 this.minViewMode = 1;
18993 this.minViewMode = 2;
18996 this.minViewMode = 0;
19001 if (typeof(this.viewMode === 'string')) {
19002 switch (this.viewMode) {
19015 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
19017 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
19019 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19021 this.picker().on('mousedown', this.onMousedown, this);
19022 this.picker().on('click', this.onClick, this);
19024 this.picker().addClass('datepicker-dropdown');
19026 this.startViewMode = this.viewMode;
19028 if(this.singleMode){
19029 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19030 v.setVisibilityMode(Roo.Element.DISPLAY);
19034 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19035 v.setStyle('width', '189px');
19039 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19040 if(!this.calendarWeeks){
19045 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19046 v.attr('colspan', function(i, val){
19047 return parseInt(val) + 1;
19052 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19054 this.setStartDate(this.startDate);
19055 this.setEndDate(this.endDate);
19057 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19064 if(this.isInline) {
19069 picker : function()
19071 return this.pickerEl;
19072 // return this.el.select('.datepicker', true).first();
19075 fillDow: function()
19077 var dowCnt = this.weekStart;
19086 if(this.calendarWeeks){
19094 while (dowCnt < this.weekStart + 7) {
19098 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19102 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19105 fillMonths: function()
19108 var months = this.picker().select('>.datepicker-months td', true).first();
19110 months.dom.innerHTML = '';
19116 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19119 months.createChild(month);
19126 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;
19128 if (this.date < this.startDate) {
19129 this.viewDate = new Date(this.startDate);
19130 } else if (this.date > this.endDate) {
19131 this.viewDate = new Date(this.endDate);
19133 this.viewDate = new Date(this.date);
19141 var d = new Date(this.viewDate),
19142 year = d.getUTCFullYear(),
19143 month = d.getUTCMonth(),
19144 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19145 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19146 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19147 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19148 currentDate = this.date && this.date.valueOf(),
19149 today = this.UTCToday();
19151 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19153 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19155 // this.picker.select('>tfoot th.today').
19156 // .text(dates[this.language].today)
19157 // .toggle(this.todayBtn !== false);
19159 this.updateNavArrows();
19162 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19164 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19166 prevMonth.setUTCDate(day);
19168 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19170 var nextMonth = new Date(prevMonth);
19172 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19174 nextMonth = nextMonth.valueOf();
19176 var fillMonths = false;
19178 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19180 while(prevMonth.valueOf() <= nextMonth) {
19183 if (prevMonth.getUTCDay() === this.weekStart) {
19185 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19193 if(this.calendarWeeks){
19194 // ISO 8601: First week contains first thursday.
19195 // ISO also states week starts on Monday, but we can be more abstract here.
19197 // Start of current week: based on weekstart/current date
19198 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19199 // Thursday of this week
19200 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19201 // First Thursday of year, year from thursday
19202 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19203 // Calendar week: ms between thursdays, div ms per day, div 7 days
19204 calWeek = (th - yth) / 864e5 / 7 + 1;
19206 fillMonths.cn.push({
19214 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19216 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19219 if (this.todayHighlight &&
19220 prevMonth.getUTCFullYear() == today.getFullYear() &&
19221 prevMonth.getUTCMonth() == today.getMonth() &&
19222 prevMonth.getUTCDate() == today.getDate()) {
19223 clsName += ' today';
19226 if (currentDate && prevMonth.valueOf() === currentDate) {
19227 clsName += ' active';
19230 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19231 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19232 clsName += ' disabled';
19235 fillMonths.cn.push({
19237 cls: 'day ' + clsName,
19238 html: prevMonth.getDate()
19241 prevMonth.setDate(prevMonth.getDate()+1);
19244 var currentYear = this.date && this.date.getUTCFullYear();
19245 var currentMonth = this.date && this.date.getUTCMonth();
19247 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19249 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19250 v.removeClass('active');
19252 if(currentYear === year && k === currentMonth){
19253 v.addClass('active');
19256 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19257 v.addClass('disabled');
19263 year = parseInt(year/10, 10) * 10;
19265 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19267 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19270 for (var i = -1; i < 11; i++) {
19271 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19273 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19281 showMode: function(dir)
19284 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19287 Roo.each(this.picker().select('>div',true).elements, function(v){
19288 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19291 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19296 if(this.isInline) {
19300 this.picker().removeClass(['bottom', 'top']);
19302 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19304 * place to the top of element!
19308 this.picker().addClass('top');
19309 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19314 this.picker().addClass('bottom');
19316 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19319 parseDate : function(value)
19321 if(!value || value instanceof Date){
19324 var v = Date.parseDate(value, this.format);
19325 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19326 v = Date.parseDate(value, 'Y-m-d');
19328 if(!v && this.altFormats){
19329 if(!this.altFormatsArray){
19330 this.altFormatsArray = this.altFormats.split("|");
19332 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19333 v = Date.parseDate(value, this.altFormatsArray[i]);
19339 formatDate : function(date, fmt)
19341 return (!date || !(date instanceof Date)) ?
19342 date : date.dateFormat(fmt || this.format);
19345 onFocus : function()
19347 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19351 onBlur : function()
19353 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19355 var d = this.inputEl().getValue();
19362 showPopup : function()
19364 this.picker().show();
19368 this.fireEvent('showpopup', this, this.date);
19371 hidePopup : function()
19373 if(this.isInline) {
19376 this.picker().hide();
19377 this.viewMode = this.startViewMode;
19380 this.fireEvent('hidepopup', this, this.date);
19384 onMousedown: function(e)
19386 e.stopPropagation();
19387 e.preventDefault();
19392 Roo.bootstrap.DateField.superclass.keyup.call(this);
19396 setValue: function(v)
19398 if(this.fireEvent('beforeselect', this, v) !== false){
19399 var d = new Date(this.parseDate(v) ).clearTime();
19401 if(isNaN(d.getTime())){
19402 this.date = this.viewDate = '';
19403 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19407 v = this.formatDate(d);
19409 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19411 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19415 this.fireEvent('select', this, this.date);
19419 getValue: function()
19421 return this.formatDate(this.date);
19424 fireKey: function(e)
19426 if (!this.picker().isVisible()){
19427 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19433 var dateChanged = false,
19435 newDate, newViewDate;
19440 e.preventDefault();
19444 if (!this.keyboardNavigation) {
19447 dir = e.keyCode == 37 ? -1 : 1;
19450 newDate = this.moveYear(this.date, dir);
19451 newViewDate = this.moveYear(this.viewDate, dir);
19452 } else if (e.shiftKey){
19453 newDate = this.moveMonth(this.date, dir);
19454 newViewDate = this.moveMonth(this.viewDate, dir);
19456 newDate = new Date(this.date);
19457 newDate.setUTCDate(this.date.getUTCDate() + dir);
19458 newViewDate = new Date(this.viewDate);
19459 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19461 if (this.dateWithinRange(newDate)){
19462 this.date = newDate;
19463 this.viewDate = newViewDate;
19464 this.setValue(this.formatDate(this.date));
19466 e.preventDefault();
19467 dateChanged = true;
19472 if (!this.keyboardNavigation) {
19475 dir = e.keyCode == 38 ? -1 : 1;
19477 newDate = this.moveYear(this.date, dir);
19478 newViewDate = this.moveYear(this.viewDate, dir);
19479 } else if (e.shiftKey){
19480 newDate = this.moveMonth(this.date, dir);
19481 newViewDate = this.moveMonth(this.viewDate, dir);
19483 newDate = new Date(this.date);
19484 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19485 newViewDate = new Date(this.viewDate);
19486 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19488 if (this.dateWithinRange(newDate)){
19489 this.date = newDate;
19490 this.viewDate = newViewDate;
19491 this.setValue(this.formatDate(this.date));
19493 e.preventDefault();
19494 dateChanged = true;
19498 this.setValue(this.formatDate(this.date));
19500 e.preventDefault();
19503 this.setValue(this.formatDate(this.date));
19517 onClick: function(e)
19519 e.stopPropagation();
19520 e.preventDefault();
19522 var target = e.getTarget();
19524 if(target.nodeName.toLowerCase() === 'i'){
19525 target = Roo.get(target).dom.parentNode;
19528 var nodeName = target.nodeName;
19529 var className = target.className;
19530 var html = target.innerHTML;
19531 //Roo.log(nodeName);
19533 switch(nodeName.toLowerCase()) {
19535 switch(className) {
19541 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19542 switch(this.viewMode){
19544 this.viewDate = this.moveMonth(this.viewDate, dir);
19548 this.viewDate = this.moveYear(this.viewDate, dir);
19554 var date = new Date();
19555 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19557 this.setValue(this.formatDate(this.date));
19564 if (className.indexOf('disabled') < 0) {
19565 this.viewDate.setUTCDate(1);
19566 if (className.indexOf('month') > -1) {
19567 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19569 var year = parseInt(html, 10) || 0;
19570 this.viewDate.setUTCFullYear(year);
19574 if(this.singleMode){
19575 this.setValue(this.formatDate(this.viewDate));
19586 //Roo.log(className);
19587 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19588 var day = parseInt(html, 10) || 1;
19589 var year = this.viewDate.getUTCFullYear(),
19590 month = this.viewDate.getUTCMonth();
19592 if (className.indexOf('old') > -1) {
19599 } else if (className.indexOf('new') > -1) {
19607 //Roo.log([year,month,day]);
19608 this.date = this.UTCDate(year, month, day,0,0,0,0);
19609 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19611 //Roo.log(this.formatDate(this.date));
19612 this.setValue(this.formatDate(this.date));
19619 setStartDate: function(startDate)
19621 this.startDate = startDate || -Infinity;
19622 if (this.startDate !== -Infinity) {
19623 this.startDate = this.parseDate(this.startDate);
19626 this.updateNavArrows();
19629 setEndDate: function(endDate)
19631 this.endDate = endDate || Infinity;
19632 if (this.endDate !== Infinity) {
19633 this.endDate = this.parseDate(this.endDate);
19636 this.updateNavArrows();
19639 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19641 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19642 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19643 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19645 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19646 return parseInt(d, 10);
19649 this.updateNavArrows();
19652 updateNavArrows: function()
19654 if(this.singleMode){
19658 var d = new Date(this.viewDate),
19659 year = d.getUTCFullYear(),
19660 month = d.getUTCMonth();
19662 Roo.each(this.picker().select('.prev', true).elements, function(v){
19664 switch (this.viewMode) {
19667 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19673 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19680 Roo.each(this.picker().select('.next', true).elements, function(v){
19682 switch (this.viewMode) {
19685 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19691 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19699 moveMonth: function(date, dir)
19704 var new_date = new Date(date.valueOf()),
19705 day = new_date.getUTCDate(),
19706 month = new_date.getUTCMonth(),
19707 mag = Math.abs(dir),
19709 dir = dir > 0 ? 1 : -1;
19712 // If going back one month, make sure month is not current month
19713 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19715 return new_date.getUTCMonth() == month;
19717 // If going forward one month, make sure month is as expected
19718 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19720 return new_date.getUTCMonth() != new_month;
19722 new_month = month + dir;
19723 new_date.setUTCMonth(new_month);
19724 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19725 if (new_month < 0 || new_month > 11) {
19726 new_month = (new_month + 12) % 12;
19729 // For magnitudes >1, move one month at a time...
19730 for (var i=0; i<mag; i++) {
19731 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19732 new_date = this.moveMonth(new_date, dir);
19734 // ...then reset the day, keeping it in the new month
19735 new_month = new_date.getUTCMonth();
19736 new_date.setUTCDate(day);
19738 return new_month != new_date.getUTCMonth();
19741 // Common date-resetting loop -- if date is beyond end of month, make it
19744 new_date.setUTCDate(--day);
19745 new_date.setUTCMonth(new_month);
19750 moveYear: function(date, dir)
19752 return this.moveMonth(date, dir*12);
19755 dateWithinRange: function(date)
19757 return date >= this.startDate && date <= this.endDate;
19763 this.picker().remove();
19766 validateValue : function(value)
19768 if(this.getVisibilityEl().hasClass('hidden')){
19772 if(value.length < 1) {
19773 if(this.allowBlank){
19779 if(value.length < this.minLength){
19782 if(value.length > this.maxLength){
19786 var vt = Roo.form.VTypes;
19787 if(!vt[this.vtype](value, this)){
19791 if(typeof this.validator == "function"){
19792 var msg = this.validator(value);
19798 if(this.regex && !this.regex.test(value)){
19802 if(typeof(this.parseDate(value)) == 'undefined'){
19806 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19810 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19820 this.date = this.viewDate = '';
19822 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19827 Roo.apply(Roo.bootstrap.DateField, {
19838 html: '<i class="fa fa-arrow-left"/>'
19848 html: '<i class="fa fa-arrow-right"/>'
19890 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19891 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19892 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19893 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19894 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19907 navFnc: 'FullYear',
19912 navFnc: 'FullYear',
19917 Roo.apply(Roo.bootstrap.DateField, {
19921 cls: 'datepicker dropdown-menu roo-dynamic',
19925 cls: 'datepicker-days',
19929 cls: 'table-condensed',
19931 Roo.bootstrap.DateField.head,
19935 Roo.bootstrap.DateField.footer
19942 cls: 'datepicker-months',
19946 cls: 'table-condensed',
19948 Roo.bootstrap.DateField.head,
19949 Roo.bootstrap.DateField.content,
19950 Roo.bootstrap.DateField.footer
19957 cls: 'datepicker-years',
19961 cls: 'table-condensed',
19963 Roo.bootstrap.DateField.head,
19964 Roo.bootstrap.DateField.content,
19965 Roo.bootstrap.DateField.footer
19984 * @class Roo.bootstrap.TimeField
19985 * @extends Roo.bootstrap.Input
19986 * Bootstrap DateField class
19990 * Create a new TimeField
19991 * @param {Object} config The config object
19994 Roo.bootstrap.TimeField = function(config){
19995 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19999 * Fires when this field show.
20000 * @param {Roo.bootstrap.DateField} thisthis
20001 * @param {Mixed} date The date value
20006 * Fires when this field hide.
20007 * @param {Roo.bootstrap.DateField} this
20008 * @param {Mixed} date The date value
20013 * Fires when select a date.
20014 * @param {Roo.bootstrap.DateField} this
20015 * @param {Mixed} date The date value
20021 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
20024 * @cfg {String} format
20025 * The default time format string which can be overriden for localization support. The format must be
20026 * valid according to {@link Date#parseDate} (defaults to 'H:i').
20030 onRender: function(ct, position)
20033 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20035 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20037 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20039 this.pop = this.picker().select('>.datepicker-time',true).first();
20040 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20042 this.picker().on('mousedown', this.onMousedown, this);
20043 this.picker().on('click', this.onClick, this);
20045 this.picker().addClass('datepicker-dropdown');
20050 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20051 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20052 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20053 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20054 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20055 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20059 fireKey: function(e){
20060 if (!this.picker().isVisible()){
20061 if (e.keyCode == 27) { // allow escape to hide and re-show picker
20067 e.preventDefault();
20075 this.onTogglePeriod();
20078 this.onIncrementMinutes();
20081 this.onDecrementMinutes();
20090 onClick: function(e) {
20091 e.stopPropagation();
20092 e.preventDefault();
20095 picker : function()
20097 return this.el.select('.datepicker', true).first();
20100 fillTime: function()
20102 var time = this.pop.select('tbody', true).first();
20104 time.dom.innerHTML = '';
20119 cls: 'hours-up glyphicon glyphicon-chevron-up'
20139 cls: 'minutes-up glyphicon glyphicon-chevron-up'
20160 cls: 'timepicker-hour',
20175 cls: 'timepicker-minute',
20190 cls: 'btn btn-primary period',
20212 cls: 'hours-down glyphicon glyphicon-chevron-down'
20232 cls: 'minutes-down glyphicon glyphicon-chevron-down'
20250 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20257 var hours = this.time.getHours();
20258 var minutes = this.time.getMinutes();
20271 hours = hours - 12;
20275 hours = '0' + hours;
20279 minutes = '0' + minutes;
20282 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20283 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20284 this.pop.select('button', true).first().dom.innerHTML = period;
20290 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20292 var cls = ['bottom'];
20294 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20301 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20306 this.picker().addClass(cls.join('-'));
20310 Roo.each(cls, function(c){
20312 _this.picker().setTop(_this.inputEl().getHeight());
20316 _this.picker().setTop(0 - _this.picker().getHeight());
20321 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20325 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20332 onFocus : function()
20334 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20338 onBlur : function()
20340 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20346 this.picker().show();
20351 this.fireEvent('show', this, this.date);
20356 this.picker().hide();
20359 this.fireEvent('hide', this, this.date);
20362 setTime : function()
20365 this.setValue(this.time.format(this.format));
20367 this.fireEvent('select', this, this.date);
20372 onMousedown: function(e){
20373 e.stopPropagation();
20374 e.preventDefault();
20377 onIncrementHours: function()
20379 Roo.log('onIncrementHours');
20380 this.time = this.time.add(Date.HOUR, 1);
20385 onDecrementHours: function()
20387 Roo.log('onDecrementHours');
20388 this.time = this.time.add(Date.HOUR, -1);
20392 onIncrementMinutes: function()
20394 Roo.log('onIncrementMinutes');
20395 this.time = this.time.add(Date.MINUTE, 1);
20399 onDecrementMinutes: function()
20401 Roo.log('onDecrementMinutes');
20402 this.time = this.time.add(Date.MINUTE, -1);
20406 onTogglePeriod: function()
20408 Roo.log('onTogglePeriod');
20409 this.time = this.time.add(Date.HOUR, 12);
20416 Roo.apply(Roo.bootstrap.TimeField, {
20446 cls: 'btn btn-info ok',
20458 Roo.apply(Roo.bootstrap.TimeField, {
20462 cls: 'datepicker dropdown-menu',
20466 cls: 'datepicker-time',
20470 cls: 'table-condensed',
20472 Roo.bootstrap.TimeField.content,
20473 Roo.bootstrap.TimeField.footer
20492 * @class Roo.bootstrap.MonthField
20493 * @extends Roo.bootstrap.Input
20494 * Bootstrap MonthField class
20496 * @cfg {String} language default en
20499 * Create a new MonthField
20500 * @param {Object} config The config object
20503 Roo.bootstrap.MonthField = function(config){
20504 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20509 * Fires when this field show.
20510 * @param {Roo.bootstrap.MonthField} this
20511 * @param {Mixed} date The date value
20516 * Fires when this field hide.
20517 * @param {Roo.bootstrap.MonthField} this
20518 * @param {Mixed} date The date value
20523 * Fires when select a date.
20524 * @param {Roo.bootstrap.MonthField} this
20525 * @param {String} oldvalue The old value
20526 * @param {String} newvalue The new value
20532 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20534 onRender: function(ct, position)
20537 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20539 this.language = this.language || 'en';
20540 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20541 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20543 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20544 this.isInline = false;
20545 this.isInput = true;
20546 this.component = this.el.select('.add-on', true).first() || false;
20547 this.component = (this.component && this.component.length === 0) ? false : this.component;
20548 this.hasInput = this.component && this.inputEL().length;
20550 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20552 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20554 this.picker().on('mousedown', this.onMousedown, this);
20555 this.picker().on('click', this.onClick, this);
20557 this.picker().addClass('datepicker-dropdown');
20559 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20560 v.setStyle('width', '189px');
20567 if(this.isInline) {
20573 setValue: function(v, suppressEvent)
20575 var o = this.getValue();
20577 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20581 if(suppressEvent !== true){
20582 this.fireEvent('select', this, o, v);
20587 getValue: function()
20592 onClick: function(e)
20594 e.stopPropagation();
20595 e.preventDefault();
20597 var target = e.getTarget();
20599 if(target.nodeName.toLowerCase() === 'i'){
20600 target = Roo.get(target).dom.parentNode;
20603 var nodeName = target.nodeName;
20604 var className = target.className;
20605 var html = target.innerHTML;
20607 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20611 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20613 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20619 picker : function()
20621 return this.pickerEl;
20624 fillMonths: function()
20627 var months = this.picker().select('>.datepicker-months td', true).first();
20629 months.dom.innerHTML = '';
20635 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20638 months.createChild(month);
20647 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20648 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20651 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20652 e.removeClass('active');
20654 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20655 e.addClass('active');
20662 if(this.isInline) {
20666 this.picker().removeClass(['bottom', 'top']);
20668 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20670 * place to the top of element!
20674 this.picker().addClass('top');
20675 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20680 this.picker().addClass('bottom');
20682 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20685 onFocus : function()
20687 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20691 onBlur : function()
20693 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20695 var d = this.inputEl().getValue();
20704 this.picker().show();
20705 this.picker().select('>.datepicker-months', true).first().show();
20709 this.fireEvent('show', this, this.date);
20714 if(this.isInline) {
20717 this.picker().hide();
20718 this.fireEvent('hide', this, this.date);
20722 onMousedown: function(e)
20724 e.stopPropagation();
20725 e.preventDefault();
20730 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20734 fireKey: function(e)
20736 if (!this.picker().isVisible()){
20737 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20748 e.preventDefault();
20752 dir = e.keyCode == 37 ? -1 : 1;
20754 this.vIndex = this.vIndex + dir;
20756 if(this.vIndex < 0){
20760 if(this.vIndex > 11){
20764 if(isNaN(this.vIndex)){
20768 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20774 dir = e.keyCode == 38 ? -1 : 1;
20776 this.vIndex = this.vIndex + dir * 4;
20778 if(this.vIndex < 0){
20782 if(this.vIndex > 11){
20786 if(isNaN(this.vIndex)){
20790 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20795 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20796 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20800 e.preventDefault();
20803 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20804 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20820 this.picker().remove();
20825 Roo.apply(Roo.bootstrap.MonthField, {
20844 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20845 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20850 Roo.apply(Roo.bootstrap.MonthField, {
20854 cls: 'datepicker dropdown-menu roo-dynamic',
20858 cls: 'datepicker-months',
20862 cls: 'table-condensed',
20864 Roo.bootstrap.DateField.content
20884 * @class Roo.bootstrap.CheckBox
20885 * @extends Roo.bootstrap.Input
20886 * Bootstrap CheckBox class
20888 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20889 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20890 * @cfg {String} boxLabel The text that appears beside the checkbox
20891 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20892 * @cfg {Boolean} checked initnal the element
20893 * @cfg {Boolean} inline inline the element (default false)
20894 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20895 * @cfg {String} tooltip label tooltip
20898 * Create a new CheckBox
20899 * @param {Object} config The config object
20902 Roo.bootstrap.CheckBox = function(config){
20903 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20908 * Fires when the element is checked or unchecked.
20909 * @param {Roo.bootstrap.CheckBox} this This input
20910 * @param {Boolean} checked The new checked value
20915 * Fires when the element is click.
20916 * @param {Roo.bootstrap.CheckBox} this This input
20923 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20925 inputType: 'checkbox',
20934 // checkbox success does not make any sense really..
20939 getAutoCreate : function()
20941 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20947 cfg.cls = 'form-group ' + this.inputType; //input-group
20950 cfg.cls += ' ' + this.inputType + '-inline';
20956 type : this.inputType,
20957 value : this.inputValue,
20958 cls : 'roo-' + this.inputType, //'form-box',
20959 placeholder : this.placeholder || ''
20963 if(this.inputType != 'radio'){
20967 cls : 'roo-hidden-value',
20968 value : this.checked ? this.inputValue : this.valueOff
20973 if (this.weight) { // Validity check?
20974 cfg.cls += " " + this.inputType + "-" + this.weight;
20977 if (this.disabled) {
20978 input.disabled=true;
20982 input.checked = this.checked;
20987 input.name = this.name;
20989 if(this.inputType != 'radio'){
20990 hidden.name = this.name;
20991 input.name = '_hidden_' + this.name;
20996 input.cls += ' input-' + this.size;
21001 ['xs','sm','md','lg'].map(function(size){
21002 if (settings[size]) {
21003 cfg.cls += ' col-' + size + '-' + settings[size];
21007 var inputblock = input;
21009 if (this.before || this.after) {
21012 cls : 'input-group',
21017 inputblock.cn.push({
21019 cls : 'input-group-addon',
21024 inputblock.cn.push(input);
21026 if(this.inputType != 'radio'){
21027 inputblock.cn.push(hidden);
21031 inputblock.cn.push({
21033 cls : 'input-group-addon',
21039 var boxLabelCfg = false;
21045 //'for': id, // box label is handled by onclick - so no for...
21047 html: this.boxLabel
21050 boxLabelCfg.tooltip = this.tooltip;
21056 if (align ==='left' && this.fieldLabel.length) {
21057 // Roo.log("left and has label");
21062 cls : 'control-label',
21063 html : this.fieldLabel
21074 cfg.cn[1].cn.push(boxLabelCfg);
21077 if(this.labelWidth > 12){
21078 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21081 if(this.labelWidth < 13 && this.labelmd == 0){
21082 this.labelmd = this.labelWidth;
21085 if(this.labellg > 0){
21086 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21087 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21090 if(this.labelmd > 0){
21091 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21092 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21095 if(this.labelsm > 0){
21096 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21097 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21100 if(this.labelxs > 0){
21101 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21102 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21105 } else if ( this.fieldLabel.length) {
21106 // Roo.log(" label");
21110 tag: this.boxLabel ? 'span' : 'label',
21112 cls: 'control-label box-input-label',
21113 //cls : 'input-group-addon',
21114 html : this.fieldLabel
21121 cfg.cn.push(boxLabelCfg);
21126 // Roo.log(" no label && no align");
21127 cfg.cn = [ inputblock ] ;
21129 cfg.cn.push(boxLabelCfg);
21137 if(this.inputType != 'radio'){
21138 cfg.cn.push(hidden);
21146 * return the real input element.
21148 inputEl: function ()
21150 return this.el.select('input.roo-' + this.inputType,true).first();
21152 hiddenEl: function ()
21154 return this.el.select('input.roo-hidden-value',true).first();
21157 labelEl: function()
21159 return this.el.select('label.control-label',true).first();
21161 /* depricated... */
21165 return this.labelEl();
21168 boxLabelEl: function()
21170 return this.el.select('label.box-label',true).first();
21173 initEvents : function()
21175 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21177 this.inputEl().on('click', this.onClick, this);
21179 if (this.boxLabel) {
21180 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
21183 this.startValue = this.getValue();
21186 Roo.bootstrap.CheckBox.register(this);
21190 onClick : function(e)
21192 if(this.fireEvent('click', this, e) !== false){
21193 this.setChecked(!this.checked);
21198 setChecked : function(state,suppressEvent)
21200 this.startValue = this.getValue();
21202 if(this.inputType == 'radio'){
21204 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21205 e.dom.checked = false;
21208 this.inputEl().dom.checked = true;
21210 this.inputEl().dom.value = this.inputValue;
21212 if(suppressEvent !== true){
21213 this.fireEvent('check', this, true);
21221 this.checked = state;
21223 this.inputEl().dom.checked = state;
21226 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21228 if(suppressEvent !== true){
21229 this.fireEvent('check', this, state);
21235 getValue : function()
21237 if(this.inputType == 'radio'){
21238 return this.getGroupValue();
21241 return this.hiddenEl().dom.value;
21245 getGroupValue : function()
21247 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21251 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21254 setValue : function(v,suppressEvent)
21256 if(this.inputType == 'radio'){
21257 this.setGroupValue(v, suppressEvent);
21261 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21266 setGroupValue : function(v, suppressEvent)
21268 this.startValue = this.getValue();
21270 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21271 e.dom.checked = false;
21273 if(e.dom.value == v){
21274 e.dom.checked = true;
21278 if(suppressEvent !== true){
21279 this.fireEvent('check', this, true);
21287 validate : function()
21289 if(this.getVisibilityEl().hasClass('hidden')){
21295 (this.inputType == 'radio' && this.validateRadio()) ||
21296 (this.inputType == 'checkbox' && this.validateCheckbox())
21302 this.markInvalid();
21306 validateRadio : function()
21308 if(this.getVisibilityEl().hasClass('hidden')){
21312 if(this.allowBlank){
21318 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21319 if(!e.dom.checked){
21331 validateCheckbox : function()
21334 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21335 //return (this.getValue() == this.inputValue) ? true : false;
21338 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21346 for(var i in group){
21347 if(group[i].el.isVisible(true)){
21355 for(var i in group){
21360 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21367 * Mark this field as valid
21369 markValid : function()
21373 this.fireEvent('valid', this);
21375 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21378 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21385 if(this.inputType == 'radio'){
21386 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21387 var fg = e.findParent('.form-group', false, true);
21388 if (Roo.bootstrap.version == 3) {
21389 fg.removeClass([_this.invalidClass, _this.validClass]);
21390 fg.addClass(_this.validClass);
21392 fg.removeClass(['is-valid', 'is-invalid']);
21393 fg.addClass('is-valid');
21401 var fg = this.el.findParent('.form-group', false, true);
21402 if (Roo.bootstrap.version == 3) {
21403 fg.removeClass([this.invalidClass, this.validClass]);
21404 fg.addClass(this.validClass);
21406 fg.removeClass(['is-valid', 'is-invalid']);
21407 fg.addClass('is-valid');
21412 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21418 for(var i in group){
21419 var fg = group[i].el.findParent('.form-group', false, true);
21420 if (Roo.bootstrap.version == 3) {
21421 fg.removeClass([this.invalidClass, this.validClass]);
21422 fg.addClass(this.validClass);
21424 fg.removeClass(['is-valid', 'is-invalid']);
21425 fg.addClass('is-valid');
21431 * Mark this field as invalid
21432 * @param {String} msg The validation message
21434 markInvalid : function(msg)
21436 if(this.allowBlank){
21442 this.fireEvent('invalid', this, msg);
21444 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21447 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21451 label.markInvalid();
21454 if(this.inputType == 'radio'){
21456 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21457 var fg = e.findParent('.form-group', false, true);
21458 if (Roo.bootstrap.version == 3) {
21459 fg.removeClass([_this.invalidClass, _this.validClass]);
21460 fg.addClass(_this.invalidClass);
21462 fg.removeClass(['is-invalid', 'is-valid']);
21463 fg.addClass('is-invalid');
21471 var fg = this.el.findParent('.form-group', false, true);
21472 if (Roo.bootstrap.version == 3) {
21473 fg.removeClass([_this.invalidClass, _this.validClass]);
21474 fg.addClass(_this.invalidClass);
21476 fg.removeClass(['is-invalid', 'is-valid']);
21477 fg.addClass('is-invalid');
21482 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21488 for(var i in group){
21489 var fg = group[i].el.findParent('.form-group', false, true);
21490 if (Roo.bootstrap.version == 3) {
21491 fg.removeClass([_this.invalidClass, _this.validClass]);
21492 fg.addClass(_this.invalidClass);
21494 fg.removeClass(['is-invalid', 'is-valid']);
21495 fg.addClass('is-invalid');
21501 clearInvalid : function()
21503 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21505 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21507 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21509 if (label && label.iconEl) {
21510 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21511 label.iconEl.removeClass(['is-invalid', 'is-valid']);
21515 disable : function()
21517 if(this.inputType != 'radio'){
21518 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21525 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21526 _this.getActionEl().addClass(this.disabledClass);
21527 e.dom.disabled = true;
21531 this.disabled = true;
21532 this.fireEvent("disable", this);
21536 enable : function()
21538 if(this.inputType != 'radio'){
21539 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21546 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21547 _this.getActionEl().removeClass(this.disabledClass);
21548 e.dom.disabled = false;
21552 this.disabled = false;
21553 this.fireEvent("enable", this);
21557 setBoxLabel : function(v)
21562 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21568 Roo.apply(Roo.bootstrap.CheckBox, {
21573 * register a CheckBox Group
21574 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21576 register : function(checkbox)
21578 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21579 this.groups[checkbox.groupId] = {};
21582 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21586 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21590 * fetch a CheckBox Group based on the group ID
21591 * @param {string} the group ID
21592 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21594 get: function(groupId) {
21595 if (typeof(this.groups[groupId]) == 'undefined') {
21599 return this.groups[groupId] ;
21612 * @class Roo.bootstrap.Radio
21613 * @extends Roo.bootstrap.Component
21614 * Bootstrap Radio class
21615 * @cfg {String} boxLabel - the label associated
21616 * @cfg {String} value - the value of radio
21619 * Create a new Radio
21620 * @param {Object} config The config object
21622 Roo.bootstrap.Radio = function(config){
21623 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21627 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21633 getAutoCreate : function()
21637 cls : 'form-group radio',
21642 html : this.boxLabel
21650 initEvents : function()
21652 this.parent().register(this);
21654 this.el.on('click', this.onClick, this);
21658 onClick : function(e)
21660 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21661 this.setChecked(true);
21665 setChecked : function(state, suppressEvent)
21667 this.parent().setValue(this.value, suppressEvent);
21671 setBoxLabel : function(v)
21676 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21691 * @class Roo.bootstrap.SecurePass
21692 * @extends Roo.bootstrap.Input
21693 * Bootstrap SecurePass class
21697 * Create a new SecurePass
21698 * @param {Object} config The config object
21701 Roo.bootstrap.SecurePass = function (config) {
21702 // these go here, so the translation tool can replace them..
21704 PwdEmpty: "Please type a password, and then retype it to confirm.",
21705 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21706 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21707 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21708 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21709 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21710 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21711 TooWeak: "Your password is Too Weak."
21713 this.meterLabel = "Password strength:";
21714 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21715 this.meterClass = [
21716 "roo-password-meter-tooweak",
21717 "roo-password-meter-weak",
21718 "roo-password-meter-medium",
21719 "roo-password-meter-strong",
21720 "roo-password-meter-grey"
21725 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21728 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21730 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21732 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21733 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21734 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21735 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21736 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21737 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21738 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21748 * @cfg {String/Object} Label for the strength meter (defaults to
21749 * 'Password strength:')
21754 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21755 * ['Weak', 'Medium', 'Strong'])
21758 pwdStrengths: false,
21771 initEvents: function ()
21773 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21775 if (this.el.is('input[type=password]') && Roo.isSafari) {
21776 this.el.on('keydown', this.SafariOnKeyDown, this);
21779 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21782 onRender: function (ct, position)
21784 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21785 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21786 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21788 this.trigger.createChild({
21793 cls: 'roo-password-meter-grey col-xs-12',
21796 //width: this.meterWidth + 'px'
21800 cls: 'roo-password-meter-text'
21806 if (this.hideTrigger) {
21807 this.trigger.setDisplayed(false);
21809 this.setSize(this.width || '', this.height || '');
21812 onDestroy: function ()
21814 if (this.trigger) {
21815 this.trigger.removeAllListeners();
21816 this.trigger.remove();
21819 this.wrap.remove();
21821 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21824 checkStrength: function ()
21826 var pwd = this.inputEl().getValue();
21827 if (pwd == this._lastPwd) {
21832 if (this.ClientSideStrongPassword(pwd)) {
21834 } else if (this.ClientSideMediumPassword(pwd)) {
21836 } else if (this.ClientSideWeakPassword(pwd)) {
21842 Roo.log('strength1: ' + strength);
21844 //var pm = this.trigger.child('div/div/div').dom;
21845 var pm = this.trigger.child('div/div');
21846 pm.removeClass(this.meterClass);
21847 pm.addClass(this.meterClass[strength]);
21850 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21852 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21854 this._lastPwd = pwd;
21858 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21860 this._lastPwd = '';
21862 var pm = this.trigger.child('div/div');
21863 pm.removeClass(this.meterClass);
21864 pm.addClass('roo-password-meter-grey');
21867 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21870 this.inputEl().dom.type='password';
21873 validateValue: function (value)
21876 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21879 if (value.length == 0) {
21880 if (this.allowBlank) {
21881 this.clearInvalid();
21885 this.markInvalid(this.errors.PwdEmpty);
21886 this.errorMsg = this.errors.PwdEmpty;
21894 if ('[\x21-\x7e]*'.match(value)) {
21895 this.markInvalid(this.errors.PwdBadChar);
21896 this.errorMsg = this.errors.PwdBadChar;
21899 if (value.length < 6) {
21900 this.markInvalid(this.errors.PwdShort);
21901 this.errorMsg = this.errors.PwdShort;
21904 if (value.length > 16) {
21905 this.markInvalid(this.errors.PwdLong);
21906 this.errorMsg = this.errors.PwdLong;
21910 if (this.ClientSideStrongPassword(value)) {
21912 } else if (this.ClientSideMediumPassword(value)) {
21914 } else if (this.ClientSideWeakPassword(value)) {
21921 if (strength < 2) {
21922 //this.markInvalid(this.errors.TooWeak);
21923 this.errorMsg = this.errors.TooWeak;
21928 console.log('strength2: ' + strength);
21930 //var pm = this.trigger.child('div/div/div').dom;
21932 var pm = this.trigger.child('div/div');
21933 pm.removeClass(this.meterClass);
21934 pm.addClass(this.meterClass[strength]);
21936 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21938 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21940 this.errorMsg = '';
21944 CharacterSetChecks: function (type)
21947 this.fResult = false;
21950 isctype: function (character, type)
21953 case this.kCapitalLetter:
21954 if (character >= 'A' && character <= 'Z') {
21959 case this.kSmallLetter:
21960 if (character >= 'a' && character <= 'z') {
21966 if (character >= '0' && character <= '9') {
21971 case this.kPunctuation:
21972 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21983 IsLongEnough: function (pwd, size)
21985 return !(pwd == null || isNaN(size) || pwd.length < size);
21988 SpansEnoughCharacterSets: function (word, nb)
21990 if (!this.IsLongEnough(word, nb))
21995 var characterSetChecks = new Array(
21996 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21997 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
22000 for (var index = 0; index < word.length; ++index) {
22001 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22002 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
22003 characterSetChecks[nCharSet].fResult = true;
22010 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22011 if (characterSetChecks[nCharSet].fResult) {
22016 if (nCharSets < nb) {
22022 ClientSideStrongPassword: function (pwd)
22024 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22027 ClientSideMediumPassword: function (pwd)
22029 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22032 ClientSideWeakPassword: function (pwd)
22034 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22037 })//<script type="text/javascript">
22040 * Based Ext JS Library 1.1.1
22041 * Copyright(c) 2006-2007, Ext JS, LLC.
22047 * @class Roo.HtmlEditorCore
22048 * @extends Roo.Component
22049 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22051 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22054 Roo.HtmlEditorCore = function(config){
22057 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22062 * @event initialize
22063 * Fires when the editor is fully initialized (including the iframe)
22064 * @param {Roo.HtmlEditorCore} this
22069 * Fires when the editor is first receives the focus. Any insertion must wait
22070 * until after this event.
22071 * @param {Roo.HtmlEditorCore} this
22075 * @event beforesync
22076 * Fires before the textarea is updated with content from the editor iframe. Return false
22077 * to cancel the sync.
22078 * @param {Roo.HtmlEditorCore} this
22079 * @param {String} html
22083 * @event beforepush
22084 * Fires before the iframe editor is updated with content from the textarea. Return false
22085 * to cancel the push.
22086 * @param {Roo.HtmlEditorCore} this
22087 * @param {String} html
22092 * Fires when the textarea is updated with content from the editor iframe.
22093 * @param {Roo.HtmlEditorCore} this
22094 * @param {String} html
22099 * Fires when the iframe editor is updated with content from the textarea.
22100 * @param {Roo.HtmlEditorCore} this
22101 * @param {String} html
22106 * @event editorevent
22107 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22108 * @param {Roo.HtmlEditorCore} this
22114 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22116 // defaults : white / black...
22117 this.applyBlacklists();
22124 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
22128 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
22134 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22139 * @cfg {Number} height (in pixels)
22143 * @cfg {Number} width (in pixels)
22148 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22151 stylesheets: false,
22156 // private properties
22157 validationEvent : false,
22159 initialized : false,
22161 sourceEditMode : false,
22162 onFocus : Roo.emptyFn,
22164 hideMode:'offsets',
22168 // blacklist + whitelisted elements..
22175 * Protected method that will not generally be called directly. It
22176 * is called when the editor initializes the iframe with HTML contents. Override this method if you
22177 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22179 getDocMarkup : function(){
22183 // inherit styels from page...??
22184 if (this.stylesheets === false) {
22186 Roo.get(document.head).select('style').each(function(node) {
22187 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22190 Roo.get(document.head).select('link').each(function(node) {
22191 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22194 } else if (!this.stylesheets.length) {
22196 st = '<style type="text/css">' +
22197 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22200 st = '<style type="text/css">' +
22205 st += '<style type="text/css">' +
22206 'IMG { cursor: pointer } ' +
22209 var cls = 'roo-htmleditor-body';
22211 if(this.bodyCls.length){
22212 cls += ' ' + this.bodyCls;
22215 return '<html><head>' + st +
22216 //<style type="text/css">' +
22217 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22219 ' </head><body class="' + cls + '"></body></html>';
22223 onRender : function(ct, position)
22226 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22227 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22230 this.el.dom.style.border = '0 none';
22231 this.el.dom.setAttribute('tabIndex', -1);
22232 this.el.addClass('x-hidden hide');
22236 if(Roo.isIE){ // fix IE 1px bogus margin
22237 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22241 this.frameId = Roo.id();
22245 var iframe = this.owner.wrap.createChild({
22247 cls: 'form-control', // bootstrap..
22249 name: this.frameId,
22250 frameBorder : 'no',
22251 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
22256 this.iframe = iframe.dom;
22258 this.assignDocWin();
22260 this.doc.designMode = 'on';
22263 this.doc.write(this.getDocMarkup());
22267 var task = { // must defer to wait for browser to be ready
22269 //console.log("run task?" + this.doc.readyState);
22270 this.assignDocWin();
22271 if(this.doc.body || this.doc.readyState == 'complete'){
22273 this.doc.designMode="on";
22277 Roo.TaskMgr.stop(task);
22278 this.initEditor.defer(10, this);
22285 Roo.TaskMgr.start(task);
22290 onResize : function(w, h)
22292 Roo.log('resize: ' +w + ',' + h );
22293 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22297 if(typeof w == 'number'){
22299 this.iframe.style.width = w + 'px';
22301 if(typeof h == 'number'){
22303 this.iframe.style.height = h + 'px';
22305 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22312 * Toggles the editor between standard and source edit mode.
22313 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22315 toggleSourceEdit : function(sourceEditMode){
22317 this.sourceEditMode = sourceEditMode === true;
22319 if(this.sourceEditMode){
22321 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
22324 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22325 //this.iframe.className = '';
22328 //this.setSize(this.owner.wrap.getSize());
22329 //this.fireEvent('editmodechange', this, this.sourceEditMode);
22336 * Protected method that will not generally be called directly. If you need/want
22337 * custom HTML cleanup, this is the method you should override.
22338 * @param {String} html The HTML to be cleaned
22339 * return {String} The cleaned HTML
22341 cleanHtml : function(html){
22342 html = String(html);
22343 if(html.length > 5){
22344 if(Roo.isSafari){ // strip safari nonsense
22345 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22348 if(html == ' '){
22355 * HTML Editor -> Textarea
22356 * Protected method that will not generally be called directly. Syncs the contents
22357 * of the editor iframe with the textarea.
22359 syncValue : function(){
22360 if(this.initialized){
22361 var bd = (this.doc.body || this.doc.documentElement);
22362 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22363 var html = bd.innerHTML;
22365 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22366 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22368 html = '<div style="'+m[0]+'">' + html + '</div>';
22371 html = this.cleanHtml(html);
22372 // fix up the special chars.. normaly like back quotes in word...
22373 // however we do not want to do this with chinese..
22374 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
22376 var cc = match.charCodeAt();
22378 // Get the character value, handling surrogate pairs
22379 if (match.length == 2) {
22380 // It's a surrogate pair, calculate the Unicode code point
22381 var high = match.charCodeAt(0) - 0xD800;
22382 var low = match.charCodeAt(1) - 0xDC00;
22383 cc = (high * 0x400) + low + 0x10000;
22385 (cc >= 0x4E00 && cc < 0xA000 ) ||
22386 (cc >= 0x3400 && cc < 0x4E00 ) ||
22387 (cc >= 0xf900 && cc < 0xfb00 )
22392 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
22393 return "&#" + cc + ";";
22400 if(this.owner.fireEvent('beforesync', this, html) !== false){
22401 this.el.dom.value = html;
22402 this.owner.fireEvent('sync', this, html);
22408 * Protected method that will not generally be called directly. Pushes the value of the textarea
22409 * into the iframe editor.
22411 pushValue : function(){
22412 if(this.initialized){
22413 var v = this.el.dom.value.trim();
22415 // if(v.length < 1){
22419 if(this.owner.fireEvent('beforepush', this, v) !== false){
22420 var d = (this.doc.body || this.doc.documentElement);
22422 this.cleanUpPaste();
22423 this.el.dom.value = d.innerHTML;
22424 this.owner.fireEvent('push', this, v);
22430 deferFocus : function(){
22431 this.focus.defer(10, this);
22435 focus : function(){
22436 if(this.win && !this.sourceEditMode){
22443 assignDocWin: function()
22445 var iframe = this.iframe;
22448 this.doc = iframe.contentWindow.document;
22449 this.win = iframe.contentWindow;
22451 // if (!Roo.get(this.frameId)) {
22454 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22455 // this.win = Roo.get(this.frameId).dom.contentWindow;
22457 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22461 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22462 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22467 initEditor : function(){
22468 //console.log("INIT EDITOR");
22469 this.assignDocWin();
22473 this.doc.designMode="on";
22475 this.doc.write(this.getDocMarkup());
22478 var dbody = (this.doc.body || this.doc.documentElement);
22479 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22480 // this copies styles from the containing element into thsi one..
22481 // not sure why we need all of this..
22482 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22484 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22485 //ss['background-attachment'] = 'fixed'; // w3c
22486 dbody.bgProperties = 'fixed'; // ie
22487 //Roo.DomHelper.applyStyles(dbody, ss);
22488 Roo.EventManager.on(this.doc, {
22489 //'mousedown': this.onEditorEvent,
22490 'mouseup': this.onEditorEvent,
22491 'dblclick': this.onEditorEvent,
22492 'click': this.onEditorEvent,
22493 'keyup': this.onEditorEvent,
22498 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22500 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22501 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22503 this.initialized = true;
22505 this.owner.fireEvent('initialize', this);
22510 onDestroy : function(){
22516 //for (var i =0; i < this.toolbars.length;i++) {
22517 // // fixme - ask toolbars for heights?
22518 // this.toolbars[i].onDestroy();
22521 //this.wrap.dom.innerHTML = '';
22522 //this.wrap.remove();
22527 onFirstFocus : function(){
22529 this.assignDocWin();
22532 this.activated = true;
22535 if(Roo.isGecko){ // prevent silly gecko errors
22537 var s = this.win.getSelection();
22538 if(!s.focusNode || s.focusNode.nodeType != 3){
22539 var r = s.getRangeAt(0);
22540 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22545 this.execCmd('useCSS', true);
22546 this.execCmd('styleWithCSS', false);
22549 this.owner.fireEvent('activate', this);
22553 adjustFont: function(btn){
22554 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22555 //if(Roo.isSafari){ // safari
22558 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22559 if(Roo.isSafari){ // safari
22560 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22561 v = (v < 10) ? 10 : v;
22562 v = (v > 48) ? 48 : v;
22563 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22568 v = Math.max(1, v+adjust);
22570 this.execCmd('FontSize', v );
22573 onEditorEvent : function(e)
22575 this.owner.fireEvent('editorevent', this, e);
22576 // this.updateToolbar();
22577 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22580 insertTag : function(tg)
22582 // could be a bit smarter... -> wrap the current selected tRoo..
22583 if (tg.toLowerCase() == 'span' ||
22584 tg.toLowerCase() == 'code' ||
22585 tg.toLowerCase() == 'sup' ||
22586 tg.toLowerCase() == 'sub'
22589 range = this.createRange(this.getSelection());
22590 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22591 wrappingNode.appendChild(range.extractContents());
22592 range.insertNode(wrappingNode);
22599 this.execCmd("formatblock", tg);
22603 insertText : function(txt)
22607 var range = this.createRange();
22608 range.deleteContents();
22609 //alert(Sender.getAttribute('label'));
22611 range.insertNode(this.doc.createTextNode(txt));
22617 * Executes a Midas editor command on the editor document and performs necessary focus and
22618 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22619 * @param {String} cmd The Midas command
22620 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22622 relayCmd : function(cmd, value){
22624 this.execCmd(cmd, value);
22625 this.owner.fireEvent('editorevent', this);
22626 //this.updateToolbar();
22627 this.owner.deferFocus();
22631 * Executes a Midas editor command directly on the editor document.
22632 * For visual commands, you should use {@link #relayCmd} instead.
22633 * <b>This should only be called after the editor is initialized.</b>
22634 * @param {String} cmd The Midas command
22635 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22637 execCmd : function(cmd, value){
22638 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22645 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22647 * @param {String} text | dom node..
22649 insertAtCursor : function(text)
22652 if(!this.activated){
22658 var r = this.doc.selection.createRange();
22669 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22673 // from jquery ui (MIT licenced)
22675 var win = this.win;
22677 if (win.getSelection && win.getSelection().getRangeAt) {
22678 range = win.getSelection().getRangeAt(0);
22679 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22680 range.insertNode(node);
22681 } else if (win.document.selection && win.document.selection.createRange) {
22682 // no firefox support
22683 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22684 win.document.selection.createRange().pasteHTML(txt);
22686 // no firefox support
22687 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22688 this.execCmd('InsertHTML', txt);
22697 mozKeyPress : function(e){
22699 var c = e.getCharCode(), cmd;
22702 c = String.fromCharCode(c).toLowerCase();
22716 this.cleanUpPaste.defer(100, this);
22724 e.preventDefault();
22732 fixKeys : function(){ // load time branching for fastest keydown performance
22734 return function(e){
22735 var k = e.getKey(), r;
22738 r = this.doc.selection.createRange();
22741 r.pasteHTML('    ');
22748 r = this.doc.selection.createRange();
22750 var target = r.parentElement();
22751 if(!target || target.tagName.toLowerCase() != 'li'){
22753 r.pasteHTML('<br />');
22759 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22760 this.cleanUpPaste.defer(100, this);
22766 }else if(Roo.isOpera){
22767 return function(e){
22768 var k = e.getKey();
22772 this.execCmd('InsertHTML','    ');
22775 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22776 this.cleanUpPaste.defer(100, this);
22781 }else if(Roo.isSafari){
22782 return function(e){
22783 var k = e.getKey();
22787 this.execCmd('InsertText','\t');
22791 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22792 this.cleanUpPaste.defer(100, this);
22800 getAllAncestors: function()
22802 var p = this.getSelectedNode();
22805 a.push(p); // push blank onto stack..
22806 p = this.getParentElement();
22810 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22814 a.push(this.doc.body);
22818 lastSelNode : false,
22821 getSelection : function()
22823 this.assignDocWin();
22824 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22827 getSelectedNode: function()
22829 // this may only work on Gecko!!!
22831 // should we cache this!!!!
22836 var range = this.createRange(this.getSelection()).cloneRange();
22839 var parent = range.parentElement();
22841 var testRange = range.duplicate();
22842 testRange.moveToElementText(parent);
22843 if (testRange.inRange(range)) {
22846 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22849 parent = parent.parentElement;
22854 // is ancestor a text element.
22855 var ac = range.commonAncestorContainer;
22856 if (ac.nodeType == 3) {
22857 ac = ac.parentNode;
22860 var ar = ac.childNodes;
22863 var other_nodes = [];
22864 var has_other_nodes = false;
22865 for (var i=0;i<ar.length;i++) {
22866 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22869 // fullly contained node.
22871 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22876 // probably selected..
22877 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22878 other_nodes.push(ar[i]);
22882 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22887 has_other_nodes = true;
22889 if (!nodes.length && other_nodes.length) {
22890 nodes= other_nodes;
22892 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22898 createRange: function(sel)
22900 // this has strange effects when using with
22901 // top toolbar - not sure if it's a great idea.
22902 //this.editor.contentWindow.focus();
22903 if (typeof sel != "undefined") {
22905 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22907 return this.doc.createRange();
22910 return this.doc.createRange();
22913 getParentElement: function()
22916 this.assignDocWin();
22917 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22919 var range = this.createRange(sel);
22922 var p = range.commonAncestorContainer;
22923 while (p.nodeType == 3) { // text node
22934 * Range intersection.. the hard stuff...
22938 * [ -- selected range --- ]
22942 * if end is before start or hits it. fail.
22943 * if start is after end or hits it fail.
22945 * if either hits (but other is outside. - then it's not
22951 // @see http://www.thismuchiknow.co.uk/?p=64.
22952 rangeIntersectsNode : function(range, node)
22954 var nodeRange = node.ownerDocument.createRange();
22956 nodeRange.selectNode(node);
22958 nodeRange.selectNodeContents(node);
22961 var rangeStartRange = range.cloneRange();
22962 rangeStartRange.collapse(true);
22964 var rangeEndRange = range.cloneRange();
22965 rangeEndRange.collapse(false);
22967 var nodeStartRange = nodeRange.cloneRange();
22968 nodeStartRange.collapse(true);
22970 var nodeEndRange = nodeRange.cloneRange();
22971 nodeEndRange.collapse(false);
22973 return rangeStartRange.compareBoundaryPoints(
22974 Range.START_TO_START, nodeEndRange) == -1 &&
22975 rangeEndRange.compareBoundaryPoints(
22976 Range.START_TO_START, nodeStartRange) == 1;
22980 rangeCompareNode : function(range, node)
22982 var nodeRange = node.ownerDocument.createRange();
22984 nodeRange.selectNode(node);
22986 nodeRange.selectNodeContents(node);
22990 range.collapse(true);
22992 nodeRange.collapse(true);
22994 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22995 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22997 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22999 var nodeIsBefore = ss == 1;
23000 var nodeIsAfter = ee == -1;
23002 if (nodeIsBefore && nodeIsAfter) {
23005 if (!nodeIsBefore && nodeIsAfter) {
23006 return 1; //right trailed.
23009 if (nodeIsBefore && !nodeIsAfter) {
23010 return 2; // left trailed.
23016 // private? - in a new class?
23017 cleanUpPaste : function()
23019 // cleans up the whole document..
23020 Roo.log('cleanuppaste');
23022 this.cleanUpChildren(this.doc.body);
23023 var clean = this.cleanWordChars(this.doc.body.innerHTML);
23024 if (clean != this.doc.body.innerHTML) {
23025 this.doc.body.innerHTML = clean;
23030 cleanWordChars : function(input) {// change the chars to hex code
23031 var he = Roo.HtmlEditorCore;
23033 var output = input;
23034 Roo.each(he.swapCodes, function(sw) {
23035 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23037 output = output.replace(swapper, sw[1]);
23044 cleanUpChildren : function (n)
23046 if (!n.childNodes.length) {
23049 for (var i = n.childNodes.length-1; i > -1 ; i--) {
23050 this.cleanUpChild(n.childNodes[i]);
23057 cleanUpChild : function (node)
23060 //console.log(node);
23061 if (node.nodeName == "#text") {
23062 // clean up silly Windows -- stuff?
23065 if (node.nodeName == "#comment") {
23066 node.parentNode.removeChild(node);
23067 // clean up silly Windows -- stuff?
23070 var lcname = node.tagName.toLowerCase();
23071 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23072 // whitelist of tags..
23074 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23076 node.parentNode.removeChild(node);
23081 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23083 // spans with no attributes - just remove them..
23084 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
23085 remove_keep_children = true;
23088 // remove <a name=....> as rendering on yahoo mailer is borked with this.
23089 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23091 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23092 // remove_keep_children = true;
23095 if (remove_keep_children) {
23096 this.cleanUpChildren(node);
23097 // inserts everything just before this node...
23098 while (node.childNodes.length) {
23099 var cn = node.childNodes[0];
23100 node.removeChild(cn);
23101 node.parentNode.insertBefore(cn, node);
23103 node.parentNode.removeChild(node);
23107 if (!node.attributes || !node.attributes.length) {
23112 this.cleanUpChildren(node);
23116 function cleanAttr(n,v)
23119 if (v.match(/^\./) || v.match(/^\//)) {
23122 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23125 if (v.match(/^#/)) {
23128 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23129 node.removeAttribute(n);
23133 var cwhite = this.cwhite;
23134 var cblack = this.cblack;
23136 function cleanStyle(n,v)
23138 if (v.match(/expression/)) { //XSS?? should we even bother..
23139 node.removeAttribute(n);
23143 var parts = v.split(/;/);
23146 Roo.each(parts, function(p) {
23147 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23151 var l = p.split(':').shift().replace(/\s+/g,'');
23152 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23154 if ( cwhite.length && cblack.indexOf(l) > -1) {
23155 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23156 //node.removeAttribute(n);
23160 // only allow 'c whitelisted system attributes'
23161 if ( cwhite.length && cwhite.indexOf(l) < 0) {
23162 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23163 //node.removeAttribute(n);
23173 if (clean.length) {
23174 node.setAttribute(n, clean.join(';'));
23176 node.removeAttribute(n);
23182 for (var i = node.attributes.length-1; i > -1 ; i--) {
23183 var a = node.attributes[i];
23186 if (a.name.toLowerCase().substr(0,2)=='on') {
23187 node.removeAttribute(a.name);
23190 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23191 node.removeAttribute(a.name);
23194 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23195 cleanAttr(a.name,a.value); // fixme..
23198 if (a.name == 'style') {
23199 cleanStyle(a.name,a.value);
23202 /// clean up MS crap..
23203 // tecnically this should be a list of valid class'es..
23206 if (a.name == 'class') {
23207 if (a.value.match(/^Mso/)) {
23208 node.removeAttribute('class');
23211 if (a.value.match(/^body$/)) {
23212 node.removeAttribute('class');
23223 this.cleanUpChildren(node);
23229 * Clean up MS wordisms...
23231 cleanWord : function(node)
23234 this.cleanWord(this.doc.body);
23239 node.nodeName == 'SPAN' &&
23240 !node.hasAttributes() &&
23241 node.childNodes.length == 1 &&
23242 node.firstChild.nodeName == "#text"
23244 var textNode = node.firstChild;
23245 node.removeChild(textNode);
23246 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
23247 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
23249 node.parentNode.insertBefore(textNode, node);
23250 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
23251 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
23253 node.parentNode.removeChild(node);
23256 if (node.nodeName == "#text") {
23257 // clean up silly Windows -- stuff?
23260 if (node.nodeName == "#comment") {
23261 node.parentNode.removeChild(node);
23262 // clean up silly Windows -- stuff?
23266 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23267 node.parentNode.removeChild(node);
23270 //Roo.log(node.tagName);
23271 // remove - but keep children..
23272 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
23273 //Roo.log('-- removed');
23274 while (node.childNodes.length) {
23275 var cn = node.childNodes[0];
23276 node.removeChild(cn);
23277 node.parentNode.insertBefore(cn, node);
23278 // move node to parent - and clean it..
23279 this.cleanWord(cn);
23281 node.parentNode.removeChild(node);
23282 /// no need to iterate chidlren = it's got none..
23283 //this.iterateChildren(node, this.cleanWord);
23287 if (node.className.length) {
23289 var cn = node.className.split(/\W+/);
23291 Roo.each(cn, function(cls) {
23292 if (cls.match(/Mso[a-zA-Z]+/)) {
23297 node.className = cna.length ? cna.join(' ') : '';
23299 node.removeAttribute("class");
23303 if (node.hasAttribute("lang")) {
23304 node.removeAttribute("lang");
23307 if (node.hasAttribute("style")) {
23309 var styles = node.getAttribute("style").split(";");
23311 Roo.each(styles, function(s) {
23312 if (!s.match(/:/)) {
23315 var kv = s.split(":");
23316 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23319 // what ever is left... we allow.
23322 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23323 if (!nstyle.length) {
23324 node.removeAttribute('style');
23327 this.iterateChildren(node, this.cleanWord);
23333 * iterateChildren of a Node, calling fn each time, using this as the scole..
23334 * @param {DomNode} node node to iterate children of.
23335 * @param {Function} fn method of this class to call on each item.
23337 iterateChildren : function(node, fn)
23339 if (!node.childNodes.length) {
23342 for (var i = node.childNodes.length-1; i > -1 ; i--) {
23343 fn.call(this, node.childNodes[i])
23349 * cleanTableWidths.
23351 * Quite often pasting from word etc.. results in tables with column and widths.
23352 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23355 cleanTableWidths : function(node)
23360 this.cleanTableWidths(this.doc.body);
23365 if (node.nodeName == "#text" || node.nodeName == "#comment") {
23368 Roo.log(node.tagName);
23369 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23370 this.iterateChildren(node, this.cleanTableWidths);
23373 if (node.hasAttribute('width')) {
23374 node.removeAttribute('width');
23378 if (node.hasAttribute("style")) {
23381 var styles = node.getAttribute("style").split(";");
23383 Roo.each(styles, function(s) {
23384 if (!s.match(/:/)) {
23387 var kv = s.split(":");
23388 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23391 // what ever is left... we allow.
23394 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23395 if (!nstyle.length) {
23396 node.removeAttribute('style');
23400 this.iterateChildren(node, this.cleanTableWidths);
23408 domToHTML : function(currentElement, depth, nopadtext) {
23410 depth = depth || 0;
23411 nopadtext = nopadtext || false;
23413 if (!currentElement) {
23414 return this.domToHTML(this.doc.body);
23417 //Roo.log(currentElement);
23419 var allText = false;
23420 var nodeName = currentElement.nodeName;
23421 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23423 if (nodeName == '#text') {
23425 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23430 if (nodeName != 'BODY') {
23433 // Prints the node tagName, such as <A>, <IMG>, etc
23436 for(i = 0; i < currentElement.attributes.length;i++) {
23438 var aname = currentElement.attributes.item(i).name;
23439 if (!currentElement.attributes.item(i).value.length) {
23442 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23445 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23454 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23457 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23462 // Traverse the tree
23464 var currentElementChild = currentElement.childNodes.item(i);
23465 var allText = true;
23466 var innerHTML = '';
23468 while (currentElementChild) {
23469 // Formatting code (indent the tree so it looks nice on the screen)
23470 var nopad = nopadtext;
23471 if (lastnode == 'SPAN') {
23475 if (currentElementChild.nodeName == '#text') {
23476 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23477 toadd = nopadtext ? toadd : toadd.trim();
23478 if (!nopad && toadd.length > 80) {
23479 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23481 innerHTML += toadd;
23484 currentElementChild = currentElement.childNodes.item(i);
23490 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23492 // Recursively traverse the tree structure of the child node
23493 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23494 lastnode = currentElementChild.nodeName;
23496 currentElementChild=currentElement.childNodes.item(i);
23502 // The remaining code is mostly for formatting the tree
23503 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23508 ret+= "</"+tagName+">";
23514 applyBlacklists : function()
23516 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23517 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23521 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23522 if (b.indexOf(tag) > -1) {
23525 this.white.push(tag);
23529 Roo.each(w, function(tag) {
23530 if (b.indexOf(tag) > -1) {
23533 if (this.white.indexOf(tag) > -1) {
23536 this.white.push(tag);
23541 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23542 if (w.indexOf(tag) > -1) {
23545 this.black.push(tag);
23549 Roo.each(b, function(tag) {
23550 if (w.indexOf(tag) > -1) {
23553 if (this.black.indexOf(tag) > -1) {
23556 this.black.push(tag);
23561 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23562 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23566 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23567 if (b.indexOf(tag) > -1) {
23570 this.cwhite.push(tag);
23574 Roo.each(w, function(tag) {
23575 if (b.indexOf(tag) > -1) {
23578 if (this.cwhite.indexOf(tag) > -1) {
23581 this.cwhite.push(tag);
23586 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23587 if (w.indexOf(tag) > -1) {
23590 this.cblack.push(tag);
23594 Roo.each(b, function(tag) {
23595 if (w.indexOf(tag) > -1) {
23598 if (this.cblack.indexOf(tag) > -1) {
23601 this.cblack.push(tag);
23606 setStylesheets : function(stylesheets)
23608 if(typeof(stylesheets) == 'string'){
23609 Roo.get(this.iframe.contentDocument.head).createChild({
23611 rel : 'stylesheet',
23620 Roo.each(stylesheets, function(s) {
23625 Roo.get(_this.iframe.contentDocument.head).createChild({
23627 rel : 'stylesheet',
23636 removeStylesheets : function()
23640 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23645 setStyle : function(style)
23647 Roo.get(this.iframe.contentDocument.head).createChild({
23656 // hide stuff that is not compatible
23670 * @event specialkey
23674 * @cfg {String} fieldClass @hide
23677 * @cfg {String} focusClass @hide
23680 * @cfg {String} autoCreate @hide
23683 * @cfg {String} inputType @hide
23686 * @cfg {String} invalidClass @hide
23689 * @cfg {String} invalidText @hide
23692 * @cfg {String} msgFx @hide
23695 * @cfg {String} validateOnBlur @hide
23699 Roo.HtmlEditorCore.white = [
23700 'area', 'br', 'img', 'input', 'hr', 'wbr',
23702 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23703 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23704 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23705 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23706 'table', 'ul', 'xmp',
23708 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23711 'dir', 'menu', 'ol', 'ul', 'dl',
23717 Roo.HtmlEditorCore.black = [
23718 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23720 'base', 'basefont', 'bgsound', 'blink', 'body',
23721 'frame', 'frameset', 'head', 'html', 'ilayer',
23722 'iframe', 'layer', 'link', 'meta', 'object',
23723 'script', 'style' ,'title', 'xml' // clean later..
23725 Roo.HtmlEditorCore.clean = [
23726 'script', 'style', 'title', 'xml'
23728 Roo.HtmlEditorCore.remove = [
23733 Roo.HtmlEditorCore.ablack = [
23737 Roo.HtmlEditorCore.aclean = [
23738 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23742 Roo.HtmlEditorCore.pwhite= [
23743 'http', 'https', 'mailto'
23746 // white listed style attributes.
23747 Roo.HtmlEditorCore.cwhite= [
23748 // 'text-align', /// default is to allow most things..
23754 // black listed style attributes.
23755 Roo.HtmlEditorCore.cblack= [
23756 // 'font-size' -- this can be set by the project
23760 Roo.HtmlEditorCore.swapCodes =[
23779 * @class Roo.bootstrap.HtmlEditor
23780 * @extends Roo.bootstrap.TextArea
23781 * Bootstrap HtmlEditor class
23784 * Create a new HtmlEditor
23785 * @param {Object} config The config object
23788 Roo.bootstrap.HtmlEditor = function(config){
23789 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23790 if (!this.toolbars) {
23791 this.toolbars = [];
23794 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23797 * @event initialize
23798 * Fires when the editor is fully initialized (including the iframe)
23799 * @param {HtmlEditor} this
23804 * Fires when the editor is first receives the focus. Any insertion must wait
23805 * until after this event.
23806 * @param {HtmlEditor} this
23810 * @event beforesync
23811 * Fires before the textarea is updated with content from the editor iframe. Return false
23812 * to cancel the sync.
23813 * @param {HtmlEditor} this
23814 * @param {String} html
23818 * @event beforepush
23819 * Fires before the iframe editor is updated with content from the textarea. Return false
23820 * to cancel the push.
23821 * @param {HtmlEditor} this
23822 * @param {String} html
23827 * Fires when the textarea is updated with content from the editor iframe.
23828 * @param {HtmlEditor} this
23829 * @param {String} html
23834 * Fires when the iframe editor is updated with content from the textarea.
23835 * @param {HtmlEditor} this
23836 * @param {String} html
23840 * @event editmodechange
23841 * Fires when the editor switches edit modes
23842 * @param {HtmlEditor} this
23843 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23845 editmodechange: true,
23847 * @event editorevent
23848 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23849 * @param {HtmlEditor} this
23853 * @event firstfocus
23854 * Fires when on first focus - needed by toolbars..
23855 * @param {HtmlEditor} this
23860 * Auto save the htmlEditor value as a file into Events
23861 * @param {HtmlEditor} this
23865 * @event savedpreview
23866 * preview the saved version of htmlEditor
23867 * @param {HtmlEditor} this
23874 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23878 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23883 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23888 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23893 * @cfg {Number} height (in pixels)
23897 * @cfg {Number} width (in pixels)
23902 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23905 stylesheets: false,
23910 // private properties
23911 validationEvent : false,
23913 initialized : false,
23916 onFocus : Roo.emptyFn,
23918 hideMode:'offsets',
23920 tbContainer : false,
23924 toolbarContainer :function() {
23925 return this.wrap.select('.x-html-editor-tb',true).first();
23929 * Protected method that will not generally be called directly. It
23930 * is called when the editor creates its toolbar. Override this method if you need to
23931 * add custom toolbar buttons.
23932 * @param {HtmlEditor} editor
23934 createToolbar : function(){
23935 Roo.log('renewing');
23936 Roo.log("create toolbars");
23938 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23939 this.toolbars[0].render(this.toolbarContainer());
23943 // if (!editor.toolbars || !editor.toolbars.length) {
23944 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23947 // for (var i =0 ; i < editor.toolbars.length;i++) {
23948 // editor.toolbars[i] = Roo.factory(
23949 // typeof(editor.toolbars[i]) == 'string' ?
23950 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23951 // Roo.bootstrap.HtmlEditor);
23952 // editor.toolbars[i].init(editor);
23958 onRender : function(ct, position)
23960 // Roo.log("Call onRender: " + this.xtype);
23962 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23964 this.wrap = this.inputEl().wrap({
23965 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23968 this.editorcore.onRender(ct, position);
23970 if (this.resizable) {
23971 this.resizeEl = new Roo.Resizable(this.wrap, {
23975 minHeight : this.height,
23976 height: this.height,
23977 handles : this.resizable,
23980 resize : function(r, w, h) {
23981 _t.onResize(w,h); // -something
23987 this.createToolbar(this);
23990 if(!this.width && this.resizable){
23991 this.setSize(this.wrap.getSize());
23993 if (this.resizeEl) {
23994 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23995 // should trigger onReize..
24001 onResize : function(w, h)
24003 Roo.log('resize: ' +w + ',' + h );
24004 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
24008 if(this.inputEl() ){
24009 if(typeof w == 'number'){
24010 var aw = w - this.wrap.getFrameWidth('lr');
24011 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
24014 if(typeof h == 'number'){
24015 var tbh = -11; // fixme it needs to tool bar size!
24016 for (var i =0; i < this.toolbars.length;i++) {
24017 // fixme - ask toolbars for heights?
24018 tbh += this.toolbars[i].el.getHeight();
24019 //if (this.toolbars[i].footer) {
24020 // tbh += this.toolbars[i].footer.el.getHeight();
24028 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24029 ah -= 5; // knock a few pixes off for look..
24030 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24034 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24035 this.editorcore.onResize(ew,eh);
24040 * Toggles the editor between standard and source edit mode.
24041 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24043 toggleSourceEdit : function(sourceEditMode)
24045 this.editorcore.toggleSourceEdit(sourceEditMode);
24047 if(this.editorcore.sourceEditMode){
24048 Roo.log('editor - showing textarea');
24051 // Roo.log(this.syncValue());
24053 this.inputEl().removeClass(['hide', 'x-hidden']);
24054 this.inputEl().dom.removeAttribute('tabIndex');
24055 this.inputEl().focus();
24057 Roo.log('editor - hiding textarea');
24059 // Roo.log(this.pushValue());
24062 this.inputEl().addClass(['hide', 'x-hidden']);
24063 this.inputEl().dom.setAttribute('tabIndex', -1);
24064 //this.deferFocus();
24067 if(this.resizable){
24068 this.setSize(this.wrap.getSize());
24071 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24074 // private (for BoxComponent)
24075 adjustSize : Roo.BoxComponent.prototype.adjustSize,
24077 // private (for BoxComponent)
24078 getResizeEl : function(){
24082 // private (for BoxComponent)
24083 getPositionEl : function(){
24088 initEvents : function(){
24089 this.originalValue = this.getValue();
24093 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24096 // markInvalid : Roo.emptyFn,
24098 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24101 // clearInvalid : Roo.emptyFn,
24103 setValue : function(v){
24104 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24105 this.editorcore.pushValue();
24110 deferFocus : function(){
24111 this.focus.defer(10, this);
24115 focus : function(){
24116 this.editorcore.focus();
24122 onDestroy : function(){
24128 for (var i =0; i < this.toolbars.length;i++) {
24129 // fixme - ask toolbars for heights?
24130 this.toolbars[i].onDestroy();
24133 this.wrap.dom.innerHTML = '';
24134 this.wrap.remove();
24139 onFirstFocus : function(){
24140 //Roo.log("onFirstFocus");
24141 this.editorcore.onFirstFocus();
24142 for (var i =0; i < this.toolbars.length;i++) {
24143 this.toolbars[i].onFirstFocus();
24149 syncValue : function()
24151 this.editorcore.syncValue();
24154 pushValue : function()
24156 this.editorcore.pushValue();
24160 // hide stuff that is not compatible
24174 * @event specialkey
24178 * @cfg {String} fieldClass @hide
24181 * @cfg {String} focusClass @hide
24184 * @cfg {String} autoCreate @hide
24187 * @cfg {String} inputType @hide
24191 * @cfg {String} invalidText @hide
24194 * @cfg {String} msgFx @hide
24197 * @cfg {String} validateOnBlur @hide
24206 Roo.namespace('Roo.bootstrap.htmleditor');
24208 * @class Roo.bootstrap.HtmlEditorToolbar1
24214 new Roo.bootstrap.HtmlEditor({
24217 new Roo.bootstrap.HtmlEditorToolbar1({
24218 disable : { fonts: 1 , format: 1, ..., ... , ...],
24224 * @cfg {Object} disable List of elements to disable..
24225 * @cfg {Array} btns List of additional buttons.
24229 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24232 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24235 Roo.apply(this, config);
24237 // default disabled, based on 'good practice'..
24238 this.disable = this.disable || {};
24239 Roo.applyIf(this.disable, {
24242 specialElements : true
24244 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24246 this.editor = config.editor;
24247 this.editorcore = config.editor.editorcore;
24249 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24251 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24252 // dont call parent... till later.
24254 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
24259 editorcore : false,
24264 "h1","h2","h3","h4","h5","h6",
24266 "abbr", "acronym", "address", "cite", "samp", "var",
24270 onRender : function(ct, position)
24272 // Roo.log("Call onRender: " + this.xtype);
24274 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24276 this.el.dom.style.marginBottom = '0';
24278 var editorcore = this.editorcore;
24279 var editor= this.editor;
24282 var btn = function(id,cmd , toggle, handler, html){
24284 var event = toggle ? 'toggle' : 'click';
24289 xns: Roo.bootstrap,
24293 enableToggle:toggle !== false,
24295 pressed : toggle ? false : null,
24298 a.listeners[toggle ? 'toggle' : 'click'] = function() {
24299 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
24305 // var cb_box = function...
24310 xns: Roo.bootstrap,
24315 xns: Roo.bootstrap,
24319 Roo.each(this.formats, function(f) {
24320 style.menu.items.push({
24322 xns: Roo.bootstrap,
24323 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24328 editorcore.insertTag(this.tagname);
24335 children.push(style);
24337 btn('bold',false,true);
24338 btn('italic',false,true);
24339 btn('align-left', 'justifyleft',true);
24340 btn('align-center', 'justifycenter',true);
24341 btn('align-right' , 'justifyright',true);
24342 btn('link', false, false, function(btn) {
24343 //Roo.log("create link?");
24344 var url = prompt(this.createLinkText, this.defaultLinkValue);
24345 if(url && url != 'http:/'+'/'){
24346 this.editorcore.relayCmd('createlink', url);
24349 btn('list','insertunorderedlist',true);
24350 btn('pencil', false,true, function(btn){
24352 this.toggleSourceEdit(btn.pressed);
24355 if (this.editor.btns.length > 0) {
24356 for (var i = 0; i<this.editor.btns.length; i++) {
24357 children.push(this.editor.btns[i]);
24365 xns: Roo.bootstrap,
24370 xns: Roo.bootstrap,
24375 cog.menu.items.push({
24377 xns: Roo.bootstrap,
24378 html : Clean styles,
24383 editorcore.insertTag(this.tagname);
24392 this.xtype = 'NavSimplebar';
24394 for(var i=0;i< children.length;i++) {
24396 this.buttons.add(this.addxtypeChild(children[i]));
24400 editor.on('editorevent', this.updateToolbar, this);
24402 onBtnClick : function(id)
24404 this.editorcore.relayCmd(id);
24405 this.editorcore.focus();
24409 * Protected method that will not generally be called directly. It triggers
24410 * a toolbar update by reading the markup state of the current selection in the editor.
24412 updateToolbar: function(){
24414 if(!this.editorcore.activated){
24415 this.editor.onFirstFocus(); // is this neeed?
24419 var btns = this.buttons;
24420 var doc = this.editorcore.doc;
24421 btns.get('bold').setActive(doc.queryCommandState('bold'));
24422 btns.get('italic').setActive(doc.queryCommandState('italic'));
24423 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24425 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24426 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24427 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24429 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24430 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24433 var ans = this.editorcore.getAllAncestors();
24434 if (this.formatCombo) {
24437 var store = this.formatCombo.store;
24438 this.formatCombo.setValue("");
24439 for (var i =0; i < ans.length;i++) {
24440 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24442 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24450 // hides menus... - so this cant be on a menu...
24451 Roo.bootstrap.MenuMgr.hideAll();
24453 Roo.bootstrap.MenuMgr.hideAll();
24454 //this.editorsyncValue();
24456 onFirstFocus: function() {
24457 this.buttons.each(function(item){
24461 toggleSourceEdit : function(sourceEditMode){
24464 if(sourceEditMode){
24465 Roo.log("disabling buttons");
24466 this.buttons.each( function(item){
24467 if(item.cmd != 'pencil'){
24473 Roo.log("enabling buttons");
24474 if(this.editorcore.initialized){
24475 this.buttons.each( function(item){
24481 Roo.log("calling toggole on editor");
24482 // tell the editor that it's been pressed..
24483 this.editor.toggleSourceEdit(sourceEditMode);
24493 * @class Roo.bootstrap.Table.AbstractSelectionModel
24494 * @extends Roo.util.Observable
24495 * Abstract base class for grid SelectionModels. It provides the interface that should be
24496 * implemented by descendant classes. This class should not be directly instantiated.
24499 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24500 this.locked = false;
24501 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24505 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24506 /** @ignore Called by the grid automatically. Do not call directly. */
24507 init : function(grid){
24513 * Locks the selections.
24516 this.locked = true;
24520 * Unlocks the selections.
24522 unlock : function(){
24523 this.locked = false;
24527 * Returns true if the selections are locked.
24528 * @return {Boolean}
24530 isLocked : function(){
24531 return this.locked;
24535 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24536 * @class Roo.bootstrap.Table.RowSelectionModel
24537 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24538 * It supports multiple selections and keyboard selection/navigation.
24540 * @param {Object} config
24543 Roo.bootstrap.Table.RowSelectionModel = function(config){
24544 Roo.apply(this, config);
24545 this.selections = new Roo.util.MixedCollection(false, function(o){
24550 this.lastActive = false;
24554 * @event selectionchange
24555 * Fires when the selection changes
24556 * @param {SelectionModel} this
24558 "selectionchange" : true,
24560 * @event afterselectionchange
24561 * Fires after the selection changes (eg. by key press or clicking)
24562 * @param {SelectionModel} this
24564 "afterselectionchange" : true,
24566 * @event beforerowselect
24567 * Fires when a row is selected being selected, return false to cancel.
24568 * @param {SelectionModel} this
24569 * @param {Number} rowIndex The selected index
24570 * @param {Boolean} keepExisting False if other selections will be cleared
24572 "beforerowselect" : true,
24575 * Fires when a row is selected.
24576 * @param {SelectionModel} this
24577 * @param {Number} rowIndex The selected index
24578 * @param {Roo.data.Record} r The record
24580 "rowselect" : true,
24582 * @event rowdeselect
24583 * Fires when a row is deselected.
24584 * @param {SelectionModel} this
24585 * @param {Number} rowIndex The selected index
24587 "rowdeselect" : true
24589 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24590 this.locked = false;
24593 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24595 * @cfg {Boolean} singleSelect
24596 * True to allow selection of only one row at a time (defaults to false)
24598 singleSelect : false,
24601 initEvents : function()
24604 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24605 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24606 //}else{ // allow click to work like normal
24607 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24609 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24610 this.grid.on("rowclick", this.handleMouseDown, this);
24612 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24613 "up" : function(e){
24615 this.selectPrevious(e.shiftKey);
24616 }else if(this.last !== false && this.lastActive !== false){
24617 var last = this.last;
24618 this.selectRange(this.last, this.lastActive-1);
24619 this.grid.getView().focusRow(this.lastActive);
24620 if(last !== false){
24624 this.selectFirstRow();
24626 this.fireEvent("afterselectionchange", this);
24628 "down" : function(e){
24630 this.selectNext(e.shiftKey);
24631 }else if(this.last !== false && this.lastActive !== false){
24632 var last = this.last;
24633 this.selectRange(this.last, this.lastActive+1);
24634 this.grid.getView().focusRow(this.lastActive);
24635 if(last !== false){
24639 this.selectFirstRow();
24641 this.fireEvent("afterselectionchange", this);
24645 this.grid.store.on('load', function(){
24646 this.selections.clear();
24649 var view = this.grid.view;
24650 view.on("refresh", this.onRefresh, this);
24651 view.on("rowupdated", this.onRowUpdated, this);
24652 view.on("rowremoved", this.onRemove, this);
24657 onRefresh : function()
24659 var ds = this.grid.store, i, v = this.grid.view;
24660 var s = this.selections;
24661 s.each(function(r){
24662 if((i = ds.indexOfId(r.id)) != -1){
24671 onRemove : function(v, index, r){
24672 this.selections.remove(r);
24676 onRowUpdated : function(v, index, r){
24677 if(this.isSelected(r)){
24678 v.onRowSelect(index);
24684 * @param {Array} records The records to select
24685 * @param {Boolean} keepExisting (optional) True to keep existing selections
24687 selectRecords : function(records, keepExisting)
24690 this.clearSelections();
24692 var ds = this.grid.store;
24693 for(var i = 0, len = records.length; i < len; i++){
24694 this.selectRow(ds.indexOf(records[i]), true);
24699 * Gets the number of selected rows.
24702 getCount : function(){
24703 return this.selections.length;
24707 * Selects the first row in the grid.
24709 selectFirstRow : function(){
24714 * Select the last row.
24715 * @param {Boolean} keepExisting (optional) True to keep existing selections
24717 selectLastRow : function(keepExisting){
24718 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24719 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24723 * Selects the row immediately following the last selected row.
24724 * @param {Boolean} keepExisting (optional) True to keep existing selections
24726 selectNext : function(keepExisting)
24728 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24729 this.selectRow(this.last+1, keepExisting);
24730 this.grid.getView().focusRow(this.last);
24735 * Selects the row that precedes the last selected row.
24736 * @param {Boolean} keepExisting (optional) True to keep existing selections
24738 selectPrevious : function(keepExisting){
24740 this.selectRow(this.last-1, keepExisting);
24741 this.grid.getView().focusRow(this.last);
24746 * Returns the selected records
24747 * @return {Array} Array of selected records
24749 getSelections : function(){
24750 return [].concat(this.selections.items);
24754 * Returns the first selected record.
24757 getSelected : function(){
24758 return this.selections.itemAt(0);
24763 * Clears all selections.
24765 clearSelections : function(fast)
24771 var ds = this.grid.store;
24772 var s = this.selections;
24773 s.each(function(r){
24774 this.deselectRow(ds.indexOfId(r.id));
24778 this.selections.clear();
24785 * Selects all rows.
24787 selectAll : function(){
24791 this.selections.clear();
24792 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24793 this.selectRow(i, true);
24798 * Returns True if there is a selection.
24799 * @return {Boolean}
24801 hasSelection : function(){
24802 return this.selections.length > 0;
24806 * Returns True if the specified row is selected.
24807 * @param {Number/Record} record The record or index of the record to check
24808 * @return {Boolean}
24810 isSelected : function(index){
24811 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24812 return (r && this.selections.key(r.id) ? true : false);
24816 * Returns True if the specified record id is selected.
24817 * @param {String} id The id of record to check
24818 * @return {Boolean}
24820 isIdSelected : function(id){
24821 return (this.selections.key(id) ? true : false);
24826 handleMouseDBClick : function(e, t){
24830 handleMouseDown : function(e, t)
24832 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24833 if(this.isLocked() || rowIndex < 0 ){
24836 if(e.shiftKey && this.last !== false){
24837 var last = this.last;
24838 this.selectRange(last, rowIndex, e.ctrlKey);
24839 this.last = last; // reset the last
24843 var isSelected = this.isSelected(rowIndex);
24844 //Roo.log("select row:" + rowIndex);
24846 this.deselectRow(rowIndex);
24848 this.selectRow(rowIndex, true);
24852 if(e.button !== 0 && isSelected){
24853 alert('rowIndex 2: ' + rowIndex);
24854 view.focusRow(rowIndex);
24855 }else if(e.ctrlKey && isSelected){
24856 this.deselectRow(rowIndex);
24857 }else if(!isSelected){
24858 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24859 view.focusRow(rowIndex);
24863 this.fireEvent("afterselectionchange", this);
24866 handleDragableRowClick : function(grid, rowIndex, e)
24868 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24869 this.selectRow(rowIndex, false);
24870 grid.view.focusRow(rowIndex);
24871 this.fireEvent("afterselectionchange", this);
24876 * Selects multiple rows.
24877 * @param {Array} rows Array of the indexes of the row to select
24878 * @param {Boolean} keepExisting (optional) True to keep existing selections
24880 selectRows : function(rows, keepExisting){
24882 this.clearSelections();
24884 for(var i = 0, len = rows.length; i < len; i++){
24885 this.selectRow(rows[i], true);
24890 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24891 * @param {Number} startRow The index of the first row in the range
24892 * @param {Number} endRow The index of the last row in the range
24893 * @param {Boolean} keepExisting (optional) True to retain existing selections
24895 selectRange : function(startRow, endRow, keepExisting){
24900 this.clearSelections();
24902 if(startRow <= endRow){
24903 for(var i = startRow; i <= endRow; i++){
24904 this.selectRow(i, true);
24907 for(var i = startRow; i >= endRow; i--){
24908 this.selectRow(i, true);
24914 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24915 * @param {Number} startRow The index of the first row in the range
24916 * @param {Number} endRow The index of the last row in the range
24918 deselectRange : function(startRow, endRow, preventViewNotify){
24922 for(var i = startRow; i <= endRow; i++){
24923 this.deselectRow(i, preventViewNotify);
24929 * @param {Number} row The index of the row to select
24930 * @param {Boolean} keepExisting (optional) True to keep existing selections
24932 selectRow : function(index, keepExisting, preventViewNotify)
24934 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24937 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24938 if(!keepExisting || this.singleSelect){
24939 this.clearSelections();
24942 var r = this.grid.store.getAt(index);
24943 //console.log('selectRow - record id :' + r.id);
24945 this.selections.add(r);
24946 this.last = this.lastActive = index;
24947 if(!preventViewNotify){
24948 var proxy = new Roo.Element(
24949 this.grid.getRowDom(index)
24951 proxy.addClass('bg-info info');
24953 this.fireEvent("rowselect", this, index, r);
24954 this.fireEvent("selectionchange", this);
24960 * @param {Number} row The index of the row to deselect
24962 deselectRow : function(index, preventViewNotify)
24967 if(this.last == index){
24970 if(this.lastActive == index){
24971 this.lastActive = false;
24974 var r = this.grid.store.getAt(index);
24979 this.selections.remove(r);
24980 //.console.log('deselectRow - record id :' + r.id);
24981 if(!preventViewNotify){
24983 var proxy = new Roo.Element(
24984 this.grid.getRowDom(index)
24986 proxy.removeClass('bg-info info');
24988 this.fireEvent("rowdeselect", this, index);
24989 this.fireEvent("selectionchange", this);
24993 restoreLast : function(){
24995 this.last = this._last;
25000 acceptsNav : function(row, col, cm){
25001 return !cm.isHidden(col) && cm.isCellEditable(col, row);
25005 onEditorKey : function(field, e){
25006 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
25011 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
25013 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
25015 }else if(k == e.ENTER && !e.ctrlKey){
25019 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
25021 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25023 }else if(k == e.ESC){
25027 g.startEditing(newCell[0], newCell[1]);
25033 * Ext JS Library 1.1.1
25034 * Copyright(c) 2006-2007, Ext JS, LLC.
25036 * Originally Released Under LGPL - original licence link has changed is not relivant.
25039 * <script type="text/javascript">
25043 * @class Roo.bootstrap.PagingToolbar
25044 * @extends Roo.bootstrap.NavSimplebar
25045 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
25047 * Create a new PagingToolbar
25048 * @param {Object} config The config object
25049 * @param {Roo.data.Store} store
25051 Roo.bootstrap.PagingToolbar = function(config)
25053 // old args format still supported... - xtype is prefered..
25054 // created from xtype...
25056 this.ds = config.dataSource;
25058 if (config.store && !this.ds) {
25059 this.store= Roo.factory(config.store, Roo.data);
25060 this.ds = this.store;
25061 this.ds.xmodule = this.xmodule || false;
25064 this.toolbarItems = [];
25065 if (config.items) {
25066 this.toolbarItems = config.items;
25069 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
25074 this.bind(this.ds);
25077 if (Roo.bootstrap.version == 4) {
25078 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
25080 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
25085 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
25087 * @cfg {Roo.data.Store} dataSource
25088 * The underlying data store providing the paged data
25091 * @cfg {String/HTMLElement/Element} container
25092 * container The id or element that will contain the toolbar
25095 * @cfg {Boolean} displayInfo
25096 * True to display the displayMsg (defaults to false)
25099 * @cfg {Number} pageSize
25100 * The number of records to display per page (defaults to 20)
25104 * @cfg {String} displayMsg
25105 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25107 displayMsg : 'Displaying {0} - {1} of {2}',
25109 * @cfg {String} emptyMsg
25110 * The message to display when no records are found (defaults to "No data to display")
25112 emptyMsg : 'No data to display',
25114 * Customizable piece of the default paging text (defaults to "Page")
25117 beforePageText : "Page",
25119 * Customizable piece of the default paging text (defaults to "of %0")
25122 afterPageText : "of {0}",
25124 * Customizable piece of the default paging text (defaults to "First Page")
25127 firstText : "First Page",
25129 * Customizable piece of the default paging text (defaults to "Previous Page")
25132 prevText : "Previous Page",
25134 * Customizable piece of the default paging text (defaults to "Next Page")
25137 nextText : "Next Page",
25139 * Customizable piece of the default paging text (defaults to "Last Page")
25142 lastText : "Last Page",
25144 * Customizable piece of the default paging text (defaults to "Refresh")
25147 refreshText : "Refresh",
25151 onRender : function(ct, position)
25153 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25154 this.navgroup.parentId = this.id;
25155 this.navgroup.onRender(this.el, null);
25156 // add the buttons to the navgroup
25158 if(this.displayInfo){
25159 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25160 this.displayEl = this.el.select('.x-paging-info', true).first();
25161 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25162 // this.displayEl = navel.el.select('span',true).first();
25168 Roo.each(_this.buttons, function(e){ // this might need to use render????
25169 Roo.factory(e).render(_this.el);
25173 Roo.each(_this.toolbarItems, function(e) {
25174 _this.navgroup.addItem(e);
25178 this.first = this.navgroup.addItem({
25179 tooltip: this.firstText,
25180 cls: "prev btn-outline-secondary",
25181 html : ' <i class="fa fa-step-backward"></i>',
25183 preventDefault: true,
25184 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25187 this.prev = this.navgroup.addItem({
25188 tooltip: this.prevText,
25189 cls: "prev btn-outline-secondary",
25190 html : ' <i class="fa fa-backward"></i>',
25192 preventDefault: true,
25193 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
25195 //this.addSeparator();
25198 var field = this.navgroup.addItem( {
25200 cls : 'x-paging-position btn-outline-secondary',
25202 html : this.beforePageText +
25203 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25204 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
25207 this.field = field.el.select('input', true).first();
25208 this.field.on("keydown", this.onPagingKeydown, this);
25209 this.field.on("focus", function(){this.dom.select();});
25212 this.afterTextEl = field.el.select('.x-paging-after',true).first();
25213 //this.field.setHeight(18);
25214 //this.addSeparator();
25215 this.next = this.navgroup.addItem({
25216 tooltip: this.nextText,
25217 cls: "next btn-outline-secondary",
25218 html : ' <i class="fa fa-forward"></i>',
25220 preventDefault: true,
25221 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
25223 this.last = this.navgroup.addItem({
25224 tooltip: this.lastText,
25225 html : ' <i class="fa fa-step-forward"></i>',
25226 cls: "next btn-outline-secondary",
25228 preventDefault: true,
25229 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
25231 //this.addSeparator();
25232 this.loading = this.navgroup.addItem({
25233 tooltip: this.refreshText,
25234 cls: "btn-outline-secondary",
25235 html : ' <i class="fa fa-refresh"></i>',
25236 preventDefault: true,
25237 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25243 updateInfo : function(){
25244 if(this.displayEl){
25245 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25246 var msg = count == 0 ?
25250 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
25252 this.displayEl.update(msg);
25257 onLoad : function(ds, r, o)
25259 this.cursor = o.params.start ? o.params.start : 0;
25261 var d = this.getPageData(),
25266 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25267 this.field.dom.value = ap;
25268 this.first.setDisabled(ap == 1);
25269 this.prev.setDisabled(ap == 1);
25270 this.next.setDisabled(ap == ps);
25271 this.last.setDisabled(ap == ps);
25272 this.loading.enable();
25277 getPageData : function(){
25278 var total = this.ds.getTotalCount();
25281 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25282 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25287 onLoadError : function(){
25288 this.loading.enable();
25292 onPagingKeydown : function(e){
25293 var k = e.getKey();
25294 var d = this.getPageData();
25296 var v = this.field.dom.value, pageNum;
25297 if(!v || isNaN(pageNum = parseInt(v, 10))){
25298 this.field.dom.value = d.activePage;
25301 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25302 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25305 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))
25307 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25308 this.field.dom.value = pageNum;
25309 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25312 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25314 var v = this.field.dom.value, pageNum;
25315 var increment = (e.shiftKey) ? 10 : 1;
25316 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25319 if(!v || isNaN(pageNum = parseInt(v, 10))) {
25320 this.field.dom.value = d.activePage;
25323 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25325 this.field.dom.value = parseInt(v, 10) + increment;
25326 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25327 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25334 beforeLoad : function(){
25336 this.loading.disable();
25341 onClick : function(which){
25350 ds.load({params:{start: 0, limit: this.pageSize}});
25353 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25356 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25359 var total = ds.getTotalCount();
25360 var extra = total % this.pageSize;
25361 var lastStart = extra ? (total - extra) : total-this.pageSize;
25362 ds.load({params:{start: lastStart, limit: this.pageSize}});
25365 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25371 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25372 * @param {Roo.data.Store} store The data store to unbind
25374 unbind : function(ds){
25375 ds.un("beforeload", this.beforeLoad, this);
25376 ds.un("load", this.onLoad, this);
25377 ds.un("loadexception", this.onLoadError, this);
25378 ds.un("remove", this.updateInfo, this);
25379 ds.un("add", this.updateInfo, this);
25380 this.ds = undefined;
25384 * Binds the paging toolbar to the specified {@link Roo.data.Store}
25385 * @param {Roo.data.Store} store The data store to bind
25387 bind : function(ds){
25388 ds.on("beforeload", this.beforeLoad, this);
25389 ds.on("load", this.onLoad, this);
25390 ds.on("loadexception", this.onLoadError, this);
25391 ds.on("remove", this.updateInfo, this);
25392 ds.on("add", this.updateInfo, this);
25403 * @class Roo.bootstrap.MessageBar
25404 * @extends Roo.bootstrap.Component
25405 * Bootstrap MessageBar class
25406 * @cfg {String} html contents of the MessageBar
25407 * @cfg {String} weight (info | success | warning | danger) default info
25408 * @cfg {String} beforeClass insert the bar before the given class
25409 * @cfg {Boolean} closable (true | false) default false
25410 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25413 * Create a new Element
25414 * @param {Object} config The config object
25417 Roo.bootstrap.MessageBar = function(config){
25418 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25421 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25427 beforeClass: 'bootstrap-sticky-wrap',
25429 getAutoCreate : function(){
25433 cls: 'alert alert-dismissable alert-' + this.weight,
25438 html: this.html || ''
25444 cfg.cls += ' alert-messages-fixed';
25458 onRender : function(ct, position)
25460 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25463 var cfg = Roo.apply({}, this.getAutoCreate());
25467 cfg.cls += ' ' + this.cls;
25470 cfg.style = this.style;
25472 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25474 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25477 this.el.select('>button.close').on('click', this.hide, this);
25483 if (!this.rendered) {
25489 this.fireEvent('show', this);
25495 if (!this.rendered) {
25501 this.fireEvent('hide', this);
25504 update : function()
25506 // var e = this.el.dom.firstChild;
25508 // if(this.closable){
25509 // e = e.nextSibling;
25512 // e.data = this.html || '';
25514 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25530 * @class Roo.bootstrap.Graph
25531 * @extends Roo.bootstrap.Component
25532 * Bootstrap Graph class
25536 @cfg {String} graphtype bar | vbar | pie
25537 @cfg {number} g_x coodinator | centre x (pie)
25538 @cfg {number} g_y coodinator | centre y (pie)
25539 @cfg {number} g_r radius (pie)
25540 @cfg {number} g_height height of the chart (respected by all elements in the set)
25541 @cfg {number} g_width width of the chart (respected by all elements in the set)
25542 @cfg {Object} title The title of the chart
25545 -opts (object) options for the chart
25547 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25548 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25550 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.
25551 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25553 o stretch (boolean)
25555 -opts (object) options for the pie
25558 o startAngle (number)
25559 o endAngle (number)
25563 * Create a new Input
25564 * @param {Object} config The config object
25567 Roo.bootstrap.Graph = function(config){
25568 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25574 * The img click event for the img.
25575 * @param {Roo.EventObject} e
25581 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25592 //g_colors: this.colors,
25599 getAutoCreate : function(){
25610 onRender : function(ct,position){
25613 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25615 if (typeof(Raphael) == 'undefined') {
25616 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25620 this.raphael = Raphael(this.el.dom);
25622 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25623 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25624 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25625 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25627 r.text(160, 10, "Single Series Chart").attr(txtattr);
25628 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25629 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25630 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25632 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25633 r.barchart(330, 10, 300, 220, data1);
25634 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25635 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25638 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25639 // r.barchart(30, 30, 560, 250, xdata, {
25640 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25641 // axis : "0 0 1 1",
25642 // axisxlabels : xdata
25643 // //yvalues : cols,
25646 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25648 // this.load(null,xdata,{
25649 // axis : "0 0 1 1",
25650 // axisxlabels : xdata
25655 load : function(graphtype,xdata,opts)
25657 this.raphael.clear();
25659 graphtype = this.graphtype;
25664 var r = this.raphael,
25665 fin = function () {
25666 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25668 fout = function () {
25669 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25671 pfin = function() {
25672 this.sector.stop();
25673 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25676 this.label[0].stop();
25677 this.label[0].attr({ r: 7.5 });
25678 this.label[1].attr({ "font-weight": 800 });
25681 pfout = function() {
25682 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25685 this.label[0].animate({ r: 5 }, 500, "bounce");
25686 this.label[1].attr({ "font-weight": 400 });
25692 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25695 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25698 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25699 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25701 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25708 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25713 setTitle: function(o)
25718 initEvents: function() {
25721 this.el.on('click', this.onClick, this);
25725 onClick : function(e)
25727 Roo.log('img onclick');
25728 this.fireEvent('click', this, e);
25740 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25743 * @class Roo.bootstrap.dash.NumberBox
25744 * @extends Roo.bootstrap.Component
25745 * Bootstrap NumberBox class
25746 * @cfg {String} headline Box headline
25747 * @cfg {String} content Box content
25748 * @cfg {String} icon Box icon
25749 * @cfg {String} footer Footer text
25750 * @cfg {String} fhref Footer href
25753 * Create a new NumberBox
25754 * @param {Object} config The config object
25758 Roo.bootstrap.dash.NumberBox = function(config){
25759 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25763 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25772 getAutoCreate : function(){
25776 cls : 'small-box ',
25784 cls : 'roo-headline',
25785 html : this.headline
25789 cls : 'roo-content',
25790 html : this.content
25804 cls : 'ion ' + this.icon
25813 cls : 'small-box-footer',
25814 href : this.fhref || '#',
25818 cfg.cn.push(footer);
25825 onRender : function(ct,position){
25826 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25833 setHeadline: function (value)
25835 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25838 setFooter: function (value, href)
25840 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25843 this.el.select('a.small-box-footer',true).first().attr('href', href);
25848 setContent: function (value)
25850 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25853 initEvents: function()
25867 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25870 * @class Roo.bootstrap.dash.TabBox
25871 * @extends Roo.bootstrap.Component
25872 * Bootstrap TabBox class
25873 * @cfg {String} title Title of the TabBox
25874 * @cfg {String} icon Icon of the TabBox
25875 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25876 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25879 * Create a new TabBox
25880 * @param {Object} config The config object
25884 Roo.bootstrap.dash.TabBox = function(config){
25885 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25890 * When a pane is added
25891 * @param {Roo.bootstrap.dash.TabPane} pane
25895 * @event activatepane
25896 * When a pane is activated
25897 * @param {Roo.bootstrap.dash.TabPane} pane
25899 "activatepane" : true
25907 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25912 tabScrollable : false,
25914 getChildContainer : function()
25916 return this.el.select('.tab-content', true).first();
25919 getAutoCreate : function(){
25923 cls: 'pull-left header',
25931 cls: 'fa ' + this.icon
25937 cls: 'nav nav-tabs pull-right',
25943 if(this.tabScrollable){
25950 cls: 'nav nav-tabs pull-right',
25961 cls: 'nav-tabs-custom',
25966 cls: 'tab-content no-padding',
25974 initEvents : function()
25976 //Roo.log('add add pane handler');
25977 this.on('addpane', this.onAddPane, this);
25980 * Updates the box title
25981 * @param {String} html to set the title to.
25983 setTitle : function(value)
25985 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25987 onAddPane : function(pane)
25989 this.panes.push(pane);
25990 //Roo.log('addpane');
25992 // tabs are rendere left to right..
25993 if(!this.showtabs){
25997 var ctr = this.el.select('.nav-tabs', true).first();
26000 var existing = ctr.select('.nav-tab',true);
26001 var qty = existing.getCount();;
26004 var tab = ctr.createChild({
26006 cls : 'nav-tab' + (qty ? '' : ' active'),
26014 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
26017 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
26019 pane.el.addClass('active');
26024 onTabClick : function(ev,un,ob,pane)
26026 //Roo.log('tab - prev default');
26027 ev.preventDefault();
26030 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26031 pane.tab.addClass('active');
26032 //Roo.log(pane.title);
26033 this.getChildContainer().select('.tab-pane',true).removeClass('active');
26034 // technically we should have a deactivate event.. but maybe add later.
26035 // and it should not de-activate the selected tab...
26036 this.fireEvent('activatepane', pane);
26037 pane.el.addClass('active');
26038 pane.fireEvent('activate');
26043 getActivePane : function()
26046 Roo.each(this.panes, function(p) {
26047 if(p.el.hasClass('active')){
26068 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26070 * @class Roo.bootstrap.TabPane
26071 * @extends Roo.bootstrap.Component
26072 * Bootstrap TabPane class
26073 * @cfg {Boolean} active (false | true) Default false
26074 * @cfg {String} title title of panel
26078 * Create a new TabPane
26079 * @param {Object} config The config object
26082 Roo.bootstrap.dash.TabPane = function(config){
26083 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
26089 * When a pane is activated
26090 * @param {Roo.bootstrap.dash.TabPane} pane
26097 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
26102 // the tabBox that this is attached to.
26105 getAutoCreate : function()
26113 cfg.cls += ' active';
26118 initEvents : function()
26120 //Roo.log('trigger add pane handler');
26121 this.parent().fireEvent('addpane', this)
26125 * Updates the tab title
26126 * @param {String} html to set the title to.
26128 setTitle: function(str)
26134 this.tab.select('a', true).first().dom.innerHTML = str;
26151 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26154 * @class Roo.bootstrap.menu.Menu
26155 * @extends Roo.bootstrap.Component
26156 * Bootstrap Menu class - container for Menu
26157 * @cfg {String} html Text of the menu
26158 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26159 * @cfg {String} icon Font awesome icon
26160 * @cfg {String} pos Menu align to (top | bottom) default bottom
26164 * Create a new Menu
26165 * @param {Object} config The config object
26169 Roo.bootstrap.menu.Menu = function(config){
26170 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26174 * @event beforeshow
26175 * Fires before this menu is displayed
26176 * @param {Roo.bootstrap.menu.Menu} this
26180 * @event beforehide
26181 * Fires before this menu is hidden
26182 * @param {Roo.bootstrap.menu.Menu} this
26187 * Fires after this menu is displayed
26188 * @param {Roo.bootstrap.menu.Menu} this
26193 * Fires after this menu is hidden
26194 * @param {Roo.bootstrap.menu.Menu} this
26199 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26200 * @param {Roo.bootstrap.menu.Menu} this
26201 * @param {Roo.EventObject} e
26208 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
26212 weight : 'default',
26217 getChildContainer : function() {
26218 if(this.isSubMenu){
26222 return this.el.select('ul.dropdown-menu', true).first();
26225 getAutoCreate : function()
26230 cls : 'roo-menu-text',
26238 cls : 'fa ' + this.icon
26249 cls : 'dropdown-button btn btn-' + this.weight,
26254 cls : 'dropdown-toggle btn btn-' + this.weight,
26264 cls : 'dropdown-menu'
26270 if(this.pos == 'top'){
26271 cfg.cls += ' dropup';
26274 if(this.isSubMenu){
26277 cls : 'dropdown-menu'
26284 onRender : function(ct, position)
26286 this.isSubMenu = ct.hasClass('dropdown-submenu');
26288 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26291 initEvents : function()
26293 if(this.isSubMenu){
26297 this.hidden = true;
26299 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26300 this.triggerEl.on('click', this.onTriggerPress, this);
26302 this.buttonEl = this.el.select('button.dropdown-button', true).first();
26303 this.buttonEl.on('click', this.onClick, this);
26309 if(this.isSubMenu){
26313 return this.el.select('ul.dropdown-menu', true).first();
26316 onClick : function(e)
26318 this.fireEvent("click", this, e);
26321 onTriggerPress : function(e)
26323 if (this.isVisible()) {
26330 isVisible : function(){
26331 return !this.hidden;
26336 this.fireEvent("beforeshow", this);
26338 this.hidden = false;
26339 this.el.addClass('open');
26341 Roo.get(document).on("mouseup", this.onMouseUp, this);
26343 this.fireEvent("show", this);
26350 this.fireEvent("beforehide", this);
26352 this.hidden = true;
26353 this.el.removeClass('open');
26355 Roo.get(document).un("mouseup", this.onMouseUp);
26357 this.fireEvent("hide", this);
26360 onMouseUp : function()
26374 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26377 * @class Roo.bootstrap.menu.Item
26378 * @extends Roo.bootstrap.Component
26379 * Bootstrap MenuItem class
26380 * @cfg {Boolean} submenu (true | false) default false
26381 * @cfg {String} html text of the item
26382 * @cfg {String} href the link
26383 * @cfg {Boolean} disable (true | false) default false
26384 * @cfg {Boolean} preventDefault (true | false) default true
26385 * @cfg {String} icon Font awesome icon
26386 * @cfg {String} pos Submenu align to (left | right) default right
26390 * Create a new Item
26391 * @param {Object} config The config object
26395 Roo.bootstrap.menu.Item = function(config){
26396 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26400 * Fires when the mouse is hovering over this menu
26401 * @param {Roo.bootstrap.menu.Item} this
26402 * @param {Roo.EventObject} e
26407 * Fires when the mouse exits this menu
26408 * @param {Roo.bootstrap.menu.Item} this
26409 * @param {Roo.EventObject} e
26415 * The raw click event for the entire grid.
26416 * @param {Roo.EventObject} e
26422 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26427 preventDefault: true,
26432 getAutoCreate : function()
26437 cls : 'roo-menu-item-text',
26445 cls : 'fa ' + this.icon
26454 href : this.href || '#',
26461 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26465 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26467 if(this.pos == 'left'){
26468 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26475 initEvents : function()
26477 this.el.on('mouseover', this.onMouseOver, this);
26478 this.el.on('mouseout', this.onMouseOut, this);
26480 this.el.select('a', true).first().on('click', this.onClick, this);
26484 onClick : function(e)
26486 if(this.preventDefault){
26487 e.preventDefault();
26490 this.fireEvent("click", this, e);
26493 onMouseOver : function(e)
26495 if(this.submenu && this.pos == 'left'){
26496 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26499 this.fireEvent("mouseover", this, e);
26502 onMouseOut : function(e)
26504 this.fireEvent("mouseout", this, e);
26516 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26519 * @class Roo.bootstrap.menu.Separator
26520 * @extends Roo.bootstrap.Component
26521 * Bootstrap Separator class
26524 * Create a new Separator
26525 * @param {Object} config The config object
26529 Roo.bootstrap.menu.Separator = function(config){
26530 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26533 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26535 getAutoCreate : function(){
26556 * @class Roo.bootstrap.Tooltip
26557 * Bootstrap Tooltip class
26558 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26559 * to determine which dom element triggers the tooltip.
26561 * It needs to add support for additional attributes like tooltip-position
26564 * Create a new Toolti
26565 * @param {Object} config The config object
26568 Roo.bootstrap.Tooltip = function(config){
26569 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26571 this.alignment = Roo.bootstrap.Tooltip.alignment;
26573 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26574 this.alignment = config.alignment;
26579 Roo.apply(Roo.bootstrap.Tooltip, {
26581 * @function init initialize tooltip monitoring.
26585 currentTip : false,
26586 currentRegion : false,
26592 Roo.get(document).on('mouseover', this.enter ,this);
26593 Roo.get(document).on('mouseout', this.leave, this);
26596 this.currentTip = new Roo.bootstrap.Tooltip();
26599 enter : function(ev)
26601 var dom = ev.getTarget();
26603 //Roo.log(['enter',dom]);
26604 var el = Roo.fly(dom);
26605 if (this.currentEl) {
26607 //Roo.log(this.currentEl);
26608 //Roo.log(this.currentEl.contains(dom));
26609 if (this.currentEl == el) {
26612 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26618 if (this.currentTip.el) {
26619 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26623 if(!el || el.dom == document){
26629 // you can not look for children, as if el is the body.. then everythign is the child..
26630 if (!el.attr('tooltip')) { //
26631 if (!el.select("[tooltip]").elements.length) {
26634 // is the mouse over this child...?
26635 bindEl = el.select("[tooltip]").first();
26636 var xy = ev.getXY();
26637 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26638 //Roo.log("not in region.");
26641 //Roo.log("child element over..");
26644 this.currentEl = bindEl;
26645 this.currentTip.bind(bindEl);
26646 this.currentRegion = Roo.lib.Region.getRegion(dom);
26647 this.currentTip.enter();
26650 leave : function(ev)
26652 var dom = ev.getTarget();
26653 //Roo.log(['leave',dom]);
26654 if (!this.currentEl) {
26659 if (dom != this.currentEl.dom) {
26662 var xy = ev.getXY();
26663 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26666 // only activate leave if mouse cursor is outside... bounding box..
26671 if (this.currentTip) {
26672 this.currentTip.leave();
26674 //Roo.log('clear currentEl');
26675 this.currentEl = false;
26680 'left' : ['r-l', [-2,0], 'right'],
26681 'right' : ['l-r', [2,0], 'left'],
26682 'bottom' : ['t-b', [0,2], 'top'],
26683 'top' : [ 'b-t', [0,-2], 'bottom']
26689 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26694 delay : null, // can be { show : 300 , hide: 500}
26698 hoverState : null, //???
26700 placement : 'bottom',
26704 getAutoCreate : function(){
26711 cls : 'tooltip-arrow'
26714 cls : 'tooltip-inner'
26721 bind : function(el)
26727 enter : function () {
26729 if (this.timeout != null) {
26730 clearTimeout(this.timeout);
26733 this.hoverState = 'in';
26734 //Roo.log("enter - show");
26735 if (!this.delay || !this.delay.show) {
26740 this.timeout = setTimeout(function () {
26741 if (_t.hoverState == 'in') {
26744 }, this.delay.show);
26748 clearTimeout(this.timeout);
26750 this.hoverState = 'out';
26751 if (!this.delay || !this.delay.hide) {
26757 this.timeout = setTimeout(function () {
26758 //Roo.log("leave - timeout");
26760 if (_t.hoverState == 'out') {
26762 Roo.bootstrap.Tooltip.currentEl = false;
26767 show : function (msg)
26770 this.render(document.body);
26773 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26775 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26777 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26779 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26781 var placement = typeof this.placement == 'function' ?
26782 this.placement.call(this, this.el, on_el) :
26785 var autoToken = /\s?auto?\s?/i;
26786 var autoPlace = autoToken.test(placement);
26788 placement = placement.replace(autoToken, '') || 'top';
26792 //this.el.setXY([0,0]);
26794 //this.el.dom.style.display='block';
26796 //this.el.appendTo(on_el);
26798 var p = this.getPosition();
26799 var box = this.el.getBox();
26805 var align = this.alignment[placement];
26807 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26809 if(placement == 'top' || placement == 'bottom'){
26811 placement = 'right';
26814 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26815 placement = 'left';
26818 var scroll = Roo.select('body', true).first().getScroll();
26820 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26824 align = this.alignment[placement];
26827 this.el.alignTo(this.bindEl, align[0],align[1]);
26828 //var arrow = this.el.select('.arrow',true).first();
26829 //arrow.set(align[2],
26831 this.el.addClass(placement);
26833 this.el.addClass('in fade');
26835 this.hoverState = null;
26837 if (this.el.hasClass('fade')) {
26848 //this.el.setXY([0,0]);
26849 this.el.removeClass('in');
26865 * @class Roo.bootstrap.LocationPicker
26866 * @extends Roo.bootstrap.Component
26867 * Bootstrap LocationPicker class
26868 * @cfg {Number} latitude Position when init default 0
26869 * @cfg {Number} longitude Position when init default 0
26870 * @cfg {Number} zoom default 15
26871 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26872 * @cfg {Boolean} mapTypeControl default false
26873 * @cfg {Boolean} disableDoubleClickZoom default false
26874 * @cfg {Boolean} scrollwheel default true
26875 * @cfg {Boolean} streetViewControl default false
26876 * @cfg {Number} radius default 0
26877 * @cfg {String} locationName
26878 * @cfg {Boolean} draggable default true
26879 * @cfg {Boolean} enableAutocomplete default false
26880 * @cfg {Boolean} enableReverseGeocode default true
26881 * @cfg {String} markerTitle
26884 * Create a new LocationPicker
26885 * @param {Object} config The config object
26889 Roo.bootstrap.LocationPicker = function(config){
26891 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26896 * Fires when the picker initialized.
26897 * @param {Roo.bootstrap.LocationPicker} this
26898 * @param {Google Location} location
26902 * @event positionchanged
26903 * Fires when the picker position changed.
26904 * @param {Roo.bootstrap.LocationPicker} this
26905 * @param {Google Location} location
26907 positionchanged : true,
26910 * Fires when the map resize.
26911 * @param {Roo.bootstrap.LocationPicker} this
26916 * Fires when the map show.
26917 * @param {Roo.bootstrap.LocationPicker} this
26922 * Fires when the map hide.
26923 * @param {Roo.bootstrap.LocationPicker} this
26928 * Fires when click the map.
26929 * @param {Roo.bootstrap.LocationPicker} this
26930 * @param {Map event} e
26934 * @event mapRightClick
26935 * Fires when right click the map.
26936 * @param {Roo.bootstrap.LocationPicker} this
26937 * @param {Map event} e
26939 mapRightClick : true,
26941 * @event markerClick
26942 * Fires when click the marker.
26943 * @param {Roo.bootstrap.LocationPicker} this
26944 * @param {Map event} e
26946 markerClick : true,
26948 * @event markerRightClick
26949 * Fires when right click the marker.
26950 * @param {Roo.bootstrap.LocationPicker} this
26951 * @param {Map event} e
26953 markerRightClick : true,
26955 * @event OverlayViewDraw
26956 * Fires when OverlayView Draw
26957 * @param {Roo.bootstrap.LocationPicker} this
26959 OverlayViewDraw : true,
26961 * @event OverlayViewOnAdd
26962 * Fires when OverlayView Draw
26963 * @param {Roo.bootstrap.LocationPicker} this
26965 OverlayViewOnAdd : true,
26967 * @event OverlayViewOnRemove
26968 * Fires when OverlayView Draw
26969 * @param {Roo.bootstrap.LocationPicker} this
26971 OverlayViewOnRemove : true,
26973 * @event OverlayViewShow
26974 * Fires when OverlayView Draw
26975 * @param {Roo.bootstrap.LocationPicker} this
26976 * @param {Pixel} cpx
26978 OverlayViewShow : true,
26980 * @event OverlayViewHide
26981 * Fires when OverlayView Draw
26982 * @param {Roo.bootstrap.LocationPicker} this
26984 OverlayViewHide : true,
26986 * @event loadexception
26987 * Fires when load google lib failed.
26988 * @param {Roo.bootstrap.LocationPicker} this
26990 loadexception : true
26995 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26997 gMapContext: false,
27003 mapTypeControl: false,
27004 disableDoubleClickZoom: false,
27006 streetViewControl: false,
27010 enableAutocomplete: false,
27011 enableReverseGeocode: true,
27014 getAutoCreate: function()
27019 cls: 'roo-location-picker'
27025 initEvents: function(ct, position)
27027 if(!this.el.getWidth() || this.isApplied()){
27031 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27036 initial: function()
27038 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27039 this.fireEvent('loadexception', this);
27043 if(!this.mapTypeId){
27044 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
27047 this.gMapContext = this.GMapContext();
27049 this.initOverlayView();
27051 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
27055 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
27056 _this.setPosition(_this.gMapContext.marker.position);
27059 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
27060 _this.fireEvent('mapClick', this, event);
27064 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
27065 _this.fireEvent('mapRightClick', this, event);
27069 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
27070 _this.fireEvent('markerClick', this, event);
27074 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
27075 _this.fireEvent('markerRightClick', this, event);
27079 this.setPosition(this.gMapContext.location);
27081 this.fireEvent('initial', this, this.gMapContext.location);
27084 initOverlayView: function()
27088 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
27092 _this.fireEvent('OverlayViewDraw', _this);
27097 _this.fireEvent('OverlayViewOnAdd', _this);
27100 onRemove: function()
27102 _this.fireEvent('OverlayViewOnRemove', _this);
27105 show: function(cpx)
27107 _this.fireEvent('OverlayViewShow', _this, cpx);
27112 _this.fireEvent('OverlayViewHide', _this);
27118 fromLatLngToContainerPixel: function(event)
27120 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27123 isApplied: function()
27125 return this.getGmapContext() == false ? false : true;
27128 getGmapContext: function()
27130 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27133 GMapContext: function()
27135 var position = new google.maps.LatLng(this.latitude, this.longitude);
27137 var _map = new google.maps.Map(this.el.dom, {
27140 mapTypeId: this.mapTypeId,
27141 mapTypeControl: this.mapTypeControl,
27142 disableDoubleClickZoom: this.disableDoubleClickZoom,
27143 scrollwheel: this.scrollwheel,
27144 streetViewControl: this.streetViewControl,
27145 locationName: this.locationName,
27146 draggable: this.draggable,
27147 enableAutocomplete: this.enableAutocomplete,
27148 enableReverseGeocode: this.enableReverseGeocode
27151 var _marker = new google.maps.Marker({
27152 position: position,
27154 title: this.markerTitle,
27155 draggable: this.draggable
27162 location: position,
27163 radius: this.radius,
27164 locationName: this.locationName,
27165 addressComponents: {
27166 formatted_address: null,
27167 addressLine1: null,
27168 addressLine2: null,
27170 streetNumber: null,
27174 stateOrProvince: null
27177 domContainer: this.el.dom,
27178 geodecoder: new google.maps.Geocoder()
27182 drawCircle: function(center, radius, options)
27184 if (this.gMapContext.circle != null) {
27185 this.gMapContext.circle.setMap(null);
27189 options = Roo.apply({}, options, {
27190 strokeColor: "#0000FF",
27191 strokeOpacity: .35,
27193 fillColor: "#0000FF",
27197 options.map = this.gMapContext.map;
27198 options.radius = radius;
27199 options.center = center;
27200 this.gMapContext.circle = new google.maps.Circle(options);
27201 return this.gMapContext.circle;
27207 setPosition: function(location)
27209 this.gMapContext.location = location;
27210 this.gMapContext.marker.setPosition(location);
27211 this.gMapContext.map.panTo(location);
27212 this.drawCircle(location, this.gMapContext.radius, {});
27216 if (this.gMapContext.settings.enableReverseGeocode) {
27217 this.gMapContext.geodecoder.geocode({
27218 latLng: this.gMapContext.location
27219 }, function(results, status) {
27221 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27222 _this.gMapContext.locationName = results[0].formatted_address;
27223 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27225 _this.fireEvent('positionchanged', this, location);
27232 this.fireEvent('positionchanged', this, location);
27237 google.maps.event.trigger(this.gMapContext.map, "resize");
27239 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27241 this.fireEvent('resize', this);
27244 setPositionByLatLng: function(latitude, longitude)
27246 this.setPosition(new google.maps.LatLng(latitude, longitude));
27249 getCurrentPosition: function()
27252 latitude: this.gMapContext.location.lat(),
27253 longitude: this.gMapContext.location.lng()
27257 getAddressName: function()
27259 return this.gMapContext.locationName;
27262 getAddressComponents: function()
27264 return this.gMapContext.addressComponents;
27267 address_component_from_google_geocode: function(address_components)
27271 for (var i = 0; i < address_components.length; i++) {
27272 var component = address_components[i];
27273 if (component.types.indexOf("postal_code") >= 0) {
27274 result.postalCode = component.short_name;
27275 } else if (component.types.indexOf("street_number") >= 0) {
27276 result.streetNumber = component.short_name;
27277 } else if (component.types.indexOf("route") >= 0) {
27278 result.streetName = component.short_name;
27279 } else if (component.types.indexOf("neighborhood") >= 0) {
27280 result.city = component.short_name;
27281 } else if (component.types.indexOf("locality") >= 0) {
27282 result.city = component.short_name;
27283 } else if (component.types.indexOf("sublocality") >= 0) {
27284 result.district = component.short_name;
27285 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27286 result.stateOrProvince = component.short_name;
27287 } else if (component.types.indexOf("country") >= 0) {
27288 result.country = component.short_name;
27292 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27293 result.addressLine2 = "";
27297 setZoomLevel: function(zoom)
27299 this.gMapContext.map.setZoom(zoom);
27312 this.fireEvent('show', this);
27323 this.fireEvent('hide', this);
27328 Roo.apply(Roo.bootstrap.LocationPicker, {
27330 OverlayView : function(map, options)
27332 options = options || {};
27339 * @class Roo.bootstrap.Alert
27340 * @extends Roo.bootstrap.Component
27341 * Bootstrap Alert class - shows an alert area box
27343 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27344 Enter a valid email address
27347 * @cfg {String} title The title of alert
27348 * @cfg {String} html The content of alert
27349 * @cfg {String} weight ( success | info | warning | danger )
27350 * @cfg {String} faicon font-awesomeicon
27353 * Create a new alert
27354 * @param {Object} config The config object
27358 Roo.bootstrap.Alert = function(config){
27359 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27363 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
27370 getAutoCreate : function()
27379 cls : 'roo-alert-icon'
27384 cls : 'roo-alert-title',
27389 cls : 'roo-alert-text',
27396 cfg.cn[0].cls += ' fa ' + this.faicon;
27400 cfg.cls += ' alert-' + this.weight;
27406 initEvents: function()
27408 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27411 setTitle : function(str)
27413 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27416 setText : function(str)
27418 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27421 setWeight : function(weight)
27424 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27427 this.weight = weight;
27429 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27432 setIcon : function(icon)
27435 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27438 this.faicon = icon;
27440 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27461 * @class Roo.bootstrap.UploadCropbox
27462 * @extends Roo.bootstrap.Component
27463 * Bootstrap UploadCropbox class
27464 * @cfg {String} emptyText show when image has been loaded
27465 * @cfg {String} rotateNotify show when image too small to rotate
27466 * @cfg {Number} errorTimeout default 3000
27467 * @cfg {Number} minWidth default 300
27468 * @cfg {Number} minHeight default 300
27469 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27470 * @cfg {Boolean} isDocument (true|false) default false
27471 * @cfg {String} url action url
27472 * @cfg {String} paramName default 'imageUpload'
27473 * @cfg {String} method default POST
27474 * @cfg {Boolean} loadMask (true|false) default true
27475 * @cfg {Boolean} loadingText default 'Loading...'
27478 * Create a new UploadCropbox
27479 * @param {Object} config The config object
27482 Roo.bootstrap.UploadCropbox = function(config){
27483 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27487 * @event beforeselectfile
27488 * Fire before select file
27489 * @param {Roo.bootstrap.UploadCropbox} this
27491 "beforeselectfile" : true,
27494 * Fire after initEvent
27495 * @param {Roo.bootstrap.UploadCropbox} this
27500 * Fire after initEvent
27501 * @param {Roo.bootstrap.UploadCropbox} this
27502 * @param {String} data
27507 * Fire when preparing the file data
27508 * @param {Roo.bootstrap.UploadCropbox} this
27509 * @param {Object} file
27514 * Fire when get exception
27515 * @param {Roo.bootstrap.UploadCropbox} this
27516 * @param {XMLHttpRequest} xhr
27518 "exception" : true,
27520 * @event beforeloadcanvas
27521 * Fire before load the canvas
27522 * @param {Roo.bootstrap.UploadCropbox} this
27523 * @param {String} src
27525 "beforeloadcanvas" : true,
27528 * Fire when trash image
27529 * @param {Roo.bootstrap.UploadCropbox} this
27534 * Fire when download the image
27535 * @param {Roo.bootstrap.UploadCropbox} this
27539 * @event footerbuttonclick
27540 * Fire when footerbuttonclick
27541 * @param {Roo.bootstrap.UploadCropbox} this
27542 * @param {String} type
27544 "footerbuttonclick" : true,
27548 * @param {Roo.bootstrap.UploadCropbox} this
27553 * Fire when rotate the image
27554 * @param {Roo.bootstrap.UploadCropbox} this
27555 * @param {String} pos
27560 * Fire when inspect the file
27561 * @param {Roo.bootstrap.UploadCropbox} this
27562 * @param {Object} file
27567 * Fire when xhr upload the file
27568 * @param {Roo.bootstrap.UploadCropbox} this
27569 * @param {Object} data
27574 * Fire when arrange the file data
27575 * @param {Roo.bootstrap.UploadCropbox} this
27576 * @param {Object} formData
27581 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27584 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27586 emptyText : 'Click to upload image',
27587 rotateNotify : 'Image is too small to rotate',
27588 errorTimeout : 3000,
27602 cropType : 'image/jpeg',
27604 canvasLoaded : false,
27605 isDocument : false,
27607 paramName : 'imageUpload',
27609 loadingText : 'Loading...',
27612 getAutoCreate : function()
27616 cls : 'roo-upload-cropbox',
27620 cls : 'roo-upload-cropbox-selector',
27625 cls : 'roo-upload-cropbox-body',
27626 style : 'cursor:pointer',
27630 cls : 'roo-upload-cropbox-preview'
27634 cls : 'roo-upload-cropbox-thumb'
27638 cls : 'roo-upload-cropbox-empty-notify',
27639 html : this.emptyText
27643 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27644 html : this.rotateNotify
27650 cls : 'roo-upload-cropbox-footer',
27653 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27663 onRender : function(ct, position)
27665 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27667 if (this.buttons.length) {
27669 Roo.each(this.buttons, function(bb) {
27671 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27673 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27679 this.maskEl = this.el;
27683 initEvents : function()
27685 this.urlAPI = (window.createObjectURL && window) ||
27686 (window.URL && URL.revokeObjectURL && URL) ||
27687 (window.webkitURL && webkitURL);
27689 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27690 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27692 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27693 this.selectorEl.hide();
27695 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27696 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27698 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27699 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27700 this.thumbEl.hide();
27702 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27703 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27705 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27706 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27707 this.errorEl.hide();
27709 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27710 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27711 this.footerEl.hide();
27713 this.setThumbBoxSize();
27719 this.fireEvent('initial', this);
27726 window.addEventListener("resize", function() { _this.resize(); } );
27728 this.bodyEl.on('click', this.beforeSelectFile, this);
27731 this.bodyEl.on('touchstart', this.onTouchStart, this);
27732 this.bodyEl.on('touchmove', this.onTouchMove, this);
27733 this.bodyEl.on('touchend', this.onTouchEnd, this);
27737 this.bodyEl.on('mousedown', this.onMouseDown, this);
27738 this.bodyEl.on('mousemove', this.onMouseMove, this);
27739 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27740 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27741 Roo.get(document).on('mouseup', this.onMouseUp, this);
27744 this.selectorEl.on('change', this.onFileSelected, this);
27750 this.baseScale = 1;
27752 this.baseRotate = 1;
27753 this.dragable = false;
27754 this.pinching = false;
27757 this.cropData = false;
27758 this.notifyEl.dom.innerHTML = this.emptyText;
27760 this.selectorEl.dom.value = '';
27764 resize : function()
27766 if(this.fireEvent('resize', this) != false){
27767 this.setThumbBoxPosition();
27768 this.setCanvasPosition();
27772 onFooterButtonClick : function(e, el, o, type)
27775 case 'rotate-left' :
27776 this.onRotateLeft(e);
27778 case 'rotate-right' :
27779 this.onRotateRight(e);
27782 this.beforeSelectFile(e);
27797 this.fireEvent('footerbuttonclick', this, type);
27800 beforeSelectFile : function(e)
27802 e.preventDefault();
27804 if(this.fireEvent('beforeselectfile', this) != false){
27805 this.selectorEl.dom.click();
27809 onFileSelected : function(e)
27811 e.preventDefault();
27813 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27817 var file = this.selectorEl.dom.files[0];
27819 if(this.fireEvent('inspect', this, file) != false){
27820 this.prepare(file);
27825 trash : function(e)
27827 this.fireEvent('trash', this);
27830 download : function(e)
27832 this.fireEvent('download', this);
27835 loadCanvas : function(src)
27837 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27841 this.imageEl = document.createElement('img');
27845 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27847 this.imageEl.src = src;
27851 onLoadCanvas : function()
27853 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27854 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27856 this.bodyEl.un('click', this.beforeSelectFile, this);
27858 this.notifyEl.hide();
27859 this.thumbEl.show();
27860 this.footerEl.show();
27862 this.baseRotateLevel();
27864 if(this.isDocument){
27865 this.setThumbBoxSize();
27868 this.setThumbBoxPosition();
27870 this.baseScaleLevel();
27876 this.canvasLoaded = true;
27879 this.maskEl.unmask();
27884 setCanvasPosition : function()
27886 if(!this.canvasEl){
27890 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27891 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27893 this.previewEl.setLeft(pw);
27894 this.previewEl.setTop(ph);
27898 onMouseDown : function(e)
27902 this.dragable = true;
27903 this.pinching = false;
27905 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27906 this.dragable = false;
27910 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27911 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27915 onMouseMove : function(e)
27919 if(!this.canvasLoaded){
27923 if (!this.dragable){
27927 var minX = Math.ceil(this.thumbEl.getLeft(true));
27928 var minY = Math.ceil(this.thumbEl.getTop(true));
27930 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27931 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27933 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27934 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27936 x = x - this.mouseX;
27937 y = y - this.mouseY;
27939 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27940 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27942 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27943 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27945 this.previewEl.setLeft(bgX);
27946 this.previewEl.setTop(bgY);
27948 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27949 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27952 onMouseUp : function(e)
27956 this.dragable = false;
27959 onMouseWheel : function(e)
27963 this.startScale = this.scale;
27965 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27967 if(!this.zoomable()){
27968 this.scale = this.startScale;
27977 zoomable : function()
27979 var minScale = this.thumbEl.getWidth() / this.minWidth;
27981 if(this.minWidth < this.minHeight){
27982 minScale = this.thumbEl.getHeight() / this.minHeight;
27985 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27986 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27990 (this.rotate == 0 || this.rotate == 180) &&
27992 width > this.imageEl.OriginWidth ||
27993 height > this.imageEl.OriginHeight ||
27994 (width < this.minWidth && height < this.minHeight)
28002 (this.rotate == 90 || this.rotate == 270) &&
28004 width > this.imageEl.OriginWidth ||
28005 height > this.imageEl.OriginHeight ||
28006 (width < this.minHeight && height < this.minWidth)
28013 !this.isDocument &&
28014 (this.rotate == 0 || this.rotate == 180) &&
28016 width < this.minWidth ||
28017 width > this.imageEl.OriginWidth ||
28018 height < this.minHeight ||
28019 height > this.imageEl.OriginHeight
28026 !this.isDocument &&
28027 (this.rotate == 90 || this.rotate == 270) &&
28029 width < this.minHeight ||
28030 width > this.imageEl.OriginWidth ||
28031 height < this.minWidth ||
28032 height > this.imageEl.OriginHeight
28042 onRotateLeft : function(e)
28044 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28046 var minScale = this.thumbEl.getWidth() / this.minWidth;
28048 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28049 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28051 this.startScale = this.scale;
28053 while (this.getScaleLevel() < minScale){
28055 this.scale = this.scale + 1;
28057 if(!this.zoomable()){
28062 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28063 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28068 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28075 this.scale = this.startScale;
28077 this.onRotateFail();
28082 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28084 if(this.isDocument){
28085 this.setThumbBoxSize();
28086 this.setThumbBoxPosition();
28087 this.setCanvasPosition();
28092 this.fireEvent('rotate', this, 'left');
28096 onRotateRight : function(e)
28098 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28100 var minScale = this.thumbEl.getWidth() / this.minWidth;
28102 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28103 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28105 this.startScale = this.scale;
28107 while (this.getScaleLevel() < minScale){
28109 this.scale = this.scale + 1;
28111 if(!this.zoomable()){
28116 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28117 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28122 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28129 this.scale = this.startScale;
28131 this.onRotateFail();
28136 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28138 if(this.isDocument){
28139 this.setThumbBoxSize();
28140 this.setThumbBoxPosition();
28141 this.setCanvasPosition();
28146 this.fireEvent('rotate', this, 'right');
28149 onRotateFail : function()
28151 this.errorEl.show(true);
28155 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28160 this.previewEl.dom.innerHTML = '';
28162 var canvasEl = document.createElement("canvas");
28164 var contextEl = canvasEl.getContext("2d");
28166 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28167 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28168 var center = this.imageEl.OriginWidth / 2;
28170 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28171 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28172 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28173 center = this.imageEl.OriginHeight / 2;
28176 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28178 contextEl.translate(center, center);
28179 contextEl.rotate(this.rotate * Math.PI / 180);
28181 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28183 this.canvasEl = document.createElement("canvas");
28185 this.contextEl = this.canvasEl.getContext("2d");
28187 switch (this.rotate) {
28190 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28191 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28193 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28198 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28199 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28201 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28202 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);
28206 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28211 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28212 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28214 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28215 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);
28219 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);
28224 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28225 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28227 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28228 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28232 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);
28239 this.previewEl.appendChild(this.canvasEl);
28241 this.setCanvasPosition();
28246 if(!this.canvasLoaded){
28250 var imageCanvas = document.createElement("canvas");
28252 var imageContext = imageCanvas.getContext("2d");
28254 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28255 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28257 var center = imageCanvas.width / 2;
28259 imageContext.translate(center, center);
28261 imageContext.rotate(this.rotate * Math.PI / 180);
28263 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28265 var canvas = document.createElement("canvas");
28267 var context = canvas.getContext("2d");
28269 canvas.width = this.minWidth;
28270 canvas.height = this.minHeight;
28272 switch (this.rotate) {
28275 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28276 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28278 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28279 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28281 var targetWidth = this.minWidth - 2 * x;
28282 var targetHeight = this.minHeight - 2 * y;
28286 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28287 scale = targetWidth / width;
28290 if(x > 0 && y == 0){
28291 scale = targetHeight / height;
28294 if(x > 0 && y > 0){
28295 scale = targetWidth / width;
28297 if(width < height){
28298 scale = targetHeight / height;
28302 context.scale(scale, scale);
28304 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28305 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28307 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28308 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28310 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28315 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28316 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28318 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28319 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28321 var targetWidth = this.minWidth - 2 * x;
28322 var targetHeight = this.minHeight - 2 * y;
28326 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28327 scale = targetWidth / width;
28330 if(x > 0 && y == 0){
28331 scale = targetHeight / height;
28334 if(x > 0 && y > 0){
28335 scale = targetWidth / width;
28337 if(width < height){
28338 scale = targetHeight / height;
28342 context.scale(scale, scale);
28344 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28345 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28347 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28348 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28350 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28352 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28357 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28358 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28360 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28361 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28363 var targetWidth = this.minWidth - 2 * x;
28364 var targetHeight = this.minHeight - 2 * y;
28368 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28369 scale = targetWidth / width;
28372 if(x > 0 && y == 0){
28373 scale = targetHeight / height;
28376 if(x > 0 && y > 0){
28377 scale = targetWidth / width;
28379 if(width < height){
28380 scale = targetHeight / height;
28384 context.scale(scale, scale);
28386 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28387 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28389 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28390 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28392 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28393 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28395 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28400 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28401 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28403 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28404 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28406 var targetWidth = this.minWidth - 2 * x;
28407 var targetHeight = this.minHeight - 2 * y;
28411 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28412 scale = targetWidth / width;
28415 if(x > 0 && y == 0){
28416 scale = targetHeight / height;
28419 if(x > 0 && y > 0){
28420 scale = targetWidth / width;
28422 if(width < height){
28423 scale = targetHeight / height;
28427 context.scale(scale, scale);
28429 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28430 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28432 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28433 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28435 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28437 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28444 this.cropData = canvas.toDataURL(this.cropType);
28446 if(this.fireEvent('crop', this, this.cropData) !== false){
28447 this.process(this.file, this.cropData);
28454 setThumbBoxSize : function()
28458 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28459 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28460 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28462 this.minWidth = width;
28463 this.minHeight = height;
28465 if(this.rotate == 90 || this.rotate == 270){
28466 this.minWidth = height;
28467 this.minHeight = width;
28472 width = Math.ceil(this.minWidth * height / this.minHeight);
28474 if(this.minWidth > this.minHeight){
28476 height = Math.ceil(this.minHeight * width / this.minWidth);
28479 this.thumbEl.setStyle({
28480 width : width + 'px',
28481 height : height + 'px'
28488 setThumbBoxPosition : function()
28490 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28491 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28493 this.thumbEl.setLeft(x);
28494 this.thumbEl.setTop(y);
28498 baseRotateLevel : function()
28500 this.baseRotate = 1;
28503 typeof(this.exif) != 'undefined' &&
28504 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28505 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28507 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28510 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28514 baseScaleLevel : function()
28518 if(this.isDocument){
28520 if(this.baseRotate == 6 || this.baseRotate == 8){
28522 height = this.thumbEl.getHeight();
28523 this.baseScale = height / this.imageEl.OriginWidth;
28525 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28526 width = this.thumbEl.getWidth();
28527 this.baseScale = width / this.imageEl.OriginHeight;
28533 height = this.thumbEl.getHeight();
28534 this.baseScale = height / this.imageEl.OriginHeight;
28536 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28537 width = this.thumbEl.getWidth();
28538 this.baseScale = width / this.imageEl.OriginWidth;
28544 if(this.baseRotate == 6 || this.baseRotate == 8){
28546 width = this.thumbEl.getHeight();
28547 this.baseScale = width / this.imageEl.OriginHeight;
28549 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28550 height = this.thumbEl.getWidth();
28551 this.baseScale = height / this.imageEl.OriginHeight;
28554 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28555 height = this.thumbEl.getWidth();
28556 this.baseScale = height / this.imageEl.OriginHeight;
28558 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28559 width = this.thumbEl.getHeight();
28560 this.baseScale = width / this.imageEl.OriginWidth;
28567 width = this.thumbEl.getWidth();
28568 this.baseScale = width / this.imageEl.OriginWidth;
28570 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28571 height = this.thumbEl.getHeight();
28572 this.baseScale = height / this.imageEl.OriginHeight;
28575 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28577 height = this.thumbEl.getHeight();
28578 this.baseScale = height / this.imageEl.OriginHeight;
28580 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28581 width = this.thumbEl.getWidth();
28582 this.baseScale = width / this.imageEl.OriginWidth;
28590 getScaleLevel : function()
28592 return this.baseScale * Math.pow(1.1, this.scale);
28595 onTouchStart : function(e)
28597 if(!this.canvasLoaded){
28598 this.beforeSelectFile(e);
28602 var touches = e.browserEvent.touches;
28608 if(touches.length == 1){
28609 this.onMouseDown(e);
28613 if(touches.length != 2){
28619 for(var i = 0, finger; finger = touches[i]; i++){
28620 coords.push(finger.pageX, finger.pageY);
28623 var x = Math.pow(coords[0] - coords[2], 2);
28624 var y = Math.pow(coords[1] - coords[3], 2);
28626 this.startDistance = Math.sqrt(x + y);
28628 this.startScale = this.scale;
28630 this.pinching = true;
28631 this.dragable = false;
28635 onTouchMove : function(e)
28637 if(!this.pinching && !this.dragable){
28641 var touches = e.browserEvent.touches;
28648 this.onMouseMove(e);
28654 for(var i = 0, finger; finger = touches[i]; i++){
28655 coords.push(finger.pageX, finger.pageY);
28658 var x = Math.pow(coords[0] - coords[2], 2);
28659 var y = Math.pow(coords[1] - coords[3], 2);
28661 this.endDistance = Math.sqrt(x + y);
28663 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28665 if(!this.zoomable()){
28666 this.scale = this.startScale;
28674 onTouchEnd : function(e)
28676 this.pinching = false;
28677 this.dragable = false;
28681 process : function(file, crop)
28684 this.maskEl.mask(this.loadingText);
28687 this.xhr = new XMLHttpRequest();
28689 file.xhr = this.xhr;
28691 this.xhr.open(this.method, this.url, true);
28694 "Accept": "application/json",
28695 "Cache-Control": "no-cache",
28696 "X-Requested-With": "XMLHttpRequest"
28699 for (var headerName in headers) {
28700 var headerValue = headers[headerName];
28702 this.xhr.setRequestHeader(headerName, headerValue);
28708 this.xhr.onload = function()
28710 _this.xhrOnLoad(_this.xhr);
28713 this.xhr.onerror = function()
28715 _this.xhrOnError(_this.xhr);
28718 var formData = new FormData();
28720 formData.append('returnHTML', 'NO');
28723 formData.append('crop', crop);
28726 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28727 formData.append(this.paramName, file, file.name);
28730 if(typeof(file.filename) != 'undefined'){
28731 formData.append('filename', file.filename);
28734 if(typeof(file.mimetype) != 'undefined'){
28735 formData.append('mimetype', file.mimetype);
28738 if(this.fireEvent('arrange', this, formData) != false){
28739 this.xhr.send(formData);
28743 xhrOnLoad : function(xhr)
28746 this.maskEl.unmask();
28749 if (xhr.readyState !== 4) {
28750 this.fireEvent('exception', this, xhr);
28754 var response = Roo.decode(xhr.responseText);
28756 if(!response.success){
28757 this.fireEvent('exception', this, xhr);
28761 var response = Roo.decode(xhr.responseText);
28763 this.fireEvent('upload', this, response);
28767 xhrOnError : function()
28770 this.maskEl.unmask();
28773 Roo.log('xhr on error');
28775 var response = Roo.decode(xhr.responseText);
28781 prepare : function(file)
28784 this.maskEl.mask(this.loadingText);
28790 if(typeof(file) === 'string'){
28791 this.loadCanvas(file);
28795 if(!file || !this.urlAPI){
28800 this.cropType = file.type;
28804 if(this.fireEvent('prepare', this, this.file) != false){
28806 var reader = new FileReader();
28808 reader.onload = function (e) {
28809 if (e.target.error) {
28810 Roo.log(e.target.error);
28814 var buffer = e.target.result,
28815 dataView = new DataView(buffer),
28817 maxOffset = dataView.byteLength - 4,
28821 if (dataView.getUint16(0) === 0xffd8) {
28822 while (offset < maxOffset) {
28823 markerBytes = dataView.getUint16(offset);
28825 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28826 markerLength = dataView.getUint16(offset + 2) + 2;
28827 if (offset + markerLength > dataView.byteLength) {
28828 Roo.log('Invalid meta data: Invalid segment size.');
28832 if(markerBytes == 0xffe1){
28833 _this.parseExifData(
28840 offset += markerLength;
28850 var url = _this.urlAPI.createObjectURL(_this.file);
28852 _this.loadCanvas(url);
28857 reader.readAsArrayBuffer(this.file);
28863 parseExifData : function(dataView, offset, length)
28865 var tiffOffset = offset + 10,
28869 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28870 // No Exif data, might be XMP data instead
28874 // Check for the ASCII code for "Exif" (0x45786966):
28875 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28876 // No Exif data, might be XMP data instead
28879 if (tiffOffset + 8 > dataView.byteLength) {
28880 Roo.log('Invalid Exif data: Invalid segment size.');
28883 // Check for the two null bytes:
28884 if (dataView.getUint16(offset + 8) !== 0x0000) {
28885 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28888 // Check the byte alignment:
28889 switch (dataView.getUint16(tiffOffset)) {
28891 littleEndian = true;
28894 littleEndian = false;
28897 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28900 // Check for the TIFF tag marker (0x002A):
28901 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28902 Roo.log('Invalid Exif data: Missing TIFF marker.');
28905 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28906 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28908 this.parseExifTags(
28911 tiffOffset + dirOffset,
28916 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28921 if (dirOffset + 6 > dataView.byteLength) {
28922 Roo.log('Invalid Exif data: Invalid directory offset.');
28925 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28926 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28927 if (dirEndOffset + 4 > dataView.byteLength) {
28928 Roo.log('Invalid Exif data: Invalid directory size.');
28931 for (i = 0; i < tagsNumber; i += 1) {
28935 dirOffset + 2 + 12 * i, // tag offset
28939 // Return the offset to the next directory:
28940 return dataView.getUint32(dirEndOffset, littleEndian);
28943 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28945 var tag = dataView.getUint16(offset, littleEndian);
28947 this.exif[tag] = this.getExifValue(
28951 dataView.getUint16(offset + 2, littleEndian), // tag type
28952 dataView.getUint32(offset + 4, littleEndian), // tag length
28957 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28959 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28968 Roo.log('Invalid Exif data: Invalid tag type.');
28972 tagSize = tagType.size * length;
28973 // Determine if the value is contained in the dataOffset bytes,
28974 // or if the value at the dataOffset is a pointer to the actual data:
28975 dataOffset = tagSize > 4 ?
28976 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28977 if (dataOffset + tagSize > dataView.byteLength) {
28978 Roo.log('Invalid Exif data: Invalid data offset.');
28981 if (length === 1) {
28982 return tagType.getValue(dataView, dataOffset, littleEndian);
28985 for (i = 0; i < length; i += 1) {
28986 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28989 if (tagType.ascii) {
28991 // Concatenate the chars:
28992 for (i = 0; i < values.length; i += 1) {
28994 // Ignore the terminating NULL byte(s):
28995 if (c === '\u0000') {
29007 Roo.apply(Roo.bootstrap.UploadCropbox, {
29009 'Orientation': 0x0112
29013 1: 0, //'top-left',
29015 3: 180, //'bottom-right',
29016 // 4: 'bottom-left',
29018 6: 90, //'right-top',
29019 // 7: 'right-bottom',
29020 8: 270 //'left-bottom'
29024 // byte, 8-bit unsigned int:
29026 getValue: function (dataView, dataOffset) {
29027 return dataView.getUint8(dataOffset);
29031 // ascii, 8-bit byte:
29033 getValue: function (dataView, dataOffset) {
29034 return String.fromCharCode(dataView.getUint8(dataOffset));
29039 // short, 16 bit int:
29041 getValue: function (dataView, dataOffset, littleEndian) {
29042 return dataView.getUint16(dataOffset, littleEndian);
29046 // long, 32 bit int:
29048 getValue: function (dataView, dataOffset, littleEndian) {
29049 return dataView.getUint32(dataOffset, littleEndian);
29053 // rational = two long values, first is numerator, second is denominator:
29055 getValue: function (dataView, dataOffset, littleEndian) {
29056 return dataView.getUint32(dataOffset, littleEndian) /
29057 dataView.getUint32(dataOffset + 4, littleEndian);
29061 // slong, 32 bit signed int:
29063 getValue: function (dataView, dataOffset, littleEndian) {
29064 return dataView.getInt32(dataOffset, littleEndian);
29068 // srational, two slongs, first is numerator, second is denominator:
29070 getValue: function (dataView, dataOffset, littleEndian) {
29071 return dataView.getInt32(dataOffset, littleEndian) /
29072 dataView.getInt32(dataOffset + 4, littleEndian);
29082 cls : 'btn-group roo-upload-cropbox-rotate-left',
29083 action : 'rotate-left',
29087 cls : 'btn btn-default',
29088 html : '<i class="fa fa-undo"></i>'
29094 cls : 'btn-group roo-upload-cropbox-picture',
29095 action : 'picture',
29099 cls : 'btn btn-default',
29100 html : '<i class="fa fa-picture-o"></i>'
29106 cls : 'btn-group roo-upload-cropbox-rotate-right',
29107 action : 'rotate-right',
29111 cls : 'btn btn-default',
29112 html : '<i class="fa fa-repeat"></i>'
29120 cls : 'btn-group roo-upload-cropbox-rotate-left',
29121 action : 'rotate-left',
29125 cls : 'btn btn-default',
29126 html : '<i class="fa fa-undo"></i>'
29132 cls : 'btn-group roo-upload-cropbox-download',
29133 action : 'download',
29137 cls : 'btn btn-default',
29138 html : '<i class="fa fa-download"></i>'
29144 cls : 'btn-group roo-upload-cropbox-crop',
29149 cls : 'btn btn-default',
29150 html : '<i class="fa fa-crop"></i>'
29156 cls : 'btn-group roo-upload-cropbox-trash',
29161 cls : 'btn btn-default',
29162 html : '<i class="fa fa-trash"></i>'
29168 cls : 'btn-group roo-upload-cropbox-rotate-right',
29169 action : 'rotate-right',
29173 cls : 'btn btn-default',
29174 html : '<i class="fa fa-repeat"></i>'
29182 cls : 'btn-group roo-upload-cropbox-rotate-left',
29183 action : 'rotate-left',
29187 cls : 'btn btn-default',
29188 html : '<i class="fa fa-undo"></i>'
29194 cls : 'btn-group roo-upload-cropbox-rotate-right',
29195 action : 'rotate-right',
29199 cls : 'btn btn-default',
29200 html : '<i class="fa fa-repeat"></i>'
29213 * @class Roo.bootstrap.DocumentManager
29214 * @extends Roo.bootstrap.Component
29215 * Bootstrap DocumentManager class
29216 * @cfg {String} paramName default 'imageUpload'
29217 * @cfg {String} toolTipName default 'filename'
29218 * @cfg {String} method default POST
29219 * @cfg {String} url action url
29220 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29221 * @cfg {Boolean} multiple multiple upload default true
29222 * @cfg {Number} thumbSize default 300
29223 * @cfg {String} fieldLabel
29224 * @cfg {Number} labelWidth default 4
29225 * @cfg {String} labelAlign (left|top) default left
29226 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29227 * @cfg {Number} labellg set the width of label (1-12)
29228 * @cfg {Number} labelmd set the width of label (1-12)
29229 * @cfg {Number} labelsm set the width of label (1-12)
29230 * @cfg {Number} labelxs set the width of label (1-12)
29233 * Create a new DocumentManager
29234 * @param {Object} config The config object
29237 Roo.bootstrap.DocumentManager = function(config){
29238 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29241 this.delegates = [];
29246 * Fire when initial the DocumentManager
29247 * @param {Roo.bootstrap.DocumentManager} this
29252 * inspect selected file
29253 * @param {Roo.bootstrap.DocumentManager} this
29254 * @param {File} file
29259 * Fire when xhr load exception
29260 * @param {Roo.bootstrap.DocumentManager} this
29261 * @param {XMLHttpRequest} xhr
29263 "exception" : true,
29265 * @event afterupload
29266 * Fire when xhr load exception
29267 * @param {Roo.bootstrap.DocumentManager} this
29268 * @param {XMLHttpRequest} xhr
29270 "afterupload" : true,
29273 * prepare the form data
29274 * @param {Roo.bootstrap.DocumentManager} this
29275 * @param {Object} formData
29280 * Fire when remove the file
29281 * @param {Roo.bootstrap.DocumentManager} this
29282 * @param {Object} file
29287 * Fire after refresh the file
29288 * @param {Roo.bootstrap.DocumentManager} this
29293 * Fire after click the image
29294 * @param {Roo.bootstrap.DocumentManager} this
29295 * @param {Object} file
29300 * Fire when upload a image and editable set to true
29301 * @param {Roo.bootstrap.DocumentManager} this
29302 * @param {Object} file
29306 * @event beforeselectfile
29307 * Fire before select file
29308 * @param {Roo.bootstrap.DocumentManager} this
29310 "beforeselectfile" : true,
29313 * Fire before process file
29314 * @param {Roo.bootstrap.DocumentManager} this
29315 * @param {Object} file
29319 * @event previewrendered
29320 * Fire when preview rendered
29321 * @param {Roo.bootstrap.DocumentManager} this
29322 * @param {Object} file
29324 "previewrendered" : true,
29327 "previewResize" : true
29332 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
29341 paramName : 'imageUpload',
29342 toolTipName : 'filename',
29345 labelAlign : 'left',
29355 getAutoCreate : function()
29357 var managerWidget = {
29359 cls : 'roo-document-manager',
29363 cls : 'roo-document-manager-selector',
29368 cls : 'roo-document-manager-uploader',
29372 cls : 'roo-document-manager-upload-btn',
29373 html : '<i class="fa fa-plus"></i>'
29384 cls : 'column col-md-12',
29389 if(this.fieldLabel.length){
29394 cls : 'column col-md-12',
29395 html : this.fieldLabel
29399 cls : 'column col-md-12',
29404 if(this.labelAlign == 'left'){
29409 html : this.fieldLabel
29418 if(this.labelWidth > 12){
29419 content[0].style = "width: " + this.labelWidth + 'px';
29422 if(this.labelWidth < 13 && this.labelmd == 0){
29423 this.labelmd = this.labelWidth;
29426 if(this.labellg > 0){
29427 content[0].cls += ' col-lg-' + this.labellg;
29428 content[1].cls += ' col-lg-' + (12 - this.labellg);
29431 if(this.labelmd > 0){
29432 content[0].cls += ' col-md-' + this.labelmd;
29433 content[1].cls += ' col-md-' + (12 - this.labelmd);
29436 if(this.labelsm > 0){
29437 content[0].cls += ' col-sm-' + this.labelsm;
29438 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29441 if(this.labelxs > 0){
29442 content[0].cls += ' col-xs-' + this.labelxs;
29443 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29451 cls : 'row clearfix',
29459 initEvents : function()
29461 this.managerEl = this.el.select('.roo-document-manager', true).first();
29462 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29464 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29465 this.selectorEl.hide();
29468 this.selectorEl.attr('multiple', 'multiple');
29471 this.selectorEl.on('change', this.onFileSelected, this);
29473 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29474 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29476 this.uploader.on('click', this.onUploaderClick, this);
29478 this.renderProgressDialog();
29482 window.addEventListener("resize", function() { _this.refresh(); } );
29484 this.fireEvent('initial', this);
29487 renderProgressDialog : function()
29491 this.progressDialog = new Roo.bootstrap.Modal({
29492 cls : 'roo-document-manager-progress-dialog',
29493 allow_close : false,
29504 btnclick : function() {
29505 _this.uploadCancel();
29511 this.progressDialog.render(Roo.get(document.body));
29513 this.progress = new Roo.bootstrap.Progress({
29514 cls : 'roo-document-manager-progress',
29519 this.progress.render(this.progressDialog.getChildContainer());
29521 this.progressBar = new Roo.bootstrap.ProgressBar({
29522 cls : 'roo-document-manager-progress-bar',
29525 aria_valuemax : 12,
29529 this.progressBar.render(this.progress.getChildContainer());
29532 onUploaderClick : function(e)
29534 e.preventDefault();
29536 if(this.fireEvent('beforeselectfile', this) != false){
29537 this.selectorEl.dom.click();
29542 onFileSelected : function(e)
29544 e.preventDefault();
29546 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29550 Roo.each(this.selectorEl.dom.files, function(file){
29551 if(this.fireEvent('inspect', this, file) != false){
29552 this.files.push(file);
29562 this.selectorEl.dom.value = '';
29564 if(!this.files || !this.files.length){
29568 if(this.boxes > 0 && this.files.length > this.boxes){
29569 this.files = this.files.slice(0, this.boxes);
29572 this.uploader.show();
29574 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29575 this.uploader.hide();
29584 Roo.each(this.files, function(file){
29586 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29587 var f = this.renderPreview(file);
29592 if(file.type.indexOf('image') != -1){
29593 this.delegates.push(
29595 _this.process(file);
29596 }).createDelegate(this)
29604 _this.process(file);
29605 }).createDelegate(this)
29610 this.files = files;
29612 this.delegates = this.delegates.concat(docs);
29614 if(!this.delegates.length){
29619 this.progressBar.aria_valuemax = this.delegates.length;
29626 arrange : function()
29628 if(!this.delegates.length){
29629 this.progressDialog.hide();
29634 var delegate = this.delegates.shift();
29636 this.progressDialog.show();
29638 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29640 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29645 refresh : function()
29647 this.uploader.show();
29649 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29650 this.uploader.hide();
29653 Roo.isTouch ? this.closable(false) : this.closable(true);
29655 this.fireEvent('refresh', this);
29658 onRemove : function(e, el, o)
29660 e.preventDefault();
29662 this.fireEvent('remove', this, o);
29666 remove : function(o)
29670 Roo.each(this.files, function(file){
29671 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29680 this.files = files;
29687 Roo.each(this.files, function(file){
29692 file.target.remove();
29701 onClick : function(e, el, o)
29703 e.preventDefault();
29705 this.fireEvent('click', this, o);
29709 closable : function(closable)
29711 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29713 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29725 xhrOnLoad : function(xhr)
29727 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29731 if (xhr.readyState !== 4) {
29733 this.fireEvent('exception', this, xhr);
29737 var response = Roo.decode(xhr.responseText);
29739 if(!response.success){
29741 this.fireEvent('exception', this, xhr);
29745 var file = this.renderPreview(response.data);
29747 this.files.push(file);
29751 this.fireEvent('afterupload', this, xhr);
29755 xhrOnError : function(xhr)
29757 Roo.log('xhr on error');
29759 var response = Roo.decode(xhr.responseText);
29766 process : function(file)
29768 if(this.fireEvent('process', this, file) !== false){
29769 if(this.editable && file.type.indexOf('image') != -1){
29770 this.fireEvent('edit', this, file);
29774 this.uploadStart(file, false);
29781 uploadStart : function(file, crop)
29783 this.xhr = new XMLHttpRequest();
29785 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29790 file.xhr = this.xhr;
29792 this.managerEl.createChild({
29794 cls : 'roo-document-manager-loading',
29798 tooltip : file.name,
29799 cls : 'roo-document-manager-thumb',
29800 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29806 this.xhr.open(this.method, this.url, true);
29809 "Accept": "application/json",
29810 "Cache-Control": "no-cache",
29811 "X-Requested-With": "XMLHttpRequest"
29814 for (var headerName in headers) {
29815 var headerValue = headers[headerName];
29817 this.xhr.setRequestHeader(headerName, headerValue);
29823 this.xhr.onload = function()
29825 _this.xhrOnLoad(_this.xhr);
29828 this.xhr.onerror = function()
29830 _this.xhrOnError(_this.xhr);
29833 var formData = new FormData();
29835 formData.append('returnHTML', 'NO');
29838 formData.append('crop', crop);
29841 formData.append(this.paramName, file, file.name);
29848 if(this.fireEvent('prepare', this, formData, options) != false){
29850 if(options.manually){
29854 this.xhr.send(formData);
29858 this.uploadCancel();
29861 uploadCancel : function()
29867 this.delegates = [];
29869 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29876 renderPreview : function(file)
29878 if(typeof(file.target) != 'undefined' && file.target){
29882 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29884 var previewEl = this.managerEl.createChild({
29886 cls : 'roo-document-manager-preview',
29890 tooltip : file[this.toolTipName],
29891 cls : 'roo-document-manager-thumb',
29892 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29897 html : '<i class="fa fa-times-circle"></i>'
29902 var close = previewEl.select('button.close', true).first();
29904 close.on('click', this.onRemove, this, file);
29906 file.target = previewEl;
29908 var image = previewEl.select('img', true).first();
29912 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29914 image.on('click', this.onClick, this, file);
29916 this.fireEvent('previewrendered', this, file);
29922 onPreviewLoad : function(file, image)
29924 if(typeof(file.target) == 'undefined' || !file.target){
29928 var width = image.dom.naturalWidth || image.dom.width;
29929 var height = image.dom.naturalHeight || image.dom.height;
29931 if(!this.previewResize) {
29935 if(width > height){
29936 file.target.addClass('wide');
29940 file.target.addClass('tall');
29945 uploadFromSource : function(file, crop)
29947 this.xhr = new XMLHttpRequest();
29949 this.managerEl.createChild({
29951 cls : 'roo-document-manager-loading',
29955 tooltip : file.name,
29956 cls : 'roo-document-manager-thumb',
29957 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29963 this.xhr.open(this.method, this.url, true);
29966 "Accept": "application/json",
29967 "Cache-Control": "no-cache",
29968 "X-Requested-With": "XMLHttpRequest"
29971 for (var headerName in headers) {
29972 var headerValue = headers[headerName];
29974 this.xhr.setRequestHeader(headerName, headerValue);
29980 this.xhr.onload = function()
29982 _this.xhrOnLoad(_this.xhr);
29985 this.xhr.onerror = function()
29987 _this.xhrOnError(_this.xhr);
29990 var formData = new FormData();
29992 formData.append('returnHTML', 'NO');
29994 formData.append('crop', crop);
29996 if(typeof(file.filename) != 'undefined'){
29997 formData.append('filename', file.filename);
30000 if(typeof(file.mimetype) != 'undefined'){
30001 formData.append('mimetype', file.mimetype);
30006 if(this.fireEvent('prepare', this, formData) != false){
30007 this.xhr.send(formData);
30017 * @class Roo.bootstrap.DocumentViewer
30018 * @extends Roo.bootstrap.Component
30019 * Bootstrap DocumentViewer class
30020 * @cfg {Boolean} showDownload (true|false) show download button (default true)
30021 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30024 * Create a new DocumentViewer
30025 * @param {Object} config The config object
30028 Roo.bootstrap.DocumentViewer = function(config){
30029 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30034 * Fire after initEvent
30035 * @param {Roo.bootstrap.DocumentViewer} this
30041 * @param {Roo.bootstrap.DocumentViewer} this
30046 * Fire after download button
30047 * @param {Roo.bootstrap.DocumentViewer} this
30052 * Fire after trash button
30053 * @param {Roo.bootstrap.DocumentViewer} this
30060 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
30062 showDownload : true,
30066 getAutoCreate : function()
30070 cls : 'roo-document-viewer',
30074 cls : 'roo-document-viewer-body',
30078 cls : 'roo-document-viewer-thumb',
30082 cls : 'roo-document-viewer-image'
30090 cls : 'roo-document-viewer-footer',
30093 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
30097 cls : 'btn-group roo-document-viewer-download',
30101 cls : 'btn btn-default',
30102 html : '<i class="fa fa-download"></i>'
30108 cls : 'btn-group roo-document-viewer-trash',
30112 cls : 'btn btn-default',
30113 html : '<i class="fa fa-trash"></i>'
30126 initEvents : function()
30128 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30129 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30131 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30132 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30134 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30135 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30137 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30138 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30140 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30141 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30143 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30144 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30146 this.bodyEl.on('click', this.onClick, this);
30147 this.downloadBtn.on('click', this.onDownload, this);
30148 this.trashBtn.on('click', this.onTrash, this);
30150 this.downloadBtn.hide();
30151 this.trashBtn.hide();
30153 if(this.showDownload){
30154 this.downloadBtn.show();
30157 if(this.showTrash){
30158 this.trashBtn.show();
30161 if(!this.showDownload && !this.showTrash) {
30162 this.footerEl.hide();
30167 initial : function()
30169 this.fireEvent('initial', this);
30173 onClick : function(e)
30175 e.preventDefault();
30177 this.fireEvent('click', this);
30180 onDownload : function(e)
30182 e.preventDefault();
30184 this.fireEvent('download', this);
30187 onTrash : function(e)
30189 e.preventDefault();
30191 this.fireEvent('trash', this);
30203 * @class Roo.bootstrap.NavProgressBar
30204 * @extends Roo.bootstrap.Component
30205 * Bootstrap NavProgressBar class
30208 * Create a new nav progress bar
30209 * @param {Object} config The config object
30212 Roo.bootstrap.NavProgressBar = function(config){
30213 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30215 this.bullets = this.bullets || [];
30217 // Roo.bootstrap.NavProgressBar.register(this);
30221 * Fires when the active item changes
30222 * @param {Roo.bootstrap.NavProgressBar} this
30223 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30224 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
30231 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
30236 getAutoCreate : function()
30238 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30242 cls : 'roo-navigation-bar-group',
30246 cls : 'roo-navigation-top-bar'
30250 cls : 'roo-navigation-bullets-bar',
30254 cls : 'roo-navigation-bar'
30261 cls : 'roo-navigation-bottom-bar'
30271 initEvents: function()
30276 onRender : function(ct, position)
30278 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30280 if(this.bullets.length){
30281 Roo.each(this.bullets, function(b){
30290 addItem : function(cfg)
30292 var item = new Roo.bootstrap.NavProgressItem(cfg);
30294 item.parentId = this.id;
30295 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30298 var top = new Roo.bootstrap.Element({
30300 cls : 'roo-navigation-bar-text'
30303 var bottom = new Roo.bootstrap.Element({
30305 cls : 'roo-navigation-bar-text'
30308 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30309 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30311 var topText = new Roo.bootstrap.Element({
30313 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30316 var bottomText = new Roo.bootstrap.Element({
30318 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30321 topText.onRender(top.el, null);
30322 bottomText.onRender(bottom.el, null);
30325 item.bottomEl = bottom;
30328 this.barItems.push(item);
30333 getActive : function()
30335 var active = false;
30337 Roo.each(this.barItems, function(v){
30339 if (!v.isActive()) {
30351 setActiveItem : function(item)
30355 Roo.each(this.barItems, function(v){
30356 if (v.rid == item.rid) {
30360 if (v.isActive()) {
30361 v.setActive(false);
30366 item.setActive(true);
30368 this.fireEvent('changed', this, item, prev);
30371 getBarItem: function(rid)
30375 Roo.each(this.barItems, function(e) {
30376 if (e.rid != rid) {
30387 indexOfItem : function(item)
30391 Roo.each(this.barItems, function(v, i){
30393 if (v.rid != item.rid) {
30404 setActiveNext : function()
30406 var i = this.indexOfItem(this.getActive());
30408 if (i > this.barItems.length) {
30412 this.setActiveItem(this.barItems[i+1]);
30415 setActivePrev : function()
30417 var i = this.indexOfItem(this.getActive());
30423 this.setActiveItem(this.barItems[i-1]);
30426 format : function()
30428 if(!this.barItems.length){
30432 var width = 100 / this.barItems.length;
30434 Roo.each(this.barItems, function(i){
30435 i.el.setStyle('width', width + '%');
30436 i.topEl.el.setStyle('width', width + '%');
30437 i.bottomEl.el.setStyle('width', width + '%');
30446 * Nav Progress Item
30451 * @class Roo.bootstrap.NavProgressItem
30452 * @extends Roo.bootstrap.Component
30453 * Bootstrap NavProgressItem class
30454 * @cfg {String} rid the reference id
30455 * @cfg {Boolean} active (true|false) Is item active default false
30456 * @cfg {Boolean} disabled (true|false) Is item active default false
30457 * @cfg {String} html
30458 * @cfg {String} position (top|bottom) text position default bottom
30459 * @cfg {String} icon show icon instead of number
30462 * Create a new NavProgressItem
30463 * @param {Object} config The config object
30465 Roo.bootstrap.NavProgressItem = function(config){
30466 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30471 * The raw click event for the entire grid.
30472 * @param {Roo.bootstrap.NavProgressItem} this
30473 * @param {Roo.EventObject} e
30480 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30486 position : 'bottom',
30489 getAutoCreate : function()
30491 var iconCls = 'roo-navigation-bar-item-icon';
30493 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30497 cls: 'roo-navigation-bar-item',
30507 cfg.cls += ' active';
30510 cfg.cls += ' disabled';
30516 disable : function()
30518 this.setDisabled(true);
30521 enable : function()
30523 this.setDisabled(false);
30526 initEvents: function()
30528 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30530 this.iconEl.on('click', this.onClick, this);
30533 onClick : function(e)
30535 e.preventDefault();
30541 if(this.fireEvent('click', this, e) === false){
30545 this.parent().setActiveItem(this);
30548 isActive: function ()
30550 return this.active;
30553 setActive : function(state)
30555 if(this.active == state){
30559 this.active = state;
30562 this.el.addClass('active');
30566 this.el.removeClass('active');
30571 setDisabled : function(state)
30573 if(this.disabled == state){
30577 this.disabled = state;
30580 this.el.addClass('disabled');
30584 this.el.removeClass('disabled');
30587 tooltipEl : function()
30589 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30602 * @class Roo.bootstrap.FieldLabel
30603 * @extends Roo.bootstrap.Component
30604 * Bootstrap FieldLabel class
30605 * @cfg {String} html contents of the element
30606 * @cfg {String} tag tag of the element default label
30607 * @cfg {String} cls class of the element
30608 * @cfg {String} target label target
30609 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30610 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30611 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30612 * @cfg {String} iconTooltip default "This field is required"
30613 * @cfg {String} indicatorpos (left|right) default left
30616 * Create a new FieldLabel
30617 * @param {Object} config The config object
30620 Roo.bootstrap.FieldLabel = function(config){
30621 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30626 * Fires after the field has been marked as invalid.
30627 * @param {Roo.form.FieldLabel} this
30628 * @param {String} msg The validation message
30633 * Fires after the field has been validated with no errors.
30634 * @param {Roo.form.FieldLabel} this
30640 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30647 invalidClass : 'has-warning',
30648 validClass : 'has-success',
30649 iconTooltip : 'This field is required',
30650 indicatorpos : 'left',
30652 getAutoCreate : function(){
30655 if (!this.allowBlank) {
30661 cls : 'roo-bootstrap-field-label ' + this.cls,
30666 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30667 tooltip : this.iconTooltip
30676 if(this.indicatorpos == 'right'){
30679 cls : 'roo-bootstrap-field-label ' + this.cls,
30688 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30689 tooltip : this.iconTooltip
30698 initEvents: function()
30700 Roo.bootstrap.Element.superclass.initEvents.call(this);
30702 this.indicator = this.indicatorEl();
30704 if(this.indicator){
30705 this.indicator.removeClass('visible');
30706 this.indicator.addClass('invisible');
30709 Roo.bootstrap.FieldLabel.register(this);
30712 indicatorEl : function()
30714 var indicator = this.el.select('i.roo-required-indicator',true).first();
30725 * Mark this field as valid
30727 markValid : function()
30729 if(this.indicator){
30730 this.indicator.removeClass('visible');
30731 this.indicator.addClass('invisible');
30733 if (Roo.bootstrap.version == 3) {
30734 this.el.removeClass(this.invalidClass);
30735 this.el.addClass(this.validClass);
30737 this.el.removeClass('is-invalid');
30738 this.el.addClass('is-valid');
30742 this.fireEvent('valid', this);
30746 * Mark this field as invalid
30747 * @param {String} msg The validation message
30749 markInvalid : function(msg)
30751 if(this.indicator){
30752 this.indicator.removeClass('invisible');
30753 this.indicator.addClass('visible');
30755 if (Roo.bootstrap.version == 3) {
30756 this.el.removeClass(this.validClass);
30757 this.el.addClass(this.invalidClass);
30759 this.el.removeClass('is-valid');
30760 this.el.addClass('is-invalid');
30764 this.fireEvent('invalid', this, msg);
30770 Roo.apply(Roo.bootstrap.FieldLabel, {
30775 * register a FieldLabel Group
30776 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30778 register : function(label)
30780 if(this.groups.hasOwnProperty(label.target)){
30784 this.groups[label.target] = label;
30788 * fetch a FieldLabel Group based on the target
30789 * @param {string} target
30790 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30792 get: function(target) {
30793 if (typeof(this.groups[target]) == 'undefined') {
30797 return this.groups[target] ;
30806 * page DateSplitField.
30812 * @class Roo.bootstrap.DateSplitField
30813 * @extends Roo.bootstrap.Component
30814 * Bootstrap DateSplitField class
30815 * @cfg {string} fieldLabel - the label associated
30816 * @cfg {Number} labelWidth set the width of label (0-12)
30817 * @cfg {String} labelAlign (top|left)
30818 * @cfg {Boolean} dayAllowBlank (true|false) default false
30819 * @cfg {Boolean} monthAllowBlank (true|false) default false
30820 * @cfg {Boolean} yearAllowBlank (true|false) default false
30821 * @cfg {string} dayPlaceholder
30822 * @cfg {string} monthPlaceholder
30823 * @cfg {string} yearPlaceholder
30824 * @cfg {string} dayFormat default 'd'
30825 * @cfg {string} monthFormat default 'm'
30826 * @cfg {string} yearFormat default 'Y'
30827 * @cfg {Number} labellg set the width of label (1-12)
30828 * @cfg {Number} labelmd set the width of label (1-12)
30829 * @cfg {Number} labelsm set the width of label (1-12)
30830 * @cfg {Number} labelxs set the width of label (1-12)
30834 * Create a new DateSplitField
30835 * @param {Object} config The config object
30838 Roo.bootstrap.DateSplitField = function(config){
30839 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30845 * getting the data of years
30846 * @param {Roo.bootstrap.DateSplitField} this
30847 * @param {Object} years
30852 * getting the data of days
30853 * @param {Roo.bootstrap.DateSplitField} this
30854 * @param {Object} days
30859 * Fires after the field has been marked as invalid.
30860 * @param {Roo.form.Field} this
30861 * @param {String} msg The validation message
30866 * Fires after the field has been validated with no errors.
30867 * @param {Roo.form.Field} this
30873 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30876 labelAlign : 'top',
30878 dayAllowBlank : false,
30879 monthAllowBlank : false,
30880 yearAllowBlank : false,
30881 dayPlaceholder : '',
30882 monthPlaceholder : '',
30883 yearPlaceholder : '',
30887 isFormField : true,
30893 getAutoCreate : function()
30897 cls : 'row roo-date-split-field-group',
30902 cls : 'form-hidden-field roo-date-split-field-group-value',
30908 var labelCls = 'col-md-12';
30909 var contentCls = 'col-md-4';
30911 if(this.fieldLabel){
30915 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30919 html : this.fieldLabel
30924 if(this.labelAlign == 'left'){
30926 if(this.labelWidth > 12){
30927 label.style = "width: " + this.labelWidth + 'px';
30930 if(this.labelWidth < 13 && this.labelmd == 0){
30931 this.labelmd = this.labelWidth;
30934 if(this.labellg > 0){
30935 labelCls = ' col-lg-' + this.labellg;
30936 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30939 if(this.labelmd > 0){
30940 labelCls = ' col-md-' + this.labelmd;
30941 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30944 if(this.labelsm > 0){
30945 labelCls = ' col-sm-' + this.labelsm;
30946 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30949 if(this.labelxs > 0){
30950 labelCls = ' col-xs-' + this.labelxs;
30951 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30955 label.cls += ' ' + labelCls;
30957 cfg.cn.push(label);
30960 Roo.each(['day', 'month', 'year'], function(t){
30963 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30970 inputEl: function ()
30972 return this.el.select('.roo-date-split-field-group-value', true).first();
30975 onRender : function(ct, position)
30979 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30981 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30983 this.dayField = new Roo.bootstrap.ComboBox({
30984 allowBlank : this.dayAllowBlank,
30985 alwaysQuery : true,
30986 displayField : 'value',
30989 forceSelection : true,
30991 placeholder : this.dayPlaceholder,
30992 selectOnFocus : true,
30993 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30994 triggerAction : 'all',
30996 valueField : 'value',
30997 store : new Roo.data.SimpleStore({
30998 data : (function() {
31000 _this.fireEvent('days', _this, days);
31003 fields : [ 'value' ]
31006 select : function (_self, record, index)
31008 _this.setValue(_this.getValue());
31013 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
31015 this.monthField = new Roo.bootstrap.MonthField({
31016 after : '<i class=\"fa fa-calendar\"></i>',
31017 allowBlank : this.monthAllowBlank,
31018 placeholder : this.monthPlaceholder,
31021 render : function (_self)
31023 this.el.select('span.input-group-addon', true).first().on('click', function(e){
31024 e.preventDefault();
31028 select : function (_self, oldvalue, newvalue)
31030 _this.setValue(_this.getValue());
31035 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31037 this.yearField = new Roo.bootstrap.ComboBox({
31038 allowBlank : this.yearAllowBlank,
31039 alwaysQuery : true,
31040 displayField : 'value',
31043 forceSelection : true,
31045 placeholder : this.yearPlaceholder,
31046 selectOnFocus : true,
31047 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31048 triggerAction : 'all',
31050 valueField : 'value',
31051 store : new Roo.data.SimpleStore({
31052 data : (function() {
31054 _this.fireEvent('years', _this, years);
31057 fields : [ 'value' ]
31060 select : function (_self, record, index)
31062 _this.setValue(_this.getValue());
31067 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
31070 setValue : function(v, format)
31072 this.inputEl.dom.value = v;
31074 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
31076 var d = Date.parseDate(v, f);
31083 this.setDay(d.format(this.dayFormat));
31084 this.setMonth(d.format(this.monthFormat));
31085 this.setYear(d.format(this.yearFormat));
31092 setDay : function(v)
31094 this.dayField.setValue(v);
31095 this.inputEl.dom.value = this.getValue();
31100 setMonth : function(v)
31102 this.monthField.setValue(v, true);
31103 this.inputEl.dom.value = this.getValue();
31108 setYear : function(v)
31110 this.yearField.setValue(v);
31111 this.inputEl.dom.value = this.getValue();
31116 getDay : function()
31118 return this.dayField.getValue();
31121 getMonth : function()
31123 return this.monthField.getValue();
31126 getYear : function()
31128 return this.yearField.getValue();
31131 getValue : function()
31133 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31135 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31145 this.inputEl.dom.value = '';
31150 validate : function()
31152 var d = this.dayField.validate();
31153 var m = this.monthField.validate();
31154 var y = this.yearField.validate();
31159 (!this.dayAllowBlank && !d) ||
31160 (!this.monthAllowBlank && !m) ||
31161 (!this.yearAllowBlank && !y)
31166 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31175 this.markInvalid();
31180 markValid : function()
31183 var label = this.el.select('label', true).first();
31184 var icon = this.el.select('i.fa-star', true).first();
31190 this.fireEvent('valid', this);
31194 * Mark this field as invalid
31195 * @param {String} msg The validation message
31197 markInvalid : function(msg)
31200 var label = this.el.select('label', true).first();
31201 var icon = this.el.select('i.fa-star', true).first();
31203 if(label && !icon){
31204 this.el.select('.roo-date-split-field-label', true).createChild({
31206 cls : 'text-danger fa fa-lg fa-star',
31207 tooltip : 'This field is required',
31208 style : 'margin-right:5px;'
31212 this.fireEvent('invalid', this, msg);
31215 clearInvalid : function()
31217 var label = this.el.select('label', true).first();
31218 var icon = this.el.select('i.fa-star', true).first();
31224 this.fireEvent('valid', this);
31227 getName: function()
31237 * http://masonry.desandro.com
31239 * The idea is to render all the bricks based on vertical width...
31241 * The original code extends 'outlayer' - we might need to use that....
31247 * @class Roo.bootstrap.LayoutMasonry
31248 * @extends Roo.bootstrap.Component
31249 * Bootstrap Layout Masonry class
31252 * Create a new Element
31253 * @param {Object} config The config object
31256 Roo.bootstrap.LayoutMasonry = function(config){
31258 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31262 Roo.bootstrap.LayoutMasonry.register(this);
31268 * Fire after layout the items
31269 * @param {Roo.bootstrap.LayoutMasonry} this
31270 * @param {Roo.EventObject} e
31277 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
31280 * @cfg {Boolean} isLayoutInstant = no animation?
31282 isLayoutInstant : false, // needed?
31285 * @cfg {Number} boxWidth width of the columns
31290 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
31295 * @cfg {Number} padWidth padding below box..
31300 * @cfg {Number} gutter gutter width..
31305 * @cfg {Number} maxCols maximum number of columns
31311 * @cfg {Boolean} isAutoInitial defalut true
31313 isAutoInitial : true,
31318 * @cfg {Boolean} isHorizontal defalut false
31320 isHorizontal : false,
31322 currentSize : null,
31328 bricks: null, //CompositeElement
31332 _isLayoutInited : false,
31334 // isAlternative : false, // only use for vertical layout...
31337 * @cfg {Number} alternativePadWidth padding below box..
31339 alternativePadWidth : 50,
31341 selectedBrick : [],
31343 getAutoCreate : function(){
31345 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31349 cls: 'blog-masonary-wrapper ' + this.cls,
31351 cls : 'mas-boxes masonary'
31358 getChildContainer: function( )
31360 if (this.boxesEl) {
31361 return this.boxesEl;
31364 this.boxesEl = this.el.select('.mas-boxes').first();
31366 return this.boxesEl;
31370 initEvents : function()
31374 if(this.isAutoInitial){
31375 Roo.log('hook children rendered');
31376 this.on('childrenrendered', function() {
31377 Roo.log('children rendered');
31383 initial : function()
31385 this.selectedBrick = [];
31387 this.currentSize = this.el.getBox(true);
31389 Roo.EventManager.onWindowResize(this.resize, this);
31391 if(!this.isAutoInitial){
31399 //this.layout.defer(500,this);
31403 resize : function()
31405 var cs = this.el.getBox(true);
31408 this.currentSize.width == cs.width &&
31409 this.currentSize.x == cs.x &&
31410 this.currentSize.height == cs.height &&
31411 this.currentSize.y == cs.y
31413 Roo.log("no change in with or X or Y");
31417 this.currentSize = cs;
31423 layout : function()
31425 this._resetLayout();
31427 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31429 this.layoutItems( isInstant );
31431 this._isLayoutInited = true;
31433 this.fireEvent('layout', this);
31437 _resetLayout : function()
31439 if(this.isHorizontal){
31440 this.horizontalMeasureColumns();
31444 this.verticalMeasureColumns();
31448 verticalMeasureColumns : function()
31450 this.getContainerWidth();
31452 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31453 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31457 var boxWidth = this.boxWidth + this.padWidth;
31459 if(this.containerWidth < this.boxWidth){
31460 boxWidth = this.containerWidth
31463 var containerWidth = this.containerWidth;
31465 var cols = Math.floor(containerWidth / boxWidth);
31467 this.cols = Math.max( cols, 1 );
31469 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31471 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31473 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31475 this.colWidth = boxWidth + avail - this.padWidth;
31477 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31478 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31481 horizontalMeasureColumns : function()
31483 this.getContainerWidth();
31485 var boxWidth = this.boxWidth;
31487 if(this.containerWidth < boxWidth){
31488 boxWidth = this.containerWidth;
31491 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31493 this.el.setHeight(boxWidth);
31497 getContainerWidth : function()
31499 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31502 layoutItems : function( isInstant )
31504 Roo.log(this.bricks);
31506 var items = Roo.apply([], this.bricks);
31508 if(this.isHorizontal){
31509 this._horizontalLayoutItems( items , isInstant );
31513 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31514 // this._verticalAlternativeLayoutItems( items , isInstant );
31518 this._verticalLayoutItems( items , isInstant );
31522 _verticalLayoutItems : function ( items , isInstant)
31524 if ( !items || !items.length ) {
31529 ['xs', 'xs', 'xs', 'tall'],
31530 ['xs', 'xs', 'tall'],
31531 ['xs', 'xs', 'sm'],
31532 ['xs', 'xs', 'xs'],
31538 ['sm', 'xs', 'xs'],
31542 ['tall', 'xs', 'xs', 'xs'],
31543 ['tall', 'xs', 'xs'],
31555 Roo.each(items, function(item, k){
31557 switch (item.size) {
31558 // these layouts take up a full box,
31569 boxes.push([item]);
31592 var filterPattern = function(box, length)
31600 var pattern = box.slice(0, length);
31604 Roo.each(pattern, function(i){
31605 format.push(i.size);
31608 Roo.each(standard, function(s){
31610 if(String(s) != String(format)){
31619 if(!match && length == 1){
31624 filterPattern(box, length - 1);
31628 queue.push(pattern);
31630 box = box.slice(length, box.length);
31632 filterPattern(box, 4);
31638 Roo.each(boxes, function(box, k){
31644 if(box.length == 1){
31649 filterPattern(box, 4);
31653 this._processVerticalLayoutQueue( queue, isInstant );
31657 // _verticalAlternativeLayoutItems : function( items , isInstant )
31659 // if ( !items || !items.length ) {
31663 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31667 _horizontalLayoutItems : function ( items , isInstant)
31669 if ( !items || !items.length || items.length < 3) {
31675 var eItems = items.slice(0, 3);
31677 items = items.slice(3, items.length);
31680 ['xs', 'xs', 'xs', 'wide'],
31681 ['xs', 'xs', 'wide'],
31682 ['xs', 'xs', 'sm'],
31683 ['xs', 'xs', 'xs'],
31689 ['sm', 'xs', 'xs'],
31693 ['wide', 'xs', 'xs', 'xs'],
31694 ['wide', 'xs', 'xs'],
31707 Roo.each(items, function(item, k){
31709 switch (item.size) {
31720 boxes.push([item]);
31744 var filterPattern = function(box, length)
31752 var pattern = box.slice(0, length);
31756 Roo.each(pattern, function(i){
31757 format.push(i.size);
31760 Roo.each(standard, function(s){
31762 if(String(s) != String(format)){
31771 if(!match && length == 1){
31776 filterPattern(box, length - 1);
31780 queue.push(pattern);
31782 box = box.slice(length, box.length);
31784 filterPattern(box, 4);
31790 Roo.each(boxes, function(box, k){
31796 if(box.length == 1){
31801 filterPattern(box, 4);
31808 var pos = this.el.getBox(true);
31812 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31814 var hit_end = false;
31816 Roo.each(queue, function(box){
31820 Roo.each(box, function(b){
31822 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31832 Roo.each(box, function(b){
31834 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31837 mx = Math.max(mx, b.x);
31841 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31845 Roo.each(box, function(b){
31847 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31861 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31864 /** Sets position of item in DOM
31865 * @param {Element} item
31866 * @param {Number} x - horizontal position
31867 * @param {Number} y - vertical position
31868 * @param {Boolean} isInstant - disables transitions
31870 _processVerticalLayoutQueue : function( queue, isInstant )
31872 var pos = this.el.getBox(true);
31877 for (var i = 0; i < this.cols; i++){
31881 Roo.each(queue, function(box, k){
31883 var col = k % this.cols;
31885 Roo.each(box, function(b,kk){
31887 b.el.position('absolute');
31889 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31890 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31892 if(b.size == 'md-left' || b.size == 'md-right'){
31893 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31894 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31897 b.el.setWidth(width);
31898 b.el.setHeight(height);
31900 b.el.select('iframe',true).setSize(width,height);
31904 for (var i = 0; i < this.cols; i++){
31906 if(maxY[i] < maxY[col]){
31911 col = Math.min(col, i);
31915 x = pos.x + col * (this.colWidth + this.padWidth);
31919 var positions = [];
31921 switch (box.length){
31923 positions = this.getVerticalOneBoxColPositions(x, y, box);
31926 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31929 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31932 positions = this.getVerticalFourBoxColPositions(x, y, box);
31938 Roo.each(box, function(b,kk){
31940 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31942 var sz = b.el.getSize();
31944 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31952 for (var i = 0; i < this.cols; i++){
31953 mY = Math.max(mY, maxY[i]);
31956 this.el.setHeight(mY - pos.y);
31960 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31962 // var pos = this.el.getBox(true);
31965 // var maxX = pos.right;
31967 // var maxHeight = 0;
31969 // Roo.each(items, function(item, k){
31973 // item.el.position('absolute');
31975 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31977 // item.el.setWidth(width);
31979 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31981 // item.el.setHeight(height);
31984 // item.el.setXY([x, y], isInstant ? false : true);
31986 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31989 // y = y + height + this.alternativePadWidth;
31991 // maxHeight = maxHeight + height + this.alternativePadWidth;
31995 // this.el.setHeight(maxHeight);
31999 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
32001 var pos = this.el.getBox(true);
32006 var maxX = pos.right;
32008 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
32010 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32012 Roo.each(queue, function(box, k){
32014 Roo.each(box, function(b, kk){
32016 b.el.position('absolute');
32018 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32019 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32021 if(b.size == 'md-left' || b.size == 'md-right'){
32022 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32023 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32026 b.el.setWidth(width);
32027 b.el.setHeight(height);
32035 var positions = [];
32037 switch (box.length){
32039 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32042 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
32045 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
32048 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
32054 Roo.each(box, function(b,kk){
32056 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32058 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
32066 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
32068 Roo.each(eItems, function(b,k){
32070 b.size = (k == 0) ? 'sm' : 'xs';
32071 b.x = (k == 0) ? 2 : 1;
32072 b.y = (k == 0) ? 2 : 1;
32074 b.el.position('absolute');
32076 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32078 b.el.setWidth(width);
32080 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32082 b.el.setHeight(height);
32086 var positions = [];
32089 x : maxX - this.unitWidth * 2 - this.gutter,
32094 x : maxX - this.unitWidth,
32095 y : minY + (this.unitWidth + this.gutter) * 2
32099 x : maxX - this.unitWidth * 3 - this.gutter * 2,
32103 Roo.each(eItems, function(b,k){
32105 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32111 getVerticalOneBoxColPositions : function(x, y, box)
32115 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32117 if(box[0].size == 'md-left'){
32121 if(box[0].size == 'md-right'){
32126 x : x + (this.unitWidth + this.gutter) * rand,
32133 getVerticalTwoBoxColPositions : function(x, y, box)
32137 if(box[0].size == 'xs'){
32141 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32145 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32159 x : x + (this.unitWidth + this.gutter) * 2,
32160 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32167 getVerticalThreeBoxColPositions : function(x, y, box)
32171 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32179 x : x + (this.unitWidth + this.gutter) * 1,
32184 x : x + (this.unitWidth + this.gutter) * 2,
32192 if(box[0].size == 'xs' && box[1].size == 'xs'){
32201 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32205 x : x + (this.unitWidth + this.gutter) * 1,
32219 x : x + (this.unitWidth + this.gutter) * 2,
32224 x : x + (this.unitWidth + this.gutter) * 2,
32225 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32232 getVerticalFourBoxColPositions : function(x, y, box)
32236 if(box[0].size == 'xs'){
32245 y : y + (this.unitHeight + this.gutter) * 1
32250 y : y + (this.unitHeight + this.gutter) * 2
32254 x : x + (this.unitWidth + this.gutter) * 1,
32268 x : x + (this.unitWidth + this.gutter) * 2,
32273 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32274 y : y + (this.unitHeight + this.gutter) * 1
32278 x : x + (this.unitWidth + this.gutter) * 2,
32279 y : y + (this.unitWidth + this.gutter) * 2
32286 getHorizontalOneBoxColPositions : function(maxX, minY, box)
32290 if(box[0].size == 'md-left'){
32292 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32299 if(box[0].size == 'md-right'){
32301 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32302 y : minY + (this.unitWidth + this.gutter) * 1
32308 var rand = Math.floor(Math.random() * (4 - box[0].y));
32311 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32312 y : minY + (this.unitWidth + this.gutter) * rand
32319 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32323 if(box[0].size == 'xs'){
32326 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32331 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32332 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32340 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32345 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32346 y : minY + (this.unitWidth + this.gutter) * 2
32353 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32357 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32360 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32365 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32366 y : minY + (this.unitWidth + this.gutter) * 1
32370 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32371 y : minY + (this.unitWidth + this.gutter) * 2
32378 if(box[0].size == 'xs' && box[1].size == 'xs'){
32381 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32386 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32391 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32392 y : minY + (this.unitWidth + this.gutter) * 1
32400 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32405 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32406 y : minY + (this.unitWidth + this.gutter) * 2
32410 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32411 y : minY + (this.unitWidth + this.gutter) * 2
32418 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32422 if(box[0].size == 'xs'){
32425 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32430 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32435 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),
32440 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32441 y : minY + (this.unitWidth + this.gutter) * 1
32449 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32454 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32455 y : minY + (this.unitWidth + this.gutter) * 2
32459 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32460 y : minY + (this.unitWidth + this.gutter) * 2
32464 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),
32465 y : minY + (this.unitWidth + this.gutter) * 2
32473 * remove a Masonry Brick
32474 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32476 removeBrick : function(brick_id)
32482 for (var i = 0; i<this.bricks.length; i++) {
32483 if (this.bricks[i].id == brick_id) {
32484 this.bricks.splice(i,1);
32485 this.el.dom.removeChild(Roo.get(brick_id).dom);
32492 * adds a Masonry Brick
32493 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32495 addBrick : function(cfg)
32497 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32498 //this.register(cn);
32499 cn.parentId = this.id;
32500 cn.render(this.el);
32505 * register a Masonry Brick
32506 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32509 register : function(brick)
32511 this.bricks.push(brick);
32512 brick.masonryId = this.id;
32516 * clear all the Masonry Brick
32518 clearAll : function()
32521 //this.getChildContainer().dom.innerHTML = "";
32522 this.el.dom.innerHTML = '';
32525 getSelected : function()
32527 if (!this.selectedBrick) {
32531 return this.selectedBrick;
32535 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32539 * register a Masonry Layout
32540 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32543 register : function(layout)
32545 this.groups[layout.id] = layout;
32548 * fetch a Masonry Layout based on the masonry layout ID
32549 * @param {string} the masonry layout to add
32550 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32553 get: function(layout_id) {
32554 if (typeof(this.groups[layout_id]) == 'undefined') {
32557 return this.groups[layout_id] ;
32569 * http://masonry.desandro.com
32571 * The idea is to render all the bricks based on vertical width...
32573 * The original code extends 'outlayer' - we might need to use that....
32579 * @class Roo.bootstrap.LayoutMasonryAuto
32580 * @extends Roo.bootstrap.Component
32581 * Bootstrap Layout Masonry class
32584 * Create a new Element
32585 * @param {Object} config The config object
32588 Roo.bootstrap.LayoutMasonryAuto = function(config){
32589 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32592 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32595 * @cfg {Boolean} isFitWidth - resize the width..
32597 isFitWidth : false, // options..
32599 * @cfg {Boolean} isOriginLeft = left align?
32601 isOriginLeft : true,
32603 * @cfg {Boolean} isOriginTop = top align?
32605 isOriginTop : false,
32607 * @cfg {Boolean} isLayoutInstant = no animation?
32609 isLayoutInstant : false, // needed?
32611 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32613 isResizingContainer : true,
32615 * @cfg {Number} columnWidth width of the columns
32621 * @cfg {Number} maxCols maximum number of columns
32626 * @cfg {Number} padHeight padding below box..
32632 * @cfg {Boolean} isAutoInitial defalut true
32635 isAutoInitial : true,
32641 initialColumnWidth : 0,
32642 currentSize : null,
32644 colYs : null, // array.
32651 bricks: null, //CompositeElement
32652 cols : 0, // array?
32653 // element : null, // wrapped now this.el
32654 _isLayoutInited : null,
32657 getAutoCreate : function(){
32661 cls: 'blog-masonary-wrapper ' + this.cls,
32663 cls : 'mas-boxes masonary'
32670 getChildContainer: function( )
32672 if (this.boxesEl) {
32673 return this.boxesEl;
32676 this.boxesEl = this.el.select('.mas-boxes').first();
32678 return this.boxesEl;
32682 initEvents : function()
32686 if(this.isAutoInitial){
32687 Roo.log('hook children rendered');
32688 this.on('childrenrendered', function() {
32689 Roo.log('children rendered');
32696 initial : function()
32698 this.reloadItems();
32700 this.currentSize = this.el.getBox(true);
32702 /// was window resize... - let's see if this works..
32703 Roo.EventManager.onWindowResize(this.resize, this);
32705 if(!this.isAutoInitial){
32710 this.layout.defer(500,this);
32713 reloadItems: function()
32715 this.bricks = this.el.select('.masonry-brick', true);
32717 this.bricks.each(function(b) {
32718 //Roo.log(b.getSize());
32719 if (!b.attr('originalwidth')) {
32720 b.attr('originalwidth', b.getSize().width);
32725 Roo.log(this.bricks.elements.length);
32728 resize : function()
32731 var cs = this.el.getBox(true);
32733 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32734 Roo.log("no change in with or X");
32737 this.currentSize = cs;
32741 layout : function()
32744 this._resetLayout();
32745 //this._manageStamps();
32747 // don't animate first layout
32748 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32749 this.layoutItems( isInstant );
32751 // flag for initalized
32752 this._isLayoutInited = true;
32755 layoutItems : function( isInstant )
32757 //var items = this._getItemsForLayout( this.items );
32758 // original code supports filtering layout items.. we just ignore it..
32760 this._layoutItems( this.bricks , isInstant );
32762 this._postLayout();
32764 _layoutItems : function ( items , isInstant)
32766 //this.fireEvent( 'layout', this, items );
32769 if ( !items || !items.elements.length ) {
32770 // no items, emit event with empty array
32775 items.each(function(item) {
32776 Roo.log("layout item");
32778 // get x/y object from method
32779 var position = this._getItemLayoutPosition( item );
32781 position.item = item;
32782 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32783 queue.push( position );
32786 this._processLayoutQueue( queue );
32788 /** Sets position of item in DOM
32789 * @param {Element} item
32790 * @param {Number} x - horizontal position
32791 * @param {Number} y - vertical position
32792 * @param {Boolean} isInstant - disables transitions
32794 _processLayoutQueue : function( queue )
32796 for ( var i=0, len = queue.length; i < len; i++ ) {
32797 var obj = queue[i];
32798 obj.item.position('absolute');
32799 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32805 * Any logic you want to do after each layout,
32806 * i.e. size the container
32808 _postLayout : function()
32810 this.resizeContainer();
32813 resizeContainer : function()
32815 if ( !this.isResizingContainer ) {
32818 var size = this._getContainerSize();
32820 this.el.setSize(size.width,size.height);
32821 this.boxesEl.setSize(size.width,size.height);
32827 _resetLayout : function()
32829 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32830 this.colWidth = this.el.getWidth();
32831 //this.gutter = this.el.getWidth();
32833 this.measureColumns();
32839 this.colYs.push( 0 );
32845 measureColumns : function()
32847 this.getContainerWidth();
32848 // if columnWidth is 0, default to outerWidth of first item
32849 if ( !this.columnWidth ) {
32850 var firstItem = this.bricks.first();
32851 Roo.log(firstItem);
32852 this.columnWidth = this.containerWidth;
32853 if (firstItem && firstItem.attr('originalwidth') ) {
32854 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32856 // columnWidth fall back to item of first element
32857 Roo.log("set column width?");
32858 this.initialColumnWidth = this.columnWidth ;
32860 // if first elem has no width, default to size of container
32865 if (this.initialColumnWidth) {
32866 this.columnWidth = this.initialColumnWidth;
32871 // column width is fixed at the top - however if container width get's smaller we should
32874 // this bit calcs how man columns..
32876 var columnWidth = this.columnWidth += this.gutter;
32878 // calculate columns
32879 var containerWidth = this.containerWidth + this.gutter;
32881 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32882 // fix rounding errors, typically with gutters
32883 var excess = columnWidth - containerWidth % columnWidth;
32886 // if overshoot is less than a pixel, round up, otherwise floor it
32887 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32888 cols = Math[ mathMethod ]( cols );
32889 this.cols = Math.max( cols, 1 );
32890 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32892 // padding positioning..
32893 var totalColWidth = this.cols * this.columnWidth;
32894 var padavail = this.containerWidth - totalColWidth;
32895 // so for 2 columns - we need 3 'pads'
32897 var padNeeded = (1+this.cols) * this.padWidth;
32899 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32901 this.columnWidth += padExtra
32902 //this.padWidth = Math.floor(padavail / ( this.cols));
32904 // adjust colum width so that padding is fixed??
32906 // we have 3 columns ... total = width * 3
32907 // we have X left over... that should be used by
32909 //if (this.expandC) {
32917 getContainerWidth : function()
32919 /* // container is parent if fit width
32920 var container = this.isFitWidth ? this.element.parentNode : this.element;
32921 // check that this.size and size are there
32922 // IE8 triggers resize on body size change, so they might not be
32924 var size = getSize( container ); //FIXME
32925 this.containerWidth = size && size.innerWidth; //FIXME
32928 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32932 _getItemLayoutPosition : function( item ) // what is item?
32934 // we resize the item to our columnWidth..
32936 item.setWidth(this.columnWidth);
32937 item.autoBoxAdjust = false;
32939 var sz = item.getSize();
32941 // how many columns does this brick span
32942 var remainder = this.containerWidth % this.columnWidth;
32944 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32945 // round if off by 1 pixel, otherwise use ceil
32946 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32947 colSpan = Math.min( colSpan, this.cols );
32949 // normally this should be '1' as we dont' currently allow multi width columns..
32951 var colGroup = this._getColGroup( colSpan );
32952 // get the minimum Y value from the columns
32953 var minimumY = Math.min.apply( Math, colGroup );
32954 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32956 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32958 // position the brick
32960 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32961 y: this.currentSize.y + minimumY + this.padHeight
32965 // apply setHeight to necessary columns
32966 var setHeight = minimumY + sz.height + this.padHeight;
32967 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32969 var setSpan = this.cols + 1 - colGroup.length;
32970 for ( var i = 0; i < setSpan; i++ ) {
32971 this.colYs[ shortColIndex + i ] = setHeight ;
32978 * @param {Number} colSpan - number of columns the element spans
32979 * @returns {Array} colGroup
32981 _getColGroup : function( colSpan )
32983 if ( colSpan < 2 ) {
32984 // if brick spans only one column, use all the column Ys
32989 // how many different places could this brick fit horizontally
32990 var groupCount = this.cols + 1 - colSpan;
32991 // for each group potential horizontal position
32992 for ( var i = 0; i < groupCount; i++ ) {
32993 // make an array of colY values for that one group
32994 var groupColYs = this.colYs.slice( i, i + colSpan );
32995 // and get the max value of the array
32996 colGroup[i] = Math.max.apply( Math, groupColYs );
33001 _manageStamp : function( stamp )
33003 var stampSize = stamp.getSize();
33004 var offset = stamp.getBox();
33005 // get the columns that this stamp affects
33006 var firstX = this.isOriginLeft ? offset.x : offset.right;
33007 var lastX = firstX + stampSize.width;
33008 var firstCol = Math.floor( firstX / this.columnWidth );
33009 firstCol = Math.max( 0, firstCol );
33011 var lastCol = Math.floor( lastX / this.columnWidth );
33012 // lastCol should not go over if multiple of columnWidth #425
33013 lastCol -= lastX % this.columnWidth ? 0 : 1;
33014 lastCol = Math.min( this.cols - 1, lastCol );
33016 // set colYs to bottom of the stamp
33017 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
33020 for ( var i = firstCol; i <= lastCol; i++ ) {
33021 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33026 _getContainerSize : function()
33028 this.maxY = Math.max.apply( Math, this.colYs );
33033 if ( this.isFitWidth ) {
33034 size.width = this._getContainerFitWidth();
33040 _getContainerFitWidth : function()
33042 var unusedCols = 0;
33043 // count unused columns
33046 if ( this.colYs[i] !== 0 ) {
33051 // fit container to columns that have been used
33052 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
33055 needsResizeLayout : function()
33057 var previousWidth = this.containerWidth;
33058 this.getContainerWidth();
33059 return previousWidth !== this.containerWidth;
33074 * @class Roo.bootstrap.MasonryBrick
33075 * @extends Roo.bootstrap.Component
33076 * Bootstrap MasonryBrick class
33079 * Create a new MasonryBrick
33080 * @param {Object} config The config object
33083 Roo.bootstrap.MasonryBrick = function(config){
33085 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
33087 Roo.bootstrap.MasonryBrick.register(this);
33093 * When a MasonryBrick is clcik
33094 * @param {Roo.bootstrap.MasonryBrick} this
33095 * @param {Roo.EventObject} e
33101 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
33104 * @cfg {String} title
33108 * @cfg {String} html
33112 * @cfg {String} bgimage
33116 * @cfg {String} videourl
33120 * @cfg {String} cls
33124 * @cfg {String} href
33128 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33133 * @cfg {String} placetitle (center|bottom)
33138 * @cfg {Boolean} isFitContainer defalut true
33140 isFitContainer : true,
33143 * @cfg {Boolean} preventDefault defalut false
33145 preventDefault : false,
33148 * @cfg {Boolean} inverse defalut false
33150 maskInverse : false,
33152 getAutoCreate : function()
33154 if(!this.isFitContainer){
33155 return this.getSplitAutoCreate();
33158 var cls = 'masonry-brick masonry-brick-full';
33160 if(this.href.length){
33161 cls += ' masonry-brick-link';
33164 if(this.bgimage.length){
33165 cls += ' masonry-brick-image';
33168 if(this.maskInverse){
33169 cls += ' mask-inverse';
33172 if(!this.html.length && !this.maskInverse && !this.videourl.length){
33173 cls += ' enable-mask';
33177 cls += ' masonry-' + this.size + '-brick';
33180 if(this.placetitle.length){
33182 switch (this.placetitle) {
33184 cls += ' masonry-center-title';
33187 cls += ' masonry-bottom-title';
33194 if(!this.html.length && !this.bgimage.length){
33195 cls += ' masonry-center-title';
33198 if(!this.html.length && this.bgimage.length){
33199 cls += ' masonry-bottom-title';
33204 cls += ' ' + this.cls;
33208 tag: (this.href.length) ? 'a' : 'div',
33213 cls: 'masonry-brick-mask'
33217 cls: 'masonry-brick-paragraph',
33223 if(this.href.length){
33224 cfg.href = this.href;
33227 var cn = cfg.cn[1].cn;
33229 if(this.title.length){
33232 cls: 'masonry-brick-title',
33237 if(this.html.length){
33240 cls: 'masonry-brick-text',
33245 if (!this.title.length && !this.html.length) {
33246 cfg.cn[1].cls += ' hide';
33249 if(this.bgimage.length){
33252 cls: 'masonry-brick-image-view',
33257 if(this.videourl.length){
33258 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33259 // youtube support only?
33262 cls: 'masonry-brick-image-view',
33265 allowfullscreen : true
33273 getSplitAutoCreate : function()
33275 var cls = 'masonry-brick masonry-brick-split';
33277 if(this.href.length){
33278 cls += ' masonry-brick-link';
33281 if(this.bgimage.length){
33282 cls += ' masonry-brick-image';
33286 cls += ' masonry-' + this.size + '-brick';
33289 switch (this.placetitle) {
33291 cls += ' masonry-center-title';
33294 cls += ' masonry-bottom-title';
33297 if(!this.bgimage.length){
33298 cls += ' masonry-center-title';
33301 if(this.bgimage.length){
33302 cls += ' masonry-bottom-title';
33308 cls += ' ' + this.cls;
33312 tag: (this.href.length) ? 'a' : 'div',
33317 cls: 'masonry-brick-split-head',
33321 cls: 'masonry-brick-paragraph',
33328 cls: 'masonry-brick-split-body',
33334 if(this.href.length){
33335 cfg.href = this.href;
33338 if(this.title.length){
33339 cfg.cn[0].cn[0].cn.push({
33341 cls: 'masonry-brick-title',
33346 if(this.html.length){
33347 cfg.cn[1].cn.push({
33349 cls: 'masonry-brick-text',
33354 if(this.bgimage.length){
33355 cfg.cn[0].cn.push({
33357 cls: 'masonry-brick-image-view',
33362 if(this.videourl.length){
33363 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33364 // youtube support only?
33365 cfg.cn[0].cn.cn.push({
33367 cls: 'masonry-brick-image-view',
33370 allowfullscreen : true
33377 initEvents: function()
33379 switch (this.size) {
33412 this.el.on('touchstart', this.onTouchStart, this);
33413 this.el.on('touchmove', this.onTouchMove, this);
33414 this.el.on('touchend', this.onTouchEnd, this);
33415 this.el.on('contextmenu', this.onContextMenu, this);
33417 this.el.on('mouseenter' ,this.enter, this);
33418 this.el.on('mouseleave', this.leave, this);
33419 this.el.on('click', this.onClick, this);
33422 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33423 this.parent().bricks.push(this);
33428 onClick: function(e, el)
33430 var time = this.endTimer - this.startTimer;
33431 // Roo.log(e.preventDefault());
33434 e.preventDefault();
33439 if(!this.preventDefault){
33443 e.preventDefault();
33445 if (this.activeClass != '') {
33446 this.selectBrick();
33449 this.fireEvent('click', this, e);
33452 enter: function(e, el)
33454 e.preventDefault();
33456 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33460 if(this.bgimage.length && this.html.length){
33461 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33465 leave: function(e, el)
33467 e.preventDefault();
33469 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33473 if(this.bgimage.length && this.html.length){
33474 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33478 onTouchStart: function(e, el)
33480 // e.preventDefault();
33482 this.touchmoved = false;
33484 if(!this.isFitContainer){
33488 if(!this.bgimage.length || !this.html.length){
33492 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33494 this.timer = new Date().getTime();
33498 onTouchMove: function(e, el)
33500 this.touchmoved = true;
33503 onContextMenu : function(e,el)
33505 e.preventDefault();
33506 e.stopPropagation();
33510 onTouchEnd: function(e, el)
33512 // e.preventDefault();
33514 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33521 if(!this.bgimage.length || !this.html.length){
33523 if(this.href.length){
33524 window.location.href = this.href;
33530 if(!this.isFitContainer){
33534 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33536 window.location.href = this.href;
33539 //selection on single brick only
33540 selectBrick : function() {
33542 if (!this.parentId) {
33546 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33547 var index = m.selectedBrick.indexOf(this.id);
33550 m.selectedBrick.splice(index,1);
33551 this.el.removeClass(this.activeClass);
33555 for(var i = 0; i < m.selectedBrick.length; i++) {
33556 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33557 b.el.removeClass(b.activeClass);
33560 m.selectedBrick = [];
33562 m.selectedBrick.push(this.id);
33563 this.el.addClass(this.activeClass);
33567 isSelected : function(){
33568 return this.el.hasClass(this.activeClass);
33573 Roo.apply(Roo.bootstrap.MasonryBrick, {
33576 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33578 * register a Masonry Brick
33579 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33582 register : function(brick)
33584 //this.groups[brick.id] = brick;
33585 this.groups.add(brick.id, brick);
33588 * fetch a masonry brick based on the masonry brick ID
33589 * @param {string} the masonry brick to add
33590 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33593 get: function(brick_id)
33595 // if (typeof(this.groups[brick_id]) == 'undefined') {
33598 // return this.groups[brick_id] ;
33600 if(this.groups.key(brick_id)) {
33601 return this.groups.key(brick_id);
33619 * @class Roo.bootstrap.Brick
33620 * @extends Roo.bootstrap.Component
33621 * Bootstrap Brick class
33624 * Create a new Brick
33625 * @param {Object} config The config object
33628 Roo.bootstrap.Brick = function(config){
33629 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33635 * When a Brick is click
33636 * @param {Roo.bootstrap.Brick} this
33637 * @param {Roo.EventObject} e
33643 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33646 * @cfg {String} title
33650 * @cfg {String} html
33654 * @cfg {String} bgimage
33658 * @cfg {String} cls
33662 * @cfg {String} href
33666 * @cfg {String} video
33670 * @cfg {Boolean} square
33674 getAutoCreate : function()
33676 var cls = 'roo-brick';
33678 if(this.href.length){
33679 cls += ' roo-brick-link';
33682 if(this.bgimage.length){
33683 cls += ' roo-brick-image';
33686 if(!this.html.length && !this.bgimage.length){
33687 cls += ' roo-brick-center-title';
33690 if(!this.html.length && this.bgimage.length){
33691 cls += ' roo-brick-bottom-title';
33695 cls += ' ' + this.cls;
33699 tag: (this.href.length) ? 'a' : 'div',
33704 cls: 'roo-brick-paragraph',
33710 if(this.href.length){
33711 cfg.href = this.href;
33714 var cn = cfg.cn[0].cn;
33716 if(this.title.length){
33719 cls: 'roo-brick-title',
33724 if(this.html.length){
33727 cls: 'roo-brick-text',
33734 if(this.bgimage.length){
33737 cls: 'roo-brick-image-view',
33745 initEvents: function()
33747 if(this.title.length || this.html.length){
33748 this.el.on('mouseenter' ,this.enter, this);
33749 this.el.on('mouseleave', this.leave, this);
33752 Roo.EventManager.onWindowResize(this.resize, this);
33754 if(this.bgimage.length){
33755 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33756 this.imageEl.on('load', this.onImageLoad, this);
33763 onImageLoad : function()
33768 resize : function()
33770 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33772 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33774 if(this.bgimage.length){
33775 var image = this.el.select('.roo-brick-image-view', true).first();
33777 image.setWidth(paragraph.getWidth());
33780 image.setHeight(paragraph.getWidth());
33783 this.el.setHeight(image.getHeight());
33784 paragraph.setHeight(image.getHeight());
33790 enter: function(e, el)
33792 e.preventDefault();
33794 if(this.bgimage.length){
33795 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33796 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33800 leave: function(e, el)
33802 e.preventDefault();
33804 if(this.bgimage.length){
33805 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33806 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33821 * @class Roo.bootstrap.NumberField
33822 * @extends Roo.bootstrap.Input
33823 * Bootstrap NumberField class
33829 * Create a new NumberField
33830 * @param {Object} config The config object
33833 Roo.bootstrap.NumberField = function(config){
33834 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33837 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33840 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33842 allowDecimals : true,
33844 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33846 decimalSeparator : ".",
33848 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33850 decimalPrecision : 2,
33852 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33854 allowNegative : true,
33857 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33861 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33863 minValue : Number.NEGATIVE_INFINITY,
33865 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33867 maxValue : Number.MAX_VALUE,
33869 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33871 minText : "The minimum value for this field is {0}",
33873 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33875 maxText : "The maximum value for this field is {0}",
33877 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33878 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33880 nanText : "{0} is not a valid number",
33882 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33884 thousandsDelimiter : false,
33886 * @cfg {String} valueAlign alignment of value
33888 valueAlign : "left",
33890 getAutoCreate : function()
33892 var hiddenInput = {
33896 cls: 'hidden-number-input'
33900 hiddenInput.name = this.name;
33905 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33907 this.name = hiddenInput.name;
33909 if(cfg.cn.length > 0) {
33910 cfg.cn.push(hiddenInput);
33917 initEvents : function()
33919 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33921 var allowed = "0123456789";
33923 if(this.allowDecimals){
33924 allowed += this.decimalSeparator;
33927 if(this.allowNegative){
33931 if(this.thousandsDelimiter) {
33935 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33937 var keyPress = function(e){
33939 var k = e.getKey();
33941 var c = e.getCharCode();
33944 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33945 allowed.indexOf(String.fromCharCode(c)) === -1
33951 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33955 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33960 this.el.on("keypress", keyPress, this);
33963 validateValue : function(value)
33966 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33970 var num = this.parseValue(value);
33973 this.markInvalid(String.format(this.nanText, value));
33977 if(num < this.minValue){
33978 this.markInvalid(String.format(this.minText, this.minValue));
33982 if(num > this.maxValue){
33983 this.markInvalid(String.format(this.maxText, this.maxValue));
33990 getValue : function()
33992 var v = this.hiddenEl().getValue();
33994 return this.fixPrecision(this.parseValue(v));
33997 parseValue : function(value)
33999 if(this.thousandsDelimiter) {
34001 r = new RegExp(",", "g");
34002 value = value.replace(r, "");
34005 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
34006 return isNaN(value) ? '' : value;
34009 fixPrecision : function(value)
34011 if(this.thousandsDelimiter) {
34013 r = new RegExp(",", "g");
34014 value = value.replace(r, "");
34017 var nan = isNaN(value);
34019 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
34020 return nan ? '' : value;
34022 return parseFloat(value).toFixed(this.decimalPrecision);
34025 setValue : function(v)
34027 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34033 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34035 this.inputEl().dom.value = (v == '') ? '' :
34036 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34038 if(!this.allowZero && v === '0') {
34039 this.hiddenEl().dom.value = '';
34040 this.inputEl().dom.value = '';
34047 decimalPrecisionFcn : function(v)
34049 return Math.floor(v);
34052 beforeBlur : function()
34054 var v = this.parseValue(this.getRawValue());
34056 if(v || v === 0 || v === ''){
34061 hiddenEl : function()
34063 return this.el.select('input.hidden-number-input',true).first();
34075 * @class Roo.bootstrap.DocumentSlider
34076 * @extends Roo.bootstrap.Component
34077 * Bootstrap DocumentSlider class
34080 * Create a new DocumentViewer
34081 * @param {Object} config The config object
34084 Roo.bootstrap.DocumentSlider = function(config){
34085 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
34092 * Fire after initEvent
34093 * @param {Roo.bootstrap.DocumentSlider} this
34098 * Fire after update
34099 * @param {Roo.bootstrap.DocumentSlider} this
34105 * @param {Roo.bootstrap.DocumentSlider} this
34111 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
34117 getAutoCreate : function()
34121 cls : 'roo-document-slider',
34125 cls : 'roo-document-slider-header',
34129 cls : 'roo-document-slider-header-title'
34135 cls : 'roo-document-slider-body',
34139 cls : 'roo-document-slider-prev',
34143 cls : 'fa fa-chevron-left'
34149 cls : 'roo-document-slider-thumb',
34153 cls : 'roo-document-slider-image'
34159 cls : 'roo-document-slider-next',
34163 cls : 'fa fa-chevron-right'
34175 initEvents : function()
34177 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34178 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34180 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34181 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34183 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34184 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34186 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34187 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34189 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34190 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34192 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34193 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34195 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34196 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34198 this.thumbEl.on('click', this.onClick, this);
34200 this.prevIndicator.on('click', this.prev, this);
34202 this.nextIndicator.on('click', this.next, this);
34206 initial : function()
34208 if(this.files.length){
34209 this.indicator = 1;
34213 this.fireEvent('initial', this);
34216 update : function()
34218 this.imageEl.attr('src', this.files[this.indicator - 1]);
34220 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34222 this.prevIndicator.show();
34224 if(this.indicator == 1){
34225 this.prevIndicator.hide();
34228 this.nextIndicator.show();
34230 if(this.indicator == this.files.length){
34231 this.nextIndicator.hide();
34234 this.thumbEl.scrollTo('top');
34236 this.fireEvent('update', this);
34239 onClick : function(e)
34241 e.preventDefault();
34243 this.fireEvent('click', this);
34248 e.preventDefault();
34250 this.indicator = Math.max(1, this.indicator - 1);
34257 e.preventDefault();
34259 this.indicator = Math.min(this.files.length, this.indicator + 1);
34273 * @class Roo.bootstrap.RadioSet
34274 * @extends Roo.bootstrap.Input
34275 * Bootstrap RadioSet class
34276 * @cfg {String} indicatorpos (left|right) default left
34277 * @cfg {Boolean} inline (true|false) inline the element (default true)
34278 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34280 * Create a new RadioSet
34281 * @param {Object} config The config object
34284 Roo.bootstrap.RadioSet = function(config){
34286 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34290 Roo.bootstrap.RadioSet.register(this);
34295 * Fires when the element is checked or unchecked.
34296 * @param {Roo.bootstrap.RadioSet} this This radio
34297 * @param {Roo.bootstrap.Radio} item The checked item
34302 * Fires when the element is click.
34303 * @param {Roo.bootstrap.RadioSet} this This radio set
34304 * @param {Roo.bootstrap.Radio} item The checked item
34305 * @param {Roo.EventObject} e The event object
34312 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
34320 indicatorpos : 'left',
34322 getAutoCreate : function()
34326 cls : 'roo-radio-set-label',
34330 html : this.fieldLabel
34334 if (Roo.bootstrap.version == 3) {
34337 if(this.indicatorpos == 'left'){
34340 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34341 tooltip : 'This field is required'
34346 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34347 tooltip : 'This field is required'
34353 cls : 'roo-radio-set-items'
34356 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34358 if (align === 'left' && this.fieldLabel.length) {
34361 cls : "roo-radio-set-right",
34367 if(this.labelWidth > 12){
34368 label.style = "width: " + this.labelWidth + 'px';
34371 if(this.labelWidth < 13 && this.labelmd == 0){
34372 this.labelmd = this.labelWidth;
34375 if(this.labellg > 0){
34376 label.cls += ' col-lg-' + this.labellg;
34377 items.cls += ' col-lg-' + (12 - this.labellg);
34380 if(this.labelmd > 0){
34381 label.cls += ' col-md-' + this.labelmd;
34382 items.cls += ' col-md-' + (12 - this.labelmd);
34385 if(this.labelsm > 0){
34386 label.cls += ' col-sm-' + this.labelsm;
34387 items.cls += ' col-sm-' + (12 - this.labelsm);
34390 if(this.labelxs > 0){
34391 label.cls += ' col-xs-' + this.labelxs;
34392 items.cls += ' col-xs-' + (12 - this.labelxs);
34398 cls : 'roo-radio-set',
34402 cls : 'roo-radio-set-input',
34405 value : this.value ? this.value : ''
34412 if(this.weight.length){
34413 cfg.cls += ' roo-radio-' + this.weight;
34417 cfg.cls += ' roo-radio-set-inline';
34421 ['xs','sm','md','lg'].map(function(size){
34422 if (settings[size]) {
34423 cfg.cls += ' col-' + size + '-' + settings[size];
34431 initEvents : function()
34433 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34434 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34436 if(!this.fieldLabel.length){
34437 this.labelEl.hide();
34440 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34441 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34443 this.indicator = this.indicatorEl();
34445 if(this.indicator){
34446 this.indicator.addClass('invisible');
34449 this.originalValue = this.getValue();
34453 inputEl: function ()
34455 return this.el.select('.roo-radio-set-input', true).first();
34458 getChildContainer : function()
34460 return this.itemsEl;
34463 register : function(item)
34465 this.radioes.push(item);
34469 validate : function()
34471 if(this.getVisibilityEl().hasClass('hidden')){
34477 Roo.each(this.radioes, function(i){
34486 if(this.allowBlank) {
34490 if(this.disabled || valid){
34495 this.markInvalid();
34500 markValid : function()
34502 if(this.labelEl.isVisible(true) && this.indicatorEl()){
34503 this.indicatorEl().removeClass('visible');
34504 this.indicatorEl().addClass('invisible');
34508 if (Roo.bootstrap.version == 3) {
34509 this.el.removeClass([this.invalidClass, this.validClass]);
34510 this.el.addClass(this.validClass);
34512 this.el.removeClass(['is-invalid','is-valid']);
34513 this.el.addClass(['is-valid']);
34515 this.fireEvent('valid', this);
34518 markInvalid : function(msg)
34520 if(this.allowBlank || this.disabled){
34524 if(this.labelEl.isVisible(true) && this.indicatorEl()){
34525 this.indicatorEl().removeClass('invisible');
34526 this.indicatorEl().addClass('visible');
34528 if (Roo.bootstrap.version == 3) {
34529 this.el.removeClass([this.invalidClass, this.validClass]);
34530 this.el.addClass(this.invalidClass);
34532 this.el.removeClass(['is-invalid','is-valid']);
34533 this.el.addClass(['is-invalid']);
34536 this.fireEvent('invalid', this, msg);
34540 setValue : function(v, suppressEvent)
34542 if(this.value === v){
34549 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34552 Roo.each(this.radioes, function(i){
34554 i.el.removeClass('checked');
34557 Roo.each(this.radioes, function(i){
34559 if(i.value === v || i.value.toString() === v.toString()){
34561 i.el.addClass('checked');
34563 if(suppressEvent !== true){
34564 this.fireEvent('check', this, i);
34575 clearInvalid : function(){
34577 if(!this.el || this.preventMark){
34581 this.el.removeClass([this.invalidClass]);
34583 this.fireEvent('valid', this);
34588 Roo.apply(Roo.bootstrap.RadioSet, {
34592 register : function(set)
34594 this.groups[set.name] = set;
34597 get: function(name)
34599 if (typeof(this.groups[name]) == 'undefined') {
34603 return this.groups[name] ;
34609 * Ext JS Library 1.1.1
34610 * Copyright(c) 2006-2007, Ext JS, LLC.
34612 * Originally Released Under LGPL - original licence link has changed is not relivant.
34615 * <script type="text/javascript">
34620 * @class Roo.bootstrap.SplitBar
34621 * @extends Roo.util.Observable
34622 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34626 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34627 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34628 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34629 split.minSize = 100;
34630 split.maxSize = 600;
34631 split.animate = true;
34632 split.on('moved', splitterMoved);
34635 * Create a new SplitBar
34636 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34637 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34638 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34639 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34640 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34641 position of the SplitBar).
34643 Roo.bootstrap.SplitBar = function(cfg){
34648 // dragElement : elm
34649 // resizingElement: el,
34651 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34652 // placement : Roo.bootstrap.SplitBar.LEFT ,
34653 // existingProxy ???
34656 this.el = Roo.get(cfg.dragElement, true);
34657 this.el.dom.unselectable = "on";
34659 this.resizingEl = Roo.get(cfg.resizingElement, true);
34663 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34664 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34667 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34670 * The minimum size of the resizing element. (Defaults to 0)
34676 * The maximum size of the resizing element. (Defaults to 2000)
34679 this.maxSize = 2000;
34682 * Whether to animate the transition to the new size
34685 this.animate = false;
34688 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34691 this.useShim = false;
34696 if(!cfg.existingProxy){
34698 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34700 this.proxy = Roo.get(cfg.existingProxy).dom;
34703 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34706 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34709 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34712 this.dragSpecs = {};
34715 * @private The adapter to use to positon and resize elements
34717 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34718 this.adapter.init(this);
34720 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34722 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34723 this.el.addClass("roo-splitbar-h");
34726 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34727 this.el.addClass("roo-splitbar-v");
34733 * Fires when the splitter is moved (alias for {@link #event-moved})
34734 * @param {Roo.bootstrap.SplitBar} this
34735 * @param {Number} newSize the new width or height
34740 * Fires when the splitter is moved
34741 * @param {Roo.bootstrap.SplitBar} this
34742 * @param {Number} newSize the new width or height
34746 * @event beforeresize
34747 * Fires before the splitter is dragged
34748 * @param {Roo.bootstrap.SplitBar} this
34750 "beforeresize" : true,
34752 "beforeapply" : true
34755 Roo.util.Observable.call(this);
34758 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34759 onStartProxyDrag : function(x, y){
34760 this.fireEvent("beforeresize", this);
34762 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34764 o.enableDisplayMode("block");
34765 // all splitbars share the same overlay
34766 Roo.bootstrap.SplitBar.prototype.overlay = o;
34768 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34769 this.overlay.show();
34770 Roo.get(this.proxy).setDisplayed("block");
34771 var size = this.adapter.getElementSize(this);
34772 this.activeMinSize = this.getMinimumSize();;
34773 this.activeMaxSize = this.getMaximumSize();;
34774 var c1 = size - this.activeMinSize;
34775 var c2 = Math.max(this.activeMaxSize - size, 0);
34776 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34777 this.dd.resetConstraints();
34778 this.dd.setXConstraint(
34779 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34780 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34782 this.dd.setYConstraint(0, 0);
34784 this.dd.resetConstraints();
34785 this.dd.setXConstraint(0, 0);
34786 this.dd.setYConstraint(
34787 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34788 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34791 this.dragSpecs.startSize = size;
34792 this.dragSpecs.startPoint = [x, y];
34793 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34797 * @private Called after the drag operation by the DDProxy
34799 onEndProxyDrag : function(e){
34800 Roo.get(this.proxy).setDisplayed(false);
34801 var endPoint = Roo.lib.Event.getXY(e);
34803 this.overlay.hide();
34806 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34807 newSize = this.dragSpecs.startSize +
34808 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34809 endPoint[0] - this.dragSpecs.startPoint[0] :
34810 this.dragSpecs.startPoint[0] - endPoint[0]
34813 newSize = this.dragSpecs.startSize +
34814 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34815 endPoint[1] - this.dragSpecs.startPoint[1] :
34816 this.dragSpecs.startPoint[1] - endPoint[1]
34819 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34820 if(newSize != this.dragSpecs.startSize){
34821 if(this.fireEvent('beforeapply', this, newSize) !== false){
34822 this.adapter.setElementSize(this, newSize);
34823 this.fireEvent("moved", this, newSize);
34824 this.fireEvent("resize", this, newSize);
34830 * Get the adapter this SplitBar uses
34831 * @return The adapter object
34833 getAdapter : function(){
34834 return this.adapter;
34838 * Set the adapter this SplitBar uses
34839 * @param {Object} adapter A SplitBar adapter object
34841 setAdapter : function(adapter){
34842 this.adapter = adapter;
34843 this.adapter.init(this);
34847 * Gets the minimum size for the resizing element
34848 * @return {Number} The minimum size
34850 getMinimumSize : function(){
34851 return this.minSize;
34855 * Sets the minimum size for the resizing element
34856 * @param {Number} minSize The minimum size
34858 setMinimumSize : function(minSize){
34859 this.minSize = minSize;
34863 * Gets the maximum size for the resizing element
34864 * @return {Number} The maximum size
34866 getMaximumSize : function(){
34867 return this.maxSize;
34871 * Sets the maximum size for the resizing element
34872 * @param {Number} maxSize The maximum size
34874 setMaximumSize : function(maxSize){
34875 this.maxSize = maxSize;
34879 * Sets the initialize size for the resizing element
34880 * @param {Number} size The initial size
34882 setCurrentSize : function(size){
34883 var oldAnimate = this.animate;
34884 this.animate = false;
34885 this.adapter.setElementSize(this, size);
34886 this.animate = oldAnimate;
34890 * Destroy this splitbar.
34891 * @param {Boolean} removeEl True to remove the element
34893 destroy : function(removeEl){
34895 this.shim.remove();
34898 this.proxy.parentNode.removeChild(this.proxy);
34906 * @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.
34908 Roo.bootstrap.SplitBar.createProxy = function(dir){
34909 var proxy = new Roo.Element(document.createElement("div"));
34910 proxy.unselectable();
34911 var cls = 'roo-splitbar-proxy';
34912 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34913 document.body.appendChild(proxy.dom);
34918 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34919 * Default Adapter. It assumes the splitter and resizing element are not positioned
34920 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34922 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34925 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34926 // do nothing for now
34927 init : function(s){
34931 * Called before drag operations to get the current size of the resizing element.
34932 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34934 getElementSize : function(s){
34935 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34936 return s.resizingEl.getWidth();
34938 return s.resizingEl.getHeight();
34943 * Called after drag operations to set the size of the resizing element.
34944 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34945 * @param {Number} newSize The new size to set
34946 * @param {Function} onComplete A function to be invoked when resizing is complete
34948 setElementSize : function(s, newSize, onComplete){
34949 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34951 s.resizingEl.setWidth(newSize);
34953 onComplete(s, newSize);
34956 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34961 s.resizingEl.setHeight(newSize);
34963 onComplete(s, newSize);
34966 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34973 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34974 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34975 * Adapter that moves the splitter element to align with the resized sizing element.
34976 * Used with an absolute positioned SplitBar.
34977 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34978 * document.body, make sure you assign an id to the body element.
34980 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34981 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34982 this.container = Roo.get(container);
34985 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34986 init : function(s){
34987 this.basic.init(s);
34990 getElementSize : function(s){
34991 return this.basic.getElementSize(s);
34994 setElementSize : function(s, newSize, onComplete){
34995 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34998 moveSplitter : function(s){
34999 var yes = Roo.bootstrap.SplitBar;
35000 switch(s.placement){
35002 s.el.setX(s.resizingEl.getRight());
35005 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
35008 s.el.setY(s.resizingEl.getBottom());
35011 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
35018 * Orientation constant - Create a vertical SplitBar
35022 Roo.bootstrap.SplitBar.VERTICAL = 1;
35025 * Orientation constant - Create a horizontal SplitBar
35029 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35032 * Placement constant - The resizing element is to the left of the splitter element
35036 Roo.bootstrap.SplitBar.LEFT = 1;
35039 * Placement constant - The resizing element is to the right of the splitter element
35043 Roo.bootstrap.SplitBar.RIGHT = 2;
35046 * Placement constant - The resizing element is positioned above the splitter element
35050 Roo.bootstrap.SplitBar.TOP = 3;
35053 * Placement constant - The resizing element is positioned under splitter element
35057 Roo.bootstrap.SplitBar.BOTTOM = 4;
35058 Roo.namespace("Roo.bootstrap.layout");/*
35060 * Ext JS Library 1.1.1
35061 * Copyright(c) 2006-2007, Ext JS, LLC.
35063 * Originally Released Under LGPL - original licence link has changed is not relivant.
35066 * <script type="text/javascript">
35070 * @class Roo.bootstrap.layout.Manager
35071 * @extends Roo.bootstrap.Component
35072 * Base class for layout managers.
35074 Roo.bootstrap.layout.Manager = function(config)
35076 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
35082 /** false to disable window resize monitoring @type Boolean */
35083 this.monitorWindowResize = true;
35088 * Fires when a layout is performed.
35089 * @param {Roo.LayoutManager} this
35093 * @event regionresized
35094 * Fires when the user resizes a region.
35095 * @param {Roo.LayoutRegion} region The resized region
35096 * @param {Number} newSize The new size (width for east/west, height for north/south)
35098 "regionresized" : true,
35100 * @event regioncollapsed
35101 * Fires when a region is collapsed.
35102 * @param {Roo.LayoutRegion} region The collapsed region
35104 "regioncollapsed" : true,
35106 * @event regionexpanded
35107 * Fires when a region is expanded.
35108 * @param {Roo.LayoutRegion} region The expanded region
35110 "regionexpanded" : true
35112 this.updating = false;
35115 this.el = Roo.get(config.el);
35121 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35126 monitorWindowResize : true,
35132 onRender : function(ct, position)
35135 this.el = Roo.get(ct);
35138 //this.fireEvent('render',this);
35142 initEvents: function()
35146 // ie scrollbar fix
35147 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35148 document.body.scroll = "no";
35149 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35150 this.el.position('relative');
35152 this.id = this.el.id;
35153 this.el.addClass("roo-layout-container");
35154 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35155 if(this.el.dom != document.body ) {
35156 this.el.on('resize', this.layout,this);
35157 this.el.on('show', this.layout,this);
35163 * Returns true if this layout is currently being updated
35164 * @return {Boolean}
35166 isUpdating : function(){
35167 return this.updating;
35171 * Suspend the LayoutManager from doing auto-layouts while
35172 * making multiple add or remove calls
35174 beginUpdate : function(){
35175 this.updating = true;
35179 * Restore auto-layouts and optionally disable the manager from performing a layout
35180 * @param {Boolean} noLayout true to disable a layout update
35182 endUpdate : function(noLayout){
35183 this.updating = false;
35189 layout: function(){
35193 onRegionResized : function(region, newSize){
35194 this.fireEvent("regionresized", region, newSize);
35198 onRegionCollapsed : function(region){
35199 this.fireEvent("regioncollapsed", region);
35202 onRegionExpanded : function(region){
35203 this.fireEvent("regionexpanded", region);
35207 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35208 * performs box-model adjustments.
35209 * @return {Object} The size as an object {width: (the width), height: (the height)}
35211 getViewSize : function()
35214 if(this.el.dom != document.body){
35215 size = this.el.getSize();
35217 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35219 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35220 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35225 * Returns the Element this layout is bound to.
35226 * @return {Roo.Element}
35228 getEl : function(){
35233 * Returns the specified region.
35234 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35235 * @return {Roo.LayoutRegion}
35237 getRegion : function(target){
35238 return this.regions[target.toLowerCase()];
35241 onWindowResize : function(){
35242 if(this.monitorWindowResize){
35249 * Ext JS Library 1.1.1
35250 * Copyright(c) 2006-2007, Ext JS, LLC.
35252 * Originally Released Under LGPL - original licence link has changed is not relivant.
35255 * <script type="text/javascript">
35258 * @class Roo.bootstrap.layout.Border
35259 * @extends Roo.bootstrap.layout.Manager
35260 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35261 * please see: examples/bootstrap/nested.html<br><br>
35263 <b>The container the layout is rendered into can be either the body element or any other element.
35264 If it is not the body element, the container needs to either be an absolute positioned element,
35265 or you will need to add "position:relative" to the css of the container. You will also need to specify
35266 the container size if it is not the body element.</b>
35269 * Create a new Border
35270 * @param {Object} config Configuration options
35272 Roo.bootstrap.layout.Border = function(config){
35273 config = config || {};
35274 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35278 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35279 if(config[region]){
35280 config[region].region = region;
35281 this.addRegion(config[region]);
35287 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
35289 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35291 parent : false, // this might point to a 'nest' or a ???
35294 * Creates and adds a new region if it doesn't already exist.
35295 * @param {String} target The target region key (north, south, east, west or center).
35296 * @param {Object} config The regions config object
35297 * @return {BorderLayoutRegion} The new region
35299 addRegion : function(config)
35301 if(!this.regions[config.region]){
35302 var r = this.factory(config);
35303 this.bindRegion(r);
35305 return this.regions[config.region];
35309 bindRegion : function(r){
35310 this.regions[r.config.region] = r;
35312 r.on("visibilitychange", this.layout, this);
35313 r.on("paneladded", this.layout, this);
35314 r.on("panelremoved", this.layout, this);
35315 r.on("invalidated", this.layout, this);
35316 r.on("resized", this.onRegionResized, this);
35317 r.on("collapsed", this.onRegionCollapsed, this);
35318 r.on("expanded", this.onRegionExpanded, this);
35322 * Performs a layout update.
35324 layout : function()
35326 if(this.updating) {
35330 // render all the rebions if they have not been done alreayd?
35331 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35332 if(this.regions[region] && !this.regions[region].bodyEl){
35333 this.regions[region].onRender(this.el)
35337 var size = this.getViewSize();
35338 var w = size.width;
35339 var h = size.height;
35344 //var x = 0, y = 0;
35346 var rs = this.regions;
35347 var north = rs["north"];
35348 var south = rs["south"];
35349 var west = rs["west"];
35350 var east = rs["east"];
35351 var center = rs["center"];
35352 //if(this.hideOnLayout){ // not supported anymore
35353 //c.el.setStyle("display", "none");
35355 if(north && north.isVisible()){
35356 var b = north.getBox();
35357 var m = north.getMargins();
35358 b.width = w - (m.left+m.right);
35361 centerY = b.height + b.y + m.bottom;
35362 centerH -= centerY;
35363 north.updateBox(this.safeBox(b));
35365 if(south && south.isVisible()){
35366 var b = south.getBox();
35367 var m = south.getMargins();
35368 b.width = w - (m.left+m.right);
35370 var totalHeight = (b.height + m.top + m.bottom);
35371 b.y = h - totalHeight + m.top;
35372 centerH -= totalHeight;
35373 south.updateBox(this.safeBox(b));
35375 if(west && west.isVisible()){
35376 var b = west.getBox();
35377 var m = west.getMargins();
35378 b.height = centerH - (m.top+m.bottom);
35380 b.y = centerY + m.top;
35381 var totalWidth = (b.width + m.left + m.right);
35382 centerX += totalWidth;
35383 centerW -= totalWidth;
35384 west.updateBox(this.safeBox(b));
35386 if(east && east.isVisible()){
35387 var b = east.getBox();
35388 var m = east.getMargins();
35389 b.height = centerH - (m.top+m.bottom);
35390 var totalWidth = (b.width + m.left + m.right);
35391 b.x = w - totalWidth + m.left;
35392 b.y = centerY + m.top;
35393 centerW -= totalWidth;
35394 east.updateBox(this.safeBox(b));
35397 var m = center.getMargins();
35399 x: centerX + m.left,
35400 y: centerY + m.top,
35401 width: centerW - (m.left+m.right),
35402 height: centerH - (m.top+m.bottom)
35404 //if(this.hideOnLayout){
35405 //center.el.setStyle("display", "block");
35407 center.updateBox(this.safeBox(centerBox));
35410 this.fireEvent("layout", this);
35414 safeBox : function(box){
35415 box.width = Math.max(0, box.width);
35416 box.height = Math.max(0, box.height);
35421 * Adds a ContentPanel (or subclass) to this layout.
35422 * @param {String} target The target region key (north, south, east, west or center).
35423 * @param {Roo.ContentPanel} panel The panel to add
35424 * @return {Roo.ContentPanel} The added panel
35426 add : function(target, panel){
35428 target = target.toLowerCase();
35429 return this.regions[target].add(panel);
35433 * Remove a ContentPanel (or subclass) to this layout.
35434 * @param {String} target The target region key (north, south, east, west or center).
35435 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35436 * @return {Roo.ContentPanel} The removed panel
35438 remove : function(target, panel){
35439 target = target.toLowerCase();
35440 return this.regions[target].remove(panel);
35444 * Searches all regions for a panel with the specified id
35445 * @param {String} panelId
35446 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35448 findPanel : function(panelId){
35449 var rs = this.regions;
35450 for(var target in rs){
35451 if(typeof rs[target] != "function"){
35452 var p = rs[target].getPanel(panelId);
35462 * Searches all regions for a panel with the specified id and activates (shows) it.
35463 * @param {String/ContentPanel} panelId The panels id or the panel itself
35464 * @return {Roo.ContentPanel} The shown panel or null
35466 showPanel : function(panelId) {
35467 var rs = this.regions;
35468 for(var target in rs){
35469 var r = rs[target];
35470 if(typeof r != "function"){
35471 if(r.hasPanel(panelId)){
35472 return r.showPanel(panelId);
35480 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35481 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35484 restoreState : function(provider){
35486 provider = Roo.state.Manager;
35488 var sm = new Roo.LayoutStateManager();
35489 sm.init(this, provider);
35495 * Adds a xtype elements to the layout.
35499 xtype : 'ContentPanel',
35506 xtype : 'NestedLayoutPanel',
35512 items : [ ... list of content panels or nested layout panels.. ]
35516 * @param {Object} cfg Xtype definition of item to add.
35518 addxtype : function(cfg)
35520 // basically accepts a pannel...
35521 // can accept a layout region..!?!?
35522 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35525 // theory? children can only be panels??
35527 //if (!cfg.xtype.match(/Panel$/)) {
35532 if (typeof(cfg.region) == 'undefined') {
35533 Roo.log("Failed to add Panel, region was not set");
35537 var region = cfg.region;
35543 xitems = cfg.items;
35548 if ( region == 'center') {
35549 Roo.log("Center: " + cfg.title);
35555 case 'Content': // ContentPanel (el, cfg)
35556 case 'Scroll': // ContentPanel (el, cfg)
35558 cfg.autoCreate = cfg.autoCreate || true;
35559 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35561 // var el = this.el.createChild();
35562 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35565 this.add(region, ret);
35569 case 'TreePanel': // our new panel!
35570 cfg.el = this.el.createChild();
35571 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35572 this.add(region, ret);
35577 // create a new Layout (which is a Border Layout...
35579 var clayout = cfg.layout;
35580 clayout.el = this.el.createChild();
35581 clayout.items = clayout.items || [];
35585 // replace this exitems with the clayout ones..
35586 xitems = clayout.items;
35588 // force background off if it's in center...
35589 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35590 cfg.background = false;
35592 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35595 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35596 //console.log('adding nested layout panel ' + cfg.toSource());
35597 this.add(region, ret);
35598 nb = {}; /// find first...
35603 // needs grid and region
35605 //var el = this.getRegion(region).el.createChild();
35607 *var el = this.el.createChild();
35608 // create the grid first...
35609 cfg.grid.container = el;
35610 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35613 if (region == 'center' && this.active ) {
35614 cfg.background = false;
35617 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35619 this.add(region, ret);
35621 if (cfg.background) {
35622 // render grid on panel activation (if panel background)
35623 ret.on('activate', function(gp) {
35624 if (!gp.grid.rendered) {
35625 // gp.grid.render(el);
35629 // cfg.grid.render(el);
35635 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35636 // it was the old xcomponent building that caused this before.
35637 // espeically if border is the top element in the tree.
35647 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35649 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35650 this.add(region, ret);
35654 throw "Can not add '" + cfg.xtype + "' to Border";
35660 this.beginUpdate();
35664 Roo.each(xitems, function(i) {
35665 region = nb && i.region ? i.region : false;
35667 var add = ret.addxtype(i);
35670 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35671 if (!i.background) {
35672 abn[region] = nb[region] ;
35679 // make the last non-background panel active..
35680 //if (nb) { Roo.log(abn); }
35683 for(var r in abn) {
35684 region = this.getRegion(r);
35686 // tried using nb[r], but it does not work..
35688 region.showPanel(abn[r]);
35699 factory : function(cfg)
35702 var validRegions = Roo.bootstrap.layout.Border.regions;
35704 var target = cfg.region;
35707 var r = Roo.bootstrap.layout;
35711 return new r.North(cfg);
35713 return new r.South(cfg);
35715 return new r.East(cfg);
35717 return new r.West(cfg);
35719 return new r.Center(cfg);
35721 throw 'Layout region "'+target+'" not supported.';
35728 * Ext JS Library 1.1.1
35729 * Copyright(c) 2006-2007, Ext JS, LLC.
35731 * Originally Released Under LGPL - original licence link has changed is not relivant.
35734 * <script type="text/javascript">
35738 * @class Roo.bootstrap.layout.Basic
35739 * @extends Roo.util.Observable
35740 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35741 * and does not have a titlebar, tabs or any other features. All it does is size and position
35742 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35743 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35744 * @cfg {string} region the region that it inhabits..
35745 * @cfg {bool} skipConfig skip config?
35749 Roo.bootstrap.layout.Basic = function(config){
35751 this.mgr = config.mgr;
35753 this.position = config.region;
35755 var skipConfig = config.skipConfig;
35759 * @scope Roo.BasicLayoutRegion
35763 * @event beforeremove
35764 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35765 * @param {Roo.LayoutRegion} this
35766 * @param {Roo.ContentPanel} panel The panel
35767 * @param {Object} e The cancel event object
35769 "beforeremove" : true,
35771 * @event invalidated
35772 * Fires when the layout for this region is changed.
35773 * @param {Roo.LayoutRegion} this
35775 "invalidated" : true,
35777 * @event visibilitychange
35778 * Fires when this region is shown or hidden
35779 * @param {Roo.LayoutRegion} this
35780 * @param {Boolean} visibility true or false
35782 "visibilitychange" : true,
35784 * @event paneladded
35785 * Fires when a panel is added.
35786 * @param {Roo.LayoutRegion} this
35787 * @param {Roo.ContentPanel} panel The panel
35789 "paneladded" : true,
35791 * @event panelremoved
35792 * Fires when a panel is removed.
35793 * @param {Roo.LayoutRegion} this
35794 * @param {Roo.ContentPanel} panel The panel
35796 "panelremoved" : true,
35798 * @event beforecollapse
35799 * Fires when this region before collapse.
35800 * @param {Roo.LayoutRegion} this
35802 "beforecollapse" : true,
35805 * Fires when this region is collapsed.
35806 * @param {Roo.LayoutRegion} this
35808 "collapsed" : true,
35811 * Fires when this region is expanded.
35812 * @param {Roo.LayoutRegion} this
35817 * Fires when this region is slid into view.
35818 * @param {Roo.LayoutRegion} this
35820 "slideshow" : true,
35823 * Fires when this region slides out of view.
35824 * @param {Roo.LayoutRegion} this
35826 "slidehide" : true,
35828 * @event panelactivated
35829 * Fires when a panel is activated.
35830 * @param {Roo.LayoutRegion} this
35831 * @param {Roo.ContentPanel} panel The activated panel
35833 "panelactivated" : true,
35836 * Fires when the user resizes this region.
35837 * @param {Roo.LayoutRegion} this
35838 * @param {Number} newSize The new size (width for east/west, height for north/south)
35842 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35843 this.panels = new Roo.util.MixedCollection();
35844 this.panels.getKey = this.getPanelId.createDelegate(this);
35846 this.activePanel = null;
35847 // ensure listeners are added...
35849 if (config.listeners || config.events) {
35850 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35851 listeners : config.listeners || {},
35852 events : config.events || {}
35856 if(skipConfig !== true){
35857 this.applyConfig(config);
35861 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35863 getPanelId : function(p){
35867 applyConfig : function(config){
35868 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35869 this.config = config;
35874 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35875 * the width, for horizontal (north, south) the height.
35876 * @param {Number} newSize The new width or height
35878 resizeTo : function(newSize){
35879 var el = this.el ? this.el :
35880 (this.activePanel ? this.activePanel.getEl() : null);
35882 switch(this.position){
35885 el.setWidth(newSize);
35886 this.fireEvent("resized", this, newSize);
35890 el.setHeight(newSize);
35891 this.fireEvent("resized", this, newSize);
35897 getBox : function(){
35898 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35901 getMargins : function(){
35902 return this.margins;
35905 updateBox : function(box){
35907 var el = this.activePanel.getEl();
35908 el.dom.style.left = box.x + "px";
35909 el.dom.style.top = box.y + "px";
35910 this.activePanel.setSize(box.width, box.height);
35914 * Returns the container element for this region.
35915 * @return {Roo.Element}
35917 getEl : function(){
35918 return this.activePanel;
35922 * Returns true if this region is currently visible.
35923 * @return {Boolean}
35925 isVisible : function(){
35926 return this.activePanel ? true : false;
35929 setActivePanel : function(panel){
35930 panel = this.getPanel(panel);
35931 if(this.activePanel && this.activePanel != panel){
35932 this.activePanel.setActiveState(false);
35933 this.activePanel.getEl().setLeftTop(-10000,-10000);
35935 this.activePanel = panel;
35936 panel.setActiveState(true);
35938 panel.setSize(this.box.width, this.box.height);
35940 this.fireEvent("panelactivated", this, panel);
35941 this.fireEvent("invalidated");
35945 * Show the specified panel.
35946 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35947 * @return {Roo.ContentPanel} The shown panel or null
35949 showPanel : function(panel){
35950 panel = this.getPanel(panel);
35952 this.setActivePanel(panel);
35958 * Get the active panel for this region.
35959 * @return {Roo.ContentPanel} The active panel or null
35961 getActivePanel : function(){
35962 return this.activePanel;
35966 * Add the passed ContentPanel(s)
35967 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35968 * @return {Roo.ContentPanel} The panel added (if only one was added)
35970 add : function(panel){
35971 if(arguments.length > 1){
35972 for(var i = 0, len = arguments.length; i < len; i++) {
35973 this.add(arguments[i]);
35977 if(this.hasPanel(panel)){
35978 this.showPanel(panel);
35981 var el = panel.getEl();
35982 if(el.dom.parentNode != this.mgr.el.dom){
35983 this.mgr.el.dom.appendChild(el.dom);
35985 if(panel.setRegion){
35986 panel.setRegion(this);
35988 this.panels.add(panel);
35989 el.setStyle("position", "absolute");
35990 if(!panel.background){
35991 this.setActivePanel(panel);
35992 if(this.config.initialSize && this.panels.getCount()==1){
35993 this.resizeTo(this.config.initialSize);
35996 this.fireEvent("paneladded", this, panel);
36001 * Returns true if the panel is in this region.
36002 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36003 * @return {Boolean}
36005 hasPanel : function(panel){
36006 if(typeof panel == "object"){ // must be panel obj
36007 panel = panel.getId();
36009 return this.getPanel(panel) ? true : false;
36013 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36014 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36015 * @param {Boolean} preservePanel Overrides the config preservePanel option
36016 * @return {Roo.ContentPanel} The panel that was removed
36018 remove : function(panel, preservePanel){
36019 panel = this.getPanel(panel);
36024 this.fireEvent("beforeremove", this, panel, e);
36025 if(e.cancel === true){
36028 var panelId = panel.getId();
36029 this.panels.removeKey(panelId);
36034 * Returns the panel specified or null if it's not in this region.
36035 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36036 * @return {Roo.ContentPanel}
36038 getPanel : function(id){
36039 if(typeof id == "object"){ // must be panel obj
36042 return this.panels.get(id);
36046 * Returns this regions position (north/south/east/west/center).
36049 getPosition: function(){
36050 return this.position;
36054 * Ext JS Library 1.1.1
36055 * Copyright(c) 2006-2007, Ext JS, LLC.
36057 * Originally Released Under LGPL - original licence link has changed is not relivant.
36060 * <script type="text/javascript">
36064 * @class Roo.bootstrap.layout.Region
36065 * @extends Roo.bootstrap.layout.Basic
36066 * This class represents a region in a layout manager.
36068 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
36069 * @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})
36070 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
36071 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
36072 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
36073 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
36074 * @cfg {String} title The title for the region (overrides panel titles)
36075 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
36076 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36077 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
36078 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
36079 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
36080 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
36081 * the space available, similar to FireFox 1.5 tabs (defaults to false)
36082 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
36083 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
36084 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
36086 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
36087 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
36088 * @cfg {Boolean} disableTabTips True to disable tab tooltips
36089 * @cfg {Number} width For East/West panels
36090 * @cfg {Number} height For North/South panels
36091 * @cfg {Boolean} split To show the splitter
36092 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
36094 * @cfg {string} cls Extra CSS classes to add to region
36096 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
36097 * @cfg {string} region the region that it inhabits..
36100 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
36101 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
36103 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
36104 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
36105 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
36107 Roo.bootstrap.layout.Region = function(config)
36109 this.applyConfig(config);
36111 var mgr = config.mgr;
36112 var pos = config.region;
36113 config.skipConfig = true;
36114 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36117 this.onRender(mgr.el);
36120 this.visible = true;
36121 this.collapsed = false;
36122 this.unrendered_panels = [];
36125 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36127 position: '', // set by wrapper (eg. north/south etc..)
36128 unrendered_panels : null, // unrendered panels.
36130 tabPosition : false,
36132 mgr: false, // points to 'Border'
36135 createBody : function(){
36136 /** This region's body element
36137 * @type Roo.Element */
36138 this.bodyEl = this.el.createChild({
36140 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36144 onRender: function(ctr, pos)
36146 var dh = Roo.DomHelper;
36147 /** This region's container element
36148 * @type Roo.Element */
36149 this.el = dh.append(ctr.dom, {
36151 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36153 /** This region's title element
36154 * @type Roo.Element */
36156 this.titleEl = dh.append(this.el.dom, {
36158 unselectable: "on",
36159 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36161 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
36162 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36166 this.titleEl.enableDisplayMode();
36167 /** This region's title text element
36168 * @type HTMLElement */
36169 this.titleTextEl = this.titleEl.dom.firstChild;
36170 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36172 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36173 this.closeBtn.enableDisplayMode();
36174 this.closeBtn.on("click", this.closeClicked, this);
36175 this.closeBtn.hide();
36177 this.createBody(this.config);
36178 if(this.config.hideWhenEmpty){
36180 this.on("paneladded", this.validateVisibility, this);
36181 this.on("panelremoved", this.validateVisibility, this);
36183 if(this.autoScroll){
36184 this.bodyEl.setStyle("overflow", "auto");
36186 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36188 //if(c.titlebar !== false){
36189 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36190 this.titleEl.hide();
36192 this.titleEl.show();
36193 if(this.config.title){
36194 this.titleTextEl.innerHTML = this.config.title;
36198 if(this.config.collapsed){
36199 this.collapse(true);
36201 if(this.config.hidden){
36205 if (this.unrendered_panels && this.unrendered_panels.length) {
36206 for (var i =0;i< this.unrendered_panels.length; i++) {
36207 this.add(this.unrendered_panels[i]);
36209 this.unrendered_panels = null;
36215 applyConfig : function(c)
36218 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36219 var dh = Roo.DomHelper;
36220 if(c.titlebar !== false){
36221 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36222 this.collapseBtn.on("click", this.collapse, this);
36223 this.collapseBtn.enableDisplayMode();
36225 if(c.showPin === true || this.showPin){
36226 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36227 this.stickBtn.enableDisplayMode();
36228 this.stickBtn.on("click", this.expand, this);
36229 this.stickBtn.hide();
36234 /** This region's collapsed element
36235 * @type Roo.Element */
36238 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36239 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36242 if(c.floatable !== false){
36243 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36244 this.collapsedEl.on("click", this.collapseClick, this);
36247 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36248 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36249 id: "message", unselectable: "on", style:{"float":"left"}});
36250 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36252 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36253 this.expandBtn.on("click", this.expand, this);
36257 if(this.collapseBtn){
36258 this.collapseBtn.setVisible(c.collapsible == true);
36261 this.cmargins = c.cmargins || this.cmargins ||
36262 (this.position == "west" || this.position == "east" ?
36263 {top: 0, left: 2, right:2, bottom: 0} :
36264 {top: 2, left: 0, right:0, bottom: 2});
36266 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36269 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36271 this.autoScroll = c.autoScroll || false;
36276 this.duration = c.duration || .30;
36277 this.slideDuration = c.slideDuration || .45;
36282 * Returns true if this region is currently visible.
36283 * @return {Boolean}
36285 isVisible : function(){
36286 return this.visible;
36290 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36291 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
36293 //setCollapsedTitle : function(title){
36294 // title = title || " ";
36295 // if(this.collapsedTitleTextEl){
36296 // this.collapsedTitleTextEl.innerHTML = title;
36300 getBox : function(){
36302 // if(!this.collapsed){
36303 b = this.el.getBox(false, true);
36305 // b = this.collapsedEl.getBox(false, true);
36310 getMargins : function(){
36311 return this.margins;
36312 //return this.collapsed ? this.cmargins : this.margins;
36315 highlight : function(){
36316 this.el.addClass("x-layout-panel-dragover");
36319 unhighlight : function(){
36320 this.el.removeClass("x-layout-panel-dragover");
36323 updateBox : function(box)
36325 if (!this.bodyEl) {
36326 return; // not rendered yet..
36330 if(!this.collapsed){
36331 this.el.dom.style.left = box.x + "px";
36332 this.el.dom.style.top = box.y + "px";
36333 this.updateBody(box.width, box.height);
36335 this.collapsedEl.dom.style.left = box.x + "px";
36336 this.collapsedEl.dom.style.top = box.y + "px";
36337 this.collapsedEl.setSize(box.width, box.height);
36340 this.tabs.autoSizeTabs();
36344 updateBody : function(w, h)
36347 this.el.setWidth(w);
36348 w -= this.el.getBorderWidth("rl");
36349 if(this.config.adjustments){
36350 w += this.config.adjustments[0];
36353 if(h !== null && h > 0){
36354 this.el.setHeight(h);
36355 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36356 h -= this.el.getBorderWidth("tb");
36357 if(this.config.adjustments){
36358 h += this.config.adjustments[1];
36360 this.bodyEl.setHeight(h);
36362 h = this.tabs.syncHeight(h);
36365 if(this.panelSize){
36366 w = w !== null ? w : this.panelSize.width;
36367 h = h !== null ? h : this.panelSize.height;
36369 if(this.activePanel){
36370 var el = this.activePanel.getEl();
36371 w = w !== null ? w : el.getWidth();
36372 h = h !== null ? h : el.getHeight();
36373 this.panelSize = {width: w, height: h};
36374 this.activePanel.setSize(w, h);
36376 if(Roo.isIE && this.tabs){
36377 this.tabs.el.repaint();
36382 * Returns the container element for this region.
36383 * @return {Roo.Element}
36385 getEl : function(){
36390 * Hides this region.
36393 //if(!this.collapsed){
36394 this.el.dom.style.left = "-2000px";
36397 // this.collapsedEl.dom.style.left = "-2000px";
36398 // this.collapsedEl.hide();
36400 this.visible = false;
36401 this.fireEvent("visibilitychange", this, false);
36405 * Shows this region if it was previously hidden.
36408 //if(!this.collapsed){
36411 // this.collapsedEl.show();
36413 this.visible = true;
36414 this.fireEvent("visibilitychange", this, true);
36417 closeClicked : function(){
36418 if(this.activePanel){
36419 this.remove(this.activePanel);
36423 collapseClick : function(e){
36425 e.stopPropagation();
36428 e.stopPropagation();
36434 * Collapses this region.
36435 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36438 collapse : function(skipAnim, skipCheck = false){
36439 if(this.collapsed) {
36443 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36445 this.collapsed = true;
36447 this.split.el.hide();
36449 if(this.config.animate && skipAnim !== true){
36450 this.fireEvent("invalidated", this);
36451 this.animateCollapse();
36453 this.el.setLocation(-20000,-20000);
36455 this.collapsedEl.show();
36456 this.fireEvent("collapsed", this);
36457 this.fireEvent("invalidated", this);
36463 animateCollapse : function(){
36468 * Expands this region if it was previously collapsed.
36469 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36470 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36473 expand : function(e, skipAnim){
36475 e.stopPropagation();
36477 if(!this.collapsed || this.el.hasActiveFx()) {
36481 this.afterSlideIn();
36484 this.collapsed = false;
36485 if(this.config.animate && skipAnim !== true){
36486 this.animateExpand();
36490 this.split.el.show();
36492 this.collapsedEl.setLocation(-2000,-2000);
36493 this.collapsedEl.hide();
36494 this.fireEvent("invalidated", this);
36495 this.fireEvent("expanded", this);
36499 animateExpand : function(){
36503 initTabs : function()
36505 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36507 var ts = new Roo.bootstrap.panel.Tabs({
36508 el: this.bodyEl.dom,
36510 tabPosition: this.tabPosition ? this.tabPosition : 'top',
36511 disableTooltips: this.config.disableTabTips,
36512 toolbar : this.config.toolbar
36515 if(this.config.hideTabs){
36516 ts.stripWrap.setDisplayed(false);
36519 ts.resizeTabs = this.config.resizeTabs === true;
36520 ts.minTabWidth = this.config.minTabWidth || 40;
36521 ts.maxTabWidth = this.config.maxTabWidth || 250;
36522 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36523 ts.monitorResize = false;
36524 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36525 ts.bodyEl.addClass('roo-layout-tabs-body');
36526 this.panels.each(this.initPanelAsTab, this);
36529 initPanelAsTab : function(panel){
36530 var ti = this.tabs.addTab(
36534 this.config.closeOnTab && panel.isClosable(),
36537 if(panel.tabTip !== undefined){
36538 ti.setTooltip(panel.tabTip);
36540 ti.on("activate", function(){
36541 this.setActivePanel(panel);
36544 if(this.config.closeOnTab){
36545 ti.on("beforeclose", function(t, e){
36547 this.remove(panel);
36551 panel.tabItem = ti;
36556 updatePanelTitle : function(panel, title)
36558 if(this.activePanel == panel){
36559 this.updateTitle(title);
36562 var ti = this.tabs.getTab(panel.getEl().id);
36564 if(panel.tabTip !== undefined){
36565 ti.setTooltip(panel.tabTip);
36570 updateTitle : function(title){
36571 if(this.titleTextEl && !this.config.title){
36572 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36576 setActivePanel : function(panel)
36578 panel = this.getPanel(panel);
36579 if(this.activePanel && this.activePanel != panel){
36580 if(this.activePanel.setActiveState(false) === false){
36584 this.activePanel = panel;
36585 panel.setActiveState(true);
36586 if(this.panelSize){
36587 panel.setSize(this.panelSize.width, this.panelSize.height);
36590 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36592 this.updateTitle(panel.getTitle());
36594 this.fireEvent("invalidated", this);
36596 this.fireEvent("panelactivated", this, panel);
36600 * Shows the specified panel.
36601 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36602 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36604 showPanel : function(panel)
36606 panel = this.getPanel(panel);
36609 var tab = this.tabs.getTab(panel.getEl().id);
36610 if(tab.isHidden()){
36611 this.tabs.unhideTab(tab.id);
36615 this.setActivePanel(panel);
36622 * Get the active panel for this region.
36623 * @return {Roo.ContentPanel} The active panel or null
36625 getActivePanel : function(){
36626 return this.activePanel;
36629 validateVisibility : function(){
36630 if(this.panels.getCount() < 1){
36631 this.updateTitle(" ");
36632 this.closeBtn.hide();
36635 if(!this.isVisible()){
36642 * Adds the passed ContentPanel(s) to this region.
36643 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36644 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36646 add : function(panel)
36648 if(arguments.length > 1){
36649 for(var i = 0, len = arguments.length; i < len; i++) {
36650 this.add(arguments[i]);
36655 // if we have not been rendered yet, then we can not really do much of this..
36656 if (!this.bodyEl) {
36657 this.unrendered_panels.push(panel);
36664 if(this.hasPanel(panel)){
36665 this.showPanel(panel);
36668 panel.setRegion(this);
36669 this.panels.add(panel);
36670 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36671 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36672 // and hide them... ???
36673 this.bodyEl.dom.appendChild(panel.getEl().dom);
36674 if(panel.background !== true){
36675 this.setActivePanel(panel);
36677 this.fireEvent("paneladded", this, panel);
36684 this.initPanelAsTab(panel);
36688 if(panel.background !== true){
36689 this.tabs.activate(panel.getEl().id);
36691 this.fireEvent("paneladded", this, panel);
36696 * Hides the tab for the specified panel.
36697 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36699 hidePanel : function(panel){
36700 if(this.tabs && (panel = this.getPanel(panel))){
36701 this.tabs.hideTab(panel.getEl().id);
36706 * Unhides the tab for a previously hidden panel.
36707 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36709 unhidePanel : function(panel){
36710 if(this.tabs && (panel = this.getPanel(panel))){
36711 this.tabs.unhideTab(panel.getEl().id);
36715 clearPanels : function(){
36716 while(this.panels.getCount() > 0){
36717 this.remove(this.panels.first());
36722 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36723 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36724 * @param {Boolean} preservePanel Overrides the config preservePanel option
36725 * @return {Roo.ContentPanel} The panel that was removed
36727 remove : function(panel, preservePanel)
36729 panel = this.getPanel(panel);
36734 this.fireEvent("beforeremove", this, panel, e);
36735 if(e.cancel === true){
36738 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36739 var panelId = panel.getId();
36740 this.panels.removeKey(panelId);
36742 document.body.appendChild(panel.getEl().dom);
36745 this.tabs.removeTab(panel.getEl().id);
36746 }else if (!preservePanel){
36747 this.bodyEl.dom.removeChild(panel.getEl().dom);
36749 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36750 var p = this.panels.first();
36751 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36752 tempEl.appendChild(p.getEl().dom);
36753 this.bodyEl.update("");
36754 this.bodyEl.dom.appendChild(p.getEl().dom);
36756 this.updateTitle(p.getTitle());
36758 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36759 this.setActivePanel(p);
36761 panel.setRegion(null);
36762 if(this.activePanel == panel){
36763 this.activePanel = null;
36765 if(this.config.autoDestroy !== false && preservePanel !== true){
36766 try{panel.destroy();}catch(e){}
36768 this.fireEvent("panelremoved", this, panel);
36773 * Returns the TabPanel component used by this region
36774 * @return {Roo.TabPanel}
36776 getTabs : function(){
36780 createTool : function(parentEl, className){
36781 var btn = Roo.DomHelper.append(parentEl, {
36783 cls: "x-layout-tools-button",
36786 cls: "roo-layout-tools-button-inner " + className,
36790 btn.addClassOnOver("roo-layout-tools-button-over");
36795 * Ext JS Library 1.1.1
36796 * Copyright(c) 2006-2007, Ext JS, LLC.
36798 * Originally Released Under LGPL - original licence link has changed is not relivant.
36801 * <script type="text/javascript">
36807 * @class Roo.SplitLayoutRegion
36808 * @extends Roo.LayoutRegion
36809 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36811 Roo.bootstrap.layout.Split = function(config){
36812 this.cursor = config.cursor;
36813 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36816 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36818 splitTip : "Drag to resize.",
36819 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36820 useSplitTips : false,
36822 applyConfig : function(config){
36823 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36826 onRender : function(ctr,pos) {
36828 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36829 if(!this.config.split){
36834 var splitEl = Roo.DomHelper.append(ctr.dom, {
36836 id: this.el.id + "-split",
36837 cls: "roo-layout-split roo-layout-split-"+this.position,
36840 /** The SplitBar for this region
36841 * @type Roo.SplitBar */
36842 // does not exist yet...
36843 Roo.log([this.position, this.orientation]);
36845 this.split = new Roo.bootstrap.SplitBar({
36846 dragElement : splitEl,
36847 resizingElement: this.el,
36848 orientation : this.orientation
36851 this.split.on("moved", this.onSplitMove, this);
36852 this.split.useShim = this.config.useShim === true;
36853 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36854 if(this.useSplitTips){
36855 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36857 //if(config.collapsible){
36858 // this.split.el.on("dblclick", this.collapse, this);
36861 if(typeof this.config.minSize != "undefined"){
36862 this.split.minSize = this.config.minSize;
36864 if(typeof this.config.maxSize != "undefined"){
36865 this.split.maxSize = this.config.maxSize;
36867 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36868 this.hideSplitter();
36873 getHMaxSize : function(){
36874 var cmax = this.config.maxSize || 10000;
36875 var center = this.mgr.getRegion("center");
36876 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36879 getVMaxSize : function(){
36880 var cmax = this.config.maxSize || 10000;
36881 var center = this.mgr.getRegion("center");
36882 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36885 onSplitMove : function(split, newSize){
36886 this.fireEvent("resized", this, newSize);
36890 * Returns the {@link Roo.SplitBar} for this region.
36891 * @return {Roo.SplitBar}
36893 getSplitBar : function(){
36898 this.hideSplitter();
36899 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36902 hideSplitter : function(){
36904 this.split.el.setLocation(-2000,-2000);
36905 this.split.el.hide();
36911 this.split.el.show();
36913 Roo.bootstrap.layout.Split.superclass.show.call(this);
36916 beforeSlide: function(){
36917 if(Roo.isGecko){// firefox overflow auto bug workaround
36918 this.bodyEl.clip();
36920 this.tabs.bodyEl.clip();
36922 if(this.activePanel){
36923 this.activePanel.getEl().clip();
36925 if(this.activePanel.beforeSlide){
36926 this.activePanel.beforeSlide();
36932 afterSlide : function(){
36933 if(Roo.isGecko){// firefox overflow auto bug workaround
36934 this.bodyEl.unclip();
36936 this.tabs.bodyEl.unclip();
36938 if(this.activePanel){
36939 this.activePanel.getEl().unclip();
36940 if(this.activePanel.afterSlide){
36941 this.activePanel.afterSlide();
36947 initAutoHide : function(){
36948 if(this.autoHide !== false){
36949 if(!this.autoHideHd){
36950 var st = new Roo.util.DelayedTask(this.slideIn, this);
36951 this.autoHideHd = {
36952 "mouseout": function(e){
36953 if(!e.within(this.el, true)){
36957 "mouseover" : function(e){
36963 this.el.on(this.autoHideHd);
36967 clearAutoHide : function(){
36968 if(this.autoHide !== false){
36969 this.el.un("mouseout", this.autoHideHd.mouseout);
36970 this.el.un("mouseover", this.autoHideHd.mouseover);
36974 clearMonitor : function(){
36975 Roo.get(document).un("click", this.slideInIf, this);
36978 // these names are backwards but not changed for compat
36979 slideOut : function(){
36980 if(this.isSlid || this.el.hasActiveFx()){
36983 this.isSlid = true;
36984 if(this.collapseBtn){
36985 this.collapseBtn.hide();
36987 this.closeBtnState = this.closeBtn.getStyle('display');
36988 this.closeBtn.hide();
36990 this.stickBtn.show();
36993 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36994 this.beforeSlide();
36995 this.el.setStyle("z-index", 10001);
36996 this.el.slideIn(this.getSlideAnchor(), {
36997 callback: function(){
36999 this.initAutoHide();
37000 Roo.get(document).on("click", this.slideInIf, this);
37001 this.fireEvent("slideshow", this);
37008 afterSlideIn : function(){
37009 this.clearAutoHide();
37010 this.isSlid = false;
37011 this.clearMonitor();
37012 this.el.setStyle("z-index", "");
37013 if(this.collapseBtn){
37014 this.collapseBtn.show();
37016 this.closeBtn.setStyle('display', this.closeBtnState);
37018 this.stickBtn.hide();
37020 this.fireEvent("slidehide", this);
37023 slideIn : function(cb){
37024 if(!this.isSlid || this.el.hasActiveFx()){
37028 this.isSlid = false;
37029 this.beforeSlide();
37030 this.el.slideOut(this.getSlideAnchor(), {
37031 callback: function(){
37032 this.el.setLeftTop(-10000, -10000);
37034 this.afterSlideIn();
37042 slideInIf : function(e){
37043 if(!e.within(this.el)){
37048 animateCollapse : function(){
37049 this.beforeSlide();
37050 this.el.setStyle("z-index", 20000);
37051 var anchor = this.getSlideAnchor();
37052 this.el.slideOut(anchor, {
37053 callback : function(){
37054 this.el.setStyle("z-index", "");
37055 this.collapsedEl.slideIn(anchor, {duration:.3});
37057 this.el.setLocation(-10000,-10000);
37059 this.fireEvent("collapsed", this);
37066 animateExpand : function(){
37067 this.beforeSlide();
37068 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
37069 this.el.setStyle("z-index", 20000);
37070 this.collapsedEl.hide({
37073 this.el.slideIn(this.getSlideAnchor(), {
37074 callback : function(){
37075 this.el.setStyle("z-index", "");
37078 this.split.el.show();
37080 this.fireEvent("invalidated", this);
37081 this.fireEvent("expanded", this);
37109 getAnchor : function(){
37110 return this.anchors[this.position];
37113 getCollapseAnchor : function(){
37114 return this.canchors[this.position];
37117 getSlideAnchor : function(){
37118 return this.sanchors[this.position];
37121 getAlignAdj : function(){
37122 var cm = this.cmargins;
37123 switch(this.position){
37139 getExpandAdj : function(){
37140 var c = this.collapsedEl, cm = this.cmargins;
37141 switch(this.position){
37143 return [-(cm.right+c.getWidth()+cm.left), 0];
37146 return [cm.right+c.getWidth()+cm.left, 0];
37149 return [0, -(cm.top+cm.bottom+c.getHeight())];
37152 return [0, cm.top+cm.bottom+c.getHeight()];
37158 * Ext JS Library 1.1.1
37159 * Copyright(c) 2006-2007, Ext JS, LLC.
37161 * Originally Released Under LGPL - original licence link has changed is not relivant.
37164 * <script type="text/javascript">
37167 * These classes are private internal classes
37169 Roo.bootstrap.layout.Center = function(config){
37170 config.region = "center";
37171 Roo.bootstrap.layout.Region.call(this, config);
37172 this.visible = true;
37173 this.minWidth = config.minWidth || 20;
37174 this.minHeight = config.minHeight || 20;
37177 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37179 // center panel can't be hidden
37183 // center panel can't be hidden
37186 getMinWidth: function(){
37187 return this.minWidth;
37190 getMinHeight: function(){
37191 return this.minHeight;
37205 Roo.bootstrap.layout.North = function(config)
37207 config.region = 'north';
37208 config.cursor = 'n-resize';
37210 Roo.bootstrap.layout.Split.call(this, config);
37214 this.split.placement = Roo.bootstrap.SplitBar.TOP;
37215 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37216 this.split.el.addClass("roo-layout-split-v");
37218 var size = config.initialSize || config.height;
37219 if(typeof size != "undefined"){
37220 this.el.setHeight(size);
37223 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37225 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37229 getBox : function(){
37230 if(this.collapsed){
37231 return this.collapsedEl.getBox();
37233 var box = this.el.getBox();
37235 box.height += this.split.el.getHeight();
37240 updateBox : function(box){
37241 if(this.split && !this.collapsed){
37242 box.height -= this.split.el.getHeight();
37243 this.split.el.setLeft(box.x);
37244 this.split.el.setTop(box.y+box.height);
37245 this.split.el.setWidth(box.width);
37247 if(this.collapsed){
37248 this.updateBody(box.width, null);
37250 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37258 Roo.bootstrap.layout.South = function(config){
37259 config.region = 'south';
37260 config.cursor = 's-resize';
37261 Roo.bootstrap.layout.Split.call(this, config);
37263 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37264 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37265 this.split.el.addClass("roo-layout-split-v");
37267 var size = config.initialSize || config.height;
37268 if(typeof size != "undefined"){
37269 this.el.setHeight(size);
37273 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37274 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37275 getBox : function(){
37276 if(this.collapsed){
37277 return this.collapsedEl.getBox();
37279 var box = this.el.getBox();
37281 var sh = this.split.el.getHeight();
37288 updateBox : function(box){
37289 if(this.split && !this.collapsed){
37290 var sh = this.split.el.getHeight();
37293 this.split.el.setLeft(box.x);
37294 this.split.el.setTop(box.y-sh);
37295 this.split.el.setWidth(box.width);
37297 if(this.collapsed){
37298 this.updateBody(box.width, null);
37300 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37304 Roo.bootstrap.layout.East = function(config){
37305 config.region = "east";
37306 config.cursor = "e-resize";
37307 Roo.bootstrap.layout.Split.call(this, config);
37309 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37310 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37311 this.split.el.addClass("roo-layout-split-h");
37313 var size = config.initialSize || config.width;
37314 if(typeof size != "undefined"){
37315 this.el.setWidth(size);
37318 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37319 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37320 getBox : function(){
37321 if(this.collapsed){
37322 return this.collapsedEl.getBox();
37324 var box = this.el.getBox();
37326 var sw = this.split.el.getWidth();
37333 updateBox : function(box){
37334 if(this.split && !this.collapsed){
37335 var sw = this.split.el.getWidth();
37337 this.split.el.setLeft(box.x);
37338 this.split.el.setTop(box.y);
37339 this.split.el.setHeight(box.height);
37342 if(this.collapsed){
37343 this.updateBody(null, box.height);
37345 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37349 Roo.bootstrap.layout.West = function(config){
37350 config.region = "west";
37351 config.cursor = "w-resize";
37353 Roo.bootstrap.layout.Split.call(this, config);
37355 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37356 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37357 this.split.el.addClass("roo-layout-split-h");
37361 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37362 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37364 onRender: function(ctr, pos)
37366 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37367 var size = this.config.initialSize || this.config.width;
37368 if(typeof size != "undefined"){
37369 this.el.setWidth(size);
37373 getBox : function(){
37374 if(this.collapsed){
37375 return this.collapsedEl.getBox();
37377 var box = this.el.getBox();
37379 box.width += this.split.el.getWidth();
37384 updateBox : function(box){
37385 if(this.split && !this.collapsed){
37386 var sw = this.split.el.getWidth();
37388 this.split.el.setLeft(box.x+box.width);
37389 this.split.el.setTop(box.y);
37390 this.split.el.setHeight(box.height);
37392 if(this.collapsed){
37393 this.updateBody(null, box.height);
37395 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37397 });Roo.namespace("Roo.bootstrap.panel");/*
37399 * Ext JS Library 1.1.1
37400 * Copyright(c) 2006-2007, Ext JS, LLC.
37402 * Originally Released Under LGPL - original licence link has changed is not relivant.
37405 * <script type="text/javascript">
37408 * @class Roo.ContentPanel
37409 * @extends Roo.util.Observable
37410 * A basic ContentPanel element.
37411 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
37412 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
37413 * @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
37414 * @cfg {Boolean} closable True if the panel can be closed/removed
37415 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
37416 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37417 * @cfg {Toolbar} toolbar A toolbar for this panel
37418 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37419 * @cfg {String} title The title for this panel
37420 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37421 * @cfg {String} url Calls {@link #setUrl} with this value
37422 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37423 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37424 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37425 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37426 * @cfg {Boolean} badges render the badges
37429 * Create a new ContentPanel.
37430 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37431 * @param {String/Object} config A string to set only the title or a config object
37432 * @param {String} content (optional) Set the HTML content for this panel
37433 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37435 Roo.bootstrap.panel.Content = function( config){
37437 this.tpl = config.tpl || false;
37439 var el = config.el;
37440 var content = config.content;
37442 if(config.autoCreate){ // xtype is available if this is called from factory
37445 this.el = Roo.get(el);
37446 if(!this.el && config && config.autoCreate){
37447 if(typeof config.autoCreate == "object"){
37448 if(!config.autoCreate.id){
37449 config.autoCreate.id = config.id||el;
37451 this.el = Roo.DomHelper.append(document.body,
37452 config.autoCreate, true);
37454 var elcfg = { tag: "div",
37455 cls: "roo-layout-inactive-content",
37459 elcfg.html = config.html;
37463 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37466 this.closable = false;
37467 this.loaded = false;
37468 this.active = false;
37471 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37473 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37475 this.wrapEl = this.el; //this.el.wrap();
37477 if (config.toolbar.items) {
37478 ti = config.toolbar.items ;
37479 delete config.toolbar.items ;
37483 this.toolbar.render(this.wrapEl, 'before');
37484 for(var i =0;i < ti.length;i++) {
37485 // Roo.log(['add child', items[i]]);
37486 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37488 this.toolbar.items = nitems;
37489 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37490 delete config.toolbar;
37494 // xtype created footer. - not sure if will work as we normally have to render first..
37495 if (this.footer && !this.footer.el && this.footer.xtype) {
37496 if (!this.wrapEl) {
37497 this.wrapEl = this.el.wrap();
37500 this.footer.container = this.wrapEl.createChild();
37502 this.footer = Roo.factory(this.footer, Roo);
37507 if(typeof config == "string"){
37508 this.title = config;
37510 Roo.apply(this, config);
37514 this.resizeEl = Roo.get(this.resizeEl, true);
37516 this.resizeEl = this.el;
37518 // handle view.xtype
37526 * Fires when this panel is activated.
37527 * @param {Roo.ContentPanel} this
37531 * @event deactivate
37532 * Fires when this panel is activated.
37533 * @param {Roo.ContentPanel} this
37535 "deactivate" : true,
37539 * Fires when this panel is resized if fitToFrame is true.
37540 * @param {Roo.ContentPanel} this
37541 * @param {Number} width The width after any component adjustments
37542 * @param {Number} height The height after any component adjustments
37548 * Fires when this tab is created
37549 * @param {Roo.ContentPanel} this
37560 if(this.autoScroll){
37561 this.resizeEl.setStyle("overflow", "auto");
37563 // fix randome scrolling
37564 //this.el.on('scroll', function() {
37565 // Roo.log('fix random scolling');
37566 // this.scrollTo('top',0);
37569 content = content || this.content;
37571 this.setContent(content);
37573 if(config && config.url){
37574 this.setUrl(this.url, this.params, this.loadOnce);
37579 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37581 if (this.view && typeof(this.view.xtype) != 'undefined') {
37582 this.view.el = this.el.appendChild(document.createElement("div"));
37583 this.view = Roo.factory(this.view);
37584 this.view.render && this.view.render(false, '');
37588 this.fireEvent('render', this);
37591 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37595 setRegion : function(region){
37596 this.region = region;
37597 this.setActiveClass(region && !this.background);
37601 setActiveClass: function(state)
37604 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37605 this.el.setStyle('position','relative');
37607 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37608 this.el.setStyle('position', 'absolute');
37613 * Returns the toolbar for this Panel if one was configured.
37614 * @return {Roo.Toolbar}
37616 getToolbar : function(){
37617 return this.toolbar;
37620 setActiveState : function(active)
37622 this.active = active;
37623 this.setActiveClass(active);
37625 if(this.fireEvent("deactivate", this) === false){
37630 this.fireEvent("activate", this);
37634 * Updates this panel's element
37635 * @param {String} content The new content
37636 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37638 setContent : function(content, loadScripts){
37639 this.el.update(content, loadScripts);
37642 ignoreResize : function(w, h){
37643 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37646 this.lastSize = {width: w, height: h};
37651 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37652 * @return {Roo.UpdateManager} The UpdateManager
37654 getUpdateManager : function(){
37655 return this.el.getUpdateManager();
37658 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37659 * @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:
37662 url: "your-url.php",
37663 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37664 callback: yourFunction,
37665 scope: yourObject, //(optional scope)
37668 text: "Loading...",
37673 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37674 * 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.
37675 * @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}
37676 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37677 * @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.
37678 * @return {Roo.ContentPanel} this
37681 var um = this.el.getUpdateManager();
37682 um.update.apply(um, arguments);
37688 * 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.
37689 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37690 * @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)
37691 * @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)
37692 * @return {Roo.UpdateManager} The UpdateManager
37694 setUrl : function(url, params, loadOnce){
37695 if(this.refreshDelegate){
37696 this.removeListener("activate", this.refreshDelegate);
37698 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37699 this.on("activate", this.refreshDelegate);
37700 return this.el.getUpdateManager();
37703 _handleRefresh : function(url, params, loadOnce){
37704 if(!loadOnce || !this.loaded){
37705 var updater = this.el.getUpdateManager();
37706 updater.update(url, params, this._setLoaded.createDelegate(this));
37710 _setLoaded : function(){
37711 this.loaded = true;
37715 * Returns this panel's id
37718 getId : function(){
37723 * Returns this panel's element - used by regiosn to add.
37724 * @return {Roo.Element}
37726 getEl : function(){
37727 return this.wrapEl || this.el;
37732 adjustForComponents : function(width, height)
37734 //Roo.log('adjustForComponents ');
37735 if(this.resizeEl != this.el){
37736 width -= this.el.getFrameWidth('lr');
37737 height -= this.el.getFrameWidth('tb');
37740 var te = this.toolbar.getEl();
37741 te.setWidth(width);
37742 height -= te.getHeight();
37745 var te = this.footer.getEl();
37746 te.setWidth(width);
37747 height -= te.getHeight();
37751 if(this.adjustments){
37752 width += this.adjustments[0];
37753 height += this.adjustments[1];
37755 return {"width": width, "height": height};
37758 setSize : function(width, height){
37759 if(this.fitToFrame && !this.ignoreResize(width, height)){
37760 if(this.fitContainer && this.resizeEl != this.el){
37761 this.el.setSize(width, height);
37763 var size = this.adjustForComponents(width, height);
37764 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37765 this.fireEvent('resize', this, size.width, size.height);
37770 * Returns this panel's title
37773 getTitle : function(){
37775 if (typeof(this.title) != 'object') {
37780 for (var k in this.title) {
37781 if (!this.title.hasOwnProperty(k)) {
37785 if (k.indexOf('-') >= 0) {
37786 var s = k.split('-');
37787 for (var i = 0; i<s.length; i++) {
37788 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37791 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37798 * Set this panel's title
37799 * @param {String} title
37801 setTitle : function(title){
37802 this.title = title;
37804 this.region.updatePanelTitle(this, title);
37809 * Returns true is this panel was configured to be closable
37810 * @return {Boolean}
37812 isClosable : function(){
37813 return this.closable;
37816 beforeSlide : function(){
37818 this.resizeEl.clip();
37821 afterSlide : function(){
37823 this.resizeEl.unclip();
37827 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37828 * Will fail silently if the {@link #setUrl} method has not been called.
37829 * This does not activate the panel, just updates its content.
37831 refresh : function(){
37832 if(this.refreshDelegate){
37833 this.loaded = false;
37834 this.refreshDelegate();
37839 * Destroys this panel
37841 destroy : function(){
37842 this.el.removeAllListeners();
37843 var tempEl = document.createElement("span");
37844 tempEl.appendChild(this.el.dom);
37845 tempEl.innerHTML = "";
37851 * form - if the content panel contains a form - this is a reference to it.
37852 * @type {Roo.form.Form}
37856 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37857 * This contains a reference to it.
37863 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37873 * @param {Object} cfg Xtype definition of item to add.
37877 getChildContainer: function () {
37878 return this.getEl();
37883 var ret = new Roo.factory(cfg);
37888 if (cfg.xtype.match(/^Form$/)) {
37891 //if (this.footer) {
37892 // el = this.footer.container.insertSibling(false, 'before');
37894 el = this.el.createChild();
37897 this.form = new Roo.form.Form(cfg);
37900 if ( this.form.allItems.length) {
37901 this.form.render(el.dom);
37905 // should only have one of theses..
37906 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37907 // views.. should not be just added - used named prop 'view''
37909 cfg.el = this.el.appendChild(document.createElement("div"));
37912 var ret = new Roo.factory(cfg);
37914 ret.render && ret.render(false, ''); // render blank..
37924 * @class Roo.bootstrap.panel.Grid
37925 * @extends Roo.bootstrap.panel.Content
37927 * Create a new GridPanel.
37928 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37929 * @param {Object} config A the config object
37935 Roo.bootstrap.panel.Grid = function(config)
37939 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37940 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37942 config.el = this.wrapper;
37943 //this.el = this.wrapper;
37945 if (config.container) {
37946 // ctor'ed from a Border/panel.grid
37949 this.wrapper.setStyle("overflow", "hidden");
37950 this.wrapper.addClass('roo-grid-container');
37955 if(config.toolbar){
37956 var tool_el = this.wrapper.createChild();
37957 this.toolbar = Roo.factory(config.toolbar);
37959 if (config.toolbar.items) {
37960 ti = config.toolbar.items ;
37961 delete config.toolbar.items ;
37965 this.toolbar.render(tool_el);
37966 for(var i =0;i < ti.length;i++) {
37967 // Roo.log(['add child', items[i]]);
37968 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37970 this.toolbar.items = nitems;
37972 delete config.toolbar;
37975 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37976 config.grid.scrollBody = true;;
37977 config.grid.monitorWindowResize = false; // turn off autosizing
37978 config.grid.autoHeight = false;
37979 config.grid.autoWidth = false;
37981 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37983 if (config.background) {
37984 // render grid on panel activation (if panel background)
37985 this.on('activate', function(gp) {
37986 if (!gp.grid.rendered) {
37987 gp.grid.render(this.wrapper);
37988 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37993 this.grid.render(this.wrapper);
37994 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37997 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37998 // ??? needed ??? config.el = this.wrapper;
38003 // xtype created footer. - not sure if will work as we normally have to render first..
38004 if (this.footer && !this.footer.el && this.footer.xtype) {
38006 var ctr = this.grid.getView().getFooterPanel(true);
38007 this.footer.dataSource = this.grid.dataSource;
38008 this.footer = Roo.factory(this.footer, Roo);
38009 this.footer.render(ctr);
38019 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
38020 getId : function(){
38021 return this.grid.id;
38025 * Returns the grid for this panel
38026 * @return {Roo.bootstrap.Table}
38028 getGrid : function(){
38032 setSize : function(width, height){
38033 if(!this.ignoreResize(width, height)){
38034 var grid = this.grid;
38035 var size = this.adjustForComponents(width, height);
38036 var gridel = grid.getGridEl();
38037 gridel.setSize(size.width, size.height);
38039 var thd = grid.getGridEl().select('thead',true).first();
38040 var tbd = grid.getGridEl().select('tbody', true).first();
38042 tbd.setSize(width, height - thd.getHeight());
38051 beforeSlide : function(){
38052 this.grid.getView().scroller.clip();
38055 afterSlide : function(){
38056 this.grid.getView().scroller.unclip();
38059 destroy : function(){
38060 this.grid.destroy();
38062 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
38067 * @class Roo.bootstrap.panel.Nest
38068 * @extends Roo.bootstrap.panel.Content
38070 * Create a new Panel, that can contain a layout.Border.
38073 * @param {Roo.BorderLayout} layout The layout for this panel
38074 * @param {String/Object} config A string to set only the title or a config object
38076 Roo.bootstrap.panel.Nest = function(config)
38078 // construct with only one argument..
38079 /* FIXME - implement nicer consturctors
38080 if (layout.layout) {
38082 layout = config.layout;
38083 delete config.layout;
38085 if (layout.xtype && !layout.getEl) {
38086 // then layout needs constructing..
38087 layout = Roo.factory(layout, Roo);
38091 config.el = config.layout.getEl();
38093 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
38095 config.layout.monitorWindowResize = false; // turn off autosizing
38096 this.layout = config.layout;
38097 this.layout.getEl().addClass("roo-layout-nested-layout");
38098 this.layout.parent = this;
38105 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38107 setSize : function(width, height){
38108 if(!this.ignoreResize(width, height)){
38109 var size = this.adjustForComponents(width, height);
38110 var el = this.layout.getEl();
38111 if (size.height < 1) {
38112 el.setWidth(size.width);
38114 el.setSize(size.width, size.height);
38116 var touch = el.dom.offsetWidth;
38117 this.layout.layout();
38118 // ie requires a double layout on the first pass
38119 if(Roo.isIE && !this.initialized){
38120 this.initialized = true;
38121 this.layout.layout();
38126 // activate all subpanels if not currently active..
38128 setActiveState : function(active){
38129 this.active = active;
38130 this.setActiveClass(active);
38133 this.fireEvent("deactivate", this);
38137 this.fireEvent("activate", this);
38138 // not sure if this should happen before or after..
38139 if (!this.layout) {
38140 return; // should not happen..
38143 for (var r in this.layout.regions) {
38144 reg = this.layout.getRegion(r);
38145 if (reg.getActivePanel()) {
38146 //reg.showPanel(reg.getActivePanel()); // force it to activate..
38147 reg.setActivePanel(reg.getActivePanel());
38150 if (!reg.panels.length) {
38153 reg.showPanel(reg.getPanel(0));
38162 * Returns the nested BorderLayout for this panel
38163 * @return {Roo.BorderLayout}
38165 getLayout : function(){
38166 return this.layout;
38170 * Adds a xtype elements to the layout of the nested panel
38174 xtype : 'ContentPanel',
38181 xtype : 'NestedLayoutPanel',
38187 items : [ ... list of content panels or nested layout panels.. ]
38191 * @param {Object} cfg Xtype definition of item to add.
38193 addxtype : function(cfg) {
38194 return this.layout.addxtype(cfg);
38199 * Ext JS Library 1.1.1
38200 * Copyright(c) 2006-2007, Ext JS, LLC.
38202 * Originally Released Under LGPL - original licence link has changed is not relivant.
38205 * <script type="text/javascript">
38208 * @class Roo.TabPanel
38209 * @extends Roo.util.Observable
38210 * A lightweight tab container.
38214 // basic tabs 1, built from existing content
38215 var tabs = new Roo.TabPanel("tabs1");
38216 tabs.addTab("script", "View Script");
38217 tabs.addTab("markup", "View Markup");
38218 tabs.activate("script");
38220 // more advanced tabs, built from javascript
38221 var jtabs = new Roo.TabPanel("jtabs");
38222 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38224 // set up the UpdateManager
38225 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38226 var updater = tab2.getUpdateManager();
38227 updater.setDefaultUrl("ajax1.htm");
38228 tab2.on('activate', updater.refresh, updater, true);
38230 // Use setUrl for Ajax loading
38231 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38232 tab3.setUrl("ajax2.htm", null, true);
38235 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38238 jtabs.activate("jtabs-1");
38241 * Create a new TabPanel.
38242 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38243 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38245 Roo.bootstrap.panel.Tabs = function(config){
38247 * The container element for this TabPanel.
38248 * @type Roo.Element
38250 this.el = Roo.get(config.el);
38253 if(typeof config == "boolean"){
38254 this.tabPosition = config ? "bottom" : "top";
38256 Roo.apply(this, config);
38260 if(this.tabPosition == "bottom"){
38261 // if tabs are at the bottom = create the body first.
38262 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38263 this.el.addClass("roo-tabs-bottom");
38265 // next create the tabs holders
38267 if (this.tabPosition == "west"){
38269 var reg = this.region; // fake it..
38271 if (!reg.mgr.parent) {
38274 reg = reg.mgr.parent.region;
38276 Roo.log("got nest?");
38278 if (reg.mgr.getRegion('west')) {
38279 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38280 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38281 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38282 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38283 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38291 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38292 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38293 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38294 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38299 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38302 // finally - if tabs are at the top, then create the body last..
38303 if(this.tabPosition != "bottom"){
38304 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38305 * @type Roo.Element
38307 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38308 this.el.addClass("roo-tabs-top");
38312 this.bodyEl.setStyle("position", "relative");
38314 this.active = null;
38315 this.activateDelegate = this.activate.createDelegate(this);
38320 * Fires when the active tab changes
38321 * @param {Roo.TabPanel} this
38322 * @param {Roo.TabPanelItem} activePanel The new active tab
38326 * @event beforetabchange
38327 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38328 * @param {Roo.TabPanel} this
38329 * @param {Object} e Set cancel to true on this object to cancel the tab change
38330 * @param {Roo.TabPanelItem} tab The tab being changed to
38332 "beforetabchange" : true
38335 Roo.EventManager.onWindowResize(this.onResize, this);
38336 this.cpad = this.el.getPadding("lr");
38337 this.hiddenCount = 0;
38340 // toolbar on the tabbar support...
38341 if (this.toolbar) {
38342 alert("no toolbar support yet");
38343 this.toolbar = false;
38345 var tcfg = this.toolbar;
38346 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
38347 this.toolbar = new Roo.Toolbar(tcfg);
38348 if (Roo.isSafari) {
38349 var tbl = tcfg.container.child('table', true);
38350 tbl.setAttribute('width', '100%');
38358 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38361 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38363 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38365 tabPosition : "top",
38367 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38369 currentTabWidth : 0,
38371 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38375 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38379 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38381 preferredTabWidth : 175,
38383 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38385 resizeTabs : false,
38387 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38389 monitorResize : true,
38391 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
38393 toolbar : false, // set by caller..
38395 region : false, /// set by caller
38397 disableTooltips : true, // not used yet...
38400 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38401 * @param {String} id The id of the div to use <b>or create</b>
38402 * @param {String} text The text for the tab
38403 * @param {String} content (optional) Content to put in the TabPanelItem body
38404 * @param {Boolean} closable (optional) True to create a close icon on the tab
38405 * @return {Roo.TabPanelItem} The created TabPanelItem
38407 addTab : function(id, text, content, closable, tpl)
38409 var item = new Roo.bootstrap.panel.TabItem({
38413 closable : closable,
38416 this.addTabItem(item);
38418 item.setContent(content);
38424 * Returns the {@link Roo.TabPanelItem} with the specified id/index
38425 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38426 * @return {Roo.TabPanelItem}
38428 getTab : function(id){
38429 return this.items[id];
38433 * Hides the {@link Roo.TabPanelItem} with the specified id/index
38434 * @param {String/Number} id The id or index of the TabPanelItem to hide.
38436 hideTab : function(id){
38437 var t = this.items[id];
38440 this.hiddenCount++;
38441 this.autoSizeTabs();
38446 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38447 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38449 unhideTab : function(id){
38450 var t = this.items[id];
38452 t.setHidden(false);
38453 this.hiddenCount--;
38454 this.autoSizeTabs();
38459 * Adds an existing {@link Roo.TabPanelItem}.
38460 * @param {Roo.TabPanelItem} item The TabPanelItem to add
38462 addTabItem : function(item)
38464 this.items[item.id] = item;
38465 this.items.push(item);
38466 this.autoSizeTabs();
38467 // if(this.resizeTabs){
38468 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38469 // this.autoSizeTabs();
38471 // item.autoSize();
38476 * Removes a {@link Roo.TabPanelItem}.
38477 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38479 removeTab : function(id){
38480 var items = this.items;
38481 var tab = items[id];
38482 if(!tab) { return; }
38483 var index = items.indexOf(tab);
38484 if(this.active == tab && items.length > 1){
38485 var newTab = this.getNextAvailable(index);
38490 this.stripEl.dom.removeChild(tab.pnode.dom);
38491 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38492 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38494 items.splice(index, 1);
38495 delete this.items[tab.id];
38496 tab.fireEvent("close", tab);
38497 tab.purgeListeners();
38498 this.autoSizeTabs();
38501 getNextAvailable : function(start){
38502 var items = this.items;
38504 // look for a next tab that will slide over to
38505 // replace the one being removed
38506 while(index < items.length){
38507 var item = items[++index];
38508 if(item && !item.isHidden()){
38512 // if one isn't found select the previous tab (on the left)
38515 var item = items[--index];
38516 if(item && !item.isHidden()){
38524 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38525 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38527 disableTab : function(id){
38528 var tab = this.items[id];
38529 if(tab && this.active != tab){
38535 * Enables a {@link Roo.TabPanelItem} that is disabled.
38536 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38538 enableTab : function(id){
38539 var tab = this.items[id];
38544 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38545 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38546 * @return {Roo.TabPanelItem} The TabPanelItem.
38548 activate : function(id)
38550 //Roo.log('activite:' + id);
38552 var tab = this.items[id];
38556 if(tab == this.active || tab.disabled){
38560 this.fireEvent("beforetabchange", this, e, tab);
38561 if(e.cancel !== true && !tab.disabled){
38563 this.active.hide();
38565 this.active = this.items[id];
38566 this.active.show();
38567 this.fireEvent("tabchange", this, this.active);
38573 * Gets the active {@link Roo.TabPanelItem}.
38574 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38576 getActiveTab : function(){
38577 return this.active;
38581 * Updates the tab body element to fit the height of the container element
38582 * for overflow scrolling
38583 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38585 syncHeight : function(targetHeight){
38586 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38587 var bm = this.bodyEl.getMargins();
38588 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38589 this.bodyEl.setHeight(newHeight);
38593 onResize : function(){
38594 if(this.monitorResize){
38595 this.autoSizeTabs();
38600 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38602 beginUpdate : function(){
38603 this.updating = true;
38607 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38609 endUpdate : function(){
38610 this.updating = false;
38611 this.autoSizeTabs();
38615 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38617 autoSizeTabs : function()
38619 var count = this.items.length;
38620 var vcount = count - this.hiddenCount;
38623 this.stripEl.hide();
38625 this.stripEl.show();
38628 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38633 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38634 var availWidth = Math.floor(w / vcount);
38635 var b = this.stripBody;
38636 if(b.getWidth() > w){
38637 var tabs = this.items;
38638 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38639 if(availWidth < this.minTabWidth){
38640 /*if(!this.sleft){ // incomplete scrolling code
38641 this.createScrollButtons();
38644 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38647 if(this.currentTabWidth < this.preferredTabWidth){
38648 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38654 * Returns the number of tabs in this TabPanel.
38657 getCount : function(){
38658 return this.items.length;
38662 * Resizes all the tabs to the passed width
38663 * @param {Number} The new width
38665 setTabWidth : function(width){
38666 this.currentTabWidth = width;
38667 for(var i = 0, len = this.items.length; i < len; i++) {
38668 if(!this.items[i].isHidden()) {
38669 this.items[i].setWidth(width);
38675 * Destroys this TabPanel
38676 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38678 destroy : function(removeEl){
38679 Roo.EventManager.removeResizeListener(this.onResize, this);
38680 for(var i = 0, len = this.items.length; i < len; i++){
38681 this.items[i].purgeListeners();
38683 if(removeEl === true){
38684 this.el.update("");
38689 createStrip : function(container)
38691 var strip = document.createElement("nav");
38692 strip.className = Roo.bootstrap.version == 4 ?
38693 "navbar-light bg-light" :
38694 "navbar navbar-default"; //"x-tabs-wrap";
38695 container.appendChild(strip);
38699 createStripList : function(strip)
38701 // div wrapper for retard IE
38702 // returns the "tr" element.
38703 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38704 //'<div class="x-tabs-strip-wrap">'+
38705 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38706 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38707 return strip.firstChild; //.firstChild.firstChild.firstChild;
38709 createBody : function(container)
38711 var body = document.createElement("div");
38712 Roo.id(body, "tab-body");
38713 //Roo.fly(body).addClass("x-tabs-body");
38714 Roo.fly(body).addClass("tab-content");
38715 container.appendChild(body);
38718 createItemBody :function(bodyEl, id){
38719 var body = Roo.getDom(id);
38721 body = document.createElement("div");
38724 //Roo.fly(body).addClass("x-tabs-item-body");
38725 Roo.fly(body).addClass("tab-pane");
38726 bodyEl.insertBefore(body, bodyEl.firstChild);
38730 createStripElements : function(stripEl, text, closable, tpl)
38732 var td = document.createElement("li"); // was td..
38733 td.className = 'nav-item';
38735 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38738 stripEl.appendChild(td);
38740 td.className = "x-tabs-closable";
38741 if(!this.closeTpl){
38742 this.closeTpl = new Roo.Template(
38743 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38744 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38745 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38748 var el = this.closeTpl.overwrite(td, {"text": text});
38749 var close = el.getElementsByTagName("div")[0];
38750 var inner = el.getElementsByTagName("em")[0];
38751 return {"el": el, "close": close, "inner": inner};
38754 // not sure what this is..
38755 // if(!this.tabTpl){
38756 //this.tabTpl = new Roo.Template(
38757 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38758 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38760 // this.tabTpl = new Roo.Template(
38761 // '<a href="#">' +
38762 // '<span unselectable="on"' +
38763 // (this.disableTooltips ? '' : ' title="{text}"') +
38764 // ' >{text}</span></a>'
38770 var template = tpl || this.tabTpl || false;
38773 template = new Roo.Template(
38774 Roo.bootstrap.version == 4 ?
38776 '<a class="nav-link" href="#" unselectable="on"' +
38777 (this.disableTooltips ? '' : ' title="{text}"') +
38780 '<a class="nav-link" href="#">' +
38781 '<span unselectable="on"' +
38782 (this.disableTooltips ? '' : ' title="{text}"') +
38783 ' >{text}</span></a>'
38788 switch (typeof(template)) {
38792 template = new Roo.Template(template);
38798 var el = template.overwrite(td, {"text": text});
38800 var inner = el.getElementsByTagName("span")[0];
38802 return {"el": el, "inner": inner};
38810 * @class Roo.TabPanelItem
38811 * @extends Roo.util.Observable
38812 * Represents an individual item (tab plus body) in a TabPanel.
38813 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38814 * @param {String} id The id of this TabPanelItem
38815 * @param {String} text The text for the tab of this TabPanelItem
38816 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38818 Roo.bootstrap.panel.TabItem = function(config){
38820 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38821 * @type Roo.TabPanel
38823 this.tabPanel = config.panel;
38825 * The id for this TabPanelItem
38828 this.id = config.id;
38830 this.disabled = false;
38832 this.text = config.text;
38834 this.loaded = false;
38835 this.closable = config.closable;
38838 * The body element for this TabPanelItem.
38839 * @type Roo.Element
38841 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38842 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38843 this.bodyEl.setStyle("display", "block");
38844 this.bodyEl.setStyle("zoom", "1");
38845 //this.hideAction();
38847 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38849 this.el = Roo.get(els.el);
38850 this.inner = Roo.get(els.inner, true);
38851 this.textEl = Roo.bootstrap.version == 4 ?
38852 this.el : Roo.get(this.el.dom.firstChild, true);
38854 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38855 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38858 // this.el.on("mousedown", this.onTabMouseDown, this);
38859 this.el.on("click", this.onTabClick, this);
38861 if(config.closable){
38862 var c = Roo.get(els.close, true);
38863 c.dom.title = this.closeText;
38864 c.addClassOnOver("close-over");
38865 c.on("click", this.closeClick, this);
38871 * Fires when this tab becomes the active tab.
38872 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38873 * @param {Roo.TabPanelItem} this
38877 * @event beforeclose
38878 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38879 * @param {Roo.TabPanelItem} this
38880 * @param {Object} e Set cancel to true on this object to cancel the close.
38882 "beforeclose": true,
38885 * Fires when this tab is closed.
38886 * @param {Roo.TabPanelItem} this
38890 * @event deactivate
38891 * Fires when this tab is no longer the active tab.
38892 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38893 * @param {Roo.TabPanelItem} this
38895 "deactivate" : true
38897 this.hidden = false;
38899 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38902 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38904 purgeListeners : function(){
38905 Roo.util.Observable.prototype.purgeListeners.call(this);
38906 this.el.removeAllListeners();
38909 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38912 this.status_node.addClass("active");
38915 this.tabPanel.stripWrap.repaint();
38917 this.fireEvent("activate", this.tabPanel, this);
38921 * Returns true if this tab is the active tab.
38922 * @return {Boolean}
38924 isActive : function(){
38925 return this.tabPanel.getActiveTab() == this;
38929 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38932 this.status_node.removeClass("active");
38934 this.fireEvent("deactivate", this.tabPanel, this);
38937 hideAction : function(){
38938 this.bodyEl.hide();
38939 this.bodyEl.setStyle("position", "absolute");
38940 this.bodyEl.setLeft("-20000px");
38941 this.bodyEl.setTop("-20000px");
38944 showAction : function(){
38945 this.bodyEl.setStyle("position", "relative");
38946 this.bodyEl.setTop("");
38947 this.bodyEl.setLeft("");
38948 this.bodyEl.show();
38952 * Set the tooltip for the tab.
38953 * @param {String} tooltip The tab's tooltip
38955 setTooltip : function(text){
38956 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38957 this.textEl.dom.qtip = text;
38958 this.textEl.dom.removeAttribute('title');
38960 this.textEl.dom.title = text;
38964 onTabClick : function(e){
38965 e.preventDefault();
38966 this.tabPanel.activate(this.id);
38969 onTabMouseDown : function(e){
38970 e.preventDefault();
38971 this.tabPanel.activate(this.id);
38974 getWidth : function(){
38975 return this.inner.getWidth();
38978 setWidth : function(width){
38979 var iwidth = width - this.linode.getPadding("lr");
38980 this.inner.setWidth(iwidth);
38981 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38982 this.linode.setWidth(width);
38986 * Show or hide the tab
38987 * @param {Boolean} hidden True to hide or false to show.
38989 setHidden : function(hidden){
38990 this.hidden = hidden;
38991 this.linode.setStyle("display", hidden ? "none" : "");
38995 * Returns true if this tab is "hidden"
38996 * @return {Boolean}
38998 isHidden : function(){
38999 return this.hidden;
39003 * Returns the text for this tab
39006 getText : function(){
39010 autoSize : function(){
39011 //this.el.beginMeasure();
39012 this.textEl.setWidth(1);
39014 * #2804 [new] Tabs in Roojs
39015 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
39017 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
39018 //this.el.endMeasure();
39022 * Sets the text for the tab (Note: this also sets the tooltip text)
39023 * @param {String} text The tab's text and tooltip
39025 setText : function(text){
39027 this.textEl.update(text);
39028 this.setTooltip(text);
39029 //if(!this.tabPanel.resizeTabs){
39030 // this.autoSize();
39034 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39036 activate : function(){
39037 this.tabPanel.activate(this.id);
39041 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
39043 disable : function(){
39044 if(this.tabPanel.active != this){
39045 this.disabled = true;
39046 this.status_node.addClass("disabled");
39051 * Enables this TabPanelItem if it was previously disabled.
39053 enable : function(){
39054 this.disabled = false;
39055 this.status_node.removeClass("disabled");
39059 * Sets the content for this TabPanelItem.
39060 * @param {String} content The content
39061 * @param {Boolean} loadScripts true to look for and load scripts
39063 setContent : function(content, loadScripts){
39064 this.bodyEl.update(content, loadScripts);
39068 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
39069 * @return {Roo.UpdateManager} The UpdateManager
39071 getUpdateManager : function(){
39072 return this.bodyEl.getUpdateManager();
39076 * Set a URL to be used to load the content for this TabPanelItem.
39077 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
39078 * @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)
39079 * @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)
39080 * @return {Roo.UpdateManager} The UpdateManager
39082 setUrl : function(url, params, loadOnce){
39083 if(this.refreshDelegate){
39084 this.un('activate', this.refreshDelegate);
39086 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39087 this.on("activate", this.refreshDelegate);
39088 return this.bodyEl.getUpdateManager();
39092 _handleRefresh : function(url, params, loadOnce){
39093 if(!loadOnce || !this.loaded){
39094 var updater = this.bodyEl.getUpdateManager();
39095 updater.update(url, params, this._setLoaded.createDelegate(this));
39100 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
39101 * Will fail silently if the setUrl method has not been called.
39102 * This does not activate the panel, just updates its content.
39104 refresh : function(){
39105 if(this.refreshDelegate){
39106 this.loaded = false;
39107 this.refreshDelegate();
39112 _setLoaded : function(){
39113 this.loaded = true;
39117 closeClick : function(e){
39120 this.fireEvent("beforeclose", this, o);
39121 if(o.cancel !== true){
39122 this.tabPanel.removeTab(this.id);
39126 * The text displayed in the tooltip for the close icon.
39129 closeText : "Close this tab"
39132 * This script refer to:
39133 * Title: International Telephone Input
39134 * Author: Jack O'Connor
39135 * Code version: v12.1.12
39136 * Availability: https://github.com/jackocnr/intl-tel-input.git
39139 Roo.bootstrap.PhoneInputData = function() {
39142 "Afghanistan (افغانستان)",
39147 "Albania (Shqipëri)",
39152 "Algeria (الجزائر)",
39177 "Antigua and Barbuda",
39187 "Armenia (Հայաստան)",
39203 "Austria (Österreich)",
39208 "Azerbaijan (Azərbaycan)",
39218 "Bahrain (البحرين)",
39223 "Bangladesh (বাংলাদেশ)",
39233 "Belarus (Беларусь)",
39238 "Belgium (België)",
39268 "Bosnia and Herzegovina (Босна и Херцеговина)",
39283 "British Indian Ocean Territory",
39288 "British Virgin Islands",
39298 "Bulgaria (България)",
39308 "Burundi (Uburundi)",
39313 "Cambodia (កម្ពុជា)",
39318 "Cameroon (Cameroun)",
39327 ["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"]
39330 "Cape Verde (Kabu Verdi)",
39335 "Caribbean Netherlands",
39346 "Central African Republic (République centrafricaine)",
39366 "Christmas Island",
39372 "Cocos (Keeling) Islands",
39383 "Comoros (جزر القمر)",
39388 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39393 "Congo (Republic) (Congo-Brazzaville)",
39413 "Croatia (Hrvatska)",
39434 "Czech Republic (Česká republika)",
39439 "Denmark (Danmark)",
39454 "Dominican Republic (República Dominicana)",
39458 ["809", "829", "849"]
39476 "Equatorial Guinea (Guinea Ecuatorial)",
39496 "Falkland Islands (Islas Malvinas)",
39501 "Faroe Islands (Føroyar)",
39522 "French Guiana (Guyane française)",
39527 "French Polynesia (Polynésie française)",
39542 "Georgia (საქართველო)",
39547 "Germany (Deutschland)",
39567 "Greenland (Kalaallit Nunaat)",
39604 "Guinea-Bissau (Guiné Bissau)",
39629 "Hungary (Magyarország)",
39634 "Iceland (Ísland)",
39654 "Iraq (العراق)",
39670 "Israel (ישראל)",
39697 "Jordan (الأردن)",
39702 "Kazakhstan (Казахстан)",
39723 "Kuwait (الكويت)",
39728 "Kyrgyzstan (Кыргызстан)",
39738 "Latvia (Latvija)",
39743 "Lebanon (لبنان)",
39758 "Libya (ليبيا)",
39768 "Lithuania (Lietuva)",
39783 "Macedonia (FYROM) (Македонија)",
39788 "Madagascar (Madagasikara)",
39818 "Marshall Islands",
39828 "Mauritania (موريتانيا)",
39833 "Mauritius (Moris)",
39854 "Moldova (Republica Moldova)",
39864 "Mongolia (Монгол)",
39869 "Montenegro (Crna Gora)",
39879 "Morocco (المغرب)",
39885 "Mozambique (Moçambique)",
39890 "Myanmar (Burma) (မြန်မာ)",
39895 "Namibia (Namibië)",
39910 "Netherlands (Nederland)",
39915 "New Caledonia (Nouvelle-Calédonie)",
39950 "North Korea (조선 민주주의 인민 공화국)",
39955 "Northern Mariana Islands",
39971 "Pakistan (پاکستان)",
39981 "Palestine (فلسطين)",
39991 "Papua New Guinea",
40033 "Réunion (La Réunion)",
40039 "Romania (România)",
40055 "Saint Barthélemy",
40066 "Saint Kitts and Nevis",
40076 "Saint Martin (Saint-Martin (partie française))",
40082 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
40087 "Saint Vincent and the Grenadines",
40102 "São Tomé and Príncipe (São Tomé e Príncipe)",
40107 "Saudi Arabia (المملكة العربية السعودية)",
40112 "Senegal (Sénégal)",
40142 "Slovakia (Slovensko)",
40147 "Slovenia (Slovenija)",
40157 "Somalia (Soomaaliya)",
40167 "South Korea (대한민국)",
40172 "South Sudan (جنوب السودان)",
40182 "Sri Lanka (ශ්රී ලංකාව)",
40187 "Sudan (السودان)",
40197 "Svalbard and Jan Mayen",
40208 "Sweden (Sverige)",
40213 "Switzerland (Schweiz)",
40218 "Syria (سوريا)",
40263 "Trinidad and Tobago",
40268 "Tunisia (تونس)",
40273 "Turkey (Türkiye)",
40283 "Turks and Caicos Islands",
40293 "U.S. Virgin Islands",
40303 "Ukraine (Україна)",
40308 "United Arab Emirates (الإمارات العربية المتحدة)",
40330 "Uzbekistan (Oʻzbekiston)",
40340 "Vatican City (Città del Vaticano)",
40351 "Vietnam (Việt Nam)",
40356 "Wallis and Futuna (Wallis-et-Futuna)",
40361 "Western Sahara (الصحراء الغربية)",
40367 "Yemen (اليمن)",
40391 * This script refer to:
40392 * Title: International Telephone Input
40393 * Author: Jack O'Connor
40394 * Code version: v12.1.12
40395 * Availability: https://github.com/jackocnr/intl-tel-input.git
40399 * @class Roo.bootstrap.PhoneInput
40400 * @extends Roo.bootstrap.TriggerField
40401 * An input with International dial-code selection
40403 * @cfg {String} defaultDialCode default '+852'
40404 * @cfg {Array} preferedCountries default []
40407 * Create a new PhoneInput.
40408 * @param {Object} config Configuration options
40411 Roo.bootstrap.PhoneInput = function(config) {
40412 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40415 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40417 listWidth: undefined,
40419 selectedClass: 'active',
40421 invalidClass : "has-warning",
40423 validClass: 'has-success',
40425 allowed: '0123456789',
40430 * @cfg {String} defaultDialCode The default dial code when initializing the input
40432 defaultDialCode: '+852',
40435 * @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
40437 preferedCountries: false,
40439 getAutoCreate : function()
40441 var data = Roo.bootstrap.PhoneInputData();
40442 var align = this.labelAlign || this.parentLabelAlign();
40445 this.allCountries = [];
40446 this.dialCodeMapping = [];
40448 for (var i = 0; i < data.length; i++) {
40450 this.allCountries[i] = {
40454 priority: c[3] || 0,
40455 areaCodes: c[4] || null
40457 this.dialCodeMapping[c[2]] = {
40460 priority: c[3] || 0,
40461 areaCodes: c[4] || null
40473 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40474 maxlength: this.max_length,
40475 cls : 'form-control tel-input',
40476 autocomplete: 'new-password'
40479 var hiddenInput = {
40482 cls: 'hidden-tel-input'
40486 hiddenInput.name = this.name;
40489 if (this.disabled) {
40490 input.disabled = true;
40493 var flag_container = {
40510 cls: this.hasFeedback ? 'has-feedback' : '',
40516 cls: 'dial-code-holder',
40523 cls: 'roo-select2-container input-group',
40530 if (this.fieldLabel.length) {
40533 tooltip: 'This field is required'
40539 cls: 'control-label',
40545 html: this.fieldLabel
40548 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40554 if(this.indicatorpos == 'right') {
40555 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40562 if(align == 'left') {
40570 if(this.labelWidth > 12){
40571 label.style = "width: " + this.labelWidth + 'px';
40573 if(this.labelWidth < 13 && this.labelmd == 0){
40574 this.labelmd = this.labelWidth;
40576 if(this.labellg > 0){
40577 label.cls += ' col-lg-' + this.labellg;
40578 input.cls += ' col-lg-' + (12 - this.labellg);
40580 if(this.labelmd > 0){
40581 label.cls += ' col-md-' + this.labelmd;
40582 container.cls += ' col-md-' + (12 - this.labelmd);
40584 if(this.labelsm > 0){
40585 label.cls += ' col-sm-' + this.labelsm;
40586 container.cls += ' col-sm-' + (12 - this.labelsm);
40588 if(this.labelxs > 0){
40589 label.cls += ' col-xs-' + this.labelxs;
40590 container.cls += ' col-xs-' + (12 - this.labelxs);
40600 var settings = this;
40602 ['xs','sm','md','lg'].map(function(size){
40603 if (settings[size]) {
40604 cfg.cls += ' col-' + size + '-' + settings[size];
40608 this.store = new Roo.data.Store({
40609 proxy : new Roo.data.MemoryProxy({}),
40610 reader : new Roo.data.JsonReader({
40621 'name' : 'dialCode',
40625 'name' : 'priority',
40629 'name' : 'areaCodes',
40636 if(!this.preferedCountries) {
40637 this.preferedCountries = [
40644 var p = this.preferedCountries.reverse();
40647 for (var i = 0; i < p.length; i++) {
40648 for (var j = 0; j < this.allCountries.length; j++) {
40649 if(this.allCountries[j].iso2 == p[i]) {
40650 var t = this.allCountries[j];
40651 this.allCountries.splice(j,1);
40652 this.allCountries.unshift(t);
40658 this.store.proxy.data = {
40660 data: this.allCountries
40666 initEvents : function()
40669 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40671 this.indicator = this.indicatorEl();
40672 this.flag = this.flagEl();
40673 this.dialCodeHolder = this.dialCodeHolderEl();
40675 this.trigger = this.el.select('div.flag-box',true).first();
40676 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40681 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40682 _this.list.setWidth(lw);
40685 this.list.on('mouseover', this.onViewOver, this);
40686 this.list.on('mousemove', this.onViewMove, this);
40687 this.inputEl().on("keyup", this.onKeyUp, this);
40688 this.inputEl().on("keypress", this.onKeyPress, this);
40690 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40692 this.view = new Roo.View(this.list, this.tpl, {
40693 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40696 this.view.on('click', this.onViewClick, this);
40697 this.setValue(this.defaultDialCode);
40700 onTriggerClick : function(e)
40702 Roo.log('trigger click');
40707 if(this.isExpanded()){
40709 this.hasFocus = false;
40711 this.store.load({});
40712 this.hasFocus = true;
40717 isExpanded : function()
40719 return this.list.isVisible();
40722 collapse : function()
40724 if(!this.isExpanded()){
40728 Roo.get(document).un('mousedown', this.collapseIf, this);
40729 Roo.get(document).un('mousewheel', this.collapseIf, this);
40730 this.fireEvent('collapse', this);
40734 expand : function()
40738 if(this.isExpanded() || !this.hasFocus){
40742 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40743 this.list.setWidth(lw);
40746 this.restrictHeight();
40748 Roo.get(document).on('mousedown', this.collapseIf, this);
40749 Roo.get(document).on('mousewheel', this.collapseIf, this);
40751 this.fireEvent('expand', this);
40754 restrictHeight : function()
40756 this.list.alignTo(this.inputEl(), this.listAlign);
40757 this.list.alignTo(this.inputEl(), this.listAlign);
40760 onViewOver : function(e, t)
40762 if(this.inKeyMode){
40765 var item = this.view.findItemFromChild(t);
40768 var index = this.view.indexOf(item);
40769 this.select(index, false);
40774 onViewClick : function(view, doFocus, el, e)
40776 var index = this.view.getSelectedIndexes()[0];
40778 var r = this.store.getAt(index);
40781 this.onSelect(r, index);
40783 if(doFocus !== false && !this.blockFocus){
40784 this.inputEl().focus();
40788 onViewMove : function(e, t)
40790 this.inKeyMode = false;
40793 select : function(index, scrollIntoView)
40795 this.selectedIndex = index;
40796 this.view.select(index);
40797 if(scrollIntoView !== false){
40798 var el = this.view.getNode(index);
40800 this.list.scrollChildIntoView(el, false);
40805 createList : function()
40807 this.list = Roo.get(document.body).createChild({
40809 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40810 style: 'display:none'
40813 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40816 collapseIf : function(e)
40818 var in_combo = e.within(this.el);
40819 var in_list = e.within(this.list);
40820 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40822 if (in_combo || in_list || is_list) {
40828 onSelect : function(record, index)
40830 if(this.fireEvent('beforeselect', this, record, index) !== false){
40832 this.setFlagClass(record.data.iso2);
40833 this.setDialCode(record.data.dialCode);
40834 this.hasFocus = false;
40836 this.fireEvent('select', this, record, index);
40840 flagEl : function()
40842 var flag = this.el.select('div.flag',true).first();
40849 dialCodeHolderEl : function()
40851 var d = this.el.select('input.dial-code-holder',true).first();
40858 setDialCode : function(v)
40860 this.dialCodeHolder.dom.value = '+'+v;
40863 setFlagClass : function(n)
40865 this.flag.dom.className = 'flag '+n;
40868 getValue : function()
40870 var v = this.inputEl().getValue();
40871 if(this.dialCodeHolder) {
40872 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40877 setValue : function(v)
40879 var d = this.getDialCode(v);
40881 //invalid dial code
40882 if(v.length == 0 || !d || d.length == 0) {
40884 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40885 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40891 this.setFlagClass(this.dialCodeMapping[d].iso2);
40892 this.setDialCode(d);
40893 this.inputEl().dom.value = v.replace('+'+d,'');
40894 this.hiddenEl().dom.value = this.getValue();
40899 getDialCode : function(v)
40903 if (v.length == 0) {
40904 return this.dialCodeHolder.dom.value;
40908 if (v.charAt(0) != "+") {
40911 var numericChars = "";
40912 for (var i = 1; i < v.length; i++) {
40913 var c = v.charAt(i);
40916 if (this.dialCodeMapping[numericChars]) {
40917 dialCode = v.substr(1, i);
40919 if (numericChars.length == 4) {
40929 this.setValue(this.defaultDialCode);
40933 hiddenEl : function()
40935 return this.el.select('input.hidden-tel-input',true).first();
40938 // after setting val
40939 onKeyUp : function(e){
40940 this.setValue(this.getValue());
40943 onKeyPress : function(e){
40944 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40951 * @class Roo.bootstrap.MoneyField
40952 * @extends Roo.bootstrap.ComboBox
40953 * Bootstrap MoneyField class
40956 * Create a new MoneyField.
40957 * @param {Object} config Configuration options
40960 Roo.bootstrap.MoneyField = function(config) {
40962 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40966 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40969 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40971 allowDecimals : true,
40973 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40975 decimalSeparator : ".",
40977 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40979 decimalPrecision : 0,
40981 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40983 allowNegative : true,
40985 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40989 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40991 minValue : Number.NEGATIVE_INFINITY,
40993 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40995 maxValue : Number.MAX_VALUE,
40997 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40999 minText : "The minimum value for this field is {0}",
41001 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41003 maxText : "The maximum value for this field is {0}",
41005 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
41006 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41008 nanText : "{0} is not a valid number",
41010 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
41014 * @cfg {String} defaults currency of the MoneyField
41015 * value should be in lkey
41017 defaultCurrency : false,
41019 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41021 thousandsDelimiter : false,
41023 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41034 getAutoCreate : function()
41036 var align = this.labelAlign || this.parentLabelAlign();
41048 cls : 'form-control roo-money-amount-input',
41049 autocomplete: 'new-password'
41052 var hiddenInput = {
41056 cls: 'hidden-number-input'
41059 if(this.max_length) {
41060 input.maxlength = this.max_length;
41064 hiddenInput.name = this.name;
41067 if (this.disabled) {
41068 input.disabled = true;
41071 var clg = 12 - this.inputlg;
41072 var cmd = 12 - this.inputmd;
41073 var csm = 12 - this.inputsm;
41074 var cxs = 12 - this.inputxs;
41078 cls : 'row roo-money-field',
41082 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
41086 cls: 'roo-select2-container input-group',
41090 cls : 'form-control roo-money-currency-input',
41091 autocomplete: 'new-password',
41093 name : this.currencyName
41097 cls : 'input-group-addon',
41111 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41115 cls: this.hasFeedback ? 'has-feedback' : '',
41126 if (this.fieldLabel.length) {
41129 tooltip: 'This field is required'
41135 cls: 'control-label',
41141 html: this.fieldLabel
41144 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41150 if(this.indicatorpos == 'right') {
41151 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41158 if(align == 'left') {
41166 if(this.labelWidth > 12){
41167 label.style = "width: " + this.labelWidth + 'px';
41169 if(this.labelWidth < 13 && this.labelmd == 0){
41170 this.labelmd = this.labelWidth;
41172 if(this.labellg > 0){
41173 label.cls += ' col-lg-' + this.labellg;
41174 input.cls += ' col-lg-' + (12 - this.labellg);
41176 if(this.labelmd > 0){
41177 label.cls += ' col-md-' + this.labelmd;
41178 container.cls += ' col-md-' + (12 - this.labelmd);
41180 if(this.labelsm > 0){
41181 label.cls += ' col-sm-' + this.labelsm;
41182 container.cls += ' col-sm-' + (12 - this.labelsm);
41184 if(this.labelxs > 0){
41185 label.cls += ' col-xs-' + this.labelxs;
41186 container.cls += ' col-xs-' + (12 - this.labelxs);
41197 var settings = this;
41199 ['xs','sm','md','lg'].map(function(size){
41200 if (settings[size]) {
41201 cfg.cls += ' col-' + size + '-' + settings[size];
41208 initEvents : function()
41210 this.indicator = this.indicatorEl();
41212 this.initCurrencyEvent();
41214 this.initNumberEvent();
41217 initCurrencyEvent : function()
41220 throw "can not find store for combo";
41223 this.store = Roo.factory(this.store, Roo.data);
41224 this.store.parent = this;
41228 this.triggerEl = this.el.select('.input-group-addon', true).first();
41230 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41235 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41236 _this.list.setWidth(lw);
41239 this.list.on('mouseover', this.onViewOver, this);
41240 this.list.on('mousemove', this.onViewMove, this);
41241 this.list.on('scroll', this.onViewScroll, this);
41244 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41247 this.view = new Roo.View(this.list, this.tpl, {
41248 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41251 this.view.on('click', this.onViewClick, this);
41253 this.store.on('beforeload', this.onBeforeLoad, this);
41254 this.store.on('load', this.onLoad, this);
41255 this.store.on('loadexception', this.onLoadException, this);
41257 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41258 "up" : function(e){
41259 this.inKeyMode = true;
41263 "down" : function(e){
41264 if(!this.isExpanded()){
41265 this.onTriggerClick();
41267 this.inKeyMode = true;
41272 "enter" : function(e){
41275 if(this.fireEvent("specialkey", this, e)){
41276 this.onViewClick(false);
41282 "esc" : function(e){
41286 "tab" : function(e){
41289 if(this.fireEvent("specialkey", this, e)){
41290 this.onViewClick(false);
41298 doRelay : function(foo, bar, hname){
41299 if(hname == 'down' || this.scope.isExpanded()){
41300 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41308 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41312 initNumberEvent : function(e)
41314 this.inputEl().on("keydown" , this.fireKey, this);
41315 this.inputEl().on("focus", this.onFocus, this);
41316 this.inputEl().on("blur", this.onBlur, this);
41318 this.inputEl().relayEvent('keyup', this);
41320 if(this.indicator){
41321 this.indicator.addClass('invisible');
41324 this.originalValue = this.getValue();
41326 if(this.validationEvent == 'keyup'){
41327 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41328 this.inputEl().on('keyup', this.filterValidation, this);
41330 else if(this.validationEvent !== false){
41331 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41334 if(this.selectOnFocus){
41335 this.on("focus", this.preFocus, this);
41338 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41339 this.inputEl().on("keypress", this.filterKeys, this);
41341 this.inputEl().relayEvent('keypress', this);
41344 var allowed = "0123456789";
41346 if(this.allowDecimals){
41347 allowed += this.decimalSeparator;
41350 if(this.allowNegative){
41354 if(this.thousandsDelimiter) {
41358 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41360 var keyPress = function(e){
41362 var k = e.getKey();
41364 var c = e.getCharCode();
41367 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41368 allowed.indexOf(String.fromCharCode(c)) === -1
41374 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41378 if(allowed.indexOf(String.fromCharCode(c)) === -1){
41383 this.inputEl().on("keypress", keyPress, this);
41387 onTriggerClick : function(e)
41394 this.loadNext = false;
41396 if(this.isExpanded()){
41401 this.hasFocus = true;
41403 if(this.triggerAction == 'all') {
41404 this.doQuery(this.allQuery, true);
41408 this.doQuery(this.getRawValue());
41411 getCurrency : function()
41413 var v = this.currencyEl().getValue();
41418 restrictHeight : function()
41420 this.list.alignTo(this.currencyEl(), this.listAlign);
41421 this.list.alignTo(this.currencyEl(), this.listAlign);
41424 onViewClick : function(view, doFocus, el, e)
41426 var index = this.view.getSelectedIndexes()[0];
41428 var r = this.store.getAt(index);
41431 this.onSelect(r, index);
41435 onSelect : function(record, index){
41437 if(this.fireEvent('beforeselect', this, record, index) !== false){
41439 this.setFromCurrencyData(index > -1 ? record.data : false);
41443 this.fireEvent('select', this, record, index);
41447 setFromCurrencyData : function(o)
41451 this.lastCurrency = o;
41453 if (this.currencyField) {
41454 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41456 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
41459 this.lastSelectionText = currency;
41461 //setting default currency
41462 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41463 this.setCurrency(this.defaultCurrency);
41467 this.setCurrency(currency);
41470 setFromData : function(o)
41474 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41476 this.setFromCurrencyData(c);
41481 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41483 Roo.log('no value set for '+ (this.name ? this.name : this.id));
41486 this.setValue(value);
41490 setCurrency : function(v)
41492 this.currencyValue = v;
41495 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41500 setValue : function(v)
41502 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41508 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41510 this.inputEl().dom.value = (v == '') ? '' :
41511 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41513 if(!this.allowZero && v === '0') {
41514 this.hiddenEl().dom.value = '';
41515 this.inputEl().dom.value = '';
41522 getRawValue : function()
41524 var v = this.inputEl().getValue();
41529 getValue : function()
41531 return this.fixPrecision(this.parseValue(this.getRawValue()));
41534 parseValue : function(value)
41536 if(this.thousandsDelimiter) {
41538 r = new RegExp(",", "g");
41539 value = value.replace(r, "");
41542 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41543 return isNaN(value) ? '' : value;
41547 fixPrecision : function(value)
41549 if(this.thousandsDelimiter) {
41551 r = new RegExp(",", "g");
41552 value = value.replace(r, "");
41555 var nan = isNaN(value);
41557 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41558 return nan ? '' : value;
41560 return parseFloat(value).toFixed(this.decimalPrecision);
41563 decimalPrecisionFcn : function(v)
41565 return Math.floor(v);
41568 validateValue : function(value)
41570 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41574 var num = this.parseValue(value);
41577 this.markInvalid(String.format(this.nanText, value));
41581 if(num < this.minValue){
41582 this.markInvalid(String.format(this.minText, this.minValue));
41586 if(num > this.maxValue){
41587 this.markInvalid(String.format(this.maxText, this.maxValue));
41594 validate : function()
41596 if(this.disabled || this.allowBlank){
41601 var currency = this.getCurrency();
41603 if(this.validateValue(this.getRawValue()) && currency.length){
41608 this.markInvalid();
41612 getName: function()
41617 beforeBlur : function()
41623 var v = this.parseValue(this.getRawValue());
41630 onBlur : function()
41634 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41635 //this.el.removeClass(this.focusClass);
41638 this.hasFocus = false;
41640 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41644 var v = this.getValue();
41646 if(String(v) !== String(this.startValue)){
41647 this.fireEvent('change', this, v, this.startValue);
41650 this.fireEvent("blur", this);
41653 inputEl : function()
41655 return this.el.select('.roo-money-amount-input', true).first();
41658 currencyEl : function()
41660 return this.el.select('.roo-money-currency-input', true).first();
41663 hiddenEl : function()
41665 return this.el.select('input.hidden-number-input',true).first();
41669 * @class Roo.bootstrap.BezierSignature
41670 * @extends Roo.bootstrap.Component
41671 * Bootstrap BezierSignature class
41672 * This script refer to:
41673 * Title: Signature Pad
41675 * Availability: https://github.com/szimek/signature_pad
41678 * Create a new BezierSignature
41679 * @param {Object} config The config object
41682 Roo.bootstrap.BezierSignature = function(config){
41683 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41689 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
41696 mouse_btn_down: true,
41699 * @cfg {int} canvas height
41701 canvas_height: '200px',
41704 * @cfg {float|function} Radius of a single dot.
41709 * @cfg {float} Minimum width of a line. Defaults to 0.5.
41714 * @cfg {float} Maximum width of a line. Defaults to 2.5.
41719 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41724 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41729 * @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.
41731 bg_color: 'rgba(0, 0, 0, 0)',
41734 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41736 dot_color: 'black',
41739 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41741 velocity_filter_weight: 0.7,
41744 * @cfg {function} Callback when stroke begin.
41749 * @cfg {function} Callback when stroke end.
41753 getAutoCreate : function()
41755 var cls = 'roo-signature column';
41758 cls += ' ' + this.cls;
41768 for(var i = 0; i < col_sizes.length; i++) {
41769 if(this[col_sizes[i]]) {
41770 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
41780 cls: 'roo-signature-body',
41784 cls: 'roo-signature-body-canvas',
41785 height: this.canvas_height,
41786 width: this.canvas_width
41793 style: 'display: none'
41801 initEvents: function()
41803 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
41805 var canvas = this.canvasEl();
41807 // mouse && touch event swapping...
41808 canvas.dom.style.touchAction = 'none';
41809 canvas.dom.style.msTouchAction = 'none';
41811 this.mouse_btn_down = false;
41812 canvas.on('mousedown', this._handleMouseDown, this);
41813 canvas.on('mousemove', this._handleMouseMove, this);
41814 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
41816 if (window.PointerEvent) {
41817 canvas.on('pointerdown', this._handleMouseDown, this);
41818 canvas.on('pointermove', this._handleMouseMove, this);
41819 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
41822 if ('ontouchstart' in window) {
41823 canvas.on('touchstart', this._handleTouchStart, this);
41824 canvas.on('touchmove', this._handleTouchMove, this);
41825 canvas.on('touchend', this._handleTouchEnd, this);
41828 Roo.EventManager.onWindowResize(this.resize, this, true);
41830 // file input event
41831 this.fileEl().on('change', this.uploadImage, this);
41838 resize: function(){
41840 var canvas = this.canvasEl().dom;
41841 var ctx = this.canvasElCtx();
41842 var img_data = false;
41844 if(canvas.width > 0) {
41845 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
41847 // setting canvas width will clean img data
41850 var style = window.getComputedStyle ?
41851 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
41853 var padding_left = parseInt(style.paddingLeft) || 0;
41854 var padding_right = parseInt(style.paddingRight) || 0;
41856 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
41859 ctx.putImageData(img_data, 0, 0);
41863 _handleMouseDown: function(e)
41865 if (e.browserEvent.which === 1) {
41866 this.mouse_btn_down = true;
41867 this.strokeBegin(e);
41871 _handleMouseMove: function (e)
41873 if (this.mouse_btn_down) {
41874 this.strokeMoveUpdate(e);
41878 _handleMouseUp: function (e)
41880 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
41881 this.mouse_btn_down = false;
41886 _handleTouchStart: function (e) {
41888 e.preventDefault();
41889 if (e.browserEvent.targetTouches.length === 1) {
41890 // var touch = e.browserEvent.changedTouches[0];
41891 // this.strokeBegin(touch);
41893 this.strokeBegin(e); // assume e catching the correct xy...
41897 _handleTouchMove: function (e) {
41898 e.preventDefault();
41899 // var touch = event.targetTouches[0];
41900 // _this._strokeMoveUpdate(touch);
41901 this.strokeMoveUpdate(e);
41904 _handleTouchEnd: function (e) {
41905 var wasCanvasTouched = e.target === this.canvasEl().dom;
41906 if (wasCanvasTouched) {
41907 e.preventDefault();
41908 // var touch = event.changedTouches[0];
41909 // _this._strokeEnd(touch);
41914 reset: function () {
41915 this._lastPoints = [];
41916 this._lastVelocity = 0;
41917 this._lastWidth = (this.min_width + this.max_width) / 2;
41918 this.canvasElCtx().fillStyle = this.dot_color;
41921 strokeMoveUpdate: function(e)
41923 this.strokeUpdate(e);
41925 if (this.throttle) {
41926 this.throttleStroke(this.strokeUpdate, this.throttle);
41929 this.strokeUpdate(e);
41933 strokeBegin: function(e)
41935 var newPointGroup = {
41936 color: this.dot_color,
41940 if (typeof this.onBegin === 'function') {
41944 this.curve_data.push(newPointGroup);
41946 this.strokeUpdate(e);
41949 strokeUpdate: function(e)
41951 var rect = this.canvasEl().dom.getBoundingClientRect();
41952 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
41953 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
41954 var lastPoints = lastPointGroup.points;
41955 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
41956 var isLastPointTooClose = lastPoint
41957 ? point.distanceTo(lastPoint) <= this.min_distance
41959 var color = lastPointGroup.color;
41960 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
41961 var curve = this.addPoint(point);
41963 this.drawDot({color: color, point: point});
41966 this.drawCurve({color: color, curve: curve});
41976 strokeEnd: function(e)
41978 this.strokeUpdate(e);
41979 if (typeof this.onEnd === 'function') {
41984 addPoint: function (point) {
41985 var _lastPoints = this._lastPoints;
41986 _lastPoints.push(point);
41987 if (_lastPoints.length > 2) {
41988 if (_lastPoints.length === 3) {
41989 _lastPoints.unshift(_lastPoints[0]);
41991 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
41992 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
41993 _lastPoints.shift();
41999 calculateCurveWidths: function (startPoint, endPoint) {
42000 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
42001 (1 - this.velocity_filter_weight) * this._lastVelocity;
42003 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
42006 start: this._lastWidth
42009 this._lastVelocity = velocity;
42010 this._lastWidth = newWidth;
42014 drawDot: function (_a) {
42015 var color = _a.color, point = _a.point;
42016 var ctx = this.canvasElCtx();
42017 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
42019 this.drawCurveSegment(point.x, point.y, width);
42021 ctx.fillStyle = color;
42025 drawCurve: function (_a) {
42026 var color = _a.color, curve = _a.curve;
42027 var ctx = this.canvasElCtx();
42028 var widthDelta = curve.endWidth - curve.startWidth;
42029 var drawSteps = Math.floor(curve.length()) * 2;
42031 ctx.fillStyle = color;
42032 for (var i = 0; i < drawSteps; i += 1) {
42033 var t = i / drawSteps;
42039 var x = uuu * curve.startPoint.x;
42040 x += 3 * uu * t * curve.control1.x;
42041 x += 3 * u * tt * curve.control2.x;
42042 x += ttt * curve.endPoint.x;
42043 var y = uuu * curve.startPoint.y;
42044 y += 3 * uu * t * curve.control1.y;
42045 y += 3 * u * tt * curve.control2.y;
42046 y += ttt * curve.endPoint.y;
42047 var width = curve.startWidth + ttt * widthDelta;
42048 this.drawCurveSegment(x, y, width);
42054 drawCurveSegment: function (x, y, width) {
42055 var ctx = this.canvasElCtx();
42057 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
42058 this.is_empty = false;
42063 var ctx = this.canvasElCtx();
42064 var canvas = this.canvasEl().dom;
42065 ctx.fillStyle = this.bg_color;
42066 ctx.clearRect(0, 0, canvas.width, canvas.height);
42067 ctx.fillRect(0, 0, canvas.width, canvas.height);
42068 this.curve_data = [];
42070 this.is_empty = true;
42075 return this.el.select('input',true).first();
42078 canvasEl: function()
42080 return this.el.select('canvas',true).first();
42083 canvasElCtx: function()
42085 return this.el.select('canvas',true).first().dom.getContext('2d');
42088 getImage: function(type)
42090 if(this.is_empty) {
42095 return this.canvasEl().dom.toDataURL('image/'+type, 1);
42098 drawFromImage: function(img_src)
42100 var img = new Image();
42102 img.onload = function(){
42103 this.canvasElCtx().drawImage(img, 0, 0);
42108 this.is_empty = false;
42111 selectImage: function()
42113 this.fileEl().dom.click();
42116 uploadImage: function(e)
42118 var reader = new FileReader();
42120 reader.onload = function(e){
42121 var img = new Image();
42122 img.onload = function(){
42124 this.canvasElCtx().drawImage(img, 0, 0);
42126 img.src = e.target.result;
42129 reader.readAsDataURL(e.target.files[0]);
42132 // Bezier Point Constructor
42133 Point: (function () {
42134 function Point(x, y, time) {
42137 this.time = time || Date.now();
42139 Point.prototype.distanceTo = function (start) {
42140 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42142 Point.prototype.equals = function (other) {
42143 return this.x === other.x && this.y === other.y && this.time === other.time;
42145 Point.prototype.velocityFrom = function (start) {
42146 return this.time !== start.time
42147 ? this.distanceTo(start) / (this.time - start.time)
42154 // Bezier Constructor
42155 Bezier: (function () {
42156 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42157 this.startPoint = startPoint;
42158 this.control2 = control2;
42159 this.control1 = control1;
42160 this.endPoint = endPoint;
42161 this.startWidth = startWidth;
42162 this.endWidth = endWidth;
42164 Bezier.fromPoints = function (points, widths, scope) {
42165 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42166 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42167 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42169 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42170 var dx1 = s1.x - s2.x;
42171 var dy1 = s1.y - s2.y;
42172 var dx2 = s2.x - s3.x;
42173 var dy2 = s2.y - s3.y;
42174 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42175 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42176 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42177 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42178 var dxm = m1.x - m2.x;
42179 var dym = m1.y - m2.y;
42180 var k = l2 / (l1 + l2);
42181 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42182 var tx = s2.x - cm.x;
42183 var ty = s2.y - cm.y;
42185 c1: new scope.Point(m1.x + tx, m1.y + ty),
42186 c2: new scope.Point(m2.x + tx, m2.y + ty)
42189 Bezier.prototype.length = function () {
42194 for (var i = 0; i <= steps; i += 1) {
42196 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42197 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42199 var xdiff = cx - px;
42200 var ydiff = cy - py;
42201 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42208 Bezier.prototype.point = function (t, start, c1, c2, end) {
42209 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42210 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42211 + (3.0 * c2 * (1.0 - t) * t * t)
42212 + (end * t * t * t);
42217 throttleStroke: function(fn, wait) {
42218 if (wait === void 0) { wait = 250; }
42220 var timeout = null;
42224 var later = function () {
42225 previous = Date.now();
42227 result = fn.apply(storedContext, storedArgs);
42229 storedContext = null;
42233 return function wrapper() {
42235 for (var _i = 0; _i < arguments.length; _i++) {
42236 args[_i] = arguments[_i];
42238 var now = Date.now();
42239 var remaining = wait - (now - previous);
42240 storedContext = this;
42242 if (remaining <= 0 || remaining > wait) {
42244 clearTimeout(timeout);
42248 result = fn.apply(storedContext, storedArgs);
42250 storedContext = null;
42254 else if (!timeout) {
42255 timeout = window.setTimeout(later, remaining);