2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
17 })(); Roo.bootstrap.menu = Roo.bootstrap.menu || {};
18 Roo.bootstrap.nav = {};
20 Roo.bootstrap.form = {};Roo.bootstrap.panel = {};Roo.bootstrap.layout = {};/*
22 * Ext JS Library 1.1.1
23 * Copyright(c) 2006-2007, Ext JS, LLC.
25 * Originally Released Under LGPL - original licence link has changed is not relivant.
28 * <script type="text/javascript">
34 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
35 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
36 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
39 * @param {Object} config The config object
41 Roo.Shadow = function(config){
42 Roo.apply(this, config);
43 if(typeof this.mode != "string"){
44 this.mode = this.defaultMode;
46 var o = this.offset, a = {h: 0};
47 var rad = Math.floor(this.offset/2);
48 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
54 a.l -= this.offset + rad;
55 a.t -= this.offset + rad;
66 a.l -= (this.offset - rad);
67 a.t -= this.offset + rad;
69 a.w -= (this.offset - rad)*2;
80 a.l -= (this.offset - rad);
81 a.t -= (this.offset - rad);
83 a.w -= (this.offset + rad + 1);
84 a.h -= (this.offset + rad);
93 Roo.Shadow.prototype = {
96 * The shadow display mode. Supports the following options:<br />
97 * sides: Shadow displays on both sides and bottom only<br />
98 * frame: Shadow displays equally on all four sides<br />
99 * drop: Traditional bottom-right drop shadow (default)
103 * @cfg {String} offset
104 * The number of pixels to offset the shadow from the element (defaults to 4)
112 * Displays the shadow under the target element
113 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
115 show : function(target){
116 target = Roo.get(target);
118 this.el = Roo.Shadow.Pool.pull();
119 if(this.el.dom.nextSibling != target.dom){
120 this.el.insertBefore(target);
123 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
125 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
128 target.getLeft(true),
133 this.el.dom.style.display = "block";
137 * Returns true if the shadow is visible, else false
139 isVisible : function(){
140 return this.el ? true : false;
144 * Direct alignment when values are already available. Show must be called at least once before
145 * calling this method to ensure it is initialized.
146 * @param {Number} left The target element left position
147 * @param {Number} top The target element top position
148 * @param {Number} width The target element width
149 * @param {Number} height The target element height
151 realign : function(l, t, w, h){
155 var a = this.adjusts, d = this.el.dom, s = d.style;
157 s.left = (l+a.l)+"px";
158 s.top = (t+a.t)+"px";
159 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
161 if(s.width != sws || s.height != shs){
165 var cn = d.childNodes;
166 var sww = Math.max(0, (sw-12))+"px";
167 cn[0].childNodes[1].style.width = sww;
168 cn[1].childNodes[1].style.width = sww;
169 cn[2].childNodes[1].style.width = sww;
170 cn[1].style.height = Math.max(0, (sh-12))+"px";
180 this.el.dom.style.display = "none";
181 Roo.Shadow.Pool.push(this.el);
187 * Adjust the z-index of this shadow
188 * @param {Number} zindex The new z-index
190 setZIndex : function(z){
193 this.el.setStyle("z-index", z);
198 // Private utility class that manages the internal Shadow cache
199 Roo.Shadow.Pool = function(){
201 var markup = Roo.isIE ?
202 '<div class="x-ie-shadow"></div>' :
203 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
208 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
209 sh.autoBoxAdjust = false;
221 * base class for bootstrap elements.
225 Roo.bootstrap = Roo.bootstrap || {};
227 * @class Roo.bootstrap.Component
228 * @extends Roo.Component
230 * @children Roo.bootstrap.Component
231 * Bootstrap Component base class
232 * @cfg {String} cls css class
233 * @cfg {String} style any extra css
234 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
235 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
236 * @cfg {string} dataId cutomer id
237 * @cfg {string} name Specifies name attribute
238 * @cfg {string} tooltip Text for the tooltip
239 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
240 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
243 * Do not use directly - it does not do anything..
244 * @param {Object} config The config object
249 Roo.bootstrap.Component = function(config){
250 console.log("BOOSTRAP COMPONENT CONSTRUCTOR");
252 Roo.bootstrap.Component.superclass.constructor.call(this, config);
256 * @event childrenrendered
257 * Fires when the children have been rendered..
258 * @param {Roo.bootstrap.Component} this
260 "childrenrendered" : true
269 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
272 allowDomMove : false, // to stop relocations in parent onRender...
282 * Initialize Events for the element
284 initEvents : function() { },
290 can_build_overlaid : true,
292 container_method : false,
299 // returns the parent component..
300 return Roo.ComponentMgr.get(this.parentId)
306 onRender : function(ct, position)
308 // Roo.log("Call onRender: " + this.xtype);
310 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
313 if (this.el.attr('xtype')) {
314 this.el.attr('xtypex', this.el.attr('xtype'));
315 this.el.dom.removeAttribute('xtype');
325 var cfg = Roo.apply({}, this.getAutoCreate());
327 cfg.id = this.id || Roo.id();
329 // fill in the extra attributes
330 if (this.xattr && typeof(this.xattr) =='object') {
331 for (var i in this.xattr) {
332 cfg[i] = this.xattr[i];
337 cfg.dataId = this.dataId;
341 cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
344 if (this.style) { // fixme needs to support more complex style data.
345 cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
349 cfg.name = this.name;
352 this.el = ct.createChild(cfg, position);
355 this.tooltipEl().attr('tooltip', this.tooltip);
358 if(this.tabIndex !== undefined){
359 this.el.dom.setAttribute('tabIndex', this.tabIndex);
366 * Fetch the element to add children to
367 * @return {Roo.Element} defaults to this.el
369 getChildContainer : function()
373 getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
375 return Roo.get(document.body);
379 * Fetch the element to display the tooltip on.
380 * @return {Roo.Element} defaults to this.el
382 tooltipEl : function()
387 addxtype : function(tree,cntr)
391 cn = Roo.factory(tree);
392 //Roo.log(['addxtype', cn]);
394 cn.parentType = this.xtype; //??
395 cn.parentId = this.id;
397 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
398 if (typeof(cn.container_method) == 'string') {
399 cntr = cn.container_method;
403 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
405 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
407 var build_from_html = Roo.XComponent.build_from_html;
409 var is_body = (tree.xtype == 'Body') ;
411 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
413 var self_cntr_el = Roo.get(this[cntr](false));
415 // do not try and build conditional elements
416 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
420 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
421 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
422 return this.addxtypeChild(tree,cntr, is_body);
425 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
428 return this.addxtypeChild(Roo.apply({}, tree),cntr);
431 Roo.log('skipping render');
437 if (!build_from_html) {
441 // this i think handles overlaying multiple children of the same type
442 // with the sam eelement.. - which might be buggy..
444 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
450 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
454 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
461 addxtypeChild : function (tree, cntr, is_body)
463 Roo.debug && Roo.log('addxtypeChild:' + cntr);
465 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
468 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
469 (typeof(tree['flexy:foreach']) != 'undefined');
473 skip_children = false;
474 // render the element if it's not BODY.
477 // if parent was disabled, then do not try and create the children..
478 if(!this[cntr](true)){
483 cn = Roo.factory(tree);
485 cn.parentType = this.xtype; //??
486 cn.parentId = this.id;
488 var build_from_html = Roo.XComponent.build_from_html;
491 // does the container contain child eleemnts with 'xtype' attributes.
492 // that match this xtype..
493 // note - when we render we create these as well..
494 // so we should check to see if body has xtype set.
495 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
497 var self_cntr_el = Roo.get(this[cntr](false));
498 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
500 //Roo.log(Roo.XComponent.build_from_html);
501 //Roo.log("got echild:");
504 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
505 // and are not displayed -this causes this to use up the wrong element when matching.
506 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
509 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
510 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
516 //echild.dom.removeAttribute('xtype');
518 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
519 Roo.debug && Roo.log(self_cntr_el);
520 Roo.debug && Roo.log(echild);
521 Roo.debug && Roo.log(cn);
527 // if object has flexy:if - then it may or may not be rendered.
528 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
529 // skip a flexy if element.
530 Roo.debug && Roo.log('skipping render');
531 Roo.debug && Roo.log(tree);
533 Roo.debug && Roo.log('skipping all children');
534 skip_children = true;
539 // actually if flexy:foreach is found, we really want to create
540 // multiple copies here...
542 //Roo.log(this[cntr]());
543 // some elements do not have render methods.. like the layouts...
545 if(this[cntr](true) === false){
550 cn.render && cn.render(this[cntr](true));
553 // then add the element..
560 if (typeof (tree.menu) != 'undefined') {
561 tree.menu.parentType = cn.xtype;
562 tree.menu.triggerEl = cn.el;
563 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
567 if (!tree.items || !tree.items.length) {
569 //Roo.log(["no children", this]);
574 var items = tree.items;
577 //Roo.log(items.length);
579 if (!skip_children) {
580 for(var i =0;i < items.length;i++) {
581 // Roo.log(['add child', items[i]]);
582 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
588 //Roo.log("fire childrenrendered");
590 cn.fireEvent('childrenrendered', this);
596 * Set the element that will be used to show or hide
598 setVisibilityEl : function(el)
600 this.visibilityEl = el;
604 * Get the element that will be used to show or hide
606 getVisibilityEl : function()
608 if (typeof(this.visibilityEl) == 'object') {
609 return this.visibilityEl;
612 if (typeof(this.visibilityEl) == 'string') {
613 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
620 * Show a component - removes 'hidden' class
624 if(!this.getVisibilityEl()){
628 this.getVisibilityEl().removeClass(['hidden','d-none']);
630 this.fireEvent('show', this);
635 * Hide a component - adds 'hidden' class
639 if(!this.getVisibilityEl()){
643 this.getVisibilityEl().addClass(['hidden','d-none']);
645 this.fireEvent('hide', this);
658 * @class Roo.bootstrap.Element
659 * @extends Roo.bootstrap.Component
660 * @children Roo.bootstrap.Component
661 * Bootstrap Element class (basically a DIV used to make random stuff )
663 * @cfg {String} html contents of the element
664 * @cfg {String} tag tag of the element
665 * @cfg {String} cls class of the element
666 * @cfg {Boolean} preventDefault (true|false) default false
667 * @cfg {Boolean} clickable (true|false) default false
668 * @cfg {String} role default blank - set to button to force cursor pointer
672 * Create a new Element
673 * @param {Object} config The config object
676 Roo.bootstrap.Element = function(config){
677 Roo.bootstrap.Element.superclass.constructor.call(this, config);
683 * When a element is chick
684 * @param {Roo.bootstrap.Element} this
685 * @param {Roo.EventObject} e
693 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
698 preventDefault: false,
703 getAutoCreate : function(){
707 // cls: this.cls, double assign in parent class Component.js :: onRender
710 if (this.role !== false) {
711 cfg.role = this.role;
717 initEvents: function()
719 Roo.bootstrap.Element.superclass.initEvents.call(this);
722 this.el.on('click', this.onClick, this);
728 onClick : function(e)
730 if(this.preventDefault){
734 this.fireEvent('click', this, e); // why was this double click before?
742 getValue : function()
744 return this.el.dom.innerHTML;
747 setValue : function(value)
749 this.el.dom.innerHTML = value;
764 * @class Roo.bootstrap.DropTarget
765 * @extends Roo.bootstrap.Element
766 * Bootstrap DropTarget class
768 * @cfg {string} name dropable name
771 * Create a new Dropable Area
772 * @param {Object} config The config object
775 Roo.bootstrap.DropTarget = function(config){
776 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
782 * When a element is chick
783 * @param {Roo.bootstrap.Element} this
784 * @param {Roo.EventObject} e
790 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
793 getAutoCreate : function(){
798 initEvents: function()
800 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
801 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
804 drop : this.dragDrop.createDelegate(this),
805 enter : this.dragEnter.createDelegate(this),
806 out : this.dragOut.createDelegate(this),
807 over : this.dragOver.createDelegate(this)
811 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
814 dragDrop : function(source,e,data)
816 // user has to decide how to impliment this.
819 //this.fireEvent('drop', this, source, e ,data);
823 dragEnter : function(n, dd, e, data)
825 // probably want to resize the element to match the dropped element..
827 this.originalSize = this.el.getSize();
828 this.el.setSize( n.el.getSize());
829 this.dropZone.DDM.refreshCache(this.name);
830 Roo.log([n, dd, e, data]);
833 dragOut : function(value)
835 // resize back to normal
837 this.el.setSize(this.originalSize);
838 this.dropZone.resetConstraints();
841 dragOver : function()
858 * @class Roo.bootstrap.Body
859 * @extends Roo.bootstrap.Component
860 * @children Roo.bootstrap.Component
861 * @parent none builder
862 * Bootstrap Body class
866 * @param {Object} config The config object
869 Roo.bootstrap.Body = function(config){
871 config = config || {};
873 Roo.bootstrap.Body.superclass.constructor.call(this, config);
874 this.el = Roo.get(config.el ? config.el : document.body );
875 if (this.cls && this.cls.length) {
876 Roo.get(document.body).addClass(this.cls);
880 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
882 is_body : true,// just to make sure it's constructed?
887 onRender : function(ct, position)
889 /* Roo.log("Roo.bootstrap.Body - onRender");
890 if (this.cls && this.cls.length) {
891 Roo.get(document.body).addClass(this.cls);
910 * @class Roo.bootstrap.ButtonGroup
911 * @extends Roo.bootstrap.Component
912 * Bootstrap ButtonGroup class
913 * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
915 * @cfg {String} size lg | sm | xs (default empty normal)
916 * @cfg {String} align vertical | justified (default none)
917 * @cfg {String} direction up | down (default down)
918 * @cfg {Boolean} toolbar false | true
919 * @cfg {Boolean} btn true | false
924 * @param {Object} config The config object
927 Roo.bootstrap.ButtonGroup = function(config){
928 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
931 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
939 getAutoCreate : function(){
945 cfg.html = this.html || cfg.html;
956 if (['vertical','justified'].indexOf(this.align)!==-1) {
957 cfg.cls = 'btn-group-' + this.align;
959 if (this.align == 'justified') {
960 console.log(this.items);
964 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
965 cfg.cls += ' btn-group-' + this.size;
968 if (this.direction == 'up') {
969 cfg.cls += ' dropup' ;
975 * Add a button to the group (similar to NavItem API.)
977 addItem : function(cfg)
979 var cn = new Roo.bootstrap.Button(cfg);
981 cn.parentId = this.id;
982 cn.onRender(this.el, null);
996 * @class Roo.bootstrap.Button
997 * @extends Roo.bootstrap.Component
998 * Bootstrap Button class
999 * @cfg {String} html The button content
1000 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
1001 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1002 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1003 * @cfg {String} size (lg|sm|xs)
1004 * @cfg {String} tag (a|input|submit)
1005 * @cfg {String} href empty or href
1006 * @cfg {Boolean} disabled default false;
1007 * @cfg {Boolean} isClose default false;
1008 * @cfg {String} glyphicon depricated - use fa
1009 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1010 * @cfg {String} badge text for badge
1011 * @cfg {String} theme (default|glow)
1012 * @cfg {Boolean} inverse dark themed version
1013 * @cfg {Boolean} toggle is it a slidy toggle button
1014 * @cfg {Boolean} pressed default null - if the button ahs active state
1015 * @cfg {String} ontext text for on slidy toggle state
1016 * @cfg {String} offtext text for off slidy toggle state
1017 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1018 * @cfg {Boolean} removeClass remove the standard class..
1019 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1020 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1021 * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
1024 * Create a new button
1025 * @param {Object} config The config object
1029 Roo.bootstrap.Button = function(config){
1030 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1036 * When a button is pressed
1037 * @param {Roo.bootstrap.Button} btn
1038 * @param {Roo.EventObject} e
1043 * When a button is double clicked
1044 * @param {Roo.bootstrap.Button} btn
1045 * @param {Roo.EventObject} e
1050 * After the button has been toggles
1051 * @param {Roo.bootstrap.Button} btn
1052 * @param {Roo.EventObject} e
1053 * @param {boolean} pressed (also available as button.pressed)
1059 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1080 preventDefault: true,
1089 getAutoCreate : function(){
1097 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1098 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1099 this.tag = 'button';
1103 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1105 if (this.toggle == true) {
1108 cls: 'slider-frame roo-button',
1112 'data-on-text':'ON',
1113 'data-off-text':'OFF',
1114 cls: 'slider-button',
1119 // why are we validating the weights?
1120 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1121 cfg.cls += ' ' + this.weight;
1128 cfg.cls += ' close';
1130 cfg["aria-hidden"] = true;
1132 cfg.html = "×";
1138 if (this.theme==='default') {
1139 cfg.cls = 'btn roo-button';
1141 //if (this.parentType != 'Navbar') {
1142 this.weight = this.weight.length ? this.weight : 'default';
1144 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1146 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1147 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1148 cfg.cls += ' btn-' + outline + weight;
1149 if (this.weight == 'default') {
1151 cfg.cls += ' btn-' + this.weight;
1154 } else if (this.theme==='glow') {
1157 cfg.cls = 'btn-glow roo-button';
1159 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1161 cfg.cls += ' ' + this.weight;
1167 this.cls += ' inverse';
1171 if (this.active || this.pressed === true) {
1172 cfg.cls += ' active';
1175 if (this.disabled) {
1176 cfg.disabled = 'disabled';
1180 Roo.log('changing to ul' );
1182 this.glyphicon = 'caret';
1183 if (Roo.bootstrap.version == 4) {
1184 this.fa = 'caret-down';
1189 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1191 //gsRoo.log(this.parentType);
1192 if (this.parentType === 'Navbar' && !this.parent().bar) {
1193 Roo.log('changing to li?');
1202 href : this.href || '#'
1205 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1206 cfg.cls += ' dropdown';
1213 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1215 if (this.glyphicon) {
1216 cfg.html = ' ' + cfg.html;
1221 cls: 'glyphicon glyphicon-' + this.glyphicon
1226 cfg.html = ' ' + cfg.html;
1231 cls: 'fa fas fa-' + this.fa
1241 // cfg.cls='btn roo-button';
1245 var value = cfg.html;
1250 cls: 'glyphicon glyphicon-' + this.glyphicon,
1257 cls: 'fa fas fa-' + this.fa,
1262 var bw = this.badge_weight.length ? this.badge_weight :
1263 (this.weight.length ? this.weight : 'secondary');
1264 bw = bw == 'default' ? 'secondary' : bw;
1270 cls: 'badge badge-' + bw,
1279 cfg.cls += ' dropdown';
1280 cfg.html = typeof(cfg.html) != 'undefined' ?
1281 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1284 if (cfg.tag !== 'a' && this.href !== '') {
1285 throw "Tag must be a to set href.";
1286 } else if (this.href.length > 0) {
1287 cfg.href = this.href;
1290 if(this.removeClass){
1295 cfg.target = this.target;
1300 initEvents: function() {
1301 // Roo.log('init events?');
1302 // Roo.log(this.el.dom);
1305 if (typeof (this.menu) != 'undefined') {
1306 this.menu.parentType = this.xtype;
1307 this.menu.triggerEl = this.el;
1308 this.addxtype(Roo.apply({}, this.menu));
1312 if (this.el.hasClass('roo-button')) {
1313 this.el.on('click', this.onClick, this);
1314 this.el.on('dblclick', this.onDblClick, this);
1316 this.el.select('.roo-button').on('click', this.onClick, this);
1317 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1321 if(this.removeClass){
1322 this.el.on('click', this.onClick, this);
1325 if (this.group === true) {
1326 if (this.pressed === false || this.pressed === true) {
1329 this.pressed = false;
1330 this.setActive(this.pressed);
1335 this.el.enableDisplayMode();
1338 onClick : function(e)
1340 if (this.disabled) {
1344 Roo.log('button on click ');
1345 if(this.href === '' || this.preventDefault){
1354 this.setActive(true);
1355 var pi = this.parent().items;
1356 for (var i = 0;i < pi.length;i++) {
1357 if (this == pi[i]) {
1360 if (pi[i].el.hasClass('roo-button')) {
1361 pi[i].setActive(false);
1364 this.fireEvent('click', this, e);
1368 if (this.pressed === true || this.pressed === false) {
1369 this.toggleActive(e);
1373 this.fireEvent('click', this, e);
1375 onDblClick: function(e)
1377 if (this.disabled) {
1380 if(this.preventDefault){
1383 this.fireEvent('dblclick', this, e);
1386 * Enables this button
1390 this.disabled = false;
1391 this.el.removeClass('disabled');
1392 this.el.dom.removeAttribute("disabled");
1396 * Disable this button
1398 disable : function()
1400 this.disabled = true;
1401 this.el.addClass('disabled');
1402 this.el.attr("disabled", "disabled")
1405 * sets the active state on/off,
1406 * @param {Boolean} state (optional) Force a particular state
1408 setActive : function(v) {
1410 this.el[v ? 'addClass' : 'removeClass']('active');
1414 * toggles the current active state
1416 toggleActive : function(e)
1418 this.setActive(!this.pressed); // this modifies pressed...
1419 this.fireEvent('toggle', this, e, this.pressed);
1422 * get the current active state
1423 * @return {boolean} true if it's active
1425 isActive : function()
1427 return this.el.hasClass('active');
1430 * set the text of the first selected button
1432 setText : function(str)
1434 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1437 * get the text of the first selected button
1439 getText : function()
1441 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1444 setWeight : function(str)
1446 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1447 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1449 var outline = this.outline ? 'outline-' : '';
1450 if (str == 'default') {
1451 this.el.addClass('btn-default btn-outline-secondary');
1454 this.el.addClass('btn-' + outline + str);
1459 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1461 Roo.bootstrap.Button.weights = [
1481 * @class Roo.bootstrap.Column
1482 * @extends Roo.bootstrap.Component
1483 * @children Roo.bootstrap.Component
1484 * Bootstrap Column class
1485 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1486 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1487 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1488 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1489 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1490 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1491 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1492 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1495 * @cfg {Boolean} hidden (true|false) hide the element
1496 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1497 * @cfg {String} fa (ban|check|...) font awesome icon
1498 * @cfg {Number} fasize (1|2|....) font awsome size
1500 * @cfg {String} icon (info-sign|check|...) glyphicon name
1502 * @cfg {String} html content of column.
1505 * Create a new Column
1506 * @param {Object} config The config object
1509 Roo.bootstrap.Column = function(config){
1510 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1513 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1531 getAutoCreate : function(){
1532 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1540 var sizes = ['xs','sm','md','lg'];
1541 sizes.map(function(size ,ix){
1542 //Roo.log( size + ':' + settings[size]);
1544 if (settings[size+'off'] !== false) {
1545 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1548 if (settings[size] === false) {
1552 if (!settings[size]) { // 0 = hidden
1553 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1555 for (var i = ix; i > -1; i--) {
1556 cfg.cls += ' d-' + sizes[i] + '-none';
1562 cfg.cls += ' col-' + size + '-' + settings[size] + (
1563 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1569 cfg.cls += ' hidden';
1572 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1573 cfg.cls +=' alert alert-' + this.alert;
1577 if (this.html.length) {
1578 cfg.html = this.html;
1582 if (this.fasize > 1) {
1583 fasize = ' fa-' + this.fasize + 'x';
1585 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1590 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1609 * @class Roo.bootstrap.Container
1610 * @extends Roo.bootstrap.Component
1611 * @children Roo.bootstrap.Component
1613 * Bootstrap Container class
1614 * @cfg {Boolean} jumbotron is it a jumbotron element
1615 * @cfg {String} html content of element
1616 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1617 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1618 * @cfg {String} header content of header (for panel)
1619 * @cfg {String} footer content of footer (for panel)
1620 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1621 * @cfg {String} tag (header|aside|section) type of HTML tag.
1622 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1623 * @cfg {String} fa font awesome icon
1624 * @cfg {String} icon (info-sign|check|...) glyphicon name
1625 * @cfg {Boolean} hidden (true|false) hide the element
1626 * @cfg {Boolean} expandable (true|false) default false
1627 * @cfg {Boolean} expanded (true|false) default true
1628 * @cfg {String} rheader contet on the right of header
1629 * @cfg {Boolean} clickable (true|false) default false
1633 * Create a new Container
1634 * @param {Object} config The config object
1637 Roo.bootstrap.Container = function(config){
1638 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1644 * After the panel has been expand
1646 * @param {Roo.bootstrap.Container} this
1651 * After the panel has been collapsed
1653 * @param {Roo.bootstrap.Container} this
1658 * When a element is chick
1659 * @param {Roo.bootstrap.Container} this
1660 * @param {Roo.EventObject} e
1666 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1684 getChildContainer : function() {
1690 if (this.panel.length) {
1691 return this.el.select('.panel-body',true).first();
1698 getAutoCreate : function(){
1701 tag : this.tag || 'div',
1705 if (this.jumbotron) {
1706 cfg.cls = 'jumbotron';
1711 // - this is applied by the parent..
1713 // cfg.cls = this.cls + '';
1716 if (this.sticky.length) {
1718 var bd = Roo.get(document.body);
1719 if (!bd.hasClass('bootstrap-sticky')) {
1720 bd.addClass('bootstrap-sticky');
1721 Roo.select('html',true).setStyle('height', '100%');
1724 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1728 if (this.well.length) {
1729 switch (this.well) {
1732 cfg.cls +=' well well-' +this.well;
1741 cfg.cls += ' hidden';
1745 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1746 cfg.cls +=' alert alert-' + this.alert;
1751 if (this.panel.length) {
1752 cfg.cls += ' panel panel-' + this.panel;
1754 if (this.header.length) {
1758 if(this.expandable){
1760 cfg.cls = cfg.cls + ' expandable';
1764 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1772 cls : 'panel-title',
1773 html : (this.expandable ? ' ' : '') + this.header
1777 cls: 'panel-header-right',
1783 cls : 'panel-heading',
1784 style : this.expandable ? 'cursor: pointer' : '',
1792 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1797 if (this.footer.length) {
1799 cls : 'panel-footer',
1808 body.html = this.html || cfg.html;
1809 // prefix with the icons..
1811 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1814 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1819 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1820 cfg.cls = 'container';
1826 initEvents: function()
1828 if(this.expandable){
1829 var headerEl = this.headerEl();
1832 headerEl.on('click', this.onToggleClick, this);
1837 this.el.on('click', this.onClick, this);
1842 onToggleClick : function()
1844 var headerEl = this.headerEl();
1860 if(this.fireEvent('expand', this)) {
1862 this.expanded = true;
1864 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1866 this.el.select('.panel-body',true).first().removeClass('hide');
1868 var toggleEl = this.toggleEl();
1874 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1879 collapse : function()
1881 if(this.fireEvent('collapse', this)) {
1883 this.expanded = false;
1885 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1886 this.el.select('.panel-body',true).first().addClass('hide');
1888 var toggleEl = this.toggleEl();
1894 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1898 toggleEl : function()
1900 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1904 return this.el.select('.panel-heading .fa',true).first();
1907 headerEl : function()
1909 if(!this.el || !this.panel.length || !this.header.length){
1913 return this.el.select('.panel-heading',true).first()
1918 if(!this.el || !this.panel.length){
1922 return this.el.select('.panel-body',true).first()
1925 titleEl : function()
1927 if(!this.el || !this.panel.length || !this.header.length){
1931 return this.el.select('.panel-title',true).first();
1934 setTitle : function(v)
1936 var titleEl = this.titleEl();
1942 titleEl.dom.innerHTML = v;
1945 getTitle : function()
1948 var titleEl = this.titleEl();
1954 return titleEl.dom.innerHTML;
1957 setRightTitle : function(v)
1959 var t = this.el.select('.panel-header-right',true).first();
1965 t.dom.innerHTML = v;
1968 onClick : function(e)
1972 this.fireEvent('click', this, e);
1977 * @class Roo.bootstrap.Card
1978 * @extends Roo.bootstrap.Component
1979 * @children Roo.bootstrap.Component
1981 * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1984 * possible... may not be implemented..
1985 * @cfg {String} header_image src url of image.
1986 * @cfg {String|Object} header
1987 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1988 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1990 * @cfg {String} title
1991 * @cfg {String} subtitle
1992 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1993 * @cfg {String} footer
1995 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1997 * @cfg {String} margin (0|1|2|3|4|5|auto)
1998 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1999 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
2000 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
2001 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2002 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2003 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2005 * @cfg {String} padding (0|1|2|3|4|5)
2006 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2007 * @cfg {String} padding_bottom (0|1|2|3|4|5)
2008 * @cfg {String} padding_left (0|1|2|3|4|5)
2009 * @cfg {String} padding_right (0|1|2|3|4|5)
2010 * @cfg {String} padding_x (0|1|2|3|4|5)
2011 * @cfg {String} padding_y (0|1|2|3|4|5)
2013 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2016 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2017 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2019 * @config {Boolean} dragable if this card can be dragged.
2020 * @config {String} drag_group group for drag
2021 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2022 * @config {String} drop_group group for drag
2024 * @config {Boolean} collapsable can the body be collapsed.
2025 * @config {Boolean} collapsed is the body collapsed when rendered...
2026 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2027 * @config {Boolean} rotated is the body rotated when rendered...
2030 * Create a new Container
2031 * @param {Object} config The config object
2034 Roo.bootstrap.Card = function(config){
2035 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2041 * When a element a card is dropped
2042 * @param {Roo.bootstrap.Card} this
2045 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2046 * @param {String} position 'above' or 'below'
2047 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2053 * When a element a card is rotate
2054 * @param {Roo.bootstrap.Card} this
2055 * @param {Roo.Element} n the node being dropped?
2056 * @param {Boolean} rotate status
2061 * When a card element is dragged over ready to drop (return false to block dropable)
2062 * @param {Roo.bootstrap.Card} this
2063 * @param {Object} data from dragdrop
2071 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2076 margin: '', /// may be better in component?
2106 collapsable : false,
2115 childContainer : false,
2116 dropEl : false, /// the dom placeholde element that indicates drop location.
2117 containerEl: false, // body container
2118 bodyEl: false, // card-body
2119 headerContainerEl : false, //
2121 header_imageEl : false,
2124 layoutCls : function()
2128 Roo.log(this.margin_bottom.length);
2129 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2130 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2132 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2133 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2135 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2136 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2140 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2141 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2142 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2146 // more generic support?
2154 // Roo.log("Call onRender: " + this.xtype);
2155 /* We are looking at something like this.
2157 <img src="..." class="card-img-top" alt="...">
2158 <div class="card-body">
2159 <h5 class="card-title">Card title</h5>
2160 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2162 >> this bit is really the body...
2163 <div> << we will ad dthis in hopefully it will not break shit.
2165 ** card text does not actually have any styling...
2167 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2170 <a href="#" class="card-link">Card link</a>
2173 <div class="card-footer">
2174 <small class="text-muted">Last updated 3 mins ago</small>
2178 getAutoCreate : function(){
2186 if (this.weight.length && this.weight != 'light') {
2187 cfg.cls += ' text-white';
2189 cfg.cls += ' text-dark'; // need as it's nested..
2191 if (this.weight.length) {
2192 cfg.cls += ' bg-' + this.weight;
2195 cfg.cls += ' ' + this.layoutCls();
2198 var hdr_ctr = false;
2199 if (this.header.length) {
2201 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2202 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2210 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2216 if (this.collapsable) {
2219 cls : 'd-block user-select-none',
2223 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2228 hdr.cn.push(hdr_ctr);
2233 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2238 if (this.header_image.length) {
2241 cls : 'card-img-top',
2242 src: this.header_image // escape?
2247 cls : 'card-img-top d-none'
2253 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2257 if (this.collapsable || this.rotateable) {
2260 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2267 if (this.title.length) {
2271 src: this.title // escape?
2275 if (this.subtitle.length) {
2279 src: this.subtitle // escape?
2285 cls : 'roo-card-body-ctr'
2288 if (this.html.length) {
2294 // fixme ? handle objects?
2296 if (this.footer.length) {
2299 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2304 cfg.cn.push({cls : 'card-footer d-none'});
2313 getCardHeader : function()
2315 var ret = this.el.select('.card-header',true).first();
2316 if (ret.hasClass('d-none')) {
2317 ret.removeClass('d-none');
2322 getCardFooter : function()
2324 var ret = this.el.select('.card-footer',true).first();
2325 if (ret.hasClass('d-none')) {
2326 ret.removeClass('d-none');
2331 getCardImageTop : function()
2333 var ret = this.header_imageEl;
2334 if (ret.hasClass('d-none')) {
2335 ret.removeClass('d-none');
2341 getChildContainer : function()
2347 return this.el.select('.roo-card-body-ctr',true).first();
2350 initEvents: function()
2352 this.bodyEl = this.el.select('.card-body',true).first();
2353 this.containerEl = this.getChildContainer();
2355 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2356 containerScroll: true,
2357 ddGroup: this.drag_group || 'default_card_drag_group'
2359 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2361 if (this.dropable) {
2362 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2363 containerScroll: true,
2364 ddGroup: this.drop_group || 'default_card_drag_group'
2366 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2367 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2368 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2369 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2370 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2373 if (this.collapsable) {
2374 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2376 if (this.rotateable) {
2377 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2379 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2381 this.footerEl = this.el.select('.card-footer',true).first();
2382 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2383 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2384 this.headerEl = this.el.select('.card-header',true).first();
2387 this.el.addClass('roo-card-rotated');
2388 this.fireEvent('rotate', this, true);
2390 this.header_imageEl = this.el.select('.card-img-top',true).first();
2391 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2394 getDragData : function(e)
2396 var target = this.getEl();
2398 //this.handleSelection(e);
2403 nodes: this.getEl(),
2408 dragData.ddel = target.dom ; // the div element
2409 Roo.log(target.getWidth( ));
2410 dragData.ddel.style.width = target.getWidth() + 'px';
2417 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2418 * whole Element becomes the target, and this causes the drop gesture to append.
2420 * Returns an object:
2423 position : 'below' or 'above'
2424 card : relateive to card OBJECT (or true for no cards listed)
2425 items_n : relative to nth item in list
2426 card_n : relative to nth card in list
2431 getTargetFromEvent : function(e, dragged_card_el)
2433 var target = e.getTarget();
2434 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2435 target = target.parentNode;
2446 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2447 // see if target is one of the 'cards'...
2450 //Roo.log(this.items.length);
2453 var last_card_n = 0;
2455 for (var i = 0;i< this.items.length;i++) {
2457 if (!this.items[i].el.hasClass('card')) {
2460 pos = this.getDropPoint(e, this.items[i].el.dom);
2462 cards_len = ret.cards.length;
2463 //Roo.log(this.items[i].el.dom.id);
2464 ret.cards.push(this.items[i]);
2466 if (ret.card_n < 0 && pos == 'above') {
2467 ret.position = cards_len > 0 ? 'below' : pos;
2468 ret.items_n = i > 0 ? i - 1 : 0;
2469 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2470 ret.card = ret.cards[ret.card_n];
2473 if (!ret.cards.length) {
2475 ret.position = 'below';
2479 // could not find a card.. stick it at the end..
2480 if (ret.card_n < 0) {
2481 ret.card_n = last_card_n;
2482 ret.card = ret.cards[last_card_n];
2483 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2484 ret.position = 'below';
2487 if (this.items[ret.items_n].el == dragged_card_el) {
2491 if (ret.position == 'below') {
2492 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2494 if (card_after && card_after.el == dragged_card_el) {
2501 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2503 if (card_before && card_before.el == dragged_card_el) {
2510 onNodeEnter : function(n, dd, e, data){
2513 onNodeOver : function(n, dd, e, data)
2516 var target_info = this.getTargetFromEvent(e,data.source.el);
2517 if (target_info === false) {
2518 this.dropPlaceHolder('hide');
2521 Roo.log(['getTargetFromEvent', target_info ]);
2524 if (this.fireEvent('cardover', this, [ data ]) === false) {
2528 this.dropPlaceHolder('show', target_info,data);
2532 onNodeOut : function(n, dd, e, data){
2533 this.dropPlaceHolder('hide');
2536 onNodeDrop : function(n, dd, e, data)
2539 // call drop - return false if
2541 // this could actually fail - if the Network drops..
2542 // we will ignore this at present..- client should probably reload
2543 // the whole set of cards if stuff like that fails.
2546 var info = this.getTargetFromEvent(e,data.source.el);
2547 if (info === false) {
2550 this.dropPlaceHolder('hide');
2554 this.acceptCard(data.source, info.position, info.card, info.items_n);
2558 firstChildCard : function()
2560 for (var i = 0;i< this.items.length;i++) {
2562 if (!this.items[i].el.hasClass('card')) {
2565 return this.items[i];
2567 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2572 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2574 acceptCard : function(move_card, position, next_to_card )
2576 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2580 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2582 move_card.parent().removeCard(move_card);
2585 var dom = move_card.el.dom;
2586 dom.style.width = ''; // clear with - which is set by drag.
2588 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2589 var cardel = next_to_card.el.dom;
2591 if (position == 'above' ) {
2592 cardel.parentNode.insertBefore(dom, cardel);
2593 } else if (cardel.nextSibling) {
2594 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2596 cardel.parentNode.append(dom);
2599 // card container???
2600 this.containerEl.dom.append(dom);
2603 //FIXME HANDLE card = true
2605 // add this to the correct place in items.
2607 // remove Card from items.
2610 if (this.items.length) {
2612 //Roo.log([info.items_n, info.position, this.items.length]);
2613 for (var i =0; i < this.items.length; i++) {
2614 if (i == to_items_n && position == 'above') {
2615 nitems.push(move_card);
2617 nitems.push(this.items[i]);
2618 if (i == to_items_n && position == 'below') {
2619 nitems.push(move_card);
2622 this.items = nitems;
2623 Roo.log(this.items);
2625 this.items.push(move_card);
2628 move_card.parentId = this.id;
2634 removeCard : function(c)
2636 this.items = this.items.filter(function(e) { return e != c });
2639 dom.parentNode.removeChild(dom);
2640 dom.style.width = ''; // clear with - which is set by drag.
2645 /** Decide whether to drop above or below a View node. */
2646 getDropPoint : function(e, n, dd)
2651 if (n == this.containerEl.dom) {
2654 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2655 var c = t + (b - t) / 2;
2656 var y = Roo.lib.Event.getPageY(e);
2663 onToggleCollapse : function(e)
2665 if (this.collapsed) {
2666 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2667 this.collapsableEl.addClass('show');
2668 this.collapsed = false;
2671 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2672 this.collapsableEl.removeClass('show');
2673 this.collapsed = true;
2678 onToggleRotate : function(e)
2680 this.collapsableEl.removeClass('show');
2681 this.footerEl.removeClass('d-none');
2682 this.el.removeClass('roo-card-rotated');
2683 this.el.removeClass('d-none');
2686 this.collapsableEl.addClass('show');
2687 this.rotated = false;
2688 this.fireEvent('rotate', this, this.rotated);
2691 this.el.addClass('roo-card-rotated');
2692 this.footerEl.addClass('d-none');
2693 this.el.select('.roo-collapsable').removeClass('show');
2695 this.rotated = true;
2696 this.fireEvent('rotate', this, this.rotated);
2700 dropPlaceHolder: function (action, info, data)
2702 if (this.dropEl === false) {
2703 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2707 this.dropEl.removeClass(['d-none', 'd-block']);
2708 if (action == 'hide') {
2710 this.dropEl.addClass('d-none');
2713 // FIXME - info.card == true!!!
2714 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2716 if (info.card !== true) {
2717 var cardel = info.card.el.dom;
2719 if (info.position == 'above') {
2720 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2721 } else if (cardel.nextSibling) {
2722 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2724 cardel.parentNode.append(this.dropEl.dom);
2727 // card container???
2728 this.containerEl.dom.append(this.dropEl.dom);
2731 this.dropEl.addClass('d-block roo-card-dropzone');
2733 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2740 setHeaderText: function(html)
2743 if (this.headerContainerEl) {
2744 this.headerContainerEl.dom.innerHTML = html;
2747 onHeaderImageLoad : function(ev, he)
2749 if (!this.header_image_fit_square) {
2753 var hw = he.naturalHeight / he.naturalWidth;
2756 //var w = he.dom.naturalWidth;
2759 he.style.position = 'relative';
2761 var nw = (ww * (1/hw));
2762 Roo.get(he).setSize( ww * (1/hw), ww);
2763 he.style.left = ((ww - nw)/ 2) + 'px';
2764 he.style.position = 'relative';
2775 * Card header - holder for the card header elements.
2780 * @class Roo.bootstrap.CardHeader
2781 * @extends Roo.bootstrap.Element
2782 * @parent Roo.bootstrap.Card
2783 * @children Roo.bootstrap.Component
2784 * Bootstrap CardHeader class
2786 * Create a new Card Header - that you can embed children into
2787 * @param {Object} config The config object
2790 Roo.bootstrap.CardHeader = function(config){
2791 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2794 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2797 container_method : 'getCardHeader'
2810 * Card footer - holder for the card footer elements.
2815 * @class Roo.bootstrap.CardFooter
2816 * @extends Roo.bootstrap.Element
2817 * @parent Roo.bootstrap.Card
2818 * @children Roo.bootstrap.Component
2819 * Bootstrap CardFooter class
2822 * Create a new Card Footer - that you can embed children into
2823 * @param {Object} config The config object
2826 Roo.bootstrap.CardFooter = function(config){
2827 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2830 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2833 container_method : 'getCardFooter'
2846 * Card header - holder for the card header elements.
2851 * @class Roo.bootstrap.CardImageTop
2852 * @extends Roo.bootstrap.Element
2853 * @parent Roo.bootstrap.Card
2854 * @children Roo.bootstrap.Component
2855 * Bootstrap CardImageTop class
2858 * Create a new Card Image Top container
2859 * @param {Object} config The config object
2862 Roo.bootstrap.CardImageTop = function(config){
2863 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2866 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2869 container_method : 'getCardImageTop'
2884 * @class Roo.bootstrap.ButtonUploader
2885 * @extends Roo.bootstrap.Button
2886 * Bootstrap Button Uploader class - it's a button which when you add files to it
2889 * @cfg {Number} errorTimeout default 3000
2890 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2891 * @cfg {Array} html The button text.
2892 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2895 * Create a new CardUploader
2896 * @param {Object} config The config object
2899 Roo.bootstrap.ButtonUploader = function(config){
2903 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2909 * @event beforeselect
2910 * When button is pressed, before show upload files dialog is shown
2911 * @param {Roo.bootstrap.UploaderButton} this
2914 'beforeselect' : true,
2916 * @event fired when files have been selected,
2917 * When a the download link is clicked
2918 * @param {Roo.bootstrap.UploaderButton} this
2919 * @param {Array} Array of files that have been uploaded
2926 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2929 errorTimeout : 3000,
2933 fileCollection : false,
2938 getAutoCreate : function()
2945 Roo.bootstrap.Button.prototype.getAutoCreate.call(this)
2953 initEvents : function()
2956 Roo.bootstrap.Button.prototype.initEvents.call(this);
2962 this.urlAPI = (window.createObjectURL && window) ||
2963 (window.URL && URL.revokeObjectURL && URL) ||
2964 (window.webkitURL && webkitURL);
2969 cls : 'd-none roo-card-upload-selector'
2972 if (this.multiple) {
2973 im.multiple = 'multiple';
2975 this.selectorEl = Roo.get(document.body).createChild(im); // so it does not capture click event for navitem.
2977 //this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2979 this.selectorEl.on('change', this.onFileSelected, this);
2986 onClick : function(e)
2990 if ( this.fireEvent('beforeselect', this) === false) {
2994 this.selectorEl.dom.click();
2998 onFileSelected : function(e)
3002 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3005 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3006 this.selectorEl.dom.value = '';// hopefully reset..
3008 this.fireEvent('uploaded', this, files );
3016 * addCard - add an Attachment to the uploader
3017 * @param data - the data about the image to upload
3021 title : "Title of file",
3022 is_uploaded : false,
3023 src : "http://.....",
3024 srcfile : { the File upload object },
3025 mimetype : file.type,
3028 .. any other data...
3053 * @class Roo.bootstrap.Img
3054 * @extends Roo.bootstrap.Component
3055 * Bootstrap Img class
3056 * @cfg {Boolean} imgResponsive false | true
3057 * @cfg {String} border rounded | circle | thumbnail
3058 * @cfg {String} src image source
3059 * @cfg {String} alt image alternative text
3060 * @cfg {String} href a tag href
3061 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3062 * @cfg {String} xsUrl xs image source
3063 * @cfg {String} smUrl sm image source
3064 * @cfg {String} mdUrl md image source
3065 * @cfg {String} lgUrl lg image source
3066 * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3069 * Create a new Input
3070 * @param {Object} config The config object
3073 Roo.bootstrap.Img = function(config){
3074 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3080 * The img click event for the img.
3081 * @param {Roo.EventObject} e
3086 * The when any image loads
3087 * @param {Roo.EventObject} e
3093 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3095 imgResponsive: true,
3104 backgroundContain : false,
3106 getAutoCreate : function()
3108 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3109 return this.createSingleImg();
3114 cls: 'roo-image-responsive-group',
3119 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3121 if(!_this[size + 'Url']){
3127 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3128 html: _this.html || cfg.html,
3129 src: _this[size + 'Url']
3132 img.cls += ' roo-image-responsive-' + size;
3134 var s = ['xs', 'sm', 'md', 'lg'];
3136 s.splice(s.indexOf(size), 1);
3138 Roo.each(s, function(ss){
3139 img.cls += ' hidden-' + ss;
3142 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3143 cfg.cls += ' img-' + _this.border;
3147 cfg.alt = _this.alt;
3160 a.target = _this.target;
3164 cfg.cn.push((_this.href) ? a : img);
3171 createSingleImg : function()
3175 cls: (this.imgResponsive) ? 'img-responsive' : '',
3177 src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
3180 if (this.backgroundContain) {
3181 cfg.cls += ' background-contain';
3184 cfg.html = this.html || cfg.html;
3186 if (this.backgroundContain) {
3187 cfg.style="background-image: url(" + this.src + ')';
3189 cfg.src = this.src || cfg.src;
3192 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3193 cfg.cls += ' img-' + this.border;
3210 a.target = this.target;
3215 return (this.href) ? a : cfg;
3218 initEvents: function()
3221 this.el.on('click', this.onClick, this);
3223 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3224 this.el.on('load', this.onImageLoad, this);
3226 // not sure if this works.. not tested
3227 this.el.select('img', true).on('load', this.onImageLoad, this);
3232 onClick : function(e)
3234 Roo.log('img onclick');
3235 this.fireEvent('click', this, e);
3237 onImageLoad: function(e)
3239 Roo.log('img load');
3240 this.fireEvent('load', this, e);
3244 * Sets the url of the image - used to update it
3245 * @param {String} url the url of the image
3248 setSrc : function(url)
3252 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3253 if (this.backgroundContain) {
3254 this.el.dom.style.backgroundImage = 'url(' + url + ')';
3256 this.el.dom.src = url;
3261 this.el.select('img', true).first().dom.src = url;
3277 * @class Roo.bootstrap.Link
3278 * @extends Roo.bootstrap.Component
3279 * @children Roo.bootstrap.Component
3280 * Bootstrap Link Class (eg. '<a href>')
3282 * @cfg {String} alt image alternative text
3283 * @cfg {String} href a tag href
3284 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3285 * @cfg {String} html the content of the link.
3286 * @cfg {String} anchor name for the anchor link
3287 * @cfg {String} fa - favicon
3289 * @cfg {Boolean} preventDefault (true | false) default false
3293 * Create a new Input
3294 * @param {Object} config The config object
3297 Roo.bootstrap.Link = function(config){
3298 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3304 * The img click event for the img.
3305 * @param {Roo.EventObject} e
3311 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3315 preventDefault: false,
3321 getAutoCreate : function()
3323 var html = this.html || '';
3325 if (this.fa !== false) {
3326 html = '<i class="fa fa-' + this.fa + '"></i>';
3331 // anchor's do not require html/href...
3332 if (this.anchor === false) {
3334 cfg.href = this.href || '#';
3336 cfg.name = this.anchor;
3337 if (this.html !== false || this.fa !== false) {
3340 if (this.href !== false) {
3341 cfg.href = this.href;
3345 if(this.alt !== false){
3350 if(this.target !== false) {
3351 cfg.target = this.target;
3357 initEvents: function() {
3359 if(!this.href || this.preventDefault){
3360 this.el.on('click', this.onClick, this);
3364 onClick : function(e)
3366 if(this.preventDefault){
3369 //Roo.log('img onclick');
3370 this.fireEvent('click', this, e);
3383 * @class Roo.bootstrap.Header
3384 * @extends Roo.bootstrap.Component
3385 * @children Roo.bootstrap.Component
3386 * Bootstrap Header class
3389 * @cfg {String} html content of header
3390 * @cfg {Number} level (1|2|3|4|5|6) default 1
3393 * Create a new Header
3394 * @param {Object} config The config object
3398 Roo.bootstrap.Header = function(config){
3399 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3402 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3410 getAutoCreate : function(){
3415 tag: 'h' + (1 *this.level),
3416 html: this.html || ''
3427 * @class Roo.bootstrap.MenuMgr
3429 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3432 Roo.bootstrap.menu.Manager = function(){
3433 var menus, active, groups = {}, attached = false, lastShow = new Date();
3435 // private - called when first menu is created
3438 active = new Roo.util.MixedCollection();
3439 Roo.get(document).addKeyListener(27, function(){
3440 if(active.length > 0){
3448 if(active && active.length > 0){
3449 var c = active.clone();
3459 if(active.length < 1){
3460 Roo.get(document).un("mouseup", onMouseDown);
3468 var last = active.last();
3469 lastShow = new Date();
3472 Roo.get(document).on("mouseup", onMouseDown);
3477 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3478 m.parentMenu.activeChild = m;
3479 }else if(last && last.isVisible()){
3480 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3485 function onBeforeHide(m){
3487 m.activeChild.hide();
3489 if(m.autoHideTimer){
3490 clearTimeout(m.autoHideTimer);
3491 delete m.autoHideTimer;
3496 function onBeforeShow(m){
3497 var pm = m.parentMenu;
3498 if(!pm && !m.allowOtherMenus){
3500 }else if(pm && pm.activeChild && active != m){
3501 pm.activeChild.hide();
3505 // private this should really trigger on mouseup..
3506 function onMouseDown(e){
3507 Roo.log("on Mouse Up");
3509 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3510 Roo.log("MenuManager hideAll");
3519 function onBeforeCheck(mi, state){
3521 var g = groups[mi.group];
3522 for(var i = 0, l = g.length; i < l; i++){
3524 g[i].setChecked(false);
3533 * Hides all menus that are currently visible
3535 hideAll : function(){
3540 register : function(menu){
3544 menus[menu.id] = menu;
3545 menu.on("beforehide", onBeforeHide);
3546 menu.on("hide", onHide);
3547 menu.on("beforeshow", onBeforeShow);
3548 menu.on("show", onShow);
3550 if(g && menu.events["checkchange"]){
3554 groups[g].push(menu);
3555 menu.on("checkchange", onCheck);
3560 * Returns a {@link Roo.menu.Menu} object
3561 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3562 * be used to generate and return a new Menu instance.
3564 get : function(menu){
3565 if(typeof menu == "string"){ // menu id
3567 }else if(menu.events){ // menu instance
3570 /*else if(typeof menu.length == 'number'){ // array of menu items?
3571 return new Roo.bootstrap.Menu({items:menu});
3572 }else{ // otherwise, must be a config
3573 return new Roo.bootstrap.Menu(menu);
3580 unregister : function(menu){
3581 delete menus[menu.id];
3582 menu.un("beforehide", onBeforeHide);
3583 menu.un("hide", onHide);
3584 menu.un("beforeshow", onBeforeShow);
3585 menu.un("show", onShow);
3587 if(g && menu.events["checkchange"]){
3588 groups[g].remove(menu);
3589 menu.un("checkchange", onCheck);
3594 registerCheckable : function(menuItem){
3595 var g = menuItem.group;
3600 groups[g].push(menuItem);
3601 menuItem.on("beforecheckchange", onBeforeCheck);
3606 unregisterCheckable : function(menuItem){
3607 var g = menuItem.group;
3609 groups[g].remove(menuItem);
3610 menuItem.un("beforecheckchange", onBeforeCheck);
3616 * @class Roo.bootstrap.menu.Menu
3617 * @extends Roo.bootstrap.Component
3619 * @children Roo.bootstrap.menu.Item Roo.bootstrap.menu.Separator
3621 * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3623 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3624 * @cfg {bool} hidden if the menu should be hidden when rendered.
3625 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3626 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3627 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3628 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3632 * @param {Object} config The config objectQ
3636 Roo.bootstrap.menu.Menu = function(config){
3638 if (config.type == 'treeview') {
3639 // normally menu's are drawn attached to the document to handle layering etc..
3640 // however treeview (used by the docs menu is drawn into the parent element)
3641 this.container_method = 'getChildContainer';
3644 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3645 if (this.registerMenu && this.type != 'treeview') {
3646 Roo.bootstrap.menu.Manager.register(this);
3653 * Fires before this menu is displayed (return false to block)
3654 * @param {Roo.menu.Menu} this
3659 * Fires before this menu is hidden (return false to block)
3660 * @param {Roo.menu.Menu} this
3665 * Fires after this menu is displayed
3666 * @param {Roo.menu.Menu} this
3671 * Fires after this menu is hidden
3672 * @param {Roo.menu.Menu} this
3677 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3678 * @param {Roo.menu.Menu} this
3679 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3680 * @param {Roo.EventObject} e
3685 * Fires when the mouse is hovering over this menu
3686 * @param {Roo.menu.Menu} this
3687 * @param {Roo.EventObject} e
3688 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3693 * Fires when the mouse exits this menu
3694 * @param {Roo.menu.Menu} this
3695 * @param {Roo.EventObject} e
3696 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3701 * Fires when a menu item contained in this menu is clicked
3702 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3703 * @param {Roo.EventObject} e
3707 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3710 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
3714 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3717 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3719 registerMenu : true,
3721 menuItems :false, // stores the menu items..
3731 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3733 hideTrigger : false,
3738 getChildContainer : function() {
3742 getAutoCreate : function(){
3744 //if (['right'].indexOf(this.align)!==-1) {
3745 // cfg.cn[1].cls += ' pull-right'
3750 cls : 'dropdown-menu shadow' ,
3751 style : 'z-index:1000'
3755 if (this.type === 'submenu') {
3756 cfg.cls = 'submenu active';
3758 if (this.type === 'treeview') {
3759 cfg.cls = 'treeview-menu';
3764 initEvents : function() {
3766 // Roo.log("ADD event");
3767 // Roo.log(this.triggerEl.dom);
3768 if (this.triggerEl) {
3770 this.triggerEl.on('click', this.onTriggerClick, this);
3772 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3774 if (!this.hideTrigger) {
3775 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3776 // dropdown toggle on the 'a' in BS4?
3777 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3779 this.triggerEl.addClass('dropdown-toggle');
3785 this.el.on('touchstart' , this.onTouch, this);
3787 this.el.on('click' , this.onClick, this);
3789 this.el.on("mouseover", this.onMouseOver, this);
3790 this.el.on("mouseout", this.onMouseOut, this);
3794 findTargetItem : function(e)
3796 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3800 //Roo.log(t); Roo.log(t.id);
3802 //Roo.log(this.menuitems);
3803 return this.menuitems.get(t.id);
3805 //return this.items.get(t.menuItemId);
3811 onTouch : function(e)
3813 Roo.log("menu.onTouch");
3814 //e.stopEvent(); this make the user popdown broken
3818 onClick : function(e)
3820 Roo.log("menu.onClick");
3822 var t = this.findTargetItem(e);
3823 if(!t || t.isContainer){
3828 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3829 if(t == this.activeItem && t.shouldDeactivate(e)){
3830 this.activeItem.deactivate();
3831 delete this.activeItem;
3835 this.setActiveItem(t, true);
3843 Roo.log('pass click event');
3847 this.fireEvent("click", this, t, e);
3851 if(!t.href.length || t.href == '#'){
3852 (function() { _this.hide(); }).defer(100);
3857 onMouseOver : function(e){
3858 var t = this.findTargetItem(e);
3861 // if(t.canActivate && !t.disabled){
3862 // this.setActiveItem(t, true);
3866 this.fireEvent("mouseover", this, e, t);
3868 isVisible : function(){
3869 return !this.hidden;
3871 onMouseOut : function(e){
3872 var t = this.findTargetItem(e);
3875 // if(t == this.activeItem && t.shouldDeactivate(e)){
3876 // this.activeItem.deactivate();
3877 // delete this.activeItem;
3880 this.fireEvent("mouseout", this, e, t);
3885 * Displays this menu relative to another element
3886 * @param {String/HTMLElement/Roo.Element} element The element to align to
3887 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3888 * the element (defaults to this.defaultAlign)
3889 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3891 show : function(el, pos, parentMenu)
3893 if (false === this.fireEvent("beforeshow", this)) {
3894 Roo.log("show canceled");
3897 this.parentMenu = parentMenu;
3901 this.el.addClass('show'); // show otherwise we do not know how big we are..
3903 var xy = this.el.getAlignToXY(el, pos);
3905 // bl-tl << left align below
3906 // tl-bl << left align
3908 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3909 // if it goes to far to the right.. -> align left.
3910 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3913 // was left align - go right?
3914 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3917 // goes down the bottom
3918 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3920 var a = this.align.replace('?', '').split('-');
3921 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3925 this.showAt( xy , parentMenu, false);
3928 * Displays this menu at a specific xy position
3929 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3930 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3932 showAt : function(xy, parentMenu, /* private: */_e){
3933 this.parentMenu = parentMenu;
3938 this.fireEvent("beforeshow", this);
3939 //xy = this.el.adjustForConstraints(xy);
3943 this.hideMenuItems();
3944 this.hidden = false;
3945 if (this.triggerEl) {
3946 this.triggerEl.addClass('open');
3949 this.el.addClass('show');
3953 // reassign x when hitting right
3955 // reassign y when hitting bottom
3957 // but the list may align on trigger left or trigger top... should it be a properity?
3959 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3964 this.fireEvent("show", this);
3970 this.doFocus.defer(50, this);
3974 doFocus : function(){
3976 this.focusEl.focus();
3981 * Hides this menu and optionally all parent menus
3982 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3984 hide : function(deep)
3986 if (false === this.fireEvent("beforehide", this)) {
3987 Roo.log("hide canceled");
3990 this.hideMenuItems();
3991 if(this.el && this.isVisible()){
3993 if(this.activeItem){
3994 this.activeItem.deactivate();
3995 this.activeItem = null;
3997 if (this.triggerEl) {
3998 this.triggerEl.removeClass('open');
4001 this.el.removeClass('show');
4003 this.fireEvent("hide", this);
4005 if(deep === true && this.parentMenu){
4006 this.parentMenu.hide(true);
4010 onTriggerClick : function(e)
4012 Roo.log('trigger click');
4014 var target = e.getTarget();
4016 Roo.log(target.nodeName.toLowerCase());
4018 if(target.nodeName.toLowerCase() === 'i'){
4024 onTriggerPress : function(e)
4026 Roo.log('trigger press');
4027 //Roo.log(e.getTarget());
4028 // Roo.log(this.triggerEl.dom);
4030 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4031 var pel = Roo.get(e.getTarget());
4032 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4033 Roo.log('is treeview or dropdown?');
4037 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4041 if (this.isVisible()) {
4047 this.show(this.triggerEl, this.align, false);
4050 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4057 hideMenuItems : function()
4059 Roo.log("hide Menu Items");
4064 this.el.select('.open',true).each(function(aa) {
4066 aa.removeClass('open');
4070 addxtypeChild : function (tree, cntr) {
4071 var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4073 this.menuitems.add(comp);
4085 this.getEl().dom.innerHTML = '';
4086 this.menuitems.clear();
4092 * @class Roo.bootstrap.menu.Item
4093 * @extends Roo.bootstrap.Component
4094 * @children Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4095 * @parent Roo.bootstrap.menu.Menu
4097 * Bootstrap MenuItem class
4099 * @cfg {String} html the menu label
4100 * @cfg {String} href the link
4101 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4102 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4103 * @cfg {Boolean} active used on sidebars to highlight active itesm
4104 * @cfg {String} fa favicon to show on left of menu item.
4105 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4109 * Create a new MenuItem
4110 * @param {Object} config The config object
4114 Roo.bootstrap.menu.Item = function(config){
4115 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4120 * The raw click event for the entire grid.
4121 * @param {Roo.bootstrap.menu.Item} this
4122 * @param {Roo.EventObject} e
4128 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
4132 preventDefault: false,
4133 isContainer : false,
4137 getAutoCreate : function(){
4139 if(this.isContainer){
4142 cls: 'dropdown-menu-item '
4152 cls : 'dropdown-item',
4157 if (this.fa !== false) {
4160 cls : 'fa fa-' + this.fa
4169 cls: 'dropdown-menu-item',
4172 if (this.parent().type == 'treeview') {
4173 cfg.cls = 'treeview-menu';
4176 cfg.cls += ' active';
4181 anc.href = this.href || cfg.cn[0].href ;
4182 ctag.html = this.html || cfg.cn[0].html ;
4186 initEvents: function()
4188 if (this.parent().type == 'treeview') {
4189 this.el.select('a').on('click', this.onClick, this);
4193 this.menu.parentType = this.xtype;
4194 this.menu.triggerEl = this.el;
4195 this.menu = this.addxtype(Roo.apply({}, this.menu));
4199 onClick : function(e)
4201 //Roo.log('item on click ');
4203 if(this.href === false || this.preventDefault){
4206 //this.parent().hideMenuItems();
4208 this.fireEvent('click', this, e);
4222 * @class Roo.bootstrap.menu.Separator
4223 * @extends Roo.bootstrap.Component
4225 * @parent Roo.bootstrap.menu.Menu
4226 * Bootstrap Separator class
4229 * Create a new Separator
4230 * @param {Object} config The config object
4234 Roo.bootstrap.menu.Separator = function(config){
4235 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4238 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
4240 getAutoCreate : function(){
4243 cls: 'dropdown-divider divider'
4259 * @class Roo.bootstrap.Modal
4260 * @extends Roo.bootstrap.Component
4261 * @parent none builder
4262 * @children Roo.bootstrap.Component
4263 * Bootstrap Modal class
4264 * @cfg {String} title Title of dialog
4265 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4266 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4267 * @cfg {Boolean} specificTitle default false
4268 * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4269 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4270 * @cfg {Boolean} animate default true
4271 * @cfg {Boolean} allow_close default true
4272 * @cfg {Boolean} fitwindow default false
4273 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4274 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4275 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4276 * @cfg {String} size (sm|lg|xl) default empty
4277 * @cfg {Number} max_width set the max width of modal
4278 * @cfg {Boolean} editableTitle can the title be edited
4283 * Create a new Modal Dialog
4284 * @param {Object} config The config object
4287 Roo.bootstrap.Modal = function(config){
4288 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4293 * The raw btnclick event for the button
4294 * @param {Roo.EventObject} e
4299 * Fire when dialog resize
4300 * @param {Roo.bootstrap.Modal} this
4301 * @param {Roo.EventObject} e
4305 * @event titlechanged
4306 * Fire when the editable title has been changed
4307 * @param {Roo.bootstrap.Modal} this
4308 * @param {Roo.EventObject} value
4310 "titlechanged" : true
4313 this.buttons = this.buttons || [];
4316 this.tmpl = Roo.factory(this.tmpl);
4321 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4323 title : 'test dialog',
4333 specificTitle: false,
4335 buttonPosition: 'right',
4357 editableTitle : false,
4359 onRender : function(ct, position)
4361 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4364 var cfg = Roo.apply({}, this.getAutoCreate());
4367 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4369 //if (!cfg.name.length) {
4373 cfg.cls += ' ' + this.cls;
4376 cfg.style = this.style;
4378 this.el = Roo.get(document.body).createChild(cfg, position);
4380 //var type = this.el.dom.type;
4383 if(this.tabIndex !== undefined){
4384 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4387 this.dialogEl = this.el.select('.modal-dialog',true).first();
4388 this.bodyEl = this.el.select('.modal-body',true).first();
4389 this.closeEl = this.el.select('.modal-header .close', true).first();
4390 this.headerEl = this.el.select('.modal-header',true).first();
4391 this.titleEl = this.el.select('.modal-title',true).first();
4392 this.footerEl = this.el.select('.modal-footer',true).first();
4394 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4396 //this.el.addClass("x-dlg-modal");
4398 if (this.buttons.length) {
4399 Roo.each(this.buttons, function(bb) {
4400 var b = Roo.apply({}, bb);
4401 b.xns = b.xns || Roo.bootstrap;
4402 b.xtype = b.xtype || 'Button';
4403 if (typeof(b.listeners) == 'undefined') {
4404 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4407 var btn = Roo.factory(b);
4409 btn.render(this.getButtonContainer());
4413 // render the children.
4416 if(typeof(this.items) != 'undefined'){
4417 var items = this.items;
4420 for(var i =0;i < items.length;i++) {
4421 // we force children not to montor widnow resize - as we do that for them.
4422 items[i].monitorWindowResize = false;
4423 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4427 this.items = nitems;
4429 // where are these used - they used to be body/close/footer
4433 //this.el.addClass([this.fieldClass, this.cls]);
4437 getAutoCreate : function()
4439 // we will default to modal-body-overflow - might need to remove or make optional later.
4441 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4442 html : this.html || ''
4447 cls : 'modal-title',
4451 if(this.specificTitle){ // WTF is this?
4456 if (this.allow_close && Roo.bootstrap.version == 3) {
4466 if (this.editableTitle) {
4468 cls: 'form-control roo-editable-title d-none',
4474 if (this.allow_close && Roo.bootstrap.version == 4) {
4484 if(this.size.length){
4485 size = 'modal-' + this.size;
4488 var footer = Roo.bootstrap.version == 3 ?
4490 cls : 'modal-footer',
4494 cls: 'btn-' + this.buttonPosition
4499 { // BS4 uses mr-auto on left buttons....
4500 cls : 'modal-footer'
4511 cls: "modal-dialog " + size,
4514 cls : "modal-content",
4517 cls : 'modal-header',
4532 modal.cls += ' fade';
4538 getChildContainer : function() {
4543 getButtonContainer : function() {
4545 return Roo.bootstrap.version == 4 ?
4546 this.el.select('.modal-footer',true).first()
4547 : this.el.select('.modal-footer div',true).first();
4550 initEvents : function()
4552 if (this.allow_close) {
4553 this.closeEl.on('click', this.hide, this);
4555 Roo.EventManager.onWindowResize(this.resize, this, true);
4556 if (this.editableTitle) {
4557 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4558 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4559 this.headerEditEl.on('keyup', function(e) {
4560 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4561 this.toggleHeaderInput(false)
4564 this.headerEditEl.on('blur', function(e) {
4565 this.toggleHeaderInput(false)
4574 this.maskEl.setSize(
4575 Roo.lib.Dom.getViewWidth(true),
4576 Roo.lib.Dom.getViewHeight(true)
4579 if (this.fitwindow) {
4581 this.dialogEl.setStyle( { 'max-width' : '100%' });
4583 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4584 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4589 if(this.max_width !== 0) {
4591 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4594 this.setSize(w, this.height);
4598 if(this.max_height) {
4599 this.setSize(w,Math.min(
4601 Roo.lib.Dom.getViewportHeight(true) - 60
4607 if(!this.fit_content) {
4608 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4612 this.setSize(w, Math.min(
4614 this.headerEl.getHeight() +
4615 this.footerEl.getHeight() +
4616 this.getChildHeight(this.bodyEl.dom.childNodes),
4617 Roo.lib.Dom.getViewportHeight(true) - 60)
4623 setSize : function(w,h)
4630 // any layout/border etc.. resize..
4632 this.items.forEach( function(e) {
4633 e.layout ? e.layout() : false;
4642 if (!this.rendered) {
4645 this.toggleHeaderInput(false);
4646 //this.el.setStyle('display', 'block');
4647 this.el.removeClass('hideing');
4648 this.el.dom.style.display='block';
4650 Roo.get(document.body).addClass('modal-open');
4652 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4655 this.el.addClass('show');
4656 this.el.addClass('in');
4659 this.el.addClass('show');
4660 this.el.addClass('in');
4663 // not sure how we can show data in here..
4665 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4668 Roo.get(document.body).addClass("x-body-masked");
4670 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4671 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4672 this.maskEl.dom.style.display = 'block';
4673 this.maskEl.addClass('show');
4678 this.fireEvent('show', this);
4680 // set zindex here - otherwise it appears to be ignored...
4681 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4684 // this is for children that are... layout.Border
4686 this.items.forEach( function(e) {
4687 e.layout ? e.layout() : false;
4695 if(this.fireEvent("beforehide", this) !== false){
4697 this.maskEl.removeClass('show');
4699 this.maskEl.dom.style.display = '';
4700 Roo.get(document.body).removeClass("x-body-masked");
4701 this.el.removeClass('in');
4702 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4704 if(this.animate){ // why
4705 this.el.addClass('hideing');
4706 this.el.removeClass('show');
4708 if (!this.el.hasClass('hideing')) {
4709 return; // it's been shown again...
4712 this.el.dom.style.display='';
4714 Roo.get(document.body).removeClass('modal-open');
4715 this.el.removeClass('hideing');
4719 this.el.removeClass('show');
4720 this.el.dom.style.display='';
4721 Roo.get(document.body).removeClass('modal-open');
4724 this.fireEvent('hide', this);
4727 isVisible : function()
4730 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4734 addButton : function(str, cb)
4738 var b = Roo.apply({}, { html : str } );
4739 b.xns = b.xns || Roo.bootstrap;
4740 b.xtype = b.xtype || 'Button';
4741 if (typeof(b.listeners) == 'undefined') {
4742 b.listeners = { click : cb.createDelegate(this) };
4745 var btn = Roo.factory(b);
4747 btn.render(this.getButtonContainer());
4753 setDefaultButton : function(btn)
4755 //this.el.select('.modal-footer').()
4758 resizeTo: function(w,h)
4760 this.dialogEl.setWidth(w);
4762 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4764 this.bodyEl.setHeight(h - diff);
4766 this.fireEvent('resize', this);
4769 setContentSize : function(w, h)
4773 onButtonClick: function(btn,e)
4776 this.fireEvent('btnclick', btn.name, e);
4779 * Set the title of the Dialog
4780 * @param {String} str new Title
4782 setTitle: function(str) {
4783 this.titleEl.dom.innerHTML = str;
4787 * Set the body of the Dialog
4788 * @param {String} str new Title
4790 setBody: function(str) {
4791 this.bodyEl.dom.innerHTML = str;
4794 * Set the body of the Dialog using the template
4795 * @param {Obj} data - apply this data to the template and replace the body contents.
4797 applyBody: function(obj)
4800 Roo.log("Error - using apply Body without a template");
4803 this.tmpl.overwrite(this.bodyEl, obj);
4806 getChildHeight : function(child_nodes)
4810 child_nodes.length == 0
4815 var child_height = 0;
4817 for(var i = 0; i < child_nodes.length; i++) {
4820 * for modal with tabs...
4821 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4823 var layout_childs = child_nodes[i].childNodes;
4825 for(var j = 0; j < layout_childs.length; j++) {
4827 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4829 var layout_body_childs = layout_childs[j].childNodes;
4831 for(var k = 0; k < layout_body_childs.length; k++) {
4833 if(layout_body_childs[k].classList.contains('navbar')) {
4834 child_height += layout_body_childs[k].offsetHeight;
4838 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4840 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4842 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4844 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4845 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4860 child_height += child_nodes[i].offsetHeight;
4861 // Roo.log(child_nodes[i].offsetHeight);
4864 return child_height;
4866 toggleHeaderInput : function(is_edit)
4868 if (!this.editableTitle) {
4869 return; // not editable.
4871 if (is_edit && this.is_header_editing) {
4872 return; // already editing..
4876 this.headerEditEl.dom.value = this.title;
4877 this.headerEditEl.removeClass('d-none');
4878 this.headerEditEl.dom.focus();
4879 this.titleEl.addClass('d-none');
4881 this.is_header_editing = true;
4884 // flip back to not editing.
4885 this.title = this.headerEditEl.dom.value;
4886 this.headerEditEl.addClass('d-none');
4887 this.titleEl.removeClass('d-none');
4888 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4889 this.is_header_editing = false;
4890 this.fireEvent('titlechanged', this, this.title);
4899 Roo.apply(Roo.bootstrap.Modal, {
4901 * Button config that displays a single OK button
4910 * Button config that displays Yes and No buttons
4926 * Button config that displays OK and Cancel buttons
4941 * Button config that displays Yes, No and Cancel buttons
4966 * messagebox - can be used as a replace
4970 * @class Roo.MessageBox
4971 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4975 Roo.Msg.alert('Status', 'Changes saved successfully.');
4977 // Prompt for user data:
4978 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4980 // process text value...
4984 // Show a dialog using config options:
4986 title:'Save Changes?',
4987 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4988 buttons: Roo.Msg.YESNOCANCEL,
4995 Roo.bootstrap.MessageBox = function(){
4996 var dlg, opt, mask, waitTimer;
4997 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4998 var buttons, activeTextEl, bwidth;
5002 var handleButton = function(button){
5004 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5008 var handleHide = function(){
5010 dlg.el.removeClass(opt.cls);
5013 // Roo.TaskMgr.stop(waitTimer);
5014 // waitTimer = null;
5019 var updateButtons = function(b){
5022 buttons["ok"].hide();
5023 buttons["cancel"].hide();
5024 buttons["yes"].hide();
5025 buttons["no"].hide();
5026 dlg.footerEl.hide();
5030 dlg.footerEl.show();
5031 for(var k in buttons){
5032 if(typeof buttons[k] != "function"){
5035 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5036 width += buttons[k].el.getWidth()+15;
5046 var handleEsc = function(d, k, e){
5047 if(opt && opt.closable !== false){
5057 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5058 * @return {Roo.BasicDialog} The BasicDialog element
5060 getDialog : function(){
5062 dlg = new Roo.bootstrap.Modal( {
5065 //constraintoviewport:false,
5067 //collapsible : false,
5072 //buttonAlign:"center",
5073 closeClick : function(){
5074 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5077 handleButton("cancel");
5082 dlg.on("hide", handleHide);
5084 //dlg.addKeyListener(27, handleEsc);
5086 this.buttons = buttons;
5087 var bt = this.buttonText;
5088 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5089 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5090 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5091 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5093 bodyEl = dlg.bodyEl.createChild({
5095 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5096 '<textarea class="roo-mb-textarea"></textarea>' +
5097 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5099 msgEl = bodyEl.dom.firstChild;
5100 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5101 textboxEl.enableDisplayMode();
5102 textboxEl.addKeyListener([10,13], function(){
5103 if(dlg.isVisible() && opt && opt.buttons){
5106 }else if(opt.buttons.yes){
5107 handleButton("yes");
5111 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5112 textareaEl.enableDisplayMode();
5113 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5114 progressEl.enableDisplayMode();
5116 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5117 var pf = progressEl.dom.firstChild;
5119 pp = Roo.get(pf.firstChild);
5120 pp.setHeight(pf.offsetHeight);
5128 * Updates the message box body text
5129 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5130 * the XHTML-compliant non-breaking space character '&#160;')
5131 * @return {Roo.MessageBox} This message box
5133 updateText : function(text)
5135 if(!dlg.isVisible() && !opt.width){
5136 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5137 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5139 msgEl.innerHTML = text || ' ';
5141 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5142 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5144 Math.min(opt.width || cw , this.maxWidth),
5145 Math.max(opt.minWidth || this.minWidth, bwidth)
5148 activeTextEl.setWidth(w);
5150 if(dlg.isVisible()){
5151 dlg.fixedcenter = false;
5153 // to big, make it scroll. = But as usual stupid IE does not support
5156 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5157 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5158 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5160 bodyEl.dom.style.height = '';
5161 bodyEl.dom.style.overflowY = '';
5164 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5166 bodyEl.dom.style.overflowX = '';
5169 dlg.setContentSize(w, bodyEl.getHeight());
5170 if(dlg.isVisible()){
5171 dlg.fixedcenter = true;
5177 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5178 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5179 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5180 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5181 * @return {Roo.MessageBox} This message box
5183 updateProgress : function(value, text){
5185 this.updateText(text);
5188 if (pp) { // weird bug on my firefox - for some reason this is not defined
5189 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5190 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5196 * Returns true if the message box is currently displayed
5197 * @return {Boolean} True if the message box is visible, else false
5199 isVisible : function(){
5200 return dlg && dlg.isVisible();
5204 * Hides the message box if it is displayed
5207 if(this.isVisible()){
5213 * Displays a new message box, or reinitializes an existing message box, based on the config options
5214 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5215 * The following config object properties are supported:
5217 Property Type Description
5218 ---------- --------------- ------------------------------------------------------------------------------------
5219 animEl String/Element An id or Element from which the message box should animate as it opens and
5220 closes (defaults to undefined)
5221 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5222 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5223 closable Boolean False to hide the top-right close button (defaults to true). Note that
5224 progress and wait dialogs will ignore this property and always hide the
5225 close button as they can only be closed programmatically.
5226 cls String A custom CSS class to apply to the message box element
5227 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5228 displayed (defaults to 75)
5229 fn Function A callback function to execute after closing the dialog. The arguments to the
5230 function will be btn (the name of the button that was clicked, if applicable,
5231 e.g. "ok"), and text (the value of the active text field, if applicable).
5232 Progress and wait dialogs will ignore this option since they do not respond to
5233 user actions and can only be closed programmatically, so any required function
5234 should be called by the same code after it closes the dialog.
5235 icon String A CSS class that provides a background image to be used as an icon for
5236 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5237 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5238 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5239 modal Boolean False to allow user interaction with the page while the message box is
5240 displayed (defaults to true)
5241 msg String A string that will replace the existing message box body text (defaults
5242 to the XHTML-compliant non-breaking space character ' ')
5243 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5244 progress Boolean True to display a progress bar (defaults to false)
5245 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5246 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5247 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5248 title String The title text
5249 value String The string value to set into the active textbox element if displayed
5250 wait Boolean True to display a progress bar (defaults to false)
5251 width Number The width of the dialog in pixels
5258 msg: 'Please enter your address:',
5260 buttons: Roo.MessageBox.OKCANCEL,
5263 animEl: 'addAddressBtn'
5266 * @param {Object} config Configuration options
5267 * @return {Roo.MessageBox} This message box
5269 show : function(options)
5272 // this causes nightmares if you show one dialog after another
5273 // especially on callbacks..
5275 if(this.isVisible()){
5278 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5279 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5280 Roo.log("New Dialog Message:" + options.msg )
5281 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5282 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5285 var d = this.getDialog();
5287 d.setTitle(opt.title || " ");
5288 d.closeEl.setDisplayed(opt.closable !== false);
5289 activeTextEl = textboxEl;
5290 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5295 textareaEl.setHeight(typeof opt.multiline == "number" ?
5296 opt.multiline : this.defaultTextHeight);
5297 activeTextEl = textareaEl;
5306 progressEl.setDisplayed(opt.progress === true);
5308 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5310 this.updateProgress(0);
5311 activeTextEl.dom.value = opt.value || "";
5313 dlg.setDefaultButton(activeTextEl);
5315 var bs = opt.buttons;
5319 }else if(bs && bs.yes){
5320 db = buttons["yes"];
5322 dlg.setDefaultButton(db);
5324 bwidth = updateButtons(opt.buttons);
5325 this.updateText(opt.msg);
5327 d.el.addClass(opt.cls);
5329 d.proxyDrag = opt.proxyDrag === true;
5330 d.modal = opt.modal !== false;
5331 d.mask = opt.modal !== false ? mask : false;
5333 // force it to the end of the z-index stack so it gets a cursor in FF
5334 document.body.appendChild(dlg.el.dom);
5335 d.animateTarget = null;
5336 d.show(options.animEl);
5342 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5343 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5344 * and closing the message box when the process is complete.
5345 * @param {String} title The title bar text
5346 * @param {String} msg The message box body text
5347 * @return {Roo.MessageBox} This message box
5349 progress : function(title, msg){
5356 minWidth: this.minProgressWidth,
5363 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5364 * If a callback function is passed it will be called after the user clicks the button, and the
5365 * id of the button that was clicked will be passed as the only parameter to the callback
5366 * (could also be the top-right close button).
5367 * @param {String} title The title bar text
5368 * @param {String} msg The message box body text
5369 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5370 * @param {Object} scope (optional) The scope of the callback function
5371 * @return {Roo.MessageBox} This message box
5373 alert : function(title, msg, fn, scope)
5388 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5389 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5390 * You are responsible for closing the message box when the process is complete.
5391 * @param {String} msg The message box body text
5392 * @param {String} title (optional) The title bar text
5393 * @return {Roo.MessageBox} This message box
5395 wait : function(msg, title){
5406 waitTimer = Roo.TaskMgr.start({
5408 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5416 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5417 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5418 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5419 * @param {String} title The title bar text
5420 * @param {String} msg The message box body text
5421 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5422 * @param {Object} scope (optional) The scope of the callback function
5423 * @return {Roo.MessageBox} This message box
5425 confirm : function(title, msg, fn, scope){
5429 buttons: this.YESNO,
5438 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5439 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5440 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5441 * (could also be the top-right close button) and the text that was entered will be passed as the two
5442 * parameters to the callback.
5443 * @param {String} title The title bar text
5444 * @param {String} msg The message box body text
5445 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5446 * @param {Object} scope (optional) The scope of the callback function
5447 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5448 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5449 * @return {Roo.MessageBox} This message box
5451 prompt : function(title, msg, fn, scope, multiline){
5455 buttons: this.OKCANCEL,
5460 multiline: multiline,
5467 * Button config that displays a single OK button
5472 * Button config that displays Yes and No buttons
5475 YESNO : {yes:true, no:true},
5477 * Button config that displays OK and Cancel buttons
5480 OKCANCEL : {ok:true, cancel:true},
5482 * Button config that displays Yes, No and Cancel buttons
5485 YESNOCANCEL : {yes:true, no:true, cancel:true},
5488 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5491 defaultTextHeight : 75,
5493 * The maximum width in pixels of the message box (defaults to 600)
5498 * The minimum width in pixels of the message box (defaults to 100)
5503 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5504 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5507 minProgressWidth : 250,
5509 * An object containing the default button text strings that can be overriden for localized language support.
5510 * Supported properties are: ok, cancel, yes and no.
5511 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5524 * Shorthand for {@link Roo.MessageBox}
5526 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5527 Roo.Msg = Roo.Msg || Roo.MessageBox;
5536 * @class Roo.bootstrap.nav.Bar
5537 * @extends Roo.bootstrap.Component
5539 * Bootstrap Navbar class
5542 * Create a new Navbar
5543 * @param {Object} config The config object
5547 Roo.bootstrap.nav.Bar = function(config){
5548 Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5552 * @event beforetoggle
5553 * Fire before toggle the menu
5554 * @param {Roo.EventObject} e
5556 "beforetoggle" : true
5560 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component, {
5569 getAutoCreate : function(){
5572 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5576 initEvents :function ()
5578 //Roo.log(this.el.select('.navbar-toggle',true));
5579 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5586 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5588 var size = this.el.getSize();
5589 this.maskEl.setSize(size.width, size.height);
5590 this.maskEl.enableDisplayMode("block");
5599 getChildContainer : function()
5601 if (this.el && this.el.select('.collapse').getCount()) {
5602 return this.el.select('.collapse',true).first();
5617 onToggle : function()
5620 if(this.fireEvent('beforetoggle', this) === false){
5623 var ce = this.el.select('.navbar-collapse',true).first();
5625 if (!ce.hasClass('show')) {
5635 * Expand the navbar pulldown
5637 expand : function ()
5640 var ce = this.el.select('.navbar-collapse',true).first();
5641 if (ce.hasClass('collapsing')) {
5644 ce.dom.style.height = '';
5646 ce.addClass('in'); // old...
5647 ce.removeClass('collapse');
5648 ce.addClass('show');
5649 var h = ce.getHeight();
5651 ce.removeClass('show');
5652 // at this point we should be able to see it..
5653 ce.addClass('collapsing');
5655 ce.setHeight(0); // resize it ...
5656 ce.on('transitionend', function() {
5657 //Roo.log('done transition');
5658 ce.removeClass('collapsing');
5659 ce.addClass('show');
5660 ce.removeClass('collapse');
5662 ce.dom.style.height = '';
5663 }, this, { single: true} );
5665 ce.dom.scrollTop = 0;
5668 * Collapse the navbar pulldown
5670 collapse : function()
5672 var ce = this.el.select('.navbar-collapse',true).first();
5674 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5675 // it's collapsed or collapsing..
5678 ce.removeClass('in'); // old...
5679 ce.setHeight(ce.getHeight());
5680 ce.removeClass('show');
5681 ce.addClass('collapsing');
5683 ce.on('transitionend', function() {
5684 ce.dom.style.height = '';
5685 ce.removeClass('collapsing');
5686 ce.addClass('collapse');
5687 }, this, { single: true} );
5707 * @class Roo.bootstrap.nav.Simplebar
5708 * @extends Roo.bootstrap.nav.Bar
5709 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5710 * Bootstrap Sidebar class
5712 * @cfg {Boolean} inverse is inverted color
5714 * @cfg {String} type (nav | pills | tabs)
5715 * @cfg {Boolean} arrangement stacked | justified
5716 * @cfg {String} align (left | right) alignment
5718 * @cfg {Boolean} main (true|false) main nav bar? default false
5719 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5721 * @cfg {String} tag (header|footer|nav|div) default is nav
5723 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5727 * Create a new Sidebar
5728 * @param {Object} config The config object
5732 Roo.bootstrap.nav.Simplebar = function(config){
5733 Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5736 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar, {
5752 getAutoCreate : function(){
5756 tag : this.tag || 'div',
5757 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5759 if (['light','white'].indexOf(this.weight) > -1) {
5760 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5762 cfg.cls += ' bg-' + this.weight;
5765 cfg.cls += ' navbar-inverse';
5769 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5771 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5780 cls: 'nav nav-' + this.xtype,
5786 this.type = this.type || 'nav';
5787 if (['tabs','pills'].indexOf(this.type) != -1) {
5788 cfg.cn[0].cls += ' nav-' + this.type
5792 if (this.type!=='nav') {
5793 Roo.log('nav type must be nav/tabs/pills')
5795 cfg.cn[0].cls += ' navbar-nav'
5801 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5802 cfg.cn[0].cls += ' nav-' + this.arrangement;
5806 if (this.align === 'right') {
5807 cfg.cn[0].cls += ' navbar-right';
5832 * navbar-expand-md fixed-top
5836 * @class Roo.bootstrap.nav.Headerbar
5837 * @extends Roo.bootstrap.nav.Simplebar
5838 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5839 * Bootstrap Sidebar class
5841 * @cfg {String} brand what is brand
5842 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5843 * @cfg {String} brand_href href of the brand
5844 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5845 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5846 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5847 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5850 * Create a new Sidebar
5851 * @param {Object} config The config object
5855 Roo.bootstrap.nav.Headerbar = function(config){
5856 Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5860 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar, {
5867 desktopCenter : false,
5870 getAutoCreate : function(){
5873 tag: this.nav || 'nav',
5874 cls: 'navbar navbar-expand-md',
5880 if (this.desktopCenter) {
5881 cn.push({cls : 'container', cn : []});
5889 cls: 'navbar-toggle navbar-toggler',
5890 'data-toggle': 'collapse',
5895 html: 'Toggle navigation'
5899 cls: 'icon-bar navbar-toggler-icon'
5912 cn.push( Roo.bootstrap.version == 4 ? btn : {
5914 cls: 'navbar-header',
5923 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5927 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5929 if (['light','white'].indexOf(this.weight) > -1) {
5930 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5932 cfg.cls += ' bg-' + this.weight;
5935 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5936 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5938 // tag can override this..
5940 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5943 if (this.brand !== '') {
5944 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5945 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5947 href: this.brand_href ? this.brand_href : '#',
5948 cls: 'navbar-brand',
5956 cfg.cls += ' main-nav';
5964 getHeaderChildContainer : function()
5966 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5967 return this.el.select('.navbar-header',true).first();
5970 return this.getChildContainer();
5973 getChildContainer : function()
5976 return this.el.select('.roo-navbar-collapse',true).first();
5981 initEvents : function()
5983 Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5985 if (this.autohide) {
5990 Roo.get(document).on('scroll',function(e) {
5991 var ns = Roo.get(document).getScroll().top;
5992 var os = prevScroll;
5996 ft.removeClass('slideDown');
5997 ft.addClass('slideUp');
6000 ft.removeClass('slideUp');
6001 ft.addClass('slideDown');
6022 * @class Roo.bootstrap.nav.Sidebar
6023 * @extends Roo.bootstrap.nav.Bar
6024 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6025 * Bootstrap Sidebar class
6028 * Create a new Sidebar
6029 * @param {Object} config The config object
6033 Roo.bootstrap.nav.Sidebar = function(config){
6034 Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6037 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar, {
6039 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6041 getAutoCreate : function(){
6046 cls: 'sidebar sidebar-nav'
6068 * @class Roo.bootstrap.nav.Group
6069 * @extends Roo.bootstrap.Component
6070 * @children Roo.bootstrap.nav.Item
6071 * Bootstrap NavGroup class
6072 * @cfg {String} align (left|right)
6073 * @cfg {Boolean} inverse
6074 * @cfg {String} type (nav|pills|tab) default nav
6075 * @cfg {String} navId - reference Id for navbar.
6076 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6079 * Create a new nav group
6080 * @param {Object} config The config object
6083 Roo.bootstrap.nav.Group = function(config){
6084 Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6087 Roo.bootstrap.nav.Group.register(this);
6091 * Fires when the active item changes
6092 * @param {Roo.bootstrap.nav.Group} this
6093 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6094 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6101 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component, {
6113 getAutoCreate : function()
6115 var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6121 if (Roo.bootstrap.version == 4) {
6122 if (['tabs','pills'].indexOf(this.type) != -1) {
6123 cfg.cls += ' nav-' + this.type;
6125 // trying to remove so header bar can right align top?
6126 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6127 // do not use on header bar...
6128 cfg.cls += ' navbar-nav';
6133 if (['tabs','pills'].indexOf(this.type) != -1) {
6134 cfg.cls += ' nav-' + this.type
6136 if (this.type !== 'nav') {
6137 Roo.log('nav type must be nav/tabs/pills')
6139 cfg.cls += ' navbar-nav'
6143 if (this.parent() && this.parent().sidebar) {
6146 cls: 'dashboard-menu sidebar-menu'
6152 if (this.form === true) {
6155 cls: 'navbar-form form-inline'
6157 //nav navbar-right ml-md-auto
6158 if (this.align === 'right') {
6159 cfg.cls += ' navbar-right ml-md-auto';
6161 cfg.cls += ' navbar-left';
6165 if (this.align === 'right') {
6166 cfg.cls += ' navbar-right ml-md-auto';
6168 cfg.cls += ' mr-auto';
6172 cfg.cls += ' navbar-inverse';
6180 * sets the active Navigation item
6181 * @param {Roo.bootstrap.nav.Item} the new current navitem
6183 setActiveItem : function(item)
6186 Roo.each(this.navItems, function(v){
6191 v.setActive(false, true);
6198 item.setActive(true, true);
6199 this.fireEvent('changed', this, item, prev);
6204 * gets the active Navigation item
6205 * @return {Roo.bootstrap.nav.Item} the current navitem
6207 getActive : function()
6211 Roo.each(this.navItems, function(v){
6222 indexOfNav : function()
6226 Roo.each(this.navItems, function(v,i){
6237 * adds a Navigation item
6238 * @param {Roo.bootstrap.nav.Item} the navitem to add
6240 addItem : function(cfg)
6242 if (this.form && Roo.bootstrap.version == 4) {
6245 var cn = new Roo.bootstrap.nav.Item(cfg);
6247 cn.parentId = this.id;
6248 cn.onRender(this.el, null);
6252 * register a Navigation item
6253 * @param {Roo.bootstrap.nav.Item} the navitem to add
6255 register : function(item)
6257 this.navItems.push( item);
6258 item.navId = this.navId;
6263 * clear all the Navigation item
6266 clearAll : function()
6269 this.el.dom.innerHTML = '';
6272 getNavItem: function(tabId)
6275 Roo.each(this.navItems, function(e) {
6276 if (e.tabId == tabId) {
6286 setActiveNext : function()
6288 var i = this.indexOfNav(this.getActive());
6289 if (i > this.navItems.length) {
6292 this.setActiveItem(this.navItems[i+1]);
6294 setActivePrev : function()
6296 var i = this.indexOfNav(this.getActive());
6300 this.setActiveItem(this.navItems[i-1]);
6302 clearWasActive : function(except) {
6303 Roo.each(this.navItems, function(e) {
6304 if (e.tabId != except.tabId && e.was_active) {
6305 e.was_active = false;
6312 getWasActive : function ()
6315 Roo.each(this.navItems, function(e) {
6330 Roo.apply(Roo.bootstrap.nav.Group, {
6334 * register a Navigation Group
6335 * @param {Roo.bootstrap.nav.Group} the navgroup to add
6337 register : function(navgrp)
6339 this.groups[navgrp.navId] = navgrp;
6343 * fetch a Navigation Group based on the navigation ID
6344 * @param {string} the navgroup to add
6345 * @returns {Roo.bootstrap.nav.Group} the navgroup
6347 get: function(navId) {
6348 if (typeof(this.groups[navId]) == 'undefined') {
6350 //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6352 return this.groups[navId] ;
6360 * @class Roo.bootstrap.nav.Item
6361 * @extends Roo.bootstrap.Component
6362 * @children Roo.bootstrap.Container Roo.bootstrap.Button
6363 * @parent Roo.bootstrap.nav.Group
6365 * Bootstrap Navbar.NavItem class
6367 * @cfg {String} href link to
6368 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6369 * @cfg {Boolean} button_outline show and outlined button
6370 * @cfg {String} html content of button
6371 * @cfg {String} badge text inside badge
6372 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6373 * @cfg {String} glyphicon DEPRICATED - use fa
6374 * @cfg {String} icon DEPRICATED - use fa
6375 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6376 * @cfg {Boolean} active Is item active
6377 * @cfg {Boolean} disabled Is item disabled
6378 * @cfg {String} linkcls Link Class
6379 * @cfg {Boolean} preventDefault (true | false) default false
6380 * @cfg {String} tabId the tab that this item activates.
6381 * @cfg {String} tagtype (a|span) render as a href or span?
6382 * @cfg {Boolean} animateRef (true|false) link to element default false
6383 * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
6386 * Create a new Navbar Item
6387 * @param {Object} config The config object
6389 Roo.bootstrap.nav.Item = function(config){
6390 Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6395 * The raw click event for the entire grid.
6396 * @param {Roo.EventObject} e
6401 * Fires when the active item active state changes
6402 * @param {Roo.bootstrap.nav.Item} this
6403 * @param {boolean} state the new state
6409 * Fires when scroll to element
6410 * @param {Roo.bootstrap.nav.Item} this
6411 * @param {Object} options
6412 * @param {Roo.EventObject} e
6420 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component, {
6429 preventDefault : false,
6437 button_outline : false,
6441 getAutoCreate : function(){
6448 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6451 cfg.cls += ' active' ;
6453 if (this.disabled) {
6454 cfg.cls += ' disabled';
6458 if (this.button_weight.length) {
6459 cfg.tag = this.href ? 'a' : 'button';
6460 cfg.html = this.html || '';
6461 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6463 cfg.href = this.href;
6466 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6468 cfg.cls += " nav-html";
6471 // menu .. should add dropdown-menu class - so no need for carat..
6473 if (this.badge !== '') {
6475 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6480 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6484 href : this.href || "#",
6485 html: this.html || '',
6489 if (this.tagtype == 'a') {
6490 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6494 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6495 } else if (this.fa) {
6496 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6497 } else if(this.glyphicon) {
6498 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6500 cfg.cn[0].cls += " nav-html";
6504 cfg.cn[0].html += " <span class='caret'></span>";
6508 if (this.badge !== '') {
6509 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6517 onRender : function(ct, position)
6519 // Roo.log("Call onRender: " + this.xtype);
6520 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6524 var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6525 this.navLink = this.el.select('.nav-link',true).first();
6526 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6531 initEvents: function()
6533 if (typeof (this.menu) != 'undefined') {
6534 this.menu.parentType = this.xtype;
6535 this.menu.triggerEl = this.el;
6536 this.menu = this.addxtype(Roo.apply({}, this.menu));
6539 this.el.on('click', this.onClick, this);
6541 //if(this.tagtype == 'span'){
6542 // this.el.select('span',true).on('click', this.onClick, this);
6545 // at this point parent should be available..
6546 this.parent().register(this);
6549 onClick : function(e)
6551 if (e.getTarget('.dropdown-menu-item')) {
6552 // did you click on a menu itemm.... - then don't trigger onclick..
6557 this.preventDefault ||
6558 this.href === false ||
6561 //Roo.log("NavItem - prevent Default?");
6565 if (this.disabled) {
6569 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6570 if (tg && tg.transition) {
6571 Roo.log("waiting for the transitionend");
6577 //Roo.log("fire event clicked");
6578 if(this.fireEvent('click', this, e) === false){
6582 if(this.tagtype == 'span'){
6586 //Roo.log(this.href);
6587 var ael = this.el.select('a',true).first();
6590 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6591 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6592 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6593 return; // ignore... - it's a 'hash' to another page.
6595 Roo.log("NavItem - prevent Default?");
6597 this.scrollToElement(e);
6601 var p = this.parent();
6603 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6604 if (typeof(p.setActiveItem) !== 'undefined') {
6605 p.setActiveItem(this);
6609 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6610 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6611 // remove the collapsed menu expand...
6612 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6616 isActive: function () {
6619 setActive : function(state, fire, is_was_active)
6621 if (this.active && !state && this.navId) {
6622 this.was_active = true;
6623 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6625 nv.clearWasActive(this);
6629 this.active = state;
6632 this.el.removeClass('active');
6633 this.navLink ? this.navLink.removeClass('active') : false;
6634 } else if (!this.el.hasClass('active')) {
6636 this.el.addClass('active');
6637 if (Roo.bootstrap.version == 4 && this.navLink ) {
6638 this.navLink.addClass('active');
6643 this.fireEvent('changed', this, state);
6646 // show a panel if it's registered and related..
6648 if (!this.navId || !this.tabId || !state || is_was_active) {
6652 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6656 var pan = tg.getPanelByName(this.tabId);
6660 // if we can not flip to new panel - go back to old nav highlight..
6661 if (false == tg.showPanel(pan)) {
6662 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6664 var onav = nv.getWasActive();
6666 onav.setActive(true, false, true);
6675 // this should not be here...
6676 setDisabled : function(state)
6678 this.disabled = state;
6680 this.el.removeClass('disabled');
6681 } else if (!this.el.hasClass('disabled')) {
6682 this.el.addClass('disabled');
6688 * Fetch the element to display the tooltip on.
6689 * @return {Roo.Element} defaults to this.el
6691 tooltipEl : function()
6693 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6696 scrollToElement : function(e)
6698 var c = document.body;
6701 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6703 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6704 c = document.documentElement;
6707 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6713 var o = target.calcOffsetsTo(c);
6720 this.fireEvent('scrollto', this, options, e);
6722 Roo.get(c).scrollTo('top', options.value, true);
6727 * Set the HTML (text content) of the item
6728 * @param {string} html content for the nav item
6730 setHtml : function(html)
6733 this.htmlEl.dom.innerHTML = html;
6745 * <span> icon </span>
6746 * <span> text </span>
6747 * <span>badge </span>
6751 * @class Roo.bootstrap.nav.SidebarItem
6752 * @extends Roo.bootstrap.nav.Item
6753 * Bootstrap Navbar.NavSidebarItem class
6755 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6756 * {Boolean} open is the menu open
6757 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6758 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6759 * {String} buttonSize (sm|md|lg)the extra classes for the button
6760 * {Boolean} showArrow show arrow next to the text (default true)
6762 * Create a new Navbar Button
6763 * @param {Object} config The config object
6765 Roo.bootstrap.nav.SidebarItem = function(config){
6766 Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6771 * The raw click event for the entire grid.
6772 * @param {Roo.EventObject} e
6777 * Fires when the active item active state changes
6778 * @param {Roo.bootstrap.nav.SidebarItem} this
6779 * @param {boolean} state the new state
6787 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item, {
6789 badgeWeight : 'default',
6795 buttonWeight : 'default',
6801 getAutoCreate : function(){
6806 href : this.href || '#',
6812 if(this.buttonView){
6815 href : this.href || '#',
6816 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6829 cfg.cls += ' active';
6832 if (this.disabled) {
6833 cfg.cls += ' disabled';
6836 cfg.cls += ' open x-open';
6839 if (this.glyphicon || this.icon) {
6840 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6841 a.cn.push({ tag : 'i', cls : c }) ;
6844 if(!this.buttonView){
6847 html : this.html || ''
6854 if (this.badge !== '') {
6855 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6861 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6864 a.cls += ' dropdown-toggle treeview' ;
6870 initEvents : function()
6872 if (typeof (this.menu) != 'undefined') {
6873 this.menu.parentType = this.xtype;
6874 this.menu.triggerEl = this.el;
6875 this.menu = this.addxtype(Roo.apply({}, this.menu));
6878 this.el.on('click', this.onClick, this);
6880 if(this.badge !== ''){
6881 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6886 onClick : function(e)
6893 if(this.preventDefault){
6897 this.fireEvent('click', this, e);
6900 disable : function()
6902 this.setDisabled(true);
6907 this.setDisabled(false);
6910 setDisabled : function(state)
6912 if(this.disabled == state){
6916 this.disabled = state;
6919 this.el.addClass('disabled');
6923 this.el.removeClass('disabled');
6928 setActive : function(state)
6930 if(this.active == state){
6934 this.active = state;
6937 this.el.addClass('active');
6941 this.el.removeClass('active');
6946 isActive: function ()
6951 setBadge : function(str)
6957 this.badgeEl.dom.innerHTML = str;
6974 * @class Roo.bootstrap.nav.ProgressBar
6975 * @extends Roo.bootstrap.Component
6976 * @children Roo.bootstrap.nav.ProgressBarItem
6977 * Bootstrap NavProgressBar class
6980 * Create a new nav progress bar - a bar indicating step along a process
6981 * @param {Object} config The config object
6984 Roo.bootstrap.nav.ProgressBar = function(config){
6985 Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6987 this.bullets = this.bullets || [];
6989 // Roo.bootstrap.nav.ProgressBar.register(this);
6993 * Fires when the active item changes
6994 * @param {Roo.bootstrap.nav.ProgressBar} this
6995 * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6996 * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item
7003 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component, {
7005 * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
7006 * Bullets for the Nav Progress bar for the toolbar
7011 getAutoCreate : function()
7013 var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7017 cls : 'roo-navigation-bar-group',
7021 cls : 'roo-navigation-top-bar'
7025 cls : 'roo-navigation-bullets-bar',
7029 cls : 'roo-navigation-bar'
7036 cls : 'roo-navigation-bottom-bar'
7046 initEvents: function()
7051 onRender : function(ct, position)
7053 Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7055 if(this.bullets.length){
7056 Roo.each(this.bullets, function(b){
7065 addItem : function(cfg)
7067 var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7069 item.parentId = this.id;
7070 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7073 var top = new Roo.bootstrap.Element({
7075 cls : 'roo-navigation-bar-text'
7078 var bottom = new Roo.bootstrap.Element({
7080 cls : 'roo-navigation-bar-text'
7083 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7084 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7086 var topText = new Roo.bootstrap.Element({
7088 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7091 var bottomText = new Roo.bootstrap.Element({
7093 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7096 topText.onRender(top.el, null);
7097 bottomText.onRender(bottom.el, null);
7100 item.bottomEl = bottom;
7103 this.barItems.push(item);
7108 getActive : function()
7112 Roo.each(this.barItems, function(v){
7114 if (!v.isActive()) {
7126 setActiveItem : function(item)
7130 Roo.each(this.barItems, function(v){
7131 if (v.rid == item.rid) {
7141 item.setActive(true);
7143 this.fireEvent('changed', this, item, prev);
7146 getBarItem: function(rid)
7150 Roo.each(this.barItems, function(e) {
7162 indexOfItem : function(item)
7166 Roo.each(this.barItems, function(v, i){
7168 if (v.rid != item.rid) {
7179 setActiveNext : function()
7181 var i = this.indexOfItem(this.getActive());
7183 if (i > this.barItems.length) {
7187 this.setActiveItem(this.barItems[i+1]);
7190 setActivePrev : function()
7192 var i = this.indexOfItem(this.getActive());
7198 this.setActiveItem(this.barItems[i-1]);
7203 if(!this.barItems.length){
7207 var width = 100 / this.barItems.length;
7209 Roo.each(this.barItems, function(i){
7210 i.el.setStyle('width', width + '%');
7211 i.topEl.el.setStyle('width', width + '%');
7212 i.bottomEl.el.setStyle('width', width + '%');
7226 * @class Roo.bootstrap.nav.ProgressBarItem
7227 * @extends Roo.bootstrap.Component
7228 * Bootstrap NavProgressBarItem class
7229 * @cfg {String} rid the reference id
7230 * @cfg {Boolean} active (true|false) Is item active default false
7231 * @cfg {Boolean} disabled (true|false) Is item active default false
7232 * @cfg {String} html
7233 * @cfg {String} position (top|bottom) text position default bottom
7234 * @cfg {String} icon show icon instead of number
7237 * Create a new NavProgressBarItem
7238 * @param {Object} config The config object
7240 Roo.bootstrap.nav.ProgressBarItem = function(config){
7241 Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7246 * The raw click event for the entire grid.
7247 * @param {Roo.bootstrap.nav.ProgressBarItem} this
7248 * @param {Roo.EventObject} e
7255 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component, {
7261 position : 'bottom',
7264 getAutoCreate : function()
7266 var iconCls = 'roo-navigation-bar-item-icon';
7268 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7272 cls: 'roo-navigation-bar-item',
7282 cfg.cls += ' active';
7285 cfg.cls += ' disabled';
7291 disable : function()
7293 this.setDisabled(true);
7298 this.setDisabled(false);
7301 initEvents: function()
7303 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7305 this.iconEl.on('click', this.onClick, this);
7308 onClick : function(e)
7316 if(this.fireEvent('click', this, e) === false){
7320 this.parent().setActiveItem(this);
7323 isActive: function ()
7328 setActive : function(state)
7330 if(this.active == state){
7334 this.active = state;
7337 this.el.addClass('active');
7341 this.el.removeClass('active');
7346 setDisabled : function(state)
7348 if(this.disabled == state){
7352 this.disabled = state;
7355 this.el.addClass('disabled');
7359 this.el.removeClass('disabled');
7362 tooltipEl : function()
7364 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7375 Roo.namespace('Roo.bootstrap.breadcrumb');
7379 * @class Roo.bootstrap.breadcrumb.Nav
7380 * @extends Roo.bootstrap.Component
7381 * Bootstrap Breadcrumb Nav Class
7383 * @children Roo.bootstrap.breadcrumb.Item
7386 * Create a new breadcrumb.Nav
7387 * @param {Object} config The config object
7391 Roo.bootstrap.breadcrumb.Nav = function(config){
7392 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7397 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
7399 getAutoCreate : function()
7416 initEvents: function()
7418 this.olEl = this.el.select('ol',true).first();
7420 getChildContainer : function()
7436 * @class Roo.bootstrap.breadcrumb.Nav
7437 * @extends Roo.bootstrap.Component
7438 * @children Roo.bootstrap.Component
7439 * @parent Roo.bootstrap.breadcrumb.Nav
7440 * Bootstrap Breadcrumb Nav Class
7443 * @cfg {String} html the content of the link.
7444 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7445 * @cfg {Boolean} active is it active
7449 * Create a new breadcrumb.Nav
7450 * @param {Object} config The config object
7453 Roo.bootstrap.breadcrumb.Item = function(config){
7454 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7459 * The img click event for the img.
7460 * @param {Roo.EventObject} e
7467 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7472 getAutoCreate : function()
7477 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7479 if (this.href !== false) {
7486 cfg.html = this.html;
7492 initEvents: function()
7495 this.el.select('a', true).first().on('click',this.onClick, this)
7499 onClick : function(e)
7502 this.fireEvent('click',this, e);
7515 * @class Roo.bootstrap.Row
7516 * @extends Roo.bootstrap.Component
7517 * @children Roo.bootstrap.Component
7518 * Bootstrap Row class (contains columns...)
7522 * @param {Object} config The config object
7525 Roo.bootstrap.Row = function(config){
7526 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7529 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7531 getAutoCreate : function(){
7550 * @class Roo.bootstrap.Pagination
7551 * @extends Roo.bootstrap.Component
7552 * @children Roo.bootstrap.Pagination
7553 * Bootstrap Pagination class
7555 * @cfg {String} size (xs|sm|md|lg|xl)
7556 * @cfg {Boolean} inverse
7559 * Create a new Pagination
7560 * @param {Object} config The config object
7563 Roo.bootstrap.Pagination = function(config){
7564 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7567 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7573 getAutoCreate : function(){
7579 cfg.cls += ' inverse';
7585 cfg.cls += " " + this.cls;
7603 * @class Roo.bootstrap.PaginationItem
7604 * @extends Roo.bootstrap.Component
7605 * Bootstrap PaginationItem class
7606 * @cfg {String} html text
7607 * @cfg {String} href the link
7608 * @cfg {Boolean} preventDefault (true | false) default true
7609 * @cfg {Boolean} active (true | false) default false
7610 * @cfg {Boolean} disabled default false
7614 * Create a new PaginationItem
7615 * @param {Object} config The config object
7619 Roo.bootstrap.PaginationItem = function(config){
7620 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7625 * The raw click event for the entire grid.
7626 * @param {Roo.EventObject} e
7632 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7636 preventDefault: true,
7641 getAutoCreate : function(){
7647 href : this.href ? this.href : '#',
7648 html : this.html ? this.html : ''
7658 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7662 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7668 initEvents: function() {
7670 this.el.on('click', this.onClick, this);
7673 onClick : function(e)
7675 Roo.log('PaginationItem on click ');
7676 if(this.preventDefault){
7684 this.fireEvent('click', this, e);
7700 * @class Roo.bootstrap.Slider
7701 * @extends Roo.bootstrap.Component
7702 * Bootstrap Slider class
7705 * Create a new Slider
7706 * @param {Object} config The config object
7709 Roo.bootstrap.Slider = function(config){
7710 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7713 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7715 getAutoCreate : function(){
7719 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7723 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7735 * Ext JS Library 1.1.1
7736 * Copyright(c) 2006-2007, Ext JS, LLC.
7738 * Originally Released Under LGPL - original licence link has changed is not relivant.
7741 * <script type="text/javascript">
7744 * @extends Roo.dd.DDProxy
7745 * @class Roo.grid.SplitDragZone
7746 * Support for Column Header resizing
7748 * @param {Object} config
7751 // This is a support class used internally by the Grid components
7752 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7754 this.view = grid.getView();
7755 this.proxy = this.view.resizeProxy;
7756 Roo.grid.SplitDragZone.superclass.constructor.call(
7759 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7761 dragElId : Roo.id(this.proxy.dom),
7766 this.setHandleElId(Roo.id(hd));
7767 if (hd2 !== false) {
7768 this.setOuterHandleElId(Roo.id(hd2));
7771 this.scroll = false;
7773 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7774 fly: Roo.Element.fly,
7776 b4StartDrag : function(x, y){
7777 this.view.headersDisabled = true;
7778 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7779 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7781 this.proxy.setHeight(h);
7783 // for old system colWidth really stored the actual width?
7784 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7785 // which in reality did not work.. - it worked only for fixed sizes
7786 // for resizable we need to use actual sizes.
7787 var w = this.cm.getColumnWidth(this.cellIndex);
7788 if (!this.view.mainWrap) {
7790 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7795 // this was w-this.grid.minColumnWidth;
7796 // doesnt really make sense? - w = thie curren width or the rendered one?
7797 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7798 this.resetConstraints();
7799 this.setXConstraint(minw, 1000);
7800 this.setYConstraint(0, 0);
7801 this.minX = x - minw;
7802 this.maxX = x + 1000;
7804 if (!this.view.mainWrap) { // this is Bootstrap code..
7805 this.getDragEl().style.display='block';
7808 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7812 handleMouseDown : function(e){
7813 ev = Roo.EventObject.setEvent(e);
7814 var t = this.fly(ev.getTarget());
7815 if(t.hasClass("x-grid-split")){
7816 this.cellIndex = this.view.getCellIndex(t.dom);
7818 this.cm = this.grid.colModel;
7819 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7820 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7825 endDrag : function(e){
7826 this.view.headersDisabled = false;
7827 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7828 var diff = endX - this.startPos;
7830 var w = this.cm.getColumnWidth(this.cellIndex);
7831 if (!this.view.mainWrap) {
7834 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7837 autoOffset : function(){
7842 * Ext JS Library 1.1.1
7843 * Copyright(c) 2006-2007, Ext JS, LLC.
7845 * Originally Released Under LGPL - original licence link has changed is not relivant.
7848 * <script type="text/javascript">
7852 * @class Roo.grid.AbstractSelectionModel
7853 * @extends Roo.util.Observable
7855 * Abstract base class for grid SelectionModels. It provides the interface that should be
7856 * implemented by descendant classes. This class should not be directly instantiated.
7859 Roo.grid.AbstractSelectionModel = function(){
7860 this.locked = false;
7861 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7864 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7865 /** @ignore Called by the grid automatically. Do not call directly. */
7866 init : function(grid){
7872 * Locks the selections.
7879 * Unlocks the selections.
7881 unlock : function(){
7882 this.locked = false;
7886 * Returns true if the selections are locked.
7889 isLocked : function(){
7894 * Ext JS Library 1.1.1
7895 * Copyright(c) 2006-2007, Ext JS, LLC.
7897 * Originally Released Under LGPL - original licence link has changed is not relivant.
7900 * <script type="text/javascript">
7903 * @extends Roo.grid.AbstractSelectionModel
7904 * @class Roo.grid.RowSelectionModel
7905 * The default SelectionModel used by {@link Roo.grid.Grid}.
7906 * It supports multiple selections and keyboard selection/navigation.
7908 * @param {Object} config
7910 Roo.grid.RowSelectionModel = function(config){
7911 Roo.apply(this, config);
7912 this.selections = new Roo.util.MixedCollection(false, function(o){
7917 this.lastActive = false;
7921 * @event selectionchange
7922 * Fires when the selection changes
7923 * @param {SelectionModel} this
7925 "selectionchange" : true,
7927 * @event afterselectionchange
7928 * Fires after the selection changes (eg. by key press or clicking)
7929 * @param {SelectionModel} this
7931 "afterselectionchange" : true,
7933 * @event beforerowselect
7934 * Fires when a row is selected being selected, return false to cancel.
7935 * @param {SelectionModel} this
7936 * @param {Number} rowIndex The selected index
7937 * @param {Boolean} keepExisting False if other selections will be cleared
7939 "beforerowselect" : true,
7942 * Fires when a row is selected.
7943 * @param {SelectionModel} this
7944 * @param {Number} rowIndex The selected index
7945 * @param {Roo.data.Record} r The record
7949 * @event rowdeselect
7950 * Fires when a row is deselected.
7951 * @param {SelectionModel} this
7952 * @param {Number} rowIndex The selected index
7954 "rowdeselect" : true
7956 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7957 this.locked = false;
7960 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7962 * @cfg {Boolean} singleSelect
7963 * True to allow selection of only one row at a time (defaults to false)
7965 singleSelect : false,
7968 initEvents : function(){
7970 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7971 this.grid.on("mousedown", this.handleMouseDown, this);
7972 }else{ // allow click to work like normal
7973 this.grid.on("rowclick", this.handleDragableRowClick, this);
7975 // bootstrap does not have a view..
7976 var view = this.grid.view ? this.grid.view : this.grid;
7977 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7980 this.selectPrevious(e.shiftKey);
7981 }else if(this.last !== false && this.lastActive !== false){
7982 var last = this.last;
7983 this.selectRange(this.last, this.lastActive-1);
7984 view.focusRow(this.lastActive);
7989 this.selectFirstRow();
7991 this.fireEvent("afterselectionchange", this);
7993 "down" : function(e){
7995 this.selectNext(e.shiftKey);
7996 }else if(this.last !== false && this.lastActive !== false){
7997 var last = this.last;
7998 this.selectRange(this.last, this.lastActive+1);
7999 view.focusRow(this.lastActive);
8004 this.selectFirstRow();
8006 this.fireEvent("afterselectionchange", this);
8012 view.on("refresh", this.onRefresh, this);
8013 view.on("rowupdated", this.onRowUpdated, this);
8014 view.on("rowremoved", this.onRemove, this);
8018 onRefresh : function(){
8019 var ds = this.grid.ds, i, v = this.grid.view;
8020 var s = this.selections;
8022 if((i = ds.indexOfId(r.id)) != -1){
8024 s.add(ds.getAt(i)); // updating the selection relate data
8032 onRemove : function(v, index, r){
8033 this.selections.remove(r);
8037 onRowUpdated : function(v, index, r){
8038 if(this.isSelected(r)){
8039 v.onRowSelect(index);
8045 * @param {Array} records The records to select
8046 * @param {Boolean} keepExisting (optional) True to keep existing selections
8048 selectRecords : function(records, keepExisting){
8050 this.clearSelections();
8052 var ds = this.grid.ds;
8053 for(var i = 0, len = records.length; i < len; i++){
8054 this.selectRow(ds.indexOf(records[i]), true);
8059 * Gets the number of selected rows.
8062 getCount : function(){
8063 return this.selections.length;
8067 * Selects the first row in the grid.
8069 selectFirstRow : function(){
8074 * Select the last row.
8075 * @param {Boolean} keepExisting (optional) True to keep existing selections
8077 selectLastRow : function(keepExisting){
8078 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8082 * Selects the row immediately following the last selected row.
8083 * @param {Boolean} keepExisting (optional) True to keep existing selections
8085 selectNext : function(keepExisting){
8086 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8087 this.selectRow(this.last+1, keepExisting);
8088 var view = this.grid.view ? this.grid.view : this.grid;
8089 view.focusRow(this.last);
8094 * Selects the row that precedes the last selected row.
8095 * @param {Boolean} keepExisting (optional) True to keep existing selections
8097 selectPrevious : function(keepExisting){
8099 this.selectRow(this.last-1, keepExisting);
8100 var view = this.grid.view ? this.grid.view : this.grid;
8101 view.focusRow(this.last);
8106 * Returns the selected records
8107 * @return {Array} Array of selected records
8109 getSelections : function(){
8110 return [].concat(this.selections.items);
8114 * Returns the first selected record.
8117 getSelected : function(){
8118 return this.selections.itemAt(0);
8123 * Clears all selections.
8125 clearSelections : function(fast){
8130 var ds = this.grid.ds;
8131 var s = this.selections;
8133 this.deselectRow(ds.indexOfId(r.id));
8137 this.selections.clear();
8146 selectAll : function(){
8150 this.selections.clear();
8151 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8152 this.selectRow(i, true);
8157 * Returns True if there is a selection.
8160 hasSelection : function(){
8161 return this.selections.length > 0;
8165 * Returns True if the specified row is selected.
8166 * @param {Number/Record} record The record or index of the record to check
8169 isSelected : function(index){
8170 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8171 return (r && this.selections.key(r.id) ? true : false);
8175 * Returns True if the specified record id is selected.
8176 * @param {String} id The id of record to check
8179 isIdSelected : function(id){
8180 return (this.selections.key(id) ? true : false);
8184 handleMouseDown : function(e, t)
8186 var view = this.grid.view ? this.grid.view : this.grid;
8188 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8191 if(e.shiftKey && this.last !== false){
8192 var last = this.last;
8193 this.selectRange(last, rowIndex, e.ctrlKey);
8194 this.last = last; // reset the last
8195 view.focusRow(rowIndex);
8197 var isSelected = this.isSelected(rowIndex);
8198 if(e.button !== 0 && isSelected){
8199 view.focusRow(rowIndex);
8200 }else if(e.ctrlKey && isSelected){
8201 this.deselectRow(rowIndex);
8202 }else if(!isSelected){
8203 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8204 view.focusRow(rowIndex);
8207 this.fireEvent("afterselectionchange", this);
8210 handleDragableRowClick : function(grid, rowIndex, e)
8212 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8213 this.selectRow(rowIndex, false);
8214 var view = this.grid.view ? this.grid.view : this.grid;
8215 view.focusRow(rowIndex);
8216 this.fireEvent("afterselectionchange", this);
8221 * Selects multiple rows.
8222 * @param {Array} rows Array of the indexes of the row to select
8223 * @param {Boolean} keepExisting (optional) True to keep existing selections
8225 selectRows : function(rows, keepExisting){
8227 this.clearSelections();
8229 for(var i = 0, len = rows.length; i < len; i++){
8230 this.selectRow(rows[i], true);
8235 * Selects a range of rows. All rows in between startRow and endRow are also selected.
8236 * @param {Number} startRow The index of the first row in the range
8237 * @param {Number} endRow The index of the last row in the range
8238 * @param {Boolean} keepExisting (optional) True to retain existing selections
8240 selectRange : function(startRow, endRow, keepExisting){
8245 this.clearSelections();
8247 if(startRow <= endRow){
8248 for(var i = startRow; i <= endRow; i++){
8249 this.selectRow(i, true);
8252 for(var i = startRow; i >= endRow; i--){
8253 this.selectRow(i, true);
8259 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8260 * @param {Number} startRow The index of the first row in the range
8261 * @param {Number} endRow The index of the last row in the range
8263 deselectRange : function(startRow, endRow, preventViewNotify){
8267 for(var i = startRow; i <= endRow; i++){
8268 this.deselectRow(i, preventViewNotify);
8274 * @param {Number} row The index of the row to select
8275 * @param {Boolean} keepExisting (optional) True to keep existing selections
8277 selectRow : function(index, keepExisting, preventViewNotify){
8278 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8281 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8282 if(!keepExisting || this.singleSelect){
8283 this.clearSelections();
8285 var r = this.grid.ds.getAt(index);
8286 this.selections.add(r);
8287 this.last = this.lastActive = index;
8288 if(!preventViewNotify){
8289 var view = this.grid.view ? this.grid.view : this.grid;
8290 view.onRowSelect(index);
8292 this.fireEvent("rowselect", this, index, r);
8293 this.fireEvent("selectionchange", this);
8299 * @param {Number} row The index of the row to deselect
8301 deselectRow : function(index, preventViewNotify){
8305 if(this.last == index){
8308 if(this.lastActive == index){
8309 this.lastActive = false;
8311 var r = this.grid.ds.getAt(index);
8312 this.selections.remove(r);
8313 if(!preventViewNotify){
8314 var view = this.grid.view ? this.grid.view : this.grid;
8315 view.onRowDeselect(index);
8317 this.fireEvent("rowdeselect", this, index);
8318 this.fireEvent("selectionchange", this);
8322 restoreLast : function(){
8324 this.last = this._last;
8329 acceptsNav : function(row, col, cm){
8330 return !cm.isHidden(col) && cm.isCellEditable(col, row);
8334 onEditorKey : function(field, e){
8335 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8340 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8342 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8344 }else if(k == e.ENTER && !e.ctrlKey){
8348 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8350 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8352 }else if(k == e.ESC){
8356 g.startEditing(newCell[0], newCell[1]);
8361 * Ext JS Library 1.1.1
8362 * Copyright(c) 2006-2007, Ext JS, LLC.
8364 * Originally Released Under LGPL - original licence link has changed is not relivant.
8367 * <script type="text/javascript">
8372 * @class Roo.grid.ColumnModel
8373 * @extends Roo.util.Observable
8374 * This is the default implementation of a ColumnModel used by the Grid. It defines
8375 * the columns in the grid.
8378 var colModel = new Roo.grid.ColumnModel([
8379 {header: "Ticker", width: 60, sortable: true, locked: true},
8380 {header: "Company Name", width: 150, sortable: true},
8381 {header: "Market Cap.", width: 100, sortable: true},
8382 {header: "$ Sales", width: 100, sortable: true, renderer: money},
8383 {header: "Employees", width: 100, sortable: true, resizable: false}
8388 * The config options listed for this class are options which may appear in each
8389 * individual column definition.
8390 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8392 * @param {Object} config An Array of column config objects. See this class's
8393 * config objects for details.
8395 Roo.grid.ColumnModel = function(config){
8397 * The config passed into the constructor
8399 this.config = []; //config;
8402 // if no id, create one
8403 // if the column does not have a dataIndex mapping,
8404 // map it to the order it is in the config
8405 for(var i = 0, len = config.length; i < len; i++){
8406 this.addColumn(config[i]);
8411 * The width of columns which have no width specified (defaults to 100)
8414 this.defaultWidth = 100;
8417 * Default sortable of columns which have no sortable specified (defaults to false)
8420 this.defaultSortable = false;
8424 * @event widthchange
8425 * Fires when the width of a column changes.
8426 * @param {ColumnModel} this
8427 * @param {Number} columnIndex The column index
8428 * @param {Number} newWidth The new width
8430 "widthchange": true,
8432 * @event headerchange
8433 * Fires when the text of a header changes.
8434 * @param {ColumnModel} this
8435 * @param {Number} columnIndex The column index
8436 * @param {Number} newText The new header text
8438 "headerchange": true,
8440 * @event hiddenchange
8441 * Fires when a column is hidden or "unhidden".
8442 * @param {ColumnModel} this
8443 * @param {Number} columnIndex The column index
8444 * @param {Boolean} hidden true if hidden, false otherwise
8446 "hiddenchange": true,
8448 * @event columnmoved
8449 * Fires when a column is moved.
8450 * @param {ColumnModel} this
8451 * @param {Number} oldIndex
8452 * @param {Number} newIndex
8454 "columnmoved" : true,
8456 * @event columlockchange
8457 * Fires when a column's locked state is changed
8458 * @param {ColumnModel} this
8459 * @param {Number} colIndex
8460 * @param {Boolean} locked true if locked
8462 "columnlockchange" : true
8464 Roo.grid.ColumnModel.superclass.constructor.call(this);
8466 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8468 * @cfg {String} header [required] The header text to display in the Grid view.
8471 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8474 * @cfg {String} smHeader Header at Bootsrap Small width
8477 * @cfg {String} mdHeader Header at Bootsrap Medium width
8480 * @cfg {String} lgHeader Header at Bootsrap Large width
8483 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8486 * @cfg {String} dataIndex The name of the field in the grid's {@link Roo.data.Store}'s
8487 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8488 * specified, the column's index is used as an index into the Record's data Array.
8491 * @cfg {Number} width The initial width in pixels of the column. Using this
8492 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8495 * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
8496 * Defaults to the value of the {@link #defaultSortable} property.
8497 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8500 * @cfg {Boolean} locked True to lock the column in place while scrolling the Grid. Defaults to false.
8503 * @cfg {Boolean} fixed True if the column width cannot be changed. Defaults to false.
8506 * @cfg {Boolean} resizable False to disable column resizing. Defaults to true.
8509 * @cfg {Boolean} hidden True to hide the column. Defaults to false.
8512 * @cfg {Function} renderer A function used to generate HTML markup for a cell
8513 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8514 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8515 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8518 * @cfg {Roo.grid.GridEditor} editor For grid editors - returns the grid editor
8521 * @cfg {String} align (left|right) Set the CSS text-align property of the column. Defaults to undefined (left).
8524 * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined (middle)
8527 * @cfg {String} cursor ( auto|default|none|context-menu|help|pointer|progress|wait|cell|crosshair|text|vertical-text|alias|copy|move|no-drop|not-allowed|e-resize|n-resize|ne-resize|nw-resize|s-resize|se-resize|sw-resize|w-resize|ew-resize|ns-resize|nesw-resize|nwse-resize|col-resize|row-resize|all-scroll|zoom-in|zoom-out|grab|grabbing)
8530 * @cfg {String} tooltip mouse over tooltip text
8533 * @cfg {Number} xs can be '0' for hidden at this size (number less than 12)
8536 * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
8539 * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
8542 * @cfg {Number} lg can be '0' for hidden at this size (number less than 12)
8545 * @cfg {Number} xl can be '0' for hidden at this size (number less than 12)
8548 * Returns the id of the column at the specified index.
8549 * @param {Number} index The column index
8550 * @return {String} the id
8552 getColumnId : function(index){
8553 return this.config[index].id;
8557 * Returns the column for a specified id.
8558 * @param {String} id The column id
8559 * @return {Object} the column
8561 getColumnById : function(id){
8562 return this.lookup[id];
8567 * Returns the column Object for a specified dataIndex.
8568 * @param {String} dataIndex The column dataIndex
8569 * @return {Object|Boolean} the column or false if not found
8571 getColumnByDataIndex: function(dataIndex){
8572 var index = this.findColumnIndex(dataIndex);
8573 return index > -1 ? this.config[index] : false;
8577 * Returns the index for a specified column id.
8578 * @param {String} id The column id
8579 * @return {Number} the index, or -1 if not found
8581 getIndexById : function(id){
8582 for(var i = 0, len = this.config.length; i < len; i++){
8583 if(this.config[i].id == id){
8591 * Returns the index for a specified column dataIndex.
8592 * @param {String} dataIndex The column dataIndex
8593 * @return {Number} the index, or -1 if not found
8596 findColumnIndex : function(dataIndex){
8597 for(var i = 0, len = this.config.length; i < len; i++){
8598 if(this.config[i].dataIndex == dataIndex){
8606 moveColumn : function(oldIndex, newIndex){
8607 var c = this.config[oldIndex];
8608 this.config.splice(oldIndex, 1);
8609 this.config.splice(newIndex, 0, c);
8610 this.dataMap = null;
8611 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8614 isLocked : function(colIndex){
8615 return this.config[colIndex].locked === true;
8618 setLocked : function(colIndex, value, suppressEvent){
8619 if(this.isLocked(colIndex) == value){
8622 this.config[colIndex].locked = value;
8624 this.fireEvent("columnlockchange", this, colIndex, value);
8628 getTotalLockedWidth : function(){
8630 for(var i = 0; i < this.config.length; i++){
8631 if(this.isLocked(i) && !this.isHidden(i)){
8632 this.totalWidth += this.getColumnWidth(i);
8638 getLockedCount : function(){
8639 for(var i = 0, len = this.config.length; i < len; i++){
8640 if(!this.isLocked(i)){
8645 return this.config.length;
8649 * Returns the number of columns.
8652 getColumnCount : function(visibleOnly){
8653 if(visibleOnly === true){
8655 for(var i = 0, len = this.config.length; i < len; i++){
8656 if(!this.isHidden(i)){
8662 return this.config.length;
8666 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8667 * @param {Function} fn
8668 * @param {Object} scope (optional)
8669 * @return {Array} result
8671 getColumnsBy : function(fn, scope){
8673 for(var i = 0, len = this.config.length; i < len; i++){
8674 var c = this.config[i];
8675 if(fn.call(scope||this, c, i) === true){
8683 * Returns true if the specified column is sortable.
8684 * @param {Number} col The column index
8687 isSortable : function(col){
8688 if(typeof this.config[col].sortable == "undefined"){
8689 return this.defaultSortable;
8691 return this.config[col].sortable;
8695 * Returns the rendering (formatting) function defined for the column.
8696 * @param {Number} col The column index.
8697 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8699 getRenderer : function(col){
8700 if(!this.config[col].renderer){
8701 return Roo.grid.ColumnModel.defaultRenderer;
8703 return this.config[col].renderer;
8707 * Sets the rendering (formatting) function for a column.
8708 * @param {Number} col The column index
8709 * @param {Function} fn The function to use to process the cell's raw data
8710 * to return HTML markup for the grid view. The render function is called with
8711 * the following parameters:<ul>
8712 * <li>Data value.</li>
8713 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8714 * <li>css A CSS style string to apply to the table cell.</li>
8715 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8716 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8717 * <li>Row index</li>
8718 * <li>Column index</li>
8719 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8721 setRenderer : function(col, fn){
8722 this.config[col].renderer = fn;
8726 * Returns the width for the specified column.
8727 * @param {Number} col The column index
8728 * @param (optional) {String} gridSize bootstrap width size.
8731 getColumnWidth : function(col, gridSize)
8733 var cfg = this.config[col];
8735 if (typeof(gridSize) == 'undefined') {
8736 return cfg.width * 1 || this.defaultWidth;
8738 if (gridSize === false) { // if we set it..
8739 return cfg.width || false;
8741 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8743 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8744 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8747 return cfg[ sizes[i] ];
8754 * Sets the width for a column.
8755 * @param {Number} col The column index
8756 * @param {Number} width The new width
8758 setColumnWidth : function(col, width, suppressEvent){
8759 this.config[col].width = width;
8760 this.totalWidth = null;
8762 this.fireEvent("widthchange", this, col, width);
8767 * Returns the total width of all columns.
8768 * @param {Boolean} includeHidden True to include hidden column widths
8771 getTotalWidth : function(includeHidden){
8772 if(!this.totalWidth){
8773 this.totalWidth = 0;
8774 for(var i = 0, len = this.config.length; i < len; i++){
8775 if(includeHidden || !this.isHidden(i)){
8776 this.totalWidth += this.getColumnWidth(i);
8780 return this.totalWidth;
8784 * Returns the header for the specified column.
8785 * @param {Number} col The column index
8788 getColumnHeader : function(col){
8789 return this.config[col].header;
8793 * Sets the header for a column.
8794 * @param {Number} col The column index
8795 * @param {String} header The new header
8797 setColumnHeader : function(col, header){
8798 this.config[col].header = header;
8799 this.fireEvent("headerchange", this, col, header);
8803 * Returns the tooltip for the specified column.
8804 * @param {Number} col The column index
8807 getColumnTooltip : function(col){
8808 return this.config[col].tooltip;
8811 * Sets the tooltip for a column.
8812 * @param {Number} col The column index
8813 * @param {String} tooltip The new tooltip
8815 setColumnTooltip : function(col, tooltip){
8816 this.config[col].tooltip = tooltip;
8820 * Returns the dataIndex for the specified column.
8821 * @param {Number} col The column index
8824 getDataIndex : function(col){
8825 return this.config[col].dataIndex;
8829 * Sets the dataIndex for a column.
8830 * @param {Number} col The column index
8831 * @param {Number} dataIndex The new dataIndex
8833 setDataIndex : function(col, dataIndex){
8834 this.config[col].dataIndex = dataIndex;
8840 * Returns true if the cell is editable.
8841 * @param {Number} colIndex The column index
8842 * @param {Number} rowIndex The row index - this is nto actually used..?
8845 isCellEditable : function(colIndex, rowIndex){
8846 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8850 * Returns the editor defined for the cell/column.
8851 * return false or null to disable editing.
8852 * @param {Number} colIndex The column index
8853 * @param {Number} rowIndex The row index
8856 getCellEditor : function(colIndex, rowIndex){
8857 return this.config[colIndex].editor;
8861 * Sets if a column is editable.
8862 * @param {Number} col The column index
8863 * @param {Boolean} editable True if the column is editable
8865 setEditable : function(col, editable){
8866 this.config[col].editable = editable;
8871 * Returns true if the column is hidden.
8872 * @param {Number} colIndex The column index
8875 isHidden : function(colIndex){
8876 return this.config[colIndex].hidden;
8881 * Returns true if the column width cannot be changed
8883 isFixed : function(colIndex){
8884 return this.config[colIndex].fixed;
8888 * Returns true if the column can be resized
8891 isResizable : function(colIndex){
8892 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8895 * Sets if a column is hidden.
8896 * @param {Number} colIndex The column index
8897 * @param {Boolean} hidden True if the column is hidden
8899 setHidden : function(colIndex, hidden){
8900 this.config[colIndex].hidden = hidden;
8901 this.totalWidth = null;
8902 this.fireEvent("hiddenchange", this, colIndex, hidden);
8906 * Sets the editor for a column.
8907 * @param {Number} col The column index
8908 * @param {Object} editor The editor object
8910 setEditor : function(col, editor){
8911 this.config[col].editor = editor;
8914 * Add a column (experimental...) - defaults to adding to the end..
8915 * @param {Object} config
8917 addColumn : function(c)
8920 var i = this.config.length;
8923 if(typeof c.dataIndex == "undefined"){
8926 if(typeof c.renderer == "string"){
8927 c.renderer = Roo.util.Format[c.renderer];
8929 if(typeof c.id == "undefined"){
8932 if(c.editor && c.editor.xtype){
8933 c.editor = Roo.factory(c.editor, Roo.grid);
8935 if(c.editor && c.editor.isFormField){
8936 c.editor = new Roo.grid.GridEditor(c.editor);
8938 this.lookup[c.id] = c;
8943 Roo.grid.ColumnModel.defaultRenderer = function(value)
8945 if(typeof value == "object") {
8948 if(typeof value == "string" && value.length < 1){
8952 return String.format("{0}", value);
8955 // Alias for backwards compatibility
8956 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8959 * Ext JS Library 1.1.1
8960 * Copyright(c) 2006-2007, Ext JS, LLC.
8962 * Originally Released Under LGPL - original licence link has changed is not relivant.
8965 * <script type="text/javascript">
8969 * @class Roo.LoadMask
8970 * A simple utility class for generically masking elements while loading data. If the element being masked has
8971 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8972 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8973 * element's UpdateManager load indicator and will be destroyed after the initial load.
8975 * Create a new LoadMask
8976 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8977 * @param {Object} config The config object
8979 Roo.LoadMask = function(el, config){
8980 this.el = Roo.get(el);
8981 Roo.apply(this, config);
8983 this.store.on('beforeload', this.onBeforeLoad, this);
8984 this.store.on('load', this.onLoad, this);
8985 this.store.on('loadexception', this.onLoadException, this);
8986 this.removeMask = false;
8988 var um = this.el.getUpdateManager();
8989 um.showLoadIndicator = false; // disable the default indicator
8990 um.on('beforeupdate', this.onBeforeLoad, this);
8991 um.on('update', this.onLoad, this);
8992 um.on('failure', this.onLoad, this);
8993 this.removeMask = true;
8997 Roo.LoadMask.prototype = {
8999 * @cfg {Boolean} removeMask
9000 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
9001 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
9006 * The text to display in a centered loading message box (defaults to 'Loading...')
9010 * @cfg {String} msgCls
9011 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9013 msgCls : 'x-mask-loading',
9016 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9022 * Disables the mask to prevent it from being displayed
9024 disable : function(){
9025 this.disabled = true;
9029 * Enables the mask so that it can be displayed
9031 enable : function(){
9032 this.disabled = false;
9035 onLoadException : function()
9039 if (typeof(arguments[3]) != 'undefined') {
9040 Roo.MessageBox.alert("Error loading",arguments[3]);
9044 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9045 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9052 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9057 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9061 onBeforeLoad : function(){
9063 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9068 destroy : function(){
9070 this.store.un('beforeload', this.onBeforeLoad, this);
9071 this.store.un('load', this.onLoad, this);
9072 this.store.un('loadexception', this.onLoadException, this);
9074 var um = this.el.getUpdateManager();
9075 um.un('beforeupdate', this.onBeforeLoad, this);
9076 um.un('update', this.onLoad, this);
9077 um.un('failure', this.onLoad, this);
9081 * @class Roo.bootstrap.Table
9083 * @extends Roo.bootstrap.Component
9084 * @children Roo.bootstrap.TableBody
9085 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
9086 * Similar to Roo.grid.Grid
9088 var table = Roo.factory({
9090 xns : Roo.bootstrap,
9091 autoSizeColumns: true,
9098 sortInfo : { direction : 'ASC', field: 'name' },
9100 xtype : 'HttpProxy',
9103 url : 'https://example.com/some.data.url.json'
9106 xtype : 'JsonReader',
9108 fields : [ 'id', 'name', whatever' ],
9115 xtype : 'ColumnModel',
9119 dataIndex : 'is_in_group',
9122 renderer : function(v, x , r) {
9124 return String.format("{0}", v)
9130 xtype : 'RowSelectionModel',
9131 xns : Roo.bootstrap.Table
9132 // you can add listeners to catch selection change here....
9138 grid.render(Roo.get("some-div"));
9141 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
9146 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9147 * @cfg {Roo.data.Store} store The data store to use
9148 * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9150 * @cfg {String} cls table class
9153 * @cfg {string} empty_results Text to display for no results
9154 * @cfg {boolean} striped Should the rows be alternative striped
9155 * @cfg {boolean} bordered Add borders to the table
9156 * @cfg {boolean} hover Add hover highlighting
9157 * @cfg {boolean} condensed Format condensed
9158 * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
9159 * also adds table-responsive (see bootstrap docs for details)
9160 * @cfg {Boolean} loadMask (true|false) default false
9161 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9162 * @cfg {Boolean} headerShow (true|false) generate thead, default true
9163 * @cfg {Boolean} rowSelection (true|false) default false
9164 * @cfg {Boolean} cellSelection (true|false) default false
9165 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9166 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
9167 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
9168 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
9169 * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9172 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
9175 * Create a new Table
9176 * @param {Object} config The config object
9179 Roo.bootstrap.Table = function(config)
9181 Roo.bootstrap.Table.superclass.constructor.call(this, config);
9184 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9185 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9186 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9187 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9189 this.view = this; // compat with grid.
9191 this.sm = this.sm || {xtype: 'RowSelectionModel'};
9193 this.sm.grid = this;
9194 this.selModel = Roo.factory(this.sm, Roo.grid);
9195 this.sm = this.selModel;
9196 this.sm.xmodule = this.xmodule || false;
9199 if (this.cm && typeof(this.cm.config) == 'undefined') {
9200 this.colModel = new Roo.grid.ColumnModel(this.cm);
9201 this.cm = this.colModel;
9202 this.cm.xmodule = this.xmodule || false;
9205 this.store= Roo.factory(this.store, Roo.data);
9206 this.ds = this.store;
9207 this.ds.xmodule = this.xmodule || false;
9210 if (this.footer && this.store) {
9211 this.footer.dataSource = this.ds;
9212 this.footer = Roo.factory(this.footer);
9219 * Fires when a cell is clicked
9220 * @param {Roo.bootstrap.Table} this
9221 * @param {Roo.Element} el
9222 * @param {Number} rowIndex
9223 * @param {Number} columnIndex
9224 * @param {Roo.EventObject} e
9228 * @event celldblclick
9229 * Fires when a cell is double clicked
9230 * @param {Roo.bootstrap.Table} this
9231 * @param {Roo.Element} el
9232 * @param {Number} rowIndex
9233 * @param {Number} columnIndex
9234 * @param {Roo.EventObject} e
9236 "celldblclick" : true,
9239 * Fires when a row is clicked
9240 * @param {Roo.bootstrap.Table} this
9241 * @param {Roo.Element} el
9242 * @param {Number} rowIndex
9243 * @param {Roo.EventObject} e
9247 * @event rowdblclick
9248 * Fires when a row is double clicked
9249 * @param {Roo.bootstrap.Table} this
9250 * @param {Roo.Element} el
9251 * @param {Number} rowIndex
9252 * @param {Roo.EventObject} e
9254 "rowdblclick" : true,
9257 * Fires when a mouseover occur
9258 * @param {Roo.bootstrap.Table} this
9259 * @param {Roo.Element} el
9260 * @param {Number} rowIndex
9261 * @param {Number} columnIndex
9262 * @param {Roo.EventObject} e
9267 * Fires when a mouseout occur
9268 * @param {Roo.bootstrap.Table} this
9269 * @param {Roo.Element} el
9270 * @param {Number} rowIndex
9271 * @param {Number} columnIndex
9272 * @param {Roo.EventObject} e
9277 * Fires when a row is rendered, so you can change add a style to it.
9278 * @param {Roo.bootstrap.Table} this
9279 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
9283 * @event rowsrendered
9284 * Fires when all the rows have been rendered
9285 * @param {Roo.bootstrap.Table} this
9287 'rowsrendered' : true,
9289 * @event contextmenu
9290 * The raw contextmenu event for the entire grid.
9291 * @param {Roo.EventObject} e
9293 "contextmenu" : true,
9295 * @event rowcontextmenu
9296 * Fires when a row is right clicked
9297 * @param {Roo.bootstrap.Table} this
9298 * @param {Number} rowIndex
9299 * @param {Roo.EventObject} e
9301 "rowcontextmenu" : true,
9303 * @event cellcontextmenu
9304 * Fires when a cell is right clicked
9305 * @param {Roo.bootstrap.Table} this
9306 * @param {Number} rowIndex
9307 * @param {Number} cellIndex
9308 * @param {Roo.EventObject} e
9310 "cellcontextmenu" : true,
9312 * @event headercontextmenu
9313 * Fires when a header is right clicked
9314 * @param {Roo.bootstrap.Table} this
9315 * @param {Number} columnIndex
9316 * @param {Roo.EventObject} e
9318 "headercontextmenu" : true,
9321 * The raw mousedown event for the entire grid.
9322 * @param {Roo.EventObject} e
9329 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
9346 enableColumnResize: true,
9348 rowSelection : false,
9349 cellSelection : false,
9352 minColumnWidth : 50,
9354 // Roo.Element - the tbody
9355 bodyEl: false, // <tbody> Roo.Element - thead element
9356 headEl: false, // <thead> Roo.Element - thead element
9357 resizeProxy : false, // proxy element for dragging?
9361 container: false, // used by gridpanel...
9367 auto_hide_footer : false,
9369 view: false, // actually points to this..
9371 getAutoCreate : function()
9373 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9380 // this get's auto added by panel.Grid
9381 if (this.scrollBody) {
9382 cfg.cls += ' table-body-fixed';
9385 cfg.cls += ' table-striped';
9389 cfg.cls += ' table-hover';
9391 if (this.bordered) {
9392 cfg.cls += ' table-bordered';
9394 if (this.condensed) {
9395 cfg.cls += ' table-condensed';
9398 if (this.responsive) {
9399 cfg.cls += ' table-responsive';
9403 cfg.cls+= ' ' +this.cls;
9409 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9412 if(this.store || this.cm){
9413 if(this.headerShow){
9414 cfg.cn.push(this.renderHeader());
9417 cfg.cn.push(this.renderBody());
9419 if(this.footerShow){
9420 cfg.cn.push(this.renderFooter());
9422 // where does this come from?
9423 //cfg.cls+= ' TableGrid';
9426 return { cn : [ cfg ] };
9429 initEvents : function()
9431 if(!this.store || !this.cm){
9434 if (this.selModel) {
9435 this.selModel.initEvents();
9439 //Roo.log('initEvents with ds!!!!');
9441 this.bodyEl = this.el.select('tbody', true).first();
9442 this.headEl = this.el.select('thead', true).first();
9443 this.mainFoot = this.el.select('tfoot', true).first();
9448 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9449 e.on('click', this.sort, this);
9453 // why is this done????? = it breaks dialogs??
9454 //this.parent().el.setStyle('position', 'relative');
9458 this.footer.parentId = this.id;
9459 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9462 this.el.select('tfoot tr td').first().addClass('hide');
9467 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9470 this.store.on('load', this.onLoad, this);
9471 this.store.on('beforeload', this.onBeforeLoad, this);
9472 this.store.on('update', this.onUpdate, this);
9473 this.store.on('add', this.onAdd, this);
9474 this.store.on("clear", this.clear, this);
9476 this.el.on("contextmenu", this.onContextMenu, this);
9479 this.cm.on("headerchange", this.onHeaderChange, this);
9480 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9482 //?? does bodyEl get replaced on render?
9483 this.bodyEl.on("click", this.onClick, this);
9484 this.bodyEl.on("dblclick", this.onDblClick, this);
9485 this.bodyEl.on('scroll', this.onBodyScroll, this);
9487 // guessing mainbody will work - this relays usually caught by selmodel at present.
9488 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9491 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9494 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9495 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9500 // Compatibility with grid - we implement all the view features at present.
9501 getView : function()
9506 initCSS : function()
9510 var cm = this.cm, styles = [];
9511 this.CSS.removeStyleSheet(this.id + '-cssrules');
9512 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9513 // we can honour xs/sm/md/xl as widths...
9514 // we first have to decide what widht we are currently at...
9515 var sz = Roo.getGridSize();
9519 var cols = []; // visable cols.
9521 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9522 var w = cm.getColumnWidth(i, false);
9524 cols.push( { rel : false, abs : 0 });
9528 cols.push( { rel : false, abs : w });
9530 last = i; // not really..
9533 var w = cm.getColumnWidth(i, sz);
9538 cols.push( { rel : w, abs : false });
9541 var avail = this.bodyEl.dom.clientWidth - total_abs;
9543 var unitWidth = Math.floor(avail / total);
9544 var rem = avail - (unitWidth * total);
9546 var hidden, width, pos = 0 , splithide , left;
9547 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9549 hidden = 'display:none;';
9551 width = 'width:0px;';
9553 if(!cm.isHidden(i)){
9557 // we can honour xs/sm/md/xl ?
9558 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9560 hidden = 'display:none;';
9562 // width should return a small number...
9564 w+=rem; // add the remaining with..
9567 left = "left:" + (pos -4) + "px;";
9568 width = "width:" + w+ "px;";
9571 if (this.responsive) {
9574 hidden = cm.isHidden(i) ? 'display:none;' : '';
9575 splithide = 'display: none;';
9578 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9581 splithide = 'display:none;';
9584 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9585 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9586 // this is the popover version..
9587 '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9592 //Roo.log(styles.join(''));
9593 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9599 onContextMenu : function(e, t)
9601 this.processEvent("contextmenu", e);
9604 processEvent : function(name, e)
9606 if (name != 'touchstart' ) {
9607 this.fireEvent(name, e);
9610 var t = e.getTarget();
9612 var cell = Roo.get(t);
9618 if(cell.findParent('tfoot', false, true)){
9622 if(cell.findParent('thead', false, true)){
9624 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9625 cell = Roo.get(t).findParent('th', false, true);
9627 Roo.log("failed to find th in thead?");
9628 Roo.log(e.getTarget());
9633 var cellIndex = cell.dom.cellIndex;
9635 var ename = name == 'touchstart' ? 'click' : name;
9636 this.fireEvent("header" + ename, this, cellIndex, e);
9641 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9642 cell = Roo.get(t).findParent('td', false, true);
9644 Roo.log("failed to find th in tbody?");
9645 Roo.log(e.getTarget());
9650 var row = cell.findParent('tr', false, true);
9651 var cellIndex = cell.dom.cellIndex;
9652 var rowIndex = row.dom.rowIndex - 1;
9656 this.fireEvent("row" + name, this, rowIndex, e);
9660 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9666 onMouseover : function(e, el)
9668 var cell = Roo.get(el);
9674 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9675 cell = cell.findParent('td', false, true);
9678 var row = cell.findParent('tr', false, true);
9679 var cellIndex = cell.dom.cellIndex;
9680 var rowIndex = row.dom.rowIndex - 1; // start from 0
9682 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9686 onMouseout : function(e, el)
9688 var cell = Roo.get(el);
9694 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9695 cell = cell.findParent('td', false, true);
9698 var row = cell.findParent('tr', false, true);
9699 var cellIndex = cell.dom.cellIndex;
9700 var rowIndex = row.dom.rowIndex - 1; // start from 0
9702 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9706 onClick : function(e, el)
9708 var cell = Roo.get(el);
9710 if(!cell || (!this.cellSelection && !this.rowSelection)){
9714 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9715 cell = cell.findParent('td', false, true);
9718 if(!cell || typeof(cell) == 'undefined'){
9722 var row = cell.findParent('tr', false, true);
9724 if(!row || typeof(row) == 'undefined'){
9728 var cellIndex = cell.dom.cellIndex;
9729 var rowIndex = this.getRowIndex(row);
9731 // why??? - should these not be based on SelectionModel?
9732 //if(this.cellSelection){
9733 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9736 //if(this.rowSelection){
9737 this.fireEvent('rowclick', this, row, rowIndex, e);
9742 onDblClick : function(e,el)
9744 var cell = Roo.get(el);
9746 if(!cell || (!this.cellSelection && !this.rowSelection)){
9750 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9751 cell = cell.findParent('td', false, true);
9754 if(!cell || typeof(cell) == 'undefined'){
9758 var row = cell.findParent('tr', false, true);
9760 if(!row || typeof(row) == 'undefined'){
9764 var cellIndex = cell.dom.cellIndex;
9765 var rowIndex = this.getRowIndex(row);
9767 if(this.cellSelection){
9768 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9771 if(this.rowSelection){
9772 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9775 findRowIndex : function(el)
9777 var cell = Roo.get(el);
9781 var row = cell.findParent('tr', false, true);
9783 if(!row || typeof(row) == 'undefined'){
9786 return this.getRowIndex(row);
9788 sort : function(e,el)
9790 var col = Roo.get(el);
9792 if(!col.hasClass('sortable')){
9796 var sort = col.attr('sort');
9799 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9803 this.store.sortInfo = {field : sort, direction : dir};
9806 Roo.log("calling footer first");
9807 this.footer.onClick('first');
9810 this.store.load({ params : { start : 0 } });
9814 renderHeader : function()
9822 this.totalWidth = 0;
9824 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9826 var config = cm.config[i];
9830 cls : 'x-hcol-' + i,
9833 html: cm.getColumnHeader(i)
9836 var tooltip = cm.getColumnTooltip(i);
9838 c.tooltip = tooltip;
9844 if(typeof(config.sortable) != 'undefined' && config.sortable){
9845 c.cls += ' sortable';
9846 c.html = '<i class="fa"></i>' + c.html;
9849 // could use BS4 hidden-..-down
9851 if(typeof(config.lgHeader) != 'undefined'){
9852 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9855 if(typeof(config.mdHeader) != 'undefined'){
9856 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9859 if(typeof(config.smHeader) != 'undefined'){
9860 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9863 if(typeof(config.xsHeader) != 'undefined'){
9864 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9871 if(typeof(config.tooltip) != 'undefined'){
9872 c.tooltip = config.tooltip;
9875 if(typeof(config.colspan) != 'undefined'){
9876 c.colspan = config.colspan;
9879 // hidden is handled by CSS now
9881 if(typeof(config.dataIndex) != 'undefined'){
9882 c.sort = config.dataIndex;
9887 if(typeof(config.align) != 'undefined' && config.align.length){
9888 c.style += ' text-align:' + config.align + ';';
9891 /* width is done in CSS
9892 *if(typeof(config.width) != 'undefined'){
9893 c.style += ' width:' + config.width + 'px;';
9894 this.totalWidth += config.width;
9896 this.totalWidth += 100; // assume minimum of 100 per column?
9900 if(typeof(config.cls) != 'undefined'){
9901 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9903 // this is the bit that doesnt reall work at all...
9905 if (this.responsive) {
9908 ['xs','sm','md','lg'].map(function(size){
9910 if(typeof(config[size]) == 'undefined'){
9914 if (!config[size]) { // 0 = hidden
9915 // BS 4 '0' is treated as hide that column and below.
9916 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9920 c.cls += ' col-' + size + '-' + config[size] + (
9921 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9929 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9940 renderBody : function()
9950 colspan : this.cm.getColumnCount()
9960 renderFooter : function()
9970 colspan : this.cm.getColumnCount()
9984 // Roo.log('ds onload');
9989 var ds = this.store;
9991 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9992 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9993 if (_this.store.sortInfo) {
9995 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9996 e.select('i', true).addClass(['fa-arrow-up']);
9999 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10000 e.select('i', true).addClass(['fa-arrow-down']);
10005 var tbody = this.bodyEl;
10007 if(ds.getCount() > 0){
10008 ds.data.each(function(d,rowIndex){
10009 var row = this.renderRow(cm, ds, rowIndex);
10011 tbody.createChild(row);
10015 if(row.cellObjects.length){
10016 Roo.each(row.cellObjects, function(r){
10017 _this.renderCellObject(r);
10022 } else if (this.empty_results.length) {
10023 this.el.mask(this.empty_results, 'no-spinner');
10026 var tfoot = this.el.select('tfoot', true).first();
10028 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10030 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10032 var total = this.ds.getTotalCount();
10034 if(this.footer.pageSize < total){
10035 this.mainFoot.show();
10039 Roo.each(this.el.select('tbody td', true).elements, function(e){
10040 e.on('mouseover', _this.onMouseover, _this);
10043 Roo.each(this.el.select('tbody td', true).elements, function(e){
10044 e.on('mouseout', _this.onMouseout, _this);
10046 this.fireEvent('rowsrendered', this);
10050 this.initCSS(); /// resize cols
10056 onUpdate : function(ds,record)
10058 this.refreshRow(record);
10062 onRemove : function(ds, record, index, isUpdate){
10063 if(isUpdate !== true){
10064 this.fireEvent("beforerowremoved", this, index, record);
10066 var bt = this.bodyEl.dom;
10068 var rows = this.el.select('tbody > tr', true).elements;
10070 if(typeof(rows[index]) != 'undefined'){
10071 bt.removeChild(rows[index].dom);
10074 // if(bt.rows[index]){
10075 // bt.removeChild(bt.rows[index]);
10078 if(isUpdate !== true){
10079 //this.stripeRows(index);
10080 //this.syncRowHeights(index, index);
10082 this.fireEvent("rowremoved", this, index, record);
10086 onAdd : function(ds, records, rowIndex)
10088 //Roo.log('on Add called');
10089 // - note this does not handle multiple adding very well..
10090 var bt = this.bodyEl.dom;
10091 for (var i =0 ; i < records.length;i++) {
10092 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10093 //Roo.log(records[i]);
10094 //Roo.log(this.store.getAt(rowIndex+i));
10095 this.insertRow(this.store, rowIndex + i, false);
10102 refreshRow : function(record){
10103 var ds = this.store, index;
10104 if(typeof record == 'number'){
10106 record = ds.getAt(index);
10108 index = ds.indexOf(record);
10110 return; // should not happen - but seems to
10113 this.insertRow(ds, index, true);
10115 this.onRemove(ds, record, index+1, true);
10117 //this.syncRowHeights(index, index);
10119 this.fireEvent("rowupdated", this, index, record);
10121 // private - called by RowSelection
10122 onRowSelect : function(rowIndex){
10123 var row = this.getRowDom(rowIndex);
10124 row.addClass(['bg-info','info']);
10126 // private - called by RowSelection
10127 onRowDeselect : function(rowIndex)
10129 if (rowIndex < 0) {
10132 var row = this.getRowDom(rowIndex);
10133 row.removeClass(['bg-info','info']);
10136 * Focuses the specified row.
10137 * @param {Number} row The row index
10139 focusRow : function(row)
10141 //Roo.log('GridView.focusRow');
10142 var x = this.bodyEl.dom.scrollLeft;
10143 this.focusCell(row, 0, false);
10144 this.bodyEl.dom.scrollLeft = x;
10148 * Focuses the specified cell.
10149 * @param {Number} row The row index
10150 * @param {Number} col The column index
10151 * @param {Boolean} hscroll false to disable horizontal scrolling
10153 focusCell : function(row, col, hscroll)
10155 //Roo.log('GridView.focusCell');
10156 var el = this.ensureVisible(row, col, hscroll);
10157 // not sure what focusEL achives = it's a <a> pos relative
10158 //this.focusEl.alignTo(el, "tl-tl");
10160 // this.focusEl.focus();
10162 // this.focusEl.focus.defer(1, this.focusEl);
10167 * Scrolls the specified cell into view
10168 * @param {Number} row The row index
10169 * @param {Number} col The column index
10170 * @param {Boolean} hscroll false to disable horizontal scrolling
10172 ensureVisible : function(row, col, hscroll)
10174 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10175 //return null; //disable for testing.
10176 if(typeof row != "number"){
10177 row = row.rowIndex;
10179 if(row < 0 && row >= this.ds.getCount()){
10182 col = (col !== undefined ? col : 0);
10184 while(cm.isHidden(col)){
10188 var el = this.getCellDom(row, col);
10192 var c = this.bodyEl.dom;
10194 var ctop = parseInt(el.offsetTop, 10);
10195 var cleft = parseInt(el.offsetLeft, 10);
10196 var cbot = ctop + el.offsetHeight;
10197 var cright = cleft + el.offsetWidth;
10199 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10200 var ch = 0; //?? header is not withing the area?
10201 var stop = parseInt(c.scrollTop, 10);
10202 var sleft = parseInt(c.scrollLeft, 10);
10203 var sbot = stop + ch;
10204 var sright = sleft + c.clientWidth;
10206 Roo.log('GridView.ensureVisible:' +
10208 ' c.clientHeight:' + c.clientHeight +
10209 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10217 c.scrollTop = ctop;
10218 //Roo.log("set scrolltop to ctop DISABLE?");
10219 }else if(cbot > sbot){
10220 //Roo.log("set scrolltop to cbot-ch");
10221 c.scrollTop = cbot-ch;
10224 if(hscroll !== false){
10226 c.scrollLeft = cleft;
10227 }else if(cright > sright){
10228 c.scrollLeft = cright-c.clientWidth;
10236 insertRow : function(dm, rowIndex, isUpdate){
10239 this.fireEvent("beforerowsinserted", this, rowIndex);
10241 //var s = this.getScrollState();
10242 var row = this.renderRow(this.cm, this.store, rowIndex);
10243 // insert before rowIndex..
10244 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10248 if(row.cellObjects.length){
10249 Roo.each(row.cellObjects, function(r){
10250 _this.renderCellObject(r);
10255 this.fireEvent("rowsinserted", this, rowIndex);
10256 //this.syncRowHeights(firstRow, lastRow);
10257 //this.stripeRows(firstRow);
10264 getRowDom : function(rowIndex)
10266 var rows = this.el.select('tbody > tr', true).elements;
10268 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10271 getCellDom : function(rowIndex, colIndex)
10273 var row = this.getRowDom(rowIndex);
10274 if (row === false) {
10277 var cols = row.select('td', true).elements;
10278 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10282 // returns the object tree for a tr..
10285 renderRow : function(cm, ds, rowIndex)
10287 var d = ds.getAt(rowIndex);
10291 cls : 'x-row-' + rowIndex,
10295 var cellObjects = [];
10297 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10298 var config = cm.config[i];
10300 var renderer = cm.getRenderer(i);
10304 if(typeof(renderer) !== 'undefined'){
10305 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10307 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10308 // and are rendered into the cells after the row is rendered - using the id for the element.
10310 if(typeof(value) === 'object'){
10320 rowIndex : rowIndex,
10325 this.fireEvent('rowclass', this, rowcfg);
10329 // this might end up displaying HTML?
10330 // this is too messy... - better to only do it on columsn you know are going to be too long
10331 //tooltip : (typeof(value) === 'object') ? '' : value,
10332 cls : rowcfg.rowClass + ' x-col-' + i,
10334 html: (typeof(value) === 'object') ? '' : value
10341 if(typeof(config.colspan) != 'undefined'){
10342 td.colspan = config.colspan;
10347 if(typeof(config.align) != 'undefined' && config.align.length){
10348 td.style += ' text-align:' + config.align + ';';
10350 if(typeof(config.valign) != 'undefined' && config.valign.length){
10351 td.style += ' vertical-align:' + config.valign + ';';
10354 if(typeof(config.width) != 'undefined'){
10355 td.style += ' width:' + config.width + 'px;';
10359 if(typeof(config.cursor) != 'undefined'){
10360 td.style += ' cursor:' + config.cursor + ';';
10363 if(typeof(config.cls) != 'undefined'){
10364 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10366 if (this.responsive) {
10367 ['xs','sm','md','lg'].map(function(size){
10369 if(typeof(config[size]) == 'undefined'){
10375 if (!config[size]) { // 0 = hidden
10376 // BS 4 '0' is treated as hide that column and below.
10377 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10381 td.cls += ' col-' + size + '-' + config[size] + (
10382 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
10392 row.cellObjects = cellObjects;
10400 onBeforeLoad : function()
10402 this.el.unmask(); // if needed.
10409 this.el.select('tbody', true).first().dom.innerHTML = '';
10412 * Show or hide a row.
10413 * @param {Number} rowIndex to show or hide
10414 * @param {Boolean} state hide
10416 setRowVisibility : function(rowIndex, state)
10418 var bt = this.bodyEl.dom;
10420 var rows = this.el.select('tbody > tr', true).elements;
10422 if(typeof(rows[rowIndex]) == 'undefined'){
10425 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10430 getSelectionModel : function(){
10431 if(!this.selModel){
10432 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10434 return this.selModel;
10437 * Render the Roo.bootstrap object from renderder
10439 renderCellObject : function(r)
10443 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10445 var t = r.cfg.render(r.container);
10448 Roo.each(r.cfg.cn, function(c){
10450 container: t.getChildContainer(),
10453 _this.renderCellObject(child);
10458 * get the Row Index from a dom element.
10459 * @param {Roo.Element} row The row to look for
10460 * @returns {Number} the row
10462 getRowIndex : function(row)
10466 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10477 * get the header TH element for columnIndex
10478 * @param {Number} columnIndex
10479 * @returns {Roo.Element}
10481 getHeaderIndex: function(colIndex)
10483 var cols = this.headEl.select('th', true).elements;
10484 return cols[colIndex];
10487 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10488 * @param {domElement} cell to look for
10489 * @returns {Number} the column
10491 getCellIndex : function(cell)
10493 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10495 return parseInt(id[1], 10);
10500 * Returns the grid's underlying element = used by panel.Grid
10501 * @return {Element} The element
10503 getGridEl : function(){
10507 * Forces a resize - used by panel.Grid
10508 * @return {Element} The element
10510 autoSize : function()
10512 //var ctr = Roo.get(this.container.dom.parentElement);
10513 var ctr = Roo.get(this.el.dom);
10515 var thd = this.getGridEl().select('thead',true).first();
10516 var tbd = this.getGridEl().select('tbody', true).first();
10517 var tfd = this.getGridEl().select('tfoot', true).first();
10519 var cw = ctr.getWidth();
10520 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10524 tbd.setWidth(ctr.getWidth());
10525 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10526 // this needs fixing for various usage - currently only hydra job advers I think..
10528 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10530 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10533 cw = Math.max(cw, this.totalWidth);
10534 this.getGridEl().select('tbody tr',true).setWidth(cw);
10537 // resize 'expandable coloumn?
10539 return; // we doe not have a view in this design..
10542 onBodyScroll: function()
10544 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10546 this.headEl.setStyle({
10547 'position' : 'relative',
10548 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10554 var scrollHeight = this.bodyEl.dom.scrollHeight;
10556 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10558 var height = this.bodyEl.getHeight();
10560 if(scrollHeight - height == scrollTop) {
10562 var total = this.ds.getTotalCount();
10564 if(this.footer.cursor + this.footer.pageSize < total){
10566 this.footer.ds.load({
10568 start : this.footer.cursor + this.footer.pageSize,
10569 limit : this.footer.pageSize
10578 onColumnSplitterMoved : function(i, diff)
10580 this.userResized = true;
10582 var cm = this.colModel;
10584 var w = this.getHeaderIndex(i).getWidth() + diff;
10587 cm.setColumnWidth(i, w, true);
10589 //var cid = cm.getColumnId(i); << not used in this version?
10590 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10592 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10593 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10594 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10596 //this.updateSplitters();
10597 //this.layout(); << ??
10598 this.fireEvent("columnresize", i, w);
10600 onHeaderChange : function()
10602 var header = this.renderHeader();
10603 var table = this.el.select('table', true).first();
10605 this.headEl.remove();
10606 this.headEl = table.createChild(header, this.bodyEl, false);
10608 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10609 e.on('click', this.sort, this);
10612 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10613 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10618 onHiddenChange : function(colModel, colIndex, hidden)
10621 this.cm.setHidden()
10622 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10623 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10625 this.CSS.updateRule(thSelector, "display", "");
10626 this.CSS.updateRule(tdSelector, "display", "");
10629 this.CSS.updateRule(thSelector, "display", "none");
10630 this.CSS.updateRule(tdSelector, "display", "none");
10633 // onload calls initCSS()
10634 this.onHeaderChange();
10638 setColumnWidth: function(col_index, width)
10640 // width = "md-2 xs-2..."
10641 if(!this.colModel.config[col_index]) {
10645 var w = width.split(" ");
10647 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10649 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10652 for(var j = 0; j < w.length; j++) {
10658 var size_cls = w[j].split("-");
10660 if(!Number.isInteger(size_cls[1] * 1)) {
10664 if(!this.colModel.config[col_index][size_cls[0]]) {
10668 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10672 h_row[0].classList.replace(
10673 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10674 "col-"+size_cls[0]+"-"+size_cls[1]
10677 for(var i = 0; i < rows.length; i++) {
10679 var size_cls = w[j].split("-");
10681 if(!Number.isInteger(size_cls[1] * 1)) {
10685 if(!this.colModel.config[col_index][size_cls[0]]) {
10689 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10693 rows[i].classList.replace(
10694 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10695 "col-"+size_cls[0]+"-"+size_cls[1]
10699 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10704 // currently only used to find the split on drag..
10705 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10710 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10711 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10720 * @class Roo.bootstrap.TableCell
10721 * @extends Roo.bootstrap.Component
10722 * @children Roo.bootstrap.Component
10723 * @parent Roo.bootstrap.TableRow
10724 * Bootstrap TableCell class
10726 * @cfg {String} html cell contain text
10727 * @cfg {String} cls cell class
10728 * @cfg {String} tag cell tag (td|th) default td
10729 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10730 * @cfg {String} align Aligns the content in a cell
10731 * @cfg {String} axis Categorizes cells
10732 * @cfg {String} bgcolor Specifies the background color of a cell
10733 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10734 * @cfg {Number} colspan Specifies the number of columns a cell should span
10735 * @cfg {String} headers Specifies one or more header cells a cell is related to
10736 * @cfg {Number} height Sets the height of a cell
10737 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10738 * @cfg {Number} rowspan Sets the number of rows a cell should span
10739 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10740 * @cfg {String} valign Vertical aligns the content in a cell
10741 * @cfg {Number} width Specifies the width of a cell
10744 * Create a new TableCell
10745 * @param {Object} config The config object
10748 Roo.bootstrap.TableCell = function(config){
10749 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10752 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10772 getAutoCreate : function(){
10773 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10780 cfg.tag = this.tag;
10793 cfg.align=this.align
10798 if (this.bgcolor) {
10799 cfg.bgcolor=this.bgcolor
10801 if (this.charoff) {
10802 cfg.charoff=this.charoff
10804 if (this.colspan) {
10805 cfg.colspan=this.colspan
10807 if (this.headers) {
10808 cfg.headers=this.headers
10811 cfg.height=this.height
10814 cfg.nowrap=this.nowrap
10816 if (this.rowspan) {
10817 cfg.rowspan=this.rowspan
10820 cfg.scope=this.scope
10823 cfg.valign=this.valign
10826 cfg.width=this.width
10845 * @class Roo.bootstrap.TableRow
10846 * @extends Roo.bootstrap.Component
10847 * @children Roo.bootstrap.TableCell
10848 * @parent Roo.bootstrap.TableBody
10849 * Bootstrap TableRow class
10850 * @cfg {String} cls row class
10851 * @cfg {String} align Aligns the content in a table row
10852 * @cfg {String} bgcolor Specifies a background color for a table row
10853 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10854 * @cfg {String} valign Vertical aligns the content in a table row
10857 * Create a new TableRow
10858 * @param {Object} config The config object
10861 Roo.bootstrap.TableRow = function(config){
10862 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10865 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10873 getAutoCreate : function(){
10874 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10881 cfg.cls = this.cls;
10884 cfg.align = this.align;
10887 cfg.bgcolor = this.bgcolor;
10890 cfg.charoff = this.charoff;
10893 cfg.valign = this.valign;
10911 * @class Roo.bootstrap.TableBody
10912 * @extends Roo.bootstrap.Component
10913 * @children Roo.bootstrap.TableRow
10914 * @parent Roo.bootstrap.Table
10915 * Bootstrap TableBody class
10916 * @cfg {String} cls element class
10917 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10918 * @cfg {String} align Aligns the content inside the element
10919 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10920 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10923 * Create a new TableBody
10924 * @param {Object} config The config object
10927 Roo.bootstrap.TableBody = function(config){
10928 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10931 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10939 getAutoCreate : function(){
10940 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10950 cfg.tag = this.tag;
10954 cfg.align = this.align;
10957 cfg.charoff = this.charoff;
10960 cfg.valign = this.valign;
10967 // initEvents : function()
10970 // if(!this.store){
10974 // this.store = Roo.factory(this.store, Roo.data);
10975 // this.store.on('load', this.onLoad, this);
10977 // this.store.load();
10981 // onLoad: function ()
10983 // this.fireEvent('load', this);
10993 * Ext JS Library 1.1.1
10994 * Copyright(c) 2006-2007, Ext JS, LLC.
10996 * Originally Released Under LGPL - original licence link has changed is not relivant.
10999 * <script type="text/javascript">
11002 // as we use this in bootstrap.
11003 Roo.namespace('Roo.form');
11005 * @class Roo.form.Action
11006 * Internal Class used to handle form actions
11008 * @param {Roo.form.BasicForm} el The form element or its id
11009 * @param {Object} config Configuration options
11014 // define the action interface
11015 Roo.form.Action = function(form, options){
11017 this.options = options || {};
11020 * Client Validation Failed
11023 Roo.form.Action.CLIENT_INVALID = 'client';
11025 * Server Validation Failed
11028 Roo.form.Action.SERVER_INVALID = 'server';
11030 * Connect to Server Failed
11033 Roo.form.Action.CONNECT_FAILURE = 'connect';
11035 * Reading Data from Server Failed
11038 Roo.form.Action.LOAD_FAILURE = 'load';
11040 Roo.form.Action.prototype = {
11042 failureType : undefined,
11043 response : undefined,
11044 result : undefined,
11046 // interface method
11047 run : function(options){
11051 // interface method
11052 success : function(response){
11056 // interface method
11057 handleResponse : function(response){
11061 // default connection failure
11062 failure : function(response){
11064 this.response = response;
11065 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11066 this.form.afterAction(this, false);
11069 processResponse : function(response){
11070 this.response = response;
11071 if(!response.responseText){
11074 this.result = this.handleResponse(response);
11075 return this.result;
11078 // utility functions used internally
11079 getUrl : function(appendParams){
11080 var url = this.options.url || this.form.url || this.form.el.dom.action;
11082 var p = this.getParams();
11084 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11090 getMethod : function(){
11091 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11094 getParams : function(){
11095 var bp = this.form.baseParams;
11096 var p = this.options.params;
11098 if(typeof p == "object"){
11099 p = Roo.urlEncode(Roo.applyIf(p, bp));
11100 }else if(typeof p == 'string' && bp){
11101 p += '&' + Roo.urlEncode(bp);
11104 p = Roo.urlEncode(bp);
11109 createCallback : function(){
11111 success: this.success,
11112 failure: this.failure,
11114 timeout: (this.form.timeout*1000),
11115 upload: this.form.fileUpload ? this.success : undefined
11120 Roo.form.Action.Submit = function(form, options){
11121 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11124 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11127 haveProgress : false,
11128 uploadComplete : false,
11130 // uploadProgress indicator.
11131 uploadProgress : function()
11133 if (!this.form.progressUrl) {
11137 if (!this.haveProgress) {
11138 Roo.MessageBox.progress("Uploading", "Uploading");
11140 if (this.uploadComplete) {
11141 Roo.MessageBox.hide();
11145 this.haveProgress = true;
11147 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11149 var c = new Roo.data.Connection();
11151 url : this.form.progressUrl,
11156 success : function(req){
11157 //console.log(data);
11161 rdata = Roo.decode(req.responseText)
11163 Roo.log("Invalid data from server..");
11167 if (!rdata || !rdata.success) {
11169 Roo.MessageBox.alert(Roo.encode(rdata));
11172 var data = rdata.data;
11174 if (this.uploadComplete) {
11175 Roo.MessageBox.hide();
11180 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11181 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11184 this.uploadProgress.defer(2000,this);
11187 failure: function(data) {
11188 Roo.log('progress url failed ');
11199 // run get Values on the form, so it syncs any secondary forms.
11200 this.form.getValues();
11202 var o = this.options;
11203 var method = this.getMethod();
11204 var isPost = method == 'POST';
11205 if(o.clientValidation === false || this.form.isValid()){
11207 if (this.form.progressUrl) {
11208 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11209 (new Date() * 1) + '' + Math.random());
11214 Roo.Ajax.request(Roo.apply(this.createCallback(), {
11215 form:this.form.el.dom,
11216 url:this.getUrl(!isPost),
11218 params:isPost ? this.getParams() : null,
11219 isUpload: this.form.fileUpload,
11220 formData : this.form.formData
11223 this.uploadProgress();
11225 }else if (o.clientValidation !== false){ // client validation failed
11226 this.failureType = Roo.form.Action.CLIENT_INVALID;
11227 this.form.afterAction(this, false);
11231 success : function(response)
11233 this.uploadComplete= true;
11234 if (this.haveProgress) {
11235 Roo.MessageBox.hide();
11239 var result = this.processResponse(response);
11240 if(result === true || result.success){
11241 this.form.afterAction(this, true);
11245 this.form.markInvalid(result.errors);
11246 this.failureType = Roo.form.Action.SERVER_INVALID;
11248 this.form.afterAction(this, false);
11250 failure : function(response)
11252 this.uploadComplete= true;
11253 if (this.haveProgress) {
11254 Roo.MessageBox.hide();
11257 this.response = response;
11258 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11259 this.form.afterAction(this, false);
11262 handleResponse : function(response){
11263 if(this.form.errorReader){
11264 var rs = this.form.errorReader.read(response);
11267 for(var i = 0, len = rs.records.length; i < len; i++) {
11268 var r = rs.records[i];
11269 errors[i] = r.data;
11272 if(errors.length < 1){
11276 success : rs.success,
11282 ret = Roo.decode(response.responseText);
11286 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11296 Roo.form.Action.Load = function(form, options){
11297 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11298 this.reader = this.form.reader;
11301 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11306 Roo.Ajax.request(Roo.apply(
11307 this.createCallback(), {
11308 method:this.getMethod(),
11309 url:this.getUrl(false),
11310 params:this.getParams()
11314 success : function(response){
11316 var result = this.processResponse(response);
11317 if(result === true || !result.success || !result.data){
11318 this.failureType = Roo.form.Action.LOAD_FAILURE;
11319 this.form.afterAction(this, false);
11322 this.form.clearInvalid();
11323 this.form.setValues(result.data);
11324 this.form.afterAction(this, true);
11327 handleResponse : function(response){
11328 if(this.form.reader){
11329 var rs = this.form.reader.read(response);
11330 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11332 success : rs.success,
11336 return Roo.decode(response.responseText);
11340 Roo.form.Action.ACTION_TYPES = {
11341 'load' : Roo.form.Action.Load,
11342 'submit' : Roo.form.Action.Submit
11351 * @class Roo.bootstrap.form.Form
11352 * @extends Roo.bootstrap.Component
11353 * @children Roo.bootstrap.Component
11354 * Bootstrap Form class
11355 * @cfg {String} method GET | POST (default POST)
11356 * @cfg {String} labelAlign top | left (default top)
11357 * @cfg {String} align left | right - for navbars
11358 * @cfg {Boolean} loadMask load mask when submit (default true)
11362 * Create a new Form
11363 * @param {Object} config The config object
11367 Roo.bootstrap.form.Form = function(config){
11369 Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11371 Roo.bootstrap.form.Form.popover.apply();
11375 * @event clientvalidation
11376 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11377 * @param {Form} this
11378 * @param {Boolean} valid true if the form has passed client-side validation
11380 clientvalidation: true,
11382 * @event beforeaction
11383 * Fires before any action is performed. Return false to cancel the action.
11384 * @param {Form} this
11385 * @param {Action} action The action to be performed
11387 beforeaction: true,
11389 * @event actionfailed
11390 * Fires when an action fails.
11391 * @param {Form} this
11392 * @param {Action} action The action that failed
11394 actionfailed : true,
11396 * @event actioncomplete
11397 * Fires when an action is completed.
11398 * @param {Form} this
11399 * @param {Action} action The action that completed
11401 actioncomplete : true
11405 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component, {
11408 * @cfg {String} method
11409 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11413 * @cfg {String} url
11414 * The URL to use for form actions if one isn't supplied in the action options.
11417 * @cfg {Boolean} fileUpload
11418 * Set to true if this form is a file upload.
11422 * @cfg {Object} baseParams
11423 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11427 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11431 * @cfg {Sting} align (left|right) for navbar forms
11436 activeAction : null,
11439 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11440 * element by passing it or its id or mask the form itself by passing in true.
11443 waitMsgTarget : false,
11448 * @cfg {Boolean} errorMask (true|false) default false
11453 * @cfg {Number} maskOffset Default 100
11458 * @cfg {Boolean} maskBody
11462 getAutoCreate : function(){
11466 method : this.method || 'POST',
11467 id : this.id || Roo.id(),
11470 if (this.parent().xtype.match(/^Nav/)) {
11471 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11475 if (this.labelAlign == 'left' ) {
11476 cfg.cls += ' form-horizontal';
11482 initEvents : function()
11484 this.el.on('submit', this.onSubmit, this);
11485 // this was added as random key presses on the form where triggering form submit.
11486 this.el.on('keypress', function(e) {
11487 if (e.getCharCode() != 13) {
11490 // we might need to allow it for textareas.. and some other items.
11491 // check e.getTarget().
11493 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11497 Roo.log("keypress blocked");
11499 e.preventDefault();
11505 onSubmit : function(e){
11510 * Returns true if client-side validation on the form is successful.
11513 isValid : function(){
11514 var items = this.getItems();
11516 var target = false;
11518 items.each(function(f){
11524 Roo.log('invalid field: ' + f.name);
11528 if(!target && f.el.isVisible(true)){
11534 if(this.errorMask && !valid){
11535 Roo.bootstrap.form.Form.popover.mask(this, target);
11542 * Returns true if any fields in this form have changed since their original load.
11545 isDirty : function(){
11547 var items = this.getItems();
11548 items.each(function(f){
11558 * Performs a predefined action (submit or load) or custom actions you define on this form.
11559 * @param {String} actionName The name of the action type
11560 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11561 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11562 * accept other config options):
11564 Property Type Description
11565 ---------------- --------------- ----------------------------------------------------------------------------------
11566 url String The url for the action (defaults to the form's url)
11567 method String The form method to use (defaults to the form's method, or POST if not defined)
11568 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11569 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11570 validate the form on the client (defaults to false)
11572 * @return {BasicForm} this
11574 doAction : function(action, options){
11575 if(typeof action == 'string'){
11576 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11578 if(this.fireEvent('beforeaction', this, action) !== false){
11579 this.beforeAction(action);
11580 action.run.defer(100, action);
11586 beforeAction : function(action){
11587 var o = action.options;
11592 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11594 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11597 // not really supported yet.. ??
11599 //if(this.waitMsgTarget === true){
11600 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11601 //}else if(this.waitMsgTarget){
11602 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11603 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11605 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11611 afterAction : function(action, success){
11612 this.activeAction = null;
11613 var o = action.options;
11618 Roo.get(document.body).unmask();
11624 //if(this.waitMsgTarget === true){
11625 // this.el.unmask();
11626 //}else if(this.waitMsgTarget){
11627 // this.waitMsgTarget.unmask();
11629 // Roo.MessageBox.updateProgress(1);
11630 // Roo.MessageBox.hide();
11637 Roo.callback(o.success, o.scope, [this, action]);
11638 this.fireEvent('actioncomplete', this, action);
11642 // failure condition..
11643 // we have a scenario where updates need confirming.
11644 // eg. if a locking scenario exists..
11645 // we look for { errors : { needs_confirm : true }} in the response.
11647 (typeof(action.result) != 'undefined') &&
11648 (typeof(action.result.errors) != 'undefined') &&
11649 (typeof(action.result.errors.needs_confirm) != 'undefined')
11652 Roo.log("not supported yet");
11655 Roo.MessageBox.confirm(
11656 "Change requires confirmation",
11657 action.result.errorMsg,
11662 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11672 Roo.callback(o.failure, o.scope, [this, action]);
11673 // show an error message if no failed handler is set..
11674 if (!this.hasListener('actionfailed')) {
11675 Roo.log("need to add dialog support");
11677 Roo.MessageBox.alert("Error",
11678 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11679 action.result.errorMsg :
11680 "Saving Failed, please check your entries or try again"
11685 this.fireEvent('actionfailed', this, action);
11690 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11691 * @param {String} id The value to search for
11694 findField : function(id){
11695 var items = this.getItems();
11696 var field = items.get(id);
11698 items.each(function(f){
11699 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11706 return field || null;
11709 * Mark fields in this form invalid in bulk.
11710 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11711 * @return {BasicForm} this
11713 markInvalid : function(errors){
11714 if(errors instanceof Array){
11715 for(var i = 0, len = errors.length; i < len; i++){
11716 var fieldError = errors[i];
11717 var f = this.findField(fieldError.id);
11719 f.markInvalid(fieldError.msg);
11725 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11726 field.markInvalid(errors[id]);
11730 //Roo.each(this.childForms || [], function (f) {
11731 // f.markInvalid(errors);
11738 * Set values for fields in this form in bulk.
11739 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11740 * @return {BasicForm} this
11742 setValues : function(values){
11743 if(values instanceof Array){ // array of objects
11744 for(var i = 0, len = values.length; i < len; i++){
11746 var f = this.findField(v.id);
11748 f.setValue(v.value);
11749 if(this.trackResetOnLoad){
11750 f.originalValue = f.getValue();
11754 }else{ // object hash
11757 if(typeof values[id] != 'function' && (field = this.findField(id))){
11759 if (field.setFromData &&
11760 field.valueField &&
11761 field.displayField &&
11762 // combos' with local stores can
11763 // be queried via setValue()
11764 // to set their value..
11765 (field.store && !field.store.isLocal)
11769 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11770 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11771 field.setFromData(sd);
11773 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11775 field.setFromData(values);
11778 field.setValue(values[id]);
11782 if(this.trackResetOnLoad){
11783 field.originalValue = field.getValue();
11789 //Roo.each(this.childForms || [], function (f) {
11790 // f.setValues(values);
11797 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11798 * they are returned as an array.
11799 * @param {Boolean} asString
11802 getValues : function(asString){
11803 //if (this.childForms) {
11804 // copy values from the child forms
11805 // Roo.each(this.childForms, function (f) {
11806 // this.setValues(f.getValues());
11812 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11813 if(asString === true){
11816 return Roo.urlDecode(fs);
11820 * Returns the fields in this form as an object with key/value pairs.
11821 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11824 getFieldValues : function(with_hidden)
11826 var items = this.getItems();
11828 items.each(function(f){
11830 if (!f.getName()) {
11834 var v = f.getValue();
11836 if (f.inputType =='radio') {
11837 if (typeof(ret[f.getName()]) == 'undefined') {
11838 ret[f.getName()] = ''; // empty..
11841 if (!f.el.dom.checked) {
11845 v = f.el.dom.value;
11849 if(f.xtype == 'MoneyField'){
11850 ret[f.currencyName] = f.getCurrency();
11853 // not sure if this supported any more..
11854 if ((typeof(v) == 'object') && f.getRawValue) {
11855 v = f.getRawValue() ; // dates..
11857 // combo boxes where name != hiddenName...
11858 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11859 ret[f.name] = f.getRawValue();
11861 ret[f.getName()] = v;
11868 * Clears all invalid messages in this form.
11869 * @return {BasicForm} this
11871 clearInvalid : function(){
11872 var items = this.getItems();
11874 items.each(function(f){
11882 * Resets this form.
11883 * @return {BasicForm} this
11885 reset : function(){
11886 var items = this.getItems();
11887 items.each(function(f){
11891 Roo.each(this.childForms || [], function (f) {
11899 getItems : function()
11901 var r=new Roo.util.MixedCollection(false, function(o){
11902 return o.id || (o.id = Roo.id());
11904 var iter = function(el) {
11911 Roo.each(el.items,function(e) {
11920 hideFields : function(items)
11922 Roo.each(items, function(i){
11924 var f = this.findField(i);
11935 showFields : function(items)
11937 Roo.each(items, function(i){
11939 var f = this.findField(i);
11952 Roo.apply(Roo.bootstrap.form.Form, {
11968 intervalID : false,
11974 if(this.isApplied){
11979 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11980 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11981 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11982 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11985 this.maskEl.top.enableDisplayMode("block");
11986 this.maskEl.left.enableDisplayMode("block");
11987 this.maskEl.bottom.enableDisplayMode("block");
11988 this.maskEl.right.enableDisplayMode("block");
11990 this.toolTip = new Roo.bootstrap.Tooltip({
11991 cls : 'roo-form-error-popover',
11993 'left' : ['r-l', [-2,0], 'right'],
11994 'right' : ['l-r', [2,0], 'left'],
11995 'bottom' : ['tl-bl', [0,2], 'top'],
11996 'top' : [ 'bl-tl', [0,-2], 'bottom']
12000 this.toolTip.render(Roo.get(document.body));
12002 this.toolTip.el.enableDisplayMode("block");
12004 Roo.get(document.body).on('click', function(){
12008 Roo.get(document.body).on('touchstart', function(){
12012 this.isApplied = true
12015 mask : function(form, target)
12019 this.target = target;
12021 if(!this.form.errorMask || !target.el){
12025 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12027 Roo.log(scrollable);
12029 var ot = this.target.el.calcOffsetsTo(scrollable);
12031 var scrollTo = ot[1] - this.form.maskOffset;
12033 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12035 scrollable.scrollTo('top', scrollTo);
12037 var box = this.target.el.getBox();
12039 var zIndex = Roo.bootstrap.Modal.zIndex++;
12042 this.maskEl.top.setStyle('position', 'absolute');
12043 this.maskEl.top.setStyle('z-index', zIndex);
12044 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12045 this.maskEl.top.setLeft(0);
12046 this.maskEl.top.setTop(0);
12047 this.maskEl.top.show();
12049 this.maskEl.left.setStyle('position', 'absolute');
12050 this.maskEl.left.setStyle('z-index', zIndex);
12051 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12052 this.maskEl.left.setLeft(0);
12053 this.maskEl.left.setTop(box.y - this.padding);
12054 this.maskEl.left.show();
12056 this.maskEl.bottom.setStyle('position', 'absolute');
12057 this.maskEl.bottom.setStyle('z-index', zIndex);
12058 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12059 this.maskEl.bottom.setLeft(0);
12060 this.maskEl.bottom.setTop(box.bottom + this.padding);
12061 this.maskEl.bottom.show();
12063 this.maskEl.right.setStyle('position', 'absolute');
12064 this.maskEl.right.setStyle('z-index', zIndex);
12065 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12066 this.maskEl.right.setLeft(box.right + this.padding);
12067 this.maskEl.right.setTop(box.y - this.padding);
12068 this.maskEl.right.show();
12070 this.toolTip.bindEl = this.target.el;
12072 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12074 var tip = this.target.blankText;
12076 if(this.target.getValue() !== '' ) {
12078 if (this.target.invalidText.length) {
12079 tip = this.target.invalidText;
12080 } else if (this.target.regexText.length){
12081 tip = this.target.regexText;
12085 this.toolTip.show(tip);
12087 this.intervalID = window.setInterval(function() {
12088 Roo.bootstrap.form.Form.popover.unmask();
12091 window.onwheel = function(){ return false;};
12093 (function(){ this.isMasked = true; }).defer(500, this);
12097 unmask : function()
12099 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12103 this.maskEl.top.setStyle('position', 'absolute');
12104 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12105 this.maskEl.top.hide();
12107 this.maskEl.left.setStyle('position', 'absolute');
12108 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12109 this.maskEl.left.hide();
12111 this.maskEl.bottom.setStyle('position', 'absolute');
12112 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12113 this.maskEl.bottom.hide();
12115 this.maskEl.right.setStyle('position', 'absolute');
12116 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12117 this.maskEl.right.hide();
12119 this.toolTip.hide();
12121 this.toolTip.el.hide();
12123 window.onwheel = function(){ return true;};
12125 if(this.intervalID){
12126 window.clearInterval(this.intervalID);
12127 this.intervalID = false;
12130 this.isMasked = false;
12140 * Ext JS Library 1.1.1
12141 * Copyright(c) 2006-2007, Ext JS, LLC.
12143 * Originally Released Under LGPL - original licence link has changed is not relivant.
12146 * <script type="text/javascript">
12149 * @class Roo.form.VTypes
12150 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12153 Roo.form.VTypes = function(){
12154 // closure these in so they are only created once.
12155 var alpha = /^[a-zA-Z_]+$/;
12156 var alphanum = /^[a-zA-Z0-9_]+$/;
12157 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12158 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12160 // All these messages and functions are configurable
12163 * The function used to validate email addresses
12164 * @param {String} value The email address
12166 'email' : function(v){
12167 return email.test(v);
12170 * The error text to display when the email validation function returns false
12173 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12175 * The keystroke filter mask to be applied on email input
12178 'emailMask' : /[a-z0-9_\.\-@]/i,
12181 * The function used to validate URLs
12182 * @param {String} value The URL
12184 'url' : function(v){
12185 return url.test(v);
12188 * The error text to display when the url validation function returns false
12191 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12194 * The function used to validate alpha values
12195 * @param {String} value The value
12197 'alpha' : function(v){
12198 return alpha.test(v);
12201 * The error text to display when the alpha validation function returns false
12204 'alphaText' : 'This field should only contain letters and _',
12206 * The keystroke filter mask to be applied on alpha input
12209 'alphaMask' : /[a-z_]/i,
12212 * The function used to validate alphanumeric values
12213 * @param {String} value The value
12215 'alphanum' : function(v){
12216 return alphanum.test(v);
12219 * The error text to display when the alphanumeric validation function returns false
12222 'alphanumText' : 'This field should only contain letters, numbers and _',
12224 * The keystroke filter mask to be applied on alphanumeric input
12227 'alphanumMask' : /[a-z0-9_]/i
12237 * @class Roo.bootstrap.form.Input
12238 * @extends Roo.bootstrap.Component
12239 * Bootstrap Input class
12240 * @cfg {Boolean} disabled is it disabled
12241 * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)
12242 * @cfg {String} name name of the input
12243 * @cfg {string} fieldLabel - the label associated
12244 * @cfg {string} placeholder - placeholder to put in text.
12245 * @cfg {string} before - input group add on before
12246 * @cfg {string} after - input group add on after
12247 * @cfg {string} size - (lg|sm) or leave empty..
12248 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12249 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12250 * @cfg {Number} md colspan out of 12 for computer-sized screens
12251 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12252 * @cfg {string} value default value of the input
12253 * @cfg {Number} labelWidth set the width of label
12254 * @cfg {Number} labellg set the width of label (1-12)
12255 * @cfg {Number} labelmd set the width of label (1-12)
12256 * @cfg {Number} labelsm set the width of label (1-12)
12257 * @cfg {Number} labelxs set the width of label (1-12)
12258 * @cfg {String} labelAlign (top|left)
12259 * @cfg {Boolean} readOnly Specifies that the field should be read-only
12260 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12261 * @cfg {String} indicatorpos (left|right) default left
12262 * @cfg {String} capture (user|camera) use for file input only. (default empty)
12263 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12264 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12265 * @cfg {Roo.bootstrap.Button} before Button to show before
12266 * @cfg {Roo.bootstrap.Button} afterButton to show before
12267 * @cfg {String} align (left|center|right) Default left
12268 * @cfg {Boolean} forceFeedback (true|false) Default false
12271 * Create a new Input
12272 * @param {Object} config The config object
12275 Roo.bootstrap.form.Input = function(config){
12277 Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12282 * Fires when this field receives input focus.
12283 * @param {Roo.form.Field} this
12288 * Fires when this field loses input focus.
12289 * @param {Roo.form.Field} this
12293 * @event specialkey
12294 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
12295 * {@link Roo.EventObject#getKey} to determine which key was pressed.
12296 * @param {Roo.form.Field} this
12297 * @param {Roo.EventObject} e The event object
12302 * Fires just before the field blurs if the field value has changed.
12303 * @param {Roo.form.Field} this
12304 * @param {Mixed} newValue The new value
12305 * @param {Mixed} oldValue The original value
12310 * Fires after the field has been marked as invalid.
12311 * @param {Roo.form.Field} this
12312 * @param {String} msg The validation message
12317 * Fires after the field has been validated with no errors.
12318 * @param {Roo.form.Field} this
12323 * Fires after the key up
12324 * @param {Roo.form.Field} this
12325 * @param {Roo.EventObject} e The event Object
12330 * Fires after the user pastes into input
12331 * @param {Roo.form.Field} this
12332 * @param {Roo.EventObject} e The event Object
12338 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component, {
12340 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12341 automatic validation (defaults to "keyup").
12343 validationEvent : "keyup",
12345 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12347 validateOnBlur : true,
12349 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12351 validationDelay : 250,
12353 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12355 focusClass : "x-form-focus", // not needed???
12359 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12361 invalidClass : "has-warning",
12364 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12366 validClass : "has-success",
12369 * @cfg {Boolean} hasFeedback (true|false) default true
12371 hasFeedback : true,
12374 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12376 invalidFeedbackClass : "glyphicon-warning-sign",
12379 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12381 validFeedbackClass : "glyphicon-ok",
12384 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12386 selectOnFocus : false,
12389 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12393 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12398 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12400 disableKeyFilter : false,
12403 * @cfg {Boolean} disabled True to disable the field (defaults to false).
12407 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12411 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12413 blankText : "Please complete this mandatory field",
12416 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12420 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12422 maxLength : Number.MAX_VALUE,
12424 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12426 minLengthText : "The minimum length for this field is {0}",
12428 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12430 maxLengthText : "The maximum length for this field is {0}",
12434 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12435 * If available, this function will be called only after the basic validators all return true, and will be passed the
12436 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12440 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12441 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12442 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
12446 * @cfg {String} regexText -- Depricated - use Invalid Text
12451 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12457 autocomplete: false,
12461 inputType : 'text',
12464 placeholder: false,
12469 preventMark: false,
12470 isFormField : true,
12473 labelAlign : false,
12476 formatedValue : false,
12477 forceFeedback : false,
12479 indicatorpos : 'left',
12489 parentLabelAlign : function()
12492 while (parent.parent()) {
12493 parent = parent.parent();
12494 if (typeof(parent.labelAlign) !='undefined') {
12495 return parent.labelAlign;
12502 getAutoCreate : function()
12504 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12510 if(this.inputType != 'hidden'){
12511 cfg.cls = 'form-group' //input-group
12517 type : this.inputType,
12518 value : this.value,
12519 cls : 'form-control',
12520 placeholder : this.placeholder || '',
12521 autocomplete : this.autocomplete || 'new-password'
12523 if (this.inputType == 'file') {
12524 input.style = 'overflow:hidden'; // why not in CSS?
12527 if(this.capture.length){
12528 input.capture = this.capture;
12531 if(this.accept.length){
12532 input.accept = this.accept + "/*";
12536 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12539 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12540 input.maxLength = this.maxLength;
12543 if (this.disabled) {
12544 input.disabled=true;
12547 if (this.readOnly) {
12548 input.readonly=true;
12552 input.name = this.name;
12556 input.cls += ' input-' + this.size;
12560 ['xs','sm','md','lg'].map(function(size){
12561 if (settings[size]) {
12562 cfg.cls += ' col-' + size + '-' + settings[size];
12566 var inputblock = input;
12570 cls: 'glyphicon form-control-feedback'
12573 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12576 cls : 'has-feedback',
12584 if (this.before || this.after) {
12587 cls : 'input-group',
12591 if (this.before && typeof(this.before) == 'string') {
12593 inputblock.cn.push({
12595 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12599 if (this.before && typeof(this.before) == 'object') {
12600 this.before = Roo.factory(this.before);
12602 inputblock.cn.push({
12604 cls : 'roo-input-before input-group-prepend input-group-' +
12605 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12609 inputblock.cn.push(input);
12611 if (this.after && typeof(this.after) == 'string') {
12612 inputblock.cn.push({
12614 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12618 if (this.after && typeof(this.after) == 'object') {
12619 this.after = Roo.factory(this.after);
12621 inputblock.cn.push({
12623 cls : 'roo-input-after input-group-append input-group-' +
12624 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12628 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12629 inputblock.cls += ' has-feedback';
12630 inputblock.cn.push(feedback);
12635 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12636 tooltip : 'This field is required'
12638 if (this.allowBlank ) {
12639 indicator.style = this.allowBlank ? ' display:none' : '';
12641 if (align ==='left' && this.fieldLabel.length) {
12643 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12650 cls : 'control-label col-form-label',
12651 html : this.fieldLabel
12662 var labelCfg = cfg.cn[1];
12663 var contentCfg = cfg.cn[2];
12665 if(this.indicatorpos == 'right'){
12670 cls : 'control-label col-form-label',
12674 html : this.fieldLabel
12688 labelCfg = cfg.cn[0];
12689 contentCfg = cfg.cn[1];
12693 if(this.labelWidth > 12){
12694 labelCfg.style = "width: " + this.labelWidth + 'px';
12697 if(this.labelWidth < 13 && this.labelmd == 0){
12698 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12701 if(this.labellg > 0){
12702 labelCfg.cls += ' col-lg-' + this.labellg;
12703 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12706 if(this.labelmd > 0){
12707 labelCfg.cls += ' col-md-' + this.labelmd;
12708 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12711 if(this.labelsm > 0){
12712 labelCfg.cls += ' col-sm-' + this.labelsm;
12713 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12716 if(this.labelxs > 0){
12717 labelCfg.cls += ' col-xs-' + this.labelxs;
12718 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12722 } else if ( this.fieldLabel.length) {
12729 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12730 tooltip : 'This field is required',
12731 style : this.allowBlank ? ' display:none' : ''
12735 //cls : 'input-group-addon',
12736 html : this.fieldLabel
12744 if(this.indicatorpos == 'right'){
12749 //cls : 'input-group-addon',
12750 html : this.fieldLabel
12755 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12756 tooltip : 'This field is required',
12757 style : this.allowBlank ? ' display:none' : ''
12777 if (this.parentType === 'Navbar' && this.parent().bar) {
12778 cfg.cls += ' navbar-form';
12781 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12782 // on BS4 we do this only if not form
12783 cfg.cls += ' navbar-form';
12791 * return the real input element.
12793 inputEl: function ()
12795 return this.el.select('input.form-control',true).first();
12798 tooltipEl : function()
12800 return this.inputEl();
12803 indicatorEl : function()
12805 if (Roo.bootstrap.version == 4) {
12806 return false; // not enabled in v4 yet.
12809 var indicator = this.el.select('i.roo-required-indicator',true).first();
12819 setDisabled : function(v)
12821 var i = this.inputEl().dom;
12823 i.removeAttribute('disabled');
12827 i.setAttribute('disabled','true');
12829 initEvents : function()
12832 this.inputEl().on("keydown" , this.fireKey, this);
12833 this.inputEl().on("focus", this.onFocus, this);
12834 this.inputEl().on("blur", this.onBlur, this);
12836 this.inputEl().relayEvent('keyup', this);
12837 this.inputEl().relayEvent('paste', this);
12839 this.indicator = this.indicatorEl();
12841 if(this.indicator){
12842 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12845 // reference to original value for reset
12846 this.originalValue = this.getValue();
12847 //Roo.form.TextField.superclass.initEvents.call(this);
12848 if(this.validationEvent == 'keyup'){
12849 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12850 this.inputEl().on('keyup', this.filterValidation, this);
12852 else if(this.validationEvent !== false){
12853 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12856 if(this.selectOnFocus){
12857 this.on("focus", this.preFocus, this);
12860 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12861 this.inputEl().on("keypress", this.filterKeys, this);
12863 this.inputEl().relayEvent('keypress', this);
12866 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12867 this.el.on("click", this.autoSize, this);
12870 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12871 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12874 if (typeof(this.before) == 'object') {
12875 this.before.render(this.el.select('.roo-input-before',true).first());
12877 if (typeof(this.after) == 'object') {
12878 this.after.render(this.el.select('.roo-input-after',true).first());
12881 this.inputEl().on('change', this.onChange, this);
12884 filterValidation : function(e){
12885 if(!e.isNavKeyPress()){
12886 this.validationTask.delay(this.validationDelay);
12890 * Validates the field value
12891 * @return {Boolean} True if the value is valid, else false
12893 validate : function(){
12894 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12895 if(this.disabled || this.validateValue(this.getRawValue())){
12900 this.markInvalid();
12906 * Validates a value according to the field's validation rules and marks the field as invalid
12907 * if the validation fails
12908 * @param {Mixed} value The value to validate
12909 * @return {Boolean} True if the value is valid, else false
12911 validateValue : function(value)
12913 if(this.getVisibilityEl().hasClass('hidden')){
12917 if(value.length < 1) { // if it's blank
12918 if(this.allowBlank){
12924 if(value.length < this.minLength){
12927 if(value.length > this.maxLength){
12931 var vt = Roo.form.VTypes;
12932 if(!vt[this.vtype](value, this)){
12936 if(typeof this.validator == "function"){
12937 var msg = this.validator(value);
12941 if (typeof(msg) == 'string') {
12942 this.invalidText = msg;
12946 if(this.regex && !this.regex.test(value)){
12954 fireKey : function(e){
12955 //Roo.log('field ' + e.getKey());
12956 if(e.isNavKeyPress()){
12957 this.fireEvent("specialkey", this, e);
12960 focus : function (selectText){
12962 this.inputEl().focus();
12963 if(selectText === true){
12964 this.inputEl().dom.select();
12970 onFocus : function(){
12971 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12972 // this.el.addClass(this.focusClass);
12974 if(!this.hasFocus){
12975 this.hasFocus = true;
12976 this.startValue = this.getValue();
12977 this.fireEvent("focus", this);
12981 beforeBlur : Roo.emptyFn,
12985 onBlur : function(){
12987 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12988 //this.el.removeClass(this.focusClass);
12990 this.hasFocus = false;
12991 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12994 var v = this.getValue();
12995 if(String(v) !== String(this.startValue)){
12996 this.fireEvent('change', this, v, this.startValue);
12998 this.fireEvent("blur", this);
13001 onChange : function(e)
13003 var v = this.getValue();
13004 if(String(v) !== String(this.startValue)){
13005 this.fireEvent('change', this, v, this.startValue);
13011 * Resets the current field value to the originally loaded value and clears any validation messages
13013 reset : function(){
13014 this.setValue(this.originalValue);
13018 * Returns the name of the field
13019 * @return {Mixed} name The name field
13021 getName: function(){
13025 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
13026 * @return {Mixed} value The field value
13028 getValue : function(){
13030 var v = this.inputEl().getValue();
13035 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
13036 * @return {Mixed} value The field value
13038 getRawValue : function(){
13039 var v = this.inputEl().getValue();
13045 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
13046 * @param {Mixed} value The value to set
13048 setRawValue : function(v){
13049 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13052 selectText : function(start, end){
13053 var v = this.getRawValue();
13055 start = start === undefined ? 0 : start;
13056 end = end === undefined ? v.length : end;
13057 var d = this.inputEl().dom;
13058 if(d.setSelectionRange){
13059 d.setSelectionRange(start, end);
13060 }else if(d.createTextRange){
13061 var range = d.createTextRange();
13062 range.moveStart("character", start);
13063 range.moveEnd("character", v.length-end);
13070 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
13071 * @param {Mixed} value The value to set
13073 setValue : function(v){
13076 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13082 processValue : function(value){
13083 if(this.stripCharsRe){
13084 var newValue = value.replace(this.stripCharsRe, '');
13085 if(newValue !== value){
13086 this.setRawValue(newValue);
13093 preFocus : function(){
13095 if(this.selectOnFocus){
13096 this.inputEl().dom.select();
13099 filterKeys : function(e){
13100 var k = e.getKey();
13101 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13104 var c = e.getCharCode(), cc = String.fromCharCode(c);
13105 if(Roo.isIE && (e.isSpecialKey() || !cc)){
13108 if(!this.maskRe.test(cc)){
13113 * Clear any invalid styles/messages for this field
13115 clearInvalid : function(){
13117 if(!this.el || this.preventMark){ // not rendered
13122 this.el.removeClass([this.invalidClass, 'is-invalid']);
13124 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13126 var feedback = this.el.select('.form-control-feedback', true).first();
13129 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13134 if(this.indicator){
13135 this.indicator.removeClass('visible');
13136 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13139 this.fireEvent('valid', this);
13143 * Mark this field as valid
13145 markValid : function()
13147 if(!this.el || this.preventMark){ // not rendered...
13151 this.el.removeClass([this.invalidClass, this.validClass]);
13152 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13154 var feedback = this.el.select('.form-control-feedback', true).first();
13157 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13160 if(this.indicator){
13161 this.indicator.removeClass('visible');
13162 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13170 if(this.allowBlank && !this.getRawValue().length){
13173 if (Roo.bootstrap.version == 3) {
13174 this.el.addClass(this.validClass);
13176 this.inputEl().addClass('is-valid');
13179 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13181 var feedback = this.el.select('.form-control-feedback', true).first();
13184 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13185 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13190 this.fireEvent('valid', this);
13194 * Mark this field as invalid
13195 * @param {String} msg The validation message
13197 markInvalid : function(msg)
13199 if(!this.el || this.preventMark){ // not rendered
13203 this.el.removeClass([this.invalidClass, this.validClass]);
13204 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13206 var feedback = this.el.select('.form-control-feedback', true).first();
13209 this.el.select('.form-control-feedback', true).first().removeClass(
13210 [this.invalidFeedbackClass, this.validFeedbackClass]);
13217 if(this.allowBlank && !this.getRawValue().length){
13221 if(this.indicator){
13222 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13223 this.indicator.addClass('visible');
13225 if (Roo.bootstrap.version == 3) {
13226 this.el.addClass(this.invalidClass);
13228 this.inputEl().addClass('is-invalid');
13233 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13235 var feedback = this.el.select('.form-control-feedback', true).first();
13238 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13240 if(this.getValue().length || this.forceFeedback){
13241 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13248 this.fireEvent('invalid', this, msg);
13251 SafariOnKeyDown : function(event)
13253 // this is a workaround for a password hang bug on chrome/ webkit.
13254 if (this.inputEl().dom.type != 'password') {
13258 var isSelectAll = false;
13260 if(this.inputEl().dom.selectionEnd > 0){
13261 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13263 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13264 event.preventDefault();
13269 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13271 event.preventDefault();
13272 // this is very hacky as keydown always get's upper case.
13274 var cc = String.fromCharCode(event.getCharCode());
13275 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
13279 adjustWidth : function(tag, w){
13280 tag = tag.toLowerCase();
13281 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13282 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13283 if(tag == 'input'){
13286 if(tag == 'textarea'){
13289 }else if(Roo.isOpera){
13290 if(tag == 'input'){
13293 if(tag == 'textarea'){
13301 setFieldLabel : function(v)
13303 if(!this.rendered){
13307 if(this.indicatorEl()){
13308 var ar = this.el.select('label > span',true);
13310 if (ar.elements.length) {
13311 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13312 this.fieldLabel = v;
13316 var br = this.el.select('label',true);
13318 if(br.elements.length) {
13319 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13320 this.fieldLabel = v;
13324 Roo.log('Cannot Found any of label > span || label in input');
13328 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13329 this.fieldLabel = v;
13344 * @class Roo.bootstrap.form.TextArea
13345 * @extends Roo.bootstrap.form.Input
13346 * Bootstrap TextArea class
13347 * @cfg {Number} cols Specifies the visible width of a text area
13348 * @cfg {Number} rows Specifies the visible number of lines in a text area
13349 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13350 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13351 * @cfg {string} html text
13354 * Create a new TextArea
13355 * @param {Object} config The config object
13358 Roo.bootstrap.form.TextArea = function(config){
13359 Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13363 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input, {
13373 getAutoCreate : function(){
13375 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13381 if(this.inputType != 'hidden'){
13382 cfg.cls = 'form-group' //input-group
13390 value : this.value || '',
13391 html: this.html || '',
13392 cls : 'form-control',
13393 placeholder : this.placeholder || ''
13397 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13398 input.maxLength = this.maxLength;
13402 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13406 input.cols = this.cols;
13409 if (this.readOnly) {
13410 input.readonly = true;
13414 input.name = this.name;
13418 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13422 ['xs','sm','md','lg'].map(function(size){
13423 if (settings[size]) {
13424 cfg.cls += ' col-' + size + '-' + settings[size];
13428 var inputblock = input;
13430 if(this.hasFeedback && !this.allowBlank){
13434 cls: 'glyphicon form-control-feedback'
13438 cls : 'has-feedback',
13447 if (this.before || this.after) {
13450 cls : 'input-group',
13454 inputblock.cn.push({
13456 cls : 'input-group-addon',
13461 inputblock.cn.push(input);
13463 if(this.hasFeedback && !this.allowBlank){
13464 inputblock.cls += ' has-feedback';
13465 inputblock.cn.push(feedback);
13469 inputblock.cn.push({
13471 cls : 'input-group-addon',
13478 if (align ==='left' && this.fieldLabel.length) {
13483 cls : 'control-label',
13484 html : this.fieldLabel
13495 if(this.labelWidth > 12){
13496 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13499 if(this.labelWidth < 13 && this.labelmd == 0){
13500 this.labelmd = this.labelWidth;
13503 if(this.labellg > 0){
13504 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13505 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13508 if(this.labelmd > 0){
13509 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13510 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13513 if(this.labelsm > 0){
13514 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13515 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13518 if(this.labelxs > 0){
13519 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13520 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13523 } else if ( this.fieldLabel.length) {
13528 //cls : 'input-group-addon',
13529 html : this.fieldLabel
13547 if (this.disabled) {
13548 input.disabled=true;
13555 * return the real textarea element.
13557 inputEl: function ()
13559 return this.el.select('textarea.form-control',true).first();
13563 * Clear any invalid styles/messages for this field
13565 clearInvalid : function()
13568 if(!this.el || this.preventMark){ // not rendered
13572 var label = this.el.select('label', true).first();
13573 var icon = this.el.select('i.fa-star', true).first();
13578 this.el.removeClass( this.validClass);
13579 this.inputEl().removeClass('is-invalid');
13581 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13583 var feedback = this.el.select('.form-control-feedback', true).first();
13586 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13591 this.fireEvent('valid', this);
13595 * Mark this field as valid
13597 markValid : function()
13599 if(!this.el || this.preventMark){ // not rendered
13603 this.el.removeClass([this.invalidClass, this.validClass]);
13604 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13606 var feedback = this.el.select('.form-control-feedback', true).first();
13609 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13612 if(this.disabled || this.allowBlank){
13616 var label = this.el.select('label', true).first();
13617 var icon = this.el.select('i.fa-star', true).first();
13622 if (Roo.bootstrap.version == 3) {
13623 this.el.addClass(this.validClass);
13625 this.inputEl().addClass('is-valid');
13629 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13631 var feedback = this.el.select('.form-control-feedback', true).first();
13634 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13635 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13640 this.fireEvent('valid', this);
13644 * Mark this field as invalid
13645 * @param {String} msg The validation message
13647 markInvalid : function(msg)
13649 if(!this.el || this.preventMark){ // not rendered
13653 this.el.removeClass([this.invalidClass, this.validClass]);
13654 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13656 var feedback = this.el.select('.form-control-feedback', true).first();
13659 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13662 if(this.disabled || this.allowBlank){
13666 var label = this.el.select('label', true).first();
13667 var icon = this.el.select('i.fa-star', true).first();
13669 if(!this.getValue().length && label && !icon){
13670 this.el.createChild({
13672 cls : 'text-danger fa fa-lg fa-star',
13673 tooltip : 'This field is required',
13674 style : 'margin-right:5px;'
13678 if (Roo.bootstrap.version == 3) {
13679 this.el.addClass(this.invalidClass);
13681 this.inputEl().addClass('is-invalid');
13684 // fixme ... this may be depricated need to test..
13685 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13687 var feedback = this.el.select('.form-control-feedback', true).first();
13690 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13692 if(this.getValue().length || this.forceFeedback){
13693 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13700 this.fireEvent('invalid', this, msg);
13708 * trigger field - base class for combo..
13713 * @class Roo.bootstrap.form.TriggerField
13714 * @extends Roo.bootstrap.form.Input
13715 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13716 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13717 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13718 * for which you can provide a custom implementation. For example:
13720 var trigger = new Roo.bootstrap.form.TriggerField();
13721 trigger.onTriggerClick = myTriggerFn;
13722 trigger.applyTo('my-field');
13725 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13726 * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13727 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13728 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13729 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13732 * Create a new TriggerField.
13733 * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13734 * to the base TextField)
13736 Roo.bootstrap.form.TriggerField = function(config){
13737 this.mimicing = false;
13738 Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13741 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input, {
13743 * @cfg {String} triggerClass A CSS class to apply to the trigger
13746 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13751 * @cfg {Boolean} removable (true|false) special filter default false
13755 /** @cfg {Boolean} grow @hide */
13756 /** @cfg {Number} growMin @hide */
13757 /** @cfg {Number} growMax @hide */
13763 autoSize: Roo.emptyFn,
13767 deferHeight : true,
13770 actionMode : 'wrap',
13775 getAutoCreate : function(){
13777 var align = this.labelAlign || this.parentLabelAlign();
13782 cls: 'form-group' //input-group
13789 type : this.inputType,
13790 cls : 'form-control',
13791 autocomplete: 'new-password',
13792 placeholder : this.placeholder || ''
13796 input.name = this.name;
13799 input.cls += ' input-' + this.size;
13802 if (this.disabled) {
13803 input.disabled=true;
13806 var inputblock = input;
13808 if(this.hasFeedback && !this.allowBlank){
13812 cls: 'glyphicon form-control-feedback'
13815 if(this.removable && !this.editable ){
13817 cls : 'has-feedback',
13823 cls : 'roo-combo-removable-btn close'
13830 cls : 'has-feedback',
13839 if(this.removable && !this.editable ){
13841 cls : 'roo-removable',
13847 cls : 'roo-combo-removable-btn close'
13854 if (this.before || this.after) {
13857 cls : 'input-group',
13861 inputblock.cn.push({
13863 cls : 'input-group-addon input-group-prepend input-group-text',
13868 inputblock.cn.push(input);
13870 if(this.hasFeedback && !this.allowBlank){
13871 inputblock.cls += ' has-feedback';
13872 inputblock.cn.push(feedback);
13876 inputblock.cn.push({
13878 cls : 'input-group-addon input-group-append input-group-text',
13887 var ibwrap = inputblock;
13892 cls: 'roo-select2-choices',
13896 cls: 'roo-select2-search-field',
13908 cls: 'roo-select2-container input-group',
13913 cls: 'form-hidden-field'
13919 if(!this.multiple && this.showToggleBtn){
13925 if (this.caret != false) {
13928 cls: 'fa fa-' + this.caret
13935 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13937 Roo.bootstrap.version == 3 ? caret : '',
13940 cls: 'combobox-clear',
13954 combobox.cls += ' roo-select2-container-multi';
13958 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13959 tooltip : 'This field is required'
13961 if (Roo.bootstrap.version == 4) {
13964 style : 'display:none'
13969 if (align ==='left' && this.fieldLabel.length) {
13971 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13978 cls : 'control-label',
13979 html : this.fieldLabel
13991 var labelCfg = cfg.cn[1];
13992 var contentCfg = cfg.cn[2];
13994 if(this.indicatorpos == 'right'){
13999 cls : 'control-label',
14003 html : this.fieldLabel
14017 labelCfg = cfg.cn[0];
14018 contentCfg = cfg.cn[1];
14021 if(this.labelWidth > 12){
14022 labelCfg.style = "width: " + this.labelWidth + 'px';
14025 if(this.labelWidth < 13 && this.labelmd == 0){
14026 this.labelmd = this.labelWidth;
14029 if(this.labellg > 0){
14030 labelCfg.cls += ' col-lg-' + this.labellg;
14031 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14034 if(this.labelmd > 0){
14035 labelCfg.cls += ' col-md-' + this.labelmd;
14036 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14039 if(this.labelsm > 0){
14040 labelCfg.cls += ' col-sm-' + this.labelsm;
14041 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14044 if(this.labelxs > 0){
14045 labelCfg.cls += ' col-xs-' + this.labelxs;
14046 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14049 } else if ( this.fieldLabel.length) {
14050 // Roo.log(" label");
14055 //cls : 'input-group-addon',
14056 html : this.fieldLabel
14064 if(this.indicatorpos == 'right'){
14072 html : this.fieldLabel
14086 // Roo.log(" no label && no align");
14093 ['xs','sm','md','lg'].map(function(size){
14094 if (settings[size]) {
14095 cfg.cls += ' col-' + size + '-' + settings[size];
14106 onResize : function(w, h){
14107 // Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14108 // if(typeof w == 'number'){
14109 // var x = w - this.trigger.getWidth();
14110 // this.inputEl().setWidth(this.adjustWidth('input', x));
14111 // this.trigger.setStyle('left', x+'px');
14116 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14119 getResizeEl : function(){
14120 return this.inputEl();
14124 getPositionEl : function(){
14125 return this.inputEl();
14129 alignErrorIcon : function(){
14130 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14134 initEvents : function(){
14138 Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14139 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14140 if(!this.multiple && this.showToggleBtn){
14141 this.trigger = this.el.select('span.dropdown-toggle',true).first();
14142 if(this.hideTrigger){
14143 this.trigger.setDisplayed(false);
14145 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14149 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14152 if(this.removable && !this.editable && !this.tickable){
14153 var close = this.closeTriggerEl();
14156 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14157 close.on('click', this.removeBtnClick, this, close);
14161 //this.trigger.addClassOnOver('x-form-trigger-over');
14162 //this.trigger.addClassOnClick('x-form-trigger-click');
14165 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14169 closeTriggerEl : function()
14171 var close = this.el.select('.roo-combo-removable-btn', true).first();
14172 return close ? close : false;
14175 removeBtnClick : function(e, h, el)
14177 e.preventDefault();
14179 if(this.fireEvent("remove", this) !== false){
14181 this.fireEvent("afterremove", this)
14185 createList : function()
14187 this.list = Roo.get(document.body).createChild({
14188 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14189 cls: 'typeahead typeahead-long dropdown-menu shadow',
14190 style: 'display:none'
14193 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14198 initTrigger : function(){
14203 onDestroy : function(){
14205 this.trigger.removeAllListeners();
14206 // this.trigger.remove();
14209 // this.wrap.remove();
14211 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14215 onFocus : function(){
14216 Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14218 if(!this.mimicing){
14219 this.wrap.addClass('x-trigger-wrap-focus');
14220 this.mimicing = true;
14221 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14222 if(this.monitorTab){
14223 this.el.on("keydown", this.checkTab, this);
14230 checkTab : function(e){
14231 if(e.getKey() == e.TAB){
14232 this.triggerBlur();
14237 onBlur : function(){
14242 mimicBlur : function(e, t){
14244 if(!this.wrap.contains(t) && this.validateBlur()){
14245 this.triggerBlur();
14251 triggerBlur : function(){
14252 this.mimicing = false;
14253 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14254 if(this.monitorTab){
14255 this.el.un("keydown", this.checkTab, this);
14257 //this.wrap.removeClass('x-trigger-wrap-focus');
14258 Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14262 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14263 validateBlur : function(e, t){
14268 onDisable : function(){
14269 this.inputEl().dom.disabled = true;
14270 //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14272 // this.wrap.addClass('x-item-disabled');
14277 onEnable : function(){
14278 this.inputEl().dom.disabled = false;
14279 //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14281 // this.el.removeClass('x-item-disabled');
14286 onShow : function(){
14287 var ae = this.getActionEl();
14290 ae.dom.style.display = '';
14291 ae.dom.style.visibility = 'visible';
14297 onHide : function(){
14298 var ae = this.getActionEl();
14299 ae.dom.style.display = 'none';
14303 * The function that should handle the trigger's click event. This method does nothing by default until overridden
14304 * by an implementing function.
14306 * @param {EventObject} e
14308 onTriggerClick : Roo.emptyFn
14316 * @class Roo.bootstrap.form.CardUploader
14317 * @extends Roo.bootstrap.Button
14318 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14319 * @cfg {Number} errorTimeout default 3000
14320 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
14321 * @cfg {Array} html The button text.
14325 * Create a new CardUploader
14326 * @param {Object} config The config object
14329 Roo.bootstrap.form.CardUploader = function(config){
14333 Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14336 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
14344 * When a image is clicked on - and needs to display a slideshow or similar..
14345 * @param {Roo.bootstrap.Card} this
14346 * @param {Object} The image information data
14352 * When a the download link is clicked
14353 * @param {Roo.bootstrap.Card} this
14354 * @param {Object} The image information data contains
14361 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input, {
14364 errorTimeout : 3000,
14368 fileCollection : false,
14371 getAutoCreate : function()
14375 cls :'form-group' ,
14380 //cls : 'input-group-addon',
14381 html : this.fieldLabel
14389 value : this.value,
14390 cls : 'd-none form-control'
14395 multiple : 'multiple',
14397 cls : 'd-none roo-card-upload-selector'
14401 cls : 'roo-card-uploader-button-container w-100 mb-2'
14404 cls : 'card-columns roo-card-uploader-container'
14414 getChildContainer : function() /// what children are added to.
14416 return this.containerEl;
14419 getButtonContainer : function() /// what children are added to.
14421 return this.el.select(".roo-card-uploader-button-container").first();
14424 initEvents : function()
14427 Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14431 xns: Roo.bootstrap,
14434 container_method : 'getButtonContainer' ,
14435 html : this.html, // fix changable?
14438 'click' : function(btn, e) {
14447 this.urlAPI = (window.createObjectURL && window) ||
14448 (window.URL && URL.revokeObjectURL && URL) ||
14449 (window.webkitURL && webkitURL);
14454 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14456 this.selectorEl.on('change', this.onFileSelected, this);
14459 this.images.forEach(function(img) {
14462 this.images = false;
14464 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14470 onClick : function(e)
14472 e.preventDefault();
14474 this.selectorEl.dom.click();
14478 onFileSelected : function(e)
14480 e.preventDefault();
14482 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14486 Roo.each(this.selectorEl.dom.files, function(file){
14487 this.addFile(file);
14496 addFile : function(file)
14499 if(typeof(file) === 'string'){
14500 throw "Add file by name?"; // should not happen
14504 if(!file || !this.urlAPI){
14514 var url = _this.urlAPI.createObjectURL( file);
14517 id : Roo.bootstrap.form.CardUploader.ID--,
14518 is_uploaded : false,
14522 mimetype : file.type,
14530 * addCard - add an Attachment to the uploader
14531 * @param data - the data about the image to upload
14535 title : "Title of file",
14536 is_uploaded : false,
14537 src : "http://.....",
14538 srcfile : { the File upload object },
14539 mimetype : file.type,
14542 .. any other data...
14548 addCard : function (data)
14550 // hidden input element?
14551 // if the file is not an image...
14552 //then we need to use something other that and header_image
14557 xns : Roo.bootstrap,
14558 xtype : 'CardFooter',
14561 xns : Roo.bootstrap,
14567 xns : Roo.bootstrap,
14569 html : String.format("<small>{0}</small>", data.title),
14570 cls : 'col-10 text-left',
14575 click : function() {
14577 t.fireEvent( "download", t, data );
14583 xns : Roo.bootstrap,
14585 style: 'max-height: 28px; ',
14591 click : function() {
14592 t.removeCard(data.id)
14604 var cn = this.addxtype(
14607 xns : Roo.bootstrap,
14610 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14611 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14612 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14617 initEvents : function() {
14618 Roo.bootstrap.Card.prototype.initEvents.call(this);
14620 this.imgEl = this.el.select('.card-img-top').first();
14622 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14623 this.imgEl.set({ 'pointer' : 'cursor' });
14626 this.getCardFooter().addClass('p-1');
14633 // dont' really need ot update items.
14634 // this.items.push(cn);
14635 this.fileCollection.add(cn);
14637 if (!data.srcfile) {
14638 this.updateInput();
14643 var reader = new FileReader();
14644 reader.addEventListener("load", function() {
14645 data.srcdata = reader.result;
14648 reader.readAsDataURL(data.srcfile);
14653 removeCard : function(id)
14656 var card = this.fileCollection.get(id);
14657 card.data.is_deleted = 1;
14658 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14659 //this.fileCollection.remove(card);
14660 //this.items = this.items.filter(function(e) { return e != card });
14661 // dont' really need ot update items.
14662 card.el.dom.parentNode.removeChild(card.el.dom);
14663 this.updateInput();
14669 this.fileCollection.each(function(card) {
14670 if (card.el.dom && card.el.dom.parentNode) {
14671 card.el.dom.parentNode.removeChild(card.el.dom);
14674 this.fileCollection.clear();
14675 this.updateInput();
14678 updateInput : function()
14681 this.fileCollection.each(function(e) {
14685 this.inputEl().dom.value = JSON.stringify(data);
14695 Roo.bootstrap.form.CardUploader.ID = -1;/*
14697 * Ext JS Library 1.1.1
14698 * Copyright(c) 2006-2007, Ext JS, LLC.
14700 * Originally Released Under LGPL - original licence link has changed is not relivant.
14703 * <script type="text/javascript">
14708 * @class Roo.data.SortTypes
14710 * Defines the default sorting (casting?) comparison functions used when sorting data.
14712 Roo.data.SortTypes = {
14714 * Default sort that does nothing
14715 * @param {Mixed} s The value being converted
14716 * @return {Mixed} The comparison value
14718 none : function(s){
14723 * The regular expression used to strip tags
14727 stripTagsRE : /<\/?[^>]+>/gi,
14730 * Strips all HTML tags to sort on text only
14731 * @param {Mixed} s The value being converted
14732 * @return {String} The comparison value
14734 asText : function(s){
14735 return String(s).replace(this.stripTagsRE, "");
14739 * Strips all HTML tags to sort on text only - Case insensitive
14740 * @param {Mixed} s The value being converted
14741 * @return {String} The comparison value
14743 asUCText : function(s){
14744 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14748 * Case insensitive string
14749 * @param {Mixed} s The value being converted
14750 * @return {String} The comparison value
14752 asUCString : function(s) {
14753 return String(s).toUpperCase();
14758 * @param {Mixed} s The value being converted
14759 * @return {Number} The comparison value
14761 asDate : function(s) {
14765 if(s instanceof Date){
14766 return s.getTime();
14768 return Date.parse(String(s));
14773 * @param {Mixed} s The value being converted
14774 * @return {Float} The comparison value
14776 asFloat : function(s) {
14777 var val = parseFloat(String(s).replace(/,/g, ""));
14786 * @param {Mixed} s The value being converted
14787 * @return {Number} The comparison value
14789 asInt : function(s) {
14790 var val = parseInt(String(s).replace(/,/g, ""));
14798 * Ext JS Library 1.1.1
14799 * Copyright(c) 2006-2007, Ext JS, LLC.
14801 * Originally Released Under LGPL - original licence link has changed is not relivant.
14804 * <script type="text/javascript">
14808 * @class Roo.data.Record
14809 * Instances of this class encapsulate both record <em>definition</em> information, and record
14810 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14811 * to access Records cached in an {@link Roo.data.Store} object.<br>
14813 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14814 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14817 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14819 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14820 * {@link #create}. The parameters are the same.
14821 * @param {Array} data An associative Array of data values keyed by the field name.
14822 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14823 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14824 * not specified an integer id is generated.
14826 Roo.data.Record = function(data, id){
14827 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14832 * Generate a constructor for a specific record layout.
14833 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14834 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14835 * Each field definition object may contain the following properties: <ul>
14836 * <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,
14837 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14838 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14839 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14840 * is being used, then this is a string containing the javascript expression to reference the data relative to
14841 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14842 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14843 * this may be omitted.</p></li>
14844 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14845 * <ul><li>auto (Default, implies no conversion)</li>
14850 * <li>date</li></ul></p></li>
14851 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14852 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14853 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14854 * by the Reader into an object that will be stored in the Record. It is passed the
14855 * following parameters:<ul>
14856 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14858 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14860 * <br>usage:<br><pre><code>
14861 var TopicRecord = Roo.data.Record.create(
14862 {name: 'title', mapping: 'topic_title'},
14863 {name: 'author', mapping: 'username'},
14864 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14865 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14866 {name: 'lastPoster', mapping: 'user2'},
14867 {name: 'excerpt', mapping: 'post_text'}
14870 var myNewRecord = new TopicRecord({
14871 title: 'Do my job please',
14874 lastPost: new Date(),
14875 lastPoster: 'Animal',
14876 excerpt: 'No way dude!'
14878 myStore.add(myNewRecord);
14883 Roo.data.Record.create = function(o){
14884 var f = function(){
14885 f.superclass.constructor.apply(this, arguments);
14887 Roo.extend(f, Roo.data.Record);
14888 var p = f.prototype;
14889 p.fields = new Roo.util.MixedCollection(false, function(field){
14892 for(var i = 0, len = o.length; i < len; i++){
14893 p.fields.add(new Roo.data.Field(o[i]));
14895 f.getField = function(name){
14896 return p.fields.get(name);
14901 Roo.data.Record.AUTO_ID = 1000;
14902 Roo.data.Record.EDIT = 'edit';
14903 Roo.data.Record.REJECT = 'reject';
14904 Roo.data.Record.COMMIT = 'commit';
14906 Roo.data.Record.prototype = {
14908 * Readonly flag - true if this record has been modified.
14917 join : function(store){
14918 this.store = store;
14922 * Set the named field to the specified value.
14923 * @param {String} name The name of the field to set.
14924 * @param {Object} value The value to set the field to.
14926 set : function(name, value){
14927 if(this.data[name] == value){
14931 if(!this.modified){
14932 this.modified = {};
14934 if(typeof this.modified[name] == 'undefined'){
14935 this.modified[name] = this.data[name];
14937 this.data[name] = value;
14938 if(!this.editing && this.store){
14939 this.store.afterEdit(this);
14944 * Get the value of the named field.
14945 * @param {String} name The name of the field to get the value of.
14946 * @return {Object} The value of the field.
14948 get : function(name){
14949 return this.data[name];
14953 beginEdit : function(){
14954 this.editing = true;
14955 this.modified = {};
14959 cancelEdit : function(){
14960 this.editing = false;
14961 delete this.modified;
14965 endEdit : function(){
14966 this.editing = false;
14967 if(this.dirty && this.store){
14968 this.store.afterEdit(this);
14973 * Usually called by the {@link Roo.data.Store} which owns the Record.
14974 * Rejects all changes made to the Record since either creation, or the last commit operation.
14975 * Modified fields are reverted to their original values.
14977 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14978 * of reject operations.
14980 reject : function(){
14981 var m = this.modified;
14983 if(typeof m[n] != "function"){
14984 this.data[n] = m[n];
14987 this.dirty = false;
14988 delete this.modified;
14989 this.editing = false;
14991 this.store.afterReject(this);
14996 * Usually called by the {@link Roo.data.Store} which owns the Record.
14997 * Commits all changes made to the Record since either creation, or the last commit operation.
14999 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15000 * of commit operations.
15002 commit : function(){
15003 this.dirty = false;
15004 delete this.modified;
15005 this.editing = false;
15007 this.store.afterCommit(this);
15012 hasError : function(){
15013 return this.error != null;
15017 clearError : function(){
15022 * Creates a copy of this record.
15023 * @param {String} id (optional) A new record id if you don't want to use this record's id
15026 copy : function(newId) {
15027 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15031 * Ext JS Library 1.1.1
15032 * Copyright(c) 2006-2007, Ext JS, LLC.
15034 * Originally Released Under LGPL - original licence link has changed is not relivant.
15037 * <script type="text/javascript">
15043 * @class Roo.data.Store
15044 * @extends Roo.util.Observable
15045 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15046 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15048 * 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
15049 * has no knowledge of the format of the data returned by the Proxy.<br>
15051 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15052 * instances from the data object. These records are cached and made available through accessor functions.
15054 * Creates a new Store.
15055 * @param {Object} config A config object containing the objects needed for the Store to access data,
15056 * and read the data into Records.
15058 Roo.data.Store = function(config){
15059 this.data = new Roo.util.MixedCollection(false);
15060 this.data.getKey = function(o){
15063 this.baseParams = {};
15065 this.paramNames = {
15070 "multisort" : "_multisort"
15073 if(config && config.data){
15074 this.inlineData = config.data;
15075 delete config.data;
15078 Roo.apply(this, config);
15080 if(this.reader){ // reader passed
15081 this.reader = Roo.factory(this.reader, Roo.data);
15082 this.reader.xmodule = this.xmodule || false;
15083 if(!this.recordType){
15084 this.recordType = this.reader.recordType;
15086 if(this.reader.onMetaChange){
15087 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15091 if(this.recordType){
15092 this.fields = this.recordType.prototype.fields;
15094 this.modified = [];
15098 * @event datachanged
15099 * Fires when the data cache has changed, and a widget which is using this Store
15100 * as a Record cache should refresh its view.
15101 * @param {Store} this
15103 datachanged : true,
15105 * @event metachange
15106 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15107 * @param {Store} this
15108 * @param {Object} meta The JSON metadata
15113 * Fires when Records have been added to the Store
15114 * @param {Store} this
15115 * @param {Roo.data.Record[]} records The array of Records added
15116 * @param {Number} index The index at which the record(s) were added
15121 * Fires when a Record has been removed from the Store
15122 * @param {Store} this
15123 * @param {Roo.data.Record} record The Record that was removed
15124 * @param {Number} index The index at which the record was removed
15129 * Fires when a Record has been updated
15130 * @param {Store} this
15131 * @param {Roo.data.Record} record The Record that was updated
15132 * @param {String} operation The update operation being performed. Value may be one of:
15134 Roo.data.Record.EDIT
15135 Roo.data.Record.REJECT
15136 Roo.data.Record.COMMIT
15142 * Fires when the data cache has been cleared.
15143 * @param {Store} this
15147 * @event beforeload
15148 * Fires before a request is made for a new data object. If the beforeload handler returns false
15149 * the load action will be canceled.
15150 * @param {Store} this
15151 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15155 * @event beforeloadadd
15156 * Fires after a new set of Records has been loaded.
15157 * @param {Store} this
15158 * @param {Roo.data.Record[]} records The Records that were loaded
15159 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15161 beforeloadadd : true,
15164 * Fires after a new set of Records has been loaded, before they are added to the store.
15165 * @param {Store} this
15166 * @param {Roo.data.Record[]} records The Records that were loaded
15167 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15168 * @params {Object} return from reader
15172 * @event loadexception
15173 * Fires if an exception occurs in the Proxy during loading.
15174 * Called with the signature of the Proxy's "loadexception" event.
15175 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15178 * @param {Object} return from JsonData.reader() - success, totalRecords, records
15179 * @param {Object} load options
15180 * @param {Object} jsonData from your request (normally this contains the Exception)
15182 loadexception : true
15186 this.proxy = Roo.factory(this.proxy, Roo.data);
15187 this.proxy.xmodule = this.xmodule || false;
15188 this.relayEvents(this.proxy, ["loadexception"]);
15190 this.sortToggle = {};
15191 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15193 Roo.data.Store.superclass.constructor.call(this);
15195 if(this.inlineData){
15196 this.loadData(this.inlineData);
15197 delete this.inlineData;
15201 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15203 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
15204 * without a remote query - used by combo/forms at present.
15208 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15211 * @cfg {Array} data Inline data to be loaded when the store is initialized.
15214 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
15215 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15218 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15219 * on any HTTP request
15222 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15225 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15229 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15230 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15232 remoteSort : false,
15235 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15236 * loaded or when a record is removed. (defaults to false).
15238 pruneModifiedRecords : false,
15241 lastOptions : null,
15244 * Add Records to the Store and fires the add event.
15245 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15247 add : function(records){
15248 records = [].concat(records);
15249 for(var i = 0, len = records.length; i < len; i++){
15250 records[i].join(this);
15252 var index = this.data.length;
15253 this.data.addAll(records);
15254 this.fireEvent("add", this, records, index);
15258 * Remove a Record from the Store and fires the remove event.
15259 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15261 remove : function(record){
15262 var index = this.data.indexOf(record);
15263 this.data.removeAt(index);
15265 if(this.pruneModifiedRecords){
15266 this.modified.remove(record);
15268 this.fireEvent("remove", this, record, index);
15272 * Remove all Records from the Store and fires the clear event.
15274 removeAll : function(){
15276 if(this.pruneModifiedRecords){
15277 this.modified = [];
15279 this.fireEvent("clear", this);
15283 * Inserts Records to the Store at the given index and fires the add event.
15284 * @param {Number} index The start index at which to insert the passed Records.
15285 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15287 insert : function(index, records){
15288 records = [].concat(records);
15289 for(var i = 0, len = records.length; i < len; i++){
15290 this.data.insert(index, records[i]);
15291 records[i].join(this);
15293 this.fireEvent("add", this, records, index);
15297 * Get the index within the cache of the passed Record.
15298 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15299 * @return {Number} The index of the passed Record. Returns -1 if not found.
15301 indexOf : function(record){
15302 return this.data.indexOf(record);
15306 * Get the index within the cache of the Record with the passed id.
15307 * @param {String} id The id of the Record to find.
15308 * @return {Number} The index of the Record. Returns -1 if not found.
15310 indexOfId : function(id){
15311 return this.data.indexOfKey(id);
15315 * Get the Record with the specified id.
15316 * @param {String} id The id of the Record to find.
15317 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15319 getById : function(id){
15320 return this.data.key(id);
15324 * Get the Record at the specified index.
15325 * @param {Number} index The index of the Record to find.
15326 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15328 getAt : function(index){
15329 return this.data.itemAt(index);
15333 * Returns a range of Records between specified indices.
15334 * @param {Number} startIndex (optional) The starting index (defaults to 0)
15335 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15336 * @return {Roo.data.Record[]} An array of Records
15338 getRange : function(start, end){
15339 return this.data.getRange(start, end);
15343 storeOptions : function(o){
15344 o = Roo.apply({}, o);
15347 this.lastOptions = o;
15351 * Loads the Record cache from the configured Proxy using the configured Reader.
15353 * If using remote paging, then the first load call must specify the <em>start</em>
15354 * and <em>limit</em> properties in the options.params property to establish the initial
15355 * position within the dataset, and the number of Records to cache on each read from the Proxy.
15357 * <strong>It is important to note that for remote data sources, loading is asynchronous,
15358 * and this call will return before the new data has been loaded. Perform any post-processing
15359 * in a callback function, or in a "load" event handler.</strong>
15361 * @param {Object} options An object containing properties which control loading options:<ul>
15362 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15363 * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15366 data : data, // array of key=>value data like JsonReader
15367 total : data.length,
15373 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15374 * passed the following arguments:<ul>
15375 * <li>r : Roo.data.Record[]</li>
15376 * <li>options: Options object from the load call</li>
15377 * <li>success: Boolean success indicator</li></ul></li>
15378 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15379 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15382 load : function(options){
15383 options = options || {};
15384 if(this.fireEvent("beforeload", this, options) !== false){
15385 this.storeOptions(options);
15386 var p = Roo.apply(options.params || {}, this.baseParams);
15387 // if meta was not loaded from remote source.. try requesting it.
15388 if (!this.reader.metaFromRemote) {
15389 p._requestMeta = 1;
15391 if(this.sortInfo && this.remoteSort){
15392 var pn = this.paramNames;
15393 p[pn["sort"]] = this.sortInfo.field;
15394 p[pn["dir"]] = this.sortInfo.direction;
15396 if (this.multiSort) {
15397 var pn = this.paramNames;
15398 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15401 this.proxy.load(p, this.reader, this.loadRecords, this, options);
15406 * Reloads the Record cache from the configured Proxy using the configured Reader and
15407 * the options from the last load operation performed.
15408 * @param {Object} options (optional) An object containing properties which may override the options
15409 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15410 * the most recently used options are reused).
15412 reload : function(options){
15413 this.load(Roo.applyIf(options||{}, this.lastOptions));
15417 // Called as a callback by the Reader during a load operation.
15418 loadRecords : function(o, options, success){
15421 if(success !== false){
15422 this.fireEvent("load", this, [], options, o);
15424 if(options.callback){
15425 options.callback.call(options.scope || this, [], options, false);
15429 // if data returned failure - throw an exception.
15430 if (o.success === false) {
15431 // show a message if no listener is registered.
15432 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15433 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15435 // loadmask wil be hooked into this..
15436 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15439 var r = o.records, t = o.totalRecords || r.length;
15441 this.fireEvent("beforeloadadd", this, r, options, o);
15443 if(!options || options.add !== true){
15444 if(this.pruneModifiedRecords){
15445 this.modified = [];
15447 for(var i = 0, len = r.length; i < len; i++){
15451 this.data = this.snapshot;
15452 delete this.snapshot;
15455 this.data.addAll(r);
15456 this.totalLength = t;
15458 this.fireEvent("datachanged", this);
15460 this.totalLength = Math.max(t, this.data.length+r.length);
15464 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15466 var e = new Roo.data.Record({});
15468 e.set(this.parent.displayField, this.parent.emptyTitle);
15469 e.set(this.parent.valueField, '');
15474 this.fireEvent("load", this, r, options, o);
15475 if(options.callback){
15476 options.callback.call(options.scope || this, r, options, true);
15482 * Loads data from a passed data block. A Reader which understands the format of the data
15483 * must have been configured in the constructor.
15484 * @param {Object} data The data block from which to read the Records. The format of the data expected
15485 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15486 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15488 loadData : function(o, append){
15489 var r = this.reader.readRecords(o);
15490 this.loadRecords(r, {add: append}, true);
15494 * using 'cn' the nested child reader read the child array into it's child stores.
15495 * @param {Object} rec The record with a 'children array
15497 loadDataFromChildren : function(rec)
15499 this.loadData(this.reader.toLoadData(rec));
15504 * Gets the number of cached records.
15506 * <em>If using paging, this may not be the total size of the dataset. If the data object
15507 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15508 * the data set size</em>
15510 getCount : function(){
15511 return this.data.length || 0;
15515 * Gets the total number of records in the dataset as returned by the server.
15517 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15518 * the dataset size</em>
15520 getTotalCount : function(){
15521 return this.totalLength || 0;
15525 * Returns the sort state of the Store as an object with two properties:
15527 field {String} The name of the field by which the Records are sorted
15528 direction {String} The sort order, "ASC" or "DESC"
15531 getSortState : function(){
15532 return this.sortInfo;
15536 applySort : function(){
15537 if(this.sortInfo && !this.remoteSort){
15538 var s = this.sortInfo, f = s.field;
15539 var st = this.fields.get(f).sortType;
15540 var fn = function(r1, r2){
15541 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15542 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15544 this.data.sort(s.direction, fn);
15545 if(this.snapshot && this.snapshot != this.data){
15546 this.snapshot.sort(s.direction, fn);
15552 * Sets the default sort column and order to be used by the next load operation.
15553 * @param {String} fieldName The name of the field to sort by.
15554 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15556 setDefaultSort : function(field, dir){
15557 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15561 * Sort the Records.
15562 * If remote sorting is used, the sort is performed on the server, and the cache is
15563 * reloaded. If local sorting is used, the cache is sorted internally.
15564 * @param {String} fieldName The name of the field to sort by.
15565 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15567 sort : function(fieldName, dir){
15568 var f = this.fields.get(fieldName);
15570 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15572 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15573 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15578 this.sortToggle[f.name] = dir;
15579 this.sortInfo = {field: f.name, direction: dir};
15580 if(!this.remoteSort){
15582 this.fireEvent("datachanged", this);
15584 this.load(this.lastOptions);
15589 * Calls the specified function for each of the Records in the cache.
15590 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15591 * Returning <em>false</em> aborts and exits the iteration.
15592 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15594 each : function(fn, scope){
15595 this.data.each(fn, scope);
15599 * Gets all records modified since the last commit. Modified records are persisted across load operations
15600 * (e.g., during paging).
15601 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15603 getModifiedRecords : function(){
15604 return this.modified;
15608 createFilterFn : function(property, value, anyMatch){
15609 if(!value.exec){ // not a regex
15610 value = String(value);
15611 if(value.length == 0){
15614 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15616 return function(r){
15617 return value.test(r.data[property]);
15622 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15623 * @param {String} property A field on your records
15624 * @param {Number} start The record index to start at (defaults to 0)
15625 * @param {Number} end The last record index to include (defaults to length - 1)
15626 * @return {Number} The sum
15628 sum : function(property, start, end){
15629 var rs = this.data.items, v = 0;
15630 start = start || 0;
15631 end = (end || end === 0) ? end : rs.length-1;
15633 for(var i = start; i <= end; i++){
15634 v += (rs[i].data[property] || 0);
15640 * Filter the records by a specified property.
15641 * @param {String} field A field on your records
15642 * @param {String/RegExp} value Either a string that the field
15643 * should start with or a RegExp to test against the field
15644 * @param {Boolean} anyMatch True to match any part not just the beginning
15646 filter : function(property, value, anyMatch){
15647 var fn = this.createFilterFn(property, value, anyMatch);
15648 return fn ? this.filterBy(fn) : this.clearFilter();
15652 * Filter by a function. The specified function will be called with each
15653 * record in this data source. If the function returns true the record is included,
15654 * otherwise it is filtered.
15655 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15656 * @param {Object} scope (optional) The scope of the function (defaults to this)
15658 filterBy : function(fn, scope){
15659 this.snapshot = this.snapshot || this.data;
15660 this.data = this.queryBy(fn, scope||this);
15661 this.fireEvent("datachanged", this);
15665 * Query the records by a specified property.
15666 * @param {String} field A field on your records
15667 * @param {String/RegExp} value Either a string that the field
15668 * should start with or a RegExp to test against the field
15669 * @param {Boolean} anyMatch True to match any part not just the beginning
15670 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15672 query : function(property, value, anyMatch){
15673 var fn = this.createFilterFn(property, value, anyMatch);
15674 return fn ? this.queryBy(fn) : this.data.clone();
15678 * Query by a function. The specified function will be called with each
15679 * record in this data source. If the function returns true the record is included
15681 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15682 * @param {Object} scope (optional) The scope of the function (defaults to this)
15683 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15685 queryBy : function(fn, scope){
15686 var data = this.snapshot || this.data;
15687 return data.filterBy(fn, scope||this);
15691 * Collects unique values for a particular dataIndex from this store.
15692 * @param {String} dataIndex The property to collect
15693 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15694 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15695 * @return {Array} An array of the unique values
15697 collect : function(dataIndex, allowNull, bypassFilter){
15698 var d = (bypassFilter === true && this.snapshot) ?
15699 this.snapshot.items : this.data.items;
15700 var v, sv, r = [], l = {};
15701 for(var i = 0, len = d.length; i < len; i++){
15702 v = d[i].data[dataIndex];
15704 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15713 * Revert to a view of the Record cache with no filtering applied.
15714 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15716 clearFilter : function(suppressEvent){
15717 if(this.snapshot && this.snapshot != this.data){
15718 this.data = this.snapshot;
15719 delete this.snapshot;
15720 if(suppressEvent !== true){
15721 this.fireEvent("datachanged", this);
15727 afterEdit : function(record){
15728 if(this.modified.indexOf(record) == -1){
15729 this.modified.push(record);
15731 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15735 afterReject : function(record){
15736 this.modified.remove(record);
15737 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15741 afterCommit : function(record){
15742 this.modified.remove(record);
15743 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15747 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15748 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15750 commitChanges : function(){
15751 var m = this.modified.slice(0);
15752 this.modified = [];
15753 for(var i = 0, len = m.length; i < len; i++){
15759 * Cancel outstanding changes on all changed records.
15761 rejectChanges : function(){
15762 var m = this.modified.slice(0);
15763 this.modified = [];
15764 for(var i = 0, len = m.length; i < len; i++){
15769 onMetaChange : function(meta, rtype, o){
15770 this.recordType = rtype;
15771 this.fields = rtype.prototype.fields;
15772 delete this.snapshot;
15773 this.sortInfo = meta.sortInfo || this.sortInfo;
15774 this.modified = [];
15775 this.fireEvent('metachange', this, this.reader.meta);
15778 moveIndex : function(data, type)
15780 var index = this.indexOf(data);
15782 var newIndex = index + type;
15786 this.insert(newIndex, data);
15791 * Ext JS Library 1.1.1
15792 * Copyright(c) 2006-2007, Ext JS, LLC.
15794 * Originally Released Under LGPL - original licence link has changed is not relivant.
15797 * <script type="text/javascript">
15801 * @class Roo.data.SimpleStore
15802 * @extends Roo.data.Store
15803 * Small helper class to make creating Stores from Array data easier.
15804 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15805 * @cfg {Array} fields An array of field definition objects, or field name strings.
15806 * @cfg {Object} an existing reader (eg. copied from another store)
15807 * @cfg {Array} data The multi-dimensional array of data
15808 * @cfg {Roo.data.DataProxy} proxy [not-required]
15809 * @cfg {Roo.data.Reader} reader [not-required]
15811 * @param {Object} config
15813 Roo.data.SimpleStore = function(config)
15815 Roo.data.SimpleStore.superclass.constructor.call(this, {
15817 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15820 Roo.data.Record.create(config.fields)
15822 proxy : new Roo.data.MemoryProxy(config.data)
15826 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15828 * Ext JS Library 1.1.1
15829 * Copyright(c) 2006-2007, Ext JS, LLC.
15831 * Originally Released Under LGPL - original licence link has changed is not relivant.
15834 * <script type="text/javascript">
15839 * @extends Roo.data.Store
15840 * @class Roo.data.JsonStore
15841 * Small helper class to make creating Stores for JSON data easier. <br/>
15843 var store = new Roo.data.JsonStore({
15844 url: 'get-images.php',
15846 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15849 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15850 * JsonReader and HttpProxy (unless inline data is provided).</b>
15851 * @cfg {Array} fields An array of field definition objects, or field name strings.
15853 * @param {Object} config
15855 Roo.data.JsonStore = function(c){
15856 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15857 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15858 reader: new Roo.data.JsonReader(c, c.fields)
15861 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15863 * Ext JS Library 1.1.1
15864 * Copyright(c) 2006-2007, Ext JS, LLC.
15866 * Originally Released Under LGPL - original licence link has changed is not relivant.
15869 * <script type="text/javascript">
15873 Roo.data.Field = function(config){
15874 if(typeof config == "string"){
15875 config = {name: config};
15877 Roo.apply(this, config);
15880 this.type = "auto";
15883 var st = Roo.data.SortTypes;
15884 // named sortTypes are supported, here we look them up
15885 if(typeof this.sortType == "string"){
15886 this.sortType = st[this.sortType];
15889 // set default sortType for strings and dates
15890 if(!this.sortType){
15893 this.sortType = st.asUCString;
15896 this.sortType = st.asDate;
15899 this.sortType = st.none;
15904 var stripRe = /[\$,%]/g;
15906 // prebuilt conversion function for this field, instead of
15907 // switching every time we're reading a value
15909 var cv, dateFormat = this.dateFormat;
15914 cv = function(v){ return v; };
15917 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15921 return v !== undefined && v !== null && v !== '' ?
15922 parseInt(String(v).replace(stripRe, ""), 10) : '';
15927 return v !== undefined && v !== null && v !== '' ?
15928 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15933 cv = function(v){ return v === true || v === "true" || v == 1; };
15940 if(v instanceof Date){
15944 if(dateFormat == "timestamp"){
15945 return new Date(v*1000);
15947 return Date.parseDate(v, dateFormat);
15949 var parsed = Date.parse(v);
15950 return parsed ? new Date(parsed) : null;
15959 Roo.data.Field.prototype = {
15967 * Ext JS Library 1.1.1
15968 * Copyright(c) 2006-2007, Ext JS, LLC.
15970 * Originally Released Under LGPL - original licence link has changed is not relivant.
15973 * <script type="text/javascript">
15976 // Base class for reading structured data from a data source. This class is intended to be
15977 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15980 * @class Roo.data.DataReader
15982 * Base class for reading structured data from a data source. This class is intended to be
15983 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15986 Roo.data.DataReader = function(meta, recordType){
15990 this.recordType = recordType instanceof Array ?
15991 Roo.data.Record.create(recordType) : recordType;
15994 Roo.data.DataReader.prototype = {
15997 readerType : 'Data',
15999 * Create an empty record
16000 * @param {Object} data (optional) - overlay some values
16001 * @return {Roo.data.Record} record created.
16003 newRow : function(d) {
16005 this.recordType.prototype.fields.each(function(c) {
16007 case 'int' : da[c.name] = 0; break;
16008 case 'date' : da[c.name] = new Date(); break;
16009 case 'float' : da[c.name] = 0.0; break;
16010 case 'boolean' : da[c.name] = false; break;
16011 default : da[c.name] = ""; break;
16015 return new this.recordType(Roo.apply(da, d));
16021 * Ext JS Library 1.1.1
16022 * Copyright(c) 2006-2007, Ext JS, LLC.
16024 * Originally Released Under LGPL - original licence link has changed is not relivant.
16027 * <script type="text/javascript">
16031 * @class Roo.data.DataProxy
16032 * @extends Roo.util.Observable
16034 * This class is an abstract base class for implementations which provide retrieval of
16035 * unformatted data objects.<br>
16037 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16038 * (of the appropriate type which knows how to parse the data object) to provide a block of
16039 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16041 * Custom implementations must implement the load method as described in
16042 * {@link Roo.data.HttpProxy#load}.
16044 Roo.data.DataProxy = function(){
16047 * @event beforeload
16048 * Fires before a network request is made to retrieve a data object.
16049 * @param {Object} This DataProxy object.
16050 * @param {Object} params The params parameter to the load function.
16055 * Fires before the load method's callback is called.
16056 * @param {Object} This DataProxy object.
16057 * @param {Object} o The data object.
16058 * @param {Object} arg The callback argument object passed to the load function.
16062 * @event loadexception
16063 * Fires if an Exception occurs during data retrieval.
16064 * @param {Object} This DataProxy object.
16065 * @param {Object} o The data object.
16066 * @param {Object} arg The callback argument object passed to the load function.
16067 * @param {Object} e The Exception.
16069 loadexception : true
16071 Roo.data.DataProxy.superclass.constructor.call(this);
16074 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16077 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16081 * Ext JS Library 1.1.1
16082 * Copyright(c) 2006-2007, Ext JS, LLC.
16084 * Originally Released Under LGPL - original licence link has changed is not relivant.
16087 * <script type="text/javascript">
16090 * @class Roo.data.MemoryProxy
16091 * @extends Roo.data.DataProxy
16092 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16093 * to the Reader when its load method is called.
16095 * @param {Object} config A config object containing the objects needed for the Store to access data,
16097 Roo.data.MemoryProxy = function(config){
16099 if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16100 data = config.data;
16102 Roo.data.MemoryProxy.superclass.constructor.call(this);
16106 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16109 * @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16112 * Load data from the requested source (in this case an in-memory
16113 * data object passed to the constructor), read the data object into
16114 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16115 * process that block using the passed callback.
16116 * @param {Object} params This parameter is not used by the MemoryProxy class.
16117 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16118 * object into a block of Roo.data.Records.
16119 * @param {Function} callback The function into which to pass the block of Roo.data.records.
16120 * The function must be passed <ul>
16121 * <li>The Record block object</li>
16122 * <li>The "arg" argument from the load function</li>
16123 * <li>A boolean success indicator</li>
16125 * @param {Object} scope The scope in which to call the callback
16126 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16128 load : function(params, reader, callback, scope, arg){
16129 params = params || {};
16132 result = reader.readRecords(params.data ? params.data :this.data);
16134 this.fireEvent("loadexception", this, arg, null, e);
16135 callback.call(scope, null, arg, false);
16138 callback.call(scope, result, arg, true);
16142 update : function(params, records){
16147 * Ext JS Library 1.1.1
16148 * Copyright(c) 2006-2007, Ext JS, LLC.
16150 * Originally Released Under LGPL - original licence link has changed is not relivant.
16153 * <script type="text/javascript">
16156 * @class Roo.data.HttpProxy
16157 * @extends Roo.data.DataProxy
16158 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16159 * configured to reference a certain URL.<br><br>
16161 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16162 * from which the running page was served.<br><br>
16164 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16166 * Be aware that to enable the browser to parse an XML document, the server must set
16167 * the Content-Type header in the HTTP response to "text/xml".
16169 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16170 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
16171 * will be used to make the request.
16173 Roo.data.HttpProxy = function(conn){
16174 Roo.data.HttpProxy.superclass.constructor.call(this);
16175 // is conn a conn config or a real conn?
16177 this.useAjax = !conn || !conn.events;
16181 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16182 // thse are take from connection...
16185 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16188 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16189 * extra parameters to each request made by this object. (defaults to undefined)
16192 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16193 * to each request made by this object. (defaults to undefined)
16196 * @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)
16199 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16202 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16208 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16212 * Return the {@link Roo.data.Connection} object being used by this Proxy.
16213 * @return {Connection} The Connection object. This object may be used to subscribe to events on
16214 * a finer-grained basis than the DataProxy events.
16216 getConnection : function(){
16217 return this.useAjax ? Roo.Ajax : this.conn;
16221 * Load data from the configured {@link Roo.data.Connection}, read the data object into
16222 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16223 * process that block using the passed callback.
16224 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16225 * for the request to the remote server.
16226 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16227 * object into a block of Roo.data.Records.
16228 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16229 * The function must be passed <ul>
16230 * <li>The Record block object</li>
16231 * <li>The "arg" argument from the load function</li>
16232 * <li>A boolean success indicator</li>
16234 * @param {Object} scope The scope in which to call the callback
16235 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16237 load : function(params, reader, callback, scope, arg){
16238 if(this.fireEvent("beforeload", this, params) !== false){
16240 params : params || {},
16242 callback : callback,
16247 callback : this.loadResponse,
16251 Roo.applyIf(o, this.conn);
16252 if(this.activeRequest){
16253 Roo.Ajax.abort(this.activeRequest);
16255 this.activeRequest = Roo.Ajax.request(o);
16257 this.conn.request(o);
16260 callback.call(scope||this, null, arg, false);
16265 loadResponse : function(o, success, response){
16266 delete this.activeRequest;
16268 this.fireEvent("loadexception", this, o, response);
16269 o.request.callback.call(o.request.scope, null, o.request.arg, false);
16274 result = o.reader.read(response);
16277 o.raw = { errorMsg : response.responseText };
16278 this.fireEvent("loadexception", this, o, response, e);
16279 o.request.callback.call(o.request.scope, o, o.request.arg, false);
16283 this.fireEvent("load", this, o, o.request.arg);
16284 o.request.callback.call(o.request.scope, result, o.request.arg, true);
16288 update : function(dataSet){
16293 updateResponse : function(dataSet){
16298 * Ext JS Library 1.1.1
16299 * Copyright(c) 2006-2007, Ext JS, LLC.
16301 * Originally Released Under LGPL - original licence link has changed is not relivant.
16304 * <script type="text/javascript">
16308 * @class Roo.data.ScriptTagProxy
16309 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16310 * other than the originating domain of the running page.<br><br>
16312 * <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
16313 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16315 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16316 * source code that is used as the source inside a <script> tag.<br><br>
16318 * In order for the browser to process the returned data, the server must wrap the data object
16319 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16320 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16321 * depending on whether the callback name was passed:
16324 boolean scriptTag = false;
16325 String cb = request.getParameter("callback");
16328 response.setContentType("text/javascript");
16330 response.setContentType("application/x-json");
16332 Writer out = response.getWriter();
16334 out.write(cb + "(");
16336 out.print(dataBlock.toJsonString());
16343 * @param {Object} config A configuration object.
16345 Roo.data.ScriptTagProxy = function(config){
16346 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16347 Roo.apply(this, config);
16348 this.head = document.getElementsByTagName("head")[0];
16351 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16353 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16355 * @cfg {String} url The URL from which to request the data object.
16358 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16362 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16363 * the server the name of the callback function set up by the load call to process the returned data object.
16364 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16365 * javascript output which calls this named function passing the data object as its only parameter.
16367 callbackParam : "callback",
16369 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16370 * name to the request.
16375 * Load data from the configured URL, read the data object into
16376 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16377 * process that block using the passed callback.
16378 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16379 * for the request to the remote server.
16380 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16381 * object into a block of Roo.data.Records.
16382 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16383 * The function must be passed <ul>
16384 * <li>The Record block object</li>
16385 * <li>The "arg" argument from the load function</li>
16386 * <li>A boolean success indicator</li>
16388 * @param {Object} scope The scope in which to call the callback
16389 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16391 load : function(params, reader, callback, scope, arg){
16392 if(this.fireEvent("beforeload", this, params) !== false){
16394 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16396 var url = this.url;
16397 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16399 url += "&_dc=" + (new Date().getTime());
16401 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16404 cb : "stcCallback"+transId,
16405 scriptId : "stcScript"+transId,
16409 callback : callback,
16415 window[trans.cb] = function(o){
16416 conn.handleResponse(o, trans);
16419 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16421 if(this.autoAbort !== false){
16425 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16427 var script = document.createElement("script");
16428 script.setAttribute("src", url);
16429 script.setAttribute("type", "text/javascript");
16430 script.setAttribute("id", trans.scriptId);
16431 this.head.appendChild(script);
16433 this.trans = trans;
16435 callback.call(scope||this, null, arg, false);
16440 isLoading : function(){
16441 return this.trans ? true : false;
16445 * Abort the current server request.
16447 abort : function(){
16448 if(this.isLoading()){
16449 this.destroyTrans(this.trans);
16454 destroyTrans : function(trans, isLoaded){
16455 this.head.removeChild(document.getElementById(trans.scriptId));
16456 clearTimeout(trans.timeoutId);
16458 window[trans.cb] = undefined;
16460 delete window[trans.cb];
16463 // if hasn't been loaded, wait for load to remove it to prevent script error
16464 window[trans.cb] = function(){
16465 window[trans.cb] = undefined;
16467 delete window[trans.cb];
16474 handleResponse : function(o, trans){
16475 this.trans = false;
16476 this.destroyTrans(trans, true);
16479 result = trans.reader.readRecords(o);
16481 this.fireEvent("loadexception", this, o, trans.arg, e);
16482 trans.callback.call(trans.scope||window, null, trans.arg, false);
16485 this.fireEvent("load", this, o, trans.arg);
16486 trans.callback.call(trans.scope||window, result, trans.arg, true);
16490 handleFailure : function(trans){
16491 this.trans = false;
16492 this.destroyTrans(trans, false);
16493 this.fireEvent("loadexception", this, null, trans.arg);
16494 trans.callback.call(trans.scope||window, null, trans.arg, false);
16498 * Ext JS Library 1.1.1
16499 * Copyright(c) 2006-2007, Ext JS, LLC.
16501 * Originally Released Under LGPL - original licence link has changed is not relivant.
16504 * <script type="text/javascript">
16508 * @class Roo.data.JsonReader
16509 * @extends Roo.data.DataReader
16510 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16511 * based on mappings in a provided Roo.data.Record constructor.
16513 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16514 * in the reply previously.
16519 var RecordDef = Roo.data.Record.create([
16520 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16521 {name: 'occupation'} // This field will use "occupation" as the mapping.
16523 var myReader = new Roo.data.JsonReader({
16524 totalProperty: "results", // The property which contains the total dataset size (optional)
16525 root: "rows", // The property which contains an Array of row objects
16526 id: "id" // The property within each row object that provides an ID for the record (optional)
16530 * This would consume a JSON file like this:
16532 { 'results': 2, 'rows': [
16533 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16534 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16537 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16538 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16539 * paged from the remote server.
16540 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16541 * @cfg {String} root name of the property which contains the Array of row objects.
16542 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16543 * @cfg {Array} fields Array of field definition objects
16545 * Create a new JsonReader
16546 * @param {Object} meta Metadata configuration options
16547 * @param {Object} recordType Either an Array of field definition objects,
16548 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16550 Roo.data.JsonReader = function(meta, recordType){
16553 // set some defaults:
16554 Roo.applyIf(meta, {
16555 totalProperty: 'total',
16556 successProperty : 'success',
16561 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16563 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16565 readerType : 'Json',
16568 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16569 * Used by Store query builder to append _requestMeta to params.
16572 metaFromRemote : false,
16574 * This method is only used by a DataProxy which has retrieved data from a remote server.
16575 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16576 * @return {Object} data A data block which is used by an Roo.data.Store object as
16577 * a cache of Roo.data.Records.
16579 read : function(response){
16580 var json = response.responseText;
16582 var o = /* eval:var:o */ eval("("+json+")");
16584 throw {message: "JsonReader.read: Json object not found"};
16590 this.metaFromRemote = true;
16591 this.meta = o.metaData;
16592 this.recordType = Roo.data.Record.create(o.metaData.fields);
16593 this.onMetaChange(this.meta, this.recordType, o);
16595 return this.readRecords(o);
16598 // private function a store will implement
16599 onMetaChange : function(meta, recordType, o){
16606 simpleAccess: function(obj, subsc) {
16613 getJsonAccessor: function(){
16615 return function(expr) {
16617 return(re.test(expr))
16618 ? new Function("obj", "return obj." + expr)
16623 return Roo.emptyFn;
16628 * Create a data block containing Roo.data.Records from an XML document.
16629 * @param {Object} o An object which contains an Array of row objects in the property specified
16630 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16631 * which contains the total size of the dataset.
16632 * @return {Object} data A data block which is used by an Roo.data.Store object as
16633 * a cache of Roo.data.Records.
16635 readRecords : function(o){
16637 * After any data loads, the raw JSON data is available for further custom processing.
16641 var s = this.meta, Record = this.recordType,
16642 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16644 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16646 if(s.totalProperty) {
16647 this.getTotal = this.getJsonAccessor(s.totalProperty);
16649 if(s.successProperty) {
16650 this.getSuccess = this.getJsonAccessor(s.successProperty);
16652 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16654 var g = this.getJsonAccessor(s.id);
16655 this.getId = function(rec) {
16657 return (r === undefined || r === "") ? null : r;
16660 this.getId = function(){return null;};
16663 for(var jj = 0; jj < fl; jj++){
16665 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16666 this.ef[jj] = this.getJsonAccessor(map);
16670 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16671 if(s.totalProperty){
16672 var vt = parseInt(this.getTotal(o), 10);
16677 if(s.successProperty){
16678 var vs = this.getSuccess(o);
16679 if(vs === false || vs === 'false'){
16684 for(var i = 0; i < c; i++){
16687 var id = this.getId(n);
16688 for(var j = 0; j < fl; j++){
16690 var v = this.ef[j](n);
16692 Roo.log('missing convert for ' + f.name);
16696 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16700 raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16706 var record = new Record(values, id);
16708 records[i] = record;
16714 totalRecords : totalRecords
16717 // used when loading children.. @see loadDataFromChildren
16718 toLoadData: function(rec)
16720 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16721 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16722 return { data : data, total : data.length };
16727 * Ext JS Library 1.1.1
16728 * Copyright(c) 2006-2007, Ext JS, LLC.
16730 * Originally Released Under LGPL - original licence link has changed is not relivant.
16733 * <script type="text/javascript">
16737 * @class Roo.data.ArrayReader
16738 * @extends Roo.data.DataReader
16739 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16740 * Each element of that Array represents a row of data fields. The
16741 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16742 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16746 var RecordDef = Roo.data.Record.create([
16747 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16748 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16750 var myReader = new Roo.data.ArrayReader({
16751 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16755 * This would consume an Array like this:
16757 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16761 * Create a new JsonReader
16762 * @param {Object} meta Metadata configuration options.
16763 * @param {Object|Array} recordType Either an Array of field definition objects
16765 * @cfg {Array} fields Array of field definition objects
16766 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16767 * as specified to {@link Roo.data.Record#create},
16768 * or an {@link Roo.data.Record} object
16771 * created using {@link Roo.data.Record#create}.
16773 Roo.data.ArrayReader = function(meta, recordType)
16775 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16778 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16781 * Create a data block containing Roo.data.Records from an XML document.
16782 * @param {Object} o An Array of row objects which represents the dataset.
16783 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16784 * a cache of Roo.data.Records.
16786 readRecords : function(o)
16788 var sid = this.meta ? this.meta.id : null;
16789 var recordType = this.recordType, fields = recordType.prototype.fields;
16792 for(var i = 0; i < root.length; i++){
16795 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16796 for(var j = 0, jlen = fields.length; j < jlen; j++){
16797 var f = fields.items[j];
16798 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16799 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16801 values[f.name] = v;
16803 var record = new recordType(values, id);
16805 records[records.length] = record;
16809 totalRecords : records.length
16812 // used when loading children.. @see loadDataFromChildren
16813 toLoadData: function(rec)
16815 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16816 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16827 * @class Roo.bootstrap.form.ComboBox
16828 * @extends Roo.bootstrap.form.TriggerField
16829 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16830 * @cfg {Boolean} append (true|false) default false
16831 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16832 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16833 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16834 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16835 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16836 * @cfg {Boolean} animate default true
16837 * @cfg {Boolean} emptyResultText only for touch device
16838 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16839 * @cfg {String} emptyTitle default ''
16840 * @cfg {Number} width fixed with? experimental
16842 * Create a new ComboBox.
16843 * @param {Object} config Configuration options
16845 Roo.bootstrap.form.ComboBox = function(config){
16846 Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16850 * Fires when the dropdown list is expanded
16851 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16856 * Fires when the dropdown list is collapsed
16857 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16861 * @event beforeselect
16862 * Fires before a list item is selected. Return false to cancel the selection.
16863 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16864 * @param {Roo.data.Record} record The data record returned from the underlying store
16865 * @param {Number} index The index of the selected item in the dropdown list
16867 'beforeselect' : true,
16870 * Fires when a list item is selected
16871 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16872 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16873 * @param {Number} index The index of the selected item in the dropdown list
16877 * @event beforequery
16878 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16879 * The event object passed has these properties:
16880 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16881 * @param {String} query The query
16882 * @param {Boolean} forceAll true to force "all" query
16883 * @param {Boolean} cancel true to cancel the query
16884 * @param {Object} e The query event object
16886 'beforequery': true,
16889 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16890 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16895 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16896 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16897 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16902 * Fires when the remove value from the combobox array
16903 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16907 * @event afterremove
16908 * Fires when the remove value from the combobox array
16909 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16911 'afterremove' : true,
16913 * @event specialfilter
16914 * Fires when specialfilter
16915 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16917 'specialfilter' : true,
16920 * Fires when tick the element
16921 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16925 * @event touchviewdisplay
16926 * Fires when touch view require special display (default is using displayField)
16927 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16928 * @param {Object} cfg set html .
16930 'touchviewdisplay' : true
16935 this.tickItems = [];
16937 this.selectedIndex = -1;
16938 if(this.mode == 'local'){
16939 if(config.queryDelay === undefined){
16940 this.queryDelay = 10;
16942 if(config.minChars === undefined){
16948 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16951 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16952 * rendering into an Roo.Editor, defaults to false)
16955 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16956 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16959 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16962 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16963 * the dropdown list (defaults to undefined, with no header element)
16967 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16971 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16973 listWidth: undefined,
16975 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16976 * mode = 'remote' or 'text' if mode = 'local')
16978 displayField: undefined,
16981 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16982 * mode = 'remote' or 'value' if mode = 'local').
16983 * Note: use of a valueField requires the user make a selection
16984 * in order for a value to be mapped.
16986 valueField: undefined,
16988 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16993 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16994 * field's data value (defaults to the underlying DOM element's name)
16996 hiddenName: undefined,
16998 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17002 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17004 selectedClass: 'active',
17007 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17011 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17012 * anchor positions (defaults to 'tl-bl')
17014 listAlign: 'tl-bl?',
17016 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17020 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
17021 * query specified by the allQuery config option (defaults to 'query')
17023 triggerAction: 'query',
17025 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17026 * (defaults to 4, does not apply if editable = false)
17030 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17031 * delay (typeAheadDelay) if it matches a known value (defaults to false)
17035 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17036 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17040 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17041 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
17045 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
17046 * when editable = true (defaults to false)
17048 selectOnFocus:false,
17050 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17052 queryParam: 'query',
17054 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
17055 * when mode = 'remote' (defaults to 'Loading...')
17057 loadingText: 'Loading...',
17059 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17063 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17067 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17068 * traditional select (defaults to true)
17072 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17076 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17080 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17081 * listWidth has a higher value)
17085 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17086 * allow the user to set arbitrary text into the field (defaults to false)
17088 forceSelection:false,
17090 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17091 * if typeAhead = true (defaults to 250)
17093 typeAheadDelay : 250,
17095 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17096 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17098 valueNotFoundText : undefined,
17100 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17102 blockFocus : false,
17105 * @cfg {Boolean} disableClear Disable showing of clear button.
17107 disableClear : false,
17109 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
17111 alwaysQuery : false,
17114 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
17119 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17121 invalidClass : "has-warning",
17124 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17126 validClass : "has-success",
17129 * @cfg {Boolean} specialFilter (true|false) special filter default false
17131 specialFilter : false,
17134 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17136 mobileTouchView : true,
17139 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17141 useNativeIOS : false,
17144 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17146 mobile_restrict_height : false,
17148 ios_options : false,
17160 btnPosition : 'right',
17161 triggerList : true,
17162 showToggleBtn : true,
17164 emptyResultText: 'Empty',
17165 triggerText : 'Select',
17169 // element that contains real text value.. (when hidden is used..)
17171 getAutoCreate : function()
17176 * Render classic select for iso
17179 if(Roo.isIOS && this.useNativeIOS){
17180 cfg = this.getAutoCreateNativeIOS();
17188 if(Roo.isTouch && this.mobileTouchView){
17189 cfg = this.getAutoCreateTouchView();
17196 if(!this.tickable){
17197 cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17202 * ComboBox with tickable selections
17205 var align = this.labelAlign || this.parentLabelAlign();
17208 cls : 'form-group roo-combobox-tickable' //input-group
17211 var btn_text_select = '';
17212 var btn_text_done = '';
17213 var btn_text_cancel = '';
17215 if (this.btn_text_show) {
17216 btn_text_select = 'Select';
17217 btn_text_done = 'Done';
17218 btn_text_cancel = 'Cancel';
17223 cls : 'tickable-buttons',
17228 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17229 //html : this.triggerText
17230 html: btn_text_select
17236 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17238 html: btn_text_done
17244 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17246 html: btn_text_cancel
17252 buttons.cn.unshift({
17254 cls: 'roo-select2-search-field-input'
17260 Roo.each(buttons.cn, function(c){
17262 c.cls += ' btn-' + _this.size;
17265 if (_this.disabled) {
17272 style : 'display: contents',
17277 cls: 'form-hidden-field'
17281 cls: 'roo-select2-choices',
17285 cls: 'roo-select2-search-field',
17296 cls: 'roo-select2-container input-group roo-select2-container-multi',
17302 // cls: 'typeahead typeahead-long dropdown-menu',
17303 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
17308 if(this.hasFeedback && !this.allowBlank){
17312 cls: 'glyphicon form-control-feedback'
17315 combobox.cn.push(feedback);
17322 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17323 tooltip : 'This field is required'
17325 if (Roo.bootstrap.version == 4) {
17328 style : 'display:none'
17331 if (align ==='left' && this.fieldLabel.length) {
17333 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
17340 cls : 'control-label col-form-label',
17341 html : this.fieldLabel
17353 var labelCfg = cfg.cn[1];
17354 var contentCfg = cfg.cn[2];
17357 if(this.indicatorpos == 'right'){
17363 cls : 'control-label col-form-label',
17367 html : this.fieldLabel
17383 labelCfg = cfg.cn[0];
17384 contentCfg = cfg.cn[1];
17388 if(this.labelWidth > 12){
17389 labelCfg.style = "width: " + this.labelWidth + 'px';
17391 if(this.width * 1 > 0){
17392 contentCfg.style = "width: " + this.width + 'px';
17394 if(this.labelWidth < 13 && this.labelmd == 0){
17395 this.labelmd = this.labelWidth;
17398 if(this.labellg > 0){
17399 labelCfg.cls += ' col-lg-' + this.labellg;
17400 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17403 if(this.labelmd > 0){
17404 labelCfg.cls += ' col-md-' + this.labelmd;
17405 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17408 if(this.labelsm > 0){
17409 labelCfg.cls += ' col-sm-' + this.labelsm;
17410 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17413 if(this.labelxs > 0){
17414 labelCfg.cls += ' col-xs-' + this.labelxs;
17415 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17419 } else if ( this.fieldLabel.length) {
17420 // Roo.log(" label");
17425 //cls : 'input-group-addon',
17426 html : this.fieldLabel
17431 if(this.indicatorpos == 'right'){
17435 //cls : 'input-group-addon',
17436 html : this.fieldLabel
17446 // Roo.log(" no label && no align");
17453 ['xs','sm','md','lg'].map(function(size){
17454 if (settings[size]) {
17455 cfg.cls += ' col-' + size + '-' + settings[size];
17463 _initEventsCalled : false,
17466 initEvents: function()
17468 if (this._initEventsCalled) { // as we call render... prevent looping...
17471 this._initEventsCalled = true;
17474 throw "can not find store for combo";
17477 this.indicator = this.indicatorEl();
17479 this.store = Roo.factory(this.store, Roo.data);
17480 this.store.parent = this;
17482 // if we are building from html. then this element is so complex, that we can not really
17483 // use the rendered HTML.
17484 // so we have to trash and replace the previous code.
17485 if (Roo.XComponent.build_from_html) {
17486 // remove this element....
17487 var e = this.el.dom, k=0;
17488 while (e ) { e = e.previousSibling; ++k;}
17493 this.rendered = false;
17495 this.render(this.parent().getChildContainer(true), k);
17498 if(Roo.isIOS && this.useNativeIOS){
17499 this.initIOSView();
17507 if(Roo.isTouch && this.mobileTouchView){
17508 this.initTouchView();
17513 this.initTickableEvents();
17517 Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17519 if(this.hiddenName){
17521 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17523 this.hiddenField.dom.value =
17524 this.hiddenValue !== undefined ? this.hiddenValue :
17525 this.value !== undefined ? this.value : '';
17527 // prevent input submission
17528 this.el.dom.removeAttribute('name');
17529 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17534 // this.el.dom.setAttribute('autocomplete', 'off');
17537 var cls = 'x-combo-list';
17539 //this.list = new Roo.Layer({
17540 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17546 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17547 _this.list.setWidth(lw);
17550 this.list.on('mouseover', this.onViewOver, this);
17551 this.list.on('mousemove', this.onViewMove, this);
17552 this.list.on('scroll', this.onViewScroll, this);
17555 this.list.swallowEvent('mousewheel');
17556 this.assetHeight = 0;
17559 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17560 this.assetHeight += this.header.getHeight();
17563 this.innerList = this.list.createChild({cls:cls+'-inner'});
17564 this.innerList.on('mouseover', this.onViewOver, this);
17565 this.innerList.on('mousemove', this.onViewMove, this);
17566 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17568 if(this.allowBlank && !this.pageSize && !this.disableClear){
17569 this.footer = this.list.createChild({cls:cls+'-ft'});
17570 this.pageTb = new Roo.Toolbar(this.footer);
17574 this.footer = this.list.createChild({cls:cls+'-ft'});
17575 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17576 {pageSize: this.pageSize});
17580 if (this.pageTb && this.allowBlank && !this.disableClear) {
17582 this.pageTb.add(new Roo.Toolbar.Fill(), {
17583 cls: 'x-btn-icon x-btn-clear',
17585 handler: function()
17588 _this.clearValue();
17589 _this.onSelect(false, -1);
17594 this.assetHeight += this.footer.getHeight();
17599 this.tpl = Roo.bootstrap.version == 4 ?
17600 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17601 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17604 this.view = new Roo.View(this.list, this.tpl, {
17605 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17607 //this.view.wrapEl.setDisplayed(false);
17608 this.view.on('click', this.onViewClick, this);
17611 this.store.on('beforeload', this.onBeforeLoad, this);
17612 this.store.on('load', this.onLoad, this);
17613 this.store.on('loadexception', this.onLoadException, this);
17615 if(this.resizable){
17616 this.resizer = new Roo.Resizable(this.list, {
17617 pinned:true, handles:'se'
17619 this.resizer.on('resize', function(r, w, h){
17620 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17621 this.listWidth = w;
17622 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17623 this.restrictHeight();
17625 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17628 if(!this.editable){
17629 this.editable = true;
17630 this.setEditable(false);
17635 if (typeof(this.events.add.listeners) != 'undefined') {
17637 this.addicon = this.wrap.createChild(
17638 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17640 this.addicon.on('click', function(e) {
17641 this.fireEvent('add', this);
17644 if (typeof(this.events.edit.listeners) != 'undefined') {
17646 this.editicon = this.wrap.createChild(
17647 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17648 if (this.addicon) {
17649 this.editicon.setStyle('margin-left', '40px');
17651 this.editicon.on('click', function(e) {
17653 // we fire even if inothing is selected..
17654 this.fireEvent('edit', this, this.lastData );
17660 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17661 "up" : function(e){
17662 this.inKeyMode = true;
17666 "down" : function(e){
17667 if(!this.isExpanded()){
17668 this.onTriggerClick();
17670 this.inKeyMode = true;
17675 "enter" : function(e){
17676 // this.onViewClick();
17680 if(this.fireEvent("specialkey", this, e)){
17681 this.onViewClick(false);
17687 "esc" : function(e){
17691 "tab" : function(e){
17694 if(this.fireEvent("specialkey", this, e)){
17695 this.onViewClick(false);
17703 doRelay : function(foo, bar, hname){
17704 if(hname == 'down' || this.scope.isExpanded()){
17705 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17714 this.queryDelay = Math.max(this.queryDelay || 10,
17715 this.mode == 'local' ? 10 : 250);
17718 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17720 if(this.typeAhead){
17721 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17723 if(this.editable !== false){
17724 this.inputEl().on("keyup", this.onKeyUp, this);
17726 if(this.forceSelection){
17727 this.inputEl().on('blur', this.doForce, this);
17731 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17732 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17736 initTickableEvents: function()
17740 if(this.hiddenName){
17742 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17744 this.hiddenField.dom.value =
17745 this.hiddenValue !== undefined ? this.hiddenValue :
17746 this.value !== undefined ? this.value : '';
17748 // prevent input submission
17749 this.el.dom.removeAttribute('name');
17750 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17755 // this.list = this.el.select('ul.dropdown-menu',true).first();
17757 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17758 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17759 if(this.triggerList){
17760 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17763 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17764 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17766 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17767 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17769 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17770 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17772 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17773 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17774 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17777 this.cancelBtn.hide();
17782 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17783 _this.list.setWidth(lw);
17786 this.list.on('mouseover', this.onViewOver, this);
17787 this.list.on('mousemove', this.onViewMove, this);
17789 this.list.on('scroll', this.onViewScroll, this);
17792 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17793 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17796 this.view = new Roo.View(this.list, this.tpl, {
17801 selectedClass: this.selectedClass
17804 //this.view.wrapEl.setDisplayed(false);
17805 this.view.on('click', this.onViewClick, this);
17809 this.store.on('beforeload', this.onBeforeLoad, this);
17810 this.store.on('load', this.onLoad, this);
17811 this.store.on('loadexception', this.onLoadException, this);
17814 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17815 "up" : function(e){
17816 this.inKeyMode = true;
17820 "down" : function(e){
17821 this.inKeyMode = true;
17825 "enter" : function(e){
17826 if(this.fireEvent("specialkey", this, e)){
17827 this.onViewClick(false);
17833 "esc" : function(e){
17834 this.onTickableFooterButtonClick(e, false, false);
17837 "tab" : function(e){
17838 this.fireEvent("specialkey", this, e);
17840 this.onTickableFooterButtonClick(e, false, false);
17847 doRelay : function(e, fn, key){
17848 if(this.scope.isExpanded()){
17849 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17858 this.queryDelay = Math.max(this.queryDelay || 10,
17859 this.mode == 'local' ? 10 : 250);
17862 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17864 if(this.typeAhead){
17865 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17868 if(this.editable !== false){
17869 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17872 this.indicator = this.indicatorEl();
17874 if(this.indicator){
17875 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17876 this.indicator.hide();
17881 onDestroy : function(){
17883 this.view.setStore(null);
17884 this.view.el.removeAllListeners();
17885 this.view.el.remove();
17886 this.view.purgeListeners();
17889 this.list.dom.innerHTML = '';
17893 this.store.un('beforeload', this.onBeforeLoad, this);
17894 this.store.un('load', this.onLoad, this);
17895 this.store.un('loadexception', this.onLoadException, this);
17897 Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17901 fireKey : function(e){
17902 if(e.isNavKeyPress() && !this.list.isVisible()){
17903 this.fireEvent("specialkey", this, e);
17908 onResize: function(w, h)
17912 // Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17914 // if(typeof w != 'number'){
17915 // // we do not handle it!?!?
17918 // var tw = this.trigger.getWidth();
17919 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17920 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17922 // this.inputEl().setWidth( this.adjustWidth('input', x));
17924 // //this.trigger.setStyle('left', x+'px');
17926 // if(this.list && this.listWidth === undefined){
17927 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17928 // this.list.setWidth(lw);
17929 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17937 * Allow or prevent the user from directly editing the field text. If false is passed,
17938 * the user will only be able to select from the items defined in the dropdown list. This method
17939 * is the runtime equivalent of setting the 'editable' config option at config time.
17940 * @param {Boolean} value True to allow the user to directly edit the field text
17942 setEditable : function(value){
17943 if(value == this.editable){
17946 this.editable = value;
17948 this.inputEl().dom.setAttribute('readOnly', true);
17949 this.inputEl().on('mousedown', this.onTriggerClick, this);
17950 this.inputEl().addClass('x-combo-noedit');
17952 this.inputEl().dom.removeAttribute('readOnly');
17953 this.inputEl().un('mousedown', this.onTriggerClick, this);
17954 this.inputEl().removeClass('x-combo-noedit');
17960 onBeforeLoad : function(combo,opts){
17961 if(!this.hasFocus){
17965 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17967 this.restrictHeight();
17968 this.selectedIndex = -1;
17972 onLoad : function(){
17974 this.hasQuery = false;
17976 if(!this.hasFocus){
17980 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17981 this.loading.hide();
17984 if(this.store.getCount() > 0){
17987 this.restrictHeight();
17988 if(this.lastQuery == this.allQuery){
17989 if(this.editable && !this.tickable){
17990 this.inputEl().dom.select();
17994 !this.selectByValue(this.value, true) &&
17997 !this.store.lastOptions ||
17998 typeof(this.store.lastOptions.add) == 'undefined' ||
17999 this.store.lastOptions.add != true
18002 this.select(0, true);
18005 if(this.autoFocus){
18008 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18009 this.taTask.delay(this.typeAheadDelay);
18013 this.onEmptyResults();
18019 onLoadException : function()
18021 this.hasQuery = false;
18023 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18024 this.loading.hide();
18027 if(this.tickable && this.editable){
18032 // only causes errors at present
18033 //Roo.log(this.store.reader.jsonData);
18034 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18036 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18042 onTypeAhead : function(){
18043 if(this.store.getCount() > 0){
18044 var r = this.store.getAt(0);
18045 var newValue = r.data[this.displayField];
18046 var len = newValue.length;
18047 var selStart = this.getRawValue().length;
18049 if(selStart != len){
18050 this.setRawValue(newValue);
18051 this.selectText(selStart, newValue.length);
18057 onSelect : function(record, index){
18059 if(this.fireEvent('beforeselect', this, record, index) !== false){
18061 this.setFromData(index > -1 ? record.data : false);
18064 this.fireEvent('select', this, record, index);
18069 * Returns the currently selected field value or empty string if no value is set.
18070 * @return {String} value The selected value
18072 getValue : function()
18074 if(Roo.isIOS && this.useNativeIOS){
18075 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18079 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18082 if(this.valueField){
18083 return typeof this.value != 'undefined' ? this.value : '';
18085 return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18089 getRawValue : function()
18091 if(Roo.isIOS && this.useNativeIOS){
18092 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18095 var v = this.inputEl().getValue();
18101 * Clears any text/value currently set in the field
18103 clearValue : function(){
18105 if(this.hiddenField){
18106 this.hiddenField.dom.value = '';
18109 this.setRawValue('');
18110 this.lastSelectionText = '';
18111 this.lastData = false;
18113 var close = this.closeTriggerEl();
18124 * Sets the specified value into the field. If the value finds a match, the corresponding record text
18125 * will be displayed in the field. If the value does not match the data value of an existing item,
18126 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18127 * Otherwise the field will be blank (although the value will still be set).
18128 * @param {String} value The value to match
18130 setValue : function(v)
18132 if(Roo.isIOS && this.useNativeIOS){
18133 this.setIOSValue(v);
18143 if(this.valueField){
18144 var r = this.findRecord(this.valueField, v);
18146 text = r.data[this.displayField];
18147 }else if(this.valueNotFoundText !== undefined){
18148 text = this.valueNotFoundText;
18151 this.lastSelectionText = text;
18152 if(this.hiddenField){
18153 this.hiddenField.dom.value = v;
18155 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18158 var close = this.closeTriggerEl();
18161 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18167 * @property {Object} the last set data for the element
18172 * Sets the value of the field based on a object which is related to the record format for the store.
18173 * @param {Object} value the value to set as. or false on reset?
18175 setFromData : function(o){
18182 var dv = ''; // display value
18183 var vv = ''; // value value..
18185 if (this.displayField) {
18186 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18188 // this is an error condition!!!
18189 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18192 if(this.valueField){
18193 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18196 var close = this.closeTriggerEl();
18199 if(dv.length || vv * 1 > 0){
18201 this.blockFocus=true;
18207 if(this.hiddenField){
18208 this.hiddenField.dom.value = vv;
18210 this.lastSelectionText = dv;
18211 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18215 // no hidden field.. - we store the value in 'value', but still display
18216 // display field!!!!
18217 this.lastSelectionText = dv;
18218 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18225 reset : function(){
18226 // overridden so that last data is reset..
18233 this.setValue(this.originalValue);
18234 //this.clearInvalid();
18235 this.lastData = false;
18237 this.view.clearSelections();
18243 findRecord : function(prop, value){
18245 if(this.store.getCount() > 0){
18246 this.store.each(function(r){
18247 if(r.data[prop] == value){
18257 getName: function()
18259 // returns hidden if it's set..
18260 if (!this.rendered) {return ''};
18261 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
18265 onViewMove : function(e, t){
18266 this.inKeyMode = false;
18270 onViewOver : function(e, t){
18271 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18274 var item = this.view.findItemFromChild(t);
18277 var index = this.view.indexOf(item);
18278 this.select(index, false);
18283 onViewClick : function(view, doFocus, el, e)
18285 var index = this.view.getSelectedIndexes()[0];
18287 var r = this.store.getAt(index);
18291 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18298 Roo.each(this.tickItems, function(v,k){
18300 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18302 _this.tickItems.splice(k, 1);
18304 if(typeof(e) == 'undefined' && view == false){
18305 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18317 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18318 this.tickItems.push(r.data);
18321 if(typeof(e) == 'undefined' && view == false){
18322 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18329 this.onSelect(r, index);
18331 if(doFocus !== false && !this.blockFocus){
18332 this.inputEl().focus();
18337 restrictHeight : function(){
18338 //this.innerList.dom.style.height = '';
18339 //var inner = this.innerList.dom;
18340 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18341 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18342 //this.list.beginUpdate();
18343 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18344 this.list.alignTo(this.inputEl(), this.listAlign);
18345 this.list.alignTo(this.inputEl(), this.listAlign);
18346 //this.list.endUpdate();
18350 onEmptyResults : function(){
18352 if(this.tickable && this.editable){
18353 this.hasFocus = false;
18354 this.restrictHeight();
18362 * Returns true if the dropdown list is expanded, else false.
18364 isExpanded : function(){
18365 return this.list.isVisible();
18369 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18370 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18371 * @param {String} value The data value of the item to select
18372 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18373 * selected item if it is not currently in view (defaults to true)
18374 * @return {Boolean} True if the value matched an item in the list, else false
18376 selectByValue : function(v, scrollIntoView){
18377 if(v !== undefined && v !== null){
18378 var r = this.findRecord(this.valueField || this.displayField, v);
18380 this.select(this.store.indexOf(r), scrollIntoView);
18388 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18389 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18390 * @param {Number} index The zero-based index of the list item to select
18391 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18392 * selected item if it is not currently in view (defaults to true)
18394 select : function(index, scrollIntoView){
18395 this.selectedIndex = index;
18396 this.view.select(index);
18397 if(scrollIntoView !== false){
18398 var el = this.view.getNode(index);
18400 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18403 this.list.scrollChildIntoView(el, false);
18409 selectNext : function(){
18410 var ct = this.store.getCount();
18412 if(this.selectedIndex == -1){
18414 }else if(this.selectedIndex < ct-1){
18415 this.select(this.selectedIndex+1);
18421 selectPrev : function(){
18422 var ct = this.store.getCount();
18424 if(this.selectedIndex == -1){
18426 }else if(this.selectedIndex != 0){
18427 this.select(this.selectedIndex-1);
18433 onKeyUp : function(e){
18434 if(this.editable !== false && !e.isSpecialKey()){
18435 this.lastKey = e.getKey();
18436 this.dqTask.delay(this.queryDelay);
18441 validateBlur : function(){
18442 return !this.list || !this.list.isVisible();
18446 initQuery : function(){
18448 var v = this.getRawValue();
18450 if(this.tickable && this.editable){
18451 v = this.tickableInputEl().getValue();
18458 doForce : function(){
18459 if(this.inputEl().dom.value.length > 0){
18460 this.inputEl().dom.value =
18461 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18467 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
18468 * query allowing the query action to be canceled if needed.
18469 * @param {String} query The SQL query to execute
18470 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18471 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
18472 * saved in the current store (defaults to false)
18474 doQuery : function(q, forceAll){
18476 if(q === undefined || q === null){
18481 forceAll: forceAll,
18485 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18490 forceAll = qe.forceAll;
18491 if(forceAll === true || (q.length >= this.minChars)){
18493 this.hasQuery = true;
18495 if(this.lastQuery != q || this.alwaysQuery){
18496 this.lastQuery = q;
18497 if(this.mode == 'local'){
18498 this.selectedIndex = -1;
18500 this.store.clearFilter();
18503 if(this.specialFilter){
18504 this.fireEvent('specialfilter', this);
18509 this.store.filter(this.displayField, q);
18512 this.store.fireEvent("datachanged", this.store);
18519 this.store.baseParams[this.queryParam] = q;
18521 var options = {params : this.getParams(q)};
18524 options.add = true;
18525 options.params.start = this.page * this.pageSize;
18528 this.store.load(options);
18531 * this code will make the page width larger, at the beginning, the list not align correctly,
18532 * we should expand the list on onLoad
18533 * so command out it
18538 this.selectedIndex = -1;
18543 this.loadNext = false;
18547 getParams : function(q){
18549 //p[this.queryParam] = q;
18553 p.limit = this.pageSize;
18559 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18561 collapse : function(){
18562 if(!this.isExpanded()){
18568 this.hasFocus = false;
18572 this.cancelBtn.hide();
18573 this.trigger.show();
18576 this.tickableInputEl().dom.value = '';
18577 this.tickableInputEl().blur();
18582 Roo.get(document).un('mousedown', this.collapseIf, this);
18583 Roo.get(document).un('mousewheel', this.collapseIf, this);
18584 if (!this.editable) {
18585 Roo.get(document).un('keydown', this.listKeyPress, this);
18587 this.fireEvent('collapse', this);
18593 collapseIf : function(e){
18594 var in_combo = e.within(this.el);
18595 var in_list = e.within(this.list);
18596 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18598 if (in_combo || in_list || is_list) {
18599 //e.stopPropagation();
18604 this.onTickableFooterButtonClick(e, false, false);
18612 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18614 expand : function(){
18616 if(this.isExpanded() || !this.hasFocus){
18620 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18621 this.list.setWidth(lw);
18627 this.restrictHeight();
18631 this.tickItems = Roo.apply([], this.item);
18634 this.cancelBtn.show();
18635 this.trigger.hide();
18638 this.tickableInputEl().focus();
18643 Roo.get(document).on('mousedown', this.collapseIf, this);
18644 Roo.get(document).on('mousewheel', this.collapseIf, this);
18645 if (!this.editable) {
18646 Roo.get(document).on('keydown', this.listKeyPress, this);
18649 this.fireEvent('expand', this);
18653 // Implements the default empty TriggerField.onTriggerClick function
18654 onTriggerClick : function(e)
18656 Roo.log('trigger click');
18658 if(this.disabled || !this.triggerList){
18663 this.loadNext = false;
18665 if(this.isExpanded()){
18667 if (!this.blockFocus) {
18668 this.inputEl().focus();
18672 this.hasFocus = true;
18673 if(this.triggerAction == 'all') {
18674 this.doQuery(this.allQuery, true);
18676 this.doQuery(this.getRawValue());
18678 if (!this.blockFocus) {
18679 this.inputEl().focus();
18684 onTickableTriggerClick : function(e)
18691 this.loadNext = false;
18692 this.hasFocus = true;
18694 if(this.triggerAction == 'all') {
18695 this.doQuery(this.allQuery, true);
18697 this.doQuery(this.getRawValue());
18701 onSearchFieldClick : function(e)
18703 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18704 this.onTickableFooterButtonClick(e, false, false);
18708 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18713 this.loadNext = false;
18714 this.hasFocus = true;
18716 if(this.triggerAction == 'all') {
18717 this.doQuery(this.allQuery, true);
18719 this.doQuery(this.getRawValue());
18723 listKeyPress : function(e)
18725 //Roo.log('listkeypress');
18726 // scroll to first matching element based on key pres..
18727 if (e.isSpecialKey()) {
18730 var k = String.fromCharCode(e.getKey()).toUpperCase();
18733 var csel = this.view.getSelectedNodes();
18734 var cselitem = false;
18736 var ix = this.view.indexOf(csel[0]);
18737 cselitem = this.store.getAt(ix);
18738 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18744 this.store.each(function(v) {
18746 // start at existing selection.
18747 if (cselitem.id == v.id) {
18753 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18754 match = this.store.indexOf(v);
18760 if (match === false) {
18761 return true; // no more action?
18764 this.view.select(match);
18765 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18766 sn.scrollIntoView(sn.dom.parentNode, false);
18769 onViewScroll : function(e, t){
18771 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){
18775 this.hasQuery = true;
18777 this.loading = this.list.select('.loading', true).first();
18779 if(this.loading === null){
18780 this.list.createChild({
18782 cls: 'loading roo-select2-more-results roo-select2-active',
18783 html: 'Loading more results...'
18786 this.loading = this.list.select('.loading', true).first();
18788 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18790 this.loading.hide();
18793 this.loading.show();
18798 this.loadNext = true;
18800 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18805 addItem : function(o)
18807 var dv = ''; // display value
18809 if (this.displayField) {
18810 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18812 // this is an error condition!!!
18813 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18820 var choice = this.choices.createChild({
18822 cls: 'roo-select2-search-choice',
18831 cls: 'roo-select2-search-choice-close fa fa-times',
18836 }, this.searchField);
18838 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18840 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18848 this.inputEl().dom.value = '';
18853 onRemoveItem : function(e, _self, o)
18855 e.preventDefault();
18857 this.lastItem = Roo.apply([], this.item);
18859 var index = this.item.indexOf(o.data) * 1;
18862 Roo.log('not this item?!');
18866 this.item.splice(index, 1);
18871 this.fireEvent('remove', this, e);
18877 syncValue : function()
18879 if(!this.item.length){
18886 Roo.each(this.item, function(i){
18887 if(_this.valueField){
18888 value.push(i[_this.valueField]);
18895 this.value = value.join(',');
18897 if(this.hiddenField){
18898 this.hiddenField.dom.value = this.value;
18901 this.store.fireEvent("datachanged", this.store);
18906 clearItem : function()
18908 if(!this.multiple){
18914 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18922 if(this.tickable && !Roo.isTouch){
18923 this.view.refresh();
18927 inputEl: function ()
18929 if(Roo.isIOS && this.useNativeIOS){
18930 return this.el.select('select.roo-ios-select', true).first();
18933 if(Roo.isTouch && this.mobileTouchView){
18934 return this.el.select('input.form-control',true).first();
18938 return this.searchField;
18941 return this.el.select('input.form-control',true).first();
18944 onTickableFooterButtonClick : function(e, btn, el)
18946 e.preventDefault();
18948 this.lastItem = Roo.apply([], this.item);
18950 if(btn && btn.name == 'cancel'){
18951 this.tickItems = Roo.apply([], this.item);
18960 Roo.each(this.tickItems, function(o){
18968 validate : function()
18970 if(this.getVisibilityEl().hasClass('hidden')){
18974 var v = this.getRawValue();
18977 v = this.getValue();
18980 if(this.disabled || this.allowBlank || v.length){
18985 this.markInvalid();
18989 tickableInputEl : function()
18991 if(!this.tickable || !this.editable){
18992 return this.inputEl();
18995 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18999 getAutoCreateTouchView : function()
19004 cls: 'form-group' //input-group
19010 type : this.inputType,
19011 cls : 'form-control x-combo-noedit',
19012 autocomplete: 'new-password',
19013 placeholder : this.placeholder || '',
19018 input.name = this.name;
19022 input.cls += ' input-' + this.size;
19025 if (this.disabled) {
19026 input.disabled = true;
19030 cls : 'roo-combobox-wrap',
19037 inputblock.cls += ' input-group';
19039 inputblock.cn.unshift({
19041 cls : 'input-group-addon input-group-prepend input-group-text',
19046 if(this.removable && !this.multiple){
19047 inputblock.cls += ' roo-removable';
19049 inputblock.cn.push({
19052 cls : 'roo-combo-removable-btn close'
19056 if(this.hasFeedback && !this.allowBlank){
19058 inputblock.cls += ' has-feedback';
19060 inputblock.cn.push({
19062 cls: 'glyphicon form-control-feedback'
19069 inputblock.cls += (this.before) ? '' : ' input-group';
19071 inputblock.cn.push({
19073 cls : 'input-group-addon input-group-append input-group-text',
19079 var ibwrap = inputblock;
19084 cls: 'roo-select2-choices',
19088 cls: 'roo-select2-search-field',
19101 cls: 'roo-select2-container input-group roo-touchview-combobox ',
19106 cls: 'form-hidden-field'
19112 if(!this.multiple && this.showToggleBtn){
19118 if (this.caret != false) {
19121 cls: 'fa fa-' + this.caret
19128 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19130 Roo.bootstrap.version == 3 ? caret : '',
19133 cls: 'combobox-clear',
19147 combobox.cls += ' roo-select2-container-multi';
19150 var required = this.allowBlank ? {
19152 style: 'display: none'
19155 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19156 tooltip : 'This field is required'
19159 var align = this.labelAlign || this.parentLabelAlign();
19161 if (align ==='left' && this.fieldLabel.length) {
19167 cls : 'control-label col-form-label',
19168 html : this.fieldLabel
19172 cls : 'roo-combobox-wrap ',
19179 var labelCfg = cfg.cn[1];
19180 var contentCfg = cfg.cn[2];
19183 if(this.indicatorpos == 'right'){
19188 cls : 'control-label col-form-label',
19192 html : this.fieldLabel
19198 cls : "roo-combobox-wrap ",
19206 labelCfg = cfg.cn[0];
19207 contentCfg = cfg.cn[1];
19212 if(this.labelWidth > 12){
19213 labelCfg.style = "width: " + this.labelWidth + 'px';
19216 if(this.labelWidth < 13 && this.labelmd == 0){
19217 this.labelmd = this.labelWidth;
19220 if(this.labellg > 0){
19221 labelCfg.cls += ' col-lg-' + this.labellg;
19222 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19225 if(this.labelmd > 0){
19226 labelCfg.cls += ' col-md-' + this.labelmd;
19227 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19230 if(this.labelsm > 0){
19231 labelCfg.cls += ' col-sm-' + this.labelsm;
19232 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19235 if(this.labelxs > 0){
19236 labelCfg.cls += ' col-xs-' + this.labelxs;
19237 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19241 } else if ( this.fieldLabel.length) {
19246 cls : 'control-label',
19247 html : this.fieldLabel
19258 if(this.indicatorpos == 'right'){
19262 cls : 'control-label',
19263 html : this.fieldLabel,
19281 var settings = this;
19283 ['xs','sm','md','lg'].map(function(size){
19284 if (settings[size]) {
19285 cfg.cls += ' col-' + size + '-' + settings[size];
19292 initTouchView : function()
19294 this.renderTouchView();
19296 this.touchViewEl.on('scroll', function(){
19297 this.el.dom.scrollTop = 0;
19300 this.originalValue = this.getValue();
19302 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19304 this.inputEl().on("click", this.showTouchView, this);
19305 if (this.triggerEl) {
19306 this.triggerEl.on("click", this.showTouchView, this);
19310 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19311 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19313 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19315 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19316 this.store.on('load', this.onTouchViewLoad, this);
19317 this.store.on('loadexception', this.onTouchViewLoadException, this);
19319 if(this.hiddenName){
19321 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19323 this.hiddenField.dom.value =
19324 this.hiddenValue !== undefined ? this.hiddenValue :
19325 this.value !== undefined ? this.value : '';
19327 this.el.dom.removeAttribute('name');
19328 this.hiddenField.dom.setAttribute('name', this.hiddenName);
19332 this.choices = this.el.select('ul.roo-select2-choices', true).first();
19333 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19336 if(this.removable && !this.multiple){
19337 var close = this.closeTriggerEl();
19339 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19340 close.on('click', this.removeBtnClick, this, close);
19344 * fix the bug in Safari iOS8
19346 this.inputEl().on("focus", function(e){
19347 document.activeElement.blur();
19350 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19357 renderTouchView : function()
19359 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19360 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19362 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19363 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19365 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19366 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19367 this.touchViewBodyEl.setStyle('overflow', 'auto');
19369 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19370 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19372 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19373 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19377 showTouchView : function()
19383 this.touchViewHeaderEl.hide();
19385 if(this.modalTitle.length){
19386 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19387 this.touchViewHeaderEl.show();
19390 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19391 this.touchViewEl.show();
19393 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19395 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19396 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19398 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19400 if(this.modalTitle.length){
19401 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19404 this.touchViewBodyEl.setHeight(bodyHeight);
19408 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19410 this.touchViewEl.addClass(['in','show']);
19413 if(this._touchViewMask){
19414 Roo.get(document.body).addClass("x-body-masked");
19415 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19416 this._touchViewMask.setStyle('z-index', 10000);
19417 this._touchViewMask.addClass('show');
19420 this.doTouchViewQuery();
19424 hideTouchView : function()
19426 this.touchViewEl.removeClass(['in','show']);
19430 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19432 this.touchViewEl.setStyle('display', 'none');
19435 if(this._touchViewMask){
19436 this._touchViewMask.removeClass('show');
19437 Roo.get(document.body).removeClass("x-body-masked");
19441 setTouchViewValue : function()
19448 Roo.each(this.tickItems, function(o){
19453 this.hideTouchView();
19456 doTouchViewQuery : function()
19465 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19469 if(!this.alwaysQuery || this.mode == 'local'){
19470 this.onTouchViewLoad();
19477 onTouchViewBeforeLoad : function(combo,opts)
19483 onTouchViewLoad : function()
19485 if(this.store.getCount() < 1){
19486 this.onTouchViewEmptyResults();
19490 this.clearTouchView();
19492 var rawValue = this.getRawValue();
19494 var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19496 this.tickItems = [];
19498 this.store.data.each(function(d, rowIndex){
19499 var row = this.touchViewListGroup.createChild(template);
19501 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19502 row.addClass(d.data.cls);
19505 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19508 html : d.data[this.displayField]
19511 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19512 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19515 row.removeClass('selected');
19516 if(!this.multiple && this.valueField &&
19517 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19520 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19521 row.addClass('selected');
19524 if(this.multiple && this.valueField &&
19525 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19529 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19530 this.tickItems.push(d.data);
19533 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19537 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19539 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19541 if(this.modalTitle.length){
19542 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19545 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19547 if(this.mobile_restrict_height && listHeight < bodyHeight){
19548 this.touchViewBodyEl.setHeight(listHeight);
19553 if(firstChecked && listHeight > bodyHeight){
19554 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19559 onTouchViewLoadException : function()
19561 this.hideTouchView();
19564 onTouchViewEmptyResults : function()
19566 this.clearTouchView();
19568 this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19570 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19574 clearTouchView : function()
19576 this.touchViewListGroup.dom.innerHTML = '';
19579 onTouchViewClick : function(e, el, o)
19581 e.preventDefault();
19584 var rowIndex = o.rowIndex;
19586 var r = this.store.getAt(rowIndex);
19588 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19590 if(!this.multiple){
19591 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19592 c.dom.removeAttribute('checked');
19595 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19597 this.setFromData(r.data);
19599 var close = this.closeTriggerEl();
19605 this.hideTouchView();
19607 this.fireEvent('select', this, r, rowIndex);
19612 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19613 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19614 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19618 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19619 this.addItem(r.data);
19620 this.tickItems.push(r.data);
19624 getAutoCreateNativeIOS : function()
19627 cls: 'form-group' //input-group,
19632 cls : 'roo-ios-select'
19636 combobox.name = this.name;
19639 if (this.disabled) {
19640 combobox.disabled = true;
19643 var settings = this;
19645 ['xs','sm','md','lg'].map(function(size){
19646 if (settings[size]) {
19647 cfg.cls += ' col-' + size + '-' + settings[size];
19657 initIOSView : function()
19659 this.store.on('load', this.onIOSViewLoad, this);
19664 onIOSViewLoad : function()
19666 if(this.store.getCount() < 1){
19670 this.clearIOSView();
19672 if(this.allowBlank) {
19674 var default_text = '-- SELECT --';
19676 if(this.placeholder.length){
19677 default_text = this.placeholder;
19680 if(this.emptyTitle.length){
19681 default_text += ' - ' + this.emptyTitle + ' -';
19684 var opt = this.inputEl().createChild({
19687 html : default_text
19691 o[this.valueField] = 0;
19692 o[this.displayField] = default_text;
19694 this.ios_options.push({
19701 this.store.data.each(function(d, rowIndex){
19705 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19706 html = d.data[this.displayField];
19711 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19712 value = d.data[this.valueField];
19721 if(this.value == d.data[this.valueField]){
19722 option['selected'] = true;
19725 var opt = this.inputEl().createChild(option);
19727 this.ios_options.push({
19734 this.inputEl().on('change', function(){
19735 this.fireEvent('select', this);
19740 clearIOSView: function()
19742 this.inputEl().dom.innerHTML = '';
19744 this.ios_options = [];
19747 setIOSValue: function(v)
19751 if(!this.ios_options){
19755 Roo.each(this.ios_options, function(opts){
19757 opts.el.dom.removeAttribute('selected');
19759 if(opts.data[this.valueField] != v){
19763 opts.el.dom.setAttribute('selected', true);
19769 * @cfg {Boolean} grow
19773 * @cfg {Number} growMin
19777 * @cfg {Number} growMax
19786 Roo.apply(Roo.bootstrap.form.ComboBox, {
19790 cls: 'modal-header',
19812 cls: 'list-group-item',
19816 cls: 'roo-combobox-list-group-item-value'
19820 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19834 listItemCheckbox : {
19836 cls: 'list-group-item',
19840 cls: 'roo-combobox-list-group-item-value'
19844 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19860 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19865 cls: 'modal-footer',
19873 cls: 'col-xs-6 text-left',
19876 cls: 'btn btn-danger roo-touch-view-cancel',
19882 cls: 'col-xs-6 text-right',
19885 cls: 'btn btn-success roo-touch-view-ok',
19896 Roo.apply(Roo.bootstrap.form.ComboBox, {
19898 touchViewTemplate : {
19900 cls: 'modal fade roo-combobox-touch-view',
19904 cls: 'modal-dialog',
19905 style : 'position:fixed', // we have to fix position....
19909 cls: 'modal-content',
19911 Roo.bootstrap.form.ComboBox.header,
19912 Roo.bootstrap.form.ComboBox.body,
19913 Roo.bootstrap.form.ComboBox.footer
19922 * Ext JS Library 1.1.1
19923 * Copyright(c) 2006-2007, Ext JS, LLC.
19925 * Originally Released Under LGPL - original licence link has changed is not relivant.
19928 * <script type="text/javascript">
19933 * @extends Roo.util.Observable
19934 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19935 * This class also supports single and multi selection modes. <br>
19936 * Create a data model bound view:
19938 var store = new Roo.data.Store(...);
19940 var view = new Roo.View({
19942 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19944 singleSelect: true,
19945 selectedClass: "ydataview-selected",
19949 // listen for node click?
19950 view.on("click", function(vw, index, node, e){
19951 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19955 dataModel.load("foobar.xml");
19957 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19959 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19960 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19962 * Note: old style constructor is still suported (container, template, config)
19965 * Create a new View
19966 * @param {Object} config The config object
19969 Roo.View = function(config, depreciated_tpl, depreciated_config){
19971 this.parent = false;
19973 if (typeof(depreciated_tpl) == 'undefined') {
19974 // new way.. - universal constructor.
19975 Roo.apply(this, config);
19976 this.el = Roo.get(this.el);
19979 this.el = Roo.get(config);
19980 this.tpl = depreciated_tpl;
19981 Roo.apply(this, depreciated_config);
19983 this.wrapEl = this.el.wrap().wrap();
19984 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19987 if(typeof(this.tpl) == "string"){
19988 this.tpl = new Roo.Template(this.tpl);
19990 // support xtype ctors..
19991 this.tpl = new Roo.factory(this.tpl, Roo);
19995 this.tpl.compile();
20000 * @event beforeclick
20001 * Fires before a click is processed. Returns false to cancel the default action.
20002 * @param {Roo.View} this
20003 * @param {Number} index The index of the target node
20004 * @param {HTMLElement} node The target node
20005 * @param {Roo.EventObject} e The raw event object
20007 "beforeclick" : true,
20010 * Fires when a template node is clicked.
20011 * @param {Roo.View} this
20012 * @param {Number} index The index of the target node
20013 * @param {HTMLElement} node The target node
20014 * @param {Roo.EventObject} e The raw event object
20019 * Fires when a template node is double clicked.
20020 * @param {Roo.View} this
20021 * @param {Number} index The index of the target node
20022 * @param {HTMLElement} node The target node
20023 * @param {Roo.EventObject} e The raw event object
20027 * @event contextmenu
20028 * Fires when a template node is right clicked.
20029 * @param {Roo.View} this
20030 * @param {Number} index The index of the target node
20031 * @param {HTMLElement} node The target node
20032 * @param {Roo.EventObject} e The raw event object
20034 "contextmenu" : true,
20036 * @event selectionchange
20037 * Fires when the selected nodes change.
20038 * @param {Roo.View} this
20039 * @param {Array} selections Array of the selected nodes
20041 "selectionchange" : true,
20044 * @event beforeselect
20045 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20046 * @param {Roo.View} this
20047 * @param {HTMLElement} node The node to be selected
20048 * @param {Array} selections Array of currently selected nodes
20050 "beforeselect" : true,
20052 * @event preparedata
20053 * Fires on every row to render, to allow you to change the data.
20054 * @param {Roo.View} this
20055 * @param {Object} data to be rendered (change this)
20057 "preparedata" : true
20065 "click": this.onClick,
20066 "dblclick": this.onDblClick,
20067 "contextmenu": this.onContextMenu,
20071 this.selections = [];
20073 this.cmp = new Roo.CompositeElementLite([]);
20075 this.store = Roo.factory(this.store, Roo.data);
20076 this.setStore(this.store, true);
20079 if ( this.footer && this.footer.xtype) {
20081 var fctr = this.wrapEl.appendChild(document.createElement("div"));
20083 this.footer.dataSource = this.store;
20084 this.footer.container = fctr;
20085 this.footer = Roo.factory(this.footer, Roo);
20086 fctr.insertFirst(this.el);
20088 // this is a bit insane - as the paging toolbar seems to detach the el..
20089 // dom.parentNode.parentNode.parentNode
20090 // they get detached?
20094 Roo.View.superclass.constructor.call(this);
20099 Roo.extend(Roo.View, Roo.util.Observable, {
20102 * @cfg {Roo.data.Store} store Data store to load data from.
20107 * @cfg {String|Roo.Element} el The container element.
20112 * @cfg {String|Roo.Template} tpl The template used by this View
20116 * @cfg {String} dataName the named area of the template to use as the data area
20117 * Works with domtemplates roo-name="name"
20121 * @cfg {String} selectedClass The css class to add to selected nodes
20123 selectedClass : "x-view-selected",
20125 * @cfg {String} emptyText The empty text to show when nothing is loaded.
20130 * @cfg {String} text to display on mask (default Loading)
20134 * @cfg {Boolean} multiSelect Allow multiple selection
20136 multiSelect : false,
20138 * @cfg {Boolean} singleSelect Allow single selection
20140 singleSelect: false,
20143 * @cfg {Boolean} toggleSelect - selecting
20145 toggleSelect : false,
20148 * @cfg {Boolean} tickable - selecting
20153 * Returns the element this view is bound to.
20154 * @return {Roo.Element}
20156 getEl : function(){
20157 return this.wrapEl;
20163 * Refreshes the view. - called by datachanged on the store. - do not call directly.
20165 refresh : function(){
20166 //Roo.log('refresh');
20169 // if we are using something like 'domtemplate', then
20170 // the what gets used is:
20171 // t.applySubtemplate(NAME, data, wrapping data..)
20172 // the outer template then get' applied with
20173 // the store 'extra data'
20174 // and the body get's added to the
20175 // roo-name="data" node?
20176 // <span class='roo-tpl-{name}'></span> ?????
20180 this.clearSelections();
20181 this.el.update("");
20183 var records = this.store.getRange();
20184 if(records.length < 1) {
20186 // is this valid?? = should it render a template??
20188 this.el.update(this.emptyText);
20192 if (this.dataName) {
20193 this.el.update(t.apply(this.store.meta)); //????
20194 el = this.el.child('.roo-tpl-' + this.dataName);
20197 for(var i = 0, len = records.length; i < len; i++){
20198 var data = this.prepareData(records[i].data, i, records[i]);
20199 this.fireEvent("preparedata", this, data, i, records[i]);
20201 var d = Roo.apply({}, data);
20204 Roo.apply(d, {'roo-id' : Roo.id()});
20208 Roo.each(this.parent.item, function(item){
20209 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20212 Roo.apply(d, {'roo-data-checked' : 'checked'});
20216 html[html.length] = Roo.util.Format.trim(
20218 t.applySubtemplate(this.dataName, d, this.store.meta) :
20225 el.update(html.join(""));
20226 this.nodes = el.dom.childNodes;
20227 this.updateIndexes(0);
20232 * Function to override to reformat the data that is sent to
20233 * the template for each node.
20234 * DEPRICATED - use the preparedata event handler.
20235 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20236 * a JSON object for an UpdateManager bound view).
20238 prepareData : function(data, index, record)
20240 this.fireEvent("preparedata", this, data, index, record);
20244 onUpdate : function(ds, record){
20245 // Roo.log('on update');
20246 this.clearSelections();
20247 var index = this.store.indexOf(record);
20248 var n = this.nodes[index];
20249 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20250 n.parentNode.removeChild(n);
20251 this.updateIndexes(index, index);
20257 onAdd : function(ds, records, index)
20259 //Roo.log(['on Add', ds, records, index] );
20260 this.clearSelections();
20261 if(this.nodes.length == 0){
20265 var n = this.nodes[index];
20266 for(var i = 0, len = records.length; i < len; i++){
20267 var d = this.prepareData(records[i].data, i, records[i]);
20269 this.tpl.insertBefore(n, d);
20272 this.tpl.append(this.el, d);
20275 this.updateIndexes(index);
20278 onRemove : function(ds, record, index){
20279 // Roo.log('onRemove');
20280 this.clearSelections();
20281 var el = this.dataName ?
20282 this.el.child('.roo-tpl-' + this.dataName) :
20285 el.dom.removeChild(this.nodes[index]);
20286 this.updateIndexes(index);
20290 * Refresh an individual node.
20291 * @param {Number} index
20293 refreshNode : function(index){
20294 this.onUpdate(this.store, this.store.getAt(index));
20297 updateIndexes : function(startIndex, endIndex){
20298 var ns = this.nodes;
20299 startIndex = startIndex || 0;
20300 endIndex = endIndex || ns.length - 1;
20301 for(var i = startIndex; i <= endIndex; i++){
20302 ns[i].nodeIndex = i;
20307 * Changes the data store this view uses and refresh the view.
20308 * @param {Store} store
20310 setStore : function(store, initial){
20311 if(!initial && this.store){
20312 this.store.un("datachanged", this.refresh);
20313 this.store.un("add", this.onAdd);
20314 this.store.un("remove", this.onRemove);
20315 this.store.un("update", this.onUpdate);
20316 this.store.un("clear", this.refresh);
20317 this.store.un("beforeload", this.onBeforeLoad);
20318 this.store.un("load", this.onLoad);
20319 this.store.un("loadexception", this.onLoad);
20323 store.on("datachanged", this.refresh, this);
20324 store.on("add", this.onAdd, this);
20325 store.on("remove", this.onRemove, this);
20326 store.on("update", this.onUpdate, this);
20327 store.on("clear", this.refresh, this);
20328 store.on("beforeload", this.onBeforeLoad, this);
20329 store.on("load", this.onLoad, this);
20330 store.on("loadexception", this.onLoad, this);
20338 * onbeforeLoad - masks the loading area.
20341 onBeforeLoad : function(store,opts)
20343 //Roo.log('onBeforeLoad');
20345 this.el.update("");
20347 this.el.mask(this.mask ? this.mask : "Loading" );
20349 onLoad : function ()
20356 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20357 * @param {HTMLElement} node
20358 * @return {HTMLElement} The template node
20360 findItemFromChild : function(node){
20361 var el = this.dataName ?
20362 this.el.child('.roo-tpl-' + this.dataName,true) :
20365 if(!node || node.parentNode == el){
20368 var p = node.parentNode;
20369 while(p && p != el){
20370 if(p.parentNode == el){
20379 onClick : function(e){
20380 var item = this.findItemFromChild(e.getTarget());
20382 var index = this.indexOf(item);
20383 if(this.onItemClick(item, index, e) !== false){
20384 this.fireEvent("click", this, index, item, e);
20387 this.clearSelections();
20392 onContextMenu : function(e){
20393 var item = this.findItemFromChild(e.getTarget());
20395 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20400 onDblClick : function(e){
20401 var item = this.findItemFromChild(e.getTarget());
20403 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20407 onItemClick : function(item, index, e)
20409 if(this.fireEvent("beforeclick", this, index, item, e) === false){
20412 if (this.toggleSelect) {
20413 var m = this.isSelected(item) ? 'unselect' : 'select';
20416 _t[m](item, true, false);
20419 if(this.multiSelect || this.singleSelect){
20420 if(this.multiSelect && e.shiftKey && this.lastSelection){
20421 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20423 this.select(item, this.multiSelect && e.ctrlKey);
20424 this.lastSelection = item;
20427 if(!this.tickable){
20428 e.preventDefault();
20436 * Get the number of selected nodes.
20439 getSelectionCount : function(){
20440 return this.selections.length;
20444 * Get the currently selected nodes.
20445 * @return {Array} An array of HTMLElements
20447 getSelectedNodes : function(){
20448 return this.selections;
20452 * Get the indexes of the selected nodes.
20455 getSelectedIndexes : function(){
20456 var indexes = [], s = this.selections;
20457 for(var i = 0, len = s.length; i < len; i++){
20458 indexes.push(s[i].nodeIndex);
20464 * Clear all selections
20465 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20467 clearSelections : function(suppressEvent){
20468 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20469 this.cmp.elements = this.selections;
20470 this.cmp.removeClass(this.selectedClass);
20471 this.selections = [];
20472 if(!suppressEvent){
20473 this.fireEvent("selectionchange", this, this.selections);
20479 * Returns true if the passed node is selected
20480 * @param {HTMLElement/Number} node The node or node index
20481 * @return {Boolean}
20483 isSelected : function(node){
20484 var s = this.selections;
20488 node = this.getNode(node);
20489 return s.indexOf(node) !== -1;
20494 * @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
20495 * @param {Boolean} keepExisting (optional) true to keep existing selections
20496 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20498 select : function(nodeInfo, keepExisting, suppressEvent){
20499 if(nodeInfo instanceof Array){
20501 this.clearSelections(true);
20503 for(var i = 0, len = nodeInfo.length; i < len; i++){
20504 this.select(nodeInfo[i], true, true);
20508 var node = this.getNode(nodeInfo);
20509 if(!node || this.isSelected(node)){
20510 return; // already selected.
20513 this.clearSelections(true);
20516 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20517 Roo.fly(node).addClass(this.selectedClass);
20518 this.selections.push(node);
20519 if(!suppressEvent){
20520 this.fireEvent("selectionchange", this, this.selections);
20528 * @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
20529 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20530 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20532 unselect : function(nodeInfo, keepExisting, suppressEvent)
20534 if(nodeInfo instanceof Array){
20535 Roo.each(this.selections, function(s) {
20536 this.unselect(s, nodeInfo);
20540 var node = this.getNode(nodeInfo);
20541 if(!node || !this.isSelected(node)){
20542 //Roo.log("not selected");
20543 return; // not selected.
20547 Roo.each(this.selections, function(s) {
20549 Roo.fly(node).removeClass(this.selectedClass);
20556 this.selections= ns;
20557 this.fireEvent("selectionchange", this, this.selections);
20561 * Gets a template node.
20562 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20563 * @return {HTMLElement} The node or null if it wasn't found
20565 getNode : function(nodeInfo){
20566 if(typeof nodeInfo == "string"){
20567 return document.getElementById(nodeInfo);
20568 }else if(typeof nodeInfo == "number"){
20569 return this.nodes[nodeInfo];
20575 * Gets a range template nodes.
20576 * @param {Number} startIndex
20577 * @param {Number} endIndex
20578 * @return {Array} An array of nodes
20580 getNodes : function(start, end){
20581 var ns = this.nodes;
20582 start = start || 0;
20583 end = typeof end == "undefined" ? ns.length - 1 : end;
20586 for(var i = start; i <= end; i++){
20590 for(var i = start; i >= end; i--){
20598 * Finds the index of the passed node
20599 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20600 * @return {Number} The index of the node or -1
20602 indexOf : function(node){
20603 node = this.getNode(node);
20604 if(typeof node.nodeIndex == "number"){
20605 return node.nodeIndex;
20607 var ns = this.nodes;
20608 for(var i = 0, len = ns.length; i < len; i++){
20619 * based on jquery fullcalendar
20623 Roo.bootstrap = Roo.bootstrap || {};
20625 * @class Roo.bootstrap.Calendar
20626 * @extends Roo.bootstrap.Component
20627 * Bootstrap Calendar class
20628 * @cfg {Boolean} loadMask (true|false) default false
20629 * @cfg {Object} header generate the user specific header of the calendar, default false
20632 * Create a new Container
20633 * @param {Object} config The config object
20638 Roo.bootstrap.Calendar = function(config){
20639 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20643 * Fires when a date is selected
20644 * @param {DatePicker} this
20645 * @param {Date} date The selected date
20649 * @event monthchange
20650 * Fires when the displayed month changes
20651 * @param {DatePicker} this
20652 * @param {Date} date The selected month
20654 'monthchange': true,
20656 * @event evententer
20657 * Fires when mouse over an event
20658 * @param {Calendar} this
20659 * @param {event} Event
20661 'evententer': true,
20663 * @event eventleave
20664 * Fires when the mouse leaves an
20665 * @param {Calendar} this
20668 'eventleave': true,
20670 * @event eventclick
20671 * Fires when the mouse click an
20672 * @param {Calendar} this
20681 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20684 * @cfg {Roo.data.Store} store
20685 * The data source for the calendar
20689 * @cfg {Number} startDay
20690 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20698 getAutoCreate : function(){
20701 var fc_button = function(name, corner, style, content ) {
20702 return Roo.apply({},{
20704 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20706 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20709 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20720 style : 'width:100%',
20727 cls : 'fc-header-left',
20729 fc_button('prev', 'left', 'arrow', '‹' ),
20730 fc_button('next', 'right', 'arrow', '›' ),
20731 { tag: 'span', cls: 'fc-header-space' },
20732 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20740 cls : 'fc-header-center',
20744 cls: 'fc-header-title',
20747 html : 'month / year'
20755 cls : 'fc-header-right',
20757 /* fc_button('month', 'left', '', 'month' ),
20758 fc_button('week', '', '', 'week' ),
20759 fc_button('day', 'right', '', 'day' )
20771 header = this.header;
20774 var cal_heads = function() {
20776 // fixme - handle this.
20778 for (var i =0; i < Date.dayNames.length; i++) {
20779 var d = Date.dayNames[i];
20782 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20783 html : d.substring(0,3)
20787 ret[0].cls += ' fc-first';
20788 ret[6].cls += ' fc-last';
20791 var cal_cell = function(n) {
20794 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20799 cls: 'fc-day-number',
20803 cls: 'fc-day-content',
20807 style: 'position: relative;' // height: 17px;
20819 var cal_rows = function() {
20822 for (var r = 0; r < 6; r++) {
20829 for (var i =0; i < Date.dayNames.length; i++) {
20830 var d = Date.dayNames[i];
20831 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20834 row.cn[0].cls+=' fc-first';
20835 row.cn[0].cn[0].style = 'min-height:90px';
20836 row.cn[6].cls+=' fc-last';
20840 ret[0].cls += ' fc-first';
20841 ret[4].cls += ' fc-prev-last';
20842 ret[5].cls += ' fc-last';
20849 cls: 'fc-border-separate',
20850 style : 'width:100%',
20858 cls : 'fc-first fc-last',
20876 cls : 'fc-content',
20877 style : "position: relative;",
20880 cls : 'fc-view fc-view-month fc-grid',
20881 style : 'position: relative',
20882 unselectable : 'on',
20885 cls : 'fc-event-container',
20886 style : 'position:absolute;z-index:8;top:0;left:0;'
20904 initEvents : function()
20907 throw "can not find store for calendar";
20913 style: "text-align:center",
20917 style: "background-color:white;width:50%;margin:250 auto",
20921 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20932 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20934 var size = this.el.select('.fc-content', true).first().getSize();
20935 this.maskEl.setSize(size.width, size.height);
20936 this.maskEl.enableDisplayMode("block");
20937 if(!this.loadMask){
20938 this.maskEl.hide();
20941 this.store = Roo.factory(this.store, Roo.data);
20942 this.store.on('load', this.onLoad, this);
20943 this.store.on('beforeload', this.onBeforeLoad, this);
20947 this.cells = this.el.select('.fc-day',true);
20948 //Roo.log(this.cells);
20949 this.textNodes = this.el.query('.fc-day-number');
20950 this.cells.addClassOnOver('fc-state-hover');
20952 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20953 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20954 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20955 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20957 this.on('monthchange', this.onMonthChange, this);
20959 this.update(new Date().clearTime());
20962 resize : function() {
20963 var sz = this.el.getSize();
20965 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20966 this.el.select('.fc-day-content div',true).setHeight(34);
20971 showPrevMonth : function(e){
20972 this.update(this.activeDate.add("mo", -1));
20974 showToday : function(e){
20975 this.update(new Date().clearTime());
20978 showNextMonth : function(e){
20979 this.update(this.activeDate.add("mo", 1));
20983 showPrevYear : function(){
20984 this.update(this.activeDate.add("y", -1));
20988 showNextYear : function(){
20989 this.update(this.activeDate.add("y", 1));
20994 update : function(date)
20996 var vd = this.activeDate;
20997 this.activeDate = date;
20998 // if(vd && this.el){
20999 // var t = date.getTime();
21000 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21001 // Roo.log('using add remove');
21003 // this.fireEvent('monthchange', this, date);
21005 // this.cells.removeClass("fc-state-highlight");
21006 // this.cells.each(function(c){
21007 // if(c.dateValue == t){
21008 // c.addClass("fc-state-highlight");
21009 // setTimeout(function(){
21010 // try{c.dom.firstChild.focus();}catch(e){}
21020 var days = date.getDaysInMonth();
21022 var firstOfMonth = date.getFirstDateOfMonth();
21023 var startingPos = firstOfMonth.getDay()-this.startDay;
21025 if(startingPos < this.startDay){
21029 var pm = date.add(Date.MONTH, -1);
21030 var prevStart = pm.getDaysInMonth()-startingPos;
21032 this.cells = this.el.select('.fc-day',true);
21033 this.textNodes = this.el.query('.fc-day-number');
21034 this.cells.addClassOnOver('fc-state-hover');
21036 var cells = this.cells.elements;
21037 var textEls = this.textNodes;
21039 Roo.each(cells, function(cell){
21040 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21043 days += startingPos;
21045 // convert everything to numbers so it's fast
21046 var day = 86400000;
21047 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21050 //Roo.log(prevStart);
21052 var today = new Date().clearTime().getTime();
21053 var sel = date.clearTime().getTime();
21054 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21055 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21056 var ddMatch = this.disabledDatesRE;
21057 var ddText = this.disabledDatesText;
21058 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21059 var ddaysText = this.disabledDaysText;
21060 var format = this.format;
21062 var setCellClass = function(cal, cell){
21066 //Roo.log('set Cell Class');
21068 var t = d.getTime();
21072 cell.dateValue = t;
21074 cell.className += " fc-today";
21075 cell.className += " fc-state-highlight";
21076 cell.title = cal.todayText;
21079 // disable highlight in other month..
21080 //cell.className += " fc-state-highlight";
21085 cell.className = " fc-state-disabled";
21086 cell.title = cal.minText;
21090 cell.className = " fc-state-disabled";
21091 cell.title = cal.maxText;
21095 if(ddays.indexOf(d.getDay()) != -1){
21096 cell.title = ddaysText;
21097 cell.className = " fc-state-disabled";
21100 if(ddMatch && format){
21101 var fvalue = d.dateFormat(format);
21102 if(ddMatch.test(fvalue)){
21103 cell.title = ddText.replace("%0", fvalue);
21104 cell.className = " fc-state-disabled";
21108 if (!cell.initialClassName) {
21109 cell.initialClassName = cell.dom.className;
21112 cell.dom.className = cell.initialClassName + ' ' + cell.className;
21117 for(; i < startingPos; i++) {
21118 textEls[i].innerHTML = (++prevStart);
21119 d.setDate(d.getDate()+1);
21121 cells[i].className = "fc-past fc-other-month";
21122 setCellClass(this, cells[i]);
21127 for(; i < days; i++){
21128 intDay = i - startingPos + 1;
21129 textEls[i].innerHTML = (intDay);
21130 d.setDate(d.getDate()+1);
21132 cells[i].className = ''; // "x-date-active";
21133 setCellClass(this, cells[i]);
21137 for(; i < 42; i++) {
21138 textEls[i].innerHTML = (++extraDays);
21139 d.setDate(d.getDate()+1);
21141 cells[i].className = "fc-future fc-other-month";
21142 setCellClass(this, cells[i]);
21145 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21147 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21149 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21150 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21152 if(totalRows != 6){
21153 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21154 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21157 this.fireEvent('monthchange', this, date);
21161 if(!this.internalRender){
21162 var main = this.el.dom.firstChild;
21163 var w = main.offsetWidth;
21164 this.el.setWidth(w + this.el.getBorderWidth("lr"));
21165 Roo.fly(main).setWidth(w);
21166 this.internalRender = true;
21167 // opera does not respect the auto grow header center column
21168 // then, after it gets a width opera refuses to recalculate
21169 // without a second pass
21170 if(Roo.isOpera && !this.secondPass){
21171 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21172 this.secondPass = true;
21173 this.update.defer(10, this, [date]);
21180 findCell : function(dt) {
21181 dt = dt.clearTime().getTime();
21183 this.cells.each(function(c){
21184 //Roo.log("check " +c.dateValue + '?=' + dt);
21185 if(c.dateValue == dt){
21195 findCells : function(ev) {
21196 var s = ev.start.clone().clearTime().getTime();
21198 var e= ev.end.clone().clearTime().getTime();
21201 this.cells.each(function(c){
21202 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21204 if(c.dateValue > e){
21207 if(c.dateValue < s){
21216 // findBestRow: function(cells)
21220 // for (var i =0 ; i < cells.length;i++) {
21221 // ret = Math.max(cells[i].rows || 0,ret);
21228 addItem : function(ev)
21230 // look for vertical location slot in
21231 var cells = this.findCells(ev);
21233 // ev.row = this.findBestRow(cells);
21235 // work out the location.
21239 for(var i =0; i < cells.length; i++) {
21241 cells[i].row = cells[0].row;
21244 cells[i].row = cells[i].row + 1;
21254 if (crow.start.getY() == cells[i].getY()) {
21256 crow.end = cells[i];
21273 cells[0].events.push(ev);
21275 this.calevents.push(ev);
21278 clearEvents: function() {
21280 if(!this.calevents){
21284 Roo.each(this.cells.elements, function(c){
21290 Roo.each(this.calevents, function(e) {
21291 Roo.each(e.els, function(el) {
21292 el.un('mouseenter' ,this.onEventEnter, this);
21293 el.un('mouseleave' ,this.onEventLeave, this);
21298 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21304 renderEvents: function()
21308 this.cells.each(function(c) {
21317 if(c.row != c.events.length){
21318 r = 4 - (4 - (c.row - c.events.length));
21321 c.events = ev.slice(0, r);
21322 c.more = ev.slice(r);
21324 if(c.more.length && c.more.length == 1){
21325 c.events.push(c.more.pop());
21328 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21332 this.cells.each(function(c) {
21334 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21337 for (var e = 0; e < c.events.length; e++){
21338 var ev = c.events[e];
21339 var rows = ev.rows;
21341 for(var i = 0; i < rows.length; i++) {
21343 // how many rows should it span..
21346 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21347 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21349 unselectable : "on",
21352 cls: 'fc-event-inner',
21356 // cls: 'fc-event-time',
21357 // html : cells.length > 1 ? '' : ev.time
21361 cls: 'fc-event-title',
21362 html : String.format('{0}', ev.title)
21369 cls: 'ui-resizable-handle ui-resizable-e',
21370 html : '  '
21377 cfg.cls += ' fc-event-start';
21379 if ((i+1) == rows.length) {
21380 cfg.cls += ' fc-event-end';
21383 var ctr = _this.el.select('.fc-event-container',true).first();
21384 var cg = ctr.createChild(cfg);
21386 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21387 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21389 var r = (c.more.length) ? 1 : 0;
21390 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
21391 cg.setWidth(ebox.right - sbox.x -2);
21393 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21394 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21395 cg.on('click', _this.onEventClick, _this, ev);
21406 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21407 style : 'position: absolute',
21408 unselectable : "on",
21411 cls: 'fc-event-inner',
21415 cls: 'fc-event-title',
21423 cls: 'ui-resizable-handle ui-resizable-e',
21424 html : '  '
21430 var ctr = _this.el.select('.fc-event-container',true).first();
21431 var cg = ctr.createChild(cfg);
21433 var sbox = c.select('.fc-day-content',true).first().getBox();
21434 var ebox = c.select('.fc-day-content',true).first().getBox();
21436 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
21437 cg.setWidth(ebox.right - sbox.x -2);
21439 cg.on('click', _this.onMoreEventClick, _this, c.more);
21449 onEventEnter: function (e, el,event,d) {
21450 this.fireEvent('evententer', this, el, event);
21453 onEventLeave: function (e, el,event,d) {
21454 this.fireEvent('eventleave', this, el, event);
21457 onEventClick: function (e, el,event,d) {
21458 this.fireEvent('eventclick', this, el, event);
21461 onMonthChange: function () {
21465 onMoreEventClick: function(e, el, more)
21469 this.calpopover.placement = 'right';
21470 this.calpopover.setTitle('More');
21472 this.calpopover.setContent('');
21474 var ctr = this.calpopover.el.select('.popover-content', true).first();
21476 Roo.each(more, function(m){
21478 cls : 'fc-event-hori fc-event-draggable',
21481 var cg = ctr.createChild(cfg);
21483 cg.on('click', _this.onEventClick, _this, m);
21486 this.calpopover.show(el);
21491 onLoad: function ()
21493 this.calevents = [];
21496 if(this.store.getCount() > 0){
21497 this.store.data.each(function(d){
21500 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21501 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21502 time : d.data.start_time,
21503 title : d.data.title,
21504 description : d.data.description,
21505 venue : d.data.venue
21510 this.renderEvents();
21512 if(this.calevents.length && this.loadMask){
21513 this.maskEl.hide();
21517 onBeforeLoad: function()
21519 this.clearEvents();
21521 this.maskEl.show();
21535 * @class Roo.bootstrap.Popover
21536 * @extends Roo.bootstrap.Component
21537 * @parent none builder
21538 * @children Roo.bootstrap.Component
21539 * Bootstrap Popover class
21540 * @cfg {String} html contents of the popover (or false to use children..)
21541 * @cfg {String} title of popover (or false to hide)
21542 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21543 * @cfg {String} trigger click || hover (or false to trigger manually)
21544 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21545 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21546 * - if false and it has a 'parent' then it will be automatically added to that element
21547 * - if string - Roo.get will be called
21548 * @cfg {Number} delay - delay before showing
21551 * Create a new Popover
21552 * @param {Object} config The config object
21555 Roo.bootstrap.Popover = function(config){
21556 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21562 * After the popover show
21564 * @param {Roo.bootstrap.Popover} this
21569 * After the popover hide
21571 * @param {Roo.bootstrap.Popover} this
21577 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21582 placement : 'right',
21583 trigger : 'hover', // hover
21589 can_build_overlaid : false,
21591 maskEl : false, // the mask element
21594 alignEl : false, // when show is called with an element - this get's stored.
21596 getChildContainer : function()
21598 return this.contentEl;
21601 getPopoverHeader : function()
21603 this.title = true; // flag not to hide it..
21604 this.headerEl.addClass('p-0');
21605 return this.headerEl
21609 getAutoCreate : function(){
21612 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21613 style: 'display:block',
21619 cls : 'popover-inner ',
21623 cls: 'popover-title popover-header',
21624 html : this.title === false ? '' : this.title
21627 cls : 'popover-content popover-body ' + (this.cls || ''),
21628 html : this.html || ''
21639 * @param {string} the title
21641 setTitle: function(str)
21645 this.headerEl.dom.innerHTML = str;
21650 * @param {string} the body content
21652 setContent: function(str)
21655 if (this.contentEl) {
21656 this.contentEl.dom.innerHTML = str;
21660 // as it get's added to the bottom of the page.
21661 onRender : function(ct, position)
21663 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21668 var cfg = Roo.apply({}, this.getAutoCreate());
21672 cfg.cls += ' ' + this.cls;
21675 cfg.style = this.style;
21677 //Roo.log("adding to ");
21678 this.el = Roo.get(document.body).createChild(cfg, position);
21679 // Roo.log(this.el);
21682 this.contentEl = this.el.select('.popover-content',true).first();
21683 this.headerEl = this.el.select('.popover-title',true).first();
21686 if(typeof(this.items) != 'undefined'){
21687 var items = this.items;
21690 for(var i =0;i < items.length;i++) {
21691 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21695 this.items = nitems;
21697 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21698 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21705 resizeMask : function()
21707 this.maskEl.setSize(
21708 Roo.lib.Dom.getViewWidth(true),
21709 Roo.lib.Dom.getViewHeight(true)
21713 initEvents : function()
21717 Roo.bootstrap.Popover.register(this);
21720 this.arrowEl = this.el.select('.arrow',true).first();
21721 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21722 this.el.enableDisplayMode('block');
21726 if (this.over === false && !this.parent()) {
21729 if (this.triggers === false) {
21734 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21735 var triggers = this.trigger ? this.trigger.split(' ') : [];
21736 Roo.each(triggers, function(trigger) {
21738 if (trigger == 'click') {
21739 on_el.on('click', this.toggle, this);
21740 } else if (trigger != 'manual') {
21741 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21742 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21744 on_el.on(eventIn ,this.enter, this);
21745 on_el.on(eventOut, this.leave, this);
21755 toggle : function () {
21756 this.hoverState == 'in' ? this.leave() : this.enter();
21759 enter : function () {
21761 clearTimeout(this.timeout);
21763 this.hoverState = 'in';
21765 if (!this.delay || !this.delay.show) {
21770 this.timeout = setTimeout(function () {
21771 if (_t.hoverState == 'in') {
21774 }, this.delay.show)
21777 leave : function() {
21778 clearTimeout(this.timeout);
21780 this.hoverState = 'out';
21782 if (!this.delay || !this.delay.hide) {
21787 this.timeout = setTimeout(function () {
21788 if (_t.hoverState == 'out') {
21791 }, this.delay.hide)
21795 * update the position of the dialog
21796 * normally this is needed if the popover get's bigger - due to a Table reload etc..
21801 doAlign : function()
21804 if (this.alignEl) {
21805 this.updatePosition(this.placement, true);
21808 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21809 var es = this.el.getSize();
21810 var x = Roo.lib.Dom.getViewWidth()/2;
21811 var y = Roo.lib.Dom.getViewHeight()/2;
21812 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21824 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21825 * @param {string} (left|right|top|bottom) position
21827 show : function (on_el, placement)
21829 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21830 on_el = on_el || false; // default to false
21833 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21834 on_el = this.parent().el;
21835 } else if (this.over) {
21836 on_el = Roo.get(this.over);
21841 this.alignEl = Roo.get( on_el );
21844 this.render(document.body);
21850 if (this.title === false) {
21851 this.headerEl.hide();
21856 this.el.dom.style.display = 'block';
21860 //var arrow = this.el.select('.arrow',true).first();
21861 //arrow.set(align[2],
21863 this.el.addClass('in');
21867 this.hoverState = 'in';
21870 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21871 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21872 this.maskEl.dom.style.display = 'block';
21873 this.maskEl.addClass('show');
21875 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21877 this.fireEvent('show', this);
21881 * fire this manually after loading a grid in the table for example
21882 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21883 * @param {Boolean} try and move it if we cant get right position.
21885 updatePosition : function(placement, try_move)
21887 // allow for calling with no parameters
21888 placement = placement ? placement : this.placement;
21889 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21891 this.el.removeClass([
21892 'fade','top','bottom', 'left', 'right','in',
21893 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21895 this.el.addClass(placement + ' bs-popover-' + placement);
21897 if (!this.alignEl ) {
21901 switch (placement) {
21903 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21904 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21905 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21906 //normal display... or moved up/down.
21907 this.el.setXY(offset);
21908 var xy = this.alignEl.getAnchorXY('tr', false);
21910 this.arrowEl.setXY(xy);
21913 // continue through...
21914 return this.updatePosition('left', false);
21918 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21919 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21920 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21921 //normal display... or moved up/down.
21922 this.el.setXY(offset);
21923 var xy = this.alignEl.getAnchorXY('tl', false);
21924 xy[0]-=10;xy[1]+=5; // << fix me
21925 this.arrowEl.setXY(xy);
21929 return this.updatePosition('right', false);
21932 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21933 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21934 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21935 //normal display... or moved up/down.
21936 this.el.setXY(offset);
21937 var xy = this.alignEl.getAnchorXY('t', false);
21938 xy[1]-=10; // << fix me
21939 this.arrowEl.setXY(xy);
21943 return this.updatePosition('bottom', false);
21946 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21947 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21948 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21949 //normal display... or moved up/down.
21950 this.el.setXY(offset);
21951 var xy = this.alignEl.getAnchorXY('b', false);
21952 xy[1]+=2; // << fix me
21953 this.arrowEl.setXY(xy);
21957 return this.updatePosition('top', false);
21968 this.el.setXY([0,0]);
21969 this.el.removeClass('in');
21971 this.hoverState = null;
21972 this.maskEl.hide(); // always..
21973 this.fireEvent('hide', this);
21979 Roo.apply(Roo.bootstrap.Popover, {
21982 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21983 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21984 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21985 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21990 clickHander : false,
21994 onMouseDown : function(e)
21996 if (this.popups.length && !e.getTarget(".roo-popover")) {
21997 /// what is nothing is showing..
22006 register : function(popup)
22008 if (!Roo.bootstrap.Popover.clickHandler) {
22009 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22011 // hide other popups.
22012 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
22013 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
22014 this.hideAll(); //<< why?
22015 //this.popups.push(popup);
22017 hideAll : function()
22019 this.popups.forEach(function(p) {
22023 onShow : function() {
22024 Roo.bootstrap.Popover.popups.push(this);
22026 onHide : function() {
22027 Roo.bootstrap.Popover.popups.remove(this);
22032 * @class Roo.bootstrap.PopoverNav
22033 * @extends Roo.bootstrap.nav.Simplebar
22034 * @parent Roo.bootstrap.Popover
22035 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22037 * Bootstrap Popover header navigation class
22038 * FIXME? should this go under nav?
22042 * Create a new Popover Header Navigation
22043 * @param {Object} config The config object
22046 Roo.bootstrap.PopoverNav = function(config){
22047 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22050 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar, {
22053 container_method : 'getPopoverHeader'
22071 * @class Roo.bootstrap.Progress
22072 * @extends Roo.bootstrap.Component
22073 * @children Roo.bootstrap.ProgressBar
22074 * Bootstrap Progress class
22075 * @cfg {Boolean} striped striped of the progress bar
22076 * @cfg {Boolean} active animated of the progress bar
22080 * Create a new Progress
22081 * @param {Object} config The config object
22084 Roo.bootstrap.Progress = function(config){
22085 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22088 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
22093 getAutoCreate : function(){
22101 cfg.cls += ' progress-striped';
22105 cfg.cls += ' active';
22124 * @class Roo.bootstrap.ProgressBar
22125 * @extends Roo.bootstrap.Component
22126 * Bootstrap ProgressBar class
22127 * @cfg {Number} aria_valuenow aria-value now
22128 * @cfg {Number} aria_valuemin aria-value min
22129 * @cfg {Number} aria_valuemax aria-value max
22130 * @cfg {String} label label for the progress bar
22131 * @cfg {String} panel (success | info | warning | danger )
22132 * @cfg {String} role role of the progress bar
22133 * @cfg {String} sr_only text
22137 * Create a new ProgressBar
22138 * @param {Object} config The config object
22141 Roo.bootstrap.ProgressBar = function(config){
22142 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22145 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
22149 aria_valuemax : 100,
22155 getAutoCreate : function()
22160 cls: 'progress-bar',
22161 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22173 cfg.role = this.role;
22176 if(this.aria_valuenow){
22177 cfg['aria-valuenow'] = this.aria_valuenow;
22180 if(this.aria_valuemin){
22181 cfg['aria-valuemin'] = this.aria_valuemin;
22184 if(this.aria_valuemax){
22185 cfg['aria-valuemax'] = this.aria_valuemax;
22188 if(this.label && !this.sr_only){
22189 cfg.html = this.label;
22193 cfg.cls += ' progress-bar-' + this.panel;
22199 update : function(aria_valuenow)
22201 this.aria_valuenow = aria_valuenow;
22203 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22211 * @class Roo.bootstrap.TabGroup
22212 * @extends Roo.bootstrap.Column
22213 * @children Roo.bootstrap.TabPanel
22214 * Bootstrap Column class
22215 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22216 * @cfg {Boolean} carousel true to make the group behave like a carousel
22217 * @cfg {Boolean} bullets show bullets for the panels
22218 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22219 * @cfg {Number} timer auto slide timer .. default 0 millisecond
22220 * @cfg {Boolean} showarrow (true|false) show arrow default true
22223 * Create a new TabGroup
22224 * @param {Object} config The config object
22227 Roo.bootstrap.TabGroup = function(config){
22228 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22230 this.navId = Roo.id();
22233 Roo.bootstrap.TabGroup.register(this);
22237 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
22240 transition : false,
22245 slideOnTouch : false,
22248 getAutoCreate : function()
22250 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22252 cfg.cls += ' tab-content';
22254 if (this.carousel) {
22255 cfg.cls += ' carousel slide';
22258 cls : 'carousel-inner',
22262 if(this.bullets && !Roo.isTouch){
22265 cls : 'carousel-bullets',
22269 if(this.bullets_cls){
22270 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22277 cfg.cn[0].cn.push(bullets);
22280 if(this.showarrow){
22281 cfg.cn[0].cn.push({
22283 class : 'carousel-arrow',
22287 class : 'carousel-prev',
22291 class : 'fa fa-chevron-left'
22297 class : 'carousel-next',
22301 class : 'fa fa-chevron-right'
22314 initEvents: function()
22316 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22317 // this.el.on("touchstart", this.onTouchStart, this);
22320 if(this.autoslide){
22323 this.slideFn = window.setInterval(function() {
22324 _this.showPanelNext();
22328 if(this.showarrow){
22329 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22330 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22336 // onTouchStart : function(e, el, o)
22338 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22342 // this.showPanelNext();
22346 getChildContainer : function()
22348 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22352 * register a Navigation item
22353 * @param {Roo.bootstrap.nav.Item} the navitem to add
22355 register : function(item)
22357 this.tabs.push( item);
22358 item.navId = this.navId; // not really needed..
22363 getActivePanel : function()
22366 Roo.each(this.tabs, function(t) {
22376 getPanelByName : function(n)
22379 Roo.each(this.tabs, function(t) {
22380 if (t.tabId == n) {
22388 indexOfPanel : function(p)
22391 Roo.each(this.tabs, function(t,i) {
22392 if (t.tabId == p.tabId) {
22401 * show a specific panel
22402 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22403 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22405 showPanel : function (pan)
22407 if(this.transition || typeof(pan) == 'undefined'){
22408 Roo.log("waiting for the transitionend");
22412 if (typeof(pan) == 'number') {
22413 pan = this.tabs[pan];
22416 if (typeof(pan) == 'string') {
22417 pan = this.getPanelByName(pan);
22420 var cur = this.getActivePanel();
22423 Roo.log('pan or acitve pan is undefined');
22427 if (pan.tabId == this.getActivePanel().tabId) {
22431 if (false === cur.fireEvent('beforedeactivate')) {
22435 if(this.bullets > 0 && !Roo.isTouch){
22436 this.setActiveBullet(this.indexOfPanel(pan));
22439 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22441 //class="carousel-item carousel-item-next carousel-item-left"
22443 this.transition = true;
22444 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
22445 var lr = dir == 'next' ? 'left' : 'right';
22446 pan.el.addClass(dir); // or prev
22447 pan.el.addClass('carousel-item-' + dir); // or prev
22448 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22449 cur.el.addClass(lr); // or right
22450 pan.el.addClass(lr);
22451 cur.el.addClass('carousel-item-' +lr); // or right
22452 pan.el.addClass('carousel-item-' +lr);
22456 cur.el.on('transitionend', function() {
22457 Roo.log("trans end?");
22459 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22460 pan.setActive(true);
22462 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22463 cur.setActive(false);
22465 _this.transition = false;
22467 }, this, { single: true } );
22472 cur.setActive(false);
22473 pan.setActive(true);
22478 showPanelNext : function()
22480 var i = this.indexOfPanel(this.getActivePanel());
22482 if (i >= this.tabs.length - 1 && !this.autoslide) {
22486 if (i >= this.tabs.length - 1 && this.autoslide) {
22490 this.showPanel(this.tabs[i+1]);
22493 showPanelPrev : function()
22495 var i = this.indexOfPanel(this.getActivePanel());
22497 if (i < 1 && !this.autoslide) {
22501 if (i < 1 && this.autoslide) {
22502 i = this.tabs.length;
22505 this.showPanel(this.tabs[i-1]);
22509 addBullet: function()
22511 if(!this.bullets || Roo.isTouch){
22514 var ctr = this.el.select('.carousel-bullets',true).first();
22515 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22516 var bullet = ctr.createChild({
22517 cls : 'bullet bullet-' + i
22518 },ctr.dom.lastChild);
22523 bullet.on('click', (function(e, el, o, ii, t){
22525 e.preventDefault();
22527 this.showPanel(ii);
22529 if(this.autoslide && this.slideFn){
22530 clearInterval(this.slideFn);
22531 this.slideFn = window.setInterval(function() {
22532 _this.showPanelNext();
22536 }).createDelegate(this, [i, bullet], true));
22541 setActiveBullet : function(i)
22547 Roo.each(this.el.select('.bullet', true).elements, function(el){
22548 el.removeClass('selected');
22551 var bullet = this.el.select('.bullet-' + i, true).first();
22557 bullet.addClass('selected');
22568 Roo.apply(Roo.bootstrap.TabGroup, {
22572 * register a Navigation Group
22573 * @param {Roo.bootstrap.nav.Group} the navgroup to add
22575 register : function(navgrp)
22577 this.groups[navgrp.navId] = navgrp;
22581 * fetch a Navigation Group based on the navigation ID
22582 * if one does not exist , it will get created.
22583 * @param {string} the navgroup to add
22584 * @returns {Roo.bootstrap.nav.Group} the navgroup
22586 get: function(navId) {
22587 if (typeof(this.groups[navId]) == 'undefined') {
22588 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22590 return this.groups[navId] ;
22605 * @class Roo.bootstrap.TabPanel
22606 * @extends Roo.bootstrap.Component
22607 * @children Roo.bootstrap.Component
22608 * Bootstrap TabPanel class
22609 * @cfg {Boolean} active panel active
22610 * @cfg {String} html panel content
22611 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22612 * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22613 * @cfg {String} href click to link..
22614 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22618 * Create a new TabPanel
22619 * @param {Object} config The config object
22622 Roo.bootstrap.TabPanel = function(config){
22623 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22627 * Fires when the active status changes
22628 * @param {Roo.bootstrap.TabPanel} this
22629 * @param {Boolean} state the new state
22634 * @event beforedeactivate
22635 * Fires before a tab is de-activated - can be used to do validation on a form.
22636 * @param {Roo.bootstrap.TabPanel} this
22637 * @return {Boolean} false if there is an error
22640 'beforedeactivate': true
22643 this.tabId = this.tabId || Roo.id();
22647 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22654 touchSlide : false,
22655 getAutoCreate : function(){
22660 // item is needed for carousel - not sure if it has any effect otherwise
22661 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22662 html: this.html || ''
22666 cfg.cls += ' active';
22670 cfg.tabId = this.tabId;
22678 initEvents: function()
22680 var p = this.parent();
22682 this.navId = this.navId || p.navId;
22684 if (typeof(this.navId) != 'undefined') {
22685 // not really needed.. but just in case.. parent should be a NavGroup.
22686 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22690 var i = tg.tabs.length - 1;
22692 if(this.active && tg.bullets > 0 && i < tg.bullets){
22693 tg.setActiveBullet(i);
22697 this.el.on('click', this.onClick, this);
22699 if(Roo.isTouch && this.touchSlide){
22700 this.el.on("touchstart", this.onTouchStart, this);
22701 this.el.on("touchmove", this.onTouchMove, this);
22702 this.el.on("touchend", this.onTouchEnd, this);
22707 onRender : function(ct, position)
22709 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22712 setActive : function(state)
22714 Roo.log("panel - set active " + this.tabId + "=" + state);
22716 this.active = state;
22718 this.el.removeClass('active');
22720 } else if (!this.el.hasClass('active')) {
22721 this.el.addClass('active');
22724 this.fireEvent('changed', this, state);
22727 onClick : function(e)
22729 e.preventDefault();
22731 if(!this.href.length){
22735 window.location.href = this.href;
22744 onTouchStart : function(e)
22746 this.swiping = false;
22748 this.startX = e.browserEvent.touches[0].clientX;
22749 this.startY = e.browserEvent.touches[0].clientY;
22752 onTouchMove : function(e)
22754 this.swiping = true;
22756 this.endX = e.browserEvent.touches[0].clientX;
22757 this.endY = e.browserEvent.touches[0].clientY;
22760 onTouchEnd : function(e)
22767 var tabGroup = this.parent();
22769 if(this.endX > this.startX){ // swiping right
22770 tabGroup.showPanelPrev();
22774 if(this.startX > this.endX){ // swiping left
22775 tabGroup.showPanelNext();
22794 * @class Roo.bootstrap.form.DateField
22795 * @extends Roo.bootstrap.form.Input
22796 * Bootstrap DateField class
22797 * @cfg {Number} weekStart default 0
22798 * @cfg {String} viewMode default empty, (months|years)
22799 * @cfg {String} minViewMode default empty, (months|years)
22800 * @cfg {Number} startDate default -Infinity
22801 * @cfg {Number} endDate default Infinity
22802 * @cfg {Boolean} todayHighlight default false
22803 * @cfg {Boolean} todayBtn default false
22804 * @cfg {Boolean} calendarWeeks default false
22805 * @cfg {Object} daysOfWeekDisabled default empty
22806 * @cfg {Boolean} singleMode default false (true | false)
22808 * @cfg {Boolean} keyboardNavigation default true
22809 * @cfg {String} language default en
22812 * Create a new DateField
22813 * @param {Object} config The config object
22816 Roo.bootstrap.form.DateField = function(config){
22817 Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22821 * Fires when this field show.
22822 * @param {Roo.bootstrap.form.DateField} this
22823 * @param {Mixed} date The date value
22828 * Fires when this field hide.
22829 * @param {Roo.bootstrap.form.DateField} this
22830 * @param {Mixed} date The date value
22835 * Fires when select a date.
22836 * @param {Roo.bootstrap.form.DateField} this
22837 * @param {Mixed} date The date value
22841 * @event beforeselect
22842 * Fires when before select a date.
22843 * @param {Roo.bootstrap.form.DateField} this
22844 * @param {Mixed} date The date value
22846 beforeselect : true
22850 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input, {
22853 * @cfg {String} format
22854 * The default date format string which can be overriden for localization support. The format must be
22855 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22859 * @cfg {String} altFormats
22860 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22861 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22863 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22871 todayHighlight : false,
22877 keyboardNavigation: true,
22879 calendarWeeks: false,
22881 startDate: -Infinity,
22885 daysOfWeekDisabled: [],
22889 singleMode : false,
22891 UTCDate: function()
22893 return new Date(Date.UTC.apply(Date, arguments));
22896 UTCToday: function()
22898 var today = new Date();
22899 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22902 getDate: function() {
22903 var d = this.getUTCDate();
22904 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22907 getUTCDate: function() {
22911 setDate: function(d) {
22912 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22915 setUTCDate: function(d) {
22917 this.setValue(this.formatDate(this.date));
22920 onRender: function(ct, position)
22923 Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22925 this.language = this.language || 'en';
22926 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22927 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22929 this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22930 this.format = this.format || 'm/d/y';
22931 this.isInline = false;
22932 this.isInput = true;
22933 this.component = this.el.select('.add-on', true).first() || false;
22934 this.component = (this.component && this.component.length === 0) ? false : this.component;
22935 this.hasInput = this.component && this.inputEl().length;
22937 if (typeof(this.minViewMode === 'string')) {
22938 switch (this.minViewMode) {
22940 this.minViewMode = 1;
22943 this.minViewMode = 2;
22946 this.minViewMode = 0;
22951 if (typeof(this.viewMode === 'string')) {
22952 switch (this.viewMode) {
22965 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22967 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22969 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22971 this.picker().on('mousedown', this.onMousedown, this);
22972 this.picker().on('click', this.onClick, this);
22974 this.picker().addClass('datepicker-dropdown');
22976 this.startViewMode = this.viewMode;
22978 if(this.singleMode){
22979 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22980 v.setVisibilityMode(Roo.Element.DISPLAY);
22984 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22985 v.setStyle('width', '189px');
22989 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22990 if(!this.calendarWeeks){
22995 v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22996 v.attr('colspan', function(i, val){
22997 return parseInt(val) + 1;
23002 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23004 this.setStartDate(this.startDate);
23005 this.setEndDate(this.endDate);
23007 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23014 if(this.isInline) {
23019 picker : function()
23021 return this.pickerEl;
23022 // return this.el.select('.datepicker', true).first();
23025 fillDow: function()
23027 var dowCnt = this.weekStart;
23036 if(this.calendarWeeks){
23044 while (dowCnt < this.weekStart + 7) {
23048 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23052 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23055 fillMonths: function()
23058 var months = this.picker().select('>.datepicker-months td', true).first();
23060 months.dom.innerHTML = '';
23066 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23069 months.createChild(month);
23076 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;
23078 if (this.date < this.startDate) {
23079 this.viewDate = new Date(this.startDate);
23080 } else if (this.date > this.endDate) {
23081 this.viewDate = new Date(this.endDate);
23083 this.viewDate = new Date(this.date);
23091 var d = new Date(this.viewDate),
23092 year = d.getUTCFullYear(),
23093 month = d.getUTCMonth(),
23094 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23095 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23096 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23097 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23098 currentDate = this.date && this.date.valueOf(),
23099 today = this.UTCToday();
23101 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23103 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23105 // this.picker.select('>tfoot th.today').
23106 // .text(dates[this.language].today)
23107 // .toggle(this.todayBtn !== false);
23109 this.updateNavArrows();
23112 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23114 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23116 prevMonth.setUTCDate(day);
23118 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23120 var nextMonth = new Date(prevMonth);
23122 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23124 nextMonth = nextMonth.valueOf();
23126 var fillMonths = false;
23128 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23130 while(prevMonth.valueOf() <= nextMonth) {
23133 if (prevMonth.getUTCDay() === this.weekStart) {
23135 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23143 if(this.calendarWeeks){
23144 // ISO 8601: First week contains first thursday.
23145 // ISO also states week starts on Monday, but we can be more abstract here.
23147 // Start of current week: based on weekstart/current date
23148 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23149 // Thursday of this week
23150 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23151 // First Thursday of year, year from thursday
23152 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23153 // Calendar week: ms between thursdays, div ms per day, div 7 days
23154 calWeek = (th - yth) / 864e5 / 7 + 1;
23156 fillMonths.cn.push({
23164 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23166 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23169 if (this.todayHighlight &&
23170 prevMonth.getUTCFullYear() == today.getFullYear() &&
23171 prevMonth.getUTCMonth() == today.getMonth() &&
23172 prevMonth.getUTCDate() == today.getDate()) {
23173 clsName += ' today';
23176 if (currentDate && prevMonth.valueOf() === currentDate) {
23177 clsName += ' active';
23180 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23181 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23182 clsName += ' disabled';
23185 fillMonths.cn.push({
23187 cls: 'day ' + clsName,
23188 html: prevMonth.getDate()
23191 prevMonth.setDate(prevMonth.getDate()+1);
23194 var currentYear = this.date && this.date.getUTCFullYear();
23195 var currentMonth = this.date && this.date.getUTCMonth();
23197 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23199 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23200 v.removeClass('active');
23202 if(currentYear === year && k === currentMonth){
23203 v.addClass('active');
23206 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23207 v.addClass('disabled');
23213 year = parseInt(year/10, 10) * 10;
23215 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23217 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23220 for (var i = -1; i < 11; i++) {
23221 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23223 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23231 showMode: function(dir)
23234 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23237 Roo.each(this.picker().select('>div',true).elements, function(v){
23238 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23241 this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23246 if(this.isInline) {
23250 this.picker().removeClass(['bottom', 'top']);
23252 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23254 * place to the top of element!
23258 this.picker().addClass('top');
23259 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23264 this.picker().addClass('bottom');
23266 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23269 parseDate : function(value)
23271 if(!value || value instanceof Date){
23274 var v = Date.parseDate(value, this.format);
23275 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23276 v = Date.parseDate(value, 'Y-m-d');
23278 if(!v && this.altFormats){
23279 if(!this.altFormatsArray){
23280 this.altFormatsArray = this.altFormats.split("|");
23282 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23283 v = Date.parseDate(value, this.altFormatsArray[i]);
23289 formatDate : function(date, fmt)
23291 return (!date || !(date instanceof Date)) ?
23292 date : date.dateFormat(fmt || this.format);
23295 onFocus : function()
23297 Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23301 onBlur : function()
23303 Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23305 var d = this.inputEl().getValue();
23312 showPopup : function()
23314 this.picker().show();
23318 this.fireEvent('showpopup', this, this.date);
23321 hidePopup : function()
23323 if(this.isInline) {
23326 this.picker().hide();
23327 this.viewMode = this.startViewMode;
23330 this.fireEvent('hidepopup', this, this.date);
23334 onMousedown: function(e)
23336 e.stopPropagation();
23337 e.preventDefault();
23342 Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23346 setValue: function(v)
23348 if(this.fireEvent('beforeselect', this, v) !== false){
23349 var d = new Date(this.parseDate(v) ).clearTime();
23351 if(isNaN(d.getTime())){
23352 this.date = this.viewDate = '';
23353 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23357 v = this.formatDate(d);
23359 Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23361 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23365 this.fireEvent('select', this, this.date);
23369 getValue: function()
23371 return this.formatDate(this.date);
23374 fireKey: function(e)
23376 if (!this.picker().isVisible()){
23377 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23383 var dateChanged = false,
23385 newDate, newViewDate;
23390 e.preventDefault();
23394 if (!this.keyboardNavigation) {
23397 dir = e.keyCode == 37 ? -1 : 1;
23400 newDate = this.moveYear(this.date, dir);
23401 newViewDate = this.moveYear(this.viewDate, dir);
23402 } else if (e.shiftKey){
23403 newDate = this.moveMonth(this.date, dir);
23404 newViewDate = this.moveMonth(this.viewDate, dir);
23406 newDate = new Date(this.date);
23407 newDate.setUTCDate(this.date.getUTCDate() + dir);
23408 newViewDate = new Date(this.viewDate);
23409 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23411 if (this.dateWithinRange(newDate)){
23412 this.date = newDate;
23413 this.viewDate = newViewDate;
23414 this.setValue(this.formatDate(this.date));
23416 e.preventDefault();
23417 dateChanged = true;
23422 if (!this.keyboardNavigation) {
23425 dir = e.keyCode == 38 ? -1 : 1;
23427 newDate = this.moveYear(this.date, dir);
23428 newViewDate = this.moveYear(this.viewDate, dir);
23429 } else if (e.shiftKey){
23430 newDate = this.moveMonth(this.date, dir);
23431 newViewDate = this.moveMonth(this.viewDate, dir);
23433 newDate = new Date(this.date);
23434 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23435 newViewDate = new Date(this.viewDate);
23436 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23438 if (this.dateWithinRange(newDate)){
23439 this.date = newDate;
23440 this.viewDate = newViewDate;
23441 this.setValue(this.formatDate(this.date));
23443 e.preventDefault();
23444 dateChanged = true;
23448 this.setValue(this.formatDate(this.date));
23450 e.preventDefault();
23453 this.setValue(this.formatDate(this.date));
23467 onClick: function(e)
23469 e.stopPropagation();
23470 e.preventDefault();
23472 var target = e.getTarget();
23474 if(target.nodeName.toLowerCase() === 'i'){
23475 target = Roo.get(target).dom.parentNode;
23478 var nodeName = target.nodeName;
23479 var className = target.className;
23480 var html = target.innerHTML;
23481 //Roo.log(nodeName);
23483 switch(nodeName.toLowerCase()) {
23485 switch(className) {
23491 var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23492 switch(this.viewMode){
23494 this.viewDate = this.moveMonth(this.viewDate, dir);
23498 this.viewDate = this.moveYear(this.viewDate, dir);
23504 var date = new Date();
23505 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23507 this.setValue(this.formatDate(this.date));
23514 if (className.indexOf('disabled') < 0) {
23515 if (!this.viewDate) {
23516 this.viewDate = new Date();
23518 this.viewDate.setUTCDate(1);
23519 if (className.indexOf('month') > -1) {
23520 this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23522 var year = parseInt(html, 10) || 0;
23523 this.viewDate.setUTCFullYear(year);
23527 if(this.singleMode){
23528 this.setValue(this.formatDate(this.viewDate));
23539 //Roo.log(className);
23540 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23541 var day = parseInt(html, 10) || 1;
23542 var year = (this.viewDate || new Date()).getUTCFullYear(),
23543 month = (this.viewDate || new Date()).getUTCMonth();
23545 if (className.indexOf('old') > -1) {
23552 } else if (className.indexOf('new') > -1) {
23560 //Roo.log([year,month,day]);
23561 this.date = this.UTCDate(year, month, day,0,0,0,0);
23562 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23564 //Roo.log(this.formatDate(this.date));
23565 this.setValue(this.formatDate(this.date));
23572 setStartDate: function(startDate)
23574 this.startDate = startDate || -Infinity;
23575 if (this.startDate !== -Infinity) {
23576 this.startDate = this.parseDate(this.startDate);
23579 this.updateNavArrows();
23582 setEndDate: function(endDate)
23584 this.endDate = endDate || Infinity;
23585 if (this.endDate !== Infinity) {
23586 this.endDate = this.parseDate(this.endDate);
23589 this.updateNavArrows();
23592 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23594 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23595 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23596 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23598 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23599 return parseInt(d, 10);
23602 this.updateNavArrows();
23605 updateNavArrows: function()
23607 if(this.singleMode){
23611 var d = new Date(this.viewDate),
23612 year = d.getUTCFullYear(),
23613 month = d.getUTCMonth();
23615 Roo.each(this.picker().select('.prev', true).elements, function(v){
23617 switch (this.viewMode) {
23620 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23626 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23633 Roo.each(this.picker().select('.next', true).elements, function(v){
23635 switch (this.viewMode) {
23638 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23644 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23652 moveMonth: function(date, dir)
23657 var new_date = new Date(date.valueOf()),
23658 day = new_date.getUTCDate(),
23659 month = new_date.getUTCMonth(),
23660 mag = Math.abs(dir),
23662 dir = dir > 0 ? 1 : -1;
23665 // If going back one month, make sure month is not current month
23666 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23668 return new_date.getUTCMonth() == month;
23670 // If going forward one month, make sure month is as expected
23671 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23673 return new_date.getUTCMonth() != new_month;
23675 new_month = month + dir;
23676 new_date.setUTCMonth(new_month);
23677 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23678 if (new_month < 0 || new_month > 11) {
23679 new_month = (new_month + 12) % 12;
23682 // For magnitudes >1, move one month at a time...
23683 for (var i=0; i<mag; i++) {
23684 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23685 new_date = this.moveMonth(new_date, dir);
23687 // ...then reset the day, keeping it in the new month
23688 new_month = new_date.getUTCMonth();
23689 new_date.setUTCDate(day);
23691 return new_month != new_date.getUTCMonth();
23694 // Common date-resetting loop -- if date is beyond end of month, make it
23697 new_date.setUTCDate(--day);
23698 new_date.setUTCMonth(new_month);
23703 moveYear: function(date, dir)
23705 return this.moveMonth(date, dir*12);
23708 dateWithinRange: function(date)
23710 return date >= this.startDate && date <= this.endDate;
23716 this.picker().remove();
23719 validateValue : function(value)
23721 if(this.getVisibilityEl().hasClass('hidden')){
23725 if(value.length < 1) {
23726 if(this.allowBlank){
23732 if(value.length < this.minLength){
23735 if(value.length > this.maxLength){
23739 var vt = Roo.form.VTypes;
23740 if(!vt[this.vtype](value, this)){
23744 if(typeof this.validator == "function"){
23745 var msg = this.validator(value);
23751 if(this.regex && !this.regex.test(value)){
23755 if(typeof(this.parseDate(value)) == 'undefined'){
23759 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23763 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23773 this.date = this.viewDate = '';
23775 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23780 Roo.apply(Roo.bootstrap.form.DateField, {
23791 html: '<i class="fa fa-arrow-left"/>'
23801 html: '<i class="fa fa-arrow-right"/>'
23843 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23844 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23845 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23846 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23847 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23860 navFnc: 'FullYear',
23865 navFnc: 'FullYear',
23870 Roo.apply(Roo.bootstrap.form.DateField, {
23874 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23878 cls: 'datepicker-days',
23882 cls: 'table-condensed',
23884 Roo.bootstrap.form.DateField.head,
23888 Roo.bootstrap.form.DateField.footer
23895 cls: 'datepicker-months',
23899 cls: 'table-condensed',
23901 Roo.bootstrap.form.DateField.head,
23902 Roo.bootstrap.form.DateField.content,
23903 Roo.bootstrap.form.DateField.footer
23910 cls: 'datepicker-years',
23914 cls: 'table-condensed',
23916 Roo.bootstrap.form.DateField.head,
23917 Roo.bootstrap.form.DateField.content,
23918 Roo.bootstrap.form.DateField.footer
23937 * @class Roo.bootstrap.form.TimeField
23938 * @extends Roo.bootstrap.form.Input
23939 * Bootstrap DateField class
23943 * Create a new TimeField
23944 * @param {Object} config The config object
23947 Roo.bootstrap.form.TimeField = function(config){
23948 Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23952 * Fires when this field show.
23953 * @param {Roo.bootstrap.form.DateField} thisthis
23954 * @param {Mixed} date The date value
23959 * Fires when this field hide.
23960 * @param {Roo.bootstrap.form.DateField} this
23961 * @param {Mixed} date The date value
23966 * Fires when select a date.
23967 * @param {Roo.bootstrap.form.DateField} this
23968 * @param {Mixed} date The date value
23974 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input, {
23977 * @cfg {String} format
23978 * The default time format string which can be overriden for localization support. The format must be
23979 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23983 getAutoCreate : function()
23985 this.after = '<i class="fa far fa-clock"></i>';
23986 return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23990 onRender: function(ct, position)
23993 Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23995 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
23997 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23999 this.pop = this.picker().select('>.datepicker-time',true).first();
24000 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24002 this.picker().on('mousedown', this.onMousedown, this);
24003 this.picker().on('click', this.onClick, this);
24005 this.picker().addClass('datepicker-dropdown');
24010 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24011 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24012 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24013 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24014 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24015 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24019 fireKey: function(e){
24020 if (!this.picker().isVisible()){
24021 if (e.keyCode == 27) { // allow escape to hide and re-show picker
24027 e.preventDefault();
24035 this.onTogglePeriod();
24038 this.onIncrementMinutes();
24041 this.onDecrementMinutes();
24050 onClick: function(e) {
24051 e.stopPropagation();
24052 e.preventDefault();
24055 picker : function()
24057 return this.pickerEl;
24060 fillTime: function()
24062 var time = this.pop.select('tbody', true).first();
24064 time.dom.innerHTML = '';
24079 cls: 'hours-up fa fas fa-chevron-up'
24099 cls: 'minutes-up fa fas fa-chevron-up'
24120 cls: 'timepicker-hour',
24135 cls: 'timepicker-minute',
24150 cls: 'btn btn-primary period',
24172 cls: 'hours-down fa fas fa-chevron-down'
24192 cls: 'minutes-down fa fas fa-chevron-down'
24210 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24217 var hours = this.time.getHours();
24218 var minutes = this.time.getMinutes();
24231 hours = hours - 12;
24235 hours = '0' + hours;
24239 minutes = '0' + minutes;
24242 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24243 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24244 this.pop.select('button', true).first().dom.innerHTML = period;
24250 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24252 var cls = ['bottom'];
24254 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24261 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24265 //this.picker().setXY(20000,20000);
24266 this.picker().addClass(cls.join('-'));
24270 Roo.each(cls, function(c){
24275 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
24276 //_this.picker().setTop(_this.inputEl().getHeight());
24280 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
24282 //_this.picker().setTop(0 - _this.picker().getHeight());
24287 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24291 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24299 onFocus : function()
24301 Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24305 onBlur : function()
24307 Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24313 this.picker().show();
24318 this.fireEvent('show', this, this.date);
24323 this.picker().hide();
24326 this.fireEvent('hide', this, this.date);
24329 setTime : function()
24332 this.setValue(this.time.format(this.format));
24334 this.fireEvent('select', this, this.date);
24339 onMousedown: function(e){
24340 e.stopPropagation();
24341 e.preventDefault();
24344 onIncrementHours: function()
24346 Roo.log('onIncrementHours');
24347 this.time = this.time.add(Date.HOUR, 1);
24352 onDecrementHours: function()
24354 Roo.log('onDecrementHours');
24355 this.time = this.time.add(Date.HOUR, -1);
24359 onIncrementMinutes: function()
24361 Roo.log('onIncrementMinutes');
24362 this.time = this.time.add(Date.MINUTE, 1);
24366 onDecrementMinutes: function()
24368 Roo.log('onDecrementMinutes');
24369 this.time = this.time.add(Date.MINUTE, -1);
24373 onTogglePeriod: function()
24375 Roo.log('onTogglePeriod');
24376 this.time = this.time.add(Date.HOUR, 12);
24384 Roo.apply(Roo.bootstrap.form.TimeField, {
24388 cls: 'datepicker dropdown-menu',
24392 cls: 'datepicker-time',
24396 cls: 'table-condensed',
24425 cls: 'btn btn-info ok',
24453 * @class Roo.bootstrap.form.MonthField
24454 * @extends Roo.bootstrap.form.Input
24455 * Bootstrap MonthField class
24457 * @cfg {String} language default en
24460 * Create a new MonthField
24461 * @param {Object} config The config object
24464 Roo.bootstrap.form.MonthField = function(config){
24465 Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24470 * Fires when this field show.
24471 * @param {Roo.bootstrap.form.MonthField} this
24472 * @param {Mixed} date The date value
24477 * Fires when this field hide.
24478 * @param {Roo.bootstrap.form.MonthField} this
24479 * @param {Mixed} date The date value
24484 * Fires when select a date.
24485 * @param {Roo.bootstrap.form.MonthField} this
24486 * @param {String} oldvalue The old value
24487 * @param {String} newvalue The new value
24493 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input, {
24495 onRender: function(ct, position)
24498 Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24500 this.language = this.language || 'en';
24501 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24502 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24504 this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24505 this.isInline = false;
24506 this.isInput = true;
24507 this.component = this.el.select('.add-on', true).first() || false;
24508 this.component = (this.component && this.component.length === 0) ? false : this.component;
24509 this.hasInput = this.component && this.inputEL().length;
24511 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24513 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24515 this.picker().on('mousedown', this.onMousedown, this);
24516 this.picker().on('click', this.onClick, this);
24518 this.picker().addClass('datepicker-dropdown');
24520 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24521 v.setStyle('width', '189px');
24528 if(this.isInline) {
24534 setValue: function(v, suppressEvent)
24536 var o = this.getValue();
24538 Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24542 if(suppressEvent !== true){
24543 this.fireEvent('select', this, o, v);
24548 getValue: function()
24553 onClick: function(e)
24555 e.stopPropagation();
24556 e.preventDefault();
24558 var target = e.getTarget();
24560 if(target.nodeName.toLowerCase() === 'i'){
24561 target = Roo.get(target).dom.parentNode;
24564 var nodeName = target.nodeName;
24565 var className = target.className;
24566 var html = target.innerHTML;
24568 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24572 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24574 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24580 picker : function()
24582 return this.pickerEl;
24585 fillMonths: function()
24588 var months = this.picker().select('>.datepicker-months td', true).first();
24590 months.dom.innerHTML = '';
24596 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24599 months.createChild(month);
24608 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24609 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24612 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24613 e.removeClass('active');
24615 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24616 e.addClass('active');
24623 if(this.isInline) {
24627 this.picker().removeClass(['bottom', 'top']);
24629 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24631 * place to the top of element!
24635 this.picker().addClass('top');
24636 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24641 this.picker().addClass('bottom');
24643 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24646 onFocus : function()
24648 Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24652 onBlur : function()
24654 Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24656 var d = this.inputEl().getValue();
24665 this.picker().show();
24666 this.picker().select('>.datepicker-months', true).first().show();
24670 this.fireEvent('show', this, this.date);
24675 if(this.isInline) {
24678 this.picker().hide();
24679 this.fireEvent('hide', this, this.date);
24683 onMousedown: function(e)
24685 e.stopPropagation();
24686 e.preventDefault();
24691 Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24695 fireKey: function(e)
24697 if (!this.picker().isVisible()){
24698 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24709 e.preventDefault();
24713 dir = e.keyCode == 37 ? -1 : 1;
24715 this.vIndex = this.vIndex + dir;
24717 if(this.vIndex < 0){
24721 if(this.vIndex > 11){
24725 if(isNaN(this.vIndex)){
24729 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24735 dir = e.keyCode == 38 ? -1 : 1;
24737 this.vIndex = this.vIndex + dir * 4;
24739 if(this.vIndex < 0){
24743 if(this.vIndex > 11){
24747 if(isNaN(this.vIndex)){
24751 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24756 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24757 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24761 e.preventDefault();
24764 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24765 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24781 this.picker().remove();
24786 Roo.apply(Roo.bootstrap.form.MonthField, {
24805 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24806 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24811 Roo.apply(Roo.bootstrap.form.MonthField, {
24815 cls: 'datepicker dropdown-menu roo-dynamic',
24819 cls: 'datepicker-months',
24823 cls: 'table-condensed',
24825 Roo.bootstrap.form.DateField.content
24845 * @class Roo.bootstrap.form.CheckBox
24846 * @extends Roo.bootstrap.form.Input
24847 * Bootstrap CheckBox class
24849 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24850 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24851 * @cfg {String} boxLabel The text that appears beside the checkbox
24852 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24853 * @cfg {Boolean} checked initnal the element
24854 * @cfg {Boolean} inline inline the element (default false)
24855 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24856 * @cfg {String} tooltip label tooltip
24859 * Create a new CheckBox
24860 * @param {Object} config The config object
24863 Roo.bootstrap.form.CheckBox = function(config){
24864 Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24869 * Fires when the element is checked or unchecked.
24870 * @param {Roo.bootstrap.form.CheckBox} this This input
24871 * @param {Boolean} checked The new checked value
24876 * Fires when the element is click.
24877 * @param {Roo.bootstrap.form.CheckBox} this This input
24884 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input, {
24886 inputType: 'checkbox',
24895 // checkbox success does not make any sense really..
24900 getAutoCreate : function()
24902 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24908 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24911 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24917 type : this.inputType,
24918 value : this.inputValue,
24919 cls : 'roo-' + this.inputType, //'form-box',
24920 placeholder : this.placeholder || ''
24924 if(this.inputType != 'radio'){
24928 cls : 'roo-hidden-value',
24929 value : this.checked ? this.inputValue : this.valueOff
24934 if (this.weight) { // Validity check?
24935 cfg.cls += " " + this.inputType + "-" + this.weight;
24938 if (this.disabled) {
24939 input.disabled=true;
24943 input.checked = this.checked;
24948 input.name = this.name;
24950 if(this.inputType != 'radio'){
24951 hidden.name = this.name;
24952 input.name = '_hidden_' + this.name;
24957 input.cls += ' input-' + this.size;
24962 ['xs','sm','md','lg'].map(function(size){
24963 if (settings[size]) {
24964 cfg.cls += ' col-' + size + '-' + settings[size];
24968 var inputblock = input;
24970 if (this.before || this.after) {
24973 cls : 'input-group',
24978 inputblock.cn.push({
24980 cls : 'input-group-addon',
24985 inputblock.cn.push(input);
24987 if(this.inputType != 'radio'){
24988 inputblock.cn.push(hidden);
24992 inputblock.cn.push({
24994 cls : 'input-group-addon',
25000 var boxLabelCfg = false;
25006 //'for': id, // box label is handled by onclick - so no for...
25008 html: this.boxLabel
25011 boxLabelCfg.tooltip = this.tooltip;
25017 if (align ==='left' && this.fieldLabel.length) {
25018 // Roo.log("left and has label");
25023 cls : 'control-label',
25024 html : this.fieldLabel
25035 cfg.cn[1].cn.push(boxLabelCfg);
25038 if(this.labelWidth > 12){
25039 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25042 if(this.labelWidth < 13 && this.labelmd == 0){
25043 this.labelmd = this.labelWidth;
25046 if(this.labellg > 0){
25047 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25048 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25051 if(this.labelmd > 0){
25052 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25053 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25056 if(this.labelsm > 0){
25057 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25058 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25061 if(this.labelxs > 0){
25062 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25063 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25066 } else if ( this.fieldLabel.length) {
25067 // Roo.log(" label");
25071 tag: this.boxLabel ? 'span' : 'label',
25073 cls: 'control-label box-input-label',
25074 //cls : 'input-group-addon',
25075 html : this.fieldLabel
25082 cfg.cn.push(boxLabelCfg);
25087 // Roo.log(" no label && no align");
25088 cfg.cn = [ inputblock ] ;
25090 cfg.cn.push(boxLabelCfg);
25098 if(this.inputType != 'radio'){
25099 cfg.cn.push(hidden);
25107 * return the real input element.
25109 inputEl: function ()
25111 return this.el.select('input.roo-' + this.inputType,true).first();
25113 hiddenEl: function ()
25115 return this.el.select('input.roo-hidden-value',true).first();
25118 labelEl: function()
25120 return this.el.select('label.control-label',true).first();
25122 /* depricated... */
25126 return this.labelEl();
25129 boxLabelEl: function()
25131 return this.el.select('label.box-label',true).first();
25134 initEvents : function()
25136 // Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25138 this.inputEl().on('click', this.onClick, this);
25140 if (this.boxLabel) {
25141 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
25144 this.startValue = this.getValue();
25147 Roo.bootstrap.form.CheckBox.register(this);
25151 onClick : function(e)
25153 if(this.fireEvent('click', this, e) !== false){
25154 this.setChecked(!this.checked);
25159 setChecked : function(state,suppressEvent)
25161 this.startValue = this.getValue();
25163 if(this.inputType == 'radio'){
25165 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25166 e.dom.checked = false;
25169 this.inputEl().dom.checked = true;
25171 this.inputEl().dom.value = this.inputValue;
25173 if(suppressEvent !== true){
25174 this.fireEvent('check', this, true);
25182 this.checked = state;
25184 this.inputEl().dom.checked = state;
25187 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25189 if(suppressEvent !== true){
25190 this.fireEvent('check', this, state);
25196 getValue : function()
25198 if(this.inputType == 'radio'){
25199 return this.getGroupValue();
25202 return this.hiddenEl().dom.value;
25206 getGroupValue : function()
25208 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25212 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25215 setValue : function(v,suppressEvent)
25217 if(this.inputType == 'radio'){
25218 this.setGroupValue(v, suppressEvent);
25222 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25227 setGroupValue : function(v, suppressEvent)
25229 this.startValue = this.getValue();
25231 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25232 e.dom.checked = false;
25234 if(e.dom.value == v){
25235 e.dom.checked = true;
25239 if(suppressEvent !== true){
25240 this.fireEvent('check', this, true);
25248 validate : function()
25250 if(this.getVisibilityEl().hasClass('hidden')){
25256 (this.inputType == 'radio' && this.validateRadio()) ||
25257 (this.inputType == 'checkbox' && this.validateCheckbox())
25263 this.markInvalid();
25267 validateRadio : function()
25269 if(this.getVisibilityEl().hasClass('hidden')){
25273 if(this.allowBlank){
25279 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25280 if(!e.dom.checked){
25292 validateCheckbox : function()
25295 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25296 //return (this.getValue() == this.inputValue) ? true : false;
25299 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25307 for(var i in group){
25308 if(group[i].el.isVisible(true)){
25316 for(var i in group){
25321 r = (group[i].getValue() == group[i].inputValue) ? true : false;
25328 * Mark this field as valid
25330 markValid : function()
25334 this.fireEvent('valid', this);
25336 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25339 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25346 if(this.inputType == 'radio'){
25347 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25348 var fg = e.findParent('.form-group', false, true);
25349 if (Roo.bootstrap.version == 3) {
25350 fg.removeClass([_this.invalidClass, _this.validClass]);
25351 fg.addClass(_this.validClass);
25353 fg.removeClass(['is-valid', 'is-invalid']);
25354 fg.addClass('is-valid');
25362 var fg = this.el.findParent('.form-group', false, true);
25363 if (Roo.bootstrap.version == 3) {
25364 fg.removeClass([this.invalidClass, this.validClass]);
25365 fg.addClass(this.validClass);
25367 fg.removeClass(['is-valid', 'is-invalid']);
25368 fg.addClass('is-valid');
25373 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25379 for(var i in group){
25380 var fg = group[i].el.findParent('.form-group', false, true);
25381 if (Roo.bootstrap.version == 3) {
25382 fg.removeClass([this.invalidClass, this.validClass]);
25383 fg.addClass(this.validClass);
25385 fg.removeClass(['is-valid', 'is-invalid']);
25386 fg.addClass('is-valid');
25392 * Mark this field as invalid
25393 * @param {String} msg The validation message
25395 markInvalid : function(msg)
25397 if(this.allowBlank){
25403 this.fireEvent('invalid', this, msg);
25405 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25408 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25412 label.markInvalid();
25415 if(this.inputType == 'radio'){
25417 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25418 var fg = e.findParent('.form-group', false, true);
25419 if (Roo.bootstrap.version == 3) {
25420 fg.removeClass([_this.invalidClass, _this.validClass]);
25421 fg.addClass(_this.invalidClass);
25423 fg.removeClass(['is-invalid', 'is-valid']);
25424 fg.addClass('is-invalid');
25432 var fg = this.el.findParent('.form-group', false, true);
25433 if (Roo.bootstrap.version == 3) {
25434 fg.removeClass([_this.invalidClass, _this.validClass]);
25435 fg.addClass(_this.invalidClass);
25437 fg.removeClass(['is-invalid', 'is-valid']);
25438 fg.addClass('is-invalid');
25443 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25449 for(var i in group){
25450 var fg = group[i].el.findParent('.form-group', false, true);
25451 if (Roo.bootstrap.version == 3) {
25452 fg.removeClass([_this.invalidClass, _this.validClass]);
25453 fg.addClass(_this.invalidClass);
25455 fg.removeClass(['is-invalid', 'is-valid']);
25456 fg.addClass('is-invalid');
25462 clearInvalid : function()
25464 Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25466 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25468 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25470 if (label && label.iconEl) {
25471 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25472 label.iconEl.removeClass(['is-invalid', 'is-valid']);
25476 disable : function()
25478 if(this.inputType != 'radio'){
25479 Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25486 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25487 _this.getActionEl().addClass(this.disabledClass);
25488 e.dom.disabled = true;
25492 this.disabled = true;
25493 this.fireEvent("disable", this);
25497 enable : function()
25499 if(this.inputType != 'radio'){
25500 Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25507 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25508 _this.getActionEl().removeClass(this.disabledClass);
25509 e.dom.disabled = false;
25513 this.disabled = false;
25514 this.fireEvent("enable", this);
25518 setBoxLabel : function(v)
25523 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25529 Roo.apply(Roo.bootstrap.form.CheckBox, {
25534 * register a CheckBox Group
25535 * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25537 register : function(checkbox)
25539 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25540 this.groups[checkbox.groupId] = {};
25543 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25547 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25551 * fetch a CheckBox Group based on the group ID
25552 * @param {string} the group ID
25553 * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25555 get: function(groupId) {
25556 if (typeof(this.groups[groupId]) == 'undefined') {
25560 return this.groups[groupId] ;
25573 * @class Roo.bootstrap.form.Radio
25574 * @extends Roo.bootstrap.Component
25575 * Bootstrap Radio class
25576 * @cfg {String} boxLabel - the label associated
25577 * @cfg {String} value - the value of radio
25580 * Create a new Radio
25581 * @param {Object} config The config object
25583 Roo.bootstrap.form.Radio = function(config){
25584 Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25588 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25594 getAutoCreate : function()
25598 cls : 'form-group radio',
25603 html : this.boxLabel
25611 initEvents : function()
25613 this.parent().register(this);
25615 this.el.on('click', this.onClick, this);
25619 onClick : function(e)
25621 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25622 this.setChecked(true);
25626 setChecked : function(state, suppressEvent)
25628 this.parent().setValue(this.value, suppressEvent);
25632 setBoxLabel : function(v)
25637 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25652 * @class Roo.bootstrap.form.SecurePass
25653 * @extends Roo.bootstrap.form.Input
25654 * Bootstrap SecurePass class
25658 * Create a new SecurePass
25659 * @param {Object} config The config object
25662 Roo.bootstrap.form.SecurePass = function (config) {
25663 // these go here, so the translation tool can replace them..
25665 PwdEmpty: "Please type a password, and then retype it to confirm.",
25666 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25667 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25668 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25669 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25670 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25671 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25672 TooWeak: "Your password is Too Weak."
25674 this.meterLabel = "Password strength:";
25675 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25676 this.meterClass = [
25677 "roo-password-meter-tooweak",
25678 "roo-password-meter-weak",
25679 "roo-password-meter-medium",
25680 "roo-password-meter-strong",
25681 "roo-password-meter-grey"
25686 Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25689 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25691 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25693 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25694 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25695 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25696 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25697 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25698 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25699 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25709 * @cfg {String/Object} Label for the strength meter (defaults to
25710 * 'Password strength:')
25715 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25716 * ['Weak', 'Medium', 'Strong'])
25719 pwdStrengths: false,
25732 initEvents: function ()
25734 Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25736 if (this.el.is('input[type=password]') && Roo.isSafari) {
25737 this.el.on('keydown', this.SafariOnKeyDown, this);
25740 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25743 onRender: function (ct, position)
25745 Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25746 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25747 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25749 this.trigger.createChild({
25754 cls: 'roo-password-meter-grey col-xs-12',
25757 //width: this.meterWidth + 'px'
25761 cls: 'roo-password-meter-text'
25767 if (this.hideTrigger) {
25768 this.trigger.setDisplayed(false);
25770 this.setSize(this.width || '', this.height || '');
25773 onDestroy: function ()
25775 if (this.trigger) {
25776 this.trigger.removeAllListeners();
25777 this.trigger.remove();
25780 this.wrap.remove();
25782 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25785 checkStrength: function ()
25787 var pwd = this.inputEl().getValue();
25788 if (pwd == this._lastPwd) {
25793 if (this.ClientSideStrongPassword(pwd)) {
25795 } else if (this.ClientSideMediumPassword(pwd)) {
25797 } else if (this.ClientSideWeakPassword(pwd)) {
25803 Roo.log('strength1: ' + strength);
25805 //var pm = this.trigger.child('div/div/div').dom;
25806 var pm = this.trigger.child('div/div');
25807 pm.removeClass(this.meterClass);
25808 pm.addClass(this.meterClass[strength]);
25811 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25813 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25815 this._lastPwd = pwd;
25819 Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25821 this._lastPwd = '';
25823 var pm = this.trigger.child('div/div');
25824 pm.removeClass(this.meterClass);
25825 pm.addClass('roo-password-meter-grey');
25828 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25831 this.inputEl().dom.type='password';
25834 validateValue: function (value)
25836 if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25839 if (value.length == 0) {
25840 if (this.allowBlank) {
25841 this.clearInvalid();
25845 this.markInvalid(this.errors.PwdEmpty);
25846 this.errorMsg = this.errors.PwdEmpty;
25854 if (!value.match(/[\x21-\x7e]+/)) {
25855 this.markInvalid(this.errors.PwdBadChar);
25856 this.errorMsg = this.errors.PwdBadChar;
25859 if (value.length < 6) {
25860 this.markInvalid(this.errors.PwdShort);
25861 this.errorMsg = this.errors.PwdShort;
25864 if (value.length > 16) {
25865 this.markInvalid(this.errors.PwdLong);
25866 this.errorMsg = this.errors.PwdLong;
25870 if (this.ClientSideStrongPassword(value)) {
25872 } else if (this.ClientSideMediumPassword(value)) {
25874 } else if (this.ClientSideWeakPassword(value)) {
25881 if (strength < 2) {
25882 //this.markInvalid(this.errors.TooWeak);
25883 this.errorMsg = this.errors.TooWeak;
25888 console.log('strength2: ' + strength);
25890 //var pm = this.trigger.child('div/div/div').dom;
25892 var pm = this.trigger.child('div/div');
25893 pm.removeClass(this.meterClass);
25894 pm.addClass(this.meterClass[strength]);
25896 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25898 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25900 this.errorMsg = '';
25904 CharacterSetChecks: function (type)
25907 this.fResult = false;
25910 isctype: function (character, type)
25913 case this.kCapitalLetter:
25914 if (character >= 'A' && character <= 'Z') {
25919 case this.kSmallLetter:
25920 if (character >= 'a' && character <= 'z') {
25926 if (character >= '0' && character <= '9') {
25931 case this.kPunctuation:
25932 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25943 IsLongEnough: function (pwd, size)
25945 return !(pwd == null || isNaN(size) || pwd.length < size);
25948 SpansEnoughCharacterSets: function (word, nb)
25950 if (!this.IsLongEnough(word, nb))
25955 var characterSetChecks = new Array(
25956 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25957 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25960 for (var index = 0; index < word.length; ++index) {
25961 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25962 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25963 characterSetChecks[nCharSet].fResult = true;
25970 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25971 if (characterSetChecks[nCharSet].fResult) {
25976 if (nCharSets < nb) {
25982 ClientSideStrongPassword: function (pwd)
25984 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25987 ClientSideMediumPassword: function (pwd)
25989 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25992 ClientSideWeakPassword: function (pwd)
25994 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25998 Roo.htmleditor = {};
26001 * @class Roo.htmleditor.Filter
26002 * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26003 * @cfg {DomElement} node The node to iterate and filter
26004 * @cfg {boolean|String|Array} tag Tags to replace
26006 * Create a new Filter.
26007 * @param {Object} config Configuration options
26012 Roo.htmleditor.Filter = function(cfg) {
26013 Roo.apply(this.cfg);
26014 // this does not actually call walk as it's really just a abstract class
26018 Roo.htmleditor.Filter.prototype = {
26024 // overrride to do replace comments.
26025 replaceComment : false,
26027 // overrride to do replace or do stuff with tags..
26028 replaceTag : false,
26030 walk : function(dom)
26032 Roo.each( Array.from(dom.childNodes), function( e ) {
26035 case e.nodeType == 8 && this.replaceComment !== false: // comment
26036 this.replaceComment(e);
26039 case e.nodeType != 1: //not a node.
26042 case this.tag === true: // everything
26043 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26044 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26045 if (this.replaceTag && false === this.replaceTag(e)) {
26048 if (e.hasChildNodes()) {
26053 default: // tags .. that do not match.
26054 if (e.hasChildNodes()) {
26065 * @class Roo.htmleditor.FilterAttributes
26066 * clean attributes and styles including http:// etc.. in attribute
26068 * Run a new Attribute Filter
26069 * @param {Object} config Configuration options
26071 Roo.htmleditor.FilterAttributes = function(cfg)
26073 Roo.apply(this, cfg);
26074 this.attrib_black = this.attrib_black || [];
26075 this.attrib_white = this.attrib_white || [];
26077 this.attrib_clean = this.attrib_clean || [];
26078 this.style_white = this.style_white || [];
26079 this.style_black = this.style_black || [];
26080 this.walk(cfg.node);
26083 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26085 tag: true, // all tags
26087 attrib_black : false, // array
26088 attrib_clean : false,
26089 attrib_white : false,
26091 style_white : false,
26092 style_black : false,
26095 replaceTag : function(node)
26097 if (!node.attributes || !node.attributes.length) {
26101 for (var i = node.attributes.length-1; i > -1 ; i--) {
26102 var a = node.attributes[i];
26104 if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26105 node.removeAttribute(a.name);
26111 if (a.name.toLowerCase().substr(0,2)=='on') {
26112 node.removeAttribute(a.name);
26117 if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26118 node.removeAttribute(a.name);
26121 if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26122 this.cleanAttr(node,a.name,a.value); // fixme..
26125 if (a.name == 'style') {
26126 this.cleanStyle(node,a.name,a.value);
26129 /// clean up MS crap..
26130 // tecnically this should be a list of valid class'es..
26133 if (a.name == 'class') {
26134 if (a.value.match(/^Mso/)) {
26135 node.removeAttribute('class');
26138 if (a.value.match(/^body$/)) {
26139 node.removeAttribute('class');
26149 return true; // clean children
26152 cleanAttr: function(node, n,v)
26155 if (v.match(/^\./) || v.match(/^\//)) {
26158 if (v.match(/^(http|https):\/\//)
26159 || v.match(/^mailto:/)
26160 || v.match(/^ftp:/)
26161 || v.match(/^data:/)
26165 if (v.match(/^#/)) {
26168 if (v.match(/^\{/)) { // allow template editing.
26171 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26172 node.removeAttribute(n);
26175 cleanStyle : function(node, n,v)
26177 if (v.match(/expression/)) { //XSS?? should we even bother..
26178 node.removeAttribute(n);
26182 var parts = v.split(/;/);
26185 Roo.each(parts, function(p) {
26186 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26190 var l = p.split(':').shift().replace(/\s+/g,'');
26191 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26193 if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26197 // only allow 'c whitelisted system attributes'
26198 if ( this.style_white.length && style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26206 if (clean.length) {
26207 node.setAttribute(n, clean.join(';'));
26209 node.removeAttribute(n);
26218 * @class Roo.htmleditor.FilterBlack
26219 * remove blacklisted elements.
26221 * Run a new Blacklisted Filter
26222 * @param {Object} config Configuration options
26225 Roo.htmleditor.FilterBlack = function(cfg)
26227 Roo.apply(this, cfg);
26228 this.walk(cfg.node);
26231 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26233 tag : true, // all elements.
26235 replaceTag : function(n)
26237 n.parentNode.removeChild(n);
26241 * @class Roo.htmleditor.FilterComment
26244 * Run a new Comments Filter
26245 * @param {Object} config Configuration options
26247 Roo.htmleditor.FilterComment = function(cfg)
26249 this.walk(cfg.node);
26252 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26255 replaceComment : function(n)
26257 n.parentNode.removeChild(n);
26260 * @class Roo.htmleditor.FilterKeepChildren
26261 * remove tags but keep children
26263 * Run a new Keep Children Filter
26264 * @param {Object} config Configuration options
26267 Roo.htmleditor.FilterKeepChildren = function(cfg)
26269 Roo.apply(this, cfg);
26270 if (this.tag === false) {
26271 return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26273 this.walk(cfg.node);
26276 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26280 replaceTag : function(node)
26282 // walk children...
26284 var ar = Array.from(node.childNodes);
26286 for (var i = 0; i < ar.length; i++) {
26287 if (ar[i].nodeType == 1) {
26289 (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
26290 || // array and it matches
26291 (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
26293 this.replaceTag(ar[i]); // child is blacklisted as well...
26298 ar = Array.from(node.childNodes);
26299 for (var i = 0; i < ar.length; i++) {
26301 node.removeChild(ar[i]);
26302 // what if we need to walk these???
26303 node.parentNode.insertBefore(ar[i], node);
26304 if (this.tag !== false) {
26309 node.parentNode.removeChild(node);
26310 return false; // don't walk children
26315 * @class Roo.htmleditor.FilterParagraph
26316 * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26317 * like on 'push' to remove the <p> tags and replace them with line breaks.
26319 * Run a new Paragraph Filter
26320 * @param {Object} config Configuration options
26323 Roo.htmleditor.FilterParagraph = function(cfg)
26325 // no need to apply config.
26326 this.walk(cfg.node);
26329 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26336 replaceTag : function(node)
26339 if (node.childNodes.length == 1 &&
26340 node.childNodes[0].nodeType == 3 &&
26341 node.childNodes[0].textContent.trim().length < 1
26343 // remove and replace with '<BR>';
26344 node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26345 return false; // no need to walk..
26347 var ar = Array.from(node.childNodes);
26348 for (var i = 0; i < ar.length; i++) {
26349 node.removeChild(ar[i]);
26350 // what if we need to walk these???
26351 node.parentNode.insertBefore(ar[i], node);
26353 // now what about this?
26357 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26358 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26359 node.parentNode.removeChild(node);
26366 * @class Roo.htmleditor.FilterSpan
26367 * filter span's with no attributes out..
26369 * Run a new Span Filter
26370 * @param {Object} config Configuration options
26373 Roo.htmleditor.FilterSpan = function(cfg)
26375 // no need to apply config.
26376 this.walk(cfg.node);
26379 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26385 replaceTag : function(node)
26387 if (node.attributes && node.attributes.length > 0) {
26388 return true; // walk if there are any.
26390 Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26396 * @class Roo.htmleditor.FilterTableWidth
26397 try and remove table width data - as that frequently messes up other stuff.
26399 * was cleanTableWidths.
26401 * Quite often pasting from word etc.. results in tables with column and widths.
26402 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26405 * Run a new Table Filter
26406 * @param {Object} config Configuration options
26409 Roo.htmleditor.FilterTableWidth = function(cfg)
26411 // no need to apply config.
26412 this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26413 this.walk(cfg.node);
26416 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26421 replaceTag: function(node) {
26425 if (node.hasAttribute('width')) {
26426 node.removeAttribute('width');
26430 if (node.hasAttribute("style")) {
26433 var styles = node.getAttribute("style").split(";");
26435 Roo.each(styles, function(s) {
26436 if (!s.match(/:/)) {
26439 var kv = s.split(":");
26440 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26443 // what ever is left... we allow.
26446 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26447 if (!nstyle.length) {
26448 node.removeAttribute('style');
26452 return true; // continue doing children..
26455 * @class Roo.htmleditor.FilterWord
26456 * try and clean up all the mess that Word generates.
26458 * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters
26461 * Run a new Span Filter
26462 * @param {Object} config Configuration options
26465 Roo.htmleditor.FilterWord = function(cfg)
26467 // no need to apply config.
26468 this.replaceDocBullets(cfg.node);
26470 // this.walk(cfg.node);
26475 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
26481 * Clean up MS wordisms...
26483 replaceTag : function(node)
26486 // no idea what this does - span with text, replaceds with just text.
26488 node.nodeName == 'SPAN' &&
26489 !node.hasAttributes() &&
26490 node.childNodes.length == 1 &&
26491 node.firstChild.nodeName == "#text"
26493 var textNode = node.firstChild;
26494 node.removeChild(textNode);
26495 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26496 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26498 node.parentNode.insertBefore(textNode, node);
26499 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26500 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26503 node.parentNode.removeChild(node);
26504 return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
26509 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26510 node.parentNode.removeChild(node);
26511 return false; // dont do chidlren
26513 //Roo.log(node.tagName);
26514 // remove - but keep children..
26515 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26516 //Roo.log('-- removed');
26517 while (node.childNodes.length) {
26518 var cn = node.childNodes[0];
26519 node.removeChild(cn);
26520 node.parentNode.insertBefore(cn, node);
26521 // move node to parent - and clean it..
26522 if (cn.nodeType == 1) {
26523 this.replaceTag(cn);
26527 node.parentNode.removeChild(node);
26528 /// no need to iterate chidlren = it's got none..
26529 //this.iterateChildren(node, this.cleanWord);
26530 return false; // no need to iterate children.
26533 if (node.className.length) {
26535 var cn = node.className.split(/\W+/);
26537 Roo.each(cn, function(cls) {
26538 if (cls.match(/Mso[a-zA-Z]+/)) {
26543 node.className = cna.length ? cna.join(' ') : '';
26545 node.removeAttribute("class");
26549 if (node.hasAttribute("lang")) {
26550 node.removeAttribute("lang");
26553 if (node.hasAttribute("style")) {
26555 var styles = node.getAttribute("style").split(";");
26557 Roo.each(styles, function(s) {
26558 if (!s.match(/:/)) {
26561 var kv = s.split(":");
26562 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26565 // what ever is left... we allow.
26568 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26569 if (!nstyle.length) {
26570 node.removeAttribute('style');
26573 return true; // do children
26579 styleToObject: function(node)
26581 var styles = (node.getAttribute("style") || '').split(";");
26583 Roo.each(styles, function(s) {
26584 if (!s.match(/:/)) {
26587 var kv = s.split(":");
26589 // what ever is left... we allow.
26590 ret[kv[0].trim()] = kv[1];
26596 replaceDocBullets : function(doc)
26598 // this is a bit odd - but it appears some indents use ql-indent-1
26600 var listpara = doc.getElementsByClassName('ql-indent-1');
26601 while(listpara.length) {
26602 this.replaceDocBullet(listpara.item(0));
26605 var listpara = doc.getElementsByClassName('MsoListParagraph');
26606 while(listpara.length) {
26607 this.replaceDocBullet(listpara.item(0));
26611 replaceDocBullet : function(p)
26613 // gather all the siblings.
26615 parent = p.parentNode,
26616 doc = parent.ownerDocument,
26621 if (ns.nodeType != 1) {
26622 ns = ns.nextSibling;
26625 if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
26629 ns = ns.nextSibling;
26633 var ul = parent.ownerDocument.createElement('ul'); // what about number lists...
26634 parent.insertBefore(ul, p);
26636 var stack = [ ul ];
26637 var last_li = false;
26639 items.forEach(function(n, ipos) {
26640 //Roo.log("got innertHMLT=" + n.innerHTML);
26642 var spans = n.getElementsByTagName('span');
26643 if (!spans.length) {
26644 //Roo.log("No spans found");
26646 parent.removeChild(n);
26647 return; // skip it...
26653 for(var i = 0; i < spans.length; i++) {
26655 style = this.styleToObject(spans[i]);
26656 if (typeof(style['mso-list']) == 'undefined') {
26660 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
26663 //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
26664 style = this.styleToObject(n); // mo-list is from the parent node.
26665 if (typeof(style['mso-list']) == 'undefined') {
26666 //Roo.log("parent is missing level");
26667 parent.removeChild(n);
26671 var nlvl = (style['mso-list'].split(' ')[1].replace(/level/,'') *1) - 1 ;
26677 var nul = doc.createElement('ul'); // what about number lists...
26678 last_li.appendChild(nul);
26684 var nli = stack[nlvl].appendChild(doc.createElement('li'));
26686 nli.innerHTML = n.innerHTML;
26687 //Roo.log("innerHTML = " + n.innerHTML);
26688 parent.removeChild(n);
26690 // copy children of p into nli
26691 /*while(n.firstChild) {
26692 var fc = n.firstChild;
26694 nli.appendChild(fc);
26709 * @class Roo.htmleditor.FilterStyleToTag
26710 * part of the word stuff... - certain 'styles' should be converted to tags.
26712 * font-weight: bold -> bold
26713 * ?? super / subscrit etc..
26716 * Run a new style to tag filter.
26717 * @param {Object} config Configuration options
26719 Roo.htmleditor.FilterStyleToTag = function(cfg)
26723 B : [ 'fontWeight' , 'bold'],
26724 I : [ 'fontStyle' , 'italic'],
26725 //pre : [ 'font-style' , 'italic'],
26726 // h1.. h6 ?? font-size?
26727 SUP : [ 'verticalAlign' , 'super' ],
26728 SUB : [ 'verticalAlign' , 'sub' ]
26733 Roo.apply(this, cfg);
26736 this.walk(cfg.node);
26743 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
26745 tag: true, // all tags
26750 replaceTag : function(node)
26754 if (node.getAttribute("style") === null) {
26758 for (var k in this.tags) {
26759 if (node.style[this.tags[k][0]] == this.tags[k][1]) {
26761 node.style.removeProperty(this.tags[k][0]);
26764 if (!inject.length) {
26767 var cn = Array.from(node.childNodes);
26769 Roo.each(inject, function(t) {
26770 var nc = node.ownerDocument.createElement(t);
26771 nn.appendChild(nc);
26774 for(var i = 0;i < cn.length;cn++) {
26775 node.removeChild(cn[i]);
26776 nn.appendChild(cn[i]);
26778 return true /// iterate thru
26782 * @class Roo.htmleditor.FilterLongBr
26783 * BR/BR/BR - keep a maximum of 2...
26785 * Run a new Long BR Filter
26786 * @param {Object} config Configuration options
26789 Roo.htmleditor.FilterLongBr = function(cfg)
26791 // no need to apply config.
26792 this.walk(cfg.node);
26795 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
26802 replaceTag : function(node)
26805 var ps = node.nextSibling;
26806 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26807 ps = ps.nextSibling;
26810 if (!ps && [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) {
26811 node.parentNode.removeChild(node); // remove last BR inside one fo these tags
26815 if (!ps || ps.nodeType != 1) {
26819 if (!ps || ps.tagName != 'BR') {
26828 if (!node.previousSibling) {
26831 var ps = node.previousSibling;
26833 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26834 ps = ps.previousSibling;
26836 if (!ps || ps.nodeType != 1) {
26839 // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
26840 if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
26844 node.parentNode.removeChild(node); // remove me...
26846 return false; // no need to do children
26853 * @class Roo.htmleditor.FilterBlock
26854 * removes id / data-block and contenteditable that are associated with blocks
26855 * usage should be done on a cloned copy of the dom
26857 * Run a new Attribute Filter { node : xxxx }}
26858 * @param {Object} config Configuration options
26860 Roo.htmleditor.FilterBlock = function(cfg)
26862 Roo.apply(this, cfg);
26863 var qa = cfg.node.querySelectorAll;
26864 this.removeAttributes('data-block');
26865 this.removeAttributes('contenteditable');
26866 this.removeAttributes('id');
26870 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
26872 node: true, // all tags
26875 removeAttributes : function(attr)
26877 var ar = this.node.querySelectorAll('*[' + attr + ']');
26878 for (var i =0;i<ar.length;i++) {
26879 ar[i].removeAttribute(attr);
26888 * @class Roo.htmleditor.KeyEnter
26889 * Handle Enter press..
26890 * @cfg {Roo.HtmlEditorCore} core the editor.
26892 * Create a new Filter.
26893 * @param {Object} config Configuration options
26900 Roo.htmleditor.KeyEnter = function(cfg) {
26901 Roo.apply(this, cfg);
26902 // this does not actually call walk as it's really just a abstract class
26904 Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
26907 //Roo.htmleditor.KeyEnter.i = 0;
26910 Roo.htmleditor.KeyEnter.prototype = {
26914 keypress : function(e)
26916 if (e.charCode != 13 && e.charCode != 10) {
26917 Roo.log([e.charCode,e]);
26920 e.preventDefault();
26921 // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
26922 var doc = this.core.doc;
26926 var sel = this.core.getSelection();
26927 var range = sel.getRangeAt(0);
26928 var n = range.commonAncestorContainer;
26929 var pc = range.closest([ 'ol', 'ul']);
26930 var pli = range.closest('li');
26931 if (!pc || e.ctrlKey) {
26932 sel.insertNode('br', 'after');
26934 this.core.undoManager.addEvent();
26935 this.core.fireEditorEvent(e);
26939 // deal with <li> insetion
26940 if (pli.innerText.trim() == '' &&
26941 pli.previousSibling &&
26942 pli.previousSibling.nodeName == 'LI' &&
26943 pli.previousSibling.innerText.trim() == '') {
26944 pli.parentNode.removeChild(pli.previousSibling);
26945 sel.cursorAfter(pc);
26946 this.core.undoManager.addEvent();
26947 this.core.fireEditorEvent(e);
26951 var li = doc.createElement('LI');
26952 li.innerHTML = ' ';
26953 if (!pli || !pli.firstSibling) {
26954 pc.appendChild(li);
26956 pli.parentNode.insertBefore(li, pli.firstSibling);
26958 sel.cursorText (li.firstChild);
26960 this.core.undoManager.addEvent();
26961 this.core.fireEditorEvent(e);
26973 * @class Roo.htmleditor.Block
26974 * Base class for html editor blocks - do not use it directly .. extend it..
26975 * @cfg {DomElement} node The node to apply stuff to.
26976 * @cfg {String} friendly_name the name that appears in the context bar about this block
26977 * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
26980 * Create a new Filter.
26981 * @param {Object} config Configuration options
26984 Roo.htmleditor.Block = function(cfg)
26986 // do nothing .. should not be called really.
26989 * factory method to get the block from an element (using cache if necessary)
26991 * @param {HtmlElement} the dom element
26993 Roo.htmleditor.Block.factory = function(node)
26995 var cc = Roo.htmleditor.Block.cache;
26996 var id = Roo.get(node).id;
26997 if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
26998 Roo.htmleditor.Block.cache[id].readElement(node);
26999 return Roo.htmleditor.Block.cache[id];
27001 var db = node.getAttribute('data-block');
27003 db = node.nodeName.toLowerCase().toUpperCaseFirst();
27005 var cls = Roo.htmleditor['Block' + db];
27006 if (typeof(cls) == 'undefined') {
27007 //Roo.log(node.getAttribute('data-block'));
27008 Roo.log("OOps missing block : " + 'Block' + db);
27011 Roo.htmleditor.Block.cache[id] = new cls({ node: node });
27012 return Roo.htmleditor.Block.cache[id]; /// should trigger update element
27016 * initalize all Elements from content that are 'blockable'
27018 * @param the body element
27020 Roo.htmleditor.Block.initAll = function(body, type)
27022 if (typeof(type) == 'undefined') {
27023 var ia = Roo.htmleditor.Block.initAll;
27029 Roo.each(Roo.get(body).query(type), function(e) {
27030 Roo.htmleditor.Block.factory(e);
27033 // question goes here... do we need to clear out this cache sometimes?
27034 // or show we make it relivant to the htmleditor.
27035 Roo.htmleditor.Block.cache = {};
27037 Roo.htmleditor.Block.prototype = {
27041 // used by context menu
27042 friendly_name : 'Based Block',
27044 // text for button to delete this element
27045 deleteTitle : false,
27049 * Update a node with values from this object
27050 * @param {DomElement} node
27052 updateElement : function(node)
27054 Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
27057 * convert to plain HTML for calling insertAtCursor..
27059 toHTML : function()
27061 return Roo.DomHelper.markup(this.toObject());
27064 * used by readEleemnt to extract data from a node
27065 * may need improving as it's pretty basic
27067 * @param {DomElement} node
27068 * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
27069 * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
27070 * @param {String} style the style property - eg. text-align
27072 getVal : function(node, tag, attr, style)
27075 if (tag !== true && n.tagName != tag.toUpperCase()) {
27076 // in theory we could do figure[3] << 3rd figure? or some more complex search..?
27077 // but kiss for now.
27078 n = node.getElementsByTagName(tag).item(0);
27083 if (attr === false) {
27086 if (attr == 'html') {
27087 return n.innerHTML;
27089 if (attr == 'style') {
27090 return n.style[style];
27093 return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
27097 * create a DomHelper friendly object - for use with
27098 * Roo.DomHelper.markup / overwrite / etc..
27101 toObject : function()
27106 * Read a node that has a 'data-block' property - and extract the values from it.
27107 * @param {DomElement} node - the node
27109 readElement : function(node)
27120 * @class Roo.htmleditor.BlockFigure
27121 * Block that has an image and a figcaption
27122 * @cfg {String} image_src the url for the image
27123 * @cfg {String} align (left|right) alignment for the block default left
27124 * @cfg {String} caption the text to appear below (and in the alt tag)
27125 * @cfg {String} caption_display (block|none) display or not the caption
27126 * @cfg {String|number} image_width the width of the image number or %?
27127 * @cfg {String|number} image_height the height of the image number or %?
27130 * Create a new Filter.
27131 * @param {Object} config Configuration options
27134 Roo.htmleditor.BlockFigure = function(cfg)
27137 this.readElement(cfg.node);
27138 this.updateElement(cfg.node);
27140 Roo.apply(this, cfg);
27142 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
27149 caption_display : 'block',
27155 // margin: '2%', not used
27157 text_align: 'left', // (left|right) alignment for the text caption default left. - not used at present
27160 // used by context menu
27161 friendly_name : 'Image with caption',
27162 deleteTitle : "Delete Image and Caption",
27164 contextMenu : function(toolbar)
27167 var block = function() {
27168 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
27172 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
27174 var syncValue = toolbar.editorcore.syncValue;
27180 xtype : 'TextItem',
27182 xns : rooui.Toolbar //Boostrap?
27186 text: 'Change Image URL',
27189 click: function (btn, state)
27193 Roo.MessageBox.show({
27194 title : "Image Source URL",
27195 msg : "Enter the url for the image",
27196 buttons: Roo.MessageBox.OKCANCEL,
27197 fn: function(btn, val){
27204 toolbar.editorcore.onEditorEvent();
27208 //multiline: multiline,
27210 value : b.image_src
27214 xns : rooui.Toolbar
27219 text: 'Change Link URL',
27222 click: function (btn, state)
27226 Roo.MessageBox.show({
27227 title : "Link URL",
27228 msg : "Enter the url for the link - leave blank to have no link",
27229 buttons: Roo.MessageBox.OKCANCEL,
27230 fn: function(btn, val){
27237 toolbar.editorcore.onEditorEvent();
27241 //multiline: multiline,
27247 xns : rooui.Toolbar
27251 text: 'Show Video URL',
27254 click: function (btn, state)
27256 Roo.MessageBox.alert("Video URL",
27257 block().video_url == '' ? 'This image is not linked ot a video' :
27258 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
27261 xns : rooui.Toolbar
27266 xtype : 'TextItem',
27268 xns : rooui.Toolbar //Boostrap?
27271 xtype : 'ComboBox',
27272 allowBlank : false,
27273 displayField : 'val',
27276 triggerAction : 'all',
27278 valueField : 'val',
27282 select : function (combo, r, index)
27284 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27286 b.width = r.get('val');
27289 toolbar.editorcore.onEditorEvent();
27294 xtype : 'SimpleStore',
27305 xtype : 'TextItem',
27307 xns : rooui.Toolbar //Boostrap?
27310 xtype : 'ComboBox',
27311 allowBlank : false,
27312 displayField : 'val',
27315 triggerAction : 'all',
27317 valueField : 'val',
27321 select : function (combo, r, index)
27323 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27325 b.align = r.get('val');
27328 toolbar.editorcore.onEditorEvent();
27333 xtype : 'SimpleStore',
27347 text: 'Hide Caption',
27348 name : 'caption_display',
27350 enableToggle : true,
27351 setValue : function(v) {
27352 // this trigger toggle.
27354 this.setText(v ? "Hide Caption" : "Show Caption");
27355 this.setPressed(v != 'block');
27358 toggle: function (btn, state)
27361 b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
27362 this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
27365 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27366 toolbar.editorcore.onEditorEvent();
27369 xns : rooui.Toolbar
27375 * create a DomHelper friendly object - for use with
27376 * Roo.DomHelper.markup / overwrite / etc..
27378 toObject : function()
27380 var d = document.createElement('div');
27381 d.innerHTML = this.caption;
27383 var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0;
27385 var iw = this.align == 'center' ? this.width : '100%';
27388 contenteditable : 'false',
27389 src : this.image_src,
27390 alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
27393 maxWidth : iw + ' !important', // this is not getting rendered?
27399 '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
27401 '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' +
27406 if (this.href.length > 0) {
27410 contenteditable : 'true',
27418 if (this.video_url.length > 0) {
27423 allowfullscreen : true,
27424 width : 420, // these are for video tricks - that we replace the outer
27426 src : this.video_url,
27432 // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
27433 var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
27438 'data-block' : 'Figure',
27439 'data-width' : this.width,
27440 contenteditable : 'false',
27444 float : this.align ,
27445 maxWidth : this.align == 'center' ? '100% !important' : (this.width + ' !important'),
27446 width : this.align == 'center' ? '100%' : this.width,
27448 padding: this.align == 'center' ? '0' : '0 10px' ,
27449 textAlign : this.align // seems to work for email..
27454 align : this.align,
27460 'data-display' : this.caption_display,
27462 textAlign : 'left',
27464 lineHeight : '24px',
27465 display : this.caption_display,
27466 maxWidth : (this.align == 'center' ? this.width : '100%' ) + ' !important',
27468 width: this.align == 'center' ? this.width : '100%'
27472 cls : this.cls.length > 0 ? (this.cls + '-thumbnail' ) : '',
27477 marginTop : '16px',
27483 // we can not rely on yahoo syndication to use CSS elements - so have to use '<i>' to encase stuff.
27485 contenteditable : true,
27501 readElement : function(node)
27503 // this should not really come from the link...
27504 this.video_url = this.getVal(node, 'div', 'src');
27505 this.cls = this.getVal(node, 'div', 'class');
27506 this.href = this.getVal(node, 'a', 'href');
27509 this.image_src = this.getVal(node, 'img', 'src');
27511 this.align = this.getVal(node, 'figure', 'align');
27512 var figcaption = this.getVal(node, 'figcaption', false);
27513 if (figcaption !== '') {
27514 this.caption = this.getVal(figcaption, 'i', 'html');
27518 this.caption_display = this.getVal(node, 'figcaption', 'data-display');
27519 //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
27520 this.width = this.getVal(node, true, 'data-width');
27521 //this.margin = this.getVal(node, 'figure', 'style', 'margin');
27524 removeNode : function()
27541 * @class Roo.htmleditor.BlockTable
27542 * Block that manages a table
27545 * Create a new Filter.
27546 * @param {Object} config Configuration options
27549 Roo.htmleditor.BlockTable = function(cfg)
27552 this.readElement(cfg.node);
27553 this.updateElement(cfg.node);
27555 Roo.apply(this, cfg);
27558 for(var r = 0; r < this.no_row; r++) {
27560 for(var c = 0; c < this.no_col; c++) {
27561 this.rows[r][c] = this.emptyCell();
27568 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
27577 // used by context menu
27578 friendly_name : 'Table',
27579 deleteTitle : 'Delete Table',
27580 // context menu is drawn once..
27582 contextMenu : function(toolbar)
27585 var block = function() {
27586 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
27590 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
27592 var syncValue = toolbar.editorcore.syncValue;
27598 xtype : 'TextItem',
27600 xns : rooui.Toolbar //Boostrap?
27603 xtype : 'ComboBox',
27604 allowBlank : false,
27605 displayField : 'val',
27608 triggerAction : 'all',
27610 valueField : 'val',
27614 select : function (combo, r, index)
27616 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27618 b.width = r.get('val');
27621 toolbar.editorcore.onEditorEvent();
27626 xtype : 'SimpleStore',
27638 xtype : 'TextItem',
27639 text : "Columns: ",
27640 xns : rooui.Toolbar //Boostrap?
27647 click : function (_self, e)
27649 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27650 block().removeColumn();
27652 toolbar.editorcore.onEditorEvent();
27655 xns : rooui.Toolbar
27661 click : function (_self, e)
27663 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27664 block().addColumn();
27666 toolbar.editorcore.onEditorEvent();
27669 xns : rooui.Toolbar
27673 xtype : 'TextItem',
27675 xns : rooui.Toolbar //Boostrap?
27682 click : function (_self, e)
27684 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27685 block().removeRow();
27687 toolbar.editorcore.onEditorEvent();
27690 xns : rooui.Toolbar
27696 click : function (_self, e)
27700 toolbar.editorcore.onEditorEvent();
27703 xns : rooui.Toolbar
27708 text: 'Reset Column Widths',
27711 click : function (_self, e)
27713 block().resetWidths();
27715 toolbar.editorcore.onEditorEvent();
27718 xns : rooui.Toolbar
27729 * create a DomHelper friendly object - for use with
27730 * Roo.DomHelper.markup / overwrite / etc..
27731 * ?? should it be called with option to hide all editing features?
27733 toObject : function()
27738 contenteditable : 'false', // this stops cell selection from picking the table.
27739 'data-block' : 'Table',
27742 border : 'solid 1px #000', // ??? hard coded?
27743 'border-collapse' : 'collapse'
27746 { tag : 'tbody' , cn : [] }
27750 // do we have a head = not really
27752 Roo.each(this.rows, function( row ) {
27757 border : 'solid 1px #000',
27763 ret.cn[0].cn.push(tr);
27764 // does the row have any properties? ?? height?
27766 Roo.each(row, function( cell ) {
27770 contenteditable : 'true',
27771 'data-block' : 'Td',
27775 if (cell.colspan > 1) {
27776 td.colspan = cell.colspan ;
27777 nc += cell.colspan;
27781 if (cell.rowspan > 1) {
27782 td.rowspan = cell.rowspan ;
27791 ncols = Math.max(nc, ncols);
27795 // add the header row..
27804 readElement : function(node)
27806 node = node ? node : this.node ;
27807 this.width = this.getVal(node, true, 'style', 'width') || '100%';
27811 var trs = Array.from(node.rows);
27812 trs.forEach(function(tr) {
27814 this.rows.push(row);
27818 Array.from(tr.cells).forEach(function(td) {
27821 colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
27822 rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
27823 style : td.hasAttribute('style') ? td.getAttribute('style') : '',
27824 html : td.innerHTML
27826 no_column += add.colspan;
27833 this.no_col = Math.max(this.no_col, no_column);
27840 normalizeRows: function()
27844 this.rows.forEach(function(row) {
27847 row = this.normalizeRow(row);
27849 row.forEach(function(c) {
27850 while (typeof(ret[rid][cid]) != 'undefined') {
27853 if (typeof(ret[rid]) == 'undefined') {
27859 if (c.rowspan < 2) {
27863 for(var i = 1 ;i < c.rowspan; i++) {
27864 if (typeof(ret[rid+i]) == 'undefined') {
27867 ret[rid+i][cid] = c;
27875 normalizeRow: function(row)
27878 row.forEach(function(c) {
27879 if (c.colspan < 2) {
27883 for(var i =0 ;i < c.colspan; i++) {
27891 deleteColumn : function(sel)
27893 if (!sel || sel.type != 'col') {
27896 if (this.no_col < 2) {
27900 this.rows.forEach(function(row) {
27901 var cols = this.normalizeRow(row);
27902 var col = cols[sel.col];
27903 if (col.colspan > 1) {
27913 removeColumn : function()
27915 this.deleteColumn({
27917 col : this.no_col-1
27919 this.updateElement();
27923 addColumn : function()
27926 this.rows.forEach(function(row) {
27927 row.push(this.emptyCell());
27930 this.updateElement();
27933 deleteRow : function(sel)
27935 if (!sel || sel.type != 'row') {
27939 if (this.no_row < 2) {
27943 var rows = this.normalizeRows();
27946 rows[sel.row].forEach(function(col) {
27947 if (col.rowspan > 1) {
27950 col.remove = 1; // flage it as removed.
27955 this.rows.forEach(function(row) {
27957 row.forEach(function(c) {
27958 if (typeof(c.remove) == 'undefined') {
27963 if (newrow.length > 0) {
27967 this.rows = newrows;
27972 this.updateElement();
27975 removeRow : function()
27979 row : this.no_row-1
27985 addRow : function()
27989 for (var i = 0; i < this.no_col; i++ ) {
27991 row.push(this.emptyCell());
27994 this.rows.push(row);
27995 this.updateElement();
27999 // the default cell object... at present...
28000 emptyCell : function() {
28001 return (new Roo.htmleditor.BlockTd({})).toObject();
28006 removeNode : function()
28013 resetWidths : function()
28015 Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
28016 var nn = Roo.htmleditor.Block.factory(n);
28018 nn.updateElement(n);
28031 * since selections really work on the table cell, then editing really should work from there
28033 * The original plan was to support merging etc... - but that may not be needed yet..
28035 * So this simple version will support:
28037 * adjust the width +/-
28038 * reset the width...
28047 * @class Roo.htmleditor.BlockTable
28048 * Block that manages a table
28051 * Create a new Filter.
28052 * @param {Object} config Configuration options
28055 Roo.htmleditor.BlockTd = function(cfg)
28058 this.readElement(cfg.node);
28059 this.updateElement(cfg.node);
28061 Roo.apply(this, cfg);
28066 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
28071 textAlign : 'left',
28078 // used by context menu
28079 friendly_name : 'Table Cell',
28080 deleteTitle : false, // use our customer delete
28082 // context menu is drawn once..
28084 contextMenu : function(toolbar)
28087 var cell = function() {
28088 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
28091 var table = function() {
28092 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
28096 var saveSel = function()
28098 lr = toolbar.editorcore.getSelection().getRangeAt(0);
28100 var restoreSel = function()
28104 toolbar.editorcore.focus();
28105 var cr = toolbar.editorcore.getSelection();
28106 cr.removeAllRanges();
28108 toolbar.editorcore.onEditorEvent();
28109 }).defer(10, this);
28115 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
28117 var syncValue = toolbar.editorcore.syncValue;
28124 text : 'Edit Table',
28126 click : function() {
28127 var t = toolbar.tb.selectedNode.closest('table');
28128 toolbar.editorcore.selectNode(t);
28129 toolbar.editorcore.onEditorEvent();
28138 xtype : 'TextItem',
28139 text : "Column Width: ",
28140 xns : rooui.Toolbar
28147 click : function (_self, e)
28149 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28150 cell().shrinkColumn();
28152 toolbar.editorcore.onEditorEvent();
28155 xns : rooui.Toolbar
28161 click : function (_self, e)
28163 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28164 cell().growColumn();
28166 toolbar.editorcore.onEditorEvent();
28169 xns : rooui.Toolbar
28173 xtype : 'TextItem',
28174 text : "Vertical Align: ",
28175 xns : rooui.Toolbar //Boostrap?
28178 xtype : 'ComboBox',
28179 allowBlank : false,
28180 displayField : 'val',
28183 triggerAction : 'all',
28185 valueField : 'val',
28189 select : function (combo, r, index)
28191 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28193 b.valign = r.get('val');
28196 toolbar.editorcore.onEditorEvent();
28201 xtype : 'SimpleStore',
28205 ['bottom'] // there are afew more...
28213 xtype : 'TextItem',
28214 text : "Merge Cells: ",
28215 xns : rooui.Toolbar
28224 click : function (_self, e)
28226 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28227 cell().mergeRight();
28228 //block().growColumn();
28230 toolbar.editorcore.onEditorEvent();
28233 xns : rooui.Toolbar
28240 click : function (_self, e)
28242 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28243 cell().mergeBelow();
28244 //block().growColumn();
28246 toolbar.editorcore.onEditorEvent();
28249 xns : rooui.Toolbar
28252 xtype : 'TextItem',
28254 xns : rooui.Toolbar
28262 click : function (_self, e)
28264 //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28267 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28268 toolbar.editorcore.onEditorEvent();
28272 xns : rooui.Toolbar
28276 xns : rooui.Toolbar
28285 xns : rooui.Toolbar,
28294 click : function (_self, e)
28298 cell().deleteColumn();
28300 toolbar.editorcore.selectNode(t.node);
28301 toolbar.editorcore.onEditorEvent();
28310 click : function (_self, e)
28313 cell().deleteRow();
28316 toolbar.editorcore.selectNode(t.node);
28317 toolbar.editorcore.onEditorEvent();
28324 xtype : 'Separator',
28331 click : function (_self, e)
28334 var nn = t.node.nextSibling || t.node.previousSibling;
28335 t.node.parentNode.removeChild(t.node);
28337 toolbar.editorcore.selectNode(nn, true);
28339 toolbar.editorcore.onEditorEvent();
28349 // align... << fixme
28357 * create a DomHelper friendly object - for use with
28358 * Roo.DomHelper.markup / overwrite / etc..
28359 * ?? should it be called with option to hide all editing features?
28362 * create a DomHelper friendly object - for use with
28363 * Roo.DomHelper.markup / overwrite / etc..
28364 * ?? should it be called with option to hide all editing features?
28366 toObject : function()
28371 contenteditable : 'true', // this stops cell selection from picking the table.
28372 'data-block' : 'Td',
28373 valign : this.valign,
28375 'text-align' : this.textAlign,
28376 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
28377 'border-collapse' : 'collapse',
28378 padding : '6px', // 8 for desktop / 4 for mobile
28379 'vertical-align': this.valign
28383 if (this.width != '') {
28384 ret.width = this.width;
28385 ret.style.width = this.width;
28389 if (this.colspan > 1) {
28390 ret.colspan = this.colspan ;
28392 if (this.rowspan > 1) {
28393 ret.rowspan = this.rowspan ;
28402 readElement : function(node)
28404 node = node ? node : this.node ;
28405 this.width = node.style.width;
28406 this.colspan = Math.max(1,1*node.getAttribute('colspan'));
28407 this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
28408 this.html = node.innerHTML;
28413 // the default cell object... at present...
28414 emptyCell : function() {
28418 textAlign : 'left',
28419 html : " " // is this going to be editable now?
28424 removeNode : function()
28426 return this.node.closest('table');
28434 toTableArray : function()
28437 var tab = this.node.closest('tr').closest('table');
28438 Array.from(tab.rows).forEach(function(r, ri){
28442 this.colWidths = [];
28443 var all_auto = true;
28444 Array.from(tab.rows).forEach(function(r, ri){
28447 Array.from(r.cells).forEach(function(ce, ci){
28452 colspan : ce.colSpan,
28453 rowspan : ce.rowSpan
28455 if (ce.isEqualNode(this.node)) {
28458 // if we have been filled up by a row?
28459 if (typeof(ret[rn][cn]) != 'undefined') {
28460 while(typeof(ret[rn][cn]) != 'undefined') {
28466 if (typeof(this.colWidths[cn]) == 'undefined') {
28467 this.colWidths[cn] = ce.style.width;
28468 if (this.colWidths[cn] != '') {
28474 if (c.colspan < 2 && c.rowspan < 2 ) {
28479 for(var j = 0; j < c.rowspan; j++) {
28480 if (typeof(ret[rn+j]) == 'undefined') {
28481 continue; // we have a problem..
28484 for(var i = 0; i < c.colspan; i++) {
28485 ret[rn+j][cn+i] = c;
28494 // initalize widths.?
28495 // either all widths or no widths..
28497 this.colWidths[0] = false; // no widths flag.
28508 mergeRight: function()
28511 // get the contents of the next cell along..
28512 var tr = this.node.closest('tr');
28513 var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
28514 if (i >= tr.childNodes.length - 1) {
28515 return; // no cells on right to merge with.
28517 var table = this.toTableArray();
28519 if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
28520 return; // nothing right?
28522 var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
28523 // right cell - must be same rowspan and on the same row.
28524 if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
28525 return; // right hand side is not same rowspan.
28530 this.node.innerHTML += ' ' + rc.cell.innerHTML;
28531 tr.removeChild(rc.cell);
28532 this.colspan += rc.colspan;
28533 this.node.setAttribute('colspan', this.colspan);
28538 mergeBelow : function()
28540 var table = this.toTableArray();
28541 if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
28542 return; // no row below
28544 if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
28545 return; // nothing right?
28547 var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
28549 if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
28550 return; // right hand side is not same rowspan.
28552 this.node.innerHTML = this.node.innerHTML + rc.cell.innerHTML ;
28553 rc.cell.parentNode.removeChild(rc.cell);
28554 this.rowspan += rc.rowspan;
28555 this.node.setAttribute('rowspan', this.rowspan);
28560 if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
28563 var table = this.toTableArray();
28564 var cd = this.cellData;
28568 for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
28572 for(var c = cd.col; c < cd.col + cd.colspan; c++) {
28573 if (r == cd.row && c == cd.col) {
28574 this.node.removeAttribute('rowspan');
28575 this.node.removeAttribute('colspan');
28579 var ntd = this.node.cloneNode(); // which col/row should be 0..
28580 ntd.removeAttribute('id'); //
28581 //ntd.style.width = '';
28582 ntd.innerHTML = '';
28583 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1 };
28587 this.redrawAllCells(table);
28595 redrawAllCells: function(table)
28599 var tab = this.node.closest('tr').closest('table');
28600 var ctr = tab.rows[0].parentNode;
28601 Array.from(tab.rows).forEach(function(r, ri){
28603 Array.from(r.cells).forEach(function(ce, ci){
28604 ce.parentNode.removeChild(ce);
28606 r.parentNode.removeChild(r);
28608 for(var r = 0 ; r < table.length; r++) {
28609 var re = tab.rows[r];
28611 var re = tab.ownerDocument.createElement('tr');
28612 ctr.appendChild(re);
28613 for(var c = 0 ; c < table[r].length; c++) {
28614 if (table[r][c].cell === false) {
28618 re.appendChild(table[r][c].cell);
28620 table[r][c].cell = false;
28625 updateWidths : function(table)
28627 for(var r = 0 ; r < table.length; r++) {
28629 for(var c = 0 ; c < table[r].length; c++) {
28630 if (table[r][c].cell === false) {
28634 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
28635 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
28636 el.width = Math.floor(this.colWidths[c]) +'%';
28637 el.updateElement(el.node);
28639 table[r][c].cell = false; // done
28643 normalizeWidths : function(table)
28646 if (this.colWidths[0] === false) {
28647 var nw = 100.0 / this.colWidths.length;
28648 this.colWidths.forEach(function(w,i) {
28649 this.colWidths[i] = nw;
28654 var t = 0, missing = [];
28656 this.colWidths.forEach(function(w,i) {
28658 this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
28659 var add = this.colWidths[i];
28668 var nc = this.colWidths.length;
28669 if (missing.length) {
28670 var mult = (nc - missing.length) / (1.0 * nc);
28672 var ew = (100 -t) / (1.0 * missing.length);
28673 this.colWidths.forEach(function(w,i) {
28675 this.colWidths[i] = w * mult;
28679 this.colWidths[i] = ew;
28681 // have to make up numbers..
28684 // now we should have all the widths..
28689 shrinkColumn : function()
28691 var table = this.toTableArray();
28692 this.normalizeWidths(table);
28693 var col = this.cellData.col;
28694 var nw = this.colWidths[col] * 0.8;
28698 var otherAdd = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
28699 this.colWidths.forEach(function(w,i) {
28701 this.colWidths[i] = nw;
28704 this.colWidths[i] += otherAdd
28706 this.updateWidths(table);
28709 growColumn : function()
28711 var table = this.toTableArray();
28712 this.normalizeWidths(table);
28713 var col = this.cellData.col;
28714 var nw = this.colWidths[col] * 1.2;
28718 var otherSub = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
28719 this.colWidths.forEach(function(w,i) {
28721 this.colWidths[i] = nw;
28724 this.colWidths[i] -= otherSub
28726 this.updateWidths(table);
28729 deleteRow : function()
28731 // delete this rows 'tr'
28732 // if any of the cells in this row have a rowspan > 1 && row!= this row..
28733 // then reduce the rowspan.
28734 var table = this.toTableArray();
28735 // this.cellData.row;
28736 for (var i =0;i< table[this.cellData.row].length ; i++) {
28737 var c = table[this.cellData.row][i];
28738 if (c.row != this.cellData.row) {
28741 c.cell.setAttribute('rowspan', c.rowspan);
28744 if (c.rowspan > 1) {
28746 c.cell.setAttribute('rowspan', c.rowspan);
28749 table.splice(this.cellData.row,1);
28750 this.redrawAllCells(table);
28753 deleteColumn : function()
28755 var table = this.toTableArray();
28757 for (var i =0;i< table.length ; i++) {
28758 var c = table[i][this.cellData.col];
28759 if (c.col != this.cellData.col) {
28760 table[i][this.cellData.col].colspan--;
28761 } else if (c.colspan > 1) {
28763 c.cell.setAttribute('colspan', c.colspan);
28765 table[i].splice(this.cellData.col,1);
28768 this.redrawAllCells(table);
28776 //<script type="text/javascript">
28779 * Based Ext JS Library 1.1.1
28780 * Copyright(c) 2006-2007, Ext JS, LLC.
28786 * @class Roo.HtmlEditorCore
28787 * @extends Roo.Component
28788 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
28790 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
28793 Roo.HtmlEditorCore = function(config){
28796 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
28801 * @event initialize
28802 * Fires when the editor is fully initialized (including the iframe)
28803 * @param {Roo.HtmlEditorCore} this
28808 * Fires when the editor is first receives the focus. Any insertion must wait
28809 * until after this event.
28810 * @param {Roo.HtmlEditorCore} this
28814 * @event beforesync
28815 * Fires before the textarea is updated with content from the editor iframe. Return false
28816 * to cancel the sync.
28817 * @param {Roo.HtmlEditorCore} this
28818 * @param {String} html
28822 * @event beforepush
28823 * Fires before the iframe editor is updated with content from the textarea. Return false
28824 * to cancel the push.
28825 * @param {Roo.HtmlEditorCore} this
28826 * @param {String} html
28831 * Fires when the textarea is updated with content from the editor iframe.
28832 * @param {Roo.HtmlEditorCore} this
28833 * @param {String} html
28838 * Fires when the iframe editor is updated with content from the textarea.
28839 * @param {Roo.HtmlEditorCore} this
28840 * @param {String} html
28845 * @event editorevent
28846 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
28847 * @param {Roo.HtmlEditorCore} this
28854 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
28856 // defaults : white / black...
28857 this.applyBlacklists();
28864 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
28868 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
28874 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
28879 * @cfg {Number} height (in pixels)
28883 * @cfg {Number} width (in pixels)
28887 * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
28888 * if you are doing an email editor, this probably needs disabling, it's designed
28893 * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
28895 enableBlocks : true,
28897 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
28900 stylesheets: false,
28902 * @cfg {String} language default en - language of text (usefull for rtl languages)
28908 * @cfg {boolean} allowComments - default false - allow comments in HTML source
28909 * - by default they are stripped - if you are editing email you may need this.
28911 allowComments: false,
28915 // private properties
28916 validationEvent : false,
28918 initialized : false,
28920 sourceEditMode : false,
28921 onFocus : Roo.emptyFn,
28923 hideMode:'offsets',
28927 // blacklist + whitelisted elements..
28934 undoManager : false,
28936 * Protected method that will not generally be called directly. It
28937 * is called when the editor initializes the iframe with HTML contents. Override this method if you
28938 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
28940 getDocMarkup : function(){
28944 // inherit styels from page...??
28945 if (this.stylesheets === false) {
28947 Roo.get(document.head).select('style').each(function(node) {
28948 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
28951 Roo.get(document.head).select('link').each(function(node) {
28952 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
28955 } else if (!this.stylesheets.length) {
28957 st = '<style type="text/css">' +
28958 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
28961 for (var i in this.stylesheets) {
28962 if (typeof(this.stylesheets[i]) != 'string') {
28965 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
28970 st += '<style type="text/css">' +
28971 'IMG { cursor: pointer } ' +
28974 st += '<meta name="google" content="notranslate">';
28976 var cls = 'notranslate roo-htmleditor-body';
28978 if(this.bodyCls.length){
28979 cls += ' ' + this.bodyCls;
28982 return '<html class="notranslate" translate="no"><head>' + st +
28983 //<style type="text/css">' +
28984 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
28986 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
28990 onRender : function(ct, position)
28993 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
28994 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
28997 this.el.dom.style.border = '0 none';
28998 this.el.dom.setAttribute('tabIndex', -1);
28999 this.el.addClass('x-hidden hide');
29003 if(Roo.isIE){ // fix IE 1px bogus margin
29004 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
29008 this.frameId = Roo.id();
29012 var iframe = this.owner.wrap.createChild({
29014 cls: 'form-control', // bootstrap..
29016 name: this.frameId,
29017 frameBorder : 'no',
29018 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
29023 this.iframe = iframe.dom;
29025 this.assignDocWin();
29027 this.doc.designMode = 'on';
29030 this.doc.write(this.getDocMarkup());
29034 var task = { // must defer to wait for browser to be ready
29036 //console.log("run task?" + this.doc.readyState);
29037 this.assignDocWin();
29038 if(this.doc.body || this.doc.readyState == 'complete'){
29040 this.doc.designMode="on";
29045 Roo.TaskMgr.stop(task);
29046 this.initEditor.defer(10, this);
29053 Roo.TaskMgr.start(task);
29058 onResize : function(w, h)
29060 Roo.log('resize: ' +w + ',' + h );
29061 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
29065 if(typeof w == 'number'){
29067 this.iframe.style.width = w + 'px';
29069 if(typeof h == 'number'){
29071 this.iframe.style.height = h + 'px';
29073 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
29080 * Toggles the editor between standard and source edit mode.
29081 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
29083 toggleSourceEdit : function(sourceEditMode){
29085 this.sourceEditMode = sourceEditMode === true;
29087 if(this.sourceEditMode){
29089 Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']); //FIXME - what's the BS styles for these
29092 Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
29093 //this.iframe.className = '';
29096 //this.setSize(this.owner.wrap.getSize());
29097 //this.fireEvent('editmodechange', this, this.sourceEditMode);
29104 * Protected method that will not generally be called directly. If you need/want
29105 * custom HTML cleanup, this is the method you should override.
29106 * @param {String} html The HTML to be cleaned
29107 * return {String} The cleaned HTML
29109 cleanHtml : function(html)
29111 html = String(html);
29112 if(html.length > 5){
29113 if(Roo.isSafari){ // strip safari nonsense
29114 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
29117 if(html == ' '){
29124 * HTML Editor -> Textarea
29125 * Protected method that will not generally be called directly. Syncs the contents
29126 * of the editor iframe with the textarea.
29128 syncValue : function()
29130 //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
29131 if(this.initialized){
29133 if (this.undoManager) {
29134 this.undoManager.addEvent();
29138 var bd = (this.doc.body || this.doc.documentElement);
29141 var sel = this.win.getSelection();
29143 var div = document.createElement('div');
29144 div.innerHTML = bd.innerHTML;
29145 var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
29146 if (gtx.length > 0) {
29147 var rm = gtx.item(0).parentNode;
29148 rm.parentNode.removeChild(rm);
29152 if (this.enableBlocks) {
29153 new Roo.htmleditor.FilterBlock({ node : div });
29156 var tidy = new Roo.htmleditor.TidySerializer({
29159 var html = tidy.serialize(div);
29163 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
29164 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
29166 html = '<div style="'+m[0]+'">' + html + '</div>';
29169 html = this.cleanHtml(html);
29170 // fix up the special chars.. normaly like back quotes in word...
29171 // however we do not want to do this with chinese..
29172 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
29174 var cc = match.charCodeAt();
29176 // Get the character value, handling surrogate pairs
29177 if (match.length == 2) {
29178 // It's a surrogate pair, calculate the Unicode code point
29179 var high = match.charCodeAt(0) - 0xD800;
29180 var low = match.charCodeAt(1) - 0xDC00;
29181 cc = (high * 0x400) + low + 0x10000;
29183 (cc >= 0x4E00 && cc < 0xA000 ) ||
29184 (cc >= 0x3400 && cc < 0x4E00 ) ||
29185 (cc >= 0xf900 && cc < 0xfb00 )
29190 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
29191 return "&#" + cc + ";";
29198 if(this.owner.fireEvent('beforesync', this, html) !== false){
29199 this.el.dom.value = html;
29200 this.owner.fireEvent('sync', this, html);
29206 * TEXTAREA -> EDITABLE
29207 * Protected method that will not generally be called directly. Pushes the value of the textarea
29208 * into the iframe editor.
29210 pushValue : function()
29212 //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
29213 if(this.initialized){
29214 var v = this.el.dom.value.trim();
29217 if(this.owner.fireEvent('beforepush', this, v) !== false){
29218 var d = (this.doc.body || this.doc.documentElement);
29221 this.el.dom.value = d.innerHTML;
29222 this.owner.fireEvent('push', this, v);
29224 if (this.autoClean) {
29225 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
29226 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
29228 if (this.enableBlocks) {
29229 Roo.htmleditor.Block.initAll(this.doc.body);
29232 this.updateLanguage();
29234 var lc = this.doc.body.lastChild;
29235 if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
29236 // add an extra line at the end.
29237 this.doc.body.appendChild(this.doc.createElement('br'));
29245 deferFocus : function(){
29246 this.focus.defer(10, this);
29250 focus : function(){
29251 if(this.win && !this.sourceEditMode){
29258 assignDocWin: function()
29260 var iframe = this.iframe;
29263 this.doc = iframe.contentWindow.document;
29264 this.win = iframe.contentWindow;
29266 // if (!Roo.get(this.frameId)) {
29269 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
29270 // this.win = Roo.get(this.frameId).dom.contentWindow;
29272 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
29276 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
29277 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
29282 initEditor : function(){
29283 //console.log("INIT EDITOR");
29284 this.assignDocWin();
29288 this.doc.designMode="on";
29290 this.doc.write(this.getDocMarkup());
29293 var dbody = (this.doc.body || this.doc.documentElement);
29294 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
29295 // this copies styles from the containing element into thsi one..
29296 // not sure why we need all of this..
29297 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
29299 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
29300 //ss['background-attachment'] = 'fixed'; // w3c
29301 dbody.bgProperties = 'fixed'; // ie
29302 dbody.setAttribute("translate", "no");
29304 //Roo.DomHelper.applyStyles(dbody, ss);
29305 Roo.EventManager.on(this.doc, {
29307 'mouseup': this.onEditorEvent,
29308 'dblclick': this.onEditorEvent,
29309 'click': this.onEditorEvent,
29310 'keyup': this.onEditorEvent,
29315 Roo.EventManager.on(this.doc, {
29316 'paste': this.onPasteEvent,
29320 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
29323 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
29324 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
29326 this.initialized = true;
29329 // initialize special key events - enter
29330 new Roo.htmleditor.KeyEnter({core : this});
29334 this.owner.fireEvent('initialize', this);
29337 // this is to prevent a href clicks resulting in a redirect?
29339 onPasteEvent : function(e,v)
29341 // I think we better assume paste is going to be a dirty load of rubish from word..
29343 // even pasting into a 'email version' of this widget will have to clean up that mess.
29344 var cd = (e.browserEvent.clipboardData || window.clipboardData);
29346 // check what type of paste - if it's an image, then handle it differently.
29347 if (cd.files && cd.files.length > 0) {
29349 var urlAPI = (window.createObjectURL && window) ||
29350 (window.URL && URL.revokeObjectURL && URL) ||
29351 (window.webkitURL && webkitURL);
29353 var url = urlAPI.createObjectURL( cd.files[0]);
29354 this.insertAtCursor('<img src=" + url + ">');
29357 if (cd.types.indexOf('text/html') < 0 ) {
29361 var html = cd.getData('text/html'); // clipboard event
29362 if (cd.types.indexOf('text/rtf') > -1) {
29363 var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
29364 images = parser.doc ? parser.doc.getElementsByType('pict') : [];
29369 images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
29370 .map(function(g) { return g.toDataURL(); })
29371 .filter(function(g) { return g != 'about:blank'; });
29374 html = this.cleanWordChars(html);
29376 var d = (new DOMParser().parseFromString(html, 'text/html')).body;
29379 var sn = this.getParentElement();
29380 // check if d contains a table, and prevent nesting??
29381 //Roo.log(d.getElementsByTagName('table'));
29383 //Roo.log(sn.closest('table'));
29384 if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
29385 e.preventDefault();
29386 this.insertAtCursor("You can not nest tables");
29387 //Roo.log("prevent?"); // fixme -
29391 if (images.length > 0) {
29392 Roo.each(d.getElementsByTagName('img'), function(img, i) {
29393 img.setAttribute('src', images[i]);
29396 if (this.autoClean) {
29397 new Roo.htmleditor.FilterWord({ node : d });
29399 new Roo.htmleditor.FilterStyleToTag({ node : d });
29400 new Roo.htmleditor.FilterAttributes({
29402 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width'],
29403 attrib_clean : ['href', 'src' ]
29405 new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
29406 // should be fonts..
29407 new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', 'O:P' ]} );
29408 new Roo.htmleditor.FilterParagraph({ node : d });
29409 new Roo.htmleditor.FilterSpan({ node : d });
29410 new Roo.htmleditor.FilterLongBr({ node : d });
29411 new Roo.htmleditor.FilterComment({ node : d });
29415 if (this.enableBlocks) {
29417 Array.from(d.getElementsByTagName('img')).forEach(function(img) {
29418 if (img.closest('figure')) { // assume!! that it's aready
29421 var fig = new Roo.htmleditor.BlockFigure({
29422 image_src : img.src
29424 fig.updateElement(img); // replace it..
29430 this.insertAtCursor(d.innerHTML.replace(/ /g,' '));
29431 if (this.enableBlocks) {
29432 Roo.htmleditor.Block.initAll(this.doc.body);
29436 e.preventDefault();
29438 // default behaveiour should be our local cleanup paste? (optional?)
29439 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
29440 //this.owner.fireEvent('paste', e, v);
29443 onDestroy : function(){
29449 //for (var i =0; i < this.toolbars.length;i++) {
29450 // // fixme - ask toolbars for heights?
29451 // this.toolbars[i].onDestroy();
29454 //this.wrap.dom.innerHTML = '';
29455 //this.wrap.remove();
29460 onFirstFocus : function(){
29462 this.assignDocWin();
29463 this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
29465 this.activated = true;
29468 if(Roo.isGecko){ // prevent silly gecko errors
29470 var s = this.win.getSelection();
29471 if(!s.focusNode || s.focusNode.nodeType != 3){
29472 var r = s.getRangeAt(0);
29473 r.selectNodeContents((this.doc.body || this.doc.documentElement));
29478 this.execCmd('useCSS', true);
29479 this.execCmd('styleWithCSS', false);
29482 this.owner.fireEvent('activate', this);
29486 adjustFont: function(btn){
29487 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
29488 //if(Roo.isSafari){ // safari
29491 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
29492 if(Roo.isSafari){ // safari
29493 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
29494 v = (v < 10) ? 10 : v;
29495 v = (v > 48) ? 48 : v;
29496 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
29501 v = Math.max(1, v+adjust);
29503 this.execCmd('FontSize', v );
29506 onEditorEvent : function(e)
29510 if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
29511 return; // we do not handle this.. (undo manager does..)
29513 // in theory this detects if the last element is not a br, then we try and do that.
29514 // its so clicking in space at bottom triggers adding a br and moving the cursor.
29516 e.target.nodeName == 'BODY' &&
29517 e.type == "mouseup" &&
29518 this.doc.body.lastChild
29520 var lc = this.doc.body.lastChild;
29521 // gtx-trans is google translate plugin adding crap.
29522 while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
29523 lc = lc.previousSibling;
29525 if (lc.nodeType == 1 && lc.nodeName != 'BR') {
29526 // if last element is <BR> - then dont do anything.
29528 var ns = this.doc.createElement('br');
29529 this.doc.body.appendChild(ns);
29530 range = this.doc.createRange();
29531 range.setStartAfter(ns);
29532 range.collapse(true);
29533 var sel = this.win.getSelection();
29534 sel.removeAllRanges();
29535 sel.addRange(range);
29541 this.fireEditorEvent(e);
29542 // this.updateToolbar();
29543 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
29546 fireEditorEvent: function(e)
29548 this.owner.fireEvent('editorevent', this, e);
29551 insertTag : function(tg)
29553 // could be a bit smarter... -> wrap the current selected tRoo..
29554 if (tg.toLowerCase() == 'span' ||
29555 tg.toLowerCase() == 'code' ||
29556 tg.toLowerCase() == 'sup' ||
29557 tg.toLowerCase() == 'sub'
29560 range = this.createRange(this.getSelection());
29561 var wrappingNode = this.doc.createElement(tg.toLowerCase());
29562 wrappingNode.appendChild(range.extractContents());
29563 range.insertNode(wrappingNode);
29570 this.execCmd("formatblock", tg);
29571 this.undoManager.addEvent();
29574 insertText : function(txt)
29578 var range = this.createRange();
29579 range.deleteContents();
29580 //alert(Sender.getAttribute('label'));
29582 range.insertNode(this.doc.createTextNode(txt));
29583 this.undoManager.addEvent();
29589 * Executes a Midas editor command on the editor document and performs necessary focus and
29590 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
29591 * @param {String} cmd The Midas command
29592 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
29594 relayCmd : function(cmd, value)
29598 case 'justifyleft':
29599 case 'justifyright':
29600 case 'justifycenter':
29601 // if we are in a cell, then we will adjust the
29602 var n = this.getParentElement();
29603 var td = n.closest('td');
29605 var bl = Roo.htmleditor.Block.factory(td);
29606 bl.textAlign = cmd.replace('justify','');
29607 bl.updateElement();
29608 this.owner.fireEvent('editorevent', this);
29611 this.execCmd('styleWithCSS', true); //
29615 // if there is no selection, then we insert, and set the curson inside it..
29616 this.execCmd('styleWithCSS', false);
29626 this.execCmd(cmd, value);
29627 this.owner.fireEvent('editorevent', this);
29628 //this.updateToolbar();
29629 this.owner.deferFocus();
29633 * Executes a Midas editor command directly on the editor document.
29634 * For visual commands, you should use {@link #relayCmd} instead.
29635 * <b>This should only be called after the editor is initialized.</b>
29636 * @param {String} cmd The Midas command
29637 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
29639 execCmd : function(cmd, value){
29640 this.doc.execCommand(cmd, false, value === undefined ? null : value);
29647 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
29649 * @param {String} text | dom node..
29651 insertAtCursor : function(text)
29654 if(!this.activated){
29658 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
29662 // from jquery ui (MIT licenced)
29664 var win = this.win;
29666 if (win.getSelection && win.getSelection().getRangeAt) {
29668 // delete the existing?
29670 this.createRange(this.getSelection()).deleteContents();
29671 range = win.getSelection().getRangeAt(0);
29672 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
29673 range.insertNode(node);
29674 range = range.cloneRange();
29675 range.collapse(false);
29677 win.getSelection().removeAllRanges();
29678 win.getSelection().addRange(range);
29682 } else if (win.document.selection && win.document.selection.createRange) {
29683 // no firefox support
29684 var txt = typeof(text) == 'string' ? text : text.outerHTML;
29685 win.document.selection.createRange().pasteHTML(txt);
29688 // no firefox support
29689 var txt = typeof(text) == 'string' ? text : text.outerHTML;
29690 this.execCmd('InsertHTML', txt);
29698 mozKeyPress : function(e){
29700 var c = e.getCharCode(), cmd;
29703 c = String.fromCharCode(c).toLowerCase();
29717 // this.cleanUpPaste.defer(100, this);
29723 this.relayCmd(cmd);
29724 //this.win.focus();
29725 //this.execCmd(cmd);
29726 //this.deferFocus();
29727 e.preventDefault();
29735 fixKeys : function(){ // load time branching for fastest keydown performance
29739 return function(e){
29740 var k = e.getKey(), r;
29743 r = this.doc.selection.createRange();
29746 r.pasteHTML('    ');
29751 /// this is handled by Roo.htmleditor.KeyEnter
29754 r = this.doc.selection.createRange();
29756 var target = r.parentElement();
29757 if(!target || target.tagName.toLowerCase() != 'li'){
29759 r.pasteHTML('<br/>');
29766 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
29767 // this.cleanUpPaste.defer(100, this);
29773 }else if(Roo.isOpera){
29774 return function(e){
29775 var k = e.getKey();
29779 this.execCmd('InsertHTML','    ');
29783 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
29784 // this.cleanUpPaste.defer(100, this);
29789 }else if(Roo.isSafari){
29790 return function(e){
29791 var k = e.getKey();
29795 this.execCmd('InsertText','\t');
29799 this.mozKeyPress(e);
29801 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
29802 // this.cleanUpPaste.defer(100, this);
29810 getAllAncestors: function()
29812 var p = this.getSelectedNode();
29815 a.push(p); // push blank onto stack..
29816 p = this.getParentElement();
29820 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
29824 a.push(this.doc.body);
29828 lastSelNode : false,
29831 getSelection : function()
29833 this.assignDocWin();
29834 return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
29837 * Select a dom node
29838 * @param {DomElement} node the node to select
29840 selectNode : function(node, collapse)
29842 var nodeRange = node.ownerDocument.createRange();
29844 nodeRange.selectNode(node);
29846 nodeRange.selectNodeContents(node);
29848 if (collapse === true) {
29849 nodeRange.collapse(true);
29852 var s = this.win.getSelection();
29853 s.removeAllRanges();
29854 s.addRange(nodeRange);
29857 getSelectedNode: function()
29859 // this may only work on Gecko!!!
29861 // should we cache this!!!!
29865 var range = this.createRange(this.getSelection()).cloneRange();
29868 var parent = range.parentElement();
29870 var testRange = range.duplicate();
29871 testRange.moveToElementText(parent);
29872 if (testRange.inRange(range)) {
29875 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
29878 parent = parent.parentElement;
29883 // is ancestor a text element.
29884 var ac = range.commonAncestorContainer;
29885 if (ac.nodeType == 3) {
29886 ac = ac.parentNode;
29889 var ar = ac.childNodes;
29892 var other_nodes = [];
29893 var has_other_nodes = false;
29894 for (var i=0;i<ar.length;i++) {
29895 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
29898 // fullly contained node.
29900 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
29905 // probably selected..
29906 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
29907 other_nodes.push(ar[i]);
29911 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
29916 has_other_nodes = true;
29918 if (!nodes.length && other_nodes.length) {
29919 nodes= other_nodes;
29921 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
29929 createRange: function(sel)
29931 // this has strange effects when using with
29932 // top toolbar - not sure if it's a great idea.
29933 //this.editor.contentWindow.focus();
29934 if (typeof sel != "undefined") {
29936 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
29938 return this.doc.createRange();
29941 return this.doc.createRange();
29944 getParentElement: function()
29947 this.assignDocWin();
29948 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
29950 var range = this.createRange(sel);
29953 var p = range.commonAncestorContainer;
29954 while (p.nodeType == 3) { // text node
29965 * Range intersection.. the hard stuff...
29969 * [ -- selected range --- ]
29973 * if end is before start or hits it. fail.
29974 * if start is after end or hits it fail.
29976 * if either hits (but other is outside. - then it's not
29982 // @see http://www.thismuchiknow.co.uk/?p=64.
29983 rangeIntersectsNode : function(range, node)
29985 var nodeRange = node.ownerDocument.createRange();
29987 nodeRange.selectNode(node);
29989 nodeRange.selectNodeContents(node);
29992 var rangeStartRange = range.cloneRange();
29993 rangeStartRange.collapse(true);
29995 var rangeEndRange = range.cloneRange();
29996 rangeEndRange.collapse(false);
29998 var nodeStartRange = nodeRange.cloneRange();
29999 nodeStartRange.collapse(true);
30001 var nodeEndRange = nodeRange.cloneRange();
30002 nodeEndRange.collapse(false);
30004 return rangeStartRange.compareBoundaryPoints(
30005 Range.START_TO_START, nodeEndRange) == -1 &&
30006 rangeEndRange.compareBoundaryPoints(
30007 Range.START_TO_START, nodeStartRange) == 1;
30011 rangeCompareNode : function(range, node)
30013 var nodeRange = node.ownerDocument.createRange();
30015 nodeRange.selectNode(node);
30017 nodeRange.selectNodeContents(node);
30021 range.collapse(true);
30023 nodeRange.collapse(true);
30025 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
30026 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
30028 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
30030 var nodeIsBefore = ss == 1;
30031 var nodeIsAfter = ee == -1;
30033 if (nodeIsBefore && nodeIsAfter) {
30036 if (!nodeIsBefore && nodeIsAfter) {
30037 return 1; //right trailed.
30040 if (nodeIsBefore && !nodeIsAfter) {
30041 return 2; // left trailed.
30047 cleanWordChars : function(input) {// change the chars to hex code
30050 [ 8211, "–" ],
30051 [ 8212, "—" ],
30059 var output = input;
30060 Roo.each(swapCodes, function(sw) {
30061 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
30063 output = output.replace(swapper, sw[1]);
30073 cleanUpChild : function (node)
30076 new Roo.htmleditor.FilterComment({node : node});
30077 new Roo.htmleditor.FilterAttributes({
30079 attrib_black : this.ablack,
30080 attrib_clean : this.aclean,
30081 style_white : this.cwhite,
30082 style_black : this.cblack
30084 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
30085 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
30091 * Clean up MS wordisms...
30092 * @deprecated - use filter directly
30094 cleanWord : function(node)
30096 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
30103 * @deprecated - use filters
30105 cleanTableWidths : function(node)
30107 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
30114 applyBlacklists : function()
30116 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
30117 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
30119 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
30120 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
30121 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
30125 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
30126 if (b.indexOf(tag) > -1) {
30129 this.white.push(tag);
30133 Roo.each(w, function(tag) {
30134 if (b.indexOf(tag) > -1) {
30137 if (this.white.indexOf(tag) > -1) {
30140 this.white.push(tag);
30145 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
30146 if (w.indexOf(tag) > -1) {
30149 this.black.push(tag);
30153 Roo.each(b, function(tag) {
30154 if (w.indexOf(tag) > -1) {
30157 if (this.black.indexOf(tag) > -1) {
30160 this.black.push(tag);
30165 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
30166 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
30170 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
30171 if (b.indexOf(tag) > -1) {
30174 this.cwhite.push(tag);
30178 Roo.each(w, function(tag) {
30179 if (b.indexOf(tag) > -1) {
30182 if (this.cwhite.indexOf(tag) > -1) {
30185 this.cwhite.push(tag);
30190 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
30191 if (w.indexOf(tag) > -1) {
30194 this.cblack.push(tag);
30198 Roo.each(b, function(tag) {
30199 if (w.indexOf(tag) > -1) {
30202 if (this.cblack.indexOf(tag) > -1) {
30205 this.cblack.push(tag);
30210 setStylesheets : function(stylesheets)
30212 if(typeof(stylesheets) == 'string'){
30213 Roo.get(this.iframe.contentDocument.head).createChild({
30215 rel : 'stylesheet',
30224 Roo.each(stylesheets, function(s) {
30229 Roo.get(_this.iframe.contentDocument.head).createChild({
30231 rel : 'stylesheet',
30241 updateLanguage : function()
30243 if (!this.iframe || !this.iframe.contentDocument) {
30246 Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
30250 removeStylesheets : function()
30254 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
30259 setStyle : function(style)
30261 Roo.get(this.iframe.contentDocument.head).createChild({
30270 // hide stuff that is not compatible
30284 * @event specialkey
30288 * @cfg {String} fieldClass @hide
30291 * @cfg {String} focusClass @hide
30294 * @cfg {String} autoCreate @hide
30297 * @cfg {String} inputType @hide
30300 * @cfg {String} invalidClass @hide
30303 * @cfg {String} invalidText @hide
30306 * @cfg {String} msgFx @hide
30309 * @cfg {String} validateOnBlur @hide
30313 Roo.HtmlEditorCore.white = [
30314 'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
30316 'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD', 'DIR', 'DIV',
30317 'DL', 'DT', 'H1', 'H2', 'H3', 'H4',
30318 'H5', 'H6', 'HR', 'ISINDEX', 'LISTING', 'MARQUEE',
30319 'MENU', 'MULTICOL', 'OL', 'P', 'PLAINTEXT', 'PRE',
30320 'TABLE', 'UL', 'XMP',
30322 'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH',
30325 'DIR', 'MENU', 'OL', 'UL', 'DL',
30331 Roo.HtmlEditorCore.black = [
30332 // 'embed', 'object', // enable - backend responsiblity to clean thiese
30334 'BASE', 'BASEFONT', 'BGSOUND', 'BLINK', 'BODY',
30335 'FRAME', 'FRAMESET', 'HEAD', 'HTML', 'ILAYER',
30336 'IFRAME', 'LAYER', 'LINK', 'META', 'OBJECT',
30337 'SCRIPT', 'STYLE' ,'TITLE', 'XML',
30338 //'FONT' // CLEAN LATER..
30339 'COLGROUP', 'COL' // messy tables.
30343 Roo.HtmlEditorCore.clean = [ // ?? needed???
30344 'SCRIPT', 'STYLE', 'TITLE', 'XML'
30346 Roo.HtmlEditorCore.tag_remove = [
30351 Roo.HtmlEditorCore.ablack = [
30355 Roo.HtmlEditorCore.aclean = [
30356 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
30360 Roo.HtmlEditorCore.pwhite= [
30361 'http', 'https', 'mailto'
30364 // white listed style attributes.
30365 Roo.HtmlEditorCore.cwhite= [
30366 // 'text-align', /// default is to allow most things..
30372 // black listed style attributes.
30373 Roo.HtmlEditorCore.cblack= [
30374 // 'font-size' -- this can be set by the project
30388 * @class Roo.bootstrap.form.HtmlEditor
30389 * @extends Roo.bootstrap.form.TextArea
30390 * Bootstrap HtmlEditor class
30393 * Create a new HtmlEditor
30394 * @param {Object} config The config object
30397 Roo.bootstrap.form.HtmlEditor = function(config){
30398 Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
30399 if (!this.toolbars) {
30400 this.toolbars = [];
30403 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
30406 * @event initialize
30407 * Fires when the editor is fully initialized (including the iframe)
30408 * @param {HtmlEditor} this
30413 * Fires when the editor is first receives the focus. Any insertion must wait
30414 * until after this event.
30415 * @param {HtmlEditor} this
30419 * @event beforesync
30420 * Fires before the textarea is updated with content from the editor iframe. Return false
30421 * to cancel the sync.
30422 * @param {HtmlEditor} this
30423 * @param {String} html
30427 * @event beforepush
30428 * Fires before the iframe editor is updated with content from the textarea. Return false
30429 * to cancel the push.
30430 * @param {HtmlEditor} this
30431 * @param {String} html
30436 * Fires when the textarea is updated with content from the editor iframe.
30437 * @param {HtmlEditor} this
30438 * @param {String} html
30443 * Fires when the iframe editor is updated with content from the textarea.
30444 * @param {HtmlEditor} this
30445 * @param {String} html
30449 * @event editmodechange
30450 * Fires when the editor switches edit modes
30451 * @param {HtmlEditor} this
30452 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
30454 editmodechange: true,
30456 * @event editorevent
30457 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30458 * @param {HtmlEditor} this
30462 * @event firstfocus
30463 * Fires when on first focus - needed by toolbars..
30464 * @param {HtmlEditor} this
30469 * Auto save the htmlEditor value as a file into Events
30470 * @param {HtmlEditor} this
30474 * @event savedpreview
30475 * preview the saved version of htmlEditor
30476 * @param {HtmlEditor} this
30483 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea, {
30487 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
30492 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
30497 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
30502 * @cfg {Number} height (in pixels)
30506 * @cfg {Number} width (in pixels)
30511 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30514 stylesheets: false,
30519 // private properties
30520 validationEvent : false,
30522 initialized : false,
30525 onFocus : Roo.emptyFn,
30527 hideMode:'offsets',
30529 tbContainer : false,
30533 toolbarContainer :function() {
30534 return this.wrap.select('.x-html-editor-tb',true).first();
30538 * Protected method that will not generally be called directly. It
30539 * is called when the editor creates its toolbar. Override this method if you need to
30540 * add custom toolbar buttons.
30541 * @param {HtmlEditor} editor
30543 createToolbar : function(){
30544 Roo.log('renewing');
30545 Roo.log("create toolbars");
30547 this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
30548 this.toolbars[0].render(this.toolbarContainer());
30552 // if (!editor.toolbars || !editor.toolbars.length) {
30553 // editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
30556 // for (var i =0 ; i < editor.toolbars.length;i++) {
30557 // editor.toolbars[i] = Roo.factory(
30558 // typeof(editor.toolbars[i]) == 'string' ?
30559 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
30560 // Roo.bootstrap.form.HtmlEditor);
30561 // editor.toolbars[i].init(editor);
30567 onRender : function(ct, position)
30569 // Roo.log("Call onRender: " + this.xtype);
30571 Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
30573 this.wrap = this.inputEl().wrap({
30574 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
30577 this.editorcore.onRender(ct, position);
30579 if (this.resizable) {
30580 this.resizeEl = new Roo.Resizable(this.wrap, {
30584 minHeight : this.height,
30585 height: this.height,
30586 handles : this.resizable,
30589 resize : function(r, w, h) {
30590 _t.onResize(w,h); // -something
30596 this.createToolbar(this);
30599 if(!this.width && this.resizable){
30600 this.setSize(this.wrap.getSize());
30602 if (this.resizeEl) {
30603 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
30604 // should trigger onReize..
30610 onResize : function(w, h)
30612 Roo.log('resize: ' +w + ',' + h );
30613 Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
30617 if(this.inputEl() ){
30618 if(typeof w == 'number'){
30619 var aw = w - this.wrap.getFrameWidth('lr');
30620 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
30623 if(typeof h == 'number'){
30624 var tbh = -11; // fixme it needs to tool bar size!
30625 for (var i =0; i < this.toolbars.length;i++) {
30626 // fixme - ask toolbars for heights?
30627 tbh += this.toolbars[i].el.getHeight();
30628 //if (this.toolbars[i].footer) {
30629 // tbh += this.toolbars[i].footer.el.getHeight();
30637 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
30638 ah -= 5; // knock a few pixes off for look..
30639 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
30643 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
30644 this.editorcore.onResize(ew,eh);
30649 * Toggles the editor between standard and source edit mode.
30650 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
30652 toggleSourceEdit : function(sourceEditMode)
30654 this.editorcore.toggleSourceEdit(sourceEditMode);
30656 if(this.editorcore.sourceEditMode){
30657 Roo.log('editor - showing textarea');
30660 // Roo.log(this.syncValue());
30662 this.inputEl().removeClass(['hide', 'x-hidden']);
30663 this.inputEl().dom.removeAttribute('tabIndex');
30664 this.inputEl().focus();
30666 Roo.log('editor - hiding textarea');
30668 // Roo.log(this.pushValue());
30671 this.inputEl().addClass(['hide', 'x-hidden']);
30672 this.inputEl().dom.setAttribute('tabIndex', -1);
30673 //this.deferFocus();
30676 if(this.resizable){
30677 this.setSize(this.wrap.getSize());
30680 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
30683 // private (for BoxComponent)
30684 adjustSize : Roo.BoxComponent.prototype.adjustSize,
30686 // private (for BoxComponent)
30687 getResizeEl : function(){
30691 // private (for BoxComponent)
30692 getPositionEl : function(){
30697 initEvents : function(){
30698 this.originalValue = this.getValue();
30702 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
30705 // markInvalid : Roo.emptyFn,
30707 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
30710 // clearInvalid : Roo.emptyFn,
30712 setValue : function(v){
30713 Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
30714 this.editorcore.pushValue();
30719 deferFocus : function(){
30720 this.focus.defer(10, this);
30724 focus : function(){
30725 this.editorcore.focus();
30731 onDestroy : function(){
30737 for (var i =0; i < this.toolbars.length;i++) {
30738 // fixme - ask toolbars for heights?
30739 this.toolbars[i].onDestroy();
30742 this.wrap.dom.innerHTML = '';
30743 this.wrap.remove();
30748 onFirstFocus : function(){
30749 //Roo.log("onFirstFocus");
30750 this.editorcore.onFirstFocus();
30751 for (var i =0; i < this.toolbars.length;i++) {
30752 this.toolbars[i].onFirstFocus();
30758 syncValue : function()
30760 this.editorcore.syncValue();
30763 pushValue : function()
30765 this.editorcore.pushValue();
30769 // hide stuff that is not compatible
30783 * @event specialkey
30787 * @cfg {String} fieldClass @hide
30790 * @cfg {String} focusClass @hide
30793 * @cfg {String} autoCreate @hide
30796 * @cfg {String} inputType @hide
30800 * @cfg {String} invalidText @hide
30803 * @cfg {String} msgFx @hide
30806 * @cfg {String} validateOnBlur @hide
30815 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
30817 * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
30818 * @parent Roo.bootstrap.form.HtmlEditor
30819 * @extends Roo.bootstrap.nav.Simplebar
30825 new Roo.bootstrap.form.HtmlEditor({
30828 new Roo.bootstrap.form.HtmlEditorToolbarStandard({
30829 disable : { fonts: 1 , format: 1, ..., ... , ...],
30835 * @cfg {Object} disable List of elements to disable..
30836 * @cfg {Array} btns List of additional buttons.
30840 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
30843 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
30846 Roo.apply(this, config);
30848 // default disabled, based on 'good practice'..
30849 this.disable = this.disable || {};
30850 Roo.applyIf(this.disable, {
30853 specialElements : true
30855 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
30857 this.editor = config.editor;
30858 this.editorcore = config.editor.editorcore;
30860 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
30862 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
30863 // dont call parent... till later.
30865 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar, {
30870 editorcore : false,
30875 "h1","h2","h3","h4","h5","h6",
30877 "abbr", "acronym", "address", "cite", "samp", "var",
30881 onRender : function(ct, position)
30883 // Roo.log("Call onRender: " + this.xtype);
30885 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
30887 this.el.dom.style.marginBottom = '0';
30889 var editorcore = this.editorcore;
30890 var editor= this.editor;
30893 var btn = function(id,cmd , toggle, handler, html){
30895 var event = toggle ? 'toggle' : 'click';
30900 xns: Roo.bootstrap,
30904 enableToggle:toggle !== false,
30906 pressed : toggle ? false : null,
30909 a.listeners[toggle ? 'toggle' : 'click'] = function() {
30910 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
30916 // var cb_box = function...
30921 xns: Roo.bootstrap,
30926 xns: Roo.bootstrap,
30930 Roo.each(this.formats, function(f) {
30931 style.menu.items.push({
30933 xns: Roo.bootstrap,
30934 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
30939 editorcore.insertTag(this.tagname);
30946 children.push(style);
30948 btn('bold',false,true);
30949 btn('italic',false,true);
30950 btn('align-left', 'justifyleft',true);
30951 btn('align-center', 'justifycenter',true);
30952 btn('align-right' , 'justifyright',true);
30953 btn('link', false, false, function(btn) {
30954 //Roo.log("create link?");
30955 var url = prompt(this.createLinkText, this.defaultLinkValue);
30956 if(url && url != 'http:/'+'/'){
30957 this.editorcore.relayCmd('createlink', url);
30960 btn('list','insertunorderedlist',true);
30961 btn('pencil', false,true, function(btn){
30963 this.toggleSourceEdit(btn.pressed);
30966 if (this.editor.btns.length > 0) {
30967 for (var i = 0; i<this.editor.btns.length; i++) {
30968 children.push(this.editor.btns[i]);
30976 xns: Roo.bootstrap,
30981 xns: Roo.bootstrap,
30986 cog.menu.items.push({
30988 xns: Roo.bootstrap,
30989 html : Clean styles,
30994 editorcore.insertTag(this.tagname);
31003 this.xtype = 'NavSimplebar';
31005 for(var i=0;i< children.length;i++) {
31007 this.buttons.add(this.addxtypeChild(children[i]));
31011 editor.on('editorevent', this.updateToolbar, this);
31013 onBtnClick : function(id)
31015 this.editorcore.relayCmd(id);
31016 this.editorcore.focus();
31020 * Protected method that will not generally be called directly. It triggers
31021 * a toolbar update by reading the markup state of the current selection in the editor.
31023 updateToolbar: function(){
31025 if(!this.editorcore.activated){
31026 this.editor.onFirstFocus(); // is this neeed?
31030 var btns = this.buttons;
31031 var doc = this.editorcore.doc;
31032 btns.get('bold').setActive(doc.queryCommandState('bold'));
31033 btns.get('italic').setActive(doc.queryCommandState('italic'));
31034 //btns.get('underline').setActive(doc.queryCommandState('underline'));
31036 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
31037 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
31038 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
31040 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
31041 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
31044 var ans = this.editorcore.getAllAncestors();
31045 if (this.formatCombo) {
31048 var store = this.formatCombo.store;
31049 this.formatCombo.setValue("");
31050 for (var i =0; i < ans.length;i++) {
31051 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
31053 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
31061 // hides menus... - so this cant be on a menu...
31062 Roo.bootstrap.MenuMgr.hideAll();
31064 Roo.bootstrap.menu.Manager.hideAll();
31065 //this.editorsyncValue();
31067 onFirstFocus: function() {
31068 this.buttons.each(function(item){
31072 toggleSourceEdit : function(sourceEditMode){
31075 if(sourceEditMode){
31076 Roo.log("disabling buttons");
31077 this.buttons.each( function(item){
31078 if(item.cmd != 'pencil'){
31084 Roo.log("enabling buttons");
31085 if(this.editorcore.initialized){
31086 this.buttons.each( function(item){
31092 Roo.log("calling toggole on editor");
31093 // tell the editor that it's been pressed..
31094 this.editor.toggleSourceEdit(sourceEditMode);
31108 * @class Roo.bootstrap.form.Markdown
31109 * @extends Roo.bootstrap.form.TextArea
31110 * Bootstrap Showdown editable area
31111 * @cfg {string} content
31114 * Create a new Showdown
31117 Roo.bootstrap.form.Markdown = function(config){
31118 Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
31122 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea, {
31126 initEvents : function()
31129 Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
31130 this.markdownEl = this.el.createChild({
31131 cls : 'roo-markdown-area'
31133 this.inputEl().addClass('d-none');
31134 if (this.getValue() == '') {
31135 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
31138 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
31140 this.markdownEl.on('click', this.toggleTextEdit, this);
31141 this.on('blur', this.toggleTextEdit, this);
31142 this.on('specialkey', this.resizeTextArea, this);
31145 toggleTextEdit : function()
31147 var sh = this.markdownEl.getHeight();
31148 this.inputEl().addClass('d-none');
31149 this.markdownEl.addClass('d-none');
31150 if (!this.editing) {
31152 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
31153 this.inputEl().removeClass('d-none');
31154 this.inputEl().focus();
31155 this.editing = true;
31158 // show showdown...
31159 this.updateMarkdown();
31160 this.markdownEl.removeClass('d-none');
31161 this.editing = false;
31164 updateMarkdown : function()
31166 if (this.getValue() == '') {
31167 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
31171 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
31174 resizeTextArea: function () {
31177 Roo.log([sh, this.getValue().split("\n").length * 30]);
31178 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
31180 setValue : function(val)
31182 Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
31183 if (!this.editing) {
31184 this.updateMarkdown();
31190 if (!this.editing) {
31191 this.toggleTextEdit();
31199 * Ext JS Library 1.1.1
31200 * Copyright(c) 2006-2007, Ext JS, LLC.
31202 * Originally Released Under LGPL - original licence link has changed is not relivant.
31205 * <script type="text/javascript">
31209 * @class Roo.bootstrap.PagingToolbar
31210 * @extends Roo.bootstrap.nav.Simplebar
31211 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31213 * Create a new PagingToolbar
31214 * @param {Object} config The config object
31215 * @param {Roo.data.Store} store
31217 Roo.bootstrap.PagingToolbar = function(config)
31219 // old args format still supported... - xtype is prefered..
31220 // created from xtype...
31222 this.ds = config.dataSource;
31224 if (config.store && !this.ds) {
31225 this.store= Roo.factory(config.store, Roo.data);
31226 this.ds = this.store;
31227 this.ds.xmodule = this.xmodule || false;
31230 this.toolbarItems = [];
31231 if (config.items) {
31232 this.toolbarItems = config.items;
31235 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
31240 this.bind(this.ds);
31243 if (Roo.bootstrap.version == 4) {
31244 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
31246 this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
31251 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
31253 * @cfg {Roo.bootstrap.Button} buttons[]
31254 * Buttons for the toolbar
31257 * @cfg {Roo.data.Store} store
31258 * The underlying data store providing the paged data
31261 * @cfg {String/HTMLElement/Element} container
31262 * container The id or element that will contain the toolbar
31265 * @cfg {Boolean} displayInfo
31266 * True to display the displayMsg (defaults to false)
31269 * @cfg {Number} pageSize
31270 * The number of records to display per page (defaults to 20)
31274 * @cfg {String} displayMsg
31275 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
31277 displayMsg : 'Displaying {0} - {1} of {2}',
31279 * @cfg {String} emptyMsg
31280 * The message to display when no records are found (defaults to "No data to display")
31282 emptyMsg : 'No data to display',
31284 * Customizable piece of the default paging text (defaults to "Page")
31287 beforePageText : "Page",
31289 * Customizable piece of the default paging text (defaults to "of %0")
31292 afterPageText : "of {0}",
31294 * Customizable piece of the default paging text (defaults to "First Page")
31297 firstText : "First Page",
31299 * Customizable piece of the default paging text (defaults to "Previous Page")
31302 prevText : "Previous Page",
31304 * Customizable piece of the default paging text (defaults to "Next Page")
31307 nextText : "Next Page",
31309 * Customizable piece of the default paging text (defaults to "Last Page")
31312 lastText : "Last Page",
31314 * Customizable piece of the default paging text (defaults to "Refresh")
31317 refreshText : "Refresh",
31321 onRender : function(ct, position)
31323 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
31324 this.navgroup.parentId = this.id;
31325 this.navgroup.onRender(this.el, null);
31326 // add the buttons to the navgroup
31328 if(this.displayInfo){
31329 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
31330 this.displayEl = this.el.select('.x-paging-info', true).first();
31331 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
31332 // this.displayEl = navel.el.select('span',true).first();
31338 Roo.each(_this.buttons, function(e){ // this might need to use render????
31339 Roo.factory(e).render(_this.el);
31343 Roo.each(_this.toolbarItems, function(e) {
31344 _this.navgroup.addItem(e);
31348 this.first = this.navgroup.addItem({
31349 tooltip: this.firstText,
31350 cls: "prev btn-outline-secondary",
31351 html : ' <i class="fa fa-step-backward"></i>',
31353 preventDefault: true,
31354 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
31357 this.prev = this.navgroup.addItem({
31358 tooltip: this.prevText,
31359 cls: "prev btn-outline-secondary",
31360 html : ' <i class="fa fa-backward"></i>',
31362 preventDefault: true,
31363 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
31365 //this.addSeparator();
31368 var field = this.navgroup.addItem( {
31370 cls : 'x-paging-position btn-outline-secondary',
31372 html : this.beforePageText +
31373 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
31374 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
31377 this.field = field.el.select('input', true).first();
31378 this.field.on("keydown", this.onPagingKeydown, this);
31379 this.field.on("focus", function(){this.dom.select();});
31382 this.afterTextEl = field.el.select('.x-paging-after',true).first();
31383 //this.field.setHeight(18);
31384 //this.addSeparator();
31385 this.next = this.navgroup.addItem({
31386 tooltip: this.nextText,
31387 cls: "next btn-outline-secondary",
31388 html : ' <i class="fa fa-forward"></i>',
31390 preventDefault: true,
31391 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
31393 this.last = this.navgroup.addItem({
31394 tooltip: this.lastText,
31395 html : ' <i class="fa fa-step-forward"></i>',
31396 cls: "next btn-outline-secondary",
31398 preventDefault: true,
31399 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
31401 //this.addSeparator();
31402 this.loading = this.navgroup.addItem({
31403 tooltip: this.refreshText,
31404 cls: "btn-outline-secondary",
31405 html : ' <i class="fa fa-refresh"></i>',
31406 preventDefault: true,
31407 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
31413 updateInfo : function(){
31414 if(this.displayEl){
31415 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
31416 var msg = count == 0 ?
31420 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
31422 this.displayEl.update(msg);
31427 onLoad : function(ds, r, o)
31429 this.cursor = o.params && o.params.start ? o.params.start : 0;
31431 var d = this.getPageData(),
31436 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
31437 this.field.dom.value = ap;
31438 this.first.setDisabled(ap == 1);
31439 this.prev.setDisabled(ap == 1);
31440 this.next.setDisabled(ap == ps);
31441 this.last.setDisabled(ap == ps);
31442 this.loading.enable();
31447 getPageData : function(){
31448 var total = this.ds.getTotalCount();
31451 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
31452 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
31457 onLoadError : function(proxy, o){
31458 this.loading.enable();
31459 if (this.ds.events.loadexception.listeners.length < 2) {
31460 // nothing has been assigned to loadexception except this...
31462 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
31468 onPagingKeydown : function(e){
31469 var k = e.getKey();
31470 var d = this.getPageData();
31472 var v = this.field.dom.value, pageNum;
31473 if(!v || isNaN(pageNum = parseInt(v, 10))){
31474 this.field.dom.value = d.activePage;
31477 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
31478 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31481 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))
31483 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
31484 this.field.dom.value = pageNum;
31485 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
31488 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
31490 var v = this.field.dom.value, pageNum;
31491 var increment = (e.shiftKey) ? 10 : 1;
31492 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31495 if(!v || isNaN(pageNum = parseInt(v, 10))) {
31496 this.field.dom.value = d.activePage;
31499 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31501 this.field.dom.value = parseInt(v, 10) + increment;
31502 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31503 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31510 beforeLoad : function(){
31512 this.loading.disable();
31517 onClick : function(which){
31526 ds.load({params:{start: 0, limit: this.pageSize}});
31529 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31532 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31535 var total = ds.getTotalCount();
31536 var extra = total % this.pageSize;
31537 var lastStart = extra ? (total - extra) : total-this.pageSize;
31538 ds.load({params:{start: lastStart, limit: this.pageSize}});
31541 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31547 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31548 * @param {Roo.data.Store} store The data store to unbind
31550 unbind : function(ds){
31551 ds.un("beforeload", this.beforeLoad, this);
31552 ds.un("load", this.onLoad, this);
31553 ds.un("loadexception", this.onLoadError, this);
31554 ds.un("remove", this.updateInfo, this);
31555 ds.un("add", this.updateInfo, this);
31556 this.ds = undefined;
31560 * Binds the paging toolbar to the specified {@link Roo.data.Store}
31561 * @param {Roo.data.Store} store The data store to bind
31563 bind : function(ds){
31564 ds.on("beforeload", this.beforeLoad, this);
31565 ds.on("load", this.onLoad, this);
31566 ds.on("loadexception", this.onLoadError, this);
31567 ds.on("remove", this.updateInfo, this);
31568 ds.on("add", this.updateInfo, this);
31579 * @class Roo.bootstrap.MessageBar
31580 * @extends Roo.bootstrap.Component
31581 * Bootstrap MessageBar class
31582 * @cfg {String} html contents of the MessageBar
31583 * @cfg {String} weight (info | success | warning | danger) default info
31584 * @cfg {String} beforeClass insert the bar before the given class
31585 * @cfg {Boolean} closable (true | false) default false
31586 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
31589 * Create a new Element
31590 * @param {Object} config The config object
31593 Roo.bootstrap.MessageBar = function(config){
31594 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
31597 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
31603 beforeClass: 'bootstrap-sticky-wrap',
31605 getAutoCreate : function(){
31609 cls: 'alert alert-dismissable alert-' + this.weight,
31614 html: this.html || ''
31620 cfg.cls += ' alert-messages-fixed';
31634 onRender : function(ct, position)
31636 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
31639 var cfg = Roo.apply({}, this.getAutoCreate());
31643 cfg.cls += ' ' + this.cls;
31646 cfg.style = this.style;
31648 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
31650 this.el.setVisibilityMode(Roo.Element.DISPLAY);
31653 this.el.select('>button.close').on('click', this.hide, this);
31659 if (!this.rendered) {
31665 this.fireEvent('show', this);
31671 if (!this.rendered) {
31677 this.fireEvent('hide', this);
31680 update : function()
31682 // var e = this.el.dom.firstChild;
31684 // if(this.closable){
31685 // e = e.nextSibling;
31688 // e.data = this.html || '';
31690 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
31706 * @class Roo.bootstrap.Graph
31707 * @extends Roo.bootstrap.Component
31708 * Bootstrap Graph class
31712 @cfg {String} graphtype bar | vbar | pie
31713 @cfg {number} g_x coodinator | centre x (pie)
31714 @cfg {number} g_y coodinator | centre y (pie)
31715 @cfg {number} g_r radius (pie)
31716 @cfg {number} g_height height of the chart (respected by all elements in the set)
31717 @cfg {number} g_width width of the chart (respected by all elements in the set)
31718 @cfg {Object} title The title of the chart
31721 -opts (object) options for the chart
31723 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
31724 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
31726 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.
31727 o stacked (boolean) whether or not to tread values as in a stacked bar chart
31729 o stretch (boolean)
31731 -opts (object) options for the pie
31734 o startAngle (number)
31735 o endAngle (number)
31739 * Create a new Input
31740 * @param {Object} config The config object
31743 Roo.bootstrap.Graph = function(config){
31744 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
31750 * The img click event for the img.
31751 * @param {Roo.EventObject} e
31757 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
31768 //g_colors: this.colors,
31775 getAutoCreate : function(){
31786 onRender : function(ct,position){
31789 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
31791 if (typeof(Raphael) == 'undefined') {
31792 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
31796 this.raphael = Raphael(this.el.dom);
31798 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
31799 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
31800 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
31801 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
31803 r.text(160, 10, "Single Series Chart").attr(txtattr);
31804 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
31805 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
31806 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
31808 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
31809 r.barchart(330, 10, 300, 220, data1);
31810 r.barchart(10, 250, 300, 220, data2, {stacked: true});
31811 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
31814 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
31815 // r.barchart(30, 30, 560, 250, xdata, {
31816 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
31817 // axis : "0 0 1 1",
31818 // axisxlabels : xdata
31819 // //yvalues : cols,
31822 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
31824 // this.load(null,xdata,{
31825 // axis : "0 0 1 1",
31826 // axisxlabels : xdata
31831 load : function(graphtype,xdata,opts)
31833 this.raphael.clear();
31835 graphtype = this.graphtype;
31840 var r = this.raphael,
31841 fin = function () {
31842 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
31844 fout = function () {
31845 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
31847 pfin = function() {
31848 this.sector.stop();
31849 this.sector.scale(1.1, 1.1, this.cx, this.cy);
31852 this.label[0].stop();
31853 this.label[0].attr({ r: 7.5 });
31854 this.label[1].attr({ "font-weight": 800 });
31857 pfout = function() {
31858 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
31861 this.label[0].animate({ r: 5 }, 500, "bounce");
31862 this.label[1].attr({ "font-weight": 400 });
31868 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
31871 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
31874 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
31875 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
31877 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
31884 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
31889 setTitle: function(o)
31894 initEvents: function() {
31897 this.el.on('click', this.onClick, this);
31901 onClick : function(e)
31903 Roo.log('img onclick');
31904 this.fireEvent('click', this, e);
31910 Roo.bootstrap.dash = {};/*
31916 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
31919 * @class Roo.bootstrap.dash.NumberBox
31920 * @extends Roo.bootstrap.Component
31921 * Bootstrap NumberBox class
31922 * @cfg {String} headline Box headline
31923 * @cfg {String} content Box content
31924 * @cfg {String} icon Box icon
31925 * @cfg {String} footer Footer text
31926 * @cfg {String} fhref Footer href
31929 * Create a new NumberBox
31930 * @param {Object} config The config object
31934 Roo.bootstrap.dash.NumberBox = function(config){
31935 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
31939 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
31948 getAutoCreate : function(){
31952 cls : 'small-box ',
31960 cls : 'roo-headline',
31961 html : this.headline
31965 cls : 'roo-content',
31966 html : this.content
31980 cls : 'ion ' + this.icon
31989 cls : 'small-box-footer',
31990 href : this.fhref || '#',
31994 cfg.cn.push(footer);
32001 onRender : function(ct,position){
32002 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
32009 setHeadline: function (value)
32011 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
32014 setFooter: function (value, href)
32016 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
32019 this.el.select('a.small-box-footer',true).first().attr('href', href);
32024 setContent: function (value)
32026 this.el.select('.roo-content',true).first().dom.innerHTML = value;
32029 initEvents: function()
32043 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
32046 * @class Roo.bootstrap.dash.TabBox
32047 * @extends Roo.bootstrap.Component
32048 * @children Roo.bootstrap.dash.TabPane
32049 * Bootstrap TabBox class
32050 * @cfg {String} title Title of the TabBox
32051 * @cfg {String} icon Icon of the TabBox
32052 * @cfg {Boolean} showtabs (true|false) show the tabs default true
32053 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
32056 * Create a new TabBox
32057 * @param {Object} config The config object
32061 Roo.bootstrap.dash.TabBox = function(config){
32062 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
32067 * When a pane is added
32068 * @param {Roo.bootstrap.dash.TabPane} pane
32072 * @event activatepane
32073 * When a pane is activated
32074 * @param {Roo.bootstrap.dash.TabPane} pane
32076 "activatepane" : true
32084 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
32089 tabScrollable : false,
32091 getChildContainer : function()
32093 return this.el.select('.tab-content', true).first();
32096 getAutoCreate : function(){
32100 cls: 'pull-left header',
32108 cls: 'fa ' + this.icon
32114 cls: 'nav nav-tabs pull-right',
32120 if(this.tabScrollable){
32127 cls: 'nav nav-tabs pull-right',
32138 cls: 'nav-tabs-custom',
32143 cls: 'tab-content no-padding',
32151 initEvents : function()
32153 //Roo.log('add add pane handler');
32154 this.on('addpane', this.onAddPane, this);
32157 * Updates the box title
32158 * @param {String} html to set the title to.
32160 setTitle : function(value)
32162 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
32164 onAddPane : function(pane)
32166 this.panes.push(pane);
32167 //Roo.log('addpane');
32169 // tabs are rendere left to right..
32170 if(!this.showtabs){
32174 var ctr = this.el.select('.nav-tabs', true).first();
32177 var existing = ctr.select('.nav-tab',true);
32178 var qty = existing.getCount();;
32181 var tab = ctr.createChild({
32183 cls : 'nav-tab' + (qty ? '' : ' active'),
32191 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
32194 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
32196 pane.el.addClass('active');
32201 onTabClick : function(ev,un,ob,pane)
32203 //Roo.log('tab - prev default');
32204 ev.preventDefault();
32207 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
32208 pane.tab.addClass('active');
32209 //Roo.log(pane.title);
32210 this.getChildContainer().select('.tab-pane',true).removeClass('active');
32211 // technically we should have a deactivate event.. but maybe add later.
32212 // and it should not de-activate the selected tab...
32213 this.fireEvent('activatepane', pane);
32214 pane.el.addClass('active');
32215 pane.fireEvent('activate');
32220 getActivePane : function()
32223 Roo.each(this.panes, function(p) {
32224 if(p.el.hasClass('active')){
32245 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
32247 * @class Roo.bootstrap.TabPane
32248 * @extends Roo.bootstrap.Component
32249 * @children Roo.bootstrap.Graph Roo.bootstrap.Column
32250 * Bootstrap TabPane class
32251 * @cfg {Boolean} active (false | true) Default false
32252 * @cfg {String} title title of panel
32256 * Create a new TabPane
32257 * @param {Object} config The config object
32260 Roo.bootstrap.dash.TabPane = function(config){
32261 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
32267 * When a pane is activated
32268 * @param {Roo.bootstrap.dash.TabPane} pane
32275 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
32280 // the tabBox that this is attached to.
32283 getAutoCreate : function()
32291 cfg.cls += ' active';
32296 initEvents : function()
32298 //Roo.log('trigger add pane handler');
32299 this.parent().fireEvent('addpane', this)
32303 * Updates the tab title
32304 * @param {String} html to set the title to.
32306 setTitle: function(str)
32312 this.tab.select('a', true).first().dom.innerHTML = str;
32331 * @class Roo.bootstrap.Tooltip
32332 * Bootstrap Tooltip class
32333 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
32334 * to determine which dom element triggers the tooltip.
32336 * It needs to add support for additional attributes like tooltip-position
32339 * Create a new Toolti
32340 * @param {Object} config The config object
32343 Roo.bootstrap.Tooltip = function(config){
32344 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
32346 this.alignment = Roo.bootstrap.Tooltip.alignment;
32348 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
32349 this.alignment = config.alignment;
32354 Roo.apply(Roo.bootstrap.Tooltip, {
32356 * @function init initialize tooltip monitoring.
32360 currentTip : false,
32361 currentRegion : false,
32367 Roo.get(document).on('mouseover', this.enter ,this);
32368 Roo.get(document).on('mouseout', this.leave, this);
32371 this.currentTip = new Roo.bootstrap.Tooltip();
32374 enter : function(ev)
32376 var dom = ev.getTarget();
32378 //Roo.log(['enter',dom]);
32379 var el = Roo.fly(dom);
32380 if (this.currentEl) {
32382 //Roo.log(this.currentEl);
32383 //Roo.log(this.currentEl.contains(dom));
32384 if (this.currentEl == el) {
32387 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
32393 if (this.currentTip.el) {
32394 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
32398 if(!el || el.dom == document){
32404 if (!el.attr('tooltip')) {
32405 pel = el.findParent("[tooltip]");
32407 bindEl = Roo.get(pel);
32413 // you can not look for children, as if el is the body.. then everythign is the child..
32414 if (!pel && !el.attr('tooltip')) { //
32415 if (!el.select("[tooltip]").elements.length) {
32418 // is the mouse over this child...?
32419 bindEl = el.select("[tooltip]").first();
32420 var xy = ev.getXY();
32421 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
32422 //Roo.log("not in region.");
32425 //Roo.log("child element over..");
32428 this.currentEl = el;
32429 this.currentTip.bind(bindEl);
32430 this.currentRegion = Roo.lib.Region.getRegion(dom);
32431 this.currentTip.enter();
32434 leave : function(ev)
32436 var dom = ev.getTarget();
32437 //Roo.log(['leave',dom]);
32438 if (!this.currentEl) {
32443 if (dom != this.currentEl.dom) {
32446 var xy = ev.getXY();
32447 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
32450 // only activate leave if mouse cursor is outside... bounding box..
32455 if (this.currentTip) {
32456 this.currentTip.leave();
32458 //Roo.log('clear currentEl');
32459 this.currentEl = false;
32464 'left' : ['r-l', [-2,0], 'right'],
32465 'right' : ['l-r', [2,0], 'left'],
32466 'bottom' : ['t-b', [0,2], 'top'],
32467 'top' : [ 'b-t', [0,-2], 'bottom']
32473 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
32478 delay : null, // can be { show : 300 , hide: 500}
32482 hoverState : null, //???
32484 placement : 'bottom',
32488 getAutoCreate : function(){
32495 cls : 'tooltip-arrow arrow'
32498 cls : 'tooltip-inner'
32505 bind : function(el)
32510 initEvents : function()
32512 this.arrowEl = this.el.select('.arrow', true).first();
32513 this.innerEl = this.el.select('.tooltip-inner', true).first();
32516 enter : function () {
32518 if (this.timeout != null) {
32519 clearTimeout(this.timeout);
32522 this.hoverState = 'in';
32523 //Roo.log("enter - show");
32524 if (!this.delay || !this.delay.show) {
32529 this.timeout = setTimeout(function () {
32530 if (_t.hoverState == 'in') {
32533 }, this.delay.show);
32537 clearTimeout(this.timeout);
32539 this.hoverState = 'out';
32540 if (!this.delay || !this.delay.hide) {
32546 this.timeout = setTimeout(function () {
32547 //Roo.log("leave - timeout");
32549 if (_t.hoverState == 'out') {
32551 Roo.bootstrap.Tooltip.currentEl = false;
32556 show : function (msg)
32559 this.render(document.body);
32562 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
32564 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
32566 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
32568 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
32569 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
32571 var placement = typeof this.placement == 'function' ?
32572 this.placement.call(this, this.el, on_el) :
32575 var autoToken = /\s?auto?\s?/i;
32576 var autoPlace = autoToken.test(placement);
32578 placement = placement.replace(autoToken, '') || 'top';
32582 //this.el.setXY([0,0]);
32584 //this.el.dom.style.display='block';
32586 //this.el.appendTo(on_el);
32588 var p = this.getPosition();
32589 var box = this.el.getBox();
32595 var align = this.alignment[placement];
32597 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
32599 if(placement == 'top' || placement == 'bottom'){
32601 placement = 'right';
32604 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
32605 placement = 'left';
32608 var scroll = Roo.select('body', true).first().getScroll();
32610 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
32614 align = this.alignment[placement];
32616 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
32620 var elems = document.getElementsByTagName('div');
32621 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
32622 for (var i = 0; i < elems.length; i++) {
32623 var zindex = Number.parseInt(
32624 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
32627 if (zindex > highest) {
32634 this.el.dom.style.zIndex = highest;
32636 this.el.alignTo(this.bindEl, align[0],align[1]);
32637 //var arrow = this.el.select('.arrow',true).first();
32638 //arrow.set(align[2],
32640 this.el.addClass(placement);
32641 this.el.addClass("bs-tooltip-"+ placement);
32643 this.el.addClass('in fade show');
32645 this.hoverState = null;
32647 if (this.el.hasClass('fade')) {
32662 //this.el.setXY([0,0]);
32663 this.el.removeClass(['show', 'in']);
32679 * @class Roo.bootstrap.LocationPicker
32680 * @extends Roo.bootstrap.Component
32681 * Bootstrap LocationPicker class
32682 * @cfg {Number} latitude Position when init default 0
32683 * @cfg {Number} longitude Position when init default 0
32684 * @cfg {Number} zoom default 15
32685 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
32686 * @cfg {Boolean} mapTypeControl default false
32687 * @cfg {Boolean} disableDoubleClickZoom default false
32688 * @cfg {Boolean} scrollwheel default true
32689 * @cfg {Boolean} streetViewControl default false
32690 * @cfg {Number} radius default 0
32691 * @cfg {String} locationName
32692 * @cfg {Boolean} draggable default true
32693 * @cfg {Boolean} enableAutocomplete default false
32694 * @cfg {Boolean} enableReverseGeocode default true
32695 * @cfg {String} markerTitle
32698 * Create a new LocationPicker
32699 * @param {Object} config The config object
32703 Roo.bootstrap.LocationPicker = function(config){
32705 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
32710 * Fires when the picker initialized.
32711 * @param {Roo.bootstrap.LocationPicker} this
32712 * @param {Google Location} location
32716 * @event positionchanged
32717 * Fires when the picker position changed.
32718 * @param {Roo.bootstrap.LocationPicker} this
32719 * @param {Google Location} location
32721 positionchanged : true,
32724 * Fires when the map resize.
32725 * @param {Roo.bootstrap.LocationPicker} this
32730 * Fires when the map show.
32731 * @param {Roo.bootstrap.LocationPicker} this
32736 * Fires when the map hide.
32737 * @param {Roo.bootstrap.LocationPicker} this
32742 * Fires when click the map.
32743 * @param {Roo.bootstrap.LocationPicker} this
32744 * @param {Map event} e
32748 * @event mapRightClick
32749 * Fires when right click the map.
32750 * @param {Roo.bootstrap.LocationPicker} this
32751 * @param {Map event} e
32753 mapRightClick : true,
32755 * @event markerClick
32756 * Fires when click the marker.
32757 * @param {Roo.bootstrap.LocationPicker} this
32758 * @param {Map event} e
32760 markerClick : true,
32762 * @event markerRightClick
32763 * Fires when right click the marker.
32764 * @param {Roo.bootstrap.LocationPicker} this
32765 * @param {Map event} e
32767 markerRightClick : true,
32769 * @event OverlayViewDraw
32770 * Fires when OverlayView Draw
32771 * @param {Roo.bootstrap.LocationPicker} this
32773 OverlayViewDraw : true,
32775 * @event OverlayViewOnAdd
32776 * Fires when OverlayView Draw
32777 * @param {Roo.bootstrap.LocationPicker} this
32779 OverlayViewOnAdd : true,
32781 * @event OverlayViewOnRemove
32782 * Fires when OverlayView Draw
32783 * @param {Roo.bootstrap.LocationPicker} this
32785 OverlayViewOnRemove : true,
32787 * @event OverlayViewShow
32788 * Fires when OverlayView Draw
32789 * @param {Roo.bootstrap.LocationPicker} this
32790 * @param {Pixel} cpx
32792 OverlayViewShow : true,
32794 * @event OverlayViewHide
32795 * Fires when OverlayView Draw
32796 * @param {Roo.bootstrap.LocationPicker} this
32798 OverlayViewHide : true,
32800 * @event loadexception
32801 * Fires when load google lib failed.
32802 * @param {Roo.bootstrap.LocationPicker} this
32804 loadexception : true
32809 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
32811 gMapContext: false,
32817 mapTypeControl: false,
32818 disableDoubleClickZoom: false,
32820 streetViewControl: false,
32824 enableAutocomplete: false,
32825 enableReverseGeocode: true,
32828 getAutoCreate: function()
32833 cls: 'roo-location-picker'
32839 initEvents: function(ct, position)
32841 if(!this.el.getWidth() || this.isApplied()){
32845 this.el.setVisibilityMode(Roo.Element.DISPLAY);
32850 initial: function()
32852 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
32853 this.fireEvent('loadexception', this);
32857 if(!this.mapTypeId){
32858 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
32861 this.gMapContext = this.GMapContext();
32863 this.initOverlayView();
32865 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
32869 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
32870 _this.setPosition(_this.gMapContext.marker.position);
32873 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
32874 _this.fireEvent('mapClick', this, event);
32878 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
32879 _this.fireEvent('mapRightClick', this, event);
32883 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
32884 _this.fireEvent('markerClick', this, event);
32888 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
32889 _this.fireEvent('markerRightClick', this, event);
32893 this.setPosition(this.gMapContext.location);
32895 this.fireEvent('initial', this, this.gMapContext.location);
32898 initOverlayView: function()
32902 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
32906 _this.fireEvent('OverlayViewDraw', _this);
32911 _this.fireEvent('OverlayViewOnAdd', _this);
32914 onRemove: function()
32916 _this.fireEvent('OverlayViewOnRemove', _this);
32919 show: function(cpx)
32921 _this.fireEvent('OverlayViewShow', _this, cpx);
32926 _this.fireEvent('OverlayViewHide', _this);
32932 fromLatLngToContainerPixel: function(event)
32934 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
32937 isApplied: function()
32939 return this.getGmapContext() == false ? false : true;
32942 getGmapContext: function()
32944 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
32947 GMapContext: function()
32949 var position = new google.maps.LatLng(this.latitude, this.longitude);
32951 var _map = new google.maps.Map(this.el.dom, {
32954 mapTypeId: this.mapTypeId,
32955 mapTypeControl: this.mapTypeControl,
32956 disableDoubleClickZoom: this.disableDoubleClickZoom,
32957 scrollwheel: this.scrollwheel,
32958 streetViewControl: this.streetViewControl,
32959 locationName: this.locationName,
32960 draggable: this.draggable,
32961 enableAutocomplete: this.enableAutocomplete,
32962 enableReverseGeocode: this.enableReverseGeocode
32965 var _marker = new google.maps.Marker({
32966 position: position,
32968 title: this.markerTitle,
32969 draggable: this.draggable
32976 location: position,
32977 radius: this.radius,
32978 locationName: this.locationName,
32979 addressComponents: {
32980 formatted_address: null,
32981 addressLine1: null,
32982 addressLine2: null,
32984 streetNumber: null,
32988 stateOrProvince: null
32991 domContainer: this.el.dom,
32992 geodecoder: new google.maps.Geocoder()
32996 drawCircle: function(center, radius, options)
32998 if (this.gMapContext.circle != null) {
32999 this.gMapContext.circle.setMap(null);
33003 options = Roo.apply({}, options, {
33004 strokeColor: "#0000FF",
33005 strokeOpacity: .35,
33007 fillColor: "#0000FF",
33011 options.map = this.gMapContext.map;
33012 options.radius = radius;
33013 options.center = center;
33014 this.gMapContext.circle = new google.maps.Circle(options);
33015 return this.gMapContext.circle;
33021 setPosition: function(location)
33023 this.gMapContext.location = location;
33024 this.gMapContext.marker.setPosition(location);
33025 this.gMapContext.map.panTo(location);
33026 this.drawCircle(location, this.gMapContext.radius, {});
33030 if (this.gMapContext.settings.enableReverseGeocode) {
33031 this.gMapContext.geodecoder.geocode({
33032 latLng: this.gMapContext.location
33033 }, function(results, status) {
33035 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
33036 _this.gMapContext.locationName = results[0].formatted_address;
33037 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
33039 _this.fireEvent('positionchanged', this, location);
33046 this.fireEvent('positionchanged', this, location);
33051 google.maps.event.trigger(this.gMapContext.map, "resize");
33053 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
33055 this.fireEvent('resize', this);
33058 setPositionByLatLng: function(latitude, longitude)
33060 this.setPosition(new google.maps.LatLng(latitude, longitude));
33063 getCurrentPosition: function()
33066 latitude: this.gMapContext.location.lat(),
33067 longitude: this.gMapContext.location.lng()
33071 getAddressName: function()
33073 return this.gMapContext.locationName;
33076 getAddressComponents: function()
33078 return this.gMapContext.addressComponents;
33081 address_component_from_google_geocode: function(address_components)
33085 for (var i = 0; i < address_components.length; i++) {
33086 var component = address_components[i];
33087 if (component.types.indexOf("postal_code") >= 0) {
33088 result.postalCode = component.short_name;
33089 } else if (component.types.indexOf("street_number") >= 0) {
33090 result.streetNumber = component.short_name;
33091 } else if (component.types.indexOf("route") >= 0) {
33092 result.streetName = component.short_name;
33093 } else if (component.types.indexOf("neighborhood") >= 0) {
33094 result.city = component.short_name;
33095 } else if (component.types.indexOf("locality") >= 0) {
33096 result.city = component.short_name;
33097 } else if (component.types.indexOf("sublocality") >= 0) {
33098 result.district = component.short_name;
33099 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
33100 result.stateOrProvince = component.short_name;
33101 } else if (component.types.indexOf("country") >= 0) {
33102 result.country = component.short_name;
33106 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
33107 result.addressLine2 = "";
33111 setZoomLevel: function(zoom)
33113 this.gMapContext.map.setZoom(zoom);
33126 this.fireEvent('show', this);
33137 this.fireEvent('hide', this);
33142 Roo.apply(Roo.bootstrap.LocationPicker, {
33144 OverlayView : function(map, options)
33146 options = options || {};
33153 * @class Roo.bootstrap.Alert
33154 * @extends Roo.bootstrap.Component
33155 * Bootstrap Alert class - shows an alert area box
33157 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
33158 Enter a valid email address
33161 * @cfg {String} title The title of alert
33162 * @cfg {String} html The content of alert
33163 * @cfg {String} weight (success|info|warning|danger) Weight of the message
33164 * @cfg {String} fa font-awesomeicon
33165 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
33166 * @cfg {Boolean} close true to show a x closer
33170 * Create a new alert
33171 * @param {Object} config The config object
33175 Roo.bootstrap.Alert = function(config){
33176 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
33180 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
33186 faicon: false, // BC
33190 getAutoCreate : function()
33202 style : this.close ? '' : 'display:none'
33206 cls : 'roo-alert-icon'
33211 cls : 'roo-alert-title',
33216 cls : 'roo-alert-text',
33223 cfg.cn[0].cls += ' fa ' + this.faicon;
33226 cfg.cn[0].cls += ' fa ' + this.fa;
33230 cfg.cls += ' alert-' + this.weight;
33236 initEvents: function()
33238 this.el.setVisibilityMode(Roo.Element.DISPLAY);
33239 this.titleEl = this.el.select('.roo-alert-title',true).first();
33240 this.iconEl = this.el.select('.roo-alert-icon',true).first();
33241 this.htmlEl = this.el.select('.roo-alert-text',true).first();
33242 if (this.seconds > 0) {
33243 this.hide.defer(this.seconds, this);
33247 * Set the Title Message HTML
33248 * @param {String} html
33250 setTitle : function(str)
33252 this.titleEl.dom.innerHTML = str;
33256 * Set the Body Message HTML
33257 * @param {String} html
33259 setHtml : function(str)
33261 this.htmlEl.dom.innerHTML = str;
33264 * Set the Weight of the alert
33265 * @param {String} (success|info|warning|danger) weight
33268 setWeight : function(weight)
33271 this.el.removeClass('alert-' + this.weight);
33274 this.weight = weight;
33276 this.el.addClass('alert-' + this.weight);
33279 * Set the Icon of the alert
33280 * @param {String} see fontawsome names (name without the 'fa-' bit)
33282 setIcon : function(icon)
33285 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
33288 this.faicon = icon;
33290 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
33315 * @class Roo.bootstrap.UploadCropbox
33316 * @extends Roo.bootstrap.Component
33317 * Bootstrap UploadCropbox class
33318 * @cfg {String} emptyText show when image has been loaded
33319 * @cfg {String} rotateNotify show when image too small to rotate
33320 * @cfg {Number} errorTimeout default 3000
33321 * @cfg {Number} minWidth default 300
33322 * @cfg {Number} minHeight default 300
33323 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
33324 * @cfg {Boolean} isDocument (true|false) default false
33325 * @cfg {String} url action url
33326 * @cfg {String} paramName default 'imageUpload'
33327 * @cfg {String} method default POST
33328 * @cfg {Boolean} loadMask (true|false) default true
33329 * @cfg {Boolean} loadingText default 'Loading...'
33332 * Create a new UploadCropbox
33333 * @param {Object} config The config object
33336 Roo.bootstrap.UploadCropbox = function(config){
33337 console.log("BOOTSTRAP UPLOAD CROPBOX CONSTRUCTOR");
33338 console.log(config);
33339 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
33343 * @event beforeselectfile
33344 * Fire before select file
33345 * @param {Roo.bootstrap.UploadCropbox} this
33347 "beforeselectfile" : true,
33350 * Fire after initEvent
33351 * @param {Roo.bootstrap.UploadCropbox} this
33356 * Fire after initEvent
33357 * @param {Roo.bootstrap.UploadCropbox} this
33358 * @param {String} data
33363 * Fire when preparing the file data
33364 * @param {Roo.bootstrap.UploadCropbox} this
33365 * @param {Object} file
33370 * Fire when get exception
33371 * @param {Roo.bootstrap.UploadCropbox} this
33372 * @param {XMLHttpRequest} xhr
33374 "exception" : true,
33376 * @event beforeloadcanvas
33377 * Fire before load the canvas
33378 * @param {Roo.bootstrap.UploadCropbox} this
33379 * @param {String} src
33381 "beforeloadcanvas" : true,
33384 * Fire when trash image
33385 * @param {Roo.bootstrap.UploadCropbox} this
33390 * Fire when download the image
33391 * @param {Roo.bootstrap.UploadCropbox} this
33395 * @event footerbuttonclick
33396 * Fire when footerbuttonclick
33397 * @param {Roo.bootstrap.UploadCropbox} this
33398 * @param {String} type
33400 "footerbuttonclick" : true,
33404 * @param {Roo.bootstrap.UploadCropbox} this
33409 * Fire when rotate the image
33410 * @param {Roo.bootstrap.UploadCropbox} this
33411 * @param {String} pos
33416 * Fire when inspect the file
33417 * @param {Roo.bootstrap.UploadCropbox} this
33418 * @param {Object} file
33423 * Fire when xhr upload the file
33424 * @param {Roo.bootstrap.UploadCropbox} this
33425 * @param {Object} data
33430 * Fire when arrange the file data
33431 * @param {Roo.bootstrap.UploadCropbox} this
33432 * @param {Object} formData
33437 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
33440 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
33442 emptyText : 'Click to upload image',
33443 rotateNotify : 'Image is too small to rotate',
33444 errorTimeout : 3000,
33458 cropType : 'image/jpeg',
33460 canvasLoaded : false,
33461 isDocument : false,
33463 paramName : 'imageUpload',
33465 loadingText : 'Loading...',
33468 getAutoCreate : function()
33472 cls : 'roo-upload-cropbox',
33476 cls : 'roo-upload-cropbox-selector',
33481 cls : 'roo-upload-cropbox-body',
33482 style : 'cursor:pointer',
33486 cls : 'roo-upload-cropbox-preview'
33490 cls : 'roo-upload-cropbox-thumb'
33494 cls : 'roo-upload-cropbox-empty-notify',
33495 html : this.emptyText
33499 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
33500 html : this.rotateNotify
33506 cls : 'roo-upload-cropbox-footer',
33509 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
33519 onRender : function(ct, position)
33521 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
33523 if (this.buttons.length) {
33525 Roo.each(this.buttons, function(bb) {
33527 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
33529 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
33535 this.maskEl = this.el;
33539 initEvents : function()
33541 this.urlAPI = (window.createObjectURL && window) ||
33542 (window.URL && URL.revokeObjectURL && URL) ||
33543 (window.webkitURL && webkitURL);
33545 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
33546 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33548 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
33549 this.selectorEl.hide();
33551 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
33552 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33554 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
33555 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33556 this.thumbEl.hide();
33558 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
33559 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33561 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
33562 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33563 this.errorEl.hide();
33565 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
33566 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33567 this.footerEl.hide();
33569 this.setThumbBoxSize();
33575 this.fireEvent('initial', this);
33582 window.addEventListener("resize", function() { _this.resize(); } );
33584 this.bodyEl.on('click', this.beforeSelectFile, this);
33587 this.bodyEl.on('touchstart', this.onTouchStart, this);
33588 this.bodyEl.on('touchmove', this.onTouchMove, this);
33589 this.bodyEl.on('touchend', this.onTouchEnd, this);
33593 this.bodyEl.on('mousedown', this.onMouseDown, this);
33594 this.bodyEl.on('mousemove', this.onMouseMove, this);
33595 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
33596 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
33597 Roo.get(document).on('mouseup', this.onMouseUp, this);
33600 this.selectorEl.on('change', this.onFileSelected, this);
33606 this.baseScale = 1;
33608 this.baseRotate = 1;
33609 this.dragable = false;
33610 this.pinching = false;
33613 this.cropData = false;
33614 this.notifyEl.dom.innerHTML = this.emptyText;
33616 this.selectorEl.dom.value = '';
33620 resize : function()
33622 if(this.fireEvent('resize', this) != false){
33623 this.setThumbBoxPosition();
33624 this.setCanvasPosition();
33628 onFooterButtonClick : function(e, el, o, type)
33631 case 'rotate-left' :
33632 this.onRotateLeft(e);
33634 case 'rotate-right' :
33635 this.onRotateRight(e);
33638 this.beforeSelectFile(e);
33653 this.fireEvent('footerbuttonclick', this, type);
33656 beforeSelectFile : function(e)
33658 e.preventDefault();
33660 if(this.fireEvent('beforeselectfile', this) != false){
33661 this.selectorEl.dom.click();
33665 onFileSelected : function(e)
33667 e.preventDefault();
33669 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
33673 var file = this.selectorEl.dom.files[0];
33675 if(this.fireEvent('inspect', this, file) != false){
33676 this.prepare(file);
33681 trash : function(e)
33683 this.fireEvent('trash', this);
33686 download : function(e)
33688 this.fireEvent('download', this);
33691 loadCanvas : function(src)
33693 if(this.fireEvent('beforeloadcanvas', this, src) != false){
33697 this.imageEl = document.createElement('img');
33701 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
33703 this.imageEl.src = src;
33707 onLoadCanvas : function()
33709 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
33710 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
33712 this.bodyEl.un('click', this.beforeSelectFile, this);
33714 this.notifyEl.hide();
33715 this.thumbEl.show();
33716 this.footerEl.show();
33718 this.baseRotateLevel();
33720 if(this.isDocument){
33721 this.setThumbBoxSize();
33724 this.setThumbBoxPosition();
33726 this.baseScaleLevel();
33732 this.canvasLoaded = true;
33735 this.maskEl.unmask();
33740 setCanvasPosition : function()
33742 if(!this.canvasEl){
33746 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
33747 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
33749 this.previewEl.setLeft(pw);
33750 this.previewEl.setTop(ph);
33754 onMouseDown : function(e)
33758 this.dragable = true;
33759 this.pinching = false;
33761 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
33762 this.dragable = false;
33766 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33767 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33771 onMouseMove : function(e)
33775 if(!this.canvasLoaded){
33779 if (!this.dragable){
33783 var minX = Math.ceil(this.thumbEl.getLeft(true));
33784 var minY = Math.ceil(this.thumbEl.getTop(true));
33786 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
33787 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
33789 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33790 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33792 x = x - this.mouseX;
33793 y = y - this.mouseY;
33795 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
33796 var bgY = Math.ceil(y + this.previewEl.getTop(true));
33798 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
33799 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
33801 this.previewEl.setLeft(bgX);
33802 this.previewEl.setTop(bgY);
33804 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33805 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33808 onMouseUp : function(e)
33812 this.dragable = false;
33815 onMouseWheel : function(e)
33819 this.startScale = this.scale;
33821 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
33823 if(!this.zoomable()){
33824 this.scale = this.startScale;
33833 zoomable : function()
33835 var minScale = this.thumbEl.getWidth() / this.minWidth;
33837 if(this.minWidth < this.minHeight){
33838 minScale = this.thumbEl.getHeight() / this.minHeight;
33841 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
33842 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
33846 (this.rotate == 0 || this.rotate == 180) &&
33848 width > this.imageEl.OriginWidth ||
33849 height > this.imageEl.OriginHeight ||
33850 (width < this.minWidth && height < this.minHeight)
33858 (this.rotate == 90 || this.rotate == 270) &&
33860 width > this.imageEl.OriginWidth ||
33861 height > this.imageEl.OriginHeight ||
33862 (width < this.minHeight && height < this.minWidth)
33869 !this.isDocument &&
33870 (this.rotate == 0 || this.rotate == 180) &&
33872 width < this.minWidth ||
33873 width > this.imageEl.OriginWidth ||
33874 height < this.minHeight ||
33875 height > this.imageEl.OriginHeight
33882 !this.isDocument &&
33883 (this.rotate == 90 || this.rotate == 270) &&
33885 width < this.minHeight ||
33886 width > this.imageEl.OriginWidth ||
33887 height < this.minWidth ||
33888 height > this.imageEl.OriginHeight
33898 onRotateLeft : function(e)
33900 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
33902 var minScale = this.thumbEl.getWidth() / this.minWidth;
33904 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
33905 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
33907 this.startScale = this.scale;
33909 while (this.getScaleLevel() < minScale){
33911 this.scale = this.scale + 1;
33913 if(!this.zoomable()){
33918 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
33919 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
33924 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
33931 this.scale = this.startScale;
33933 this.onRotateFail();
33938 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
33940 if(this.isDocument){
33941 this.setThumbBoxSize();
33942 this.setThumbBoxPosition();
33943 this.setCanvasPosition();
33948 this.fireEvent('rotate', this, 'left');
33952 onRotateRight : function(e)
33954 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
33956 var minScale = this.thumbEl.getWidth() / this.minWidth;
33958 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
33959 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
33961 this.startScale = this.scale;
33963 while (this.getScaleLevel() < minScale){
33965 this.scale = this.scale + 1;
33967 if(!this.zoomable()){
33972 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
33973 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
33978 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
33985 this.scale = this.startScale;
33987 this.onRotateFail();
33992 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
33994 if(this.isDocument){
33995 this.setThumbBoxSize();
33996 this.setThumbBoxPosition();
33997 this.setCanvasPosition();
34002 this.fireEvent('rotate', this, 'right');
34005 onRotateFail : function()
34007 this.errorEl.show(true);
34011 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
34016 this.previewEl.dom.innerHTML = '';
34018 var canvasEl = document.createElement("canvas");
34020 var contextEl = canvasEl.getContext("2d");
34022 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34023 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34024 var center = this.imageEl.OriginWidth / 2;
34026 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
34027 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34028 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34029 center = this.imageEl.OriginHeight / 2;
34032 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
34034 contextEl.translate(center, center);
34035 contextEl.rotate(this.rotate * Math.PI / 180);
34037 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34039 this.canvasEl = document.createElement("canvas");
34041 this.contextEl = this.canvasEl.getContext("2d");
34043 switch (this.rotate) {
34046 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34047 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34049 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34054 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34055 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34057 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34058 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);
34062 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34067 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34068 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34070 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34071 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);
34075 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);
34080 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34081 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34083 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34084 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34088 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);
34095 this.previewEl.appendChild(this.canvasEl);
34097 this.setCanvasPosition();
34102 if(!this.canvasLoaded){
34106 var imageCanvas = document.createElement("canvas");
34108 var imageContext = imageCanvas.getContext("2d");
34110 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34111 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34113 var center = imageCanvas.width / 2;
34115 imageContext.translate(center, center);
34117 imageContext.rotate(this.rotate * Math.PI / 180);
34119 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34121 var canvas = document.createElement("canvas");
34123 var context = canvas.getContext("2d");
34125 canvas.width = this.minWidth;
34126 canvas.height = this.minHeight;
34128 switch (this.rotate) {
34131 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34132 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34134 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34135 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34137 var targetWidth = this.minWidth - 2 * x;
34138 var targetHeight = this.minHeight - 2 * y;
34142 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34143 scale = targetWidth / width;
34146 if(x > 0 && y == 0){
34147 scale = targetHeight / height;
34150 if(x > 0 && y > 0){
34151 scale = targetWidth / width;
34153 if(width < height){
34154 scale = targetHeight / height;
34158 context.scale(scale, scale);
34160 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34161 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34163 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34164 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34166 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34171 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34172 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34174 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34175 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34177 var targetWidth = this.minWidth - 2 * x;
34178 var targetHeight = this.minHeight - 2 * y;
34182 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34183 scale = targetWidth / width;
34186 if(x > 0 && y == 0){
34187 scale = targetHeight / height;
34190 if(x > 0 && y > 0){
34191 scale = targetWidth / width;
34193 if(width < height){
34194 scale = targetHeight / height;
34198 context.scale(scale, scale);
34200 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34201 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34203 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34204 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34206 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34208 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34213 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34214 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34216 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34217 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34219 var targetWidth = this.minWidth - 2 * x;
34220 var targetHeight = this.minHeight - 2 * y;
34224 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34225 scale = targetWidth / width;
34228 if(x > 0 && y == 0){
34229 scale = targetHeight / height;
34232 if(x > 0 && y > 0){
34233 scale = targetWidth / width;
34235 if(width < height){
34236 scale = targetHeight / height;
34240 context.scale(scale, scale);
34242 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34243 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34245 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34246 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34248 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34249 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34251 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34256 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34257 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34259 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34260 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34262 var targetWidth = this.minWidth - 2 * x;
34263 var targetHeight = this.minHeight - 2 * y;
34267 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34268 scale = targetWidth / width;
34271 if(x > 0 && y == 0){
34272 scale = targetHeight / height;
34275 if(x > 0 && y > 0){
34276 scale = targetWidth / width;
34278 if(width < height){
34279 scale = targetHeight / height;
34283 context.scale(scale, scale);
34285 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34286 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34288 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34289 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34291 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34293 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34300 this.cropData = canvas.toDataURL(this.cropType);
34302 if(this.fireEvent('crop', this, this.cropData) !== false){
34303 this.process(this.file, this.cropData);
34310 setThumbBoxSize : function()
34314 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
34315 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
34316 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
34318 this.minWidth = width;
34319 this.minHeight = height;
34321 if(this.rotate == 90 || this.rotate == 270){
34322 this.minWidth = height;
34323 this.minHeight = width;
34328 width = Math.ceil(this.minWidth * height / this.minHeight);
34330 if(this.minWidth > this.minHeight){
34332 height = Math.ceil(this.minHeight * width / this.minWidth);
34335 this.thumbEl.setStyle({
34336 width : width + 'px',
34337 height : height + 'px'
34344 setThumbBoxPosition : function()
34346 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
34347 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
34349 this.thumbEl.setLeft(x);
34350 this.thumbEl.setTop(y);
34354 baseRotateLevel : function()
34356 this.baseRotate = 1;
34359 typeof(this.exif) != 'undefined' &&
34360 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
34361 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
34363 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
34366 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
34370 baseScaleLevel : function()
34374 if(this.isDocument){
34376 if(this.baseRotate == 6 || this.baseRotate == 8){
34378 height = this.thumbEl.getHeight();
34379 this.baseScale = height / this.imageEl.OriginWidth;
34381 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
34382 width = this.thumbEl.getWidth();
34383 this.baseScale = width / this.imageEl.OriginHeight;
34389 height = this.thumbEl.getHeight();
34390 this.baseScale = height / this.imageEl.OriginHeight;
34392 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
34393 width = this.thumbEl.getWidth();
34394 this.baseScale = width / this.imageEl.OriginWidth;
34400 if(this.baseRotate == 6 || this.baseRotate == 8){
34402 width = this.thumbEl.getHeight();
34403 this.baseScale = width / this.imageEl.OriginHeight;
34405 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
34406 height = this.thumbEl.getWidth();
34407 this.baseScale = height / this.imageEl.OriginHeight;
34410 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34411 height = this.thumbEl.getWidth();
34412 this.baseScale = height / this.imageEl.OriginHeight;
34414 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
34415 width = this.thumbEl.getHeight();
34416 this.baseScale = width / this.imageEl.OriginWidth;
34423 width = this.thumbEl.getWidth();
34424 this.baseScale = width / this.imageEl.OriginWidth;
34426 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
34427 height = this.thumbEl.getHeight();
34428 this.baseScale = height / this.imageEl.OriginHeight;
34431 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34433 height = this.thumbEl.getHeight();
34434 this.baseScale = height / this.imageEl.OriginHeight;
34436 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
34437 width = this.thumbEl.getWidth();
34438 this.baseScale = width / this.imageEl.OriginWidth;
34446 getScaleLevel : function()
34448 return this.baseScale * Math.pow(1.1, this.scale);
34451 onTouchStart : function(e)
34453 if(!this.canvasLoaded){
34454 this.beforeSelectFile(e);
34458 var touches = e.browserEvent.touches;
34464 if(touches.length == 1){
34465 this.onMouseDown(e);
34469 if(touches.length != 2){
34475 for(var i = 0, finger; finger = touches[i]; i++){
34476 coords.push(finger.pageX, finger.pageY);
34479 var x = Math.pow(coords[0] - coords[2], 2);
34480 var y = Math.pow(coords[1] - coords[3], 2);
34482 this.startDistance = Math.sqrt(x + y);
34484 this.startScale = this.scale;
34486 this.pinching = true;
34487 this.dragable = false;
34491 onTouchMove : function(e)
34493 if(!this.pinching && !this.dragable){
34497 var touches = e.browserEvent.touches;
34504 this.onMouseMove(e);
34510 for(var i = 0, finger; finger = touches[i]; i++){
34511 coords.push(finger.pageX, finger.pageY);
34514 var x = Math.pow(coords[0] - coords[2], 2);
34515 var y = Math.pow(coords[1] - coords[3], 2);
34517 this.endDistance = Math.sqrt(x + y);
34519 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
34521 if(!this.zoomable()){
34522 this.scale = this.startScale;
34530 onTouchEnd : function(e)
34532 this.pinching = false;
34533 this.dragable = false;
34537 process : function(file, crop)
34540 this.maskEl.mask(this.loadingText);
34543 this.xhr = new XMLHttpRequest();
34545 file.xhr = this.xhr;
34547 this.xhr.open(this.method, this.url, true);
34550 "Accept": "application/json",
34551 "Cache-Control": "no-cache",
34552 "X-Requested-With": "XMLHttpRequest"
34555 for (var headerName in headers) {
34556 var headerValue = headers[headerName];
34558 this.xhr.setRequestHeader(headerName, headerValue);
34564 this.xhr.onload = function()
34566 _this.xhrOnLoad(_this.xhr);
34569 this.xhr.onerror = function()
34571 _this.xhrOnError(_this.xhr);
34574 var formData = new FormData();
34576 formData.append('returnHTML', 'NO');
34579 formData.append('crop', crop);
34582 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
34583 formData.append(this.paramName, file, file.name);
34586 if(typeof(file.filename) != 'undefined'){
34587 formData.append('filename', file.filename);
34590 if(typeof(file.mimetype) != 'undefined'){
34591 formData.append('mimetype', file.mimetype);
34594 if(this.fireEvent('arrange', this, formData) != false){
34595 this.xhr.send(formData);
34599 xhrOnLoad : function(xhr)
34602 this.maskEl.unmask();
34605 if (xhr.readyState !== 4) {
34606 this.fireEvent('exception', this, xhr);
34610 var response = Roo.decode(xhr.responseText);
34612 if(!response.success){
34613 this.fireEvent('exception', this, xhr);
34617 var response = Roo.decode(xhr.responseText);
34619 this.fireEvent('upload', this, response);
34623 xhrOnError : function()
34626 this.maskEl.unmask();
34629 Roo.log('xhr on error');
34631 var response = Roo.decode(xhr.responseText);
34637 prepare : function(file)
34640 this.maskEl.mask(this.loadingText);
34646 if(typeof(file) === 'string'){
34647 this.loadCanvas(file);
34651 if(!file || !this.urlAPI){
34656 this.cropType = file.type;
34660 if(this.fireEvent('prepare', this, this.file) != false){
34662 var reader = new FileReader();
34664 reader.onload = function (e) {
34665 if (e.target.error) {
34666 Roo.log(e.target.error);
34670 var buffer = e.target.result,
34671 dataView = new DataView(buffer),
34673 maxOffset = dataView.byteLength - 4,
34677 if (dataView.getUint16(0) === 0xffd8) {
34678 while (offset < maxOffset) {
34679 markerBytes = dataView.getUint16(offset);
34681 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
34682 markerLength = dataView.getUint16(offset + 2) + 2;
34683 if (offset + markerLength > dataView.byteLength) {
34684 Roo.log('Invalid meta data: Invalid segment size.');
34688 if(markerBytes == 0xffe1){
34689 _this.parseExifData(
34696 offset += markerLength;
34706 var url = _this.urlAPI.createObjectURL(_this.file);
34708 _this.loadCanvas(url);
34713 reader.readAsArrayBuffer(this.file);
34719 parseExifData : function(dataView, offset, length)
34721 var tiffOffset = offset + 10,
34725 if (dataView.getUint32(offset + 4) !== 0x45786966) {
34726 // No Exif data, might be XMP data instead
34730 // Check for the ASCII code for "Exif" (0x45786966):
34731 if (dataView.getUint32(offset + 4) !== 0x45786966) {
34732 // No Exif data, might be XMP data instead
34735 if (tiffOffset + 8 > dataView.byteLength) {
34736 Roo.log('Invalid Exif data: Invalid segment size.');
34739 // Check for the two null bytes:
34740 if (dataView.getUint16(offset + 8) !== 0x0000) {
34741 Roo.log('Invalid Exif data: Missing byte alignment offset.');
34744 // Check the byte alignment:
34745 switch (dataView.getUint16(tiffOffset)) {
34747 littleEndian = true;
34750 littleEndian = false;
34753 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
34756 // Check for the TIFF tag marker (0x002A):
34757 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
34758 Roo.log('Invalid Exif data: Missing TIFF marker.');
34761 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
34762 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
34764 this.parseExifTags(
34767 tiffOffset + dirOffset,
34772 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
34777 if (dirOffset + 6 > dataView.byteLength) {
34778 Roo.log('Invalid Exif data: Invalid directory offset.');
34781 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
34782 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
34783 if (dirEndOffset + 4 > dataView.byteLength) {
34784 Roo.log('Invalid Exif data: Invalid directory size.');
34787 for (i = 0; i < tagsNumber; i += 1) {
34791 dirOffset + 2 + 12 * i, // tag offset
34795 // Return the offset to the next directory:
34796 return dataView.getUint32(dirEndOffset, littleEndian);
34799 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
34801 var tag = dataView.getUint16(offset, littleEndian);
34803 this.exif[tag] = this.getExifValue(
34807 dataView.getUint16(offset + 2, littleEndian), // tag type
34808 dataView.getUint32(offset + 4, littleEndian), // tag length
34813 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
34815 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
34824 Roo.log('Invalid Exif data: Invalid tag type.');
34828 tagSize = tagType.size * length;
34829 // Determine if the value is contained in the dataOffset bytes,
34830 // or if the value at the dataOffset is a pointer to the actual data:
34831 dataOffset = tagSize > 4 ?
34832 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
34833 if (dataOffset + tagSize > dataView.byteLength) {
34834 Roo.log('Invalid Exif data: Invalid data offset.');
34837 if (length === 1) {
34838 return tagType.getValue(dataView, dataOffset, littleEndian);
34841 for (i = 0; i < length; i += 1) {
34842 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
34845 if (tagType.ascii) {
34847 // Concatenate the chars:
34848 for (i = 0; i < values.length; i += 1) {
34850 // Ignore the terminating NULL byte(s):
34851 if (c === '\u0000') {
34863 Roo.apply(Roo.bootstrap.UploadCropbox, {
34865 'Orientation': 0x0112
34869 1: 0, //'top-left',
34871 3: 180, //'bottom-right',
34872 // 4: 'bottom-left',
34874 6: 90, //'right-top',
34875 // 7: 'right-bottom',
34876 8: 270 //'left-bottom'
34880 // byte, 8-bit unsigned int:
34882 getValue: function (dataView, dataOffset) {
34883 return dataView.getUint8(dataOffset);
34887 // ascii, 8-bit byte:
34889 getValue: function (dataView, dataOffset) {
34890 return String.fromCharCode(dataView.getUint8(dataOffset));
34895 // short, 16 bit int:
34897 getValue: function (dataView, dataOffset, littleEndian) {
34898 return dataView.getUint16(dataOffset, littleEndian);
34902 // long, 32 bit int:
34904 getValue: function (dataView, dataOffset, littleEndian) {
34905 return dataView.getUint32(dataOffset, littleEndian);
34909 // rational = two long values, first is numerator, second is denominator:
34911 getValue: function (dataView, dataOffset, littleEndian) {
34912 return dataView.getUint32(dataOffset, littleEndian) /
34913 dataView.getUint32(dataOffset + 4, littleEndian);
34917 // slong, 32 bit signed int:
34919 getValue: function (dataView, dataOffset, littleEndian) {
34920 return dataView.getInt32(dataOffset, littleEndian);
34924 // srational, two slongs, first is numerator, second is denominator:
34926 getValue: function (dataView, dataOffset, littleEndian) {
34927 return dataView.getInt32(dataOffset, littleEndian) /
34928 dataView.getInt32(dataOffset + 4, littleEndian);
34938 cls : 'btn-group roo-upload-cropbox-rotate-left',
34939 action : 'rotate-left',
34943 cls : 'btn btn-default',
34944 html : '<i class="fa fa-undo"></i>'
34950 cls : 'btn-group roo-upload-cropbox-picture',
34951 action : 'picture',
34955 cls : 'btn btn-default',
34956 html : '<i class="fa fa-picture-o"></i>'
34962 cls : 'btn-group roo-upload-cropbox-rotate-right',
34963 action : 'rotate-right',
34967 cls : 'btn btn-default',
34968 html : '<i class="fa fa-repeat"></i>'
34976 cls : 'btn-group roo-upload-cropbox-rotate-left',
34977 action : 'rotate-left',
34981 cls : 'btn btn-default',
34982 html : '<i class="fa fa-undo"></i>'
34988 cls : 'btn-group roo-upload-cropbox-download',
34989 action : 'download',
34993 cls : 'btn btn-default',
34994 html : '<i class="fa fa-download"></i>'
35000 cls : 'btn-group roo-upload-cropbox-crop',
35005 cls : 'btn btn-default',
35006 html : '<i class="fa fa-crop"></i>'
35012 cls : 'btn-group roo-upload-cropbox-trash',
35017 cls : 'btn btn-default',
35018 html : '<i class="fa fa-trash"></i>'
35024 cls : 'btn-group roo-upload-cropbox-rotate-right',
35025 action : 'rotate-right',
35029 cls : 'btn btn-default',
35030 html : '<i class="fa fa-repeat"></i>'
35038 cls : 'btn-group roo-upload-cropbox-rotate-left',
35039 action : 'rotate-left',
35043 cls : 'btn btn-default',
35044 html : '<i class="fa fa-undo"></i>'
35050 cls : 'btn-group roo-upload-cropbox-rotate-right',
35051 action : 'rotate-right',
35055 cls : 'btn btn-default',
35056 html : '<i class="fa fa-repeat"></i>'
35069 * @class Roo.bootstrap.DocumentManager
35070 * @extends Roo.bootstrap.Component
35071 * Bootstrap DocumentManager class
35072 * @cfg {String} paramName default 'imageUpload'
35073 * @cfg {String} toolTipName default 'filename'
35074 * @cfg {String} method default POST
35075 * @cfg {String} url action url
35076 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
35077 * @cfg {Boolean} multiple multiple upload default true
35078 * @cfg {Number} thumbSize default 300
35079 * @cfg {String} fieldLabel
35080 * @cfg {Number} labelWidth default 4
35081 * @cfg {String} labelAlign (left|top) default left
35082 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
35083 * @cfg {Number} labellg set the width of label (1-12)
35084 * @cfg {Number} labelmd set the width of label (1-12)
35085 * @cfg {Number} labelsm set the width of label (1-12)
35086 * @cfg {Number} labelxs set the width of label (1-12)
35089 * Create a new DocumentManager
35090 * @param {Object} config The config object
35093 Roo.bootstrap.DocumentManager = function(config){
35094 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
35097 this.delegates = [];
35102 * Fire when initial the DocumentManager
35103 * @param {Roo.bootstrap.DocumentManager} this
35108 * inspect selected file
35109 * @param {Roo.bootstrap.DocumentManager} this
35110 * @param {File} file
35115 * Fire when xhr load exception
35116 * @param {Roo.bootstrap.DocumentManager} this
35117 * @param {XMLHttpRequest} xhr
35119 "exception" : true,
35121 * @event afterupload
35122 * Fire when xhr load exception
35123 * @param {Roo.bootstrap.DocumentManager} this
35124 * @param {XMLHttpRequest} xhr
35126 "afterupload" : true,
35129 * prepare the form data
35130 * @param {Roo.bootstrap.DocumentManager} this
35131 * @param {Object} formData
35136 * Fire when remove the file
35137 * @param {Roo.bootstrap.DocumentManager} this
35138 * @param {Object} file
35143 * Fire after refresh the file
35144 * @param {Roo.bootstrap.DocumentManager} this
35149 * Fire after click the image
35150 * @param {Roo.bootstrap.DocumentManager} this
35151 * @param {Object} file
35156 * Fire when upload a image and editable set to true
35157 * @param {Roo.bootstrap.DocumentManager} this
35158 * @param {Object} file
35162 * @event beforeselectfile
35163 * Fire before select file
35164 * @param {Roo.bootstrap.DocumentManager} this
35166 "beforeselectfile" : true,
35169 * Fire before process file
35170 * @param {Roo.bootstrap.DocumentManager} this
35171 * @param {Object} file
35175 * @event previewrendered
35176 * Fire when preview rendered
35177 * @param {Roo.bootstrap.DocumentManager} this
35178 * @param {Object} file
35180 "previewrendered" : true,
35183 "previewResize" : true
35188 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
35197 paramName : 'imageUpload',
35198 toolTipName : 'filename',
35201 labelAlign : 'left',
35211 getAutoCreate : function()
35213 var managerWidget = {
35215 cls : 'roo-document-manager',
35219 cls : 'roo-document-manager-selector',
35224 cls : 'roo-document-manager-uploader',
35228 cls : 'roo-document-manager-upload-btn',
35229 html : '<i class="fa fa-plus"></i>'
35240 cls : 'column col-md-12',
35245 if(this.fieldLabel.length){
35250 cls : 'column col-md-12',
35251 html : this.fieldLabel
35255 cls : 'column col-md-12',
35260 if(this.labelAlign == 'left'){
35265 html : this.fieldLabel
35274 if(this.labelWidth > 12){
35275 content[0].style = "width: " + this.labelWidth + 'px';
35278 if(this.labelWidth < 13 && this.labelmd == 0){
35279 this.labelmd = this.labelWidth;
35282 if(this.labellg > 0){
35283 content[0].cls += ' col-lg-' + this.labellg;
35284 content[1].cls += ' col-lg-' + (12 - this.labellg);
35287 if(this.labelmd > 0){
35288 content[0].cls += ' col-md-' + this.labelmd;
35289 content[1].cls += ' col-md-' + (12 - this.labelmd);
35292 if(this.labelsm > 0){
35293 content[0].cls += ' col-sm-' + this.labelsm;
35294 content[1].cls += ' col-sm-' + (12 - this.labelsm);
35297 if(this.labelxs > 0){
35298 content[0].cls += ' col-xs-' + this.labelxs;
35299 content[1].cls += ' col-xs-' + (12 - this.labelxs);
35307 cls : 'row clearfix',
35315 initEvents : function()
35317 this.managerEl = this.el.select('.roo-document-manager', true).first();
35318 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35320 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
35321 this.selectorEl.hide();
35324 this.selectorEl.attr('multiple', 'multiple');
35327 this.selectorEl.on('change', this.onFileSelected, this);
35329 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
35330 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35332 this.uploader.on('click', this.onUploaderClick, this);
35334 this.renderProgressDialog();
35338 window.addEventListener("resize", function() { _this.refresh(); } );
35340 this.fireEvent('initial', this);
35343 renderProgressDialog : function()
35347 this.progressDialog = new Roo.bootstrap.Modal({
35348 cls : 'roo-document-manager-progress-dialog',
35349 allow_close : false,
35360 btnclick : function() {
35361 _this.uploadCancel();
35367 this.progressDialog.render(Roo.get(document.body));
35369 this.progress = new Roo.bootstrap.Progress({
35370 cls : 'roo-document-manager-progress',
35375 this.progress.render(this.progressDialog.getChildContainer());
35377 this.progressBar = new Roo.bootstrap.ProgressBar({
35378 cls : 'roo-document-manager-progress-bar',
35381 aria_valuemax : 12,
35385 this.progressBar.render(this.progress.getChildContainer());
35388 onUploaderClick : function(e)
35390 e.preventDefault();
35392 if(this.fireEvent('beforeselectfile', this) != false){
35393 this.selectorEl.dom.click();
35398 onFileSelected : function(e)
35400 e.preventDefault();
35402 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35406 Roo.each(this.selectorEl.dom.files, function(file){
35407 if(this.fireEvent('inspect', this, file) != false){
35408 this.files.push(file);
35418 this.selectorEl.dom.value = '';
35420 if(!this.files || !this.files.length){
35424 if(this.boxes > 0 && this.files.length > this.boxes){
35425 this.files = this.files.slice(0, this.boxes);
35428 this.uploader.show();
35430 if(this.boxes > 0 && this.files.length > this.boxes - 1){
35431 this.uploader.hide();
35440 Roo.each(this.files, function(file){
35442 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
35443 var f = this.renderPreview(file);
35448 if(file.type.indexOf('image') != -1){
35449 this.delegates.push(
35451 _this.process(file);
35452 }).createDelegate(this)
35460 _this.process(file);
35461 }).createDelegate(this)
35466 this.files = files;
35468 this.delegates = this.delegates.concat(docs);
35470 if(!this.delegates.length){
35475 this.progressBar.aria_valuemax = this.delegates.length;
35482 arrange : function()
35484 if(!this.delegates.length){
35485 this.progressDialog.hide();
35490 var delegate = this.delegates.shift();
35492 this.progressDialog.show();
35494 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
35496 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
35501 refresh : function()
35503 this.uploader.show();
35505 if(this.boxes > 0 && this.files.length > this.boxes - 1){
35506 this.uploader.hide();
35509 Roo.isTouch ? this.closable(false) : this.closable(true);
35511 this.fireEvent('refresh', this);
35514 onRemove : function(e, el, o)
35516 e.preventDefault();
35518 this.fireEvent('remove', this, o);
35522 remove : function(o)
35526 Roo.each(this.files, function(file){
35527 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
35536 this.files = files;
35543 Roo.each(this.files, function(file){
35548 file.target.remove();
35557 onClick : function(e, el, o)
35559 e.preventDefault();
35561 this.fireEvent('click', this, o);
35565 closable : function(closable)
35567 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
35569 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35581 xhrOnLoad : function(xhr)
35583 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
35587 if (xhr.readyState !== 4) {
35589 this.fireEvent('exception', this, xhr);
35593 var response = Roo.decode(xhr.responseText);
35595 if(!response.success){
35597 this.fireEvent('exception', this, xhr);
35601 var file = this.renderPreview(response.data);
35603 this.files.push(file);
35607 this.fireEvent('afterupload', this, xhr);
35611 xhrOnError : function(xhr)
35613 Roo.log('xhr on error');
35615 var response = Roo.decode(xhr.responseText);
35622 process : function(file)
35624 if(this.fireEvent('process', this, file) !== false){
35625 if(this.editable && file.type.indexOf('image') != -1){
35626 this.fireEvent('edit', this, file);
35630 this.uploadStart(file, false);
35637 uploadStart : function(file, crop)
35639 this.xhr = new XMLHttpRequest();
35641 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
35646 file.xhr = this.xhr;
35648 this.managerEl.createChild({
35650 cls : 'roo-document-manager-loading',
35654 tooltip : file.name,
35655 cls : 'roo-document-manager-thumb',
35656 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
35662 this.xhr.open(this.method, this.url, true);
35665 "Accept": "application/json",
35666 "Cache-Control": "no-cache",
35667 "X-Requested-With": "XMLHttpRequest"
35670 for (var headerName in headers) {
35671 var headerValue = headers[headerName];
35673 this.xhr.setRequestHeader(headerName, headerValue);
35679 this.xhr.onload = function()
35681 _this.xhrOnLoad(_this.xhr);
35684 this.xhr.onerror = function()
35686 _this.xhrOnError(_this.xhr);
35689 var formData = new FormData();
35691 formData.append('returnHTML', 'NO');
35694 formData.append('crop', crop);
35697 formData.append(this.paramName, file, file.name);
35704 if(this.fireEvent('prepare', this, formData, options) != false){
35706 if(options.manually){
35710 this.xhr.send(formData);
35714 this.uploadCancel();
35717 uploadCancel : function()
35723 this.delegates = [];
35725 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
35732 renderPreview : function(file)
35734 if(typeof(file.target) != 'undefined' && file.target){
35738 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
35740 var previewEl = this.managerEl.createChild({
35742 cls : 'roo-document-manager-preview',
35746 tooltip : file[this.toolTipName],
35747 cls : 'roo-document-manager-thumb',
35748 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
35753 html : '<i class="fa fa-times-circle"></i>'
35758 var close = previewEl.select('button.close', true).first();
35760 close.on('click', this.onRemove, this, file);
35762 file.target = previewEl;
35764 var image = previewEl.select('img', true).first();
35768 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
35770 image.on('click', this.onClick, this, file);
35772 this.fireEvent('previewrendered', this, file);
35778 onPreviewLoad : function(file, image)
35780 if(typeof(file.target) == 'undefined' || !file.target){
35784 var width = image.dom.naturalWidth || image.dom.width;
35785 var height = image.dom.naturalHeight || image.dom.height;
35787 if(!this.previewResize) {
35791 if(width > height){
35792 file.target.addClass('wide');
35796 file.target.addClass('tall');
35801 uploadFromSource : function(file, crop)
35803 this.xhr = new XMLHttpRequest();
35805 this.managerEl.createChild({
35807 cls : 'roo-document-manager-loading',
35811 tooltip : file.name,
35812 cls : 'roo-document-manager-thumb',
35813 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
35819 this.xhr.open(this.method, this.url, true);
35822 "Accept": "application/json",
35823 "Cache-Control": "no-cache",
35824 "X-Requested-With": "XMLHttpRequest"
35827 for (var headerName in headers) {
35828 var headerValue = headers[headerName];
35830 this.xhr.setRequestHeader(headerName, headerValue);
35836 this.xhr.onload = function()
35838 _this.xhrOnLoad(_this.xhr);
35841 this.xhr.onerror = function()
35843 _this.xhrOnError(_this.xhr);
35846 var formData = new FormData();
35848 formData.append('returnHTML', 'NO');
35850 formData.append('crop', crop);
35852 if(typeof(file.filename) != 'undefined'){
35853 formData.append('filename', file.filename);
35856 if(typeof(file.mimetype) != 'undefined'){
35857 formData.append('mimetype', file.mimetype);
35862 if(this.fireEvent('prepare', this, formData) != false){
35863 this.xhr.send(formData);
35873 * @class Roo.bootstrap.DocumentViewer
35874 * @extends Roo.bootstrap.Component
35875 * Bootstrap DocumentViewer class
35876 * @cfg {Boolean} showDownload (true|false) show download button (default true)
35877 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
35880 * Create a new DocumentViewer
35881 * @param {Object} config The config object
35884 Roo.bootstrap.DocumentViewer = function(config){
35885 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
35890 * Fire after initEvent
35891 * @param {Roo.bootstrap.DocumentViewer} this
35897 * @param {Roo.bootstrap.DocumentViewer} this
35902 * Fire after download button
35903 * @param {Roo.bootstrap.DocumentViewer} this
35908 * Fire after trash button
35909 * @param {Roo.bootstrap.DocumentViewer} this
35916 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
35918 showDownload : true,
35922 getAutoCreate : function()
35926 cls : 'roo-document-viewer',
35930 cls : 'roo-document-viewer-body',
35934 cls : 'roo-document-viewer-thumb',
35938 cls : 'roo-document-viewer-image'
35946 cls : 'roo-document-viewer-footer',
35949 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
35953 cls : 'btn-group roo-document-viewer-download',
35957 cls : 'btn btn-default',
35958 html : '<i class="fa fa-download"></i>'
35964 cls : 'btn-group roo-document-viewer-trash',
35968 cls : 'btn btn-default',
35969 html : '<i class="fa fa-trash"></i>'
35982 initEvents : function()
35984 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
35985 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35987 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
35988 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35990 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
35991 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35993 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
35994 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
35996 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
35997 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
35999 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
36000 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
36002 this.bodyEl.on('click', this.onClick, this);
36003 this.downloadBtn.on('click', this.onDownload, this);
36004 this.trashBtn.on('click', this.onTrash, this);
36006 this.downloadBtn.hide();
36007 this.trashBtn.hide();
36009 if(this.showDownload){
36010 this.downloadBtn.show();
36013 if(this.showTrash){
36014 this.trashBtn.show();
36017 if(!this.showDownload && !this.showTrash) {
36018 this.footerEl.hide();
36023 initial : function()
36025 this.fireEvent('initial', this);
36029 onClick : function(e)
36031 e.preventDefault();
36033 this.fireEvent('click', this);
36036 onDownload : function(e)
36038 e.preventDefault();
36040 this.fireEvent('download', this);
36043 onTrash : function(e)
36045 e.preventDefault();
36047 this.fireEvent('trash', this);
36059 * @class Roo.bootstrap.form.FieldLabel
36060 * @extends Roo.bootstrap.Component
36061 * Bootstrap FieldLabel class
36062 * @cfg {String} html contents of the element
36063 * @cfg {String} tag tag of the element default label
36064 * @cfg {String} cls class of the element
36065 * @cfg {String} target label target
36066 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
36067 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
36068 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
36069 * @cfg {String} iconTooltip default "This field is required"
36070 * @cfg {String} indicatorpos (left|right) default left
36073 * Create a new FieldLabel
36074 * @param {Object} config The config object
36077 Roo.bootstrap.form.FieldLabel = function(config){
36078 Roo.bootstrap.Element.superclass.constructor.call(this, config);
36083 * Fires after the field has been marked as invalid.
36084 * @param {Roo.form.FieldLabel} this
36085 * @param {String} msg The validation message
36090 * Fires after the field has been validated with no errors.
36091 * @param {Roo.form.FieldLabel} this
36097 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component, {
36104 invalidClass : 'has-warning',
36105 validClass : 'has-success',
36106 iconTooltip : 'This field is required',
36107 indicatorpos : 'left',
36109 getAutoCreate : function(){
36112 if (!this.allowBlank) {
36118 cls : 'roo-bootstrap-field-label ' + this.cls,
36123 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
36124 tooltip : this.iconTooltip
36133 if(this.indicatorpos == 'right'){
36136 cls : 'roo-bootstrap-field-label ' + this.cls,
36145 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
36146 tooltip : this.iconTooltip
36155 initEvents: function()
36157 Roo.bootstrap.Element.superclass.initEvents.call(this);
36159 this.indicator = this.indicatorEl();
36161 if(this.indicator){
36162 this.indicator.removeClass('visible');
36163 this.indicator.addClass('invisible');
36166 Roo.bootstrap.form.FieldLabel.register(this);
36169 indicatorEl : function()
36171 var indicator = this.el.select('i.roo-required-indicator',true).first();
36182 * Mark this field as valid
36184 markValid : function()
36186 if(this.indicator){
36187 this.indicator.removeClass('visible');
36188 this.indicator.addClass('invisible');
36190 if (Roo.bootstrap.version == 3) {
36191 this.el.removeClass(this.invalidClass);
36192 this.el.addClass(this.validClass);
36194 this.el.removeClass('is-invalid');
36195 this.el.addClass('is-valid');
36199 this.fireEvent('valid', this);
36203 * Mark this field as invalid
36204 * @param {String} msg The validation message
36206 markInvalid : function(msg)
36208 if(this.indicator){
36209 this.indicator.removeClass('invisible');
36210 this.indicator.addClass('visible');
36212 if (Roo.bootstrap.version == 3) {
36213 this.el.removeClass(this.validClass);
36214 this.el.addClass(this.invalidClass);
36216 this.el.removeClass('is-valid');
36217 this.el.addClass('is-invalid');
36221 this.fireEvent('invalid', this, msg);
36227 Roo.apply(Roo.bootstrap.form.FieldLabel, {
36232 * register a FieldLabel Group
36233 * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
36235 register : function(label)
36237 if(this.groups.hasOwnProperty(label.target)){
36241 this.groups[label.target] = label;
36245 * fetch a FieldLabel Group based on the target
36246 * @param {string} target
36247 * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
36249 get: function(target) {
36250 if (typeof(this.groups[target]) == 'undefined') {
36254 return this.groups[target] ;
36263 * page DateSplitField.
36269 * @class Roo.bootstrap.form.DateSplitField
36270 * @extends Roo.bootstrap.Component
36271 * Bootstrap DateSplitField class
36272 * @cfg {string} fieldLabel - the label associated
36273 * @cfg {Number} labelWidth set the width of label (0-12)
36274 * @cfg {String} labelAlign (top|left)
36275 * @cfg {Boolean} dayAllowBlank (true|false) default false
36276 * @cfg {Boolean} monthAllowBlank (true|false) default false
36277 * @cfg {Boolean} yearAllowBlank (true|false) default false
36278 * @cfg {string} dayPlaceholder
36279 * @cfg {string} monthPlaceholder
36280 * @cfg {string} yearPlaceholder
36281 * @cfg {string} dayFormat default 'd'
36282 * @cfg {string} monthFormat default 'm'
36283 * @cfg {string} yearFormat default 'Y'
36284 * @cfg {Number} labellg set the width of label (1-12)
36285 * @cfg {Number} labelmd set the width of label (1-12)
36286 * @cfg {Number} labelsm set the width of label (1-12)
36287 * @cfg {Number} labelxs set the width of label (1-12)
36291 * Create a new DateSplitField
36292 * @param {Object} config The config object
36295 Roo.bootstrap.form.DateSplitField = function(config){
36296 Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
36302 * getting the data of years
36303 * @param {Roo.bootstrap.form.DateSplitField} this
36304 * @param {Object} years
36309 * getting the data of days
36310 * @param {Roo.bootstrap.form.DateSplitField} this
36311 * @param {Object} days
36316 * Fires after the field has been marked as invalid.
36317 * @param {Roo.form.Field} this
36318 * @param {String} msg The validation message
36323 * Fires after the field has been validated with no errors.
36324 * @param {Roo.form.Field} this
36330 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component, {
36333 labelAlign : 'top',
36335 dayAllowBlank : false,
36336 monthAllowBlank : false,
36337 yearAllowBlank : false,
36338 dayPlaceholder : '',
36339 monthPlaceholder : '',
36340 yearPlaceholder : '',
36344 isFormField : true,
36350 getAutoCreate : function()
36354 cls : 'row roo-date-split-field-group',
36359 cls : 'form-hidden-field roo-date-split-field-group-value',
36365 var labelCls = 'col-md-12';
36366 var contentCls = 'col-md-4';
36368 if(this.fieldLabel){
36372 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
36376 html : this.fieldLabel
36381 if(this.labelAlign == 'left'){
36383 if(this.labelWidth > 12){
36384 label.style = "width: " + this.labelWidth + 'px';
36387 if(this.labelWidth < 13 && this.labelmd == 0){
36388 this.labelmd = this.labelWidth;
36391 if(this.labellg > 0){
36392 labelCls = ' col-lg-' + this.labellg;
36393 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
36396 if(this.labelmd > 0){
36397 labelCls = ' col-md-' + this.labelmd;
36398 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
36401 if(this.labelsm > 0){
36402 labelCls = ' col-sm-' + this.labelsm;
36403 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
36406 if(this.labelxs > 0){
36407 labelCls = ' col-xs-' + this.labelxs;
36408 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
36412 label.cls += ' ' + labelCls;
36414 cfg.cn.push(label);
36417 Roo.each(['day', 'month', 'year'], function(t){
36420 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
36427 inputEl: function ()
36429 return this.el.select('.roo-date-split-field-group-value', true).first();
36432 onRender : function(ct, position)
36436 Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
36438 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
36440 this.dayField = new Roo.bootstrap.form.ComboBox({
36441 allowBlank : this.dayAllowBlank,
36442 alwaysQuery : true,
36443 displayField : 'value',
36446 forceSelection : true,
36448 placeholder : this.dayPlaceholder,
36449 selectOnFocus : true,
36450 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
36451 triggerAction : 'all',
36453 valueField : 'value',
36454 store : new Roo.data.SimpleStore({
36455 data : (function() {
36457 _this.fireEvent('days', _this, days);
36460 fields : [ 'value' ]
36463 select : function (_self, record, index)
36465 _this.setValue(_this.getValue());
36470 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
36472 this.monthField = new Roo.bootstrap.form.MonthField({
36473 after : '<i class=\"fa fa-calendar\"></i>',
36474 allowBlank : this.monthAllowBlank,
36475 placeholder : this.monthPlaceholder,
36478 render : function (_self)
36480 this.el.select('span.input-group-addon', true).first().on('click', function(e){
36481 e.preventDefault();
36485 select : function (_self, oldvalue, newvalue)
36487 _this.setValue(_this.getValue());
36492 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
36494 this.yearField = new Roo.bootstrap.form.ComboBox({
36495 allowBlank : this.yearAllowBlank,
36496 alwaysQuery : true,
36497 displayField : 'value',
36500 forceSelection : true,
36502 placeholder : this.yearPlaceholder,
36503 selectOnFocus : true,
36504 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
36505 triggerAction : 'all',
36507 valueField : 'value',
36508 store : new Roo.data.SimpleStore({
36509 data : (function() {
36511 _this.fireEvent('years', _this, years);
36514 fields : [ 'value' ]
36517 select : function (_self, record, index)
36519 _this.setValue(_this.getValue());
36524 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
36527 setValue : function(v, format)
36529 this.inputEl.dom.value = v;
36531 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
36533 var d = Date.parseDate(v, f);
36540 this.setDay(d.format(this.dayFormat));
36541 this.setMonth(d.format(this.monthFormat));
36542 this.setYear(d.format(this.yearFormat));
36549 setDay : function(v)
36551 this.dayField.setValue(v);
36552 this.inputEl.dom.value = this.getValue();
36557 setMonth : function(v)
36559 this.monthField.setValue(v, true);
36560 this.inputEl.dom.value = this.getValue();
36565 setYear : function(v)
36567 this.yearField.setValue(v);
36568 this.inputEl.dom.value = this.getValue();
36573 getDay : function()
36575 return this.dayField.getValue();
36578 getMonth : function()
36580 return this.monthField.getValue();
36583 getYear : function()
36585 return this.yearField.getValue();
36588 getValue : function()
36590 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
36592 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
36602 this.inputEl.dom.value = '';
36607 validate : function()
36609 var d = this.dayField.validate();
36610 var m = this.monthField.validate();
36611 var y = this.yearField.validate();
36616 (!this.dayAllowBlank && !d) ||
36617 (!this.monthAllowBlank && !m) ||
36618 (!this.yearAllowBlank && !y)
36623 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
36632 this.markInvalid();
36637 markValid : function()
36640 var label = this.el.select('label', true).first();
36641 var icon = this.el.select('i.fa-star', true).first();
36647 this.fireEvent('valid', this);
36651 * Mark this field as invalid
36652 * @param {String} msg The validation message
36654 markInvalid : function(msg)
36657 var label = this.el.select('label', true).first();
36658 var icon = this.el.select('i.fa-star', true).first();
36660 if(label && !icon){
36661 this.el.select('.roo-date-split-field-label', true).createChild({
36663 cls : 'text-danger fa fa-lg fa-star',
36664 tooltip : 'This field is required',
36665 style : 'margin-right:5px;'
36669 this.fireEvent('invalid', this, msg);
36672 clearInvalid : function()
36674 var label = this.el.select('label', true).first();
36675 var icon = this.el.select('i.fa-star', true).first();
36681 this.fireEvent('valid', this);
36684 getName: function()
36694 * @class Roo.bootstrap.LayoutMasonry
36695 * @extends Roo.bootstrap.Component
36696 * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
36697 * Bootstrap Layout Masonry class
36700 * http://masonry.desandro.com
36702 * The idea is to render all the bricks based on vertical width...
36704 * The original code extends 'outlayer' - we might need to use that....
36707 * Create a new Element
36708 * @param {Object} config The config object
36711 Roo.bootstrap.LayoutMasonry = function(config){
36713 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
36717 Roo.bootstrap.LayoutMasonry.register(this);
36723 * Fire after layout the items
36724 * @param {Roo.bootstrap.LayoutMasonry} this
36725 * @param {Roo.EventObject} e
36732 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
36735 * @cfg {Boolean} isLayoutInstant = no animation?
36737 isLayoutInstant : false, // needed?
36740 * @cfg {Number} boxWidth width of the columns
36745 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
36750 * @cfg {Number} padWidth padding below box..
36755 * @cfg {Number} gutter gutter width..
36760 * @cfg {Number} maxCols maximum number of columns
36766 * @cfg {Boolean} isAutoInitial defalut true
36768 isAutoInitial : true,
36773 * @cfg {Boolean} isHorizontal defalut false
36775 isHorizontal : false,
36777 currentSize : null,
36783 bricks: null, //CompositeElement
36787 _isLayoutInited : false,
36789 // isAlternative : false, // only use for vertical layout...
36792 * @cfg {Number} alternativePadWidth padding below box..
36794 alternativePadWidth : 50,
36796 selectedBrick : [],
36798 getAutoCreate : function(){
36800 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
36804 cls: 'blog-masonary-wrapper ' + this.cls,
36806 cls : 'mas-boxes masonary'
36813 getChildContainer: function( )
36815 if (this.boxesEl) {
36816 return this.boxesEl;
36819 this.boxesEl = this.el.select('.mas-boxes').first();
36821 return this.boxesEl;
36825 initEvents : function()
36829 if(this.isAutoInitial){
36830 Roo.log('hook children rendered');
36831 this.on('childrenrendered', function() {
36832 Roo.log('children rendered');
36838 initial : function()
36840 this.selectedBrick = [];
36842 this.currentSize = this.el.getBox(true);
36844 Roo.EventManager.onWindowResize(this.resize, this);
36846 if(!this.isAutoInitial){
36854 //this.layout.defer(500,this);
36858 resize : function()
36860 var cs = this.el.getBox(true);
36863 this.currentSize.width == cs.width &&
36864 this.currentSize.x == cs.x &&
36865 this.currentSize.height == cs.height &&
36866 this.currentSize.y == cs.y
36868 Roo.log("no change in with or X or Y");
36872 this.currentSize = cs;
36878 layout : function()
36880 this._resetLayout();
36882 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
36884 this.layoutItems( isInstant );
36886 this._isLayoutInited = true;
36888 this.fireEvent('layout', this);
36892 _resetLayout : function()
36894 if(this.isHorizontal){
36895 this.horizontalMeasureColumns();
36899 this.verticalMeasureColumns();
36903 verticalMeasureColumns : function()
36905 this.getContainerWidth();
36907 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
36908 // this.colWidth = Math.floor(this.containerWidth * 0.8);
36912 var boxWidth = this.boxWidth + this.padWidth;
36914 if(this.containerWidth < this.boxWidth){
36915 boxWidth = this.containerWidth
36918 var containerWidth = this.containerWidth;
36920 var cols = Math.floor(containerWidth / boxWidth);
36922 this.cols = Math.max( cols, 1 );
36924 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36926 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
36928 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
36930 this.colWidth = boxWidth + avail - this.padWidth;
36932 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
36933 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
36936 horizontalMeasureColumns : function()
36938 this.getContainerWidth();
36940 var boxWidth = this.boxWidth;
36942 if(this.containerWidth < boxWidth){
36943 boxWidth = this.containerWidth;
36946 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
36948 this.el.setHeight(boxWidth);
36952 getContainerWidth : function()
36954 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
36957 layoutItems : function( isInstant )
36959 Roo.log(this.bricks);
36961 var items = Roo.apply([], this.bricks);
36963 if(this.isHorizontal){
36964 this._horizontalLayoutItems( items , isInstant );
36968 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
36969 // this._verticalAlternativeLayoutItems( items , isInstant );
36973 this._verticalLayoutItems( items , isInstant );
36977 _verticalLayoutItems : function ( items , isInstant)
36979 if ( !items || !items.length ) {
36984 ['xs', 'xs', 'xs', 'tall'],
36985 ['xs', 'xs', 'tall'],
36986 ['xs', 'xs', 'sm'],
36987 ['xs', 'xs', 'xs'],
36993 ['sm', 'xs', 'xs'],
36997 ['tall', 'xs', 'xs', 'xs'],
36998 ['tall', 'xs', 'xs'],
37010 Roo.each(items, function(item, k){
37012 switch (item.size) {
37013 // these layouts take up a full box,
37024 boxes.push([item]);
37047 var filterPattern = function(box, length)
37055 var pattern = box.slice(0, length);
37059 Roo.each(pattern, function(i){
37060 format.push(i.size);
37063 Roo.each(standard, function(s){
37065 if(String(s) != String(format)){
37074 if(!match && length == 1){
37079 filterPattern(box, length - 1);
37083 queue.push(pattern);
37085 box = box.slice(length, box.length);
37087 filterPattern(box, 4);
37093 Roo.each(boxes, function(box, k){
37099 if(box.length == 1){
37104 filterPattern(box, 4);
37108 this._processVerticalLayoutQueue( queue, isInstant );
37112 // _verticalAlternativeLayoutItems : function( items , isInstant )
37114 // if ( !items || !items.length ) {
37118 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
37122 _horizontalLayoutItems : function ( items , isInstant)
37124 if ( !items || !items.length || items.length < 3) {
37130 var eItems = items.slice(0, 3);
37132 items = items.slice(3, items.length);
37135 ['xs', 'xs', 'xs', 'wide'],
37136 ['xs', 'xs', 'wide'],
37137 ['xs', 'xs', 'sm'],
37138 ['xs', 'xs', 'xs'],
37144 ['sm', 'xs', 'xs'],
37148 ['wide', 'xs', 'xs', 'xs'],
37149 ['wide', 'xs', 'xs'],
37162 Roo.each(items, function(item, k){
37164 switch (item.size) {
37175 boxes.push([item]);
37199 var filterPattern = function(box, length)
37207 var pattern = box.slice(0, length);
37211 Roo.each(pattern, function(i){
37212 format.push(i.size);
37215 Roo.each(standard, function(s){
37217 if(String(s) != String(format)){
37226 if(!match && length == 1){
37231 filterPattern(box, length - 1);
37235 queue.push(pattern);
37237 box = box.slice(length, box.length);
37239 filterPattern(box, 4);
37245 Roo.each(boxes, function(box, k){
37251 if(box.length == 1){
37256 filterPattern(box, 4);
37263 var pos = this.el.getBox(true);
37267 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37269 var hit_end = false;
37271 Roo.each(queue, function(box){
37275 Roo.each(box, function(b){
37277 b.el.setVisibilityMode(Roo.Element.DISPLAY);
37287 Roo.each(box, function(b){
37289 b.el.setVisibilityMode(Roo.Element.DISPLAY);
37292 mx = Math.max(mx, b.x);
37296 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
37300 Roo.each(box, function(b){
37302 b.el.setVisibilityMode(Roo.Element.DISPLAY);
37316 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
37319 /** Sets position of item in DOM
37320 * @param {Element} item
37321 * @param {Number} x - horizontal position
37322 * @param {Number} y - vertical position
37323 * @param {Boolean} isInstant - disables transitions
37325 _processVerticalLayoutQueue : function( queue, isInstant )
37327 var pos = this.el.getBox(true);
37332 for (var i = 0; i < this.cols; i++){
37336 Roo.each(queue, function(box, k){
37338 var col = k % this.cols;
37340 Roo.each(box, function(b,kk){
37342 b.el.position('absolute');
37344 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37345 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37347 if(b.size == 'md-left' || b.size == 'md-right'){
37348 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
37349 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
37352 b.el.setWidth(width);
37353 b.el.setHeight(height);
37355 b.el.select('iframe',true).setSize(width,height);
37359 for (var i = 0; i < this.cols; i++){
37361 if(maxY[i] < maxY[col]){
37366 col = Math.min(col, i);
37370 x = pos.x + col * (this.colWidth + this.padWidth);
37374 var positions = [];
37376 switch (box.length){
37378 positions = this.getVerticalOneBoxColPositions(x, y, box);
37381 positions = this.getVerticalTwoBoxColPositions(x, y, box);
37384 positions = this.getVerticalThreeBoxColPositions(x, y, box);
37387 positions = this.getVerticalFourBoxColPositions(x, y, box);
37393 Roo.each(box, function(b,kk){
37395 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
37397 var sz = b.el.getSize();
37399 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
37407 for (var i = 0; i < this.cols; i++){
37408 mY = Math.max(mY, maxY[i]);
37411 this.el.setHeight(mY - pos.y);
37415 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
37417 // var pos = this.el.getBox(true);
37420 // var maxX = pos.right;
37422 // var maxHeight = 0;
37424 // Roo.each(items, function(item, k){
37428 // item.el.position('absolute');
37430 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
37432 // item.el.setWidth(width);
37434 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
37436 // item.el.setHeight(height);
37439 // item.el.setXY([x, y], isInstant ? false : true);
37441 // item.el.setXY([maxX - width, y], isInstant ? false : true);
37444 // y = y + height + this.alternativePadWidth;
37446 // maxHeight = maxHeight + height + this.alternativePadWidth;
37450 // this.el.setHeight(maxHeight);
37454 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
37456 var pos = this.el.getBox(true);
37461 var maxX = pos.right;
37463 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
37465 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37467 Roo.each(queue, function(box, k){
37469 Roo.each(box, function(b, kk){
37471 b.el.position('absolute');
37473 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37474 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37476 if(b.size == 'md-left' || b.size == 'md-right'){
37477 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
37478 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
37481 b.el.setWidth(width);
37482 b.el.setHeight(height);
37490 var positions = [];
37492 switch (box.length){
37494 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
37497 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
37500 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
37503 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
37509 Roo.each(box, function(b,kk){
37511 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
37513 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
37521 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
37523 Roo.each(eItems, function(b,k){
37525 b.size = (k == 0) ? 'sm' : 'xs';
37526 b.x = (k == 0) ? 2 : 1;
37527 b.y = (k == 0) ? 2 : 1;
37529 b.el.position('absolute');
37531 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37533 b.el.setWidth(width);
37535 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37537 b.el.setHeight(height);
37541 var positions = [];
37544 x : maxX - this.unitWidth * 2 - this.gutter,
37549 x : maxX - this.unitWidth,
37550 y : minY + (this.unitWidth + this.gutter) * 2
37554 x : maxX - this.unitWidth * 3 - this.gutter * 2,
37558 Roo.each(eItems, function(b,k){
37560 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
37566 getVerticalOneBoxColPositions : function(x, y, box)
37570 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
37572 if(box[0].size == 'md-left'){
37576 if(box[0].size == 'md-right'){
37581 x : x + (this.unitWidth + this.gutter) * rand,
37588 getVerticalTwoBoxColPositions : function(x, y, box)
37592 if(box[0].size == 'xs'){
37596 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
37600 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
37614 x : x + (this.unitWidth + this.gutter) * 2,
37615 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
37622 getVerticalThreeBoxColPositions : function(x, y, box)
37626 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
37634 x : x + (this.unitWidth + this.gutter) * 1,
37639 x : x + (this.unitWidth + this.gutter) * 2,
37647 if(box[0].size == 'xs' && box[1].size == 'xs'){
37656 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
37660 x : x + (this.unitWidth + this.gutter) * 1,
37674 x : x + (this.unitWidth + this.gutter) * 2,
37679 x : x + (this.unitWidth + this.gutter) * 2,
37680 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
37687 getVerticalFourBoxColPositions : function(x, y, box)
37691 if(box[0].size == 'xs'){
37700 y : y + (this.unitHeight + this.gutter) * 1
37705 y : y + (this.unitHeight + this.gutter) * 2
37709 x : x + (this.unitWidth + this.gutter) * 1,
37723 x : x + (this.unitWidth + this.gutter) * 2,
37728 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
37729 y : y + (this.unitHeight + this.gutter) * 1
37733 x : x + (this.unitWidth + this.gutter) * 2,
37734 y : y + (this.unitWidth + this.gutter) * 2
37741 getHorizontalOneBoxColPositions : function(maxX, minY, box)
37745 if(box[0].size == 'md-left'){
37747 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
37754 if(box[0].size == 'md-right'){
37756 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
37757 y : minY + (this.unitWidth + this.gutter) * 1
37763 var rand = Math.floor(Math.random() * (4 - box[0].y));
37766 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37767 y : minY + (this.unitWidth + this.gutter) * rand
37774 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
37778 if(box[0].size == 'xs'){
37781 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37786 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37787 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
37795 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37800 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37801 y : minY + (this.unitWidth + this.gutter) * 2
37808 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
37812 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
37815 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37820 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37821 y : minY + (this.unitWidth + this.gutter) * 1
37825 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
37826 y : minY + (this.unitWidth + this.gutter) * 2
37833 if(box[0].size == 'xs' && box[1].size == 'xs'){
37836 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37841 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37846 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
37847 y : minY + (this.unitWidth + this.gutter) * 1
37855 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37860 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37861 y : minY + (this.unitWidth + this.gutter) * 2
37865 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
37866 y : minY + (this.unitWidth + this.gutter) * 2
37873 getHorizontalFourBoxColPositions : function(maxX, minY, box)
37877 if(box[0].size == 'xs'){
37880 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37885 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37890 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),
37895 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
37896 y : minY + (this.unitWidth + this.gutter) * 1
37904 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37909 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37910 y : minY + (this.unitWidth + this.gutter) * 2
37914 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
37915 y : minY + (this.unitWidth + this.gutter) * 2
37919 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),
37920 y : minY + (this.unitWidth + this.gutter) * 2
37928 * remove a Masonry Brick
37929 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
37931 removeBrick : function(brick_id)
37937 for (var i = 0; i<this.bricks.length; i++) {
37938 if (this.bricks[i].id == brick_id) {
37939 this.bricks.splice(i,1);
37940 this.el.dom.removeChild(Roo.get(brick_id).dom);
37947 * adds a Masonry Brick
37948 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
37950 addBrick : function(cfg)
37952 var cn = new Roo.bootstrap.MasonryBrick(cfg);
37953 //this.register(cn);
37954 cn.parentId = this.id;
37955 cn.render(this.el);
37960 * register a Masonry Brick
37961 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
37964 register : function(brick)
37966 this.bricks.push(brick);
37967 brick.masonryId = this.id;
37971 * clear all the Masonry Brick
37973 clearAll : function()
37976 //this.getChildContainer().dom.innerHTML = "";
37977 this.el.dom.innerHTML = '';
37980 getSelected : function()
37982 if (!this.selectedBrick) {
37986 return this.selectedBrick;
37990 Roo.apply(Roo.bootstrap.LayoutMasonry, {
37994 * register a Masonry Layout
37995 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
37998 register : function(layout)
38000 this.groups[layout.id] = layout;
38003 * fetch a Masonry Layout based on the masonry layout ID
38004 * @param {string} the masonry layout to add
38005 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
38008 get: function(layout_id) {
38009 if (typeof(this.groups[layout_id]) == 'undefined') {
38012 return this.groups[layout_id] ;
38024 * http://masonry.desandro.com
38026 * The idea is to render all the bricks based on vertical width...
38028 * The original code extends 'outlayer' - we might need to use that....
38034 * @class Roo.bootstrap.LayoutMasonryAuto
38035 * @extends Roo.bootstrap.Component
38036 * Bootstrap Layout Masonry class
38039 * Create a new Element
38040 * @param {Object} config The config object
38043 Roo.bootstrap.LayoutMasonryAuto = function(config){
38044 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
38047 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
38050 * @cfg {Boolean} isFitWidth - resize the width..
38052 isFitWidth : false, // options..
38054 * @cfg {Boolean} isOriginLeft = left align?
38056 isOriginLeft : true,
38058 * @cfg {Boolean} isOriginTop = top align?
38060 isOriginTop : false,
38062 * @cfg {Boolean} isLayoutInstant = no animation?
38064 isLayoutInstant : false, // needed?
38066 * @cfg {Boolean} isResizingContainer = not sure if this is used..
38068 isResizingContainer : true,
38070 * @cfg {Number} columnWidth width of the columns
38076 * @cfg {Number} maxCols maximum number of columns
38081 * @cfg {Number} padHeight padding below box..
38087 * @cfg {Boolean} isAutoInitial defalut true
38090 isAutoInitial : true,
38096 initialColumnWidth : 0,
38097 currentSize : null,
38099 colYs : null, // array.
38106 bricks: null, //CompositeElement
38107 cols : 0, // array?
38108 // element : null, // wrapped now this.el
38109 _isLayoutInited : null,
38112 getAutoCreate : function(){
38116 cls: 'blog-masonary-wrapper ' + this.cls,
38118 cls : 'mas-boxes masonary'
38125 getChildContainer: function( )
38127 if (this.boxesEl) {
38128 return this.boxesEl;
38131 this.boxesEl = this.el.select('.mas-boxes').first();
38133 return this.boxesEl;
38137 initEvents : function()
38141 if(this.isAutoInitial){
38142 Roo.log('hook children rendered');
38143 this.on('childrenrendered', function() {
38144 Roo.log('children rendered');
38151 initial : function()
38153 this.reloadItems();
38155 this.currentSize = this.el.getBox(true);
38157 /// was window resize... - let's see if this works..
38158 Roo.EventManager.onWindowResize(this.resize, this);
38160 if(!this.isAutoInitial){
38165 this.layout.defer(500,this);
38168 reloadItems: function()
38170 this.bricks = this.el.select('.masonry-brick', true);
38172 this.bricks.each(function(b) {
38173 //Roo.log(b.getSize());
38174 if (!b.attr('originalwidth')) {
38175 b.attr('originalwidth', b.getSize().width);
38180 Roo.log(this.bricks.elements.length);
38183 resize : function()
38186 var cs = this.el.getBox(true);
38188 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
38189 Roo.log("no change in with or X");
38192 this.currentSize = cs;
38196 layout : function()
38199 this._resetLayout();
38200 //this._manageStamps();
38202 // don't animate first layout
38203 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38204 this.layoutItems( isInstant );
38206 // flag for initalized
38207 this._isLayoutInited = true;
38210 layoutItems : function( isInstant )
38212 //var items = this._getItemsForLayout( this.items );
38213 // original code supports filtering layout items.. we just ignore it..
38215 this._layoutItems( this.bricks , isInstant );
38217 this._postLayout();
38219 _layoutItems : function ( items , isInstant)
38221 //this.fireEvent( 'layout', this, items );
38224 if ( !items || !items.elements.length ) {
38225 // no items, emit event with empty array
38230 items.each(function(item) {
38231 Roo.log("layout item");
38233 // get x/y object from method
38234 var position = this._getItemLayoutPosition( item );
38236 position.item = item;
38237 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
38238 queue.push( position );
38241 this._processLayoutQueue( queue );
38243 /** Sets position of item in DOM
38244 * @param {Element} item
38245 * @param {Number} x - horizontal position
38246 * @param {Number} y - vertical position
38247 * @param {Boolean} isInstant - disables transitions
38249 _processLayoutQueue : function( queue )
38251 for ( var i=0, len = queue.length; i < len; i++ ) {
38252 var obj = queue[i];
38253 obj.item.position('absolute');
38254 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
38260 * Any logic you want to do after each layout,
38261 * i.e. size the container
38263 _postLayout : function()
38265 this.resizeContainer();
38268 resizeContainer : function()
38270 if ( !this.isResizingContainer ) {
38273 var size = this._getContainerSize();
38275 this.el.setSize(size.width,size.height);
38276 this.boxesEl.setSize(size.width,size.height);
38282 _resetLayout : function()
38284 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
38285 this.colWidth = this.el.getWidth();
38286 //this.gutter = this.el.getWidth();
38288 this.measureColumns();
38294 this.colYs.push( 0 );
38300 measureColumns : function()
38302 this.getContainerWidth();
38303 // if columnWidth is 0, default to outerWidth of first item
38304 if ( !this.columnWidth ) {
38305 var firstItem = this.bricks.first();
38306 Roo.log(firstItem);
38307 this.columnWidth = this.containerWidth;
38308 if (firstItem && firstItem.attr('originalwidth') ) {
38309 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
38311 // columnWidth fall back to item of first element
38312 Roo.log("set column width?");
38313 this.initialColumnWidth = this.columnWidth ;
38315 // if first elem has no width, default to size of container
38320 if (this.initialColumnWidth) {
38321 this.columnWidth = this.initialColumnWidth;
38326 // column width is fixed at the top - however if container width get's smaller we should
38329 // this bit calcs how man columns..
38331 var columnWidth = this.columnWidth += this.gutter;
38333 // calculate columns
38334 var containerWidth = this.containerWidth + this.gutter;
38336 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
38337 // fix rounding errors, typically with gutters
38338 var excess = columnWidth - containerWidth % columnWidth;
38341 // if overshoot is less than a pixel, round up, otherwise floor it
38342 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
38343 cols = Math[ mathMethod ]( cols );
38344 this.cols = Math.max( cols, 1 );
38345 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38347 // padding positioning..
38348 var totalColWidth = this.cols * this.columnWidth;
38349 var padavail = this.containerWidth - totalColWidth;
38350 // so for 2 columns - we need 3 'pads'
38352 var padNeeded = (1+this.cols) * this.padWidth;
38354 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
38356 this.columnWidth += padExtra
38357 //this.padWidth = Math.floor(padavail / ( this.cols));
38359 // adjust colum width so that padding is fixed??
38361 // we have 3 columns ... total = width * 3
38362 // we have X left over... that should be used by
38364 //if (this.expandC) {
38372 getContainerWidth : function()
38374 /* // container is parent if fit width
38375 var container = this.isFitWidth ? this.element.parentNode : this.element;
38376 // check that this.size and size are there
38377 // IE8 triggers resize on body size change, so they might not be
38379 var size = getSize( container ); //FIXME
38380 this.containerWidth = size && size.innerWidth; //FIXME
38383 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
38387 _getItemLayoutPosition : function( item ) // what is item?
38389 // we resize the item to our columnWidth..
38391 item.setWidth(this.columnWidth);
38392 item.autoBoxAdjust = false;
38394 var sz = item.getSize();
38396 // how many columns does this brick span
38397 var remainder = this.containerWidth % this.columnWidth;
38399 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
38400 // round if off by 1 pixel, otherwise use ceil
38401 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
38402 colSpan = Math.min( colSpan, this.cols );
38404 // normally this should be '1' as we dont' currently allow multi width columns..
38406 var colGroup = this._getColGroup( colSpan );
38407 // get the minimum Y value from the columns
38408 var minimumY = Math.min.apply( Math, colGroup );
38409 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
38411 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
38413 // position the brick
38415 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
38416 y: this.currentSize.y + minimumY + this.padHeight
38420 // apply setHeight to necessary columns
38421 var setHeight = minimumY + sz.height + this.padHeight;
38422 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
38424 var setSpan = this.cols + 1 - colGroup.length;
38425 for ( var i = 0; i < setSpan; i++ ) {
38426 this.colYs[ shortColIndex + i ] = setHeight ;
38433 * @param {Number} colSpan - number of columns the element spans
38434 * @returns {Array} colGroup
38436 _getColGroup : function( colSpan )
38438 if ( colSpan < 2 ) {
38439 // if brick spans only one column, use all the column Ys
38444 // how many different places could this brick fit horizontally
38445 var groupCount = this.cols + 1 - colSpan;
38446 // for each group potential horizontal position
38447 for ( var i = 0; i < groupCount; i++ ) {
38448 // make an array of colY values for that one group
38449 var groupColYs = this.colYs.slice( i, i + colSpan );
38450 // and get the max value of the array
38451 colGroup[i] = Math.max.apply( Math, groupColYs );
38456 _manageStamp : function( stamp )
38458 var stampSize = stamp.getSize();
38459 var offset = stamp.getBox();
38460 // get the columns that this stamp affects
38461 var firstX = this.isOriginLeft ? offset.x : offset.right;
38462 var lastX = firstX + stampSize.width;
38463 var firstCol = Math.floor( firstX / this.columnWidth );
38464 firstCol = Math.max( 0, firstCol );
38466 var lastCol = Math.floor( lastX / this.columnWidth );
38467 // lastCol should not go over if multiple of columnWidth #425
38468 lastCol -= lastX % this.columnWidth ? 0 : 1;
38469 lastCol = Math.min( this.cols - 1, lastCol );
38471 // set colYs to bottom of the stamp
38472 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
38475 for ( var i = firstCol; i <= lastCol; i++ ) {
38476 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
38481 _getContainerSize : function()
38483 this.maxY = Math.max.apply( Math, this.colYs );
38488 if ( this.isFitWidth ) {
38489 size.width = this._getContainerFitWidth();
38495 _getContainerFitWidth : function()
38497 var unusedCols = 0;
38498 // count unused columns
38501 if ( this.colYs[i] !== 0 ) {
38506 // fit container to columns that have been used
38507 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
38510 needsResizeLayout : function()
38512 var previousWidth = this.containerWidth;
38513 this.getContainerWidth();
38514 return previousWidth !== this.containerWidth;
38529 * @class Roo.bootstrap.MasonryBrick
38530 * @extends Roo.bootstrap.Component
38531 * Bootstrap MasonryBrick class
38534 * Create a new MasonryBrick
38535 * @param {Object} config The config object
38538 Roo.bootstrap.MasonryBrick = function(config){
38540 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
38542 Roo.bootstrap.MasonryBrick.register(this);
38548 * When a MasonryBrick is clcik
38549 * @param {Roo.bootstrap.MasonryBrick} this
38550 * @param {Roo.EventObject} e
38556 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
38559 * @cfg {String} title
38563 * @cfg {String} html
38567 * @cfg {String} bgimage
38571 * @cfg {String} videourl
38575 * @cfg {String} cls
38579 * @cfg {String} href
38583 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
38588 * @cfg {String} placetitle (center|bottom)
38593 * @cfg {Boolean} isFitContainer defalut true
38595 isFitContainer : true,
38598 * @cfg {Boolean} preventDefault defalut false
38600 preventDefault : false,
38603 * @cfg {Boolean} inverse defalut false
38605 maskInverse : false,
38607 getAutoCreate : function()
38609 if(!this.isFitContainer){
38610 return this.getSplitAutoCreate();
38613 var cls = 'masonry-brick masonry-brick-full';
38615 if(this.href.length){
38616 cls += ' masonry-brick-link';
38619 if(this.bgimage.length){
38620 cls += ' masonry-brick-image';
38623 if(this.maskInverse){
38624 cls += ' mask-inverse';
38627 if(!this.html.length && !this.maskInverse && !this.videourl.length){
38628 cls += ' enable-mask';
38632 cls += ' masonry-' + this.size + '-brick';
38635 if(this.placetitle.length){
38637 switch (this.placetitle) {
38639 cls += ' masonry-center-title';
38642 cls += ' masonry-bottom-title';
38649 if(!this.html.length && !this.bgimage.length){
38650 cls += ' masonry-center-title';
38653 if(!this.html.length && this.bgimage.length){
38654 cls += ' masonry-bottom-title';
38659 cls += ' ' + this.cls;
38663 tag: (this.href.length) ? 'a' : 'div',
38668 cls: 'masonry-brick-mask'
38672 cls: 'masonry-brick-paragraph',
38678 if(this.href.length){
38679 cfg.href = this.href;
38682 var cn = cfg.cn[1].cn;
38684 if(this.title.length){
38687 cls: 'masonry-brick-title',
38692 if(this.html.length){
38695 cls: 'masonry-brick-text',
38700 if (!this.title.length && !this.html.length) {
38701 cfg.cn[1].cls += ' hide';
38704 if(this.bgimage.length){
38707 cls: 'masonry-brick-image-view',
38712 if(this.videourl.length){
38713 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
38714 // youtube support only?
38717 cls: 'masonry-brick-image-view',
38720 allowfullscreen : true
38728 getSplitAutoCreate : function()
38730 var cls = 'masonry-brick masonry-brick-split';
38732 if(this.href.length){
38733 cls += ' masonry-brick-link';
38736 if(this.bgimage.length){
38737 cls += ' masonry-brick-image';
38741 cls += ' masonry-' + this.size + '-brick';
38744 switch (this.placetitle) {
38746 cls += ' masonry-center-title';
38749 cls += ' masonry-bottom-title';
38752 if(!this.bgimage.length){
38753 cls += ' masonry-center-title';
38756 if(this.bgimage.length){
38757 cls += ' masonry-bottom-title';
38763 cls += ' ' + this.cls;
38767 tag: (this.href.length) ? 'a' : 'div',
38772 cls: 'masonry-brick-split-head',
38776 cls: 'masonry-brick-paragraph',
38783 cls: 'masonry-brick-split-body',
38789 if(this.href.length){
38790 cfg.href = this.href;
38793 if(this.title.length){
38794 cfg.cn[0].cn[0].cn.push({
38796 cls: 'masonry-brick-title',
38801 if(this.html.length){
38802 cfg.cn[1].cn.push({
38804 cls: 'masonry-brick-text',
38809 if(this.bgimage.length){
38810 cfg.cn[0].cn.push({
38812 cls: 'masonry-brick-image-view',
38817 if(this.videourl.length){
38818 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
38819 // youtube support only?
38820 cfg.cn[0].cn.cn.push({
38822 cls: 'masonry-brick-image-view',
38825 allowfullscreen : true
38832 initEvents: function()
38834 switch (this.size) {
38867 this.el.on('touchstart', this.onTouchStart, this);
38868 this.el.on('touchmove', this.onTouchMove, this);
38869 this.el.on('touchend', this.onTouchEnd, this);
38870 this.el.on('contextmenu', this.onContextMenu, this);
38872 this.el.on('mouseenter' ,this.enter, this);
38873 this.el.on('mouseleave', this.leave, this);
38874 this.el.on('click', this.onClick, this);
38877 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
38878 this.parent().bricks.push(this);
38883 onClick: function(e, el)
38885 var time = this.endTimer - this.startTimer;
38886 // Roo.log(e.preventDefault());
38889 e.preventDefault();
38894 if(!this.preventDefault){
38898 e.preventDefault();
38900 if (this.activeClass != '') {
38901 this.selectBrick();
38904 this.fireEvent('click', this, e);
38907 enter: function(e, el)
38909 e.preventDefault();
38911 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
38915 if(this.bgimage.length && this.html.length){
38916 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
38920 leave: function(e, el)
38922 e.preventDefault();
38924 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
38928 if(this.bgimage.length && this.html.length){
38929 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
38933 onTouchStart: function(e, el)
38935 // e.preventDefault();
38937 this.touchmoved = false;
38939 if(!this.isFitContainer){
38943 if(!this.bgimage.length || !this.html.length){
38947 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
38949 this.timer = new Date().getTime();
38953 onTouchMove: function(e, el)
38955 this.touchmoved = true;
38958 onContextMenu : function(e,el)
38960 e.preventDefault();
38961 e.stopPropagation();
38965 onTouchEnd: function(e, el)
38967 // e.preventDefault();
38969 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
38976 if(!this.bgimage.length || !this.html.length){
38978 if(this.href.length){
38979 window.location.href = this.href;
38985 if(!this.isFitContainer){
38989 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
38991 window.location.href = this.href;
38994 //selection on single brick only
38995 selectBrick : function() {
38997 if (!this.parentId) {
39001 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
39002 var index = m.selectedBrick.indexOf(this.id);
39005 m.selectedBrick.splice(index,1);
39006 this.el.removeClass(this.activeClass);
39010 for(var i = 0; i < m.selectedBrick.length; i++) {
39011 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
39012 b.el.removeClass(b.activeClass);
39015 m.selectedBrick = [];
39017 m.selectedBrick.push(this.id);
39018 this.el.addClass(this.activeClass);
39022 isSelected : function(){
39023 return this.el.hasClass(this.activeClass);
39028 Roo.apply(Roo.bootstrap.MasonryBrick, {
39031 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
39033 * register a Masonry Brick
39034 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39037 register : function(brick)
39039 //this.groups[brick.id] = brick;
39040 this.groups.add(brick.id, brick);
39043 * fetch a masonry brick based on the masonry brick ID
39044 * @param {string} the masonry brick to add
39045 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
39048 get: function(brick_id)
39050 // if (typeof(this.groups[brick_id]) == 'undefined') {
39053 // return this.groups[brick_id] ;
39055 if(this.groups.key(brick_id)) {
39056 return this.groups.key(brick_id);
39074 * @class Roo.bootstrap.Brick
39075 * @extends Roo.bootstrap.Component
39076 * Bootstrap Brick class
39079 * Create a new Brick
39080 * @param {Object} config The config object
39083 Roo.bootstrap.Brick = function(config){
39084 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
39090 * When a Brick is click
39091 * @param {Roo.bootstrap.Brick} this
39092 * @param {Roo.EventObject} e
39098 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
39101 * @cfg {String} title
39105 * @cfg {String} html
39109 * @cfg {String} bgimage
39113 * @cfg {String} cls
39117 * @cfg {String} href
39121 * @cfg {String} video
39125 * @cfg {Boolean} square
39129 getAutoCreate : function()
39131 var cls = 'roo-brick';
39133 if(this.href.length){
39134 cls += ' roo-brick-link';
39137 if(this.bgimage.length){
39138 cls += ' roo-brick-image';
39141 if(!this.html.length && !this.bgimage.length){
39142 cls += ' roo-brick-center-title';
39145 if(!this.html.length && this.bgimage.length){
39146 cls += ' roo-brick-bottom-title';
39150 cls += ' ' + this.cls;
39154 tag: (this.href.length) ? 'a' : 'div',
39159 cls: 'roo-brick-paragraph',
39165 if(this.href.length){
39166 cfg.href = this.href;
39169 var cn = cfg.cn[0].cn;
39171 if(this.title.length){
39174 cls: 'roo-brick-title',
39179 if(this.html.length){
39182 cls: 'roo-brick-text',
39189 if(this.bgimage.length){
39192 cls: 'roo-brick-image-view',
39200 initEvents: function()
39202 if(this.title.length || this.html.length){
39203 this.el.on('mouseenter' ,this.enter, this);
39204 this.el.on('mouseleave', this.leave, this);
39207 Roo.EventManager.onWindowResize(this.resize, this);
39209 if(this.bgimage.length){
39210 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
39211 this.imageEl.on('load', this.onImageLoad, this);
39218 onImageLoad : function()
39223 resize : function()
39225 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
39227 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
39229 if(this.bgimage.length){
39230 var image = this.el.select('.roo-brick-image-view', true).first();
39232 image.setWidth(paragraph.getWidth());
39235 image.setHeight(paragraph.getWidth());
39238 this.el.setHeight(image.getHeight());
39239 paragraph.setHeight(image.getHeight());
39245 enter: function(e, el)
39247 e.preventDefault();
39249 if(this.bgimage.length){
39250 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
39251 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
39255 leave: function(e, el)
39257 e.preventDefault();
39259 if(this.bgimage.length){
39260 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
39261 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
39276 * @class Roo.bootstrap.form.NumberField
39277 * @extends Roo.bootstrap.form.Input
39278 * Bootstrap NumberField class
39284 * Create a new NumberField
39285 * @param {Object} config The config object
39288 Roo.bootstrap.form.NumberField = function(config){
39289 Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
39292 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
39295 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39297 allowDecimals : true,
39299 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39301 decimalSeparator : ".",
39303 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39305 decimalPrecision : 2,
39307 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39309 allowNegative : true,
39312 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
39316 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39318 minValue : Number.NEGATIVE_INFINITY,
39320 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39322 maxValue : Number.MAX_VALUE,
39324 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39326 minText : "The minimum value for this field is {0}",
39328 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39330 maxText : "The maximum value for this field is {0}",
39332 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
39333 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39335 nanText : "{0} is not a valid number",
39337 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
39339 thousandsDelimiter : false,
39341 * @cfg {String} valueAlign alignment of value
39343 valueAlign : "left",
39345 getAutoCreate : function()
39347 var hiddenInput = {
39351 cls: 'hidden-number-input'
39355 hiddenInput.name = this.name;
39360 var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
39362 this.name = hiddenInput.name;
39364 if(cfg.cn.length > 0) {
39365 cfg.cn.push(hiddenInput);
39372 initEvents : function()
39374 Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
39376 var allowed = "0123456789";
39378 if(this.allowDecimals){
39379 allowed += this.decimalSeparator;
39382 if(this.allowNegative){
39386 if(this.thousandsDelimiter) {
39390 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
39392 var keyPress = function(e){
39394 var k = e.getKey();
39396 var c = e.getCharCode();
39399 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39400 allowed.indexOf(String.fromCharCode(c)) === -1
39406 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39410 if(allowed.indexOf(String.fromCharCode(c)) === -1){
39415 this.el.on("keypress", keyPress, this);
39418 validateValue : function(value)
39421 if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
39425 var num = this.parseValue(value);
39428 this.markInvalid(String.format(this.nanText, value));
39432 if(num < this.minValue){
39433 this.markInvalid(String.format(this.minText, this.minValue));
39437 if(num > this.maxValue){
39438 this.markInvalid(String.format(this.maxText, this.maxValue));
39445 getValue : function()
39447 var v = this.hiddenEl().getValue();
39449 return this.fixPrecision(this.parseValue(v));
39452 parseValue : function(value)
39454 if(this.thousandsDelimiter) {
39456 r = new RegExp(",", "g");
39457 value = value.replace(r, "");
39460 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
39461 return isNaN(value) ? '' : value;
39464 fixPrecision : function(value)
39466 if(this.thousandsDelimiter) {
39468 r = new RegExp(",", "g");
39469 value = value.replace(r, "");
39472 var nan = isNaN(value);
39474 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
39475 return nan ? '' : value;
39477 return parseFloat(value).toFixed(this.decimalPrecision);
39480 setValue : function(v)
39482 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
39488 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39490 this.inputEl().dom.value = (v == '') ? '' :
39491 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
39493 if(!this.allowZero && v === '0') {
39494 this.hiddenEl().dom.value = '';
39495 this.inputEl().dom.value = '';
39502 decimalPrecisionFcn : function(v)
39504 return Math.floor(v);
39507 beforeBlur : function()
39509 var v = this.parseValue(this.getRawValue());
39511 if(v || v === 0 || v === ''){
39516 hiddenEl : function()
39518 return this.el.select('input.hidden-number-input',true).first();
39530 * @class Roo.bootstrap.DocumentSlider
39531 * @extends Roo.bootstrap.Component
39532 * Bootstrap DocumentSlider class
39535 * Create a new DocumentViewer
39536 * @param {Object} config The config object
39539 Roo.bootstrap.DocumentSlider = function(config){
39540 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
39547 * Fire after initEvent
39548 * @param {Roo.bootstrap.DocumentSlider} this
39553 * Fire after update
39554 * @param {Roo.bootstrap.DocumentSlider} this
39560 * @param {Roo.bootstrap.DocumentSlider} this
39566 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
39572 getAutoCreate : function()
39576 cls : 'roo-document-slider',
39580 cls : 'roo-document-slider-header',
39584 cls : 'roo-document-slider-header-title'
39590 cls : 'roo-document-slider-body',
39594 cls : 'roo-document-slider-prev',
39598 cls : 'fa fa-chevron-left'
39604 cls : 'roo-document-slider-thumb',
39608 cls : 'roo-document-slider-image'
39614 cls : 'roo-document-slider-next',
39618 cls : 'fa fa-chevron-right'
39630 initEvents : function()
39632 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
39633 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
39635 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
39636 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
39638 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
39639 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
39641 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
39642 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
39644 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
39645 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
39647 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
39648 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
39650 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
39651 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
39653 this.thumbEl.on('click', this.onClick, this);
39655 this.prevIndicator.on('click', this.prev, this);
39657 this.nextIndicator.on('click', this.next, this);
39661 initial : function()
39663 if(this.files.length){
39664 this.indicator = 1;
39668 this.fireEvent('initial', this);
39671 update : function()
39673 this.imageEl.attr('src', this.files[this.indicator - 1]);
39675 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
39677 this.prevIndicator.show();
39679 if(this.indicator == 1){
39680 this.prevIndicator.hide();
39683 this.nextIndicator.show();
39685 if(this.indicator == this.files.length){
39686 this.nextIndicator.hide();
39689 this.thumbEl.scrollTo('top');
39691 this.fireEvent('update', this);
39694 onClick : function(e)
39696 e.preventDefault();
39698 this.fireEvent('click', this);
39703 e.preventDefault();
39705 this.indicator = Math.max(1, this.indicator - 1);
39712 e.preventDefault();
39714 this.indicator = Math.min(this.files.length, this.indicator + 1);
39728 * @class Roo.bootstrap.form.RadioSet
39729 * @extends Roo.bootstrap.form.Input
39730 * @children Roo.bootstrap.form.Radio
39731 * Bootstrap RadioSet class
39732 * @cfg {String} indicatorpos (left|right) default left
39733 * @cfg {Boolean} inline (true|false) inline the element (default true)
39734 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
39736 * Create a new RadioSet
39737 * @param {Object} config The config object
39740 Roo.bootstrap.form.RadioSet = function(config){
39742 Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
39746 Roo.bootstrap.form.RadioSet.register(this);
39751 * Fires when the element is checked or unchecked.
39752 * @param {Roo.bootstrap.form.RadioSet} this This radio
39753 * @param {Roo.bootstrap.form.Radio} item The checked item
39758 * Fires when the element is click.
39759 * @param {Roo.bootstrap.form.RadioSet} this This radio set
39760 * @param {Roo.bootstrap.form.Radio} item The checked item
39761 * @param {Roo.EventObject} e The event object
39768 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input, {
39776 indicatorpos : 'left',
39778 getAutoCreate : function()
39782 cls : 'roo-radio-set-label',
39786 html : this.fieldLabel
39790 if (Roo.bootstrap.version == 3) {
39793 if(this.indicatorpos == 'left'){
39796 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
39797 tooltip : 'This field is required'
39802 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
39803 tooltip : 'This field is required'
39809 cls : 'roo-radio-set-items'
39812 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
39814 if (align === 'left' && this.fieldLabel.length) {
39817 cls : "roo-radio-set-right",
39823 if(this.labelWidth > 12){
39824 label.style = "width: " + this.labelWidth + 'px';
39827 if(this.labelWidth < 13 && this.labelmd == 0){
39828 this.labelmd = this.labelWidth;
39831 if(this.labellg > 0){
39832 label.cls += ' col-lg-' + this.labellg;
39833 items.cls += ' col-lg-' + (12 - this.labellg);
39836 if(this.labelmd > 0){
39837 label.cls += ' col-md-' + this.labelmd;
39838 items.cls += ' col-md-' + (12 - this.labelmd);
39841 if(this.labelsm > 0){
39842 label.cls += ' col-sm-' + this.labelsm;
39843 items.cls += ' col-sm-' + (12 - this.labelsm);
39846 if(this.labelxs > 0){
39847 label.cls += ' col-xs-' + this.labelxs;
39848 items.cls += ' col-xs-' + (12 - this.labelxs);
39854 cls : 'roo-radio-set',
39858 cls : 'roo-radio-set-input',
39861 value : this.value ? this.value : ''
39868 if(this.weight.length){
39869 cfg.cls += ' roo-radio-' + this.weight;
39873 cfg.cls += ' roo-radio-set-inline';
39877 ['xs','sm','md','lg'].map(function(size){
39878 if (settings[size]) {
39879 cfg.cls += ' col-' + size + '-' + settings[size];
39887 initEvents : function()
39889 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
39890 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
39892 if(!this.fieldLabel.length){
39893 this.labelEl.hide();
39896 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
39897 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
39899 this.indicator = this.indicatorEl();
39901 if(this.indicator){
39902 this.indicator.addClass('invisible');
39905 this.originalValue = this.getValue();
39909 inputEl: function ()
39911 return this.el.select('.roo-radio-set-input', true).first();
39914 getChildContainer : function()
39916 return this.itemsEl;
39919 register : function(item)
39921 this.radioes.push(item);
39925 validate : function()
39927 if(this.getVisibilityEl().hasClass('hidden')){
39933 Roo.each(this.radioes, function(i){
39942 if(this.allowBlank) {
39946 if(this.disabled || valid){
39951 this.markInvalid();
39956 markValid : function()
39958 if(this.labelEl.isVisible(true) && this.indicatorEl()){
39959 this.indicatorEl().removeClass('visible');
39960 this.indicatorEl().addClass('invisible');
39964 if (Roo.bootstrap.version == 3) {
39965 this.el.removeClass([this.invalidClass, this.validClass]);
39966 this.el.addClass(this.validClass);
39968 this.el.removeClass(['is-invalid','is-valid']);
39969 this.el.addClass(['is-valid']);
39971 this.fireEvent('valid', this);
39974 markInvalid : function(msg)
39976 if(this.allowBlank || this.disabled){
39980 if(this.labelEl.isVisible(true) && this.indicatorEl()){
39981 this.indicatorEl().removeClass('invisible');
39982 this.indicatorEl().addClass('visible');
39984 if (Roo.bootstrap.version == 3) {
39985 this.el.removeClass([this.invalidClass, this.validClass]);
39986 this.el.addClass(this.invalidClass);
39988 this.el.removeClass(['is-invalid','is-valid']);
39989 this.el.addClass(['is-invalid']);
39992 this.fireEvent('invalid', this, msg);
39996 setValue : function(v, suppressEvent)
39998 if(this.value === v){
40005 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40008 Roo.each(this.radioes, function(i){
40010 i.el.removeClass('checked');
40013 Roo.each(this.radioes, function(i){
40015 if(i.value === v || i.value.toString() === v.toString()){
40017 i.el.addClass('checked');
40019 if(suppressEvent !== true){
40020 this.fireEvent('check', this, i);
40031 clearInvalid : function(){
40033 if(!this.el || this.preventMark){
40037 this.el.removeClass([this.invalidClass]);
40039 this.fireEvent('valid', this);
40044 Roo.apply(Roo.bootstrap.form.RadioSet, {
40048 register : function(set)
40050 this.groups[set.name] = set;
40053 get: function(name)
40055 if (typeof(this.groups[name]) == 'undefined') {
40059 return this.groups[name] ;
40065 * Ext JS Library 1.1.1
40066 * Copyright(c) 2006-2007, Ext JS, LLC.
40068 * Originally Released Under LGPL - original licence link has changed is not relivant.
40071 * <script type="text/javascript">
40076 * @class Roo.bootstrap.SplitBar
40077 * @extends Roo.util.Observable
40078 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
40082 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
40083 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
40084 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
40085 split.minSize = 100;
40086 split.maxSize = 600;
40087 split.animate = true;
40088 split.on('moved', splitterMoved);
40091 * Create a new SplitBar
40092 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
40093 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
40094 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40095 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
40096 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
40097 position of the SplitBar).
40099 Roo.bootstrap.SplitBar = function(cfg){
40104 // dragElement : elm
40105 // resizingElement: el,
40107 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
40108 // placement : Roo.bootstrap.SplitBar.LEFT ,
40109 // existingProxy ???
40112 this.el = Roo.get(cfg.dragElement, true);
40113 this.el.dom.unselectable = "on";
40115 this.resizingEl = Roo.get(cfg.resizingElement, true);
40119 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40120 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
40123 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
40126 * The minimum size of the resizing element. (Defaults to 0)
40132 * The maximum size of the resizing element. (Defaults to 2000)
40135 this.maxSize = 2000;
40138 * Whether to animate the transition to the new size
40141 this.animate = false;
40144 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
40147 this.useShim = false;
40152 if(!cfg.existingProxy){
40154 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
40156 this.proxy = Roo.get(cfg.existingProxy).dom;
40159 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
40162 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
40165 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
40168 this.dragSpecs = {};
40171 * @private The adapter to use to positon and resize elements
40173 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40174 this.adapter.init(this);
40176 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40178 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
40179 this.el.addClass("roo-splitbar-h");
40182 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
40183 this.el.addClass("roo-splitbar-v");
40189 * Fires when the splitter is moved (alias for {@link #event-moved})
40190 * @param {Roo.bootstrap.SplitBar} this
40191 * @param {Number} newSize the new width or height
40196 * Fires when the splitter is moved
40197 * @param {Roo.bootstrap.SplitBar} this
40198 * @param {Number} newSize the new width or height
40202 * @event beforeresize
40203 * Fires before the splitter is dragged
40204 * @param {Roo.bootstrap.SplitBar} this
40206 "beforeresize" : true,
40208 "beforeapply" : true
40211 Roo.util.Observable.call(this);
40214 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
40215 onStartProxyDrag : function(x, y){
40216 this.fireEvent("beforeresize", this);
40218 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
40220 o.enableDisplayMode("block");
40221 // all splitbars share the same overlay
40222 Roo.bootstrap.SplitBar.prototype.overlay = o;
40224 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
40225 this.overlay.show();
40226 Roo.get(this.proxy).setDisplayed("block");
40227 var size = this.adapter.getElementSize(this);
40228 this.activeMinSize = this.getMinimumSize();;
40229 this.activeMaxSize = this.getMaximumSize();;
40230 var c1 = size - this.activeMinSize;
40231 var c2 = Math.max(this.activeMaxSize - size, 0);
40232 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40233 this.dd.resetConstraints();
40234 this.dd.setXConstraint(
40235 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
40236 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
40238 this.dd.setYConstraint(0, 0);
40240 this.dd.resetConstraints();
40241 this.dd.setXConstraint(0, 0);
40242 this.dd.setYConstraint(
40243 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
40244 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
40247 this.dragSpecs.startSize = size;
40248 this.dragSpecs.startPoint = [x, y];
40249 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
40253 * @private Called after the drag operation by the DDProxy
40255 onEndProxyDrag : function(e){
40256 Roo.get(this.proxy).setDisplayed(false);
40257 var endPoint = Roo.lib.Event.getXY(e);
40259 this.overlay.hide();
40262 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40263 newSize = this.dragSpecs.startSize +
40264 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
40265 endPoint[0] - this.dragSpecs.startPoint[0] :
40266 this.dragSpecs.startPoint[0] - endPoint[0]
40269 newSize = this.dragSpecs.startSize +
40270 (this.placement == Roo.bootstrap.SplitBar.TOP ?
40271 endPoint[1] - this.dragSpecs.startPoint[1] :
40272 this.dragSpecs.startPoint[1] - endPoint[1]
40275 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
40276 if(newSize != this.dragSpecs.startSize){
40277 if(this.fireEvent('beforeapply', this, newSize) !== false){
40278 this.adapter.setElementSize(this, newSize);
40279 this.fireEvent("moved", this, newSize);
40280 this.fireEvent("resize", this, newSize);
40286 * Get the adapter this SplitBar uses
40287 * @return The adapter object
40289 getAdapter : function(){
40290 return this.adapter;
40294 * Set the adapter this SplitBar uses
40295 * @param {Object} adapter A SplitBar adapter object
40297 setAdapter : function(adapter){
40298 this.adapter = adapter;
40299 this.adapter.init(this);
40303 * Gets the minimum size for the resizing element
40304 * @return {Number} The minimum size
40306 getMinimumSize : function(){
40307 return this.minSize;
40311 * Sets the minimum size for the resizing element
40312 * @param {Number} minSize The minimum size
40314 setMinimumSize : function(minSize){
40315 this.minSize = minSize;
40319 * Gets the maximum size for the resizing element
40320 * @return {Number} The maximum size
40322 getMaximumSize : function(){
40323 return this.maxSize;
40327 * Sets the maximum size for the resizing element
40328 * @param {Number} maxSize The maximum size
40330 setMaximumSize : function(maxSize){
40331 this.maxSize = maxSize;
40335 * Sets the initialize size for the resizing element
40336 * @param {Number} size The initial size
40338 setCurrentSize : function(size){
40339 var oldAnimate = this.animate;
40340 this.animate = false;
40341 this.adapter.setElementSize(this, size);
40342 this.animate = oldAnimate;
40346 * Destroy this splitbar.
40347 * @param {Boolean} removeEl True to remove the element
40349 destroy : function(removeEl){
40351 this.shim.remove();
40354 this.proxy.parentNode.removeChild(this.proxy);
40362 * @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.
40364 Roo.bootstrap.SplitBar.createProxy = function(dir){
40365 var proxy = new Roo.Element(document.createElement("div"));
40366 proxy.unselectable();
40367 var cls = 'roo-splitbar-proxy';
40368 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
40369 document.body.appendChild(proxy.dom);
40374 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
40375 * Default Adapter. It assumes the splitter and resizing element are not positioned
40376 * elements and only gets/sets the width of the element. Generally used for table based layouts.
40378 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
40381 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
40382 // do nothing for now
40383 init : function(s){
40387 * Called before drag operations to get the current size of the resizing element.
40388 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
40390 getElementSize : function(s){
40391 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40392 return s.resizingEl.getWidth();
40394 return s.resizingEl.getHeight();
40399 * Called after drag operations to set the size of the resizing element.
40400 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
40401 * @param {Number} newSize The new size to set
40402 * @param {Function} onComplete A function to be invoked when resizing is complete
40404 setElementSize : function(s, newSize, onComplete){
40405 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40407 s.resizingEl.setWidth(newSize);
40409 onComplete(s, newSize);
40412 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
40417 s.resizingEl.setHeight(newSize);
40419 onComplete(s, newSize);
40422 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
40429 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
40430 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
40431 * Adapter that moves the splitter element to align with the resized sizing element.
40432 * Used with an absolute positioned SplitBar.
40433 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
40434 * document.body, make sure you assign an id to the body element.
40436 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
40437 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40438 this.container = Roo.get(container);
40441 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
40442 init : function(s){
40443 this.basic.init(s);
40446 getElementSize : function(s){
40447 return this.basic.getElementSize(s);
40450 setElementSize : function(s, newSize, onComplete){
40451 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
40454 moveSplitter : function(s){
40455 var yes = Roo.bootstrap.SplitBar;
40456 switch(s.placement){
40458 s.el.setX(s.resizingEl.getRight());
40461 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
40464 s.el.setY(s.resizingEl.getBottom());
40467 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
40474 * Orientation constant - Create a vertical SplitBar
40478 Roo.bootstrap.SplitBar.VERTICAL = 1;
40481 * Orientation constant - Create a horizontal SplitBar
40485 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
40488 * Placement constant - The resizing element is to the left of the splitter element
40492 Roo.bootstrap.SplitBar.LEFT = 1;
40495 * Placement constant - The resizing element is to the right of the splitter element
40499 Roo.bootstrap.SplitBar.RIGHT = 2;
40502 * Placement constant - The resizing element is positioned above the splitter element
40506 Roo.bootstrap.SplitBar.TOP = 3;
40509 * Placement constant - The resizing element is positioned under splitter element
40513 Roo.bootstrap.SplitBar.BOTTOM = 4;
40516 * Ext JS Library 1.1.1
40517 * Copyright(c) 2006-2007, Ext JS, LLC.
40519 * Originally Released Under LGPL - original licence link has changed is not relivant.
40522 * <script type="text/javascript">
40526 * @class Roo.bootstrap.layout.Manager
40527 * @extends Roo.bootstrap.Component
40529 * Base class for layout managers.
40531 Roo.bootstrap.layout.Manager = function(config)
40533 this.monitorWindowResize = true; // do this before we apply configuration.
40535 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
40541 /** false to disable window resize monitoring @type Boolean */
40547 * Fires when a layout is performed.
40548 * @param {Roo.LayoutManager} this
40552 * @event regionresized
40553 * Fires when the user resizes a region.
40554 * @param {Roo.LayoutRegion} region The resized region
40555 * @param {Number} newSize The new size (width for east/west, height for north/south)
40557 "regionresized" : true,
40559 * @event regioncollapsed
40560 * Fires when a region is collapsed.
40561 * @param {Roo.LayoutRegion} region The collapsed region
40563 "regioncollapsed" : true,
40565 * @event regionexpanded
40566 * Fires when a region is expanded.
40567 * @param {Roo.LayoutRegion} region The expanded region
40569 "regionexpanded" : true
40571 this.updating = false;
40574 this.el = Roo.get(config.el);
40580 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
40585 monitorWindowResize : true,
40591 onRender : function(ct, position)
40594 this.el = Roo.get(ct);
40597 //this.fireEvent('render',this);
40601 initEvents: function()
40605 // ie scrollbar fix
40606 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
40607 document.body.scroll = "no";
40608 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
40609 this.el.position('relative');
40611 this.id = this.el.id;
40612 this.el.addClass("roo-layout-container");
40613 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
40614 if(this.el.dom != document.body ) {
40615 this.el.on('resize', this.layout,this);
40616 this.el.on('show', this.layout,this);
40622 * Returns true if this layout is currently being updated
40623 * @return {Boolean}
40625 isUpdating : function(){
40626 return this.updating;
40630 * Suspend the LayoutManager from doing auto-layouts while
40631 * making multiple add or remove calls
40633 beginUpdate : function(){
40634 this.updating = true;
40638 * Restore auto-layouts and optionally disable the manager from performing a layout
40639 * @param {Boolean} noLayout true to disable a layout update
40641 endUpdate : function(noLayout){
40642 this.updating = false;
40648 layout: function(){
40652 onRegionResized : function(region, newSize){
40653 this.fireEvent("regionresized", region, newSize);
40657 onRegionCollapsed : function(region){
40658 this.fireEvent("regioncollapsed", region);
40661 onRegionExpanded : function(region){
40662 this.fireEvent("regionexpanded", region);
40666 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
40667 * performs box-model adjustments.
40668 * @return {Object} The size as an object {width: (the width), height: (the height)}
40670 getViewSize : function()
40673 if(this.el.dom != document.body){
40674 size = this.el.getSize();
40676 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
40678 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
40679 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40684 * Returns the Element this layout is bound to.
40685 * @return {Roo.Element}
40687 getEl : function(){
40692 * Returns the specified region.
40693 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
40694 * @return {Roo.LayoutRegion}
40696 getRegion : function(target){
40697 return this.regions[target.toLowerCase()];
40700 onWindowResize : function(){
40701 if(this.monitorWindowResize){
40708 * Ext JS Library 1.1.1
40709 * Copyright(c) 2006-2007, Ext JS, LLC.
40711 * Originally Released Under LGPL - original licence link has changed is not relivant.
40714 * <script type="text/javascript">
40717 * @class Roo.bootstrap.layout.Border
40718 * @extends Roo.bootstrap.layout.Manager
40719 * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
40720 * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
40721 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
40722 * please see: examples/bootstrap/nested.html<br><br>
40724 <b>The container the layout is rendered into can be either the body element or any other element.
40725 If it is not the body element, the container needs to either be an absolute positioned element,
40726 or you will need to add "position:relative" to the css of the container. You will also need to specify
40727 the container size if it is not the body element.</b>
40730 * Create a new Border
40731 * @param {Object} config Configuration options
40733 Roo.bootstrap.layout.Border = function(config){
40734 config = config || {};
40735 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
40739 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
40740 if(config[region]){
40741 config[region].region = region;
40742 this.addRegion(config[region]);
40748 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
40750 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
40753 * @cfg {Roo.bootstrap.layout.Region} center region to go in center
40756 * @cfg {Roo.bootstrap.layout.Region} west region to go in west
40759 * @cfg {Roo.bootstrap.layout.Region} east region to go in east
40762 * @cfg {Roo.bootstrap.layout.Region} south region to go in south
40765 * @cfg {Roo.bootstrap.layout.Region} north region to go in north
40771 parent : false, // this might point to a 'nest' or a ???
40774 * Creates and adds a new region if it doesn't already exist.
40775 * @param {String} target The target region key (north, south, east, west or center).
40776 * @param {Object} config The regions config object
40777 * @return {BorderLayoutRegion} The new region
40779 addRegion : function(config)
40781 if(!this.regions[config.region]){
40782 var r = this.factory(config);
40783 this.bindRegion(r);
40785 return this.regions[config.region];
40789 bindRegion : function(r){
40790 this.regions[r.config.region] = r;
40792 r.on("visibilitychange", this.layout, this);
40793 r.on("paneladded", this.layout, this);
40794 r.on("panelremoved", this.layout, this);
40795 r.on("invalidated", this.layout, this);
40796 r.on("resized", this.onRegionResized, this);
40797 r.on("collapsed", this.onRegionCollapsed, this);
40798 r.on("expanded", this.onRegionExpanded, this);
40802 * Performs a layout update.
40804 layout : function()
40806 if(this.updating) {
40810 // render all the rebions if they have not been done alreayd?
40811 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
40812 if(this.regions[region] && !this.regions[region].bodyEl){
40813 this.regions[region].onRender(this.el)
40817 var size = this.getViewSize();
40818 var w = size.width;
40819 var h = size.height;
40824 //var x = 0, y = 0;
40826 var rs = this.regions;
40827 var north = rs["north"];
40828 var south = rs["south"];
40829 var west = rs["west"];
40830 var east = rs["east"];
40831 var center = rs["center"];
40832 //if(this.hideOnLayout){ // not supported anymore
40833 //c.el.setStyle("display", "none");
40835 if(north && north.isVisible()){
40836 var b = north.getBox();
40837 var m = north.getMargins();
40838 b.width = w - (m.left+m.right);
40841 centerY = b.height + b.y + m.bottom;
40842 centerH -= centerY;
40843 north.updateBox(this.safeBox(b));
40845 if(south && south.isVisible()){
40846 var b = south.getBox();
40847 var m = south.getMargins();
40848 b.width = w - (m.left+m.right);
40850 var totalHeight = (b.height + m.top + m.bottom);
40851 b.y = h - totalHeight + m.top;
40852 centerH -= totalHeight;
40853 south.updateBox(this.safeBox(b));
40855 if(west && west.isVisible()){
40856 var b = west.getBox();
40857 var m = west.getMargins();
40858 b.height = centerH - (m.top+m.bottom);
40860 b.y = centerY + m.top;
40861 var totalWidth = (b.width + m.left + m.right);
40862 centerX += totalWidth;
40863 centerW -= totalWidth;
40864 west.updateBox(this.safeBox(b));
40866 if(east && east.isVisible()){
40867 var b = east.getBox();
40868 var m = east.getMargins();
40869 b.height = centerH - (m.top+m.bottom);
40870 var totalWidth = (b.width + m.left + m.right);
40871 b.x = w - totalWidth + m.left;
40872 b.y = centerY + m.top;
40873 centerW -= totalWidth;
40874 east.updateBox(this.safeBox(b));
40877 var m = center.getMargins();
40879 x: centerX + m.left,
40880 y: centerY + m.top,
40881 width: centerW - (m.left+m.right),
40882 height: centerH - (m.top+m.bottom)
40884 //if(this.hideOnLayout){
40885 //center.el.setStyle("display", "block");
40887 center.updateBox(this.safeBox(centerBox));
40890 this.fireEvent("layout", this);
40894 safeBox : function(box){
40895 box.width = Math.max(0, box.width);
40896 box.height = Math.max(0, box.height);
40901 * Adds a ContentPanel (or subclass) to this layout.
40902 * @param {String} target The target region key (north, south, east, west or center).
40903 * @param {Roo.ContentPanel} panel The panel to add
40904 * @return {Roo.ContentPanel} The added panel
40906 add : function(target, panel){
40908 target = target.toLowerCase();
40909 return this.regions[target].add(panel);
40913 * Remove a ContentPanel (or subclass) to this layout.
40914 * @param {String} target The target region key (north, south, east, west or center).
40915 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
40916 * @return {Roo.ContentPanel} The removed panel
40918 remove : function(target, panel){
40919 target = target.toLowerCase();
40920 return this.regions[target].remove(panel);
40924 * Searches all regions for a panel with the specified id
40925 * @param {String} panelId
40926 * @return {Roo.ContentPanel} The panel or null if it wasn't found
40928 findPanel : function(panelId){
40929 var rs = this.regions;
40930 for(var target in rs){
40931 if(typeof rs[target] != "function"){
40932 var p = rs[target].getPanel(panelId);
40942 * Searches all regions for a panel with the specified id and activates (shows) it.
40943 * @param {String/ContentPanel} panelId The panels id or the panel itself
40944 * @return {Roo.ContentPanel} The shown panel or null
40946 showPanel : function(panelId) {
40947 var rs = this.regions;
40948 for(var target in rs){
40949 var r = rs[target];
40950 if(typeof r != "function"){
40951 if(r.hasPanel(panelId)){
40952 return r.showPanel(panelId);
40960 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
40961 * @param {Roo.state.Provider} provider (optional) An alternate state provider
40964 restoreState : function(provider){
40966 provider = Roo.state.Manager;
40968 var sm = new Roo.LayoutStateManager();
40969 sm.init(this, provider);
40975 * Adds a xtype elements to the layout.
40979 xtype : 'ContentPanel',
40986 xtype : 'NestedLayoutPanel',
40992 items : [ ... list of content panels or nested layout panels.. ]
40996 * @param {Object} cfg Xtype definition of item to add.
40998 addxtype : function(cfg)
41000 // basically accepts a pannel...
41001 // can accept a layout region..!?!?
41002 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
41005 // theory? children can only be panels??
41007 //if (!cfg.xtype.match(/Panel$/)) {
41012 if (typeof(cfg.region) == 'undefined') {
41013 Roo.log("Failed to add Panel, region was not set");
41017 var region = cfg.region;
41023 xitems = cfg.items;
41028 if ( region == 'center') {
41029 Roo.log("Center: " + cfg.title);
41035 case 'Content': // ContentPanel (el, cfg)
41036 case 'Scroll': // ContentPanel (el, cfg)
41038 cfg.autoCreate = cfg.autoCreate || true;
41039 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41041 // var el = this.el.createChild();
41042 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
41045 this.add(region, ret);
41049 case 'TreePanel': // our new panel!
41050 cfg.el = this.el.createChild();
41051 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41052 this.add(region, ret);
41057 // create a new Layout (which is a Border Layout...
41059 var clayout = cfg.layout;
41060 clayout.el = this.el.createChild();
41061 clayout.items = clayout.items || [];
41065 // replace this exitems with the clayout ones..
41066 xitems = clayout.items;
41068 // force background off if it's in center...
41069 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
41070 cfg.background = false;
41072 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
41075 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41076 //console.log('adding nested layout panel ' + cfg.toSource());
41077 this.add(region, ret);
41078 nb = {}; /// find first...
41083 // needs grid and region
41085 //var el = this.getRegion(region).el.createChild();
41087 *var el = this.el.createChild();
41088 // create the grid first...
41089 cfg.grid.container = el;
41090 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
41093 if (region == 'center' && this.active ) {
41094 cfg.background = false;
41097 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41099 this.add(region, ret);
41101 if (cfg.background) {
41102 // render grid on panel activation (if panel background)
41103 ret.on('activate', function(gp) {
41104 if (!gp.grid.rendered) {
41105 // gp.grid.render(el);
41109 // cfg.grid.render(el);
41115 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
41116 // it was the old xcomponent building that caused this before.
41117 // espeically if border is the top element in the tree.
41127 if (typeof(Roo[cfg.xtype]) != 'undefined') {
41129 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41130 this.add(region, ret);
41134 throw "Can not add '" + cfg.xtype + "' to Border";
41140 this.beginUpdate();
41144 Roo.each(xitems, function(i) {
41145 region = nb && i.region ? i.region : false;
41147 var add = ret.addxtype(i);
41150 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
41151 if (!i.background) {
41152 abn[region] = nb[region] ;
41159 // make the last non-background panel active..
41160 //if (nb) { Roo.log(abn); }
41163 for(var r in abn) {
41164 region = this.getRegion(r);
41166 // tried using nb[r], but it does not work..
41168 region.showPanel(abn[r]);
41179 factory : function(cfg)
41182 var validRegions = Roo.bootstrap.layout.Border.regions;
41184 var target = cfg.region;
41187 var r = Roo.bootstrap.layout;
41191 return new r.North(cfg);
41193 return new r.South(cfg);
41195 return new r.East(cfg);
41197 return new r.West(cfg);
41199 return new r.Center(cfg);
41201 throw 'Layout region "'+target+'" not supported.';
41208 * Ext JS Library 1.1.1
41209 * Copyright(c) 2006-2007, Ext JS, LLC.
41211 * Originally Released Under LGPL - original licence link has changed is not relivant.
41214 * <script type="text/javascript">
41218 * @class Roo.bootstrap.layout.Basic
41219 * @extends Roo.util.Observable
41220 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
41221 * and does not have a titlebar, tabs or any other features. All it does is size and position
41222 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
41223 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
41224 * @cfg {string} region the region that it inhabits..
41225 * @cfg {bool} skipConfig skip config?
41229 Roo.bootstrap.layout.Basic = function(config){
41231 this.mgr = config.mgr;
41233 this.position = config.region;
41235 var skipConfig = config.skipConfig;
41239 * @scope Roo.BasicLayoutRegion
41243 * @event beforeremove
41244 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
41245 * @param {Roo.LayoutRegion} this
41246 * @param {Roo.ContentPanel} panel The panel
41247 * @param {Object} e The cancel event object
41249 "beforeremove" : true,
41251 * @event invalidated
41252 * Fires when the layout for this region is changed.
41253 * @param {Roo.LayoutRegion} this
41255 "invalidated" : true,
41257 * @event visibilitychange
41258 * Fires when this region is shown or hidden
41259 * @param {Roo.LayoutRegion} this
41260 * @param {Boolean} visibility true or false
41262 "visibilitychange" : true,
41264 * @event paneladded
41265 * Fires when a panel is added.
41266 * @param {Roo.LayoutRegion} this
41267 * @param {Roo.ContentPanel} panel The panel
41269 "paneladded" : true,
41271 * @event panelremoved
41272 * Fires when a panel is removed.
41273 * @param {Roo.LayoutRegion} this
41274 * @param {Roo.ContentPanel} panel The panel
41276 "panelremoved" : true,
41278 * @event beforecollapse
41279 * Fires when this region before collapse.
41280 * @param {Roo.LayoutRegion} this
41282 "beforecollapse" : true,
41285 * Fires when this region is collapsed.
41286 * @param {Roo.LayoutRegion} this
41288 "collapsed" : true,
41291 * Fires when this region is expanded.
41292 * @param {Roo.LayoutRegion} this
41297 * Fires when this region is slid into view.
41298 * @param {Roo.LayoutRegion} this
41300 "slideshow" : true,
41303 * Fires when this region slides out of view.
41304 * @param {Roo.LayoutRegion} this
41306 "slidehide" : true,
41308 * @event panelactivated
41309 * Fires when a panel is activated.
41310 * @param {Roo.LayoutRegion} this
41311 * @param {Roo.ContentPanel} panel The activated panel
41313 "panelactivated" : true,
41316 * Fires when the user resizes this region.
41317 * @param {Roo.LayoutRegion} this
41318 * @param {Number} newSize The new size (width for east/west, height for north/south)
41322 /** A collection of panels in this region. @type Roo.util.MixedCollection */
41323 this.panels = new Roo.util.MixedCollection();
41324 this.panels.getKey = this.getPanelId.createDelegate(this);
41326 this.activePanel = null;
41327 // ensure listeners are added...
41329 if (config.listeners || config.events) {
41330 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
41331 listeners : config.listeners || {},
41332 events : config.events || {}
41336 if(skipConfig !== true){
41337 this.applyConfig(config);
41341 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
41343 getPanelId : function(p){
41347 applyConfig : function(config){
41348 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
41349 this.config = config;
41354 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
41355 * the width, for horizontal (north, south) the height.
41356 * @param {Number} newSize The new width or height
41358 resizeTo : function(newSize){
41359 var el = this.el ? this.el :
41360 (this.activePanel ? this.activePanel.getEl() : null);
41362 switch(this.position){
41365 el.setWidth(newSize);
41366 this.fireEvent("resized", this, newSize);
41370 el.setHeight(newSize);
41371 this.fireEvent("resized", this, newSize);
41377 getBox : function(){
41378 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
41381 getMargins : function(){
41382 return this.margins;
41385 updateBox : function(box){
41387 var el = this.activePanel.getEl();
41388 el.dom.style.left = box.x + "px";
41389 el.dom.style.top = box.y + "px";
41390 this.activePanel.setSize(box.width, box.height);
41394 * Returns the container element for this region.
41395 * @return {Roo.Element}
41397 getEl : function(){
41398 return this.activePanel;
41402 * Returns true if this region is currently visible.
41403 * @return {Boolean}
41405 isVisible : function(){
41406 return this.activePanel ? true : false;
41409 setActivePanel : function(panel){
41410 panel = this.getPanel(panel);
41411 if(this.activePanel && this.activePanel != panel){
41412 this.activePanel.setActiveState(false);
41413 this.activePanel.getEl().setLeftTop(-10000,-10000);
41415 this.activePanel = panel;
41416 panel.setActiveState(true);
41418 panel.setSize(this.box.width, this.box.height);
41420 this.fireEvent("panelactivated", this, panel);
41421 this.fireEvent("invalidated");
41425 * Show the specified panel.
41426 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
41427 * @return {Roo.ContentPanel} The shown panel or null
41429 showPanel : function(panel){
41430 panel = this.getPanel(panel);
41432 this.setActivePanel(panel);
41438 * Get the active panel for this region.
41439 * @return {Roo.ContentPanel} The active panel or null
41441 getActivePanel : function(){
41442 return this.activePanel;
41446 * Add the passed ContentPanel(s)
41447 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
41448 * @return {Roo.ContentPanel} The panel added (if only one was added)
41450 add : function(panel){
41451 if(arguments.length > 1){
41452 for(var i = 0, len = arguments.length; i < len; i++) {
41453 this.add(arguments[i]);
41457 if(this.hasPanel(panel)){
41458 this.showPanel(panel);
41461 var el = panel.getEl();
41462 if(el.dom.parentNode != this.mgr.el.dom){
41463 this.mgr.el.dom.appendChild(el.dom);
41465 if(panel.setRegion){
41466 panel.setRegion(this);
41468 this.panels.add(panel);
41469 el.setStyle("position", "absolute");
41470 if(!panel.background){
41471 this.setActivePanel(panel);
41472 if(this.config.initialSize && this.panels.getCount()==1){
41473 this.resizeTo(this.config.initialSize);
41476 this.fireEvent("paneladded", this, panel);
41481 * Returns true if the panel is in this region.
41482 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41483 * @return {Boolean}
41485 hasPanel : function(panel){
41486 if(typeof panel == "object"){ // must be panel obj
41487 panel = panel.getId();
41489 return this.getPanel(panel) ? true : false;
41493 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
41494 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41495 * @param {Boolean} preservePanel Overrides the config preservePanel option
41496 * @return {Roo.ContentPanel} The panel that was removed
41498 remove : function(panel, preservePanel){
41499 panel = this.getPanel(panel);
41504 this.fireEvent("beforeremove", this, panel, e);
41505 if(e.cancel === true){
41508 var panelId = panel.getId();
41509 this.panels.removeKey(panelId);
41514 * Returns the panel specified or null if it's not in this region.
41515 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41516 * @return {Roo.ContentPanel}
41518 getPanel : function(id){
41519 if(typeof id == "object"){ // must be panel obj
41522 return this.panels.get(id);
41526 * Returns this regions position (north/south/east/west/center).
41529 getPosition: function(){
41530 return this.position;
41534 * Ext JS Library 1.1.1
41535 * Copyright(c) 2006-2007, Ext JS, LLC.
41537 * Originally Released Under LGPL - original licence link has changed is not relivant.
41540 * <script type="text/javascript">
41544 * @class Roo.bootstrap.layout.Region
41545 * @extends Roo.bootstrap.layout.Basic
41546 * This class represents a region in a layout manager.
41548 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
41549 * @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})
41550 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
41551 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
41552 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
41553 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
41554 * @cfg {String} title The title for the region (overrides panel titles)
41555 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
41556 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
41557 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
41558 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
41559 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
41560 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
41561 * the space available, similar to FireFox 1.5 tabs (defaults to false)
41562 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
41563 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
41564 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
41566 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
41567 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
41568 * @cfg {Boolean} disableTabTips True to disable tab tooltips
41569 * @cfg {Number} width For East/West panels
41570 * @cfg {Number} height For North/South panels
41571 * @cfg {Boolean} split To show the splitter
41572 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
41574 * @cfg {string} cls Extra CSS classes to add to region
41576 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
41577 * @cfg {string} region the region that it inhabits..
41580 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
41581 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
41583 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
41584 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
41585 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
41587 Roo.bootstrap.layout.Region = function(config)
41589 this.applyConfig(config);
41591 var mgr = config.mgr;
41592 var pos = config.region;
41593 config.skipConfig = true;
41594 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
41597 this.onRender(mgr.el);
41600 this.visible = true;
41601 this.collapsed = false;
41602 this.unrendered_panels = [];
41605 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
41607 position: '', // set by wrapper (eg. north/south etc..)
41608 unrendered_panels : null, // unrendered panels.
41610 tabPosition : false,
41612 mgr: false, // points to 'Border'
41615 createBody : function(){
41616 /** This region's body element
41617 * @type Roo.Element */
41618 this.bodyEl = this.el.createChild({
41620 cls: "roo-layout-panel-body tab-content" // bootstrap added...
41624 onRender: function(ctr, pos)
41626 var dh = Roo.DomHelper;
41627 /** This region's container element
41628 * @type Roo.Element */
41629 this.el = dh.append(ctr.dom, {
41631 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
41633 /** This region's title element
41634 * @type Roo.Element */
41636 this.titleEl = dh.append(this.el.dom, {
41638 unselectable: "on",
41639 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
41641 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
41642 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
41646 this.titleEl.enableDisplayMode();
41647 /** This region's title text element
41648 * @type HTMLElement */
41649 this.titleTextEl = this.titleEl.dom.firstChild;
41650 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
41652 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
41653 this.closeBtn.enableDisplayMode();
41654 this.closeBtn.on("click", this.closeClicked, this);
41655 this.closeBtn.hide();
41657 this.createBody(this.config);
41658 if(this.config.hideWhenEmpty){
41660 this.on("paneladded", this.validateVisibility, this);
41661 this.on("panelremoved", this.validateVisibility, this);
41663 if(this.autoScroll){
41664 this.bodyEl.setStyle("overflow", "auto");
41666 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
41668 //if(c.titlebar !== false){
41669 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
41670 this.titleEl.hide();
41672 this.titleEl.show();
41673 if(this.config.title){
41674 this.titleTextEl.innerHTML = this.config.title;
41678 if(this.config.collapsed){
41679 this.collapse(true);
41681 if(this.config.hidden){
41685 if (this.unrendered_panels && this.unrendered_panels.length) {
41686 for (var i =0;i< this.unrendered_panels.length; i++) {
41687 this.add(this.unrendered_panels[i]);
41689 this.unrendered_panels = null;
41695 applyConfig : function(c)
41698 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
41699 var dh = Roo.DomHelper;
41700 if(c.titlebar !== false){
41701 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
41702 this.collapseBtn.on("click", this.collapse, this);
41703 this.collapseBtn.enableDisplayMode();
41705 if(c.showPin === true || this.showPin){
41706 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
41707 this.stickBtn.enableDisplayMode();
41708 this.stickBtn.on("click", this.expand, this);
41709 this.stickBtn.hide();
41714 /** This region's collapsed element
41715 * @type Roo.Element */
41718 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
41719 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
41722 if(c.floatable !== false){
41723 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
41724 this.collapsedEl.on("click", this.collapseClick, this);
41727 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
41728 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
41729 id: "message", unselectable: "on", style:{"float":"left"}});
41730 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
41732 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
41733 this.expandBtn.on("click", this.expand, this);
41737 if(this.collapseBtn){
41738 this.collapseBtn.setVisible(c.collapsible == true);
41741 this.cmargins = c.cmargins || this.cmargins ||
41742 (this.position == "west" || this.position == "east" ?
41743 {top: 0, left: 2, right:2, bottom: 0} :
41744 {top: 2, left: 0, right:0, bottom: 2});
41746 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
41749 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
41751 this.autoScroll = c.autoScroll || false;
41756 this.duration = c.duration || .30;
41757 this.slideDuration = c.slideDuration || .45;
41762 * Returns true if this region is currently visible.
41763 * @return {Boolean}
41765 isVisible : function(){
41766 return this.visible;
41770 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
41771 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
41773 //setCollapsedTitle : function(title){
41774 // title = title || " ";
41775 // if(this.collapsedTitleTextEl){
41776 // this.collapsedTitleTextEl.innerHTML = title;
41780 getBox : function(){
41782 // if(!this.collapsed){
41783 b = this.el.getBox(false, true);
41785 // b = this.collapsedEl.getBox(false, true);
41790 getMargins : function(){
41791 return this.margins;
41792 //return this.collapsed ? this.cmargins : this.margins;
41795 highlight : function(){
41796 this.el.addClass("x-layout-panel-dragover");
41799 unhighlight : function(){
41800 this.el.removeClass("x-layout-panel-dragover");
41803 updateBox : function(box)
41805 if (!this.bodyEl) {
41806 return; // not rendered yet..
41810 if(!this.collapsed){
41811 this.el.dom.style.left = box.x + "px";
41812 this.el.dom.style.top = box.y + "px";
41813 this.updateBody(box.width, box.height);
41815 this.collapsedEl.dom.style.left = box.x + "px";
41816 this.collapsedEl.dom.style.top = box.y + "px";
41817 this.collapsedEl.setSize(box.width, box.height);
41820 this.tabs.autoSizeTabs();
41824 updateBody : function(w, h)
41827 this.el.setWidth(w);
41828 w -= this.el.getBorderWidth("rl");
41829 if(this.config.adjustments){
41830 w += this.config.adjustments[0];
41833 if(h !== null && h > 0){
41834 this.el.setHeight(h);
41835 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
41836 h -= this.el.getBorderWidth("tb");
41837 if(this.config.adjustments){
41838 h += this.config.adjustments[1];
41840 this.bodyEl.setHeight(h);
41842 h = this.tabs.syncHeight(h);
41845 if(this.panelSize){
41846 w = w !== null ? w : this.panelSize.width;
41847 h = h !== null ? h : this.panelSize.height;
41849 if(this.activePanel){
41850 var el = this.activePanel.getEl();
41851 w = w !== null ? w : el.getWidth();
41852 h = h !== null ? h : el.getHeight();
41853 this.panelSize = {width: w, height: h};
41854 this.activePanel.setSize(w, h);
41856 if(Roo.isIE && this.tabs){
41857 this.tabs.el.repaint();
41862 * Returns the container element for this region.
41863 * @return {Roo.Element}
41865 getEl : function(){
41870 * Hides this region.
41873 //if(!this.collapsed){
41874 this.el.dom.style.left = "-2000px";
41877 // this.collapsedEl.dom.style.left = "-2000px";
41878 // this.collapsedEl.hide();
41880 this.visible = false;
41881 this.fireEvent("visibilitychange", this, false);
41885 * Shows this region if it was previously hidden.
41888 //if(!this.collapsed){
41891 // this.collapsedEl.show();
41893 this.visible = true;
41894 this.fireEvent("visibilitychange", this, true);
41897 closeClicked : function(){
41898 if(this.activePanel){
41899 this.remove(this.activePanel);
41903 collapseClick : function(e){
41905 e.stopPropagation();
41908 e.stopPropagation();
41914 * Collapses this region.
41915 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
41918 collapse : function(skipAnim, skipCheck = false){
41919 if(this.collapsed) {
41923 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
41925 this.collapsed = true;
41927 this.split.el.hide();
41929 if(this.config.animate && skipAnim !== true){
41930 this.fireEvent("invalidated", this);
41931 this.animateCollapse();
41933 this.el.setLocation(-20000,-20000);
41935 this.collapsedEl.show();
41936 this.fireEvent("collapsed", this);
41937 this.fireEvent("invalidated", this);
41943 animateCollapse : function(){
41948 * Expands this region if it was previously collapsed.
41949 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
41950 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
41953 expand : function(e, skipAnim){
41955 e.stopPropagation();
41957 if(!this.collapsed || this.el.hasActiveFx()) {
41961 this.afterSlideIn();
41964 this.collapsed = false;
41965 if(this.config.animate && skipAnim !== true){
41966 this.animateExpand();
41970 this.split.el.show();
41972 this.collapsedEl.setLocation(-2000,-2000);
41973 this.collapsedEl.hide();
41974 this.fireEvent("invalidated", this);
41975 this.fireEvent("expanded", this);
41979 animateExpand : function(){
41983 initTabs : function()
41985 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
41987 var ts = new Roo.bootstrap.panel.Tabs({
41988 el: this.bodyEl.dom,
41990 tabPosition: this.tabPosition ? this.tabPosition : 'top',
41991 disableTooltips: this.config.disableTabTips,
41992 toolbar : this.config.toolbar
41995 if(this.config.hideTabs){
41996 ts.stripWrap.setDisplayed(false);
41999 ts.resizeTabs = this.config.resizeTabs === true;
42000 ts.minTabWidth = this.config.minTabWidth || 40;
42001 ts.maxTabWidth = this.config.maxTabWidth || 250;
42002 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
42003 ts.monitorResize = false;
42004 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
42005 ts.bodyEl.addClass('roo-layout-tabs-body');
42006 this.panels.each(this.initPanelAsTab, this);
42009 initPanelAsTab : function(panel){
42010 var ti = this.tabs.addTab(
42014 this.config.closeOnTab && panel.isClosable(),
42017 if(panel.tabTip !== undefined){
42018 ti.setTooltip(panel.tabTip);
42020 ti.on("activate", function(){
42021 this.setActivePanel(panel);
42024 if(this.config.closeOnTab){
42025 ti.on("beforeclose", function(t, e){
42027 this.remove(panel);
42031 panel.tabItem = ti;
42036 updatePanelTitle : function(panel, title)
42038 if(this.activePanel == panel){
42039 this.updateTitle(title);
42042 var ti = this.tabs.getTab(panel.getEl().id);
42044 if(panel.tabTip !== undefined){
42045 ti.setTooltip(panel.tabTip);
42050 updateTitle : function(title){
42051 if(this.titleTextEl && !this.config.title){
42052 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
42056 setActivePanel : function(panel)
42058 panel = this.getPanel(panel);
42059 if(this.activePanel && this.activePanel != panel){
42060 if(this.activePanel.setActiveState(false) === false){
42064 this.activePanel = panel;
42065 panel.setActiveState(true);
42066 if(this.panelSize){
42067 panel.setSize(this.panelSize.width, this.panelSize.height);
42070 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
42072 this.updateTitle(panel.getTitle());
42074 this.fireEvent("invalidated", this);
42076 this.fireEvent("panelactivated", this, panel);
42080 * Shows the specified panel.
42081 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
42082 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
42084 showPanel : function(panel)
42086 panel = this.getPanel(panel);
42089 var tab = this.tabs.getTab(panel.getEl().id);
42090 if(tab.isHidden()){
42091 this.tabs.unhideTab(tab.id);
42095 this.setActivePanel(panel);
42102 * Get the active panel for this region.
42103 * @return {Roo.ContentPanel} The active panel or null
42105 getActivePanel : function(){
42106 return this.activePanel;
42109 validateVisibility : function(){
42110 if(this.panels.getCount() < 1){
42111 this.updateTitle(" ");
42112 this.closeBtn.hide();
42115 if(!this.isVisible()){
42122 * Adds the passed ContentPanel(s) to this region.
42123 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42124 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
42126 add : function(panel)
42128 if(arguments.length > 1){
42129 for(var i = 0, len = arguments.length; i < len; i++) {
42130 this.add(arguments[i]);
42135 // if we have not been rendered yet, then we can not really do much of this..
42136 if (!this.bodyEl) {
42137 this.unrendered_panels.push(panel);
42144 if(this.hasPanel(panel)){
42145 this.showPanel(panel);
42148 panel.setRegion(this);
42149 this.panels.add(panel);
42150 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
42151 // sinle panel - no tab...?? would it not be better to render it with the tabs,
42152 // and hide them... ???
42153 this.bodyEl.dom.appendChild(panel.getEl().dom);
42154 if(panel.background !== true){
42155 this.setActivePanel(panel);
42157 this.fireEvent("paneladded", this, panel);
42164 this.initPanelAsTab(panel);
42168 if(panel.background !== true){
42169 this.tabs.activate(panel.getEl().id);
42171 this.fireEvent("paneladded", this, panel);
42176 * Hides the tab for the specified panel.
42177 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42179 hidePanel : function(panel){
42180 if(this.tabs && (panel = this.getPanel(panel))){
42181 this.tabs.hideTab(panel.getEl().id);
42186 * Unhides the tab for a previously hidden panel.
42187 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42189 unhidePanel : function(panel){
42190 if(this.tabs && (panel = this.getPanel(panel))){
42191 this.tabs.unhideTab(panel.getEl().id);
42195 clearPanels : function(){
42196 while(this.panels.getCount() > 0){
42197 this.remove(this.panels.first());
42202 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
42203 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42204 * @param {Boolean} preservePanel Overrides the config preservePanel option
42205 * @return {Roo.ContentPanel} The panel that was removed
42207 remove : function(panel, preservePanel)
42209 panel = this.getPanel(panel);
42214 this.fireEvent("beforeremove", this, panel, e);
42215 if(e.cancel === true){
42218 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
42219 var panelId = panel.getId();
42220 this.panels.removeKey(panelId);
42222 document.body.appendChild(panel.getEl().dom);
42225 this.tabs.removeTab(panel.getEl().id);
42226 }else if (!preservePanel){
42227 this.bodyEl.dom.removeChild(panel.getEl().dom);
42229 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
42230 var p = this.panels.first();
42231 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
42232 tempEl.appendChild(p.getEl().dom);
42233 this.bodyEl.update("");
42234 this.bodyEl.dom.appendChild(p.getEl().dom);
42236 this.updateTitle(p.getTitle());
42238 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
42239 this.setActivePanel(p);
42241 panel.setRegion(null);
42242 if(this.activePanel == panel){
42243 this.activePanel = null;
42245 if(this.config.autoDestroy !== false && preservePanel !== true){
42246 try{panel.destroy();}catch(e){}
42248 this.fireEvent("panelremoved", this, panel);
42253 * Returns the TabPanel component used by this region
42254 * @return {Roo.TabPanel}
42256 getTabs : function(){
42260 createTool : function(parentEl, className){
42261 var btn = Roo.DomHelper.append(parentEl, {
42263 cls: "x-layout-tools-button",
42266 cls: "roo-layout-tools-button-inner " + className,
42270 btn.addClassOnOver("roo-layout-tools-button-over");
42275 * Ext JS Library 1.1.1
42276 * Copyright(c) 2006-2007, Ext JS, LLC.
42278 * Originally Released Under LGPL - original licence link has changed is not relivant.
42281 * <script type="text/javascript">
42287 * @class Roo.SplitLayoutRegion
42288 * @extends Roo.LayoutRegion
42289 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
42291 Roo.bootstrap.layout.Split = function(config){
42292 this.cursor = config.cursor;
42293 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
42296 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
42298 splitTip : "Drag to resize.",
42299 collapsibleSplitTip : "Drag to resize. Double click to hide.",
42300 useSplitTips : false,
42302 applyConfig : function(config){
42303 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
42306 onRender : function(ctr,pos) {
42308 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
42309 if(!this.config.split){
42314 var splitEl = Roo.DomHelper.append(ctr.dom, {
42316 id: this.el.id + "-split",
42317 cls: "roo-layout-split roo-layout-split-"+this.position,
42320 /** The SplitBar for this region
42321 * @type Roo.SplitBar */
42322 // does not exist yet...
42323 Roo.log([this.position, this.orientation]);
42325 this.split = new Roo.bootstrap.SplitBar({
42326 dragElement : splitEl,
42327 resizingElement: this.el,
42328 orientation : this.orientation
42331 this.split.on("moved", this.onSplitMove, this);
42332 this.split.useShim = this.config.useShim === true;
42333 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
42334 if(this.useSplitTips){
42335 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
42337 //if(config.collapsible){
42338 // this.split.el.on("dblclick", this.collapse, this);
42341 if(typeof this.config.minSize != "undefined"){
42342 this.split.minSize = this.config.minSize;
42344 if(typeof this.config.maxSize != "undefined"){
42345 this.split.maxSize = this.config.maxSize;
42347 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
42348 this.hideSplitter();
42353 getHMaxSize : function(){
42354 var cmax = this.config.maxSize || 10000;
42355 var center = this.mgr.getRegion("center");
42356 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
42359 getVMaxSize : function(){
42360 var cmax = this.config.maxSize || 10000;
42361 var center = this.mgr.getRegion("center");
42362 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
42365 onSplitMove : function(split, newSize){
42366 this.fireEvent("resized", this, newSize);
42370 * Returns the {@link Roo.SplitBar} for this region.
42371 * @return {Roo.SplitBar}
42373 getSplitBar : function(){
42378 this.hideSplitter();
42379 Roo.bootstrap.layout.Split.superclass.hide.call(this);
42382 hideSplitter : function(){
42384 this.split.el.setLocation(-2000,-2000);
42385 this.split.el.hide();
42391 this.split.el.show();
42393 Roo.bootstrap.layout.Split.superclass.show.call(this);
42396 beforeSlide: function(){
42397 if(Roo.isGecko){// firefox overflow auto bug workaround
42398 this.bodyEl.clip();
42400 this.tabs.bodyEl.clip();
42402 if(this.activePanel){
42403 this.activePanel.getEl().clip();
42405 if(this.activePanel.beforeSlide){
42406 this.activePanel.beforeSlide();
42412 afterSlide : function(){
42413 if(Roo.isGecko){// firefox overflow auto bug workaround
42414 this.bodyEl.unclip();
42416 this.tabs.bodyEl.unclip();
42418 if(this.activePanel){
42419 this.activePanel.getEl().unclip();
42420 if(this.activePanel.afterSlide){
42421 this.activePanel.afterSlide();
42427 initAutoHide : function(){
42428 if(this.autoHide !== false){
42429 if(!this.autoHideHd){
42430 var st = new Roo.util.DelayedTask(this.slideIn, this);
42431 this.autoHideHd = {
42432 "mouseout": function(e){
42433 if(!e.within(this.el, true)){
42437 "mouseover" : function(e){
42443 this.el.on(this.autoHideHd);
42447 clearAutoHide : function(){
42448 if(this.autoHide !== false){
42449 this.el.un("mouseout", this.autoHideHd.mouseout);
42450 this.el.un("mouseover", this.autoHideHd.mouseover);
42454 clearMonitor : function(){
42455 Roo.get(document).un("click", this.slideInIf, this);
42458 // these names are backwards but not changed for compat
42459 slideOut : function(){
42460 if(this.isSlid || this.el.hasActiveFx()){
42463 this.isSlid = true;
42464 if(this.collapseBtn){
42465 this.collapseBtn.hide();
42467 this.closeBtnState = this.closeBtn.getStyle('display');
42468 this.closeBtn.hide();
42470 this.stickBtn.show();
42473 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
42474 this.beforeSlide();
42475 this.el.setStyle("z-index", 10001);
42476 this.el.slideIn(this.getSlideAnchor(), {
42477 callback: function(){
42479 this.initAutoHide();
42480 Roo.get(document).on("click", this.slideInIf, this);
42481 this.fireEvent("slideshow", this);
42488 afterSlideIn : function(){
42489 this.clearAutoHide();
42490 this.isSlid = false;
42491 this.clearMonitor();
42492 this.el.setStyle("z-index", "");
42493 if(this.collapseBtn){
42494 this.collapseBtn.show();
42496 this.closeBtn.setStyle('display', this.closeBtnState);
42498 this.stickBtn.hide();
42500 this.fireEvent("slidehide", this);
42503 slideIn : function(cb){
42504 if(!this.isSlid || this.el.hasActiveFx()){
42508 this.isSlid = false;
42509 this.beforeSlide();
42510 this.el.slideOut(this.getSlideAnchor(), {
42511 callback: function(){
42512 this.el.setLeftTop(-10000, -10000);
42514 this.afterSlideIn();
42522 slideInIf : function(e){
42523 if(!e.within(this.el)){
42528 animateCollapse : function(){
42529 this.beforeSlide();
42530 this.el.setStyle("z-index", 20000);
42531 var anchor = this.getSlideAnchor();
42532 this.el.slideOut(anchor, {
42533 callback : function(){
42534 this.el.setStyle("z-index", "");
42535 this.collapsedEl.slideIn(anchor, {duration:.3});
42537 this.el.setLocation(-10000,-10000);
42539 this.fireEvent("collapsed", this);
42546 animateExpand : function(){
42547 this.beforeSlide();
42548 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
42549 this.el.setStyle("z-index", 20000);
42550 this.collapsedEl.hide({
42553 this.el.slideIn(this.getSlideAnchor(), {
42554 callback : function(){
42555 this.el.setStyle("z-index", "");
42558 this.split.el.show();
42560 this.fireEvent("invalidated", this);
42561 this.fireEvent("expanded", this);
42589 getAnchor : function(){
42590 return this.anchors[this.position];
42593 getCollapseAnchor : function(){
42594 return this.canchors[this.position];
42597 getSlideAnchor : function(){
42598 return this.sanchors[this.position];
42601 getAlignAdj : function(){
42602 var cm = this.cmargins;
42603 switch(this.position){
42619 getExpandAdj : function(){
42620 var c = this.collapsedEl, cm = this.cmargins;
42621 switch(this.position){
42623 return [-(cm.right+c.getWidth()+cm.left), 0];
42626 return [cm.right+c.getWidth()+cm.left, 0];
42629 return [0, -(cm.top+cm.bottom+c.getHeight())];
42632 return [0, cm.top+cm.bottom+c.getHeight()];
42638 * Ext JS Library 1.1.1
42639 * Copyright(c) 2006-2007, Ext JS, LLC.
42641 * Originally Released Under LGPL - original licence link has changed is not relivant.
42644 * <script type="text/javascript">
42647 * These classes are private internal classes
42649 Roo.bootstrap.layout.Center = function(config){
42650 config.region = "center";
42651 Roo.bootstrap.layout.Region.call(this, config);
42652 this.visible = true;
42653 this.minWidth = config.minWidth || 20;
42654 this.minHeight = config.minHeight || 20;
42657 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
42659 // center panel can't be hidden
42663 // center panel can't be hidden
42666 getMinWidth: function(){
42667 return this.minWidth;
42670 getMinHeight: function(){
42671 return this.minHeight;
42685 Roo.bootstrap.layout.North = function(config)
42687 config.region = 'north';
42688 config.cursor = 'n-resize';
42690 Roo.bootstrap.layout.Split.call(this, config);
42694 this.split.placement = Roo.bootstrap.SplitBar.TOP;
42695 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
42696 this.split.el.addClass("roo-layout-split-v");
42698 //var size = config.initialSize || config.height;
42699 //if(this.el && typeof size != "undefined"){
42700 // this.el.setHeight(size);
42703 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
42705 orientation: Roo.bootstrap.SplitBar.VERTICAL,
42708 onRender : function(ctr, pos)
42710 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42711 var size = this.config.initialSize || this.config.height;
42712 if(this.el && typeof size != "undefined"){
42713 this.el.setHeight(size);
42718 getBox : function(){
42719 if(this.collapsed){
42720 return this.collapsedEl.getBox();
42722 var box = this.el.getBox();
42724 box.height += this.split.el.getHeight();
42729 updateBox : function(box){
42730 if(this.split && !this.collapsed){
42731 box.height -= this.split.el.getHeight();
42732 this.split.el.setLeft(box.x);
42733 this.split.el.setTop(box.y+box.height);
42734 this.split.el.setWidth(box.width);
42736 if(this.collapsed){
42737 this.updateBody(box.width, null);
42739 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42747 Roo.bootstrap.layout.South = function(config){
42748 config.region = 'south';
42749 config.cursor = 's-resize';
42750 Roo.bootstrap.layout.Split.call(this, config);
42752 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
42753 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
42754 this.split.el.addClass("roo-layout-split-v");
42759 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
42760 orientation: Roo.bootstrap.SplitBar.VERTICAL,
42762 onRender : function(ctr, pos)
42764 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42765 var size = this.config.initialSize || this.config.height;
42766 if(this.el && typeof size != "undefined"){
42767 this.el.setHeight(size);
42772 getBox : function(){
42773 if(this.collapsed){
42774 return this.collapsedEl.getBox();
42776 var box = this.el.getBox();
42778 var sh = this.split.el.getHeight();
42785 updateBox : function(box){
42786 if(this.split && !this.collapsed){
42787 var sh = this.split.el.getHeight();
42790 this.split.el.setLeft(box.x);
42791 this.split.el.setTop(box.y-sh);
42792 this.split.el.setWidth(box.width);
42794 if(this.collapsed){
42795 this.updateBody(box.width, null);
42797 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42801 Roo.bootstrap.layout.East = function(config){
42802 config.region = "east";
42803 config.cursor = "e-resize";
42804 Roo.bootstrap.layout.Split.call(this, config);
42806 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
42807 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
42808 this.split.el.addClass("roo-layout-split-h");
42812 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
42813 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
42815 onRender : function(ctr, pos)
42817 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42818 var size = this.config.initialSize || this.config.width;
42819 if(this.el && typeof size != "undefined"){
42820 this.el.setWidth(size);
42825 getBox : function(){
42826 if(this.collapsed){
42827 return this.collapsedEl.getBox();
42829 var box = this.el.getBox();
42831 var sw = this.split.el.getWidth();
42838 updateBox : function(box){
42839 if(this.split && !this.collapsed){
42840 var sw = this.split.el.getWidth();
42842 this.split.el.setLeft(box.x);
42843 this.split.el.setTop(box.y);
42844 this.split.el.setHeight(box.height);
42847 if(this.collapsed){
42848 this.updateBody(null, box.height);
42850 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42854 Roo.bootstrap.layout.West = function(config){
42855 config.region = "west";
42856 config.cursor = "w-resize";
42858 Roo.bootstrap.layout.Split.call(this, config);
42860 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
42861 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
42862 this.split.el.addClass("roo-layout-split-h");
42866 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
42867 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
42869 onRender: function(ctr, pos)
42871 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
42872 var size = this.config.initialSize || this.config.width;
42873 if(typeof size != "undefined"){
42874 this.el.setWidth(size);
42878 getBox : function(){
42879 if(this.collapsed){
42880 return this.collapsedEl.getBox();
42882 var box = this.el.getBox();
42883 if (box.width == 0) {
42884 box.width = this.config.width; // kludge?
42887 box.width += this.split.el.getWidth();
42892 updateBox : function(box){
42893 if(this.split && !this.collapsed){
42894 var sw = this.split.el.getWidth();
42896 this.split.el.setLeft(box.x+box.width);
42897 this.split.el.setTop(box.y);
42898 this.split.el.setHeight(box.height);
42900 if(this.collapsed){
42901 this.updateBody(null, box.height);
42903 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42907 * Ext JS Library 1.1.1
42908 * Copyright(c) 2006-2007, Ext JS, LLC.
42910 * Originally Released Under LGPL - original licence link has changed is not relivant.
42913 * <script type="text/javascript">
42916 * @class Roo.bootstrap.paenl.Content
42917 * @extends Roo.util.Observable
42918 * @children Roo.bootstrap.Component
42919 * @parent builder Roo.bootstrap.layout.Border
42920 * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
42921 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
42922 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
42923 * @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
42924 * @cfg {Boolean} closable True if the panel can be closed/removed
42925 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
42926 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
42927 * @cfg {Toolbar} toolbar A toolbar for this panel
42928 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
42929 * @cfg {String} title The title for this panel
42930 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
42931 * @cfg {String} url Calls {@link #setUrl} with this value
42932 * @cfg {String} region [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
42933 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
42934 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
42935 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
42936 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
42937 * @cfg {Boolean} badges render the badges
42938 * @cfg {String} cls extra classes to use
42939 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
42942 * Create a new ContentPanel.
42943 * @param {String/Object} config A string to set only the title or a config object
42946 Roo.bootstrap.panel.Content = function( config){
42948 this.tpl = config.tpl || false;
42950 var el = config.el;
42951 var content = config.content;
42953 if(config.autoCreate){ // xtype is available if this is called from factory
42956 this.el = Roo.get(el);
42957 if(!this.el && config && config.autoCreate){
42958 if(typeof config.autoCreate == "object"){
42959 if(!config.autoCreate.id){
42960 config.autoCreate.id = config.id||el;
42962 this.el = Roo.DomHelper.append(document.body,
42963 config.autoCreate, true);
42967 cls: (config.cls || '') +
42968 (config.background ? ' bg-' + config.background : '') +
42969 " roo-layout-inactive-content",
42972 if (config.iframe) {
42976 style : 'border: 0px',
42977 src : 'about:blank'
42983 elcfg.html = config.html;
42987 this.el = Roo.DomHelper.append(document.body, elcfg , true);
42988 if (config.iframe) {
42989 this.iframeEl = this.el.select('iframe',true).first();
42994 this.closable = false;
42995 this.loaded = false;
42996 this.active = false;
42999 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
43001 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
43003 this.wrapEl = this.el; //this.el.wrap();
43005 if (config.toolbar.items) {
43006 ti = config.toolbar.items ;
43007 delete config.toolbar.items ;
43011 this.toolbar.render(this.wrapEl, 'before');
43012 for(var i =0;i < ti.length;i++) {
43013 // Roo.log(['add child', items[i]]);
43014 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43016 this.toolbar.items = nitems;
43017 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
43018 delete config.toolbar;
43022 // xtype created footer. - not sure if will work as we normally have to render first..
43023 if (this.footer && !this.footer.el && this.footer.xtype) {
43024 if (!this.wrapEl) {
43025 this.wrapEl = this.el.wrap();
43028 this.footer.container = this.wrapEl.createChild();
43030 this.footer = Roo.factory(this.footer, Roo);
43035 if(typeof config == "string"){
43036 this.title = config;
43038 Roo.apply(this, config);
43042 this.resizeEl = Roo.get(this.resizeEl, true);
43044 this.resizeEl = this.el;
43046 // handle view.xtype
43054 * Fires when this panel is activated.
43055 * @param {Roo.ContentPanel} this
43059 * @event deactivate
43060 * Fires when this panel is activated.
43061 * @param {Roo.ContentPanel} this
43063 "deactivate" : true,
43067 * Fires when this panel is resized if fitToFrame is true.
43068 * @param {Roo.ContentPanel} this
43069 * @param {Number} width The width after any component adjustments
43070 * @param {Number} height The height after any component adjustments
43076 * Fires when this tab is created
43077 * @param {Roo.ContentPanel} this
43083 * Fires when this content is scrolled
43084 * @param {Roo.ContentPanel} this
43085 * @param {Event} scrollEvent
43096 if(this.autoScroll && !this.iframe){
43097 this.resizeEl.setStyle("overflow", "auto");
43098 this.resizeEl.on('scroll', this.onScroll, this);
43100 // fix randome scrolling
43101 //this.el.on('scroll', function() {
43102 // Roo.log('fix random scolling');
43103 // this.scrollTo('top',0);
43106 content = content || this.content;
43108 this.setContent(content);
43110 if(config && config.url){
43111 this.setUrl(this.url, this.params, this.loadOnce);
43116 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
43118 if (this.view && typeof(this.view.xtype) != 'undefined') {
43119 this.view.el = this.el.appendChild(document.createElement("div"));
43120 this.view = Roo.factory(this.view);
43121 this.view.render && this.view.render(false, '');
43125 this.fireEvent('render', this);
43128 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
43138 /* Resize Element - use this to work out scroll etc. */
43141 setRegion : function(region){
43142 this.region = region;
43143 this.setActiveClass(region && !this.background);
43147 setActiveClass: function(state)
43150 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
43151 this.el.setStyle('position','relative');
43153 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
43154 this.el.setStyle('position', 'absolute');
43159 * Returns the toolbar for this Panel if one was configured.
43160 * @return {Roo.Toolbar}
43162 getToolbar : function(){
43163 return this.toolbar;
43166 setActiveState : function(active)
43168 this.active = active;
43169 this.setActiveClass(active);
43171 if(this.fireEvent("deactivate", this) === false){
43176 this.fireEvent("activate", this);
43180 * Updates this panel's element (not for iframe)
43181 * @param {String} content The new content
43182 * @param {Boolean} loadScripts (optional) true to look for and process scripts
43184 setContent : function(content, loadScripts){
43189 this.el.update(content, loadScripts);
43192 ignoreResize : function(w, h)
43194 //return false; // always resize?
43195 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
43198 this.lastSize = {width: w, height: h};
43203 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
43204 * @return {Roo.UpdateManager} The UpdateManager
43206 getUpdateManager : function(){
43210 return this.el.getUpdateManager();
43213 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
43214 * Does not work with IFRAME contents
43215 * @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:
43218 url: "your-url.php",
43219 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
43220 callback: yourFunction,
43221 scope: yourObject, //(optional scope)
43224 text: "Loading...",
43230 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
43231 * 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.
43232 * @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}
43233 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
43234 * @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.
43235 * @return {Roo.ContentPanel} this
43243 var um = this.el.getUpdateManager();
43244 um.update.apply(um, arguments);
43250 * 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.
43251 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
43252 * @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)
43253 * @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)
43254 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
43256 setUrl : function(url, params, loadOnce){
43258 this.iframeEl.dom.src = url;
43262 if(this.refreshDelegate){
43263 this.removeListener("activate", this.refreshDelegate);
43265 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
43266 this.on("activate", this.refreshDelegate);
43267 return this.el.getUpdateManager();
43270 _handleRefresh : function(url, params, loadOnce){
43271 if(!loadOnce || !this.loaded){
43272 var updater = this.el.getUpdateManager();
43273 updater.update(url, params, this._setLoaded.createDelegate(this));
43277 _setLoaded : function(){
43278 this.loaded = true;
43282 * Returns this panel's id
43285 getId : function(){
43290 * Returns this panel's element - used by regiosn to add.
43291 * @return {Roo.Element}
43293 getEl : function(){
43294 return this.wrapEl || this.el;
43299 adjustForComponents : function(width, height)
43301 //Roo.log('adjustForComponents ');
43302 if(this.resizeEl != this.el){
43303 width -= this.el.getFrameWidth('lr');
43304 height -= this.el.getFrameWidth('tb');
43307 var te = this.toolbar.getEl();
43308 te.setWidth(width);
43309 height -= te.getHeight();
43312 var te = this.footer.getEl();
43313 te.setWidth(width);
43314 height -= te.getHeight();
43318 if(this.adjustments){
43319 width += this.adjustments[0];
43320 height += this.adjustments[1];
43322 return {"width": width, "height": height};
43325 setSize : function(width, height){
43326 if(this.fitToFrame && !this.ignoreResize(width, height)){
43327 if(this.fitContainer && this.resizeEl != this.el){
43328 this.el.setSize(width, height);
43330 var size = this.adjustForComponents(width, height);
43332 this.iframeEl.setSize(width,height);
43335 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
43336 this.fireEvent('resize', this, size.width, size.height);
43343 * Returns this panel's title
43346 getTitle : function(){
43348 if (typeof(this.title) != 'object') {
43353 for (var k in this.title) {
43354 if (!this.title.hasOwnProperty(k)) {
43358 if (k.indexOf('-') >= 0) {
43359 var s = k.split('-');
43360 for (var i = 0; i<s.length; i++) {
43361 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
43364 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
43371 * Set this panel's title
43372 * @param {String} title
43374 setTitle : function(title){
43375 this.title = title;
43377 this.region.updatePanelTitle(this, title);
43382 * Returns true is this panel was configured to be closable
43383 * @return {Boolean}
43385 isClosable : function(){
43386 return this.closable;
43389 beforeSlide : function(){
43391 this.resizeEl.clip();
43394 afterSlide : function(){
43396 this.resizeEl.unclip();
43400 * Force a content refresh from the URL specified in the {@link #setUrl} method.
43401 * Will fail silently if the {@link #setUrl} method has not been called.
43402 * This does not activate the panel, just updates its content.
43404 refresh : function(){
43405 if(this.refreshDelegate){
43406 this.loaded = false;
43407 this.refreshDelegate();
43412 * Destroys this panel
43414 destroy : function(){
43415 this.el.removeAllListeners();
43416 var tempEl = document.createElement("span");
43417 tempEl.appendChild(this.el.dom);
43418 tempEl.innerHTML = "";
43424 * form - if the content panel contains a form - this is a reference to it.
43425 * @type {Roo.form.Form}
43429 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
43430 * This contains a reference to it.
43436 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
43446 * @param {Object} cfg Xtype definition of item to add.
43450 getChildContainer: function () {
43451 return this.getEl();
43455 onScroll : function(e)
43457 this.fireEvent('scroll', this, e);
43462 var ret = new Roo.factory(cfg);
43467 if (cfg.xtype.match(/^Form$/)) {
43470 //if (this.footer) {
43471 // el = this.footer.container.insertSibling(false, 'before');
43473 el = this.el.createChild();
43476 this.form = new Roo.form.Form(cfg);
43479 if ( this.form.allItems.length) {
43480 this.form.render(el.dom);
43484 // should only have one of theses..
43485 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
43486 // views.. should not be just added - used named prop 'view''
43488 cfg.el = this.el.appendChild(document.createElement("div"));
43491 var ret = new Roo.factory(cfg);
43493 ret.render && ret.render(false, ''); // render blank..
43503 * @class Roo.bootstrap.panel.Grid
43504 * @extends Roo.bootstrap.panel.Content
43506 * Create a new GridPanel.
43507 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
43508 * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
43509 * @param {Object} config A the config object
43515 Roo.bootstrap.panel.Grid = function(config)
43519 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
43520 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
43522 config.el = this.wrapper;
43523 //this.el = this.wrapper;
43525 if (config.container) {
43526 // ctor'ed from a Border/panel.grid
43529 this.wrapper.setStyle("overflow", "hidden");
43530 this.wrapper.addClass('roo-grid-container');
43535 if(config.toolbar){
43536 var tool_el = this.wrapper.createChild();
43537 this.toolbar = Roo.factory(config.toolbar);
43539 if (config.toolbar.items) {
43540 ti = config.toolbar.items ;
43541 delete config.toolbar.items ;
43545 this.toolbar.render(tool_el);
43546 for(var i =0;i < ti.length;i++) {
43547 // Roo.log(['add child', items[i]]);
43548 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43550 this.toolbar.items = nitems;
43552 delete config.toolbar;
43555 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
43556 config.grid.scrollBody = true;;
43557 config.grid.monitorWindowResize = false; // turn off autosizing
43558 config.grid.autoHeight = false;
43559 config.grid.autoWidth = false;
43561 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
43563 if (config.background) {
43564 // render grid on panel activation (if panel background)
43565 this.on('activate', function(gp) {
43566 if (!gp.grid.rendered) {
43567 gp.grid.render(this.wrapper);
43568 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
43573 this.grid.render(this.wrapper);
43574 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
43577 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
43578 // ??? needed ??? config.el = this.wrapper;
43583 // xtype created footer. - not sure if will work as we normally have to render first..
43584 if (this.footer && !this.footer.el && this.footer.xtype) {
43586 var ctr = this.grid.getView().getFooterPanel(true);
43587 this.footer.dataSource = this.grid.dataSource;
43588 this.footer = Roo.factory(this.footer, Roo);
43589 this.footer.render(ctr);
43599 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
43602 getId : function(){
43603 return this.grid.id;
43607 * Returns the grid for this panel
43608 * @return {Roo.bootstrap.Table}
43610 getGrid : function(){
43614 setSize : function(width, height)
43617 //if(!this.ignoreResize(width, height)){
43618 var grid = this.grid;
43619 var size = this.adjustForComponents(width, height);
43620 // tfoot is not a footer?
43623 var gridel = grid.getGridEl();
43624 gridel.setSize(size.width, size.height);
43626 var tbd = grid.getGridEl().select('tbody', true).first();
43627 var thd = grid.getGridEl().select('thead',true).first();
43628 var tbf= grid.getGridEl().select('tfoot', true).first();
43631 size.height -= tbf.getHeight();
43634 size.height -= thd.getHeight();
43637 tbd.setSize(size.width, size.height );
43638 // this is for the account management tab -seems to work there.
43639 var thd = grid.getGridEl().select('thead',true).first();
43641 // tbd.setSize(size.width, size.height - thd.getHeight());
43651 beforeSlide : function(){
43652 this.grid.getView().scroller.clip();
43655 afterSlide : function(){
43656 this.grid.getView().scroller.unclip();
43659 destroy : function(){
43660 this.grid.destroy();
43662 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
43667 * @class Roo.bootstrap.panel.Nest
43668 * @extends Roo.bootstrap.panel.Content
43670 * Create a new Panel, that can contain a layout.Border.
43673 * @param {String/Object} config A string to set only the title or a config object
43675 Roo.bootstrap.panel.Nest = function(config)
43677 // construct with only one argument..
43678 /* FIXME - implement nicer consturctors
43679 if (layout.layout) {
43681 layout = config.layout;
43682 delete config.layout;
43684 if (layout.xtype && !layout.getEl) {
43685 // then layout needs constructing..
43686 layout = Roo.factory(layout, Roo);
43690 config.el = config.layout.getEl();
43692 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
43694 config.layout.monitorWindowResize = false; // turn off autosizing
43695 this.layout = config.layout;
43696 this.layout.getEl().addClass("roo-layout-nested-layout");
43697 this.layout.parent = this;
43704 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
43706 * @cfg {Roo.BorderLayout} layout The layout for this panel
43710 setSize : function(width, height){
43711 if(!this.ignoreResize(width, height)){
43712 var size = this.adjustForComponents(width, height);
43713 var el = this.layout.getEl();
43714 if (size.height < 1) {
43715 el.setWidth(size.width);
43717 el.setSize(size.width, size.height);
43719 var touch = el.dom.offsetWidth;
43720 this.layout.layout();
43721 // ie requires a double layout on the first pass
43722 if(Roo.isIE && !this.initialized){
43723 this.initialized = true;
43724 this.layout.layout();
43729 // activate all subpanels if not currently active..
43731 setActiveState : function(active){
43732 this.active = active;
43733 this.setActiveClass(active);
43736 this.fireEvent("deactivate", this);
43740 this.fireEvent("activate", this);
43741 // not sure if this should happen before or after..
43742 if (!this.layout) {
43743 return; // should not happen..
43746 for (var r in this.layout.regions) {
43747 reg = this.layout.getRegion(r);
43748 if (reg.getActivePanel()) {
43749 //reg.showPanel(reg.getActivePanel()); // force it to activate..
43750 reg.setActivePanel(reg.getActivePanel());
43753 if (!reg.panels.length) {
43756 reg.showPanel(reg.getPanel(0));
43765 * Returns the nested BorderLayout for this panel
43766 * @return {Roo.BorderLayout}
43768 getLayout : function(){
43769 return this.layout;
43773 * Adds a xtype elements to the layout of the nested panel
43777 xtype : 'ContentPanel',
43784 xtype : 'NestedLayoutPanel',
43790 items : [ ... list of content panels or nested layout panels.. ]
43794 * @param {Object} cfg Xtype definition of item to add.
43796 addxtype : function(cfg) {
43797 return this.layout.addxtype(cfg);
43802 * Ext JS Library 1.1.1
43803 * Copyright(c) 2006-2007, Ext JS, LLC.
43805 * Originally Released Under LGPL - original licence link has changed is not relivant.
43808 * <script type="text/javascript">
43811 * @class Roo.TabPanel
43812 * @extends Roo.util.Observable
43813 * A lightweight tab container.
43817 // basic tabs 1, built from existing content
43818 var tabs = new Roo.TabPanel("tabs1");
43819 tabs.addTab("script", "View Script");
43820 tabs.addTab("markup", "View Markup");
43821 tabs.activate("script");
43823 // more advanced tabs, built from javascript
43824 var jtabs = new Roo.TabPanel("jtabs");
43825 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
43827 // set up the UpdateManager
43828 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
43829 var updater = tab2.getUpdateManager();
43830 updater.setDefaultUrl("ajax1.htm");
43831 tab2.on('activate', updater.refresh, updater, true);
43833 // Use setUrl for Ajax loading
43834 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
43835 tab3.setUrl("ajax2.htm", null, true);
43838 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
43841 jtabs.activate("jtabs-1");
43844 * Create a new TabPanel.
43845 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
43846 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
43848 Roo.bootstrap.panel.Tabs = function(config){
43850 * The container element for this TabPanel.
43851 * @type Roo.Element
43853 this.el = Roo.get(config.el);
43856 if(typeof config == "boolean"){
43857 this.tabPosition = config ? "bottom" : "top";
43859 Roo.apply(this, config);
43863 if(this.tabPosition == "bottom"){
43864 // if tabs are at the bottom = create the body first.
43865 this.bodyEl = Roo.get(this.createBody(this.el.dom));
43866 this.el.addClass("roo-tabs-bottom");
43868 // next create the tabs holders
43870 if (this.tabPosition == "west"){
43872 var reg = this.region; // fake it..
43874 if (!reg.mgr.parent) {
43877 reg = reg.mgr.parent.region;
43879 Roo.log("got nest?");
43881 if (reg.mgr.getRegion('west')) {
43882 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
43883 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
43884 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
43885 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
43886 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
43894 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
43895 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
43896 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
43897 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
43902 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
43905 // finally - if tabs are at the top, then create the body last..
43906 if(this.tabPosition != "bottom"){
43907 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
43908 * @type Roo.Element
43910 this.bodyEl = Roo.get(this.createBody(this.el.dom));
43911 this.el.addClass("roo-tabs-top");
43915 this.bodyEl.setStyle("position", "relative");
43917 this.active = null;
43918 this.activateDelegate = this.activate.createDelegate(this);
43923 * Fires when the active tab changes
43924 * @param {Roo.TabPanel} this
43925 * @param {Roo.TabPanelItem} activePanel The new active tab
43929 * @event beforetabchange
43930 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
43931 * @param {Roo.TabPanel} this
43932 * @param {Object} e Set cancel to true on this object to cancel the tab change
43933 * @param {Roo.TabPanelItem} tab The tab being changed to
43935 "beforetabchange" : true
43938 Roo.EventManager.onWindowResize(this.onResize, this);
43939 this.cpad = this.el.getPadding("lr");
43940 this.hiddenCount = 0;
43943 // toolbar on the tabbar support...
43944 if (this.toolbar) {
43945 alert("no toolbar support yet");
43946 this.toolbar = false;
43948 var tcfg = this.toolbar;
43949 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
43950 this.toolbar = new Roo.Toolbar(tcfg);
43951 if (Roo.isSafari) {
43952 var tbl = tcfg.container.child('table', true);
43953 tbl.setAttribute('width', '100%');
43961 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
43964 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
43966 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
43968 tabPosition : "top",
43970 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
43972 currentTabWidth : 0,
43974 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
43978 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
43982 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
43984 preferredTabWidth : 175,
43986 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
43988 resizeTabs : false,
43990 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
43992 monitorResize : true,
43994 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
43996 toolbar : false, // set by caller..
43998 region : false, /// set by caller
44000 disableTooltips : true, // not used yet...
44003 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
44004 * @param {String} id The id of the div to use <b>or create</b>
44005 * @param {String} text The text for the tab
44006 * @param {String} content (optional) Content to put in the TabPanelItem body
44007 * @param {Boolean} closable (optional) True to create a close icon on the tab
44008 * @return {Roo.TabPanelItem} The created TabPanelItem
44010 addTab : function(id, text, content, closable, tpl)
44012 var item = new Roo.bootstrap.panel.TabItem({
44016 closable : closable,
44019 this.addTabItem(item);
44021 item.setContent(content);
44027 * Returns the {@link Roo.TabPanelItem} with the specified id/index
44028 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
44029 * @return {Roo.TabPanelItem}
44031 getTab : function(id){
44032 return this.items[id];
44036 * Hides the {@link Roo.TabPanelItem} with the specified id/index
44037 * @param {String/Number} id The id or index of the TabPanelItem to hide.
44039 hideTab : function(id){
44040 var t = this.items[id];
44043 this.hiddenCount++;
44044 this.autoSizeTabs();
44049 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
44050 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
44052 unhideTab : function(id){
44053 var t = this.items[id];
44055 t.setHidden(false);
44056 this.hiddenCount--;
44057 this.autoSizeTabs();
44062 * Adds an existing {@link Roo.TabPanelItem}.
44063 * @param {Roo.TabPanelItem} item The TabPanelItem to add
44065 addTabItem : function(item)
44067 this.items[item.id] = item;
44068 this.items.push(item);
44069 this.autoSizeTabs();
44070 // if(this.resizeTabs){
44071 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
44072 // this.autoSizeTabs();
44074 // item.autoSize();
44079 * Removes a {@link Roo.TabPanelItem}.
44080 * @param {String/Number} id The id or index of the TabPanelItem to remove.
44082 removeTab : function(id){
44083 var items = this.items;
44084 var tab = items[id];
44085 if(!tab) { return; }
44086 var index = items.indexOf(tab);
44087 if(this.active == tab && items.length > 1){
44088 var newTab = this.getNextAvailable(index);
44093 this.stripEl.dom.removeChild(tab.pnode.dom);
44094 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
44095 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
44097 items.splice(index, 1);
44098 delete this.items[tab.id];
44099 tab.fireEvent("close", tab);
44100 tab.purgeListeners();
44101 this.autoSizeTabs();
44104 getNextAvailable : function(start){
44105 var items = this.items;
44107 // look for a next tab that will slide over to
44108 // replace the one being removed
44109 while(index < items.length){
44110 var item = items[++index];
44111 if(item && !item.isHidden()){
44115 // if one isn't found select the previous tab (on the left)
44118 var item = items[--index];
44119 if(item && !item.isHidden()){
44127 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
44128 * @param {String/Number} id The id or index of the TabPanelItem to disable.
44130 disableTab : function(id){
44131 var tab = this.items[id];
44132 if(tab && this.active != tab){
44138 * Enables a {@link Roo.TabPanelItem} that is disabled.
44139 * @param {String/Number} id The id or index of the TabPanelItem to enable.
44141 enableTab : function(id){
44142 var tab = this.items[id];
44147 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
44148 * @param {String/Number} id The id or index of the TabPanelItem to activate.
44149 * @return {Roo.TabPanelItem} The TabPanelItem.
44151 activate : function(id)
44153 //Roo.log('activite:' + id);
44155 var tab = this.items[id];
44159 if(tab == this.active || tab.disabled){
44163 this.fireEvent("beforetabchange", this, e, tab);
44164 if(e.cancel !== true && !tab.disabled){
44166 this.active.hide();
44168 this.active = this.items[id];
44169 this.active.show();
44170 this.fireEvent("tabchange", this, this.active);
44176 * Gets the active {@link Roo.TabPanelItem}.
44177 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
44179 getActiveTab : function(){
44180 return this.active;
44184 * Updates the tab body element to fit the height of the container element
44185 * for overflow scrolling
44186 * @param {Number} targetHeight (optional) Override the starting height from the elements height
44188 syncHeight : function(targetHeight){
44189 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44190 var bm = this.bodyEl.getMargins();
44191 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
44192 this.bodyEl.setHeight(newHeight);
44196 onResize : function(){
44197 if(this.monitorResize){
44198 this.autoSizeTabs();
44203 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
44205 beginUpdate : function(){
44206 this.updating = true;
44210 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
44212 endUpdate : function(){
44213 this.updating = false;
44214 this.autoSizeTabs();
44218 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
44220 autoSizeTabs : function()
44222 var count = this.items.length;
44223 var vcount = count - this.hiddenCount;
44226 this.stripEl.hide();
44228 this.stripEl.show();
44231 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
44236 var w = Math.max(this.el.getWidth() - this.cpad, 10);
44237 var availWidth = Math.floor(w / vcount);
44238 var b = this.stripBody;
44239 if(b.getWidth() > w){
44240 var tabs = this.items;
44241 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
44242 if(availWidth < this.minTabWidth){
44243 /*if(!this.sleft){ // incomplete scrolling code
44244 this.createScrollButtons();
44247 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
44250 if(this.currentTabWidth < this.preferredTabWidth){
44251 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
44257 * Returns the number of tabs in this TabPanel.
44260 getCount : function(){
44261 return this.items.length;
44265 * Resizes all the tabs to the passed width
44266 * @param {Number} The new width
44268 setTabWidth : function(width){
44269 this.currentTabWidth = width;
44270 for(var i = 0, len = this.items.length; i < len; i++) {
44271 if(!this.items[i].isHidden()) {
44272 this.items[i].setWidth(width);
44278 * Destroys this TabPanel
44279 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
44281 destroy : function(removeEl){
44282 Roo.EventManager.removeResizeListener(this.onResize, this);
44283 for(var i = 0, len = this.items.length; i < len; i++){
44284 this.items[i].purgeListeners();
44286 if(removeEl === true){
44287 this.el.update("");
44292 createStrip : function(container)
44294 var strip = document.createElement("nav");
44295 strip.className = Roo.bootstrap.version == 4 ?
44296 "navbar-light bg-light" :
44297 "navbar navbar-default"; //"x-tabs-wrap";
44298 container.appendChild(strip);
44302 createStripList : function(strip)
44304 // div wrapper for retard IE
44305 // returns the "tr" element.
44306 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
44307 //'<div class="x-tabs-strip-wrap">'+
44308 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
44309 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
44310 return strip.firstChild; //.firstChild.firstChild.firstChild;
44312 createBody : function(container)
44314 var body = document.createElement("div");
44315 Roo.id(body, "tab-body");
44316 //Roo.fly(body).addClass("x-tabs-body");
44317 Roo.fly(body).addClass("tab-content");
44318 container.appendChild(body);
44321 createItemBody :function(bodyEl, id){
44322 var body = Roo.getDom(id);
44324 body = document.createElement("div");
44327 //Roo.fly(body).addClass("x-tabs-item-body");
44328 Roo.fly(body).addClass("tab-pane");
44329 bodyEl.insertBefore(body, bodyEl.firstChild);
44333 createStripElements : function(stripEl, text, closable, tpl)
44335 var td = document.createElement("li"); // was td..
44336 td.className = 'nav-item';
44338 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
44341 stripEl.appendChild(td);
44343 td.className = "x-tabs-closable";
44344 if(!this.closeTpl){
44345 this.closeTpl = new Roo.Template(
44346 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
44347 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
44348 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
44351 var el = this.closeTpl.overwrite(td, {"text": text});
44352 var close = el.getElementsByTagName("div")[0];
44353 var inner = el.getElementsByTagName("em")[0];
44354 return {"el": el, "close": close, "inner": inner};
44357 // not sure what this is..
44358 // if(!this.tabTpl){
44359 //this.tabTpl = new Roo.Template(
44360 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
44361 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
44363 // this.tabTpl = new Roo.Template(
44364 // '<a href="#">' +
44365 // '<span unselectable="on"' +
44366 // (this.disableTooltips ? '' : ' title="{text}"') +
44367 // ' >{text}</span></a>'
44373 var template = tpl || this.tabTpl || false;
44376 template = new Roo.Template(
44377 Roo.bootstrap.version == 4 ?
44379 '<a class="nav-link" href="#" unselectable="on"' +
44380 (this.disableTooltips ? '' : ' title="{text}"') +
44383 '<a class="nav-link" href="#">' +
44384 '<span unselectable="on"' +
44385 (this.disableTooltips ? '' : ' title="{text}"') +
44386 ' >{text}</span></a>'
44391 switch (typeof(template)) {
44395 template = new Roo.Template(template);
44401 var el = template.overwrite(td, {"text": text});
44403 var inner = el.getElementsByTagName("span")[0];
44405 return {"el": el, "inner": inner};
44413 * @class Roo.TabPanelItem
44414 * @extends Roo.util.Observable
44415 * Represents an individual item (tab plus body) in a TabPanel.
44416 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
44417 * @param {String} id The id of this TabPanelItem
44418 * @param {String} text The text for the tab of this TabPanelItem
44419 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
44421 Roo.bootstrap.panel.TabItem = function(config){
44423 * The {@link Roo.TabPanel} this TabPanelItem belongs to
44424 * @type Roo.TabPanel
44426 this.tabPanel = config.panel;
44428 * The id for this TabPanelItem
44431 this.id = config.id;
44433 this.disabled = false;
44435 this.text = config.text;
44437 this.loaded = false;
44438 this.closable = config.closable;
44441 * The body element for this TabPanelItem.
44442 * @type Roo.Element
44444 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
44445 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
44446 this.bodyEl.setStyle("display", "block");
44447 this.bodyEl.setStyle("zoom", "1");
44448 //this.hideAction();
44450 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
44452 this.el = Roo.get(els.el);
44453 this.inner = Roo.get(els.inner, true);
44454 this.textEl = Roo.bootstrap.version == 4 ?
44455 this.el : Roo.get(this.el.dom.firstChild, true);
44457 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
44458 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
44461 // this.el.on("mousedown", this.onTabMouseDown, this);
44462 this.el.on("click", this.onTabClick, this);
44464 if(config.closable){
44465 var c = Roo.get(els.close, true);
44466 c.dom.title = this.closeText;
44467 c.addClassOnOver("close-over");
44468 c.on("click", this.closeClick, this);
44474 * Fires when this tab becomes the active tab.
44475 * @param {Roo.TabPanel} tabPanel The parent TabPanel
44476 * @param {Roo.TabPanelItem} this
44480 * @event beforeclose
44481 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
44482 * @param {Roo.TabPanelItem} this
44483 * @param {Object} e Set cancel to true on this object to cancel the close.
44485 "beforeclose": true,
44488 * Fires when this tab is closed.
44489 * @param {Roo.TabPanelItem} this
44493 * @event deactivate
44494 * Fires when this tab is no longer the active tab.
44495 * @param {Roo.TabPanel} tabPanel The parent TabPanel
44496 * @param {Roo.TabPanelItem} this
44498 "deactivate" : true
44500 this.hidden = false;
44502 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
44505 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
44507 purgeListeners : function(){
44508 Roo.util.Observable.prototype.purgeListeners.call(this);
44509 this.el.removeAllListeners();
44512 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
44515 this.status_node.addClass("active");
44518 this.tabPanel.stripWrap.repaint();
44520 this.fireEvent("activate", this.tabPanel, this);
44524 * Returns true if this tab is the active tab.
44525 * @return {Boolean}
44527 isActive : function(){
44528 return this.tabPanel.getActiveTab() == this;
44532 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
44535 this.status_node.removeClass("active");
44537 this.fireEvent("deactivate", this.tabPanel, this);
44540 hideAction : function(){
44541 this.bodyEl.hide();
44542 this.bodyEl.setStyle("position", "absolute");
44543 this.bodyEl.setLeft("-20000px");
44544 this.bodyEl.setTop("-20000px");
44547 showAction : function(){
44548 this.bodyEl.setStyle("position", "relative");
44549 this.bodyEl.setTop("");
44550 this.bodyEl.setLeft("");
44551 this.bodyEl.show();
44555 * Set the tooltip for the tab.
44556 * @param {String} tooltip The tab's tooltip
44558 setTooltip : function(text){
44559 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
44560 this.textEl.dom.qtip = text;
44561 this.textEl.dom.removeAttribute('title');
44563 this.textEl.dom.title = text;
44567 onTabClick : function(e){
44568 e.preventDefault();
44569 this.tabPanel.activate(this.id);
44572 onTabMouseDown : function(e){
44573 e.preventDefault();
44574 this.tabPanel.activate(this.id);
44577 getWidth : function(){
44578 return this.inner.getWidth();
44581 setWidth : function(width){
44582 var iwidth = width - this.linode.getPadding("lr");
44583 this.inner.setWidth(iwidth);
44584 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
44585 this.linode.setWidth(width);
44589 * Show or hide the tab
44590 * @param {Boolean} hidden True to hide or false to show.
44592 setHidden : function(hidden){
44593 this.hidden = hidden;
44594 this.linode.setStyle("display", hidden ? "none" : "");
44598 * Returns true if this tab is "hidden"
44599 * @return {Boolean}
44601 isHidden : function(){
44602 return this.hidden;
44606 * Returns the text for this tab
44609 getText : function(){
44613 autoSize : function(){
44614 //this.el.beginMeasure();
44615 this.textEl.setWidth(1);
44617 * #2804 [new] Tabs in Roojs
44618 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
44620 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
44621 //this.el.endMeasure();
44625 * Sets the text for the tab (Note: this also sets the tooltip text)
44626 * @param {String} text The tab's text and tooltip
44628 setText : function(text){
44630 this.textEl.update(text);
44631 this.setTooltip(text);
44632 //if(!this.tabPanel.resizeTabs){
44633 // this.autoSize();
44637 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
44639 activate : function(){
44640 this.tabPanel.activate(this.id);
44644 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
44646 disable : function(){
44647 if(this.tabPanel.active != this){
44648 this.disabled = true;
44649 this.status_node.addClass("disabled");
44654 * Enables this TabPanelItem if it was previously disabled.
44656 enable : function(){
44657 this.disabled = false;
44658 this.status_node.removeClass("disabled");
44662 * Sets the content for this TabPanelItem.
44663 * @param {String} content The content
44664 * @param {Boolean} loadScripts true to look for and load scripts
44666 setContent : function(content, loadScripts){
44667 this.bodyEl.update(content, loadScripts);
44671 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
44672 * @return {Roo.UpdateManager} The UpdateManager
44674 getUpdateManager : function(){
44675 return this.bodyEl.getUpdateManager();
44679 * Set a URL to be used to load the content for this TabPanelItem.
44680 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
44681 * @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)
44682 * @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)
44683 * @return {Roo.UpdateManager} The UpdateManager
44685 setUrl : function(url, params, loadOnce){
44686 if(this.refreshDelegate){
44687 this.un('activate', this.refreshDelegate);
44689 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44690 this.on("activate", this.refreshDelegate);
44691 return this.bodyEl.getUpdateManager();
44695 _handleRefresh : function(url, params, loadOnce){
44696 if(!loadOnce || !this.loaded){
44697 var updater = this.bodyEl.getUpdateManager();
44698 updater.update(url, params, this._setLoaded.createDelegate(this));
44703 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
44704 * Will fail silently if the setUrl method has not been called.
44705 * This does not activate the panel, just updates its content.
44707 refresh : function(){
44708 if(this.refreshDelegate){
44709 this.loaded = false;
44710 this.refreshDelegate();
44715 _setLoaded : function(){
44716 this.loaded = true;
44720 closeClick : function(e){
44723 this.fireEvent("beforeclose", this, o);
44724 if(o.cancel !== true){
44725 this.tabPanel.removeTab(this.id);
44729 * The text displayed in the tooltip for the close icon.
44732 closeText : "Close this tab"
44735 * This script refer to:
44736 * Title: International Telephone Input
44737 * Author: Jack O'Connor
44738 * Code version: v12.1.12
44739 * Availability: https://github.com/jackocnr/intl-tel-input.git
44742 Roo.bootstrap.form.PhoneInputData = function() {
44745 "Afghanistan (افغانستان)",
44750 "Albania (Shqipëri)",
44755 "Algeria (الجزائر)",
44780 "Antigua and Barbuda",
44790 "Armenia (Հայաստան)",
44806 "Austria (Österreich)",
44811 "Azerbaijan (Azərbaycan)",
44821 "Bahrain (البحرين)",
44826 "Bangladesh (বাংলাদেশ)",
44836 "Belarus (Беларусь)",
44841 "Belgium (België)",
44871 "Bosnia and Herzegovina (Босна и Херцеговина)",
44886 "British Indian Ocean Territory",
44891 "British Virgin Islands",
44901 "Bulgaria (България)",
44911 "Burundi (Uburundi)",
44916 "Cambodia (កម្ពុជា)",
44921 "Cameroon (Cameroun)",
44930 ["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"]
44933 "Cape Verde (Kabu Verdi)",
44938 "Caribbean Netherlands",
44949 "Central African Republic (République centrafricaine)",
44969 "Christmas Island",
44975 "Cocos (Keeling) Islands",
44986 "Comoros (جزر القمر)",
44991 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
44996 "Congo (Republic) (Congo-Brazzaville)",
45016 "Croatia (Hrvatska)",
45037 "Czech Republic (Česká republika)",
45042 "Denmark (Danmark)",
45057 "Dominican Republic (República Dominicana)",
45061 ["809", "829", "849"]
45079 "Equatorial Guinea (Guinea Ecuatorial)",
45099 "Falkland Islands (Islas Malvinas)",
45104 "Faroe Islands (Føroyar)",
45125 "French Guiana (Guyane française)",
45130 "French Polynesia (Polynésie française)",
45145 "Georgia (საქართველო)",
45150 "Germany (Deutschland)",
45170 "Greenland (Kalaallit Nunaat)",
45207 "Guinea-Bissau (Guiné Bissau)",
45232 "Hungary (Magyarország)",
45237 "Iceland (Ísland)",
45257 "Iraq (العراق)",
45273 "Israel (ישראל)",
45300 "Jordan (الأردن)",
45305 "Kazakhstan (Казахстан)",
45326 "Kuwait (الكويت)",
45331 "Kyrgyzstan (Кыргызстан)",
45341 "Latvia (Latvija)",
45346 "Lebanon (لبنان)",
45361 "Libya (ليبيا)",
45371 "Lithuania (Lietuva)",
45386 "Macedonia (FYROM) (Македонија)",
45391 "Madagascar (Madagasikara)",
45421 "Marshall Islands",
45431 "Mauritania (موريتانيا)",
45436 "Mauritius (Moris)",
45457 "Moldova (Republica Moldova)",
45467 "Mongolia (Монгол)",
45472 "Montenegro (Crna Gora)",
45482 "Morocco (المغرب)",
45488 "Mozambique (Moçambique)",
45493 "Myanmar (Burma) (မြန်မာ)",
45498 "Namibia (Namibië)",
45513 "Netherlands (Nederland)",
45518 "New Caledonia (Nouvelle-Calédonie)",
45553 "North Korea (조선 민주주의 인민 공화국)",
45558 "Northern Mariana Islands",
45574 "Pakistan (پاکستان)",
45584 "Palestine (فلسطين)",
45594 "Papua New Guinea",
45636 "Réunion (La Réunion)",
45642 "Romania (România)",
45658 "Saint Barthélemy",
45669 "Saint Kitts and Nevis",
45679 "Saint Martin (Saint-Martin (partie française))",
45685 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
45690 "Saint Vincent and the Grenadines",
45705 "São Tomé and Príncipe (São Tomé e Príncipe)",
45710 "Saudi Arabia (المملكة العربية السعودية)",
45715 "Senegal (Sénégal)",
45745 "Slovakia (Slovensko)",
45750 "Slovenia (Slovenija)",
45760 "Somalia (Soomaaliya)",
45770 "South Korea (대한민국)",
45775 "South Sudan (جنوب السودان)",
45785 "Sri Lanka (ශ්රී ලංකාව)",
45790 "Sudan (السودان)",
45800 "Svalbard and Jan Mayen",
45811 "Sweden (Sverige)",
45816 "Switzerland (Schweiz)",
45821 "Syria (سوريا)",
45866 "Trinidad and Tobago",
45871 "Tunisia (تونس)",
45876 "Turkey (Türkiye)",
45886 "Turks and Caicos Islands",
45896 "U.S. Virgin Islands",
45906 "Ukraine (Україна)",
45911 "United Arab Emirates (الإمارات العربية المتحدة)",
45933 "Uzbekistan (Oʻzbekiston)",
45943 "Vatican City (Città del Vaticano)",
45954 "Vietnam (Việt Nam)",
45959 "Wallis and Futuna (Wallis-et-Futuna)",
45964 "Western Sahara (الصحراء الغربية)",
45970 "Yemen (اليمن)",
45994 * This script refer to:
45995 * Title: International Telephone Input
45996 * Author: Jack O'Connor
45997 * Code version: v12.1.12
45998 * Availability: https://github.com/jackocnr/intl-tel-input.git
46002 * @class Roo.bootstrap.form.PhoneInput
46003 * @extends Roo.bootstrap.form.TriggerField
46004 * An input with International dial-code selection
46006 * @cfg {String} defaultDialCode default '+852'
46007 * @cfg {Array} preferedCountries default []
46010 * Create a new PhoneInput.
46011 * @param {Object} config Configuration options
46014 Roo.bootstrap.form.PhoneInput = function(config) {
46015 Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
46018 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
46020 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
46022 listWidth: undefined,
46024 selectedClass: 'active',
46026 invalidClass : "has-warning",
46028 validClass: 'has-success',
46030 allowed: '0123456789',
46035 * @cfg {String} defaultDialCode The default dial code when initializing the input
46037 defaultDialCode: '+852',
46040 * @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
46042 preferedCountries: false,
46044 getAutoCreate : function()
46046 var data = Roo.bootstrap.form.PhoneInputData();
46047 var align = this.labelAlign || this.parentLabelAlign();
46050 this.allCountries = [];
46051 this.dialCodeMapping = [];
46053 for (var i = 0; i < data.length; i++) {
46055 this.allCountries[i] = {
46059 priority: c[3] || 0,
46060 areaCodes: c[4] || null
46062 this.dialCodeMapping[c[2]] = {
46065 priority: c[3] || 0,
46066 areaCodes: c[4] || null
46078 // type: 'number', -- do not use number - we get the flaky up/down arrows.
46079 maxlength: this.max_length,
46080 cls : 'form-control tel-input',
46081 autocomplete: 'new-password'
46084 var hiddenInput = {
46087 cls: 'hidden-tel-input'
46091 hiddenInput.name = this.name;
46094 if (this.disabled) {
46095 input.disabled = true;
46098 var flag_container = {
46115 cls: this.hasFeedback ? 'has-feedback' : '',
46121 cls: 'dial-code-holder',
46128 cls: 'roo-select2-container input-group',
46135 if (this.fieldLabel.length) {
46138 tooltip: 'This field is required'
46144 cls: 'control-label',
46150 html: this.fieldLabel
46153 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46159 if(this.indicatorpos == 'right') {
46160 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46167 if(align == 'left') {
46175 if(this.labelWidth > 12){
46176 label.style = "width: " + this.labelWidth + 'px';
46178 if(this.labelWidth < 13 && this.labelmd == 0){
46179 this.labelmd = this.labelWidth;
46181 if(this.labellg > 0){
46182 label.cls += ' col-lg-' + this.labellg;
46183 input.cls += ' col-lg-' + (12 - this.labellg);
46185 if(this.labelmd > 0){
46186 label.cls += ' col-md-' + this.labelmd;
46187 container.cls += ' col-md-' + (12 - this.labelmd);
46189 if(this.labelsm > 0){
46190 label.cls += ' col-sm-' + this.labelsm;
46191 container.cls += ' col-sm-' + (12 - this.labelsm);
46193 if(this.labelxs > 0){
46194 label.cls += ' col-xs-' + this.labelxs;
46195 container.cls += ' col-xs-' + (12 - this.labelxs);
46205 var settings = this;
46207 ['xs','sm','md','lg'].map(function(size){
46208 if (settings[size]) {
46209 cfg.cls += ' col-' + size + '-' + settings[size];
46213 this.store = new Roo.data.Store({
46214 proxy : new Roo.data.MemoryProxy({}),
46215 reader : new Roo.data.JsonReader({
46226 'name' : 'dialCode',
46230 'name' : 'priority',
46234 'name' : 'areaCodes',
46241 if(!this.preferedCountries) {
46242 this.preferedCountries = [
46249 var p = this.preferedCountries.reverse();
46252 for (var i = 0; i < p.length; i++) {
46253 for (var j = 0; j < this.allCountries.length; j++) {
46254 if(this.allCountries[j].iso2 == p[i]) {
46255 var t = this.allCountries[j];
46256 this.allCountries.splice(j,1);
46257 this.allCountries.unshift(t);
46263 this.store.proxy.data = {
46265 data: this.allCountries
46271 initEvents : function()
46274 Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
46276 this.indicator = this.indicatorEl();
46277 this.flag = this.flagEl();
46278 this.dialCodeHolder = this.dialCodeHolderEl();
46280 this.trigger = this.el.select('div.flag-box',true).first();
46281 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
46286 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
46287 _this.list.setWidth(lw);
46290 this.list.on('mouseover', this.onViewOver, this);
46291 this.list.on('mousemove', this.onViewMove, this);
46292 this.inputEl().on("keyup", this.onKeyUp, this);
46293 this.inputEl().on("keypress", this.onKeyPress, this);
46295 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
46297 this.view = new Roo.View(this.list, this.tpl, {
46298 singleSelect:true, store: this.store, selectedClass: this.selectedClass
46301 this.view.on('click', this.onViewClick, this);
46302 this.setValue(this.defaultDialCode);
46305 onTriggerClick : function(e)
46307 Roo.log('trigger click');
46312 if(this.isExpanded()){
46314 this.hasFocus = false;
46316 this.store.load({});
46317 this.hasFocus = true;
46322 isExpanded : function()
46324 return this.list.isVisible();
46327 collapse : function()
46329 if(!this.isExpanded()){
46333 Roo.get(document).un('mousedown', this.collapseIf, this);
46334 Roo.get(document).un('mousewheel', this.collapseIf, this);
46335 this.fireEvent('collapse', this);
46339 expand : function()
46343 if(this.isExpanded() || !this.hasFocus){
46347 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
46348 this.list.setWidth(lw);
46351 this.restrictHeight();
46353 Roo.get(document).on('mousedown', this.collapseIf, this);
46354 Roo.get(document).on('mousewheel', this.collapseIf, this);
46356 this.fireEvent('expand', this);
46359 restrictHeight : function()
46361 this.list.alignTo(this.inputEl(), this.listAlign);
46362 this.list.alignTo(this.inputEl(), this.listAlign);
46365 onViewOver : function(e, t)
46367 if(this.inKeyMode){
46370 var item = this.view.findItemFromChild(t);
46373 var index = this.view.indexOf(item);
46374 this.select(index, false);
46379 onViewClick : function(view, doFocus, el, e)
46381 var index = this.view.getSelectedIndexes()[0];
46383 var r = this.store.getAt(index);
46386 this.onSelect(r, index);
46388 if(doFocus !== false && !this.blockFocus){
46389 this.inputEl().focus();
46393 onViewMove : function(e, t)
46395 this.inKeyMode = false;
46398 select : function(index, scrollIntoView)
46400 this.selectedIndex = index;
46401 this.view.select(index);
46402 if(scrollIntoView !== false){
46403 var el = this.view.getNode(index);
46405 this.list.scrollChildIntoView(el, false);
46410 createList : function()
46412 this.list = Roo.get(document.body).createChild({
46414 cls: 'typeahead typeahead-long dropdown-menu tel-list',
46415 style: 'display:none'
46418 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
46421 collapseIf : function(e)
46423 var in_combo = e.within(this.el);
46424 var in_list = e.within(this.list);
46425 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
46427 if (in_combo || in_list || is_list) {
46433 onSelect : function(record, index)
46435 if(this.fireEvent('beforeselect', this, record, index) !== false){
46437 this.setFlagClass(record.data.iso2);
46438 this.setDialCode(record.data.dialCode);
46439 this.hasFocus = false;
46441 this.fireEvent('select', this, record, index);
46445 flagEl : function()
46447 var flag = this.el.select('div.flag',true).first();
46454 dialCodeHolderEl : function()
46456 var d = this.el.select('input.dial-code-holder',true).first();
46463 setDialCode : function(v)
46465 this.dialCodeHolder.dom.value = '+'+v;
46468 setFlagClass : function(n)
46470 this.flag.dom.className = 'flag '+n;
46473 getValue : function()
46475 var v = this.inputEl().getValue();
46476 if(this.dialCodeHolder) {
46477 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
46482 setValue : function(v)
46484 var d = this.getDialCode(v);
46486 //invalid dial code
46487 if(v.length == 0 || !d || d.length == 0) {
46489 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
46490 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
46496 this.setFlagClass(this.dialCodeMapping[d].iso2);
46497 this.setDialCode(d);
46498 this.inputEl().dom.value = v.replace('+'+d,'');
46499 this.hiddenEl().dom.value = this.getValue();
46504 getDialCode : function(v)
46508 if (v.length == 0) {
46509 return this.dialCodeHolder.dom.value;
46513 if (v.charAt(0) != "+") {
46516 var numericChars = "";
46517 for (var i = 1; i < v.length; i++) {
46518 var c = v.charAt(i);
46521 if (this.dialCodeMapping[numericChars]) {
46522 dialCode = v.substr(1, i);
46524 if (numericChars.length == 4) {
46534 this.setValue(this.defaultDialCode);
46538 hiddenEl : function()
46540 return this.el.select('input.hidden-tel-input',true).first();
46543 // after setting val
46544 onKeyUp : function(e){
46545 this.setValue(this.getValue());
46548 onKeyPress : function(e){
46549 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
46556 * @class Roo.bootstrap.form.MoneyField
46557 * @extends Roo.bootstrap.form.ComboBox
46558 * Bootstrap MoneyField class
46561 * Create a new MoneyField.
46562 * @param {Object} config Configuration options
46565 Roo.bootstrap.form.MoneyField = function(config) {
46567 Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
46571 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
46574 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
46576 allowDecimals : true,
46578 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
46580 decimalSeparator : ".",
46582 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
46584 decimalPrecision : 0,
46586 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
46588 allowNegative : true,
46590 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
46594 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
46596 minValue : Number.NEGATIVE_INFINITY,
46598 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
46600 maxValue : Number.MAX_VALUE,
46602 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
46604 minText : "The minimum value for this field is {0}",
46606 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
46608 maxText : "The maximum value for this field is {0}",
46610 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
46611 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
46613 nanText : "{0} is not a valid number",
46615 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
46619 * @cfg {String} defaults currency of the MoneyField
46620 * value should be in lkey
46622 defaultCurrency : false,
46624 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
46626 thousandsDelimiter : false,
46628 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
46637 * @cfg {Roo.data.Store} store Store to lookup currency??
46641 getAutoCreate : function()
46643 var align = this.labelAlign || this.parentLabelAlign();
46655 cls : 'form-control roo-money-amount-input',
46656 autocomplete: 'new-password'
46659 var hiddenInput = {
46663 cls: 'hidden-number-input'
46666 if(this.max_length) {
46667 input.maxlength = this.max_length;
46671 hiddenInput.name = this.name;
46674 if (this.disabled) {
46675 input.disabled = true;
46678 var clg = 12 - this.inputlg;
46679 var cmd = 12 - this.inputmd;
46680 var csm = 12 - this.inputsm;
46681 var cxs = 12 - this.inputxs;
46685 cls : 'row roo-money-field',
46689 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
46693 cls: 'roo-select2-container input-group',
46697 cls : 'form-control roo-money-currency-input',
46698 autocomplete: 'new-password',
46700 name : this.currencyName
46704 cls : 'input-group-addon',
46718 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
46722 cls: this.hasFeedback ? 'has-feedback' : '',
46733 if (this.fieldLabel.length) {
46736 tooltip: 'This field is required'
46742 cls: 'control-label',
46748 html: this.fieldLabel
46751 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46757 if(this.indicatorpos == 'right') {
46758 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46765 if(align == 'left') {
46773 if(this.labelWidth > 12){
46774 label.style = "width: " + this.labelWidth + 'px';
46776 if(this.labelWidth < 13 && this.labelmd == 0){
46777 this.labelmd = this.labelWidth;
46779 if(this.labellg > 0){
46780 label.cls += ' col-lg-' + this.labellg;
46781 input.cls += ' col-lg-' + (12 - this.labellg);
46783 if(this.labelmd > 0){
46784 label.cls += ' col-md-' + this.labelmd;
46785 container.cls += ' col-md-' + (12 - this.labelmd);
46787 if(this.labelsm > 0){
46788 label.cls += ' col-sm-' + this.labelsm;
46789 container.cls += ' col-sm-' + (12 - this.labelsm);
46791 if(this.labelxs > 0){
46792 label.cls += ' col-xs-' + this.labelxs;
46793 container.cls += ' col-xs-' + (12 - this.labelxs);
46804 var settings = this;
46806 ['xs','sm','md','lg'].map(function(size){
46807 if (settings[size]) {
46808 cfg.cls += ' col-' + size + '-' + settings[size];
46815 initEvents : function()
46817 this.indicator = this.indicatorEl();
46819 this.initCurrencyEvent();
46821 this.initNumberEvent();
46824 initCurrencyEvent : function()
46827 throw "can not find store for combo";
46830 this.store = Roo.factory(this.store, Roo.data);
46831 this.store.parent = this;
46835 this.triggerEl = this.el.select('.input-group-addon', true).first();
46837 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
46842 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
46843 _this.list.setWidth(lw);
46846 this.list.on('mouseover', this.onViewOver, this);
46847 this.list.on('mousemove', this.onViewMove, this);
46848 this.list.on('scroll', this.onViewScroll, this);
46851 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
46854 this.view = new Roo.View(this.list, this.tpl, {
46855 singleSelect:true, store: this.store, selectedClass: this.selectedClass
46858 this.view.on('click', this.onViewClick, this);
46860 this.store.on('beforeload', this.onBeforeLoad, this);
46861 this.store.on('load', this.onLoad, this);
46862 this.store.on('loadexception', this.onLoadException, this);
46864 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
46865 "up" : function(e){
46866 this.inKeyMode = true;
46870 "down" : function(e){
46871 if(!this.isExpanded()){
46872 this.onTriggerClick();
46874 this.inKeyMode = true;
46879 "enter" : function(e){
46882 if(this.fireEvent("specialkey", this, e)){
46883 this.onViewClick(false);
46889 "esc" : function(e){
46893 "tab" : function(e){
46896 if(this.fireEvent("specialkey", this, e)){
46897 this.onViewClick(false);
46905 doRelay : function(foo, bar, hname){
46906 if(hname == 'down' || this.scope.isExpanded()){
46907 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
46915 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
46919 initNumberEvent : function(e)
46921 this.inputEl().on("keydown" , this.fireKey, this);
46922 this.inputEl().on("focus", this.onFocus, this);
46923 this.inputEl().on("blur", this.onBlur, this);
46925 this.inputEl().relayEvent('keyup', this);
46927 if(this.indicator){
46928 this.indicator.addClass('invisible');
46931 this.originalValue = this.getValue();
46933 if(this.validationEvent == 'keyup'){
46934 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
46935 this.inputEl().on('keyup', this.filterValidation, this);
46937 else if(this.validationEvent !== false){
46938 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
46941 if(this.selectOnFocus){
46942 this.on("focus", this.preFocus, this);
46945 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
46946 this.inputEl().on("keypress", this.filterKeys, this);
46948 this.inputEl().relayEvent('keypress', this);
46951 var allowed = "0123456789";
46953 if(this.allowDecimals){
46954 allowed += this.decimalSeparator;
46957 if(this.allowNegative){
46961 if(this.thousandsDelimiter) {
46965 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
46967 var keyPress = function(e){
46969 var k = e.getKey();
46971 var c = e.getCharCode();
46974 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
46975 allowed.indexOf(String.fromCharCode(c)) === -1
46981 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
46985 if(allowed.indexOf(String.fromCharCode(c)) === -1){
46990 this.inputEl().on("keypress", keyPress, this);
46994 onTriggerClick : function(e)
47001 this.loadNext = false;
47003 if(this.isExpanded()){
47008 this.hasFocus = true;
47010 if(this.triggerAction == 'all') {
47011 this.doQuery(this.allQuery, true);
47015 this.doQuery(this.getRawValue());
47018 getCurrency : function()
47020 var v = this.currencyEl().getValue();
47025 restrictHeight : function()
47027 this.list.alignTo(this.currencyEl(), this.listAlign);
47028 this.list.alignTo(this.currencyEl(), this.listAlign);
47031 onViewClick : function(view, doFocus, el, e)
47033 var index = this.view.getSelectedIndexes()[0];
47035 var r = this.store.getAt(index);
47038 this.onSelect(r, index);
47042 onSelect : function(record, index){
47044 if(this.fireEvent('beforeselect', this, record, index) !== false){
47046 this.setFromCurrencyData(index > -1 ? record.data : false);
47050 this.fireEvent('select', this, record, index);
47054 setFromCurrencyData : function(o)
47058 this.lastCurrency = o;
47060 if (this.currencyField) {
47061 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
47063 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
47066 this.lastSelectionText = currency;
47068 //setting default currency
47069 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
47070 this.setCurrency(this.defaultCurrency);
47074 this.setCurrency(currency);
47077 setFromData : function(o)
47081 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
47083 this.setFromCurrencyData(c);
47088 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
47090 Roo.log('no value set for '+ (this.name ? this.name : this.id));
47093 this.setValue(value);
47097 setCurrency : function(v)
47099 this.currencyValue = v;
47102 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
47107 setValue : function(v)
47109 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
47115 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
47117 this.inputEl().dom.value = (v == '') ? '' :
47118 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
47120 if(!this.allowZero && v === '0') {
47121 this.hiddenEl().dom.value = '';
47122 this.inputEl().dom.value = '';
47129 getRawValue : function()
47131 var v = this.inputEl().getValue();
47136 getValue : function()
47138 return this.fixPrecision(this.parseValue(this.getRawValue()));
47141 parseValue : function(value)
47143 if(this.thousandsDelimiter) {
47145 r = new RegExp(",", "g");
47146 value = value.replace(r, "");
47149 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
47150 return isNaN(value) ? '' : value;
47154 fixPrecision : function(value)
47156 if(this.thousandsDelimiter) {
47158 r = new RegExp(",", "g");
47159 value = value.replace(r, "");
47162 var nan = isNaN(value);
47164 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
47165 return nan ? '' : value;
47167 return parseFloat(value).toFixed(this.decimalPrecision);
47170 decimalPrecisionFcn : function(v)
47172 return Math.floor(v);
47175 validateValue : function(value)
47177 if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
47181 var num = this.parseValue(value);
47184 this.markInvalid(String.format(this.nanText, value));
47188 if(num < this.minValue){
47189 this.markInvalid(String.format(this.minText, this.minValue));
47193 if(num > this.maxValue){
47194 this.markInvalid(String.format(this.maxText, this.maxValue));
47201 validate : function()
47203 if(this.disabled || this.allowBlank){
47208 var currency = this.getCurrency();
47210 if(this.validateValue(this.getRawValue()) && currency.length){
47215 this.markInvalid();
47219 getName: function()
47224 beforeBlur : function()
47230 var v = this.parseValue(this.getRawValue());
47237 onBlur : function()
47241 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
47242 //this.el.removeClass(this.focusClass);
47245 this.hasFocus = false;
47247 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
47251 var v = this.getValue();
47253 if(String(v) !== String(this.startValue)){
47254 this.fireEvent('change', this, v, this.startValue);
47257 this.fireEvent("blur", this);
47260 inputEl : function()
47262 return this.el.select('.roo-money-amount-input', true).first();
47265 currencyEl : function()
47267 return this.el.select('.roo-money-currency-input', true).first();
47270 hiddenEl : function()
47272 return this.el.select('input.hidden-number-input',true).first();
47276 * @class Roo.bootstrap.BezierSignature
47277 * @extends Roo.bootstrap.Component
47278 * Bootstrap BezierSignature class
47279 * This script refer to:
47280 * Title: Signature Pad
47282 * Availability: https://github.com/szimek/signature_pad
47285 * Create a new BezierSignature
47286 * @param {Object} config The config object
47289 Roo.bootstrap.BezierSignature = function(config){
47290 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
47296 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
47303 mouse_btn_down: true,
47306 * @cfg {int} canvas height
47308 canvas_height: '200px',
47311 * @cfg {float|function} Radius of a single dot.
47316 * @cfg {float} Minimum width of a line. Defaults to 0.5.
47321 * @cfg {float} Maximum width of a line. Defaults to 2.5.
47326 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
47331 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
47336 * @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.
47338 bg_color: 'rgba(0, 0, 0, 0)',
47341 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
47343 dot_color: 'black',
47346 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
47348 velocity_filter_weight: 0.7,
47351 * @cfg {function} Callback when stroke begin.
47356 * @cfg {function} Callback when stroke end.
47360 getAutoCreate : function()
47362 var cls = 'roo-signature column';
47365 cls += ' ' + this.cls;
47375 for(var i = 0; i < col_sizes.length; i++) {
47376 if(this[col_sizes[i]]) {
47377 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
47387 cls: 'roo-signature-body',
47391 cls: 'roo-signature-body-canvas',
47392 height: this.canvas_height,
47393 width: this.canvas_width
47400 style: 'display: none'
47408 initEvents: function()
47410 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
47412 var canvas = this.canvasEl();
47414 // mouse && touch event swapping...
47415 canvas.dom.style.touchAction = 'none';
47416 canvas.dom.style.msTouchAction = 'none';
47418 this.mouse_btn_down = false;
47419 canvas.on('mousedown', this._handleMouseDown, this);
47420 canvas.on('mousemove', this._handleMouseMove, this);
47421 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
47423 if (window.PointerEvent) {
47424 canvas.on('pointerdown', this._handleMouseDown, this);
47425 canvas.on('pointermove', this._handleMouseMove, this);
47426 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
47429 if ('ontouchstart' in window) {
47430 canvas.on('touchstart', this._handleTouchStart, this);
47431 canvas.on('touchmove', this._handleTouchMove, this);
47432 canvas.on('touchend', this._handleTouchEnd, this);
47435 Roo.EventManager.onWindowResize(this.resize, this, true);
47437 // file input event
47438 this.fileEl().on('change', this.uploadImage, this);
47445 resize: function(){
47447 var canvas = this.canvasEl().dom;
47448 var ctx = this.canvasElCtx();
47449 var img_data = false;
47451 if(canvas.width > 0) {
47452 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
47454 // setting canvas width will clean img data
47457 var style = window.getComputedStyle ?
47458 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
47460 var padding_left = parseInt(style.paddingLeft) || 0;
47461 var padding_right = parseInt(style.paddingRight) || 0;
47463 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
47466 ctx.putImageData(img_data, 0, 0);
47470 _handleMouseDown: function(e)
47472 if (e.browserEvent.which === 1) {
47473 this.mouse_btn_down = true;
47474 this.strokeBegin(e);
47478 _handleMouseMove: function (e)
47480 if (this.mouse_btn_down) {
47481 this.strokeMoveUpdate(e);
47485 _handleMouseUp: function (e)
47487 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
47488 this.mouse_btn_down = false;
47493 _handleTouchStart: function (e) {
47495 e.preventDefault();
47496 if (e.browserEvent.targetTouches.length === 1) {
47497 // var touch = e.browserEvent.changedTouches[0];
47498 // this.strokeBegin(touch);
47500 this.strokeBegin(e); // assume e catching the correct xy...
47504 _handleTouchMove: function (e) {
47505 e.preventDefault();
47506 // var touch = event.targetTouches[0];
47507 // _this._strokeMoveUpdate(touch);
47508 this.strokeMoveUpdate(e);
47511 _handleTouchEnd: function (e) {
47512 var wasCanvasTouched = e.target === this.canvasEl().dom;
47513 if (wasCanvasTouched) {
47514 e.preventDefault();
47515 // var touch = event.changedTouches[0];
47516 // _this._strokeEnd(touch);
47521 reset: function () {
47522 this._lastPoints = [];
47523 this._lastVelocity = 0;
47524 this._lastWidth = (this.min_width + this.max_width) / 2;
47525 this.canvasElCtx().fillStyle = this.dot_color;
47528 strokeMoveUpdate: function(e)
47530 this.strokeUpdate(e);
47532 if (this.throttle) {
47533 this.throttleStroke(this.strokeUpdate, this.throttle);
47536 this.strokeUpdate(e);
47540 strokeBegin: function(e)
47542 var newPointGroup = {
47543 color: this.dot_color,
47547 if (typeof this.onBegin === 'function') {
47551 this.curve_data.push(newPointGroup);
47553 this.strokeUpdate(e);
47556 strokeUpdate: function(e)
47558 var rect = this.canvasEl().dom.getBoundingClientRect();
47559 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
47560 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
47561 var lastPoints = lastPointGroup.points;
47562 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
47563 var isLastPointTooClose = lastPoint
47564 ? point.distanceTo(lastPoint) <= this.min_distance
47566 var color = lastPointGroup.color;
47567 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
47568 var curve = this.addPoint(point);
47570 this.drawDot({color: color, point: point});
47573 this.drawCurve({color: color, curve: curve});
47583 strokeEnd: function(e)
47585 this.strokeUpdate(e);
47586 if (typeof this.onEnd === 'function') {
47591 addPoint: function (point) {
47592 var _lastPoints = this._lastPoints;
47593 _lastPoints.push(point);
47594 if (_lastPoints.length > 2) {
47595 if (_lastPoints.length === 3) {
47596 _lastPoints.unshift(_lastPoints[0]);
47598 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
47599 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
47600 _lastPoints.shift();
47606 calculateCurveWidths: function (startPoint, endPoint) {
47607 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
47608 (1 - this.velocity_filter_weight) * this._lastVelocity;
47610 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
47613 start: this._lastWidth
47616 this._lastVelocity = velocity;
47617 this._lastWidth = newWidth;
47621 drawDot: function (_a) {
47622 var color = _a.color, point = _a.point;
47623 var ctx = this.canvasElCtx();
47624 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
47626 this.drawCurveSegment(point.x, point.y, width);
47628 ctx.fillStyle = color;
47632 drawCurve: function (_a) {
47633 var color = _a.color, curve = _a.curve;
47634 var ctx = this.canvasElCtx();
47635 var widthDelta = curve.endWidth - curve.startWidth;
47636 var drawSteps = Math.floor(curve.length()) * 2;
47638 ctx.fillStyle = color;
47639 for (var i = 0; i < drawSteps; i += 1) {
47640 var t = i / drawSteps;
47646 var x = uuu * curve.startPoint.x;
47647 x += 3 * uu * t * curve.control1.x;
47648 x += 3 * u * tt * curve.control2.x;
47649 x += ttt * curve.endPoint.x;
47650 var y = uuu * curve.startPoint.y;
47651 y += 3 * uu * t * curve.control1.y;
47652 y += 3 * u * tt * curve.control2.y;
47653 y += ttt * curve.endPoint.y;
47654 var width = curve.startWidth + ttt * widthDelta;
47655 this.drawCurveSegment(x, y, width);
47661 drawCurveSegment: function (x, y, width) {
47662 var ctx = this.canvasElCtx();
47664 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
47665 this.is_empty = false;
47670 var ctx = this.canvasElCtx();
47671 var canvas = this.canvasEl().dom;
47672 ctx.fillStyle = this.bg_color;
47673 ctx.clearRect(0, 0, canvas.width, canvas.height);
47674 ctx.fillRect(0, 0, canvas.width, canvas.height);
47675 this.curve_data = [];
47677 this.is_empty = true;
47682 return this.el.select('input',true).first();
47685 canvasEl: function()
47687 return this.el.select('canvas',true).first();
47690 canvasElCtx: function()
47692 return this.el.select('canvas',true).first().dom.getContext('2d');
47695 getImage: function(type)
47697 if(this.is_empty) {
47702 return this.canvasEl().dom.toDataURL('image/'+type, 1);
47705 drawFromImage: function(img_src)
47707 var img = new Image();
47709 img.onload = function(){
47710 this.canvasElCtx().drawImage(img, 0, 0);
47715 this.is_empty = false;
47718 selectImage: function()
47720 this.fileEl().dom.click();
47723 uploadImage: function(e)
47725 var reader = new FileReader();
47727 reader.onload = function(e){
47728 var img = new Image();
47729 img.onload = function(){
47731 this.canvasElCtx().drawImage(img, 0, 0);
47733 img.src = e.target.result;
47736 reader.readAsDataURL(e.target.files[0]);
47739 // Bezier Point Constructor
47740 Point: (function () {
47741 function Point(x, y, time) {
47744 this.time = time || Date.now();
47746 Point.prototype.distanceTo = function (start) {
47747 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
47749 Point.prototype.equals = function (other) {
47750 return this.x === other.x && this.y === other.y && this.time === other.time;
47752 Point.prototype.velocityFrom = function (start) {
47753 return this.time !== start.time
47754 ? this.distanceTo(start) / (this.time - start.time)
47761 // Bezier Constructor
47762 Bezier: (function () {
47763 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
47764 this.startPoint = startPoint;
47765 this.control2 = control2;
47766 this.control1 = control1;
47767 this.endPoint = endPoint;
47768 this.startWidth = startWidth;
47769 this.endWidth = endWidth;
47771 Bezier.fromPoints = function (points, widths, scope) {
47772 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
47773 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
47774 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
47776 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
47777 var dx1 = s1.x - s2.x;
47778 var dy1 = s1.y - s2.y;
47779 var dx2 = s2.x - s3.x;
47780 var dy2 = s2.y - s3.y;
47781 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
47782 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
47783 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
47784 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
47785 var dxm = m1.x - m2.x;
47786 var dym = m1.y - m2.y;
47787 var k = l2 / (l1 + l2);
47788 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
47789 var tx = s2.x - cm.x;
47790 var ty = s2.y - cm.y;
47792 c1: new scope.Point(m1.x + tx, m1.y + ty),
47793 c2: new scope.Point(m2.x + tx, m2.y + ty)
47796 Bezier.prototype.length = function () {
47801 for (var i = 0; i <= steps; i += 1) {
47803 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
47804 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
47806 var xdiff = cx - px;
47807 var ydiff = cy - py;
47808 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
47815 Bezier.prototype.point = function (t, start, c1, c2, end) {
47816 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
47817 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
47818 + (3.0 * c2 * (1.0 - t) * t * t)
47819 + (end * t * t * t);
47824 throttleStroke: function(fn, wait) {
47825 if (wait === void 0) { wait = 250; }
47827 var timeout = null;
47831 var later = function () {
47832 previous = Date.now();
47834 result = fn.apply(storedContext, storedArgs);
47836 storedContext = null;
47840 return function wrapper() {
47842 for (var _i = 0; _i < arguments.length; _i++) {
47843 args[_i] = arguments[_i];
47845 var now = Date.now();
47846 var remaining = wait - (now - previous);
47847 storedContext = this;
47849 if (remaining <= 0 || remaining > wait) {
47851 clearTimeout(timeout);
47855 result = fn.apply(storedContext, storedArgs);
47857 storedContext = null;
47861 else if (!timeout) {
47862 timeout = window.setTimeout(later, remaining);
47872 // old names for form elements
47873 Roo.bootstrap.Form = Roo.bootstrap.form.Form;
47874 Roo.bootstrap.Input = Roo.bootstrap.form.Input;
47875 Roo.bootstrap.TextArea = Roo.bootstrap.form.TextArea;
47876 Roo.bootstrap.TriggerField = Roo.bootstrap.form.TriggerField;
47877 Roo.bootstrap.ComboBox = Roo.bootstrap.form.ComboBox;
47878 Roo.bootstrap.DateField = Roo.bootstrap.form.DateField;
47879 Roo.bootstrap.TimeField = Roo.bootstrap.form.TimeField;
47880 Roo.bootstrap.MonthField = Roo.bootstrap.form.MonthField;
47881 Roo.bootstrap.CheckBox = Roo.bootstrap.form.CheckBox;
47882 Roo.bootstrap.Radio = Roo.bootstrap.form.Radio;
47883 Roo.bootstrap.RadioSet = Roo.bootstrap.form.RadioSet;
47884 Roo.bootstrap.SecurePass = Roo.bootstrap.form.SecurePass;
47885 Roo.bootstrap.FieldLabel = Roo.bootstrap.form.FieldLabel;
47886 Roo.bootstrap.DateSplitField= Roo.bootstrap.form.DateSplitField;
47887 Roo.bootstrap.NumberField = Roo.bootstrap.form.NumberField;
47888 Roo.bootstrap.PhoneInput = Roo.bootstrap.form.PhoneInput;
47889 Roo.bootstrap.PhoneInputData= Roo.bootstrap.form.PhoneInputData;
47890 Roo.bootstrap.MoneyField = Roo.bootstrap.form.MoneyField;
47891 Roo.bootstrap.HtmlEditor = Roo.bootstrap.form.HtmlEditor;
47892 Roo.bootstrap.HtmlEditor.ToolbarStandard = Roo.bootstrap.form.HtmlEditorToolbarStandard;
47893 Roo.bootstrap.Markdown = Roo.bootstrap.form.Markdown;
47894 Roo.bootstrap.CardUploader = Roo.bootstrap.form.CardUploader;// depricated.
47895 Roo.bootstrap.Navbar = Roo.bootstrap.nav.Bar;
47896 Roo.bootstrap.NavGroup = Roo.bootstrap.nav.Group;
47897 Roo.bootstrap.NavHeaderbar = Roo.bootstrap.nav.Headerbar;
47898 Roo.bootstrap.NavItem = Roo.bootstrap.nav.Item;
47900 Roo.bootstrap.NavProgressBar = Roo.bootstrap.nav.ProgressBar;
47901 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
47903 Roo.bootstrap.NavSidebar = Roo.bootstrap.nav.Sidebar;
47904 Roo.bootstrap.NavSidebarItem = Roo.bootstrap.nav.SidebarItem;
47906 Roo.bootstrap.NavSimplebar = Roo.bootstrap.nav.Simplebar;// deprciated
47907 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
47908 Roo.bootstrap.MenuItem = Roo.bootstrap.menu.Item;
47909 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator