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 Roo.bootstrap.Component.superclass.constructor.call(this, config);
254 * @event childrenrendered
255 * Fires when the children have been rendered..
256 * @param {Roo.bootstrap.Component} this
258 "childrenrendered" : true
267 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
270 allowDomMove : false, // to stop relocations in parent onRender...
280 * Initialize Events for the element
282 initEvents : function() { },
288 can_build_overlaid : true,
290 container_method : false,
297 // returns the parent component..
298 return Roo.ComponentMgr.get(this.parentId)
304 onRender : function(ct, position)
306 // Roo.log("Call onRender: " + this.xtype);
308 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
311 if (this.el.attr('xtype')) {
312 this.el.attr('xtypex', this.el.attr('xtype'));
313 this.el.dom.removeAttribute('xtype');
323 var cfg = Roo.apply({}, this.getAutoCreate());
325 cfg.id = this.id || Roo.id();
327 // fill in the extra attributes
328 if (this.xattr && typeof(this.xattr) =='object') {
329 for (var i in this.xattr) {
330 cfg[i] = this.xattr[i];
335 cfg.dataId = this.dataId;
339 cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
342 if (this.style) { // fixme needs to support more complex style data.
343 cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
347 cfg.name = this.name;
350 this.el = ct.createChild(cfg, position);
353 this.tooltipEl().attr('tooltip', this.tooltip);
356 if(this.tabIndex !== undefined){
357 this.el.dom.setAttribute('tabIndex', this.tabIndex);
364 * Fetch the element to add children to
365 * @return {Roo.Element} defaults to this.el
367 getChildContainer : function()
371 getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
373 return Roo.get(document.body);
377 * Fetch the element to display the tooltip on.
378 * @return {Roo.Element} defaults to this.el
380 tooltipEl : function()
385 addxtype : function(tree,cntr)
389 cn = Roo.factory(tree);
390 //Roo.log(['addxtype', cn]);
392 cn.parentType = this.xtype; //??
393 cn.parentId = this.id;
395 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
396 if (typeof(cn.container_method) == 'string') {
397 cntr = cn.container_method;
401 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
403 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
405 var build_from_html = Roo.XComponent.build_from_html;
407 var is_body = (tree.xtype == 'Body') ;
409 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
411 var self_cntr_el = Roo.get(this[cntr](false));
413 // do not try and build conditional elements
414 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
418 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
419 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
420 return this.addxtypeChild(tree,cntr, is_body);
423 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
426 return this.addxtypeChild(Roo.apply({}, tree),cntr);
429 Roo.log('skipping render');
435 if (!build_from_html) {
439 // this i think handles overlaying multiple children of the same type
440 // with the sam eelement.. - which might be buggy..
442 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
448 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
452 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
459 addxtypeChild : function (tree, cntr, is_body)
461 Roo.debug && Roo.log('addxtypeChild:' + cntr);
463 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
466 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
467 (typeof(tree['flexy:foreach']) != 'undefined');
471 skip_children = false;
472 // render the element if it's not BODY.
475 // if parent was disabled, then do not try and create the children..
476 if(!this[cntr](true)){
481 cn = Roo.factory(tree);
483 cn.parentType = this.xtype; //??
484 cn.parentId = this.id;
486 var build_from_html = Roo.XComponent.build_from_html;
489 // does the container contain child eleemnts with 'xtype' attributes.
490 // that match this xtype..
491 // note - when we render we create these as well..
492 // so we should check to see if body has xtype set.
493 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
495 var self_cntr_el = Roo.get(this[cntr](false));
496 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
498 //Roo.log(Roo.XComponent.build_from_html);
499 //Roo.log("got echild:");
502 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
503 // and are not displayed -this causes this to use up the wrong element when matching.
504 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
507 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
508 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
514 //echild.dom.removeAttribute('xtype');
516 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
517 Roo.debug && Roo.log(self_cntr_el);
518 Roo.debug && Roo.log(echild);
519 Roo.debug && Roo.log(cn);
525 // if object has flexy:if - then it may or may not be rendered.
526 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
527 // skip a flexy if element.
528 Roo.debug && Roo.log('skipping render');
529 Roo.debug && Roo.log(tree);
531 Roo.debug && Roo.log('skipping all children');
532 skip_children = true;
537 // actually if flexy:foreach is found, we really want to create
538 // multiple copies here...
540 //Roo.log(this[cntr]());
541 // some elements do not have render methods.. like the layouts...
543 if(this[cntr](true) === false){
548 cn.render && cn.render(this[cntr](true));
551 // then add the element..
558 if (typeof (tree.menu) != 'undefined') {
559 tree.menu.parentType = cn.xtype;
560 tree.menu.triggerEl = cn.el;
561 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
565 if (!tree.items || !tree.items.length) {
567 //Roo.log(["no children", this]);
572 var items = tree.items;
575 //Roo.log(items.length);
577 if (!skip_children) {
578 for(var i =0;i < items.length;i++) {
579 // Roo.log(['add child', items[i]]);
580 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
586 //Roo.log("fire childrenrendered");
588 cn.fireEvent('childrenrendered', this);
594 * Set the element that will be used to show or hide
596 setVisibilityEl : function(el)
598 this.visibilityEl = el;
602 * Get the element that will be used to show or hide
604 getVisibilityEl : function()
606 if (typeof(this.visibilityEl) == 'object') {
607 return this.visibilityEl;
610 if (typeof(this.visibilityEl) == 'string') {
611 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
618 * Show a component - removes 'hidden' class
622 if(!this.getVisibilityEl()){
626 this.getVisibilityEl().removeClass(['hidden','d-none']);
628 this.fireEvent('show', this);
633 * Hide a component - adds 'hidden' class
637 if(!this.getVisibilityEl()){
641 this.getVisibilityEl().addClass(['hidden','d-none']);
643 this.fireEvent('hide', this);
656 * @class Roo.bootstrap.Element
657 * @extends Roo.bootstrap.Component
658 * @children Roo.bootstrap.Component
659 * Bootstrap Element class (basically a DIV used to make random stuff )
661 * @cfg {String} html contents of the element
662 * @cfg {String} tag tag of the element
663 * @cfg {String} cls class of the element
664 * @cfg {Boolean} preventDefault (true|false) default false
665 * @cfg {Boolean} clickable (true|false) default false
666 * @cfg {String} role default blank - set to button to force cursor pointer
670 * Create a new Element
671 * @param {Object} config The config object
674 Roo.bootstrap.Element = function(config){
675 Roo.bootstrap.Element.superclass.constructor.call(this, config);
681 * When a element is chick
682 * @param {Roo.bootstrap.Element} this
683 * @param {Roo.EventObject} e
691 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
696 preventDefault: false,
701 getAutoCreate : function(){
705 // cls: this.cls, double assign in parent class Component.js :: onRender
708 if (this.role !== false) {
709 cfg.role = this.role;
715 initEvents: function()
717 Roo.bootstrap.Element.superclass.initEvents.call(this);
720 this.el.on('click', this.onClick, this);
726 onClick : function(e)
728 if(this.preventDefault){
732 this.fireEvent('click', this, e); // why was this double click before?
740 getValue : function()
742 return this.el.dom.innerHTML;
745 setValue : function(value)
747 this.el.dom.innerHTML = value;
762 * @class Roo.bootstrap.DropTarget
763 * @extends Roo.bootstrap.Element
764 * Bootstrap DropTarget class
766 * @cfg {string} name dropable name
769 * Create a new Dropable Area
770 * @param {Object} config The config object
773 Roo.bootstrap.DropTarget = function(config){
774 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
780 * When a element is chick
781 * @param {Roo.bootstrap.Element} this
782 * @param {Roo.EventObject} e
788 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
791 getAutoCreate : function(){
796 initEvents: function()
798 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
799 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
802 drop : this.dragDrop.createDelegate(this),
803 enter : this.dragEnter.createDelegate(this),
804 out : this.dragOut.createDelegate(this),
805 over : this.dragOver.createDelegate(this)
809 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
812 dragDrop : function(source,e,data)
814 // user has to decide how to impliment this.
817 //this.fireEvent('drop', this, source, e ,data);
821 dragEnter : function(n, dd, e, data)
823 // probably want to resize the element to match the dropped element..
825 this.originalSize = this.el.getSize();
826 this.el.setSize( n.el.getSize());
827 this.dropZone.DDM.refreshCache(this.name);
828 Roo.log([n, dd, e, data]);
831 dragOut : function(value)
833 // resize back to normal
835 this.el.setSize(this.originalSize);
836 this.dropZone.resetConstraints();
839 dragOver : function()
856 * @class Roo.bootstrap.Body
857 * @extends Roo.bootstrap.Component
858 * @children Roo.bootstrap.Component
859 * @parent none builder
860 * Bootstrap Body class
864 * @param {Object} config The config object
867 Roo.bootstrap.Body = function(config){
869 config = config || {};
871 Roo.bootstrap.Body.superclass.constructor.call(this, config);
872 this.el = Roo.get(config.el ? config.el : document.body );
873 if (this.cls && this.cls.length) {
874 Roo.get(document.body).addClass(this.cls);
878 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
880 is_body : true,// just to make sure it's constructed?
885 onRender : function(ct, position)
887 /* Roo.log("Roo.bootstrap.Body - onRender");
888 if (this.cls && this.cls.length) {
889 Roo.get(document.body).addClass(this.cls);
908 * @class Roo.bootstrap.ButtonGroup
909 * @extends Roo.bootstrap.Component
910 * Bootstrap ButtonGroup class
911 * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
913 * @cfg {String} size lg | sm | xs (default empty normal)
914 * @cfg {String} align vertical | justified (default none)
915 * @cfg {String} direction up | down (default down)
916 * @cfg {Boolean} toolbar false | true
917 * @cfg {Boolean} btn true | false
922 * @param {Object} config The config object
925 Roo.bootstrap.ButtonGroup = function(config){
926 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
929 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
937 getAutoCreate : function(){
943 cfg.html = this.html || cfg.html;
954 if (['vertical','justified'].indexOf(this.align)!==-1) {
955 cfg.cls = 'btn-group-' + this.align;
957 if (this.align == 'justified') {
958 console.log(this.items);
962 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
963 cfg.cls += ' btn-group-' + this.size;
966 if (this.direction == 'up') {
967 cfg.cls += ' dropup' ;
973 * Add a button to the group (similar to NavItem API.)
975 addItem : function(cfg)
977 var cn = new Roo.bootstrap.Button(cfg);
979 cn.parentId = this.id;
980 cn.onRender(this.el, null);
994 * @class Roo.bootstrap.Button
995 * @extends Roo.bootstrap.Component
996 * Bootstrap Button class
997 * @cfg {String} html The button content
998 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
999 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1000 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1001 * @cfg {String} size (lg|sm|xs)
1002 * @cfg {String} tag (a|input|submit)
1003 * @cfg {String} href empty or href
1004 * @cfg {Boolean} disabled default false;
1005 * @cfg {Boolean} isClose default false;
1006 * @cfg {String} glyphicon depricated - use fa
1007 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1008 * @cfg {String} badge text for badge
1009 * @cfg {String} theme (default|glow)
1010 * @cfg {Boolean} inverse dark themed version
1011 * @cfg {Boolean} toggle is it a slidy toggle button
1012 * @cfg {Boolean} pressed default null - if the button ahs active state
1013 * @cfg {String} ontext text for on slidy toggle state
1014 * @cfg {String} offtext text for off slidy toggle state
1015 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1016 * @cfg {Boolean} removeClass remove the standard class..
1017 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1018 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1019 * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
1022 * Create a new button
1023 * @param {Object} config The config object
1027 Roo.bootstrap.Button = function(config){
1028 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1034 * When a button is pressed
1035 * @param {Roo.bootstrap.Button} btn
1036 * @param {Roo.EventObject} e
1041 * When a button is double clicked
1042 * @param {Roo.bootstrap.Button} btn
1043 * @param {Roo.EventObject} e
1048 * After the button has been toggles
1049 * @param {Roo.bootstrap.Button} btn
1050 * @param {Roo.EventObject} e
1051 * @param {boolean} pressed (also available as button.pressed)
1057 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1078 preventDefault: true,
1087 getAutoCreate : function(){
1095 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1096 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1097 this.tag = 'button';
1101 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1103 if (this.toggle == true) {
1106 cls: 'slider-frame roo-button',
1110 'data-on-text':'ON',
1111 'data-off-text':'OFF',
1112 cls: 'slider-button',
1117 // why are we validating the weights?
1118 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1119 cfg.cls += ' ' + this.weight;
1126 cfg.cls += ' close';
1128 cfg["aria-hidden"] = true;
1130 cfg.html = "×";
1136 if (this.theme==='default') {
1137 cfg.cls = 'btn roo-button';
1139 //if (this.parentType != 'Navbar') {
1140 this.weight = this.weight.length ? this.weight : 'default';
1142 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1144 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1145 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1146 cfg.cls += ' btn-' + outline + weight;
1147 if (this.weight == 'default') {
1149 cfg.cls += ' btn-' + this.weight;
1152 } else if (this.theme==='glow') {
1155 cfg.cls = 'btn-glow roo-button';
1157 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1159 cfg.cls += ' ' + this.weight;
1165 this.cls += ' inverse';
1169 if (this.active || this.pressed === true) {
1170 cfg.cls += ' active';
1173 if (this.disabled) {
1174 cfg.disabled = 'disabled';
1178 Roo.log('changing to ul' );
1180 this.glyphicon = 'caret';
1181 if (Roo.bootstrap.version == 4) {
1182 this.fa = 'caret-down';
1187 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1189 //gsRoo.log(this.parentType);
1190 if (this.parentType === 'Navbar' && !this.parent().bar) {
1191 Roo.log('changing to li?');
1200 href : this.href || '#'
1203 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1204 cfg.cls += ' dropdown';
1211 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1213 if (this.glyphicon) {
1214 cfg.html = ' ' + cfg.html;
1219 cls: 'glyphicon glyphicon-' + this.glyphicon
1224 cfg.html = ' ' + cfg.html;
1229 cls: 'fa fas fa-' + this.fa
1239 // cfg.cls='btn roo-button';
1243 var value = cfg.html;
1248 cls: 'glyphicon glyphicon-' + this.glyphicon,
1255 cls: 'fa fas fa-' + this.fa,
1260 var bw = this.badge_weight.length ? this.badge_weight :
1261 (this.weight.length ? this.weight : 'secondary');
1262 bw = bw == 'default' ? 'secondary' : bw;
1268 cls: 'badge badge-' + bw,
1277 cfg.cls += ' dropdown';
1278 cfg.html = typeof(cfg.html) != 'undefined' ?
1279 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1282 if (cfg.tag !== 'a' && this.href !== '') {
1283 throw "Tag must be a to set href.";
1284 } else if (this.href.length > 0) {
1285 cfg.href = this.href;
1288 if(this.removeClass){
1293 cfg.target = this.target;
1298 initEvents: function() {
1299 // Roo.log('init events?');
1300 // Roo.log(this.el.dom);
1303 if (typeof (this.menu) != 'undefined') {
1304 this.menu.parentType = this.xtype;
1305 this.menu.triggerEl = this.el;
1306 this.addxtype(Roo.apply({}, this.menu));
1310 if (this.el.hasClass('roo-button')) {
1311 this.el.on('click', this.onClick, this);
1312 this.el.on('dblclick', this.onDblClick, this);
1314 this.el.select('.roo-button').on('click', this.onClick, this);
1315 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1319 if(this.removeClass){
1320 this.el.on('click', this.onClick, this);
1323 if (this.group === true) {
1324 if (this.pressed === false || this.pressed === true) {
1327 this.pressed = false;
1328 this.setActive(this.pressed);
1333 this.el.enableDisplayMode();
1336 onClick : function(e)
1338 if (this.disabled) {
1342 Roo.log('button on click ');
1343 if(this.href === '' || this.preventDefault){
1352 this.setActive(true);
1353 var pi = this.parent().items;
1354 for (var i = 0;i < pi.length;i++) {
1355 if (this == pi[i]) {
1358 if (pi[i].el.hasClass('roo-button')) {
1359 pi[i].setActive(false);
1362 this.fireEvent('click', this, e);
1366 if (this.pressed === true || this.pressed === false) {
1367 this.toggleActive(e);
1371 this.fireEvent('click', this, e);
1373 onDblClick: function(e)
1375 if (this.disabled) {
1378 if(this.preventDefault){
1381 this.fireEvent('dblclick', this, e);
1384 * Enables this button
1388 this.disabled = false;
1389 this.el.removeClass('disabled');
1390 this.el.dom.removeAttribute("disabled");
1394 * Disable this button
1396 disable : function()
1398 this.disabled = true;
1399 this.el.addClass('disabled');
1400 this.el.attr("disabled", "disabled")
1403 * sets the active state on/off,
1404 * @param {Boolean} state (optional) Force a particular state
1406 setActive : function(v) {
1408 this.el[v ? 'addClass' : 'removeClass']('active');
1412 * toggles the current active state
1414 toggleActive : function(e)
1416 this.setActive(!this.pressed); // this modifies pressed...
1417 this.fireEvent('toggle', this, e, this.pressed);
1420 * get the current active state
1421 * @return {boolean} true if it's active
1423 isActive : function()
1425 return this.el.hasClass('active');
1428 * set the text of the first selected button
1430 setText : function(str)
1432 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1435 * get the text of the first selected button
1437 getText : function()
1439 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1442 setWeight : function(str)
1444 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1445 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1447 var outline = this.outline ? 'outline-' : '';
1448 if (str == 'default') {
1449 this.el.addClass('btn-default btn-outline-secondary');
1452 this.el.addClass('btn-' + outline + str);
1457 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1459 Roo.bootstrap.Button.weights = [
1479 * @class Roo.bootstrap.Column
1480 * @extends Roo.bootstrap.Component
1481 * @children Roo.bootstrap.Component
1482 * Bootstrap Column class
1483 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1484 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1485 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1486 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1487 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1488 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1489 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1490 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1493 * @cfg {Boolean} hidden (true|false) hide the element
1494 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1495 * @cfg {String} fa (ban|check|...) font awesome icon
1496 * @cfg {Number} fasize (1|2|....) font awsome size
1498 * @cfg {String} icon (info-sign|check|...) glyphicon name
1500 * @cfg {String} html content of column.
1503 * Create a new Column
1504 * @param {Object} config The config object
1507 Roo.bootstrap.Column = function(config){
1508 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1511 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1529 getAutoCreate : function(){
1530 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1538 var sizes = ['xs','sm','md','lg'];
1539 sizes.map(function(size ,ix){
1540 //Roo.log( size + ':' + settings[size]);
1542 if (settings[size+'off'] !== false) {
1543 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1546 if (settings[size] === false) {
1550 if (!settings[size]) { // 0 = hidden
1551 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1553 for (var i = ix; i > -1; i--) {
1554 cfg.cls += ' d-' + sizes[i] + '-none';
1560 cfg.cls += ' col-' + size + '-' + settings[size] + (
1561 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1567 cfg.cls += ' hidden';
1570 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1571 cfg.cls +=' alert alert-' + this.alert;
1575 if (this.html.length) {
1576 cfg.html = this.html;
1580 if (this.fasize > 1) {
1581 fasize = ' fa-' + this.fasize + 'x';
1583 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1588 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1607 * @class Roo.bootstrap.Container
1608 * @extends Roo.bootstrap.Component
1609 * @children Roo.bootstrap.Component
1611 * Bootstrap Container class
1612 * @cfg {Boolean} jumbotron is it a jumbotron element
1613 * @cfg {String} html content of element
1614 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1615 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1616 * @cfg {String} header content of header (for panel)
1617 * @cfg {String} footer content of footer (for panel)
1618 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1619 * @cfg {String} tag (header|aside|section) type of HTML tag.
1620 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1621 * @cfg {String} fa font awesome icon
1622 * @cfg {String} icon (info-sign|check|...) glyphicon name
1623 * @cfg {Boolean} hidden (true|false) hide the element
1624 * @cfg {Boolean} expandable (true|false) default false
1625 * @cfg {Boolean} expanded (true|false) default true
1626 * @cfg {String} rheader contet on the right of header
1627 * @cfg {Boolean} clickable (true|false) default false
1631 * Create a new Container
1632 * @param {Object} config The config object
1635 Roo.bootstrap.Container = function(config){
1636 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1642 * After the panel has been expand
1644 * @param {Roo.bootstrap.Container} this
1649 * After the panel has been collapsed
1651 * @param {Roo.bootstrap.Container} this
1656 * When a element is chick
1657 * @param {Roo.bootstrap.Container} this
1658 * @param {Roo.EventObject} e
1664 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1682 getChildContainer : function() {
1688 if (this.panel.length) {
1689 return this.el.select('.panel-body',true).first();
1696 getAutoCreate : function(){
1699 tag : this.tag || 'div',
1703 if (this.jumbotron) {
1704 cfg.cls = 'jumbotron';
1709 // - this is applied by the parent..
1711 // cfg.cls = this.cls + '';
1714 if (this.sticky.length) {
1716 var bd = Roo.get(document.body);
1717 if (!bd.hasClass('bootstrap-sticky')) {
1718 bd.addClass('bootstrap-sticky');
1719 Roo.select('html',true).setStyle('height', '100%');
1722 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1726 if (this.well.length) {
1727 switch (this.well) {
1730 cfg.cls +=' well well-' +this.well;
1739 cfg.cls += ' hidden';
1743 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1744 cfg.cls +=' alert alert-' + this.alert;
1749 if (this.panel.length) {
1750 cfg.cls += ' panel panel-' + this.panel;
1752 if (this.header.length) {
1756 if(this.expandable){
1758 cfg.cls = cfg.cls + ' expandable';
1762 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1770 cls : 'panel-title',
1771 html : (this.expandable ? ' ' : '') + this.header
1775 cls: 'panel-header-right',
1781 cls : 'panel-heading',
1782 style : this.expandable ? 'cursor: pointer' : '',
1790 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1795 if (this.footer.length) {
1797 cls : 'panel-footer',
1806 body.html = this.html || cfg.html;
1807 // prefix with the icons..
1809 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1812 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1817 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1818 cfg.cls = 'container';
1824 initEvents: function()
1826 if(this.expandable){
1827 var headerEl = this.headerEl();
1830 headerEl.on('click', this.onToggleClick, this);
1835 this.el.on('click', this.onClick, this);
1840 onToggleClick : function()
1842 var headerEl = this.headerEl();
1858 if(this.fireEvent('expand', this)) {
1860 this.expanded = true;
1862 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1864 this.el.select('.panel-body',true).first().removeClass('hide');
1866 var toggleEl = this.toggleEl();
1872 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1877 collapse : function()
1879 if(this.fireEvent('collapse', this)) {
1881 this.expanded = false;
1883 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1884 this.el.select('.panel-body',true).first().addClass('hide');
1886 var toggleEl = this.toggleEl();
1892 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1896 toggleEl : function()
1898 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1902 return this.el.select('.panel-heading .fa',true).first();
1905 headerEl : function()
1907 if(!this.el || !this.panel.length || !this.header.length){
1911 return this.el.select('.panel-heading',true).first()
1916 if(!this.el || !this.panel.length){
1920 return this.el.select('.panel-body',true).first()
1923 titleEl : function()
1925 if(!this.el || !this.panel.length || !this.header.length){
1929 return this.el.select('.panel-title',true).first();
1932 setTitle : function(v)
1934 var titleEl = this.titleEl();
1940 titleEl.dom.innerHTML = v;
1943 getTitle : function()
1946 var titleEl = this.titleEl();
1952 return titleEl.dom.innerHTML;
1955 setRightTitle : function(v)
1957 var t = this.el.select('.panel-header-right',true).first();
1963 t.dom.innerHTML = v;
1966 onClick : function(e)
1970 this.fireEvent('click', this, e);
1975 * @class Roo.bootstrap.Card
1976 * @extends Roo.bootstrap.Component
1977 * @children Roo.bootstrap.Component
1979 * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1982 * possible... may not be implemented..
1983 * @cfg {String} header_image src url of image.
1984 * @cfg {String|Object} header
1985 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1986 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1988 * @cfg {String} title
1989 * @cfg {String} subtitle
1990 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1991 * @cfg {String} footer
1993 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1995 * @cfg {String} margin (0|1|2|3|4|5|auto)
1996 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1997 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1998 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1999 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2000 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2001 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2003 * @cfg {String} padding (0|1|2|3|4|5)
2004 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2005 * @cfg {String} padding_bottom (0|1|2|3|4|5)
2006 * @cfg {String} padding_left (0|1|2|3|4|5)
2007 * @cfg {String} padding_right (0|1|2|3|4|5)
2008 * @cfg {String} padding_x (0|1|2|3|4|5)
2009 * @cfg {String} padding_y (0|1|2|3|4|5)
2011 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2012 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2017 * @config {Boolean} dragable if this card can be dragged.
2018 * @config {String} drag_group group for drag
2019 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2020 * @config {String} drop_group group for drag
2022 * @config {Boolean} collapsable can the body be collapsed.
2023 * @config {Boolean} collapsed is the body collapsed when rendered...
2024 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2025 * @config {Boolean} rotated is the body rotated when rendered...
2028 * Create a new Container
2029 * @param {Object} config The config object
2032 Roo.bootstrap.Card = function(config){
2033 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2039 * When a element a card is dropped
2040 * @param {Roo.bootstrap.Card} this
2043 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2044 * @param {String} position 'above' or 'below'
2045 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2051 * When a element a card is rotate
2052 * @param {Roo.bootstrap.Card} this
2053 * @param {Roo.Element} n the node being dropped?
2054 * @param {Boolean} rotate status
2059 * When a card element is dragged over ready to drop (return false to block dropable)
2060 * @param {Roo.bootstrap.Card} this
2061 * @param {Object} data from dragdrop
2069 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2074 margin: '', /// may be better in component?
2104 collapsable : false,
2113 childContainer : false,
2114 dropEl : false, /// the dom placeholde element that indicates drop location.
2115 containerEl: false, // body container
2116 bodyEl: false, // card-body
2117 headerContainerEl : false, //
2119 header_imageEl : false,
2122 layoutCls : function()
2126 Roo.log(this.margin_bottom.length);
2127 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2128 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2130 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2131 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2133 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2134 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2138 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2139 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2140 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2144 // more generic support?
2152 // Roo.log("Call onRender: " + this.xtype);
2153 /* We are looking at something like this.
2155 <img src="..." class="card-img-top" alt="...">
2156 <div class="card-body">
2157 <h5 class="card-title">Card title</h5>
2158 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2160 >> this bit is really the body...
2161 <div> << we will ad dthis in hopefully it will not break shit.
2163 ** card text does not actually have any styling...
2165 <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>
2168 <a href="#" class="card-link">Card link</a>
2171 <div class="card-footer">
2172 <small class="text-muted">Last updated 3 mins ago</small>
2176 getAutoCreate : function(){
2184 if (this.weight.length && this.weight != 'light') {
2185 cfg.cls += ' text-white';
2187 cfg.cls += ' text-dark'; // need as it's nested..
2189 if (this.weight.length) {
2190 cfg.cls += ' bg-' + this.weight;
2193 cfg.cls += ' ' + this.layoutCls();
2196 var hdr_ctr = false;
2197 if (this.header.length) {
2199 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2200 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2208 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2214 if (this.collapsable) {
2217 cls : 'd-block user-select-none',
2221 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2226 hdr.cn.push(hdr_ctr);
2231 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2236 if (this.header_image.length) {
2239 cls : 'card-img-top',
2240 src: this.header_image // escape?
2245 cls : 'card-img-top d-none'
2251 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2255 if (this.collapsable || this.rotateable) {
2258 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2265 if (this.title.length) {
2269 src: this.title // escape?
2273 if (this.subtitle.length) {
2277 src: this.subtitle // escape?
2283 cls : 'roo-card-body-ctr'
2286 if (this.html.length) {
2292 // fixme ? handle objects?
2294 if (this.footer.length) {
2297 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2302 cfg.cn.push({cls : 'card-footer d-none'});
2311 getCardHeader : function()
2313 var ret = this.el.select('.card-header',true).first();
2314 if (ret.hasClass('d-none')) {
2315 ret.removeClass('d-none');
2320 getCardFooter : function()
2322 var ret = this.el.select('.card-footer',true).first();
2323 if (ret.hasClass('d-none')) {
2324 ret.removeClass('d-none');
2329 getCardImageTop : function()
2331 var ret = this.header_imageEl;
2332 if (ret.hasClass('d-none')) {
2333 ret.removeClass('d-none');
2339 getChildContainer : function()
2345 return this.el.select('.roo-card-body-ctr',true).first();
2348 initEvents: function()
2350 this.bodyEl = this.el.select('.card-body',true).first();
2351 this.containerEl = this.getChildContainer();
2353 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2354 containerScroll: true,
2355 ddGroup: this.drag_group || 'default_card_drag_group'
2357 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2359 if (this.dropable) {
2360 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2361 containerScroll: true,
2362 ddGroup: this.drop_group || 'default_card_drag_group'
2364 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2365 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2366 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2367 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2368 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2371 if (this.collapsable) {
2372 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2374 if (this.rotateable) {
2375 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2377 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2379 this.footerEl = this.el.select('.card-footer',true).first();
2380 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2381 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2382 this.headerEl = this.el.select('.card-header',true).first();
2385 this.el.addClass('roo-card-rotated');
2386 this.fireEvent('rotate', this, true);
2388 this.header_imageEl = this.el.select('.card-img-top',true).first();
2389 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2392 getDragData : function(e)
2394 var target = this.getEl();
2396 //this.handleSelection(e);
2401 nodes: this.getEl(),
2406 dragData.ddel = target.dom ; // the div element
2407 Roo.log(target.getWidth( ));
2408 dragData.ddel.style.width = target.getWidth() + 'px';
2415 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2416 * whole Element becomes the target, and this causes the drop gesture to append.
2418 * Returns an object:
2421 position : 'below' or 'above'
2422 card : relateive to card OBJECT (or true for no cards listed)
2423 items_n : relative to nth item in list
2424 card_n : relative to nth card in list
2429 getTargetFromEvent : function(e, dragged_card_el)
2431 var target = e.getTarget();
2432 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2433 target = target.parentNode;
2444 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2445 // see if target is one of the 'cards'...
2448 //Roo.log(this.items.length);
2451 var last_card_n = 0;
2453 for (var i = 0;i< this.items.length;i++) {
2455 if (!this.items[i].el.hasClass('card')) {
2458 pos = this.getDropPoint(e, this.items[i].el.dom);
2460 cards_len = ret.cards.length;
2461 //Roo.log(this.items[i].el.dom.id);
2462 ret.cards.push(this.items[i]);
2464 if (ret.card_n < 0 && pos == 'above') {
2465 ret.position = cards_len > 0 ? 'below' : pos;
2466 ret.items_n = i > 0 ? i - 1 : 0;
2467 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2468 ret.card = ret.cards[ret.card_n];
2471 if (!ret.cards.length) {
2473 ret.position = 'below';
2477 // could not find a card.. stick it at the end..
2478 if (ret.card_n < 0) {
2479 ret.card_n = last_card_n;
2480 ret.card = ret.cards[last_card_n];
2481 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2482 ret.position = 'below';
2485 if (this.items[ret.items_n].el == dragged_card_el) {
2489 if (ret.position == 'below') {
2490 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2492 if (card_after && card_after.el == dragged_card_el) {
2499 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2501 if (card_before && card_before.el == dragged_card_el) {
2508 onNodeEnter : function(n, dd, e, data){
2511 onNodeOver : function(n, dd, e, data)
2514 var target_info = this.getTargetFromEvent(e,data.source.el);
2515 if (target_info === false) {
2516 this.dropPlaceHolder('hide');
2519 Roo.log(['getTargetFromEvent', target_info ]);
2522 if (this.fireEvent('cardover', this, [ data ]) === false) {
2526 this.dropPlaceHolder('show', target_info,data);
2530 onNodeOut : function(n, dd, e, data){
2531 this.dropPlaceHolder('hide');
2534 onNodeDrop : function(n, dd, e, data)
2537 // call drop - return false if
2539 // this could actually fail - if the Network drops..
2540 // we will ignore this at present..- client should probably reload
2541 // the whole set of cards if stuff like that fails.
2544 var info = this.getTargetFromEvent(e,data.source.el);
2545 if (info === false) {
2548 this.dropPlaceHolder('hide');
2552 this.acceptCard(data.source, info.position, info.card, info.items_n);
2556 firstChildCard : function()
2558 for (var i = 0;i< this.items.length;i++) {
2560 if (!this.items[i].el.hasClass('card')) {
2563 return this.items[i];
2565 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2570 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2572 acceptCard : function(move_card, position, next_to_card )
2574 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2578 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2580 move_card.parent().removeCard(move_card);
2583 var dom = move_card.el.dom;
2584 dom.style.width = ''; // clear with - which is set by drag.
2586 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2587 var cardel = next_to_card.el.dom;
2589 if (position == 'above' ) {
2590 cardel.parentNode.insertBefore(dom, cardel);
2591 } else if (cardel.nextSibling) {
2592 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2594 cardel.parentNode.append(dom);
2597 // card container???
2598 this.containerEl.dom.append(dom);
2601 //FIXME HANDLE card = true
2603 // add this to the correct place in items.
2605 // remove Card from items.
2608 if (this.items.length) {
2610 //Roo.log([info.items_n, info.position, this.items.length]);
2611 for (var i =0; i < this.items.length; i++) {
2612 if (i == to_items_n && position == 'above') {
2613 nitems.push(move_card);
2615 nitems.push(this.items[i]);
2616 if (i == to_items_n && position == 'below') {
2617 nitems.push(move_card);
2620 this.items = nitems;
2621 Roo.log(this.items);
2623 this.items.push(move_card);
2626 move_card.parentId = this.id;
2632 removeCard : function(c)
2634 this.items = this.items.filter(function(e) { return e != c });
2637 dom.parentNode.removeChild(dom);
2638 dom.style.width = ''; // clear with - which is set by drag.
2643 /** Decide whether to drop above or below a View node. */
2644 getDropPoint : function(e, n, dd)
2649 if (n == this.containerEl.dom) {
2652 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2653 var c = t + (b - t) / 2;
2654 var y = Roo.lib.Event.getPageY(e);
2661 onToggleCollapse : function(e)
2663 if (this.collapsed) {
2664 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2665 this.collapsableEl.addClass('show');
2666 this.collapsed = false;
2669 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2670 this.collapsableEl.removeClass('show');
2671 this.collapsed = true;
2676 onToggleRotate : function(e)
2678 this.collapsableEl.removeClass('show');
2679 this.footerEl.removeClass('d-none');
2680 this.el.removeClass('roo-card-rotated');
2681 this.el.removeClass('d-none');
2684 this.collapsableEl.addClass('show');
2685 this.rotated = false;
2686 this.fireEvent('rotate', this, this.rotated);
2689 this.el.addClass('roo-card-rotated');
2690 this.footerEl.addClass('d-none');
2691 this.el.select('.roo-collapsable').removeClass('show');
2693 this.rotated = true;
2694 this.fireEvent('rotate', this, this.rotated);
2698 dropPlaceHolder: function (action, info, data)
2700 if (this.dropEl === false) {
2701 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2705 this.dropEl.removeClass(['d-none', 'd-block']);
2706 if (action == 'hide') {
2708 this.dropEl.addClass('d-none');
2711 // FIXME - info.card == true!!!
2712 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2714 if (info.card !== true) {
2715 var cardel = info.card.el.dom;
2717 if (info.position == 'above') {
2718 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2719 } else if (cardel.nextSibling) {
2720 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2722 cardel.parentNode.append(this.dropEl.dom);
2725 // card container???
2726 this.containerEl.dom.append(this.dropEl.dom);
2729 this.dropEl.addClass('d-block roo-card-dropzone');
2731 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2738 setHeaderText: function(html)
2741 if (this.headerContainerEl) {
2742 this.headerContainerEl.dom.innerHTML = html;
2745 onHeaderImageLoad : function(ev, he)
2747 if (!this.header_image_fit_square) {
2751 var hw = he.naturalHeight / he.naturalWidth;
2754 //var w = he.dom.naturalWidth;
2757 he.style.position = 'relative';
2759 var nw = (ww * (1/hw));
2760 Roo.get(he).setSize( ww * (1/hw), ww);
2761 he.style.left = ((ww - nw)/ 2) + 'px';
2762 he.style.position = 'relative';
2773 * Card header - holder for the card header elements.
2778 * @class Roo.bootstrap.CardHeader
2779 * @extends Roo.bootstrap.Element
2780 * @parent Roo.bootstrap.Card
2781 * @children Roo.bootstrap.Component
2782 * Bootstrap CardHeader class
2784 * Create a new Card Header - that you can embed children into
2785 * @param {Object} config The config object
2788 Roo.bootstrap.CardHeader = function(config){
2789 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2792 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2795 container_method : 'getCardHeader'
2808 * Card footer - holder for the card footer elements.
2813 * @class Roo.bootstrap.CardFooter
2814 * @extends Roo.bootstrap.Element
2815 * @parent Roo.bootstrap.Card
2816 * @children Roo.bootstrap.Component
2817 * Bootstrap CardFooter class
2820 * Create a new Card Footer - that you can embed children into
2821 * @param {Object} config The config object
2824 Roo.bootstrap.CardFooter = function(config){
2825 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2828 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2831 container_method : 'getCardFooter'
2844 * Card header - holder for the card header elements.
2849 * @class Roo.bootstrap.CardImageTop
2850 * @extends Roo.bootstrap.Element
2851 * @parent Roo.bootstrap.Card
2852 * @children Roo.bootstrap.Component
2853 * Bootstrap CardImageTop class
2856 * Create a new Card Image Top container
2857 * @param {Object} config The config object
2860 Roo.bootstrap.CardImageTop = function(config){
2861 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2864 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2867 container_method : 'getCardImageTop'
2882 * @class Roo.bootstrap.ButtonUploader
2883 * @extends Roo.bootstrap.Button
2884 * Bootstrap Button Uploader class - it's a button which when you add files to it
2887 * @cfg {Number} errorTimeout default 3000
2888 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2889 * @cfg {Array} html The button text.
2890 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2893 * Create a new CardUploader
2894 * @param {Object} config The config object
2897 Roo.bootstrap.ButtonUploader = function(config){
2901 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2907 * @event beforeselect
2908 * When button is pressed, before show upload files dialog is shown
2909 * @param {Roo.bootstrap.UploaderButton} this
2912 'beforeselect' : true,
2914 * @event fired when files have been selected,
2915 * When a the download link is clicked
2916 * @param {Roo.bootstrap.UploaderButton} this
2917 * @param {Array} Array of files that have been uploaded
2924 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2927 errorTimeout : 3000,
2931 fileCollection : false,
2936 getAutoCreate : function()
2943 Roo.bootstrap.Button.prototype.getAutoCreate.call(this)
2951 initEvents : function()
2954 Roo.bootstrap.Button.prototype.initEvents.call(this);
2960 this.urlAPI = (window.createObjectURL && window) ||
2961 (window.URL && URL.revokeObjectURL && URL) ||
2962 (window.webkitURL && webkitURL);
2967 cls : 'd-none roo-card-upload-selector'
2970 if (this.multiple) {
2971 im.multiple = 'multiple';
2973 this.selectorEl = Roo.get(document.body).createChild(im); // so it does not capture click event for navitem.
2975 //this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2977 this.selectorEl.on('change', this.onFileSelected, this);
2984 onClick : function(e)
2988 if ( this.fireEvent('beforeselect', this) === false) {
2992 this.selectorEl.dom.click();
2996 onFileSelected : function(e)
3000 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3003 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3004 this.selectorEl.dom.value = '';// hopefully reset..
3006 this.fireEvent('uploaded', this, files );
3014 * addCard - add an Attachment to the uploader
3015 * @param data - the data about the image to upload
3019 title : "Title of file",
3020 is_uploaded : false,
3021 src : "http://.....",
3022 srcfile : { the File upload object },
3023 mimetype : file.type,
3026 .. any other data...
3051 * @class Roo.bootstrap.Img
3052 * @extends Roo.bootstrap.Component
3053 * Bootstrap Img class
3054 * @cfg {Boolean} imgResponsive false | true
3055 * @cfg {String} border rounded | circle | thumbnail
3056 * @cfg {String} src image source
3057 * @cfg {String} alt image alternative text
3058 * @cfg {String} href a tag href
3059 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3060 * @cfg {String} xsUrl xs image source
3061 * @cfg {String} smUrl sm image source
3062 * @cfg {String} mdUrl md image source
3063 * @cfg {String} lgUrl lg image source
3064 * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3067 * Create a new Input
3068 * @param {Object} config The config object
3071 Roo.bootstrap.Img = function(config){
3072 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3078 * The img click event for the img.
3079 * @param {Roo.EventObject} e
3084 * The when any image loads
3085 * @param {Roo.EventObject} e
3091 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3093 imgResponsive: true,
3102 backgroundContain : false,
3104 getAutoCreate : function()
3106 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3107 return this.createSingleImg();
3112 cls: 'roo-image-responsive-group',
3117 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3119 if(!_this[size + 'Url']){
3125 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3126 html: _this.html || cfg.html,
3127 src: _this[size + 'Url']
3130 img.cls += ' roo-image-responsive-' + size;
3132 var s = ['xs', 'sm', 'md', 'lg'];
3134 s.splice(s.indexOf(size), 1);
3136 Roo.each(s, function(ss){
3137 img.cls += ' hidden-' + ss;
3140 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3141 cfg.cls += ' img-' + _this.border;
3145 cfg.alt = _this.alt;
3158 a.target = _this.target;
3162 cfg.cn.push((_this.href) ? a : img);
3169 createSingleImg : function()
3173 cls: (this.imgResponsive) ? 'img-responsive' : '',
3175 src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
3178 if (this.backgroundContain) {
3179 cfg.cls += ' background-contain';
3182 cfg.html = this.html || cfg.html;
3184 if (this.backgroundContain) {
3185 cfg.style="background-image: url(" + this.src + ')';
3187 cfg.src = this.src || cfg.src;
3190 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3191 cfg.cls += ' img-' + this.border;
3208 a.target = this.target;
3213 return (this.href) ? a : cfg;
3216 initEvents: function()
3219 this.el.on('click', this.onClick, this);
3221 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3222 this.el.on('load', this.onImageLoad, this);
3224 // not sure if this works.. not tested
3225 this.el.select('img', true).on('load', this.onImageLoad, this);
3230 onClick : function(e)
3232 Roo.log('img onclick');
3233 this.fireEvent('click', this, e);
3235 onImageLoad: function(e)
3237 Roo.log('img load');
3238 this.fireEvent('load', this, e);
3242 * Sets the url of the image - used to update it
3243 * @param {String} url the url of the image
3246 setSrc : function(url)
3250 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3251 if (this.backgroundContain) {
3252 this.el.dom.style.backgroundImage = 'url(' + url + ')';
3254 this.el.dom.src = url;
3259 this.el.select('img', true).first().dom.src = url;
3275 * @class Roo.bootstrap.Link
3276 * @extends Roo.bootstrap.Component
3277 * @children Roo.bootstrap.Component
3278 * Bootstrap Link Class (eg. '<a href>')
3280 * @cfg {String} alt image alternative text
3281 * @cfg {String} href a tag href
3282 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3283 * @cfg {String} html the content of the link.
3284 * @cfg {String} anchor name for the anchor link
3285 * @cfg {String} fa - favicon
3287 * @cfg {Boolean} preventDefault (true | false) default false
3291 * Create a new Input
3292 * @param {Object} config The config object
3295 Roo.bootstrap.Link = function(config){
3296 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3302 * The img click event for the img.
3303 * @param {Roo.EventObject} e
3309 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3313 preventDefault: false,
3319 getAutoCreate : function()
3321 var html = this.html || '';
3323 if (this.fa !== false) {
3324 html = '<i class="fa fa-' + this.fa + '"></i>';
3329 // anchor's do not require html/href...
3330 if (this.anchor === false) {
3332 cfg.href = this.href || '#';
3334 cfg.name = this.anchor;
3335 if (this.html !== false || this.fa !== false) {
3338 if (this.href !== false) {
3339 cfg.href = this.href;
3343 if(this.alt !== false){
3348 if(this.target !== false) {
3349 cfg.target = this.target;
3355 initEvents: function() {
3357 if(!this.href || this.preventDefault){
3358 this.el.on('click', this.onClick, this);
3362 onClick : function(e)
3364 if(this.preventDefault){
3367 //Roo.log('img onclick');
3368 this.fireEvent('click', this, e);
3381 * @class Roo.bootstrap.Header
3382 * @extends Roo.bootstrap.Component
3383 * @children Roo.bootstrap.Component
3384 * Bootstrap Header class
3387 * @cfg {String} html content of header
3388 * @cfg {Number} level (1|2|3|4|5|6) default 1
3391 * Create a new Header
3392 * @param {Object} config The config object
3396 Roo.bootstrap.Header = function(config){
3397 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3400 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3408 getAutoCreate : function(){
3413 tag: 'h' + (1 *this.level),
3414 html: this.html || ''
3425 * @class Roo.bootstrap.MenuMgr
3427 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3430 Roo.bootstrap.menu.Manager = function(){
3431 var menus, active, groups = {}, attached = false, lastShow = new Date();
3433 // private - called when first menu is created
3436 active = new Roo.util.MixedCollection();
3437 Roo.get(document).addKeyListener(27, function(){
3438 if(active.length > 0){
3446 if(active && active.length > 0){
3447 var c = active.clone();
3457 if(active.length < 1){
3458 Roo.get(document).un("mouseup", onMouseDown);
3466 var last = active.last();
3467 lastShow = new Date();
3470 Roo.get(document).on("mouseup", onMouseDown);
3475 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3476 m.parentMenu.activeChild = m;
3477 }else if(last && last.isVisible()){
3478 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3483 function onBeforeHide(m){
3485 m.activeChild.hide();
3487 if(m.autoHideTimer){
3488 clearTimeout(m.autoHideTimer);
3489 delete m.autoHideTimer;
3494 function onBeforeShow(m){
3495 var pm = m.parentMenu;
3496 if(!pm && !m.allowOtherMenus){
3498 }else if(pm && pm.activeChild && active != m){
3499 pm.activeChild.hide();
3503 // private this should really trigger on mouseup..
3504 function onMouseDown(e){
3505 Roo.log("on Mouse Up");
3507 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3508 Roo.log("MenuManager hideAll");
3517 function onBeforeCheck(mi, state){
3519 var g = groups[mi.group];
3520 for(var i = 0, l = g.length; i < l; i++){
3522 g[i].setChecked(false);
3531 * Hides all menus that are currently visible
3533 hideAll : function(){
3538 register : function(menu){
3542 menus[menu.id] = menu;
3543 menu.on("beforehide", onBeforeHide);
3544 menu.on("hide", onHide);
3545 menu.on("beforeshow", onBeforeShow);
3546 menu.on("show", onShow);
3548 if(g && menu.events["checkchange"]){
3552 groups[g].push(menu);
3553 menu.on("checkchange", onCheck);
3558 * Returns a {@link Roo.menu.Menu} object
3559 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3560 * be used to generate and return a new Menu instance.
3562 get : function(menu){
3563 if(typeof menu == "string"){ // menu id
3565 }else if(menu.events){ // menu instance
3568 /*else if(typeof menu.length == 'number'){ // array of menu items?
3569 return new Roo.bootstrap.Menu({items:menu});
3570 }else{ // otherwise, must be a config
3571 return new Roo.bootstrap.Menu(menu);
3578 unregister : function(menu){
3579 delete menus[menu.id];
3580 menu.un("beforehide", onBeforeHide);
3581 menu.un("hide", onHide);
3582 menu.un("beforeshow", onBeforeShow);
3583 menu.un("show", onShow);
3585 if(g && menu.events["checkchange"]){
3586 groups[g].remove(menu);
3587 menu.un("checkchange", onCheck);
3592 registerCheckable : function(menuItem){
3593 var g = menuItem.group;
3598 groups[g].push(menuItem);
3599 menuItem.on("beforecheckchange", onBeforeCheck);
3604 unregisterCheckable : function(menuItem){
3605 var g = menuItem.group;
3607 groups[g].remove(menuItem);
3608 menuItem.un("beforecheckchange", onBeforeCheck);
3614 * @class Roo.bootstrap.menu.Menu
3615 * @extends Roo.bootstrap.Component
3617 * @children Roo.bootstrap.menu.Item Roo.bootstrap.menu.Separator
3619 * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3621 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3622 * @cfg {bool} hidden if the menu should be hidden when rendered.
3623 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3624 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3625 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3626 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3630 * @param {Object} config The config objectQ
3634 Roo.bootstrap.menu.Menu = function(config){
3636 if (config.type == 'treeview') {
3637 // normally menu's are drawn attached to the document to handle layering etc..
3638 // however treeview (used by the docs menu is drawn into the parent element)
3639 this.container_method = 'getChildContainer';
3642 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3643 if (this.registerMenu && this.type != 'treeview') {
3644 Roo.bootstrap.menu.Manager.register(this);
3651 * Fires before this menu is displayed (return false to block)
3652 * @param {Roo.menu.Menu} this
3657 * Fires before this menu is hidden (return false to block)
3658 * @param {Roo.menu.Menu} this
3663 * Fires after this menu is displayed
3664 * @param {Roo.menu.Menu} this
3669 * Fires after this menu is hidden
3670 * @param {Roo.menu.Menu} this
3675 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3676 * @param {Roo.menu.Menu} this
3677 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3678 * @param {Roo.EventObject} e
3683 * Fires when the mouse is hovering over this menu
3684 * @param {Roo.menu.Menu} this
3685 * @param {Roo.EventObject} e
3686 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3691 * Fires when the mouse exits this menu
3692 * @param {Roo.menu.Menu} this
3693 * @param {Roo.EventObject} e
3694 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3699 * Fires when a menu item contained in this menu is clicked
3700 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3701 * @param {Roo.EventObject} e
3705 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3708 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
3712 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3715 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3717 registerMenu : true,
3719 menuItems :false, // stores the menu items..
3729 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3731 hideTrigger : false,
3736 getChildContainer : function() {
3740 getAutoCreate : function(){
3742 //if (['right'].indexOf(this.align)!==-1) {
3743 // cfg.cn[1].cls += ' pull-right'
3748 cls : 'dropdown-menu shadow' ,
3749 style : 'z-index:1000'
3753 if (this.type === 'submenu') {
3754 cfg.cls = 'submenu active';
3756 if (this.type === 'treeview') {
3757 cfg.cls = 'treeview-menu';
3762 initEvents : function() {
3764 // Roo.log("ADD event");
3765 // Roo.log(this.triggerEl.dom);
3766 if (this.triggerEl) {
3768 this.triggerEl.on('click', this.onTriggerClick, this);
3770 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3772 if (!this.hideTrigger) {
3773 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3774 // dropdown toggle on the 'a' in BS4?
3775 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3777 this.triggerEl.addClass('dropdown-toggle');
3783 this.el.on('touchstart' , this.onTouch, this);
3785 this.el.on('click' , this.onClick, this);
3787 this.el.on("mouseover", this.onMouseOver, this);
3788 this.el.on("mouseout", this.onMouseOut, this);
3792 findTargetItem : function(e)
3794 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3798 //Roo.log(t); Roo.log(t.id);
3800 //Roo.log(this.menuitems);
3801 return this.menuitems.get(t.id);
3803 //return this.items.get(t.menuItemId);
3809 onTouch : function(e)
3811 Roo.log("menu.onTouch");
3812 //e.stopEvent(); this make the user popdown broken
3816 onClick : function(e)
3818 Roo.log("menu.onClick");
3820 var t = this.findTargetItem(e);
3821 if(!t || t.isContainer){
3826 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3827 if(t == this.activeItem && t.shouldDeactivate(e)){
3828 this.activeItem.deactivate();
3829 delete this.activeItem;
3833 this.setActiveItem(t, true);
3841 Roo.log('pass click event');
3845 this.fireEvent("click", this, t, e);
3849 if(!t.href.length || t.href == '#'){
3850 (function() { _this.hide(); }).defer(100);
3855 onMouseOver : function(e){
3856 var t = this.findTargetItem(e);
3859 // if(t.canActivate && !t.disabled){
3860 // this.setActiveItem(t, true);
3864 this.fireEvent("mouseover", this, e, t);
3866 isVisible : function(){
3867 return !this.hidden;
3869 onMouseOut : function(e){
3870 var t = this.findTargetItem(e);
3873 // if(t == this.activeItem && t.shouldDeactivate(e)){
3874 // this.activeItem.deactivate();
3875 // delete this.activeItem;
3878 this.fireEvent("mouseout", this, e, t);
3883 * Displays this menu relative to another element
3884 * @param {String/HTMLElement/Roo.Element} element The element to align to
3885 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3886 * the element (defaults to this.defaultAlign)
3887 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3889 show : function(el, pos, parentMenu)
3891 if (false === this.fireEvent("beforeshow", this)) {
3892 Roo.log("show canceled");
3895 this.parentMenu = parentMenu;
3899 this.el.addClass('show'); // show otherwise we do not know how big we are..
3901 var xy = this.el.getAlignToXY(el, pos);
3903 // bl-tl << left align below
3904 // tl-bl << left align
3906 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3907 // if it goes to far to the right.. -> align left.
3908 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3911 // was left align - go right?
3912 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3915 // goes down the bottom
3916 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3918 var a = this.align.replace('?', '').split('-');
3919 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3923 this.showAt( xy , parentMenu, false);
3926 * Displays this menu at a specific xy position
3927 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3928 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3930 showAt : function(xy, parentMenu, /* private: */_e){
3931 this.parentMenu = parentMenu;
3936 this.fireEvent("beforeshow", this);
3937 //xy = this.el.adjustForConstraints(xy);
3941 this.hideMenuItems();
3942 this.hidden = false;
3943 if (this.triggerEl) {
3944 this.triggerEl.addClass('open');
3947 this.el.addClass('show');
3951 // reassign x when hitting right
3953 // reassign y when hitting bottom
3955 // but the list may align on trigger left or trigger top... should it be a properity?
3957 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3962 this.fireEvent("show", this);
3968 this.doFocus.defer(50, this);
3972 doFocus : function(){
3974 this.focusEl.focus();
3979 * Hides this menu and optionally all parent menus
3980 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3982 hide : function(deep)
3984 if (false === this.fireEvent("beforehide", this)) {
3985 Roo.log("hide canceled");
3988 this.hideMenuItems();
3989 if(this.el && this.isVisible()){
3991 if(this.activeItem){
3992 this.activeItem.deactivate();
3993 this.activeItem = null;
3995 if (this.triggerEl) {
3996 this.triggerEl.removeClass('open');
3999 this.el.removeClass('show');
4001 this.fireEvent("hide", this);
4003 if(deep === true && this.parentMenu){
4004 this.parentMenu.hide(true);
4008 onTriggerClick : function(e)
4010 Roo.log('trigger click');
4012 var target = e.getTarget();
4014 Roo.log(target.nodeName.toLowerCase());
4016 if(target.nodeName.toLowerCase() === 'i'){
4022 onTriggerPress : function(e)
4024 Roo.log('trigger press');
4025 //Roo.log(e.getTarget());
4026 // Roo.log(this.triggerEl.dom);
4028 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4029 var pel = Roo.get(e.getTarget());
4030 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4031 Roo.log('is treeview or dropdown?');
4035 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4039 if (this.isVisible()) {
4045 this.show(this.triggerEl, this.align, false);
4048 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4055 hideMenuItems : function()
4057 Roo.log("hide Menu Items");
4062 this.el.select('.open',true).each(function(aa) {
4064 aa.removeClass('open');
4068 addxtypeChild : function (tree, cntr) {
4069 var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4071 this.menuitems.add(comp);
4083 this.getEl().dom.innerHTML = '';
4084 this.menuitems.clear();
4090 * @class Roo.bootstrap.menu.Item
4091 * @extends Roo.bootstrap.Component
4092 * @children Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4093 * @parent Roo.bootstrap.menu.Menu
4095 * Bootstrap MenuItem class
4097 * @cfg {String} html the menu label
4098 * @cfg {String} href the link
4099 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4100 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4101 * @cfg {Boolean} active used on sidebars to highlight active itesm
4102 * @cfg {String} fa favicon to show on left of menu item.
4103 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4107 * Create a new MenuItem
4108 * @param {Object} config The config object
4112 Roo.bootstrap.menu.Item = function(config){
4113 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4118 * The raw click event for the entire grid.
4119 * @param {Roo.bootstrap.menu.Item} this
4120 * @param {Roo.EventObject} e
4126 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
4130 preventDefault: false,
4131 isContainer : false,
4135 getAutoCreate : function(){
4137 if(this.isContainer){
4140 cls: 'dropdown-menu-item '
4150 cls : 'dropdown-item',
4155 if (this.fa !== false) {
4158 cls : 'fa fa-' + this.fa
4167 cls: 'dropdown-menu-item',
4170 if (this.parent().type == 'treeview') {
4171 cfg.cls = 'treeview-menu';
4174 cfg.cls += ' active';
4179 anc.href = this.href || cfg.cn[0].href ;
4180 ctag.html = this.html || cfg.cn[0].html ;
4184 initEvents: function()
4186 if (this.parent().type == 'treeview') {
4187 this.el.select('a').on('click', this.onClick, this);
4191 this.menu.parentType = this.xtype;
4192 this.menu.triggerEl = this.el;
4193 this.menu = this.addxtype(Roo.apply({}, this.menu));
4197 onClick : function(e)
4199 //Roo.log('item on click ');
4201 if(this.href === false || this.preventDefault){
4204 //this.parent().hideMenuItems();
4206 this.fireEvent('click', this, e);
4220 * @class Roo.bootstrap.menu.Separator
4221 * @extends Roo.bootstrap.Component
4223 * @parent Roo.bootstrap.menu.Menu
4224 * Bootstrap Separator class
4227 * Create a new Separator
4228 * @param {Object} config The config object
4232 Roo.bootstrap.menu.Separator = function(config){
4233 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4236 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
4238 getAutoCreate : function(){
4241 cls: 'dropdown-divider divider'
4257 * @class Roo.bootstrap.Modal
4258 * @extends Roo.bootstrap.Component
4259 * @parent none builder
4260 * @children Roo.bootstrap.Component
4261 * Bootstrap Modal class
4262 * @cfg {String} title Title of dialog
4263 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4264 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4265 * @cfg {Boolean} specificTitle default false
4266 * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4267 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4268 * @cfg {Boolean} animate default true
4269 * @cfg {Boolean} allow_close default true
4270 * @cfg {Boolean} fitwindow default false
4271 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4272 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4273 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4274 * @cfg {String} size (sm|lg|xl) default empty
4275 * @cfg {Number} max_width set the max width of modal
4276 * @cfg {Boolean} editableTitle can the title be edited
4281 * Create a new Modal Dialog
4282 * @param {Object} config The config object
4285 Roo.bootstrap.Modal = function(config){
4286 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4291 * The raw btnclick event for the button
4292 * @param {Roo.EventObject} e
4297 * Fire when dialog resize
4298 * @param {Roo.bootstrap.Modal} this
4299 * @param {Roo.EventObject} e
4303 * @event titlechanged
4304 * Fire when the editable title has been changed
4305 * @param {Roo.bootstrap.Modal} this
4306 * @param {Roo.EventObject} value
4308 "titlechanged" : true
4311 this.buttons = this.buttons || [];
4314 this.tmpl = Roo.factory(this.tmpl);
4319 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4321 title : 'test dialog',
4331 specificTitle: false,
4333 buttonPosition: 'right',
4355 editableTitle : false,
4357 onRender : function(ct, position)
4359 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4362 var cfg = Roo.apply({}, this.getAutoCreate());
4365 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4367 //if (!cfg.name.length) {
4371 cfg.cls += ' ' + this.cls;
4374 cfg.style = this.style;
4376 this.el = Roo.get(document.body).createChild(cfg, position);
4378 //var type = this.el.dom.type;
4381 if(this.tabIndex !== undefined){
4382 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4385 this.dialogEl = this.el.select('.modal-dialog',true).first();
4386 this.bodyEl = this.el.select('.modal-body',true).first();
4387 this.closeEl = this.el.select('.modal-header .close', true).first();
4388 this.headerEl = this.el.select('.modal-header',true).first();
4389 this.titleEl = this.el.select('.modal-title',true).first();
4390 this.footerEl = this.el.select('.modal-footer',true).first();
4392 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4394 //this.el.addClass("x-dlg-modal");
4396 if (this.buttons.length) {
4397 Roo.each(this.buttons, function(bb) {
4398 var b = Roo.apply({}, bb);
4399 b.xns = b.xns || Roo.bootstrap;
4400 b.xtype = b.xtype || 'Button';
4401 if (typeof(b.listeners) == 'undefined') {
4402 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4405 var btn = Roo.factory(b);
4407 btn.render(this.getButtonContainer());
4411 // render the children.
4414 if(typeof(this.items) != 'undefined'){
4415 var items = this.items;
4418 for(var i =0;i < items.length;i++) {
4419 // we force children not to montor widnow resize - as we do that for them.
4420 items[i].monitorWindowResize = false;
4421 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4425 this.items = nitems;
4427 // where are these used - they used to be body/close/footer
4431 //this.el.addClass([this.fieldClass, this.cls]);
4435 getAutoCreate : function()
4437 // we will default to modal-body-overflow - might need to remove or make optional later.
4439 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4440 html : this.html || ''
4445 cls : 'modal-title',
4449 if(this.specificTitle){ // WTF is this?
4454 if (this.allow_close && Roo.bootstrap.version == 3) {
4464 if (this.editableTitle) {
4466 cls: 'form-control roo-editable-title d-none',
4472 if (this.allow_close && Roo.bootstrap.version == 4) {
4482 if(this.size.length){
4483 size = 'modal-' + this.size;
4486 var footer = Roo.bootstrap.version == 3 ?
4488 cls : 'modal-footer',
4492 cls: 'btn-' + this.buttonPosition
4497 { // BS4 uses mr-auto on left buttons....
4498 cls : 'modal-footer'
4509 cls: "modal-dialog " + size,
4512 cls : "modal-content",
4515 cls : 'modal-header',
4530 modal.cls += ' fade';
4536 getChildContainer : function() {
4541 getButtonContainer : function() {
4543 return Roo.bootstrap.version == 4 ?
4544 this.el.select('.modal-footer',true).first()
4545 : this.el.select('.modal-footer div',true).first();
4548 initEvents : function()
4550 if (this.allow_close) {
4551 this.closeEl.on('click', this.hide, this);
4553 Roo.EventManager.onWindowResize(this.resize, this, true);
4554 if (this.editableTitle) {
4555 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4556 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4557 this.headerEditEl.on('keyup', function(e) {
4558 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4559 this.toggleHeaderInput(false)
4562 this.headerEditEl.on('blur', function(e) {
4563 this.toggleHeaderInput(false)
4572 this.maskEl.setSize(
4573 Roo.lib.Dom.getViewWidth(true),
4574 Roo.lib.Dom.getViewHeight(true)
4577 if (this.fitwindow) {
4579 this.dialogEl.setStyle( { 'max-width' : '100%' });
4581 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4582 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4587 if(this.max_width !== 0) {
4589 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4592 this.setSize(w, this.height);
4596 if(this.max_height) {
4597 this.setSize(w,Math.min(
4599 Roo.lib.Dom.getViewportHeight(true) - 60
4605 if(!this.fit_content) {
4606 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4610 this.setSize(w, Math.min(
4612 this.headerEl.getHeight() +
4613 this.footerEl.getHeight() +
4614 this.getChildHeight(this.bodyEl.dom.childNodes),
4615 Roo.lib.Dom.getViewportHeight(true) - 60)
4621 setSize : function(w,h)
4628 // any layout/border etc.. resize..
4630 this.items.forEach( function(e) {
4631 e.layout ? e.layout() : false;
4640 if (!this.rendered) {
4643 this.toggleHeaderInput(false);
4644 //this.el.setStyle('display', 'block');
4645 this.el.removeClass('hideing');
4646 this.el.dom.style.display='block';
4648 Roo.get(document.body).addClass('modal-open');
4650 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4653 this.el.addClass('show');
4654 this.el.addClass('in');
4657 this.el.addClass('show');
4658 this.el.addClass('in');
4661 // not sure how we can show data in here..
4663 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4666 Roo.get(document.body).addClass("x-body-masked");
4668 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4669 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4670 this.maskEl.dom.style.display = 'block';
4671 this.maskEl.addClass('show');
4676 this.fireEvent('show', this);
4678 // set zindex here - otherwise it appears to be ignored...
4679 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4682 // this is for children that are... layout.Border
4684 this.items.forEach( function(e) {
4685 e.layout ? e.layout() : false;
4693 if(this.fireEvent("beforehide", this) !== false){
4695 this.maskEl.removeClass('show');
4697 this.maskEl.dom.style.display = '';
4698 Roo.get(document.body).removeClass("x-body-masked");
4699 this.el.removeClass('in');
4700 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4702 if(this.animate){ // why
4703 this.el.addClass('hideing');
4704 this.el.removeClass('show');
4706 if (!this.el.hasClass('hideing')) {
4707 return; // it's been shown again...
4710 this.el.dom.style.display='';
4712 Roo.get(document.body).removeClass('modal-open');
4713 this.el.removeClass('hideing');
4717 this.el.removeClass('show');
4718 this.el.dom.style.display='';
4719 Roo.get(document.body).removeClass('modal-open');
4722 this.fireEvent('hide', this);
4725 isVisible : function()
4728 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4732 addButton : function(str, cb)
4736 var b = Roo.apply({}, { html : str } );
4737 b.xns = b.xns || Roo.bootstrap;
4738 b.xtype = b.xtype || 'Button';
4739 if (typeof(b.listeners) == 'undefined') {
4740 b.listeners = { click : cb.createDelegate(this) };
4743 var btn = Roo.factory(b);
4745 btn.render(this.getButtonContainer());
4751 setDefaultButton : function(btn)
4753 //this.el.select('.modal-footer').()
4756 resizeTo: function(w,h)
4758 this.dialogEl.setWidth(w);
4760 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4762 this.bodyEl.setHeight(h - diff);
4764 this.fireEvent('resize', this);
4767 setContentSize : function(w, h)
4771 onButtonClick: function(btn,e)
4774 this.fireEvent('btnclick', btn.name, e);
4777 * Set the title of the Dialog
4778 * @param {String} str new Title
4780 setTitle: function(str) {
4781 this.titleEl.dom.innerHTML = str;
4785 * Set the body of the Dialog
4786 * @param {String} str new Title
4788 setBody: function(str) {
4789 this.bodyEl.dom.innerHTML = str;
4792 * Set the body of the Dialog using the template
4793 * @param {Obj} data - apply this data to the template and replace the body contents.
4795 applyBody: function(obj)
4798 Roo.log("Error - using apply Body without a template");
4801 this.tmpl.overwrite(this.bodyEl, obj);
4804 getChildHeight : function(child_nodes)
4808 child_nodes.length == 0
4813 var child_height = 0;
4815 for(var i = 0; i < child_nodes.length; i++) {
4818 * for modal with tabs...
4819 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4821 var layout_childs = child_nodes[i].childNodes;
4823 for(var j = 0; j < layout_childs.length; j++) {
4825 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4827 var layout_body_childs = layout_childs[j].childNodes;
4829 for(var k = 0; k < layout_body_childs.length; k++) {
4831 if(layout_body_childs[k].classList.contains('navbar')) {
4832 child_height += layout_body_childs[k].offsetHeight;
4836 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4838 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4840 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4842 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4843 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4858 child_height += child_nodes[i].offsetHeight;
4859 // Roo.log(child_nodes[i].offsetHeight);
4862 return child_height;
4864 toggleHeaderInput : function(is_edit)
4866 if (!this.editableTitle) {
4867 return; // not editable.
4869 if (is_edit && this.is_header_editing) {
4870 return; // already editing..
4874 this.headerEditEl.dom.value = this.title;
4875 this.headerEditEl.removeClass('d-none');
4876 this.headerEditEl.dom.focus();
4877 this.titleEl.addClass('d-none');
4879 this.is_header_editing = true;
4882 // flip back to not editing.
4883 this.title = this.headerEditEl.dom.value;
4884 this.headerEditEl.addClass('d-none');
4885 this.titleEl.removeClass('d-none');
4886 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4887 this.is_header_editing = false;
4888 this.fireEvent('titlechanged', this, this.title);
4897 Roo.apply(Roo.bootstrap.Modal, {
4899 * Button config that displays a single OK button
4908 * Button config that displays Yes and No buttons
4924 * Button config that displays OK and Cancel buttons
4939 * Button config that displays Yes, No and Cancel buttons
4964 * messagebox - can be used as a replace
4968 * @class Roo.MessageBox
4969 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4973 Roo.Msg.alert('Status', 'Changes saved successfully.');
4975 // Prompt for user data:
4976 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4978 // process text value...
4982 // Show a dialog using config options:
4984 title:'Save Changes?',
4985 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4986 buttons: Roo.Msg.YESNOCANCEL,
4993 Roo.bootstrap.MessageBox = function(){
4994 var dlg, opt, mask, waitTimer;
4995 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4996 var buttons, activeTextEl, bwidth;
5000 var handleButton = function(button){
5002 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5006 var handleHide = function(){
5008 dlg.el.removeClass(opt.cls);
5011 // Roo.TaskMgr.stop(waitTimer);
5012 // waitTimer = null;
5017 var updateButtons = function(b){
5020 buttons["ok"].hide();
5021 buttons["cancel"].hide();
5022 buttons["yes"].hide();
5023 buttons["no"].hide();
5024 dlg.footerEl.hide();
5028 dlg.footerEl.show();
5029 for(var k in buttons){
5030 if(typeof buttons[k] != "function"){
5033 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5034 width += buttons[k].el.getWidth()+15;
5044 var handleEsc = function(d, k, e){
5045 if(opt && opt.closable !== false){
5055 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5056 * @return {Roo.BasicDialog} The BasicDialog element
5058 getDialog : function(){
5060 dlg = new Roo.bootstrap.Modal( {
5063 //constraintoviewport:false,
5065 //collapsible : false,
5070 //buttonAlign:"center",
5071 closeClick : function(){
5072 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5075 handleButton("cancel");
5080 dlg.on("hide", handleHide);
5082 //dlg.addKeyListener(27, handleEsc);
5084 this.buttons = buttons;
5085 var bt = this.buttonText;
5086 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5087 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5088 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5089 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5091 bodyEl = dlg.bodyEl.createChild({
5093 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5094 '<textarea class="roo-mb-textarea"></textarea>' +
5095 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5097 msgEl = bodyEl.dom.firstChild;
5098 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5099 textboxEl.enableDisplayMode();
5100 textboxEl.addKeyListener([10,13], function(){
5101 if(dlg.isVisible() && opt && opt.buttons){
5104 }else if(opt.buttons.yes){
5105 handleButton("yes");
5109 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5110 textareaEl.enableDisplayMode();
5111 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5112 progressEl.enableDisplayMode();
5114 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5115 var pf = progressEl.dom.firstChild;
5117 pp = Roo.get(pf.firstChild);
5118 pp.setHeight(pf.offsetHeight);
5126 * Updates the message box body text
5127 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5128 * the XHTML-compliant non-breaking space character '&#160;')
5129 * @return {Roo.MessageBox} This message box
5131 updateText : function(text)
5133 if(!dlg.isVisible() && !opt.width){
5134 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5135 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5137 msgEl.innerHTML = text || ' ';
5139 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5140 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5142 Math.min(opt.width || cw , this.maxWidth),
5143 Math.max(opt.minWidth || this.minWidth, bwidth)
5146 activeTextEl.setWidth(w);
5148 if(dlg.isVisible()){
5149 dlg.fixedcenter = false;
5151 // to big, make it scroll. = But as usual stupid IE does not support
5154 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5155 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5156 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5158 bodyEl.dom.style.height = '';
5159 bodyEl.dom.style.overflowY = '';
5162 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5164 bodyEl.dom.style.overflowX = '';
5167 dlg.setContentSize(w, bodyEl.getHeight());
5168 if(dlg.isVisible()){
5169 dlg.fixedcenter = true;
5175 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5176 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5177 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5178 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5179 * @return {Roo.MessageBox} This message box
5181 updateProgress : function(value, text){
5183 this.updateText(text);
5186 if (pp) { // weird bug on my firefox - for some reason this is not defined
5187 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5188 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5194 * Returns true if the message box is currently displayed
5195 * @return {Boolean} True if the message box is visible, else false
5197 isVisible : function(){
5198 return dlg && dlg.isVisible();
5202 * Hides the message box if it is displayed
5205 if(this.isVisible()){
5211 * Displays a new message box, or reinitializes an existing message box, based on the config options
5212 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5213 * The following config object properties are supported:
5215 Property Type Description
5216 ---------- --------------- ------------------------------------------------------------------------------------
5217 animEl String/Element An id or Element from which the message box should animate as it opens and
5218 closes (defaults to undefined)
5219 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5220 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5221 closable Boolean False to hide the top-right close button (defaults to true). Note that
5222 progress and wait dialogs will ignore this property and always hide the
5223 close button as they can only be closed programmatically.
5224 cls String A custom CSS class to apply to the message box element
5225 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5226 displayed (defaults to 75)
5227 fn Function A callback function to execute after closing the dialog. The arguments to the
5228 function will be btn (the name of the button that was clicked, if applicable,
5229 e.g. "ok"), and text (the value of the active text field, if applicable).
5230 Progress and wait dialogs will ignore this option since they do not respond to
5231 user actions and can only be closed programmatically, so any required function
5232 should be called by the same code after it closes the dialog.
5233 icon String A CSS class that provides a background image to be used as an icon for
5234 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5235 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5236 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5237 modal Boolean False to allow user interaction with the page while the message box is
5238 displayed (defaults to true)
5239 msg String A string that will replace the existing message box body text (defaults
5240 to the XHTML-compliant non-breaking space character ' ')
5241 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5242 progress Boolean True to display a progress bar (defaults to false)
5243 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5244 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5245 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5246 title String The title text
5247 value String The string value to set into the active textbox element if displayed
5248 wait Boolean True to display a progress bar (defaults to false)
5249 width Number The width of the dialog in pixels
5256 msg: 'Please enter your address:',
5258 buttons: Roo.MessageBox.OKCANCEL,
5261 animEl: 'addAddressBtn'
5264 * @param {Object} config Configuration options
5265 * @return {Roo.MessageBox} This message box
5267 show : function(options)
5270 // this causes nightmares if you show one dialog after another
5271 // especially on callbacks..
5273 if(this.isVisible()){
5276 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5277 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5278 Roo.log("New Dialog Message:" + options.msg )
5279 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5280 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5283 var d = this.getDialog();
5285 d.setTitle(opt.title || " ");
5286 d.closeEl.setDisplayed(opt.closable !== false);
5287 activeTextEl = textboxEl;
5288 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5293 textareaEl.setHeight(typeof opt.multiline == "number" ?
5294 opt.multiline : this.defaultTextHeight);
5295 activeTextEl = textareaEl;
5304 progressEl.setDisplayed(opt.progress === true);
5306 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5308 this.updateProgress(0);
5309 activeTextEl.dom.value = opt.value || "";
5311 dlg.setDefaultButton(activeTextEl);
5313 var bs = opt.buttons;
5317 }else if(bs && bs.yes){
5318 db = buttons["yes"];
5320 dlg.setDefaultButton(db);
5322 bwidth = updateButtons(opt.buttons);
5323 this.updateText(opt.msg);
5325 d.el.addClass(opt.cls);
5327 d.proxyDrag = opt.proxyDrag === true;
5328 d.modal = opt.modal !== false;
5329 d.mask = opt.modal !== false ? mask : false;
5331 // force it to the end of the z-index stack so it gets a cursor in FF
5332 document.body.appendChild(dlg.el.dom);
5333 d.animateTarget = null;
5334 d.show(options.animEl);
5340 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5341 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5342 * and closing the message box when the process is complete.
5343 * @param {String} title The title bar text
5344 * @param {String} msg The message box body text
5345 * @return {Roo.MessageBox} This message box
5347 progress : function(title, msg){
5354 minWidth: this.minProgressWidth,
5361 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5362 * If a callback function is passed it will be called after the user clicks the button, and the
5363 * id of the button that was clicked will be passed as the only parameter to the callback
5364 * (could also be the top-right close button).
5365 * @param {String} title The title bar text
5366 * @param {String} msg The message box body text
5367 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5368 * @param {Object} scope (optional) The scope of the callback function
5369 * @return {Roo.MessageBox} This message box
5371 alert : function(title, msg, fn, scope)
5386 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5387 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5388 * You are responsible for closing the message box when the process is complete.
5389 * @param {String} msg The message box body text
5390 * @param {String} title (optional) The title bar text
5391 * @return {Roo.MessageBox} This message box
5393 wait : function(msg, title){
5404 waitTimer = Roo.TaskMgr.start({
5406 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5414 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5415 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5416 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5417 * @param {String} title The title bar text
5418 * @param {String} msg The message box body text
5419 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5420 * @param {Object} scope (optional) The scope of the callback function
5421 * @return {Roo.MessageBox} This message box
5423 confirm : function(title, msg, fn, scope){
5427 buttons: this.YESNO,
5436 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5437 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5438 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5439 * (could also be the top-right close button) and the text that was entered will be passed as the two
5440 * parameters to the callback.
5441 * @param {String} title The title bar text
5442 * @param {String} msg The message box body text
5443 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5444 * @param {Object} scope (optional) The scope of the callback function
5445 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5446 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5447 * @return {Roo.MessageBox} This message box
5449 prompt : function(title, msg, fn, scope, multiline){
5453 buttons: this.OKCANCEL,
5458 multiline: multiline,
5465 * Button config that displays a single OK button
5470 * Button config that displays Yes and No buttons
5473 YESNO : {yes:true, no:true},
5475 * Button config that displays OK and Cancel buttons
5478 OKCANCEL : {ok:true, cancel:true},
5480 * Button config that displays Yes, No and Cancel buttons
5483 YESNOCANCEL : {yes:true, no:true, cancel:true},
5486 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5489 defaultTextHeight : 75,
5491 * The maximum width in pixels of the message box (defaults to 600)
5496 * The minimum width in pixels of the message box (defaults to 100)
5501 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5502 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5505 minProgressWidth : 250,
5507 * An object containing the default button text strings that can be overriden for localized language support.
5508 * Supported properties are: ok, cancel, yes and no.
5509 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5522 * Shorthand for {@link Roo.MessageBox}
5524 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5525 Roo.Msg = Roo.Msg || Roo.MessageBox;
5534 * @class Roo.bootstrap.nav.Bar
5535 * @extends Roo.bootstrap.Component
5537 * Bootstrap Navbar class
5540 * Create a new Navbar
5541 * @param {Object} config The config object
5545 Roo.bootstrap.nav.Bar = function(config){
5546 Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5550 * @event beforetoggle
5551 * Fire before toggle the menu
5552 * @param {Roo.EventObject} e
5554 "beforetoggle" : true
5558 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component, {
5567 getAutoCreate : function(){
5570 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5574 initEvents :function ()
5576 //Roo.log(this.el.select('.navbar-toggle',true));
5577 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5584 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5586 var size = this.el.getSize();
5587 this.maskEl.setSize(size.width, size.height);
5588 this.maskEl.enableDisplayMode("block");
5597 getChildContainer : function()
5599 if (this.el && this.el.select('.collapse').getCount()) {
5600 return this.el.select('.collapse',true).first();
5615 onToggle : function()
5618 if(this.fireEvent('beforetoggle', this) === false){
5621 var ce = this.el.select('.navbar-collapse',true).first();
5623 if (!ce.hasClass('show')) {
5633 * Expand the navbar pulldown
5635 expand : function ()
5638 var ce = this.el.select('.navbar-collapse',true).first();
5639 if (ce.hasClass('collapsing')) {
5642 ce.dom.style.height = '';
5644 ce.addClass('in'); // old...
5645 ce.removeClass('collapse');
5646 ce.addClass('show');
5647 var h = ce.getHeight();
5649 ce.removeClass('show');
5650 // at this point we should be able to see it..
5651 ce.addClass('collapsing');
5653 ce.setHeight(0); // resize it ...
5654 ce.on('transitionend', function() {
5655 //Roo.log('done transition');
5656 ce.removeClass('collapsing');
5657 ce.addClass('show');
5658 ce.removeClass('collapse');
5660 ce.dom.style.height = '';
5661 }, this, { single: true} );
5663 ce.dom.scrollTop = 0;
5666 * Collapse the navbar pulldown
5668 collapse : function()
5670 var ce = this.el.select('.navbar-collapse',true).first();
5672 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5673 // it's collapsed or collapsing..
5676 ce.removeClass('in'); // old...
5677 ce.setHeight(ce.getHeight());
5678 ce.removeClass('show');
5679 ce.addClass('collapsing');
5681 ce.on('transitionend', function() {
5682 ce.dom.style.height = '';
5683 ce.removeClass('collapsing');
5684 ce.addClass('collapse');
5685 }, this, { single: true} );
5705 * @class Roo.bootstrap.nav.Simplebar
5706 * @extends Roo.bootstrap.nav.Bar
5707 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5708 * Bootstrap Sidebar class
5710 * @cfg {Boolean} inverse is inverted color
5712 * @cfg {String} type (nav | pills | tabs)
5713 * @cfg {Boolean} arrangement stacked | justified
5714 * @cfg {String} align (left | right) alignment
5716 * @cfg {Boolean} main (true|false) main nav bar? default false
5717 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5719 * @cfg {String} tag (header|footer|nav|div) default is nav
5721 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5725 * Create a new Sidebar
5726 * @param {Object} config The config object
5730 Roo.bootstrap.nav.Simplebar = function(config){
5731 Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5734 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar, {
5750 getAutoCreate : function(){
5754 tag : this.tag || 'div',
5755 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5757 if (['light','white'].indexOf(this.weight) > -1) {
5758 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5760 cfg.cls += ' bg-' + this.weight;
5763 cfg.cls += ' navbar-inverse';
5767 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5769 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5778 cls: 'nav nav-' + this.xtype,
5784 this.type = this.type || 'nav';
5785 if (['tabs','pills'].indexOf(this.type) != -1) {
5786 cfg.cn[0].cls += ' nav-' + this.type
5790 if (this.type!=='nav') {
5791 Roo.log('nav type must be nav/tabs/pills')
5793 cfg.cn[0].cls += ' navbar-nav'
5799 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5800 cfg.cn[0].cls += ' nav-' + this.arrangement;
5804 if (this.align === 'right') {
5805 cfg.cn[0].cls += ' navbar-right';
5830 * navbar-expand-md fixed-top
5834 * @class Roo.bootstrap.nav.Headerbar
5835 * @extends Roo.bootstrap.nav.Simplebar
5836 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5837 * Bootstrap Sidebar class
5839 * @cfg {String} brand what is brand
5840 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5841 * @cfg {String} brand_href href of the brand
5842 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5843 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5844 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5845 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5848 * Create a new Sidebar
5849 * @param {Object} config The config object
5853 Roo.bootstrap.nav.Headerbar = function(config){
5854 Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5858 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar, {
5865 desktopCenter : false,
5868 getAutoCreate : function(){
5871 tag: this.nav || 'nav',
5872 cls: 'navbar navbar-expand-md',
5878 if (this.desktopCenter) {
5879 cn.push({cls : 'container', cn : []});
5887 cls: 'navbar-toggle navbar-toggler',
5888 'data-toggle': 'collapse',
5893 html: 'Toggle navigation'
5897 cls: 'icon-bar navbar-toggler-icon'
5910 cn.push( Roo.bootstrap.version == 4 ? btn : {
5912 cls: 'navbar-header',
5921 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5925 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5927 if (['light','white'].indexOf(this.weight) > -1) {
5928 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5930 cfg.cls += ' bg-' + this.weight;
5933 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5934 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5936 // tag can override this..
5938 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5941 if (this.brand !== '') {
5942 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5943 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5945 href: this.brand_href ? this.brand_href : '#',
5946 cls: 'navbar-brand',
5954 cfg.cls += ' main-nav';
5962 getHeaderChildContainer : function()
5964 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5965 return this.el.select('.navbar-header',true).first();
5968 return this.getChildContainer();
5971 getChildContainer : function()
5974 return this.el.select('.roo-navbar-collapse',true).first();
5979 initEvents : function()
5981 Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5983 if (this.autohide) {
5988 Roo.get(document).on('scroll',function(e) {
5989 var ns = Roo.get(document).getScroll().top;
5990 var os = prevScroll;
5994 ft.removeClass('slideDown');
5995 ft.addClass('slideUp');
5998 ft.removeClass('slideUp');
5999 ft.addClass('slideDown');
6020 * @class Roo.bootstrap.nav.Sidebar
6021 * @extends Roo.bootstrap.nav.Bar
6022 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6023 * Bootstrap Sidebar class
6026 * Create a new Sidebar
6027 * @param {Object} config The config object
6031 Roo.bootstrap.nav.Sidebar = function(config){
6032 Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6035 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar, {
6037 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6039 getAutoCreate : function(){
6044 cls: 'sidebar sidebar-nav'
6066 * @class Roo.bootstrap.nav.Group
6067 * @extends Roo.bootstrap.Component
6068 * @children Roo.bootstrap.nav.Item
6069 * Bootstrap NavGroup class
6070 * @cfg {String} align (left|right)
6071 * @cfg {Boolean} inverse
6072 * @cfg {String} type (nav|pills|tab) default nav
6073 * @cfg {String} navId - reference Id for navbar.
6074 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6077 * Create a new nav group
6078 * @param {Object} config The config object
6081 Roo.bootstrap.nav.Group = function(config){
6082 Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6085 Roo.bootstrap.nav.Group.register(this);
6089 * Fires when the active item changes
6090 * @param {Roo.bootstrap.nav.Group} this
6091 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6092 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6099 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component, {
6111 getAutoCreate : function()
6113 var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6119 if (Roo.bootstrap.version == 4) {
6120 if (['tabs','pills'].indexOf(this.type) != -1) {
6121 cfg.cls += ' nav-' + this.type;
6123 // trying to remove so header bar can right align top?
6124 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6125 // do not use on header bar...
6126 cfg.cls += ' navbar-nav';
6131 if (['tabs','pills'].indexOf(this.type) != -1) {
6132 cfg.cls += ' nav-' + this.type
6134 if (this.type !== 'nav') {
6135 Roo.log('nav type must be nav/tabs/pills')
6137 cfg.cls += ' navbar-nav'
6141 if (this.parent() && this.parent().sidebar) {
6144 cls: 'dashboard-menu sidebar-menu'
6150 if (this.form === true) {
6153 cls: 'navbar-form form-inline'
6155 //nav navbar-right ml-md-auto
6156 if (this.align === 'right') {
6157 cfg.cls += ' navbar-right ml-md-auto';
6159 cfg.cls += ' navbar-left';
6163 if (this.align === 'right') {
6164 cfg.cls += ' navbar-right ml-md-auto';
6166 cfg.cls += ' mr-auto';
6170 cfg.cls += ' navbar-inverse';
6178 * sets the active Navigation item
6179 * @param {Roo.bootstrap.nav.Item} the new current navitem
6181 setActiveItem : function(item)
6184 Roo.each(this.navItems, function(v){
6189 v.setActive(false, true);
6196 item.setActive(true, true);
6197 this.fireEvent('changed', this, item, prev);
6202 * gets the active Navigation item
6203 * @return {Roo.bootstrap.nav.Item} the current navitem
6205 getActive : function()
6209 Roo.each(this.navItems, function(v){
6220 indexOfNav : function()
6224 Roo.each(this.navItems, function(v,i){
6235 * adds a Navigation item
6236 * @param {Roo.bootstrap.nav.Item} the navitem to add
6238 addItem : function(cfg)
6240 if (this.form && Roo.bootstrap.version == 4) {
6243 var cn = new Roo.bootstrap.nav.Item(cfg);
6245 cn.parentId = this.id;
6246 cn.onRender(this.el, null);
6250 * register a Navigation item
6251 * @param {Roo.bootstrap.nav.Item} the navitem to add
6253 register : function(item)
6255 this.navItems.push( item);
6256 item.navId = this.navId;
6261 * clear all the Navigation item
6264 clearAll : function()
6267 this.el.dom.innerHTML = '';
6270 getNavItem: function(tabId)
6273 Roo.each(this.navItems, function(e) {
6274 if (e.tabId == tabId) {
6284 setActiveNext : function()
6286 var i = this.indexOfNav(this.getActive());
6287 if (i > this.navItems.length) {
6290 this.setActiveItem(this.navItems[i+1]);
6292 setActivePrev : function()
6294 var i = this.indexOfNav(this.getActive());
6298 this.setActiveItem(this.navItems[i-1]);
6300 clearWasActive : function(except) {
6301 Roo.each(this.navItems, function(e) {
6302 if (e.tabId != except.tabId && e.was_active) {
6303 e.was_active = false;
6310 getWasActive : function ()
6313 Roo.each(this.navItems, function(e) {
6328 Roo.apply(Roo.bootstrap.nav.Group, {
6332 * register a Navigation Group
6333 * @param {Roo.bootstrap.nav.Group} the navgroup to add
6335 register : function(navgrp)
6337 this.groups[navgrp.navId] = navgrp;
6341 * fetch a Navigation Group based on the navigation ID
6342 * @param {string} the navgroup to add
6343 * @returns {Roo.bootstrap.nav.Group} the navgroup
6345 get: function(navId) {
6346 if (typeof(this.groups[navId]) == 'undefined') {
6348 //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6350 return this.groups[navId] ;
6358 * @class Roo.bootstrap.nav.Item
6359 * @extends Roo.bootstrap.Component
6360 * @children Roo.bootstrap.Container Roo.bootstrap.Button
6361 * @parent Roo.bootstrap.nav.Group
6363 * Bootstrap Navbar.NavItem class
6365 * @cfg {String} href link to
6366 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6367 * @cfg {Boolean} button_outline show and outlined button
6368 * @cfg {String} html content of button
6369 * @cfg {String} badge text inside badge
6370 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6371 * @cfg {String} glyphicon DEPRICATED - use fa
6372 * @cfg {String} icon DEPRICATED - use fa
6373 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6374 * @cfg {Boolean} active Is item active
6375 * @cfg {Boolean} disabled Is item disabled
6376 * @cfg {String} linkcls Link Class
6377 * @cfg {Boolean} preventDefault (true | false) default false
6378 * @cfg {String} tabId the tab that this item activates.
6379 * @cfg {String} tagtype (a|span) render as a href or span?
6380 * @cfg {Boolean} animateRef (true|false) link to element default false
6381 * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
6384 * Create a new Navbar Item
6385 * @param {Object} config The config object
6387 Roo.bootstrap.nav.Item = function(config){
6388 Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6393 * The raw click event for the entire grid.
6394 * @param {Roo.EventObject} e
6399 * Fires when the active item active state changes
6400 * @param {Roo.bootstrap.nav.Item} this
6401 * @param {boolean} state the new state
6407 * Fires when scroll to element
6408 * @param {Roo.bootstrap.nav.Item} this
6409 * @param {Object} options
6410 * @param {Roo.EventObject} e
6418 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component, {
6427 preventDefault : false,
6435 button_outline : false,
6439 getAutoCreate : function(){
6446 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6449 cfg.cls += ' active' ;
6451 if (this.disabled) {
6452 cfg.cls += ' disabled';
6456 if (this.button_weight.length) {
6457 cfg.tag = this.href ? 'a' : 'button';
6458 cfg.html = this.html || '';
6459 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6461 cfg.href = this.href;
6464 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6466 cfg.cls += " nav-html";
6469 // menu .. should add dropdown-menu class - so no need for carat..
6471 if (this.badge !== '') {
6473 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6478 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6482 href : this.href || "#",
6483 html: this.html || '',
6487 if (this.tagtype == 'a') {
6488 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6492 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6493 } else if (this.fa) {
6494 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6495 } else if(this.glyphicon) {
6496 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6498 cfg.cn[0].cls += " nav-html";
6502 cfg.cn[0].html += " <span class='caret'></span>";
6506 if (this.badge !== '') {
6507 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6515 onRender : function(ct, position)
6517 // Roo.log("Call onRender: " + this.xtype);
6518 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6522 var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6523 this.navLink = this.el.select('.nav-link',true).first();
6524 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6529 initEvents: function()
6531 if (typeof (this.menu) != 'undefined') {
6532 this.menu.parentType = this.xtype;
6533 this.menu.triggerEl = this.el;
6534 this.menu = this.addxtype(Roo.apply({}, this.menu));
6537 this.el.on('click', this.onClick, this);
6539 //if(this.tagtype == 'span'){
6540 // this.el.select('span',true).on('click', this.onClick, this);
6543 // at this point parent should be available..
6544 this.parent().register(this);
6547 onClick : function(e)
6549 if (e.getTarget('.dropdown-menu-item')) {
6550 // did you click on a menu itemm.... - then don't trigger onclick..
6555 this.preventDefault ||
6556 this.href === false ||
6559 //Roo.log("NavItem - prevent Default?");
6563 if (this.disabled) {
6567 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6568 if (tg && tg.transition) {
6569 Roo.log("waiting for the transitionend");
6575 //Roo.log("fire event clicked");
6576 if(this.fireEvent('click', this, e) === false){
6580 if(this.tagtype == 'span'){
6584 //Roo.log(this.href);
6585 var ael = this.el.select('a',true).first();
6588 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6589 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6590 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6591 return; // ignore... - it's a 'hash' to another page.
6593 Roo.log("NavItem - prevent Default?");
6595 this.scrollToElement(e);
6599 var p = this.parent();
6601 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6602 if (typeof(p.setActiveItem) !== 'undefined') {
6603 p.setActiveItem(this);
6607 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6608 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6609 // remove the collapsed menu expand...
6610 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6614 isActive: function () {
6617 setActive : function(state, fire, is_was_active)
6619 if (this.active && !state && this.navId) {
6620 this.was_active = true;
6621 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6623 nv.clearWasActive(this);
6627 this.active = state;
6630 this.el.removeClass('active');
6631 this.navLink ? this.navLink.removeClass('active') : false;
6632 } else if (!this.el.hasClass('active')) {
6634 this.el.addClass('active');
6635 if (Roo.bootstrap.version == 4 && this.navLink ) {
6636 this.navLink.addClass('active');
6641 this.fireEvent('changed', this, state);
6644 // show a panel if it's registered and related..
6646 if (!this.navId || !this.tabId || !state || is_was_active) {
6650 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6654 var pan = tg.getPanelByName(this.tabId);
6658 // if we can not flip to new panel - go back to old nav highlight..
6659 if (false == tg.showPanel(pan)) {
6660 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6662 var onav = nv.getWasActive();
6664 onav.setActive(true, false, true);
6673 // this should not be here...
6674 setDisabled : function(state)
6676 this.disabled = state;
6678 this.el.removeClass('disabled');
6679 } else if (!this.el.hasClass('disabled')) {
6680 this.el.addClass('disabled');
6686 * Fetch the element to display the tooltip on.
6687 * @return {Roo.Element} defaults to this.el
6689 tooltipEl : function()
6691 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6694 scrollToElement : function(e)
6696 var c = document.body;
6699 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6701 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6702 c = document.documentElement;
6705 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6711 var o = target.calcOffsetsTo(c);
6718 this.fireEvent('scrollto', this, options, e);
6720 Roo.get(c).scrollTo('top', options.value, true);
6725 * Set the HTML (text content) of the item
6726 * @param {string} html content for the nav item
6728 setHtml : function(html)
6731 this.htmlEl.dom.innerHTML = html;
6743 * <span> icon </span>
6744 * <span> text </span>
6745 * <span>badge </span>
6749 * @class Roo.bootstrap.nav.SidebarItem
6750 * @extends Roo.bootstrap.nav.Item
6751 * Bootstrap Navbar.NavSidebarItem class
6753 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6754 * {Boolean} open is the menu open
6755 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6756 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6757 * {String} buttonSize (sm|md|lg)the extra classes for the button
6758 * {Boolean} showArrow show arrow next to the text (default true)
6760 * Create a new Navbar Button
6761 * @param {Object} config The config object
6763 Roo.bootstrap.nav.SidebarItem = function(config){
6764 Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6769 * The raw click event for the entire grid.
6770 * @param {Roo.EventObject} e
6775 * Fires when the active item active state changes
6776 * @param {Roo.bootstrap.nav.SidebarItem} this
6777 * @param {boolean} state the new state
6785 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item, {
6787 badgeWeight : 'default',
6793 buttonWeight : 'default',
6799 getAutoCreate : function(){
6804 href : this.href || '#',
6810 if(this.buttonView){
6813 href : this.href || '#',
6814 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6827 cfg.cls += ' active';
6830 if (this.disabled) {
6831 cfg.cls += ' disabled';
6834 cfg.cls += ' open x-open';
6837 if (this.glyphicon || this.icon) {
6838 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6839 a.cn.push({ tag : 'i', cls : c }) ;
6842 if(!this.buttonView){
6845 html : this.html || ''
6852 if (this.badge !== '') {
6853 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6859 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6862 a.cls += ' dropdown-toggle treeview' ;
6868 initEvents : function()
6870 if (typeof (this.menu) != 'undefined') {
6871 this.menu.parentType = this.xtype;
6872 this.menu.triggerEl = this.el;
6873 this.menu = this.addxtype(Roo.apply({}, this.menu));
6876 this.el.on('click', this.onClick, this);
6878 if(this.badge !== ''){
6879 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6884 onClick : function(e)
6891 if(this.preventDefault){
6895 this.fireEvent('click', this, e);
6898 disable : function()
6900 this.setDisabled(true);
6905 this.setDisabled(false);
6908 setDisabled : function(state)
6910 if(this.disabled == state){
6914 this.disabled = state;
6917 this.el.addClass('disabled');
6921 this.el.removeClass('disabled');
6926 setActive : function(state)
6928 if(this.active == state){
6932 this.active = state;
6935 this.el.addClass('active');
6939 this.el.removeClass('active');
6944 isActive: function ()
6949 setBadge : function(str)
6955 this.badgeEl.dom.innerHTML = str;
6972 * @class Roo.bootstrap.nav.ProgressBar
6973 * @extends Roo.bootstrap.Component
6974 * @children Roo.bootstrap.nav.ProgressBarItem
6975 * Bootstrap NavProgressBar class
6978 * Create a new nav progress bar - a bar indicating step along a process
6979 * @param {Object} config The config object
6982 Roo.bootstrap.nav.ProgressBar = function(config){
6983 Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6985 this.bullets = this.bullets || [];
6987 // Roo.bootstrap.nav.ProgressBar.register(this);
6991 * Fires when the active item changes
6992 * @param {Roo.bootstrap.nav.ProgressBar} this
6993 * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6994 * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item
7001 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component, {
7003 * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
7004 * Bullets for the Nav Progress bar for the toolbar
7009 getAutoCreate : function()
7011 var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7015 cls : 'roo-navigation-bar-group',
7019 cls : 'roo-navigation-top-bar'
7023 cls : 'roo-navigation-bullets-bar',
7027 cls : 'roo-navigation-bar'
7034 cls : 'roo-navigation-bottom-bar'
7044 initEvents: function()
7049 onRender : function(ct, position)
7051 Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7053 if(this.bullets.length){
7054 Roo.each(this.bullets, function(b){
7063 addItem : function(cfg)
7065 var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7067 item.parentId = this.id;
7068 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7071 var top = new Roo.bootstrap.Element({
7073 cls : 'roo-navigation-bar-text'
7076 var bottom = new Roo.bootstrap.Element({
7078 cls : 'roo-navigation-bar-text'
7081 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7082 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7084 var topText = new Roo.bootstrap.Element({
7086 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7089 var bottomText = new Roo.bootstrap.Element({
7091 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7094 topText.onRender(top.el, null);
7095 bottomText.onRender(bottom.el, null);
7098 item.bottomEl = bottom;
7101 this.barItems.push(item);
7106 getActive : function()
7110 Roo.each(this.barItems, function(v){
7112 if (!v.isActive()) {
7124 setActiveItem : function(item)
7128 Roo.each(this.barItems, function(v){
7129 if (v.rid == item.rid) {
7139 item.setActive(true);
7141 this.fireEvent('changed', this, item, prev);
7144 getBarItem: function(rid)
7148 Roo.each(this.barItems, function(e) {
7160 indexOfItem : function(item)
7164 Roo.each(this.barItems, function(v, i){
7166 if (v.rid != item.rid) {
7177 setActiveNext : function()
7179 var i = this.indexOfItem(this.getActive());
7181 if (i > this.barItems.length) {
7185 this.setActiveItem(this.barItems[i+1]);
7188 setActivePrev : function()
7190 var i = this.indexOfItem(this.getActive());
7196 this.setActiveItem(this.barItems[i-1]);
7201 if(!this.barItems.length){
7205 var width = 100 / this.barItems.length;
7207 Roo.each(this.barItems, function(i){
7208 i.el.setStyle('width', width + '%');
7209 i.topEl.el.setStyle('width', width + '%');
7210 i.bottomEl.el.setStyle('width', width + '%');
7224 * @class Roo.bootstrap.nav.ProgressBarItem
7225 * @extends Roo.bootstrap.Component
7226 * Bootstrap NavProgressBarItem class
7227 * @cfg {String} rid the reference id
7228 * @cfg {Boolean} active (true|false) Is item active default false
7229 * @cfg {Boolean} disabled (true|false) Is item active default false
7230 * @cfg {String} html
7231 * @cfg {String} position (top|bottom) text position default bottom
7232 * @cfg {String} icon show icon instead of number
7235 * Create a new NavProgressBarItem
7236 * @param {Object} config The config object
7238 Roo.bootstrap.nav.ProgressBarItem = function(config){
7239 Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7244 * The raw click event for the entire grid.
7245 * @param {Roo.bootstrap.nav.ProgressBarItem} this
7246 * @param {Roo.EventObject} e
7253 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component, {
7259 position : 'bottom',
7262 getAutoCreate : function()
7264 var iconCls = 'roo-navigation-bar-item-icon';
7266 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7270 cls: 'roo-navigation-bar-item',
7280 cfg.cls += ' active';
7283 cfg.cls += ' disabled';
7289 disable : function()
7291 this.setDisabled(true);
7296 this.setDisabled(false);
7299 initEvents: function()
7301 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7303 this.iconEl.on('click', this.onClick, this);
7306 onClick : function(e)
7314 if(this.fireEvent('click', this, e) === false){
7318 this.parent().setActiveItem(this);
7321 isActive: function ()
7326 setActive : function(state)
7328 if(this.active == state){
7332 this.active = state;
7335 this.el.addClass('active');
7339 this.el.removeClass('active');
7344 setDisabled : function(state)
7346 if(this.disabled == state){
7350 this.disabled = state;
7353 this.el.addClass('disabled');
7357 this.el.removeClass('disabled');
7360 tooltipEl : function()
7362 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7373 Roo.namespace('Roo.bootstrap.breadcrumb');
7377 * @class Roo.bootstrap.breadcrumb.Nav
7378 * @extends Roo.bootstrap.Component
7379 * Bootstrap Breadcrumb Nav Class
7381 * @children Roo.bootstrap.breadcrumb.Item
7384 * Create a new breadcrumb.Nav
7385 * @param {Object} config The config object
7389 Roo.bootstrap.breadcrumb.Nav = function(config){
7390 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7395 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
7397 getAutoCreate : function()
7414 initEvents: function()
7416 this.olEl = this.el.select('ol',true).first();
7418 getChildContainer : function()
7434 * @class Roo.bootstrap.breadcrumb.Nav
7435 * @extends Roo.bootstrap.Component
7436 * @children Roo.bootstrap.Component
7437 * @parent Roo.bootstrap.breadcrumb.Nav
7438 * Bootstrap Breadcrumb Nav Class
7441 * @cfg {String} html the content of the link.
7442 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7443 * @cfg {Boolean} active is it active
7447 * Create a new breadcrumb.Nav
7448 * @param {Object} config The config object
7451 Roo.bootstrap.breadcrumb.Item = function(config){
7452 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7457 * The img click event for the img.
7458 * @param {Roo.EventObject} e
7465 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7470 getAutoCreate : function()
7475 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7477 if (this.href !== false) {
7484 cfg.html = this.html;
7490 initEvents: function()
7493 this.el.select('a', true).first().on('click',this.onClick, this)
7497 onClick : function(e)
7500 this.fireEvent('click',this, e);
7513 * @class Roo.bootstrap.Row
7514 * @extends Roo.bootstrap.Component
7515 * @children Roo.bootstrap.Component
7516 * Bootstrap Row class (contains columns...)
7520 * @param {Object} config The config object
7523 Roo.bootstrap.Row = function(config){
7524 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7527 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7529 getAutoCreate : function(){
7548 * @class Roo.bootstrap.Pagination
7549 * @extends Roo.bootstrap.Component
7550 * @children Roo.bootstrap.Pagination
7551 * Bootstrap Pagination class
7553 * @cfg {String} size (xs|sm|md|lg|xl)
7554 * @cfg {Boolean} inverse
7557 * Create a new Pagination
7558 * @param {Object} config The config object
7561 Roo.bootstrap.Pagination = function(config){
7562 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7565 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7571 getAutoCreate : function(){
7577 cfg.cls += ' inverse';
7583 cfg.cls += " " + this.cls;
7601 * @class Roo.bootstrap.PaginationItem
7602 * @extends Roo.bootstrap.Component
7603 * Bootstrap PaginationItem class
7604 * @cfg {String} html text
7605 * @cfg {String} href the link
7606 * @cfg {Boolean} preventDefault (true | false) default true
7607 * @cfg {Boolean} active (true | false) default false
7608 * @cfg {Boolean} disabled default false
7612 * Create a new PaginationItem
7613 * @param {Object} config The config object
7617 Roo.bootstrap.PaginationItem = function(config){
7618 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7623 * The raw click event for the entire grid.
7624 * @param {Roo.EventObject} e
7630 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7634 preventDefault: true,
7639 getAutoCreate : function(){
7645 href : this.href ? this.href : '#',
7646 html : this.html ? this.html : ''
7656 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7660 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7666 initEvents: function() {
7668 this.el.on('click', this.onClick, this);
7671 onClick : function(e)
7673 Roo.log('PaginationItem on click ');
7674 if(this.preventDefault){
7682 this.fireEvent('click', this, e);
7698 * @class Roo.bootstrap.Slider
7699 * @extends Roo.bootstrap.Component
7700 * Bootstrap Slider class
7703 * Create a new Slider
7704 * @param {Object} config The config object
7707 Roo.bootstrap.Slider = function(config){
7708 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7711 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7713 getAutoCreate : function(){
7717 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7721 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7733 * Ext JS Library 1.1.1
7734 * Copyright(c) 2006-2007, Ext JS, LLC.
7736 * Originally Released Under LGPL - original licence link has changed is not relivant.
7739 * <script type="text/javascript">
7742 * @extends Roo.dd.DDProxy
7743 * @class Roo.grid.SplitDragZone
7744 * Support for Column Header resizing
7746 * @param {Object} config
7749 // This is a support class used internally by the Grid components
7750 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7752 this.view = grid.getView();
7753 this.proxy = this.view.resizeProxy;
7754 Roo.grid.SplitDragZone.superclass.constructor.call(
7757 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7759 dragElId : Roo.id(this.proxy.dom),
7764 this.setHandleElId(Roo.id(hd));
7765 if (hd2 !== false) {
7766 this.setOuterHandleElId(Roo.id(hd2));
7769 this.scroll = false;
7771 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7772 fly: Roo.Element.fly,
7774 b4StartDrag : function(x, y){
7775 this.view.headersDisabled = true;
7776 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7777 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7779 this.proxy.setHeight(h);
7781 // for old system colWidth really stored the actual width?
7782 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7783 // which in reality did not work.. - it worked only for fixed sizes
7784 // for resizable we need to use actual sizes.
7785 var w = this.cm.getColumnWidth(this.cellIndex);
7786 if (!this.view.mainWrap) {
7788 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7793 // this was w-this.grid.minColumnWidth;
7794 // doesnt really make sense? - w = thie curren width or the rendered one?
7795 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7796 this.resetConstraints();
7797 this.setXConstraint(minw, 1000);
7798 this.setYConstraint(0, 0);
7799 this.minX = x - minw;
7800 this.maxX = x + 1000;
7802 if (!this.view.mainWrap) { // this is Bootstrap code..
7803 this.getDragEl().style.display='block';
7806 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7810 handleMouseDown : function(e){
7811 ev = Roo.EventObject.setEvent(e);
7812 var t = this.fly(ev.getTarget());
7813 if(t.hasClass("x-grid-split")){
7814 this.cellIndex = this.view.getCellIndex(t.dom);
7816 this.cm = this.grid.colModel;
7817 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7818 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7823 endDrag : function(e){
7824 this.view.headersDisabled = false;
7825 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7826 var diff = endX - this.startPos;
7828 var w = this.cm.getColumnWidth(this.cellIndex);
7829 if (!this.view.mainWrap) {
7832 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7835 autoOffset : function(){
7840 * Ext JS Library 1.1.1
7841 * Copyright(c) 2006-2007, Ext JS, LLC.
7843 * Originally Released Under LGPL - original licence link has changed is not relivant.
7846 * <script type="text/javascript">
7850 * @class Roo.grid.AbstractSelectionModel
7851 * @extends Roo.util.Observable
7853 * Abstract base class for grid SelectionModels. It provides the interface that should be
7854 * implemented by descendant classes. This class should not be directly instantiated.
7857 Roo.grid.AbstractSelectionModel = function(){
7858 this.locked = false;
7859 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7862 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7863 /** @ignore Called by the grid automatically. Do not call directly. */
7864 init : function(grid){
7870 * Locks the selections.
7877 * Unlocks the selections.
7879 unlock : function(){
7880 this.locked = false;
7884 * Returns true if the selections are locked.
7887 isLocked : function(){
7892 * Ext JS Library 1.1.1
7893 * Copyright(c) 2006-2007, Ext JS, LLC.
7895 * Originally Released Under LGPL - original licence link has changed is not relivant.
7898 * <script type="text/javascript">
7901 * @extends Roo.grid.AbstractSelectionModel
7902 * @class Roo.grid.RowSelectionModel
7903 * The default SelectionModel used by {@link Roo.grid.Grid}.
7904 * It supports multiple selections and keyboard selection/navigation.
7906 * @param {Object} config
7908 Roo.grid.RowSelectionModel = function(config){
7909 Roo.apply(this, config);
7910 this.selections = new Roo.util.MixedCollection(false, function(o){
7915 this.lastActive = false;
7919 * @event selectionchange
7920 * Fires when the selection changes
7921 * @param {SelectionModel} this
7923 "selectionchange" : true,
7925 * @event afterselectionchange
7926 * Fires after the selection changes (eg. by key press or clicking)
7927 * @param {SelectionModel} this
7929 "afterselectionchange" : true,
7931 * @event beforerowselect
7932 * Fires when a row is selected being selected, return false to cancel.
7933 * @param {SelectionModel} this
7934 * @param {Number} rowIndex The selected index
7935 * @param {Boolean} keepExisting False if other selections will be cleared
7937 "beforerowselect" : true,
7940 * Fires when a row is selected.
7941 * @param {SelectionModel} this
7942 * @param {Number} rowIndex The selected index
7943 * @param {Roo.data.Record} r The record
7947 * @event rowdeselect
7948 * Fires when a row is deselected.
7949 * @param {SelectionModel} this
7950 * @param {Number} rowIndex The selected index
7952 "rowdeselect" : true
7954 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7955 this.locked = false;
7958 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7960 * @cfg {Boolean} singleSelect
7961 * True to allow selection of only one row at a time (defaults to false)
7963 singleSelect : false,
7966 initEvents : function(){
7968 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7969 this.grid.on("mousedown", this.handleMouseDown, this);
7970 }else{ // allow click to work like normal
7971 this.grid.on("rowclick", this.handleDragableRowClick, this);
7973 // bootstrap does not have a view..
7974 var view = this.grid.view ? this.grid.view : this.grid;
7975 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7978 this.selectPrevious(e.shiftKey);
7979 }else if(this.last !== false && this.lastActive !== false){
7980 var last = this.last;
7981 this.selectRange(this.last, this.lastActive-1);
7982 view.focusRow(this.lastActive);
7987 this.selectFirstRow();
7989 this.fireEvent("afterselectionchange", this);
7991 "down" : function(e){
7993 this.selectNext(e.shiftKey);
7994 }else if(this.last !== false && this.lastActive !== false){
7995 var last = this.last;
7996 this.selectRange(this.last, this.lastActive+1);
7997 view.focusRow(this.lastActive);
8002 this.selectFirstRow();
8004 this.fireEvent("afterselectionchange", this);
8010 view.on("refresh", this.onRefresh, this);
8011 view.on("rowupdated", this.onRowUpdated, this);
8012 view.on("rowremoved", this.onRemove, this);
8016 onRefresh : function(){
8017 var ds = this.grid.ds, i, v = this.grid.view;
8018 var s = this.selections;
8020 if((i = ds.indexOfId(r.id)) != -1){
8022 s.add(ds.getAt(i)); // updating the selection relate data
8030 onRemove : function(v, index, r){
8031 this.selections.remove(r);
8035 onRowUpdated : function(v, index, r){
8036 if(this.isSelected(r)){
8037 v.onRowSelect(index);
8043 * @param {Array} records The records to select
8044 * @param {Boolean} keepExisting (optional) True to keep existing selections
8046 selectRecords : function(records, keepExisting){
8048 this.clearSelections();
8050 var ds = this.grid.ds;
8051 for(var i = 0, len = records.length; i < len; i++){
8052 this.selectRow(ds.indexOf(records[i]), true);
8057 * Gets the number of selected rows.
8060 getCount : function(){
8061 return this.selections.length;
8065 * Selects the first row in the grid.
8067 selectFirstRow : function(){
8072 * Select the last row.
8073 * @param {Boolean} keepExisting (optional) True to keep existing selections
8075 selectLastRow : function(keepExisting){
8076 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8080 * Selects the row immediately following the last selected row.
8081 * @param {Boolean} keepExisting (optional) True to keep existing selections
8083 selectNext : function(keepExisting){
8084 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8085 this.selectRow(this.last+1, keepExisting);
8086 var view = this.grid.view ? this.grid.view : this.grid;
8087 view.focusRow(this.last);
8092 * Selects the row that precedes the last selected row.
8093 * @param {Boolean} keepExisting (optional) True to keep existing selections
8095 selectPrevious : function(keepExisting){
8097 this.selectRow(this.last-1, keepExisting);
8098 var view = this.grid.view ? this.grid.view : this.grid;
8099 view.focusRow(this.last);
8104 * Returns the selected records
8105 * @return {Array} Array of selected records
8107 getSelections : function(){
8108 return [].concat(this.selections.items);
8112 * Returns the first selected record.
8115 getSelected : function(){
8116 return this.selections.itemAt(0);
8121 * Clears all selections.
8123 clearSelections : function(fast){
8128 var ds = this.grid.ds;
8129 var s = this.selections;
8131 this.deselectRow(ds.indexOfId(r.id));
8135 this.selections.clear();
8144 selectAll : function(){
8148 this.selections.clear();
8149 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8150 this.selectRow(i, true);
8155 * Returns True if there is a selection.
8158 hasSelection : function(){
8159 return this.selections.length > 0;
8163 * Returns True if the specified row is selected.
8164 * @param {Number/Record} record The record or index of the record to check
8167 isSelected : function(index){
8168 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8169 return (r && this.selections.key(r.id) ? true : false);
8173 * Returns True if the specified record id is selected.
8174 * @param {String} id The id of record to check
8177 isIdSelected : function(id){
8178 return (this.selections.key(id) ? true : false);
8182 handleMouseDown : function(e, t)
8184 var view = this.grid.view ? this.grid.view : this.grid;
8186 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8189 if(e.shiftKey && this.last !== false){
8190 var last = this.last;
8191 this.selectRange(last, rowIndex, e.ctrlKey);
8192 this.last = last; // reset the last
8193 view.focusRow(rowIndex);
8195 var isSelected = this.isSelected(rowIndex);
8196 if(e.button !== 0 && isSelected){
8197 view.focusRow(rowIndex);
8198 }else if(e.ctrlKey && isSelected){
8199 this.deselectRow(rowIndex);
8200 }else if(!isSelected){
8201 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8202 view.focusRow(rowIndex);
8205 this.fireEvent("afterselectionchange", this);
8208 handleDragableRowClick : function(grid, rowIndex, e)
8210 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8211 this.selectRow(rowIndex, false);
8212 var view = this.grid.view ? this.grid.view : this.grid;
8213 view.focusRow(rowIndex);
8214 this.fireEvent("afterselectionchange", this);
8219 * Selects multiple rows.
8220 * @param {Array} rows Array of the indexes of the row to select
8221 * @param {Boolean} keepExisting (optional) True to keep existing selections
8223 selectRows : function(rows, keepExisting){
8225 this.clearSelections();
8227 for(var i = 0, len = rows.length; i < len; i++){
8228 this.selectRow(rows[i], true);
8233 * Selects a range of rows. All rows in between startRow and endRow are also selected.
8234 * @param {Number} startRow The index of the first row in the range
8235 * @param {Number} endRow The index of the last row in the range
8236 * @param {Boolean} keepExisting (optional) True to retain existing selections
8238 selectRange : function(startRow, endRow, keepExisting){
8243 this.clearSelections();
8245 if(startRow <= endRow){
8246 for(var i = startRow; i <= endRow; i++){
8247 this.selectRow(i, true);
8250 for(var i = startRow; i >= endRow; i--){
8251 this.selectRow(i, true);
8257 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8258 * @param {Number} startRow The index of the first row in the range
8259 * @param {Number} endRow The index of the last row in the range
8261 deselectRange : function(startRow, endRow, preventViewNotify){
8265 for(var i = startRow; i <= endRow; i++){
8266 this.deselectRow(i, preventViewNotify);
8272 * @param {Number} row The index of the row to select
8273 * @param {Boolean} keepExisting (optional) True to keep existing selections
8275 selectRow : function(index, keepExisting, preventViewNotify){
8276 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8279 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8280 if(!keepExisting || this.singleSelect){
8281 this.clearSelections();
8283 var r = this.grid.ds.getAt(index);
8284 this.selections.add(r);
8285 this.last = this.lastActive = index;
8286 if(!preventViewNotify){
8287 var view = this.grid.view ? this.grid.view : this.grid;
8288 view.onRowSelect(index);
8290 this.fireEvent("rowselect", this, index, r);
8291 this.fireEvent("selectionchange", this);
8297 * @param {Number} row The index of the row to deselect
8299 deselectRow : function(index, preventViewNotify){
8303 if(this.last == index){
8306 if(this.lastActive == index){
8307 this.lastActive = false;
8309 var r = this.grid.ds.getAt(index);
8310 this.selections.remove(r);
8311 if(!preventViewNotify){
8312 var view = this.grid.view ? this.grid.view : this.grid;
8313 view.onRowDeselect(index);
8315 this.fireEvent("rowdeselect", this, index);
8316 this.fireEvent("selectionchange", this);
8320 restoreLast : function(){
8322 this.last = this._last;
8327 acceptsNav : function(row, col, cm){
8328 return !cm.isHidden(col) && cm.isCellEditable(col, row);
8332 onEditorKey : function(field, e){
8333 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8338 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8340 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8342 }else if(k == e.ENTER && !e.ctrlKey){
8346 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8348 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8350 }else if(k == e.ESC){
8354 g.startEditing(newCell[0], newCell[1]);
8359 * Ext JS Library 1.1.1
8360 * Copyright(c) 2006-2007, Ext JS, LLC.
8362 * Originally Released Under LGPL - original licence link has changed is not relivant.
8365 * <script type="text/javascript">
8370 * @class Roo.grid.ColumnModel
8371 * @extends Roo.util.Observable
8372 * This is the default implementation of a ColumnModel used by the Grid. It defines
8373 * the columns in the grid.
8376 var colModel = new Roo.grid.ColumnModel([
8377 {header: "Ticker", width: 60, sortable: true, locked: true},
8378 {header: "Company Name", width: 150, sortable: true},
8379 {header: "Market Cap.", width: 100, sortable: true},
8380 {header: "$ Sales", width: 100, sortable: true, renderer: money},
8381 {header: "Employees", width: 100, sortable: true, resizable: false}
8386 * The config options listed for this class are options which may appear in each
8387 * individual column definition.
8388 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8390 * @param {Object} config An Array of column config objects. See this class's
8391 * config objects for details.
8393 Roo.grid.ColumnModel = function(config){
8395 * The config passed into the constructor
8397 this.config = []; //config;
8400 // if no id, create one
8401 // if the column does not have a dataIndex mapping,
8402 // map it to the order it is in the config
8403 for(var i = 0, len = config.length; i < len; i++){
8404 this.addColumn(config[i]);
8409 * The width of columns which have no width specified (defaults to 100)
8412 this.defaultWidth = 100;
8415 * Default sortable of columns which have no sortable specified (defaults to false)
8418 this.defaultSortable = false;
8422 * @event widthchange
8423 * Fires when the width of a column changes.
8424 * @param {ColumnModel} this
8425 * @param {Number} columnIndex The column index
8426 * @param {Number} newWidth The new width
8428 "widthchange": true,
8430 * @event headerchange
8431 * Fires when the text of a header changes.
8432 * @param {ColumnModel} this
8433 * @param {Number} columnIndex The column index
8434 * @param {Number} newText The new header text
8436 "headerchange": true,
8438 * @event hiddenchange
8439 * Fires when a column is hidden or "unhidden".
8440 * @param {ColumnModel} this
8441 * @param {Number} columnIndex The column index
8442 * @param {Boolean} hidden true if hidden, false otherwise
8444 "hiddenchange": true,
8446 * @event columnmoved
8447 * Fires when a column is moved.
8448 * @param {ColumnModel} this
8449 * @param {Number} oldIndex
8450 * @param {Number} newIndex
8452 "columnmoved" : true,
8454 * @event columlockchange
8455 * Fires when a column's locked state is changed
8456 * @param {ColumnModel} this
8457 * @param {Number} colIndex
8458 * @param {Boolean} locked true if locked
8460 "columnlockchange" : true
8462 Roo.grid.ColumnModel.superclass.constructor.call(this);
8464 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8466 * @cfg {String} header [required] The header text to display in the Grid view.
8469 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8472 * @cfg {String} smHeader Header at Bootsrap Small width
8475 * @cfg {String} mdHeader Header at Bootsrap Medium width
8478 * @cfg {String} lgHeader Header at Bootsrap Large width
8481 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8484 * @cfg {String} dataIndex The name of the field in the grid's {@link Roo.data.Store}'s
8485 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8486 * specified, the column's index is used as an index into the Record's data Array.
8489 * @cfg {Number} width The initial width in pixels of the column. Using this
8490 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8493 * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
8494 * Defaults to the value of the {@link #defaultSortable} property.
8495 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8498 * @cfg {Boolean} locked True to lock the column in place while scrolling the Grid. Defaults to false.
8501 * @cfg {Boolean} fixed True if the column width cannot be changed. Defaults to false.
8504 * @cfg {Boolean} resizable False to disable column resizing. Defaults to true.
8507 * @cfg {Boolean} hidden True to hide the column. Defaults to false.
8510 * @cfg {Function} renderer A function used to generate HTML markup for a cell
8511 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8512 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8513 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8516 * @cfg {Roo.grid.GridEditor} editor For grid editors - returns the grid editor
8519 * @cfg {String} align (left|right) Set the CSS text-align property of the column. Defaults to undefined (left).
8522 * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined (middle)
8525 * @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)
8528 * @cfg {String} tooltip mouse over tooltip text
8531 * @cfg {Number} xs can be '0' for hidden at this size (number less than 12)
8534 * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
8537 * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
8540 * @cfg {Number} lg can be '0' for hidden at this size (number less than 12)
8543 * @cfg {Number} xl can be '0' for hidden at this size (number less than 12)
8546 * Returns the id of the column at the specified index.
8547 * @param {Number} index The column index
8548 * @return {String} the id
8550 getColumnId : function(index){
8551 return this.config[index].id;
8555 * Returns the column for a specified id.
8556 * @param {String} id The column id
8557 * @return {Object} the column
8559 getColumnById : function(id){
8560 return this.lookup[id];
8565 * Returns the column Object for a specified dataIndex.
8566 * @param {String} dataIndex The column dataIndex
8567 * @return {Object|Boolean} the column or false if not found
8569 getColumnByDataIndex: function(dataIndex){
8570 var index = this.findColumnIndex(dataIndex);
8571 return index > -1 ? this.config[index] : false;
8575 * Returns the index for a specified column id.
8576 * @param {String} id The column id
8577 * @return {Number} the index, or -1 if not found
8579 getIndexById : function(id){
8580 for(var i = 0, len = this.config.length; i < len; i++){
8581 if(this.config[i].id == id){
8589 * Returns the index for a specified column dataIndex.
8590 * @param {String} dataIndex The column dataIndex
8591 * @return {Number} the index, or -1 if not found
8594 findColumnIndex : function(dataIndex){
8595 for(var i = 0, len = this.config.length; i < len; i++){
8596 if(this.config[i].dataIndex == dataIndex){
8604 moveColumn : function(oldIndex, newIndex){
8605 var c = this.config[oldIndex];
8606 this.config.splice(oldIndex, 1);
8607 this.config.splice(newIndex, 0, c);
8608 this.dataMap = null;
8609 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8612 isLocked : function(colIndex){
8613 return this.config[colIndex].locked === true;
8616 setLocked : function(colIndex, value, suppressEvent){
8617 if(this.isLocked(colIndex) == value){
8620 this.config[colIndex].locked = value;
8622 this.fireEvent("columnlockchange", this, colIndex, value);
8626 getTotalLockedWidth : function(){
8628 for(var i = 0; i < this.config.length; i++){
8629 if(this.isLocked(i) && !this.isHidden(i)){
8630 this.totalWidth += this.getColumnWidth(i);
8636 getLockedCount : function(){
8637 for(var i = 0, len = this.config.length; i < len; i++){
8638 if(!this.isLocked(i)){
8643 return this.config.length;
8647 * Returns the number of columns.
8650 getColumnCount : function(visibleOnly){
8651 if(visibleOnly === true){
8653 for(var i = 0, len = this.config.length; i < len; i++){
8654 if(!this.isHidden(i)){
8660 return this.config.length;
8664 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8665 * @param {Function} fn
8666 * @param {Object} scope (optional)
8667 * @return {Array} result
8669 getColumnsBy : function(fn, scope){
8671 for(var i = 0, len = this.config.length; i < len; i++){
8672 var c = this.config[i];
8673 if(fn.call(scope||this, c, i) === true){
8681 * Returns true if the specified column is sortable.
8682 * @param {Number} col The column index
8685 isSortable : function(col){
8686 if(typeof this.config[col].sortable == "undefined"){
8687 return this.defaultSortable;
8689 return this.config[col].sortable;
8693 * Returns the rendering (formatting) function defined for the column.
8694 * @param {Number} col The column index.
8695 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8697 getRenderer : function(col){
8698 if(!this.config[col].renderer){
8699 return Roo.grid.ColumnModel.defaultRenderer;
8701 return this.config[col].renderer;
8705 * Sets the rendering (formatting) function for a column.
8706 * @param {Number} col The column index
8707 * @param {Function} fn The function to use to process the cell's raw data
8708 * to return HTML markup for the grid view. The render function is called with
8709 * the following parameters:<ul>
8710 * <li>Data value.</li>
8711 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8712 * <li>css A CSS style string to apply to the table cell.</li>
8713 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8714 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8715 * <li>Row index</li>
8716 * <li>Column index</li>
8717 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8719 setRenderer : function(col, fn){
8720 this.config[col].renderer = fn;
8724 * Returns the width for the specified column.
8725 * @param {Number} col The column index
8726 * @param (optional) {String} gridSize bootstrap width size.
8729 getColumnWidth : function(col, gridSize)
8731 var cfg = this.config[col];
8733 if (typeof(gridSize) == 'undefined') {
8734 return cfg.width * 1 || this.defaultWidth;
8736 if (gridSize === false) { // if we set it..
8737 return cfg.width || false;
8739 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8741 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8742 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8745 return cfg[ sizes[i] ];
8752 * Sets the width for a column.
8753 * @param {Number} col The column index
8754 * @param {Number} width The new width
8756 setColumnWidth : function(col, width, suppressEvent){
8757 this.config[col].width = width;
8758 this.totalWidth = null;
8760 this.fireEvent("widthchange", this, col, width);
8765 * Returns the total width of all columns.
8766 * @param {Boolean} includeHidden True to include hidden column widths
8769 getTotalWidth : function(includeHidden){
8770 if(!this.totalWidth){
8771 this.totalWidth = 0;
8772 for(var i = 0, len = this.config.length; i < len; i++){
8773 if(includeHidden || !this.isHidden(i)){
8774 this.totalWidth += this.getColumnWidth(i);
8778 return this.totalWidth;
8782 * Returns the header for the specified column.
8783 * @param {Number} col The column index
8786 getColumnHeader : function(col){
8787 return this.config[col].header;
8791 * Sets the header for a column.
8792 * @param {Number} col The column index
8793 * @param {String} header The new header
8795 setColumnHeader : function(col, header){
8796 this.config[col].header = header;
8797 this.fireEvent("headerchange", this, col, header);
8801 * Returns the tooltip for the specified column.
8802 * @param {Number} col The column index
8805 getColumnTooltip : function(col){
8806 return this.config[col].tooltip;
8809 * Sets the tooltip for a column.
8810 * @param {Number} col The column index
8811 * @param {String} tooltip The new tooltip
8813 setColumnTooltip : function(col, tooltip){
8814 this.config[col].tooltip = tooltip;
8818 * Returns the dataIndex for the specified column.
8819 * @param {Number} col The column index
8822 getDataIndex : function(col){
8823 return this.config[col].dataIndex;
8827 * Sets the dataIndex for a column.
8828 * @param {Number} col The column index
8829 * @param {Number} dataIndex The new dataIndex
8831 setDataIndex : function(col, dataIndex){
8832 this.config[col].dataIndex = dataIndex;
8838 * Returns true if the cell is editable.
8839 * @param {Number} colIndex The column index
8840 * @param {Number} rowIndex The row index - this is nto actually used..?
8843 isCellEditable : function(colIndex, rowIndex){
8844 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8848 * Returns the editor defined for the cell/column.
8849 * return false or null to disable editing.
8850 * @param {Number} colIndex The column index
8851 * @param {Number} rowIndex The row index
8854 getCellEditor : function(colIndex, rowIndex){
8855 return this.config[colIndex].editor;
8859 * Sets if a column is editable.
8860 * @param {Number} col The column index
8861 * @param {Boolean} editable True if the column is editable
8863 setEditable : function(col, editable){
8864 this.config[col].editable = editable;
8869 * Returns true if the column is hidden.
8870 * @param {Number} colIndex The column index
8873 isHidden : function(colIndex){
8874 return this.config[colIndex].hidden;
8879 * Returns true if the column width cannot be changed
8881 isFixed : function(colIndex){
8882 return this.config[colIndex].fixed;
8886 * Returns true if the column can be resized
8889 isResizable : function(colIndex){
8890 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8893 * Sets if a column is hidden.
8894 * @param {Number} colIndex The column index
8895 * @param {Boolean} hidden True if the column is hidden
8897 setHidden : function(colIndex, hidden){
8898 this.config[colIndex].hidden = hidden;
8899 this.totalWidth = null;
8900 this.fireEvent("hiddenchange", this, colIndex, hidden);
8904 * Sets the editor for a column.
8905 * @param {Number} col The column index
8906 * @param {Object} editor The editor object
8908 setEditor : function(col, editor){
8909 this.config[col].editor = editor;
8912 * Add a column (experimental...) - defaults to adding to the end..
8913 * @param {Object} config
8915 addColumn : function(c)
8918 var i = this.config.length;
8921 if(typeof c.dataIndex == "undefined"){
8924 if(typeof c.renderer == "string"){
8925 c.renderer = Roo.util.Format[c.renderer];
8927 if(typeof c.id == "undefined"){
8930 if(c.editor && c.editor.xtype){
8931 c.editor = Roo.factory(c.editor, Roo.grid);
8933 if(c.editor && c.editor.isFormField){
8934 c.editor = new Roo.grid.GridEditor(c.editor);
8936 this.lookup[c.id] = c;
8941 Roo.grid.ColumnModel.defaultRenderer = function(value)
8943 if(typeof value == "object") {
8946 if(typeof value == "string" && value.length < 1){
8950 return String.format("{0}", value);
8953 // Alias for backwards compatibility
8954 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8957 * Ext JS Library 1.1.1
8958 * Copyright(c) 2006-2007, Ext JS, LLC.
8960 * Originally Released Under LGPL - original licence link has changed is not relivant.
8963 * <script type="text/javascript">
8967 * @class Roo.LoadMask
8968 * A simple utility class for generically masking elements while loading data. If the element being masked has
8969 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8970 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8971 * element's UpdateManager load indicator and will be destroyed after the initial load.
8973 * Create a new LoadMask
8974 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8975 * @param {Object} config The config object
8977 Roo.LoadMask = function(el, config){
8978 this.el = Roo.get(el);
8979 Roo.apply(this, config);
8981 this.store.on('beforeload', this.onBeforeLoad, this);
8982 this.store.on('load', this.onLoad, this);
8983 this.store.on('loadexception', this.onLoadException, this);
8984 this.removeMask = false;
8986 var um = this.el.getUpdateManager();
8987 um.showLoadIndicator = false; // disable the default indicator
8988 um.on('beforeupdate', this.onBeforeLoad, this);
8989 um.on('update', this.onLoad, this);
8990 um.on('failure', this.onLoad, this);
8991 this.removeMask = true;
8995 Roo.LoadMask.prototype = {
8997 * @cfg {Boolean} removeMask
8998 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8999 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
9004 * The text to display in a centered loading message box (defaults to 'Loading...')
9008 * @cfg {String} msgCls
9009 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9011 msgCls : 'x-mask-loading',
9014 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9020 * Disables the mask to prevent it from being displayed
9022 disable : function(){
9023 this.disabled = true;
9027 * Enables the mask so that it can be displayed
9029 enable : function(){
9030 this.disabled = false;
9033 onLoadException : function()
9037 if (typeof(arguments[3]) != 'undefined') {
9038 Roo.MessageBox.alert("Error loading",arguments[3]);
9042 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9043 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9050 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9055 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9059 onBeforeLoad : function(){
9061 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9066 destroy : function(){
9068 this.store.un('beforeload', this.onBeforeLoad, this);
9069 this.store.un('load', this.onLoad, this);
9070 this.store.un('loadexception', this.onLoadException, this);
9072 var um = this.el.getUpdateManager();
9073 um.un('beforeupdate', this.onBeforeLoad, this);
9074 um.un('update', this.onLoad, this);
9075 um.un('failure', this.onLoad, this);
9079 * @class Roo.bootstrap.Table
9081 * @extends Roo.bootstrap.Component
9082 * @children Roo.bootstrap.TableBody
9083 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
9084 * Similar to Roo.grid.Grid
9086 var table = Roo.factory({
9088 xns : Roo.bootstrap,
9089 autoSizeColumns: true,
9096 sortInfo : { direction : 'ASC', field: 'name' },
9098 xtype : 'HttpProxy',
9101 url : 'https://example.com/some.data.url.json'
9104 xtype : 'JsonReader',
9106 fields : [ 'id', 'name', whatever' ],
9113 xtype : 'ColumnModel',
9117 dataIndex : 'is_in_group',
9120 renderer : function(v, x , r) {
9122 return String.format("{0}", v)
9128 xtype : 'RowSelectionModel',
9129 xns : Roo.bootstrap.Table
9130 // you can add listeners to catch selection change here....
9136 grid.render(Roo.get("some-div"));
9139 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
9144 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9145 * @cfg {Roo.data.Store} store The data store to use
9146 * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9148 * @cfg {String} cls table class
9151 * @cfg {string} empty_results Text to display for no results
9152 * @cfg {boolean} striped Should the rows be alternative striped
9153 * @cfg {boolean} bordered Add borders to the table
9154 * @cfg {boolean} hover Add hover highlighting
9155 * @cfg {boolean} condensed Format condensed
9156 * @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,
9157 * also adds table-responsive (see bootstrap docs for details)
9158 * @cfg {Boolean} loadMask (true|false) default false
9159 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9160 * @cfg {Boolean} headerShow (true|false) generate thead, default true
9161 * @cfg {Boolean} rowSelection (true|false) default false
9162 * @cfg {Boolean} cellSelection (true|false) default false
9163 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9164 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
9165 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
9166 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
9167 * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9168 * @cfg {Boolean} disableAutoSize disable auto size
9171 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
9174 * Create a new Table
9175 * @param {Object} config The config object
9178 Roo.bootstrap.Table = function(config)
9180 Roo.bootstrap.Table.superclass.constructor.call(this, config);
9183 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9184 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9185 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9186 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9188 this.view = this; // compat with grid.
9190 this.sm = this.sm || {xtype: 'RowSelectionModel'};
9192 this.sm.grid = this;
9193 this.selModel = Roo.factory(this.sm, Roo.grid);
9194 this.sm = this.selModel;
9195 this.sm.xmodule = this.xmodule || false;
9198 if (this.cm && typeof(this.cm.config) == 'undefined') {
9199 this.colModel = new Roo.grid.ColumnModel(this.cm);
9200 this.cm = this.colModel;
9201 this.cm.xmodule = this.xmodule || false;
9204 this.store= Roo.factory(this.store, Roo.data);
9205 this.ds = this.store;
9206 this.ds.xmodule = this.xmodule || false;
9209 if (this.footer && this.store) {
9210 this.footer.dataSource = this.ds;
9211 this.footer = Roo.factory(this.footer);
9218 * Fires when a cell is clicked
9219 * @param {Roo.bootstrap.Table} this
9220 * @param {Roo.Element} el
9221 * @param {Number} rowIndex
9222 * @param {Number} columnIndex
9223 * @param {Roo.EventObject} e
9227 * @event celldblclick
9228 * Fires when a cell is double clicked
9229 * @param {Roo.bootstrap.Table} this
9230 * @param {Roo.Element} el
9231 * @param {Number} rowIndex
9232 * @param {Number} columnIndex
9233 * @param {Roo.EventObject} e
9235 "celldblclick" : true,
9238 * Fires when a row is clicked
9239 * @param {Roo.bootstrap.Table} this
9240 * @param {Roo.Element} el
9241 * @param {Number} rowIndex
9242 * @param {Roo.EventObject} e
9246 * @event rowdblclick
9247 * Fires when a row is double clicked
9248 * @param {Roo.bootstrap.Table} this
9249 * @param {Roo.Element} el
9250 * @param {Number} rowIndex
9251 * @param {Roo.EventObject} e
9253 "rowdblclick" : true,
9256 * Fires when a mouseover occur
9257 * @param {Roo.bootstrap.Table} this
9258 * @param {Roo.Element} el
9259 * @param {Number} rowIndex
9260 * @param {Number} columnIndex
9261 * @param {Roo.EventObject} e
9266 * Fires when a mouseout occur
9267 * @param {Roo.bootstrap.Table} this
9268 * @param {Roo.Element} el
9269 * @param {Number} rowIndex
9270 * @param {Number} columnIndex
9271 * @param {Roo.EventObject} e
9276 * Fires when a row is rendered, so you can change add a style to it.
9277 * @param {Roo.bootstrap.Table} this
9278 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
9282 * @event rowsrendered
9283 * Fires when all the rows have been rendered
9284 * @param {Roo.bootstrap.Table} this
9286 'rowsrendered' : true,
9288 * @event contextmenu
9289 * The raw contextmenu event for the entire grid.
9290 * @param {Roo.EventObject} e
9292 "contextmenu" : true,
9294 * @event rowcontextmenu
9295 * Fires when a row is right clicked
9296 * @param {Roo.bootstrap.Table} this
9297 * @param {Number} rowIndex
9298 * @param {Roo.EventObject} e
9300 "rowcontextmenu" : true,
9302 * @event cellcontextmenu
9303 * Fires when a cell is right clicked
9304 * @param {Roo.bootstrap.Table} this
9305 * @param {Number} rowIndex
9306 * @param {Number} cellIndex
9307 * @param {Roo.EventObject} e
9309 "cellcontextmenu" : true,
9311 * @event headercontextmenu
9312 * Fires when a header is right clicked
9313 * @param {Roo.bootstrap.Table} this
9314 * @param {Number} columnIndex
9315 * @param {Roo.EventObject} e
9317 "headercontextmenu" : true,
9320 * The raw mousedown event for the entire grid.
9321 * @param {Roo.EventObject} e
9328 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
9345 enableColumnResize: true,
9346 disableAutoSize: false,
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()
9508 if(this.disableAutoSize) {
9509 Roo.log("DISABLEINITCSS");
9513 var cm = this.cm, styles = [];
9514 this.CSS.removeStyleSheet(this.id + '-cssrules');
9515 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9516 // we can honour xs/sm/md/xl as widths...
9517 // we first have to decide what widht we are currently at...
9518 var sz = Roo.getGridSize();
9522 var cols = []; // visable cols.
9524 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9525 var w = cm.getColumnWidth(i, false);
9527 cols.push( { rel : false, abs : 0 });
9531 cols.push( { rel : false, abs : w });
9533 last = i; // not really..
9536 var w = cm.getColumnWidth(i, sz);
9541 cols.push( { rel : w, abs : false });
9544 var avail = this.bodyEl.dom.clientWidth - total_abs;
9546 var unitWidth = Math.floor(avail / total);
9547 var rem = avail - (unitWidth * total);
9549 var hidden, width, pos = 0 , splithide , left;
9550 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9552 hidden = 'display:none;';
9554 width = 'width:0px;';
9556 if(!cm.isHidden(i)){
9560 // we can honour xs/sm/md/xl ?
9561 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9563 hidden = 'display:none;';
9565 // width should return a small number...
9567 w+=rem; // add the remaining with..
9570 left = "left:" + (pos -4) + "px;";
9571 width = "width:" + w+ "px;";
9574 if (this.responsive) {
9577 hidden = cm.isHidden(i) ? 'display:none;' : '';
9578 splithide = 'display: none;';
9581 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9584 splithide = 'display:none;';
9587 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9588 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9589 // this is the popover version..
9590 '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9595 //Roo.log(styles.join(''));
9596 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9602 onContextMenu : function(e, t)
9604 this.processEvent("contextmenu", e);
9607 processEvent : function(name, e)
9609 if (name != 'touchstart' ) {
9610 this.fireEvent(name, e);
9613 var t = e.getTarget();
9615 var cell = Roo.get(t);
9621 if(cell.findParent('tfoot', false, true)){
9625 if(cell.findParent('thead', false, true)){
9627 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9628 cell = Roo.get(t).findParent('th', false, true);
9630 Roo.log("failed to find th in thead?");
9631 Roo.log(e.getTarget());
9636 var cellIndex = cell.dom.cellIndex;
9638 var ename = name == 'touchstart' ? 'click' : name;
9639 this.fireEvent("header" + ename, this, cellIndex, e);
9644 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9645 cell = Roo.get(t).findParent('td', false, true);
9647 Roo.log("failed to find th in tbody?");
9648 Roo.log(e.getTarget());
9653 var row = cell.findParent('tr', false, true);
9654 var cellIndex = cell.dom.cellIndex;
9655 var rowIndex = row.dom.rowIndex - 1;
9659 this.fireEvent("row" + name, this, rowIndex, e);
9663 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9669 onMouseover : function(e, el)
9671 var cell = Roo.get(el);
9677 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9678 cell = cell.findParent('td', false, true);
9681 var row = cell.findParent('tr', false, true);
9682 var cellIndex = cell.dom.cellIndex;
9683 var rowIndex = row.dom.rowIndex - 1; // start from 0
9685 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9689 onMouseout : function(e, el)
9691 var cell = Roo.get(el);
9697 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9698 cell = cell.findParent('td', false, true);
9701 var row = cell.findParent('tr', false, true);
9702 var cellIndex = cell.dom.cellIndex;
9703 var rowIndex = row.dom.rowIndex - 1; // start from 0
9705 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9709 onClick : function(e, el)
9711 var cell = Roo.get(el);
9713 if(!cell || (!this.cellSelection && !this.rowSelection)){
9717 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9718 cell = cell.findParent('td', false, true);
9721 if(!cell || typeof(cell) == 'undefined'){
9725 var row = cell.findParent('tr', false, true);
9727 if(!row || typeof(row) == 'undefined'){
9731 var cellIndex = cell.dom.cellIndex;
9732 var rowIndex = this.getRowIndex(row);
9734 // why??? - should these not be based on SelectionModel?
9735 //if(this.cellSelection){
9736 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9739 //if(this.rowSelection){
9740 this.fireEvent('rowclick', this, row, rowIndex, e);
9745 onDblClick : function(e,el)
9747 var cell = Roo.get(el);
9749 if(!cell || (!this.cellSelection && !this.rowSelection)){
9753 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9754 cell = cell.findParent('td', false, true);
9757 if(!cell || typeof(cell) == 'undefined'){
9761 var row = cell.findParent('tr', false, true);
9763 if(!row || typeof(row) == 'undefined'){
9767 var cellIndex = cell.dom.cellIndex;
9768 var rowIndex = this.getRowIndex(row);
9770 if(this.cellSelection){
9771 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9774 if(this.rowSelection){
9775 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9778 findRowIndex : function(el)
9780 var cell = Roo.get(el);
9784 var row = cell.findParent('tr', false, true);
9786 if(!row || typeof(row) == 'undefined'){
9789 return this.getRowIndex(row);
9791 sort : function(e,el)
9793 var col = Roo.get(el);
9795 if(!col.hasClass('sortable')){
9799 var sort = col.attr('sort');
9802 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9806 this.store.sortInfo = {field : sort, direction : dir};
9809 Roo.log("calling footer first");
9810 this.footer.onClick('first');
9813 this.store.load({ params : { start : 0 } });
9817 renderHeader : function()
9825 this.totalWidth = 0;
9827 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9829 var config = cm.config[i];
9833 cls : 'x-hcol-' + i,
9836 html: cm.getColumnHeader(i)
9839 var tooltip = cm.getColumnTooltip(i);
9841 c.tooltip = tooltip;
9847 if(typeof(config.sortable) != 'undefined' && config.sortable){
9848 c.cls += ' sortable';
9849 c.html = '<i class="fa"></i>' + c.html;
9852 // could use BS4 hidden-..-down
9854 if(typeof(config.lgHeader) != 'undefined'){
9855 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9858 if(typeof(config.mdHeader) != 'undefined'){
9859 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9862 if(typeof(config.smHeader) != 'undefined'){
9863 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9866 if(typeof(config.xsHeader) != 'undefined'){
9867 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9874 if(typeof(config.tooltip) != 'undefined'){
9875 c.tooltip = config.tooltip;
9878 if(typeof(config.colspan) != 'undefined'){
9879 c.colspan = config.colspan;
9882 // hidden is handled by CSS now
9884 if(typeof(config.dataIndex) != 'undefined'){
9885 c.sort = config.dataIndex;
9890 if(typeof(config.align) != 'undefined' && config.align.length){
9891 c.style += ' text-align:' + config.align + ';';
9894 /* width is done in CSS
9895 *if(typeof(config.width) != 'undefined'){
9896 c.style += ' width:' + config.width + 'px;';
9897 this.totalWidth += config.width;
9899 this.totalWidth += 100; // assume minimum of 100 per column?
9903 if(typeof(config.cls) != 'undefined'){
9904 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9906 // this is the bit that doesnt reall work at all...
9908 if (this.responsive) {
9911 ['xs','sm','md','lg'].map(function(size){
9913 if(typeof(config[size]) == 'undefined'){
9917 if (!config[size]) { // 0 = hidden
9918 // BS 4 '0' is treated as hide that column and below.
9919 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9923 c.cls += ' col-' + size + '-' + config[size] + (
9924 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9932 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9943 renderBody : function()
9953 colspan : this.cm.getColumnCount()
9963 renderFooter : function()
9973 colspan : this.cm.getColumnCount()
9987 // Roo.log('ds onload');
9992 var ds = this.store;
9994 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9995 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9996 if (_this.store.sortInfo) {
9998 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9999 e.select('i', true).addClass(['fa-arrow-up']);
10002 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10003 e.select('i', true).addClass(['fa-arrow-down']);
10008 var tbody = this.bodyEl;
10010 if(ds.getCount() > 0){
10011 ds.data.each(function(d,rowIndex){
10012 var row = this.renderRow(cm, ds, rowIndex);
10014 tbody.createChild(row);
10018 if(row.cellObjects.length){
10019 Roo.each(row.cellObjects, function(r){
10020 _this.renderCellObject(r);
10025 } else if (this.empty_results.length) {
10026 this.el.mask(this.empty_results, 'no-spinner');
10029 var tfoot = this.el.select('tfoot', true).first();
10031 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10033 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10035 var total = this.ds.getTotalCount();
10037 if(this.footer.pageSize < total){
10038 this.mainFoot.show();
10042 Roo.each(this.el.select('tbody td', true).elements, function(e){
10043 e.on('mouseover', _this.onMouseover, _this);
10046 Roo.each(this.el.select('tbody td', true).elements, function(e){
10047 e.on('mouseout', _this.onMouseout, _this);
10049 this.fireEvent('rowsrendered', this);
10053 this.initCSS(); /// resize cols
10059 onUpdate : function(ds,record)
10061 this.refreshRow(record);
10065 onRemove : function(ds, record, index, isUpdate){
10066 if(isUpdate !== true){
10067 this.fireEvent("beforerowremoved", this, index, record);
10069 var bt = this.bodyEl.dom;
10071 var rows = this.el.select('tbody > tr', true).elements;
10073 if(typeof(rows[index]) != 'undefined'){
10074 bt.removeChild(rows[index].dom);
10077 // if(bt.rows[index]){
10078 // bt.removeChild(bt.rows[index]);
10081 if(isUpdate !== true){
10082 //this.stripeRows(index);
10083 //this.syncRowHeights(index, index);
10085 this.fireEvent("rowremoved", this, index, record);
10089 onAdd : function(ds, records, rowIndex)
10091 //Roo.log('on Add called');
10092 // - note this does not handle multiple adding very well..
10093 var bt = this.bodyEl.dom;
10094 for (var i =0 ; i < records.length;i++) {
10095 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10096 //Roo.log(records[i]);
10097 //Roo.log(this.store.getAt(rowIndex+i));
10098 this.insertRow(this.store, rowIndex + i, false);
10105 refreshRow : function(record){
10106 var ds = this.store, index;
10107 if(typeof record == 'number'){
10109 record = ds.getAt(index);
10111 index = ds.indexOf(record);
10113 return; // should not happen - but seems to
10116 this.insertRow(ds, index, true);
10118 this.onRemove(ds, record, index+1, true);
10120 //this.syncRowHeights(index, index);
10122 this.fireEvent("rowupdated", this, index, record);
10124 // private - called by RowSelection
10125 onRowSelect : function(rowIndex){
10126 var row = this.getRowDom(rowIndex);
10127 row.addClass(['bg-info','info']);
10129 // private - called by RowSelection
10130 onRowDeselect : function(rowIndex)
10132 if (rowIndex < 0) {
10135 var row = this.getRowDom(rowIndex);
10136 row.removeClass(['bg-info','info']);
10139 * Focuses the specified row.
10140 * @param {Number} row The row index
10142 focusRow : function(row)
10144 //Roo.log('GridView.focusRow');
10145 var x = this.bodyEl.dom.scrollLeft;
10146 this.focusCell(row, 0, false);
10147 this.bodyEl.dom.scrollLeft = x;
10151 * Focuses the specified cell.
10152 * @param {Number} row The row index
10153 * @param {Number} col The column index
10154 * @param {Boolean} hscroll false to disable horizontal scrolling
10156 focusCell : function(row, col, hscroll)
10158 //Roo.log('GridView.focusCell');
10159 var el = this.ensureVisible(row, col, hscroll);
10160 // not sure what focusEL achives = it's a <a> pos relative
10161 //this.focusEl.alignTo(el, "tl-tl");
10163 // this.focusEl.focus();
10165 // this.focusEl.focus.defer(1, this.focusEl);
10170 * Scrolls the specified cell into view
10171 * @param {Number} row The row index
10172 * @param {Number} col The column index
10173 * @param {Boolean} hscroll false to disable horizontal scrolling
10175 ensureVisible : function(row, col, hscroll)
10177 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10178 //return null; //disable for testing.
10179 if(typeof row != "number"){
10180 row = row.rowIndex;
10182 if(row < 0 && row >= this.ds.getCount()){
10185 col = (col !== undefined ? col : 0);
10187 while(cm.isHidden(col)){
10191 var el = this.getCellDom(row, col);
10195 var c = this.bodyEl.dom;
10197 var ctop = parseInt(el.offsetTop, 10);
10198 var cleft = parseInt(el.offsetLeft, 10);
10199 var cbot = ctop + el.offsetHeight;
10200 var cright = cleft + el.offsetWidth;
10202 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10203 var ch = 0; //?? header is not withing the area?
10204 var stop = parseInt(c.scrollTop, 10);
10205 var sleft = parseInt(c.scrollLeft, 10);
10206 var sbot = stop + ch;
10207 var sright = sleft + c.clientWidth;
10209 Roo.log('GridView.ensureVisible:' +
10211 ' c.clientHeight:' + c.clientHeight +
10212 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10220 c.scrollTop = ctop;
10221 //Roo.log("set scrolltop to ctop DISABLE?");
10222 }else if(cbot > sbot){
10223 //Roo.log("set scrolltop to cbot-ch");
10224 c.scrollTop = cbot-ch;
10227 if(hscroll !== false){
10229 c.scrollLeft = cleft;
10230 }else if(cright > sright){
10231 c.scrollLeft = cright-c.clientWidth;
10239 insertRow : function(dm, rowIndex, isUpdate){
10242 this.fireEvent("beforerowsinserted", this, rowIndex);
10244 //var s = this.getScrollState();
10245 var row = this.renderRow(this.cm, this.store, rowIndex);
10246 // insert before rowIndex..
10247 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10251 if(row.cellObjects.length){
10252 Roo.each(row.cellObjects, function(r){
10253 _this.renderCellObject(r);
10258 this.fireEvent("rowsinserted", this, rowIndex);
10259 //this.syncRowHeights(firstRow, lastRow);
10260 //this.stripeRows(firstRow);
10267 getRowDom : function(rowIndex)
10269 var rows = this.el.select('tbody > tr', true).elements;
10271 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10274 getCellDom : function(rowIndex, colIndex)
10276 var row = this.getRowDom(rowIndex);
10277 if (row === false) {
10280 var cols = row.select('td', true).elements;
10281 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10285 // returns the object tree for a tr..
10288 renderRow : function(cm, ds, rowIndex)
10290 var d = ds.getAt(rowIndex);
10294 cls : 'x-row-' + rowIndex,
10298 var cellObjects = [];
10300 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10301 var config = cm.config[i];
10303 var renderer = cm.getRenderer(i);
10307 if(typeof(renderer) !== 'undefined'){
10308 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10310 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10311 // and are rendered into the cells after the row is rendered - using the id for the element.
10313 if(typeof(value) === 'object'){
10323 rowIndex : rowIndex,
10328 this.fireEvent('rowclass', this, rowcfg);
10332 // this might end up displaying HTML?
10333 // this is too messy... - better to only do it on columsn you know are going to be too long
10334 //tooltip : (typeof(value) === 'object') ? '' : value,
10335 cls : rowcfg.rowClass + ' x-col-' + i,
10337 html: (typeof(value) === 'object') ? '' : value
10344 if(typeof(config.colspan) != 'undefined'){
10345 td.colspan = config.colspan;
10350 if(typeof(config.align) != 'undefined' && config.align.length){
10351 td.style += ' text-align:' + config.align + ';';
10353 if(typeof(config.valign) != 'undefined' && config.valign.length){
10354 td.style += ' vertical-align:' + config.valign + ';';
10357 if(typeof(config.width) != 'undefined'){
10358 td.style += ' width:' + config.width + 'px;';
10362 if(typeof(config.cursor) != 'undefined'){
10363 td.style += ' cursor:' + config.cursor + ';';
10366 if(typeof(config.cls) != 'undefined'){
10367 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10369 if (this.responsive) {
10370 ['xs','sm','md','lg'].map(function(size){
10372 if(typeof(config[size]) == 'undefined'){
10378 if (!config[size]) { // 0 = hidden
10379 // BS 4 '0' is treated as hide that column and below.
10380 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10384 td.cls += ' col-' + size + '-' + config[size] + (
10385 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
10395 row.cellObjects = cellObjects;
10403 onBeforeLoad : function()
10405 this.el.unmask(); // if needed.
10412 this.el.select('tbody', true).first().dom.innerHTML = '';
10415 * Show or hide a row.
10416 * @param {Number} rowIndex to show or hide
10417 * @param {Boolean} state hide
10419 setRowVisibility : function(rowIndex, state)
10421 var bt = this.bodyEl.dom;
10423 var rows = this.el.select('tbody > tr', true).elements;
10425 if(typeof(rows[rowIndex]) == 'undefined'){
10428 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10433 getSelectionModel : function(){
10434 if(!this.selModel){
10435 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10437 return this.selModel;
10440 * Render the Roo.bootstrap object from renderder
10442 renderCellObject : function(r)
10446 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10448 var t = r.cfg.render(r.container);
10451 Roo.each(r.cfg.cn, function(c){
10453 container: t.getChildContainer(),
10456 _this.renderCellObject(child);
10461 * get the Row Index from a dom element.
10462 * @param {Roo.Element} row The row to look for
10463 * @returns {Number} the row
10465 getRowIndex : function(row)
10469 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10480 * get the header TH element for columnIndex
10481 * @param {Number} columnIndex
10482 * @returns {Roo.Element}
10484 getHeaderIndex: function(colIndex)
10486 var cols = this.headEl.select('th', true).elements;
10487 return cols[colIndex];
10490 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10491 * @param {domElement} cell to look for
10492 * @returns {Number} the column
10494 getCellIndex : function(cell)
10496 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10498 return parseInt(id[1], 10);
10503 * Returns the grid's underlying element = used by panel.Grid
10504 * @return {Element} The element
10506 getGridEl : function(){
10510 * Forces a resize - used by panel.Grid
10511 * @return {Element} The element
10513 autoSize : function()
10515 if(this.disableAutoSize) {
10516 Roo.log("DISABLEAUTOSIZE");
10519 //var ctr = Roo.get(this.container.dom.parentElement);
10520 var ctr = Roo.get(this.el.dom);
10522 var thd = this.getGridEl().select('thead',true).first();
10523 var tbd = this.getGridEl().select('tbody', true).first();
10524 var tfd = this.getGridEl().select('tfoot', true).first();
10526 var cw = ctr.getWidth();
10527 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10531 tbd.setWidth(ctr.getWidth());
10532 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10533 // this needs fixing for various usage - currently only hydra job advers I think..
10535 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10537 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10540 cw = Math.max(cw, this.totalWidth);
10541 this.getGridEl().select('tbody tr',true).setWidth(cw);
10544 // resize 'expandable coloumn?
10546 return; // we doe not have a view in this design..
10549 onBodyScroll: function()
10551 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10553 this.headEl.setStyle({
10554 'position' : 'relative',
10555 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10561 var scrollHeight = this.bodyEl.dom.scrollHeight;
10563 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10565 var height = this.bodyEl.getHeight();
10567 if(scrollHeight - height == scrollTop) {
10569 var total = this.ds.getTotalCount();
10571 if(this.footer.cursor + this.footer.pageSize < total){
10573 this.footer.ds.load({
10575 start : this.footer.cursor + this.footer.pageSize,
10576 limit : this.footer.pageSize
10585 onColumnSplitterMoved : function(i, diff)
10587 this.userResized = true;
10589 var cm = this.colModel;
10591 var w = this.getHeaderIndex(i).getWidth() + diff;
10594 cm.setColumnWidth(i, w, true);
10596 //var cid = cm.getColumnId(i); << not used in this version?
10597 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10599 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10600 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10601 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10603 //this.updateSplitters();
10604 //this.layout(); << ??
10605 this.fireEvent("columnresize", i, w);
10607 onHeaderChange : function()
10609 var header = this.renderHeader();
10610 var table = this.el.select('table', true).first();
10612 this.headEl.remove();
10613 this.headEl = table.createChild(header, this.bodyEl, false);
10615 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10616 e.on('click', this.sort, this);
10619 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10620 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10625 onHiddenChange : function(colModel, colIndex, hidden)
10628 this.cm.setHidden()
10629 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10630 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10632 this.CSS.updateRule(thSelector, "display", "");
10633 this.CSS.updateRule(tdSelector, "display", "");
10636 this.CSS.updateRule(thSelector, "display", "none");
10637 this.CSS.updateRule(tdSelector, "display", "none");
10640 // onload calls initCSS()
10641 this.onHeaderChange();
10645 setColumnWidth: function(col_index, width)
10647 // width = "md-2 xs-2..."
10648 if(!this.colModel.config[col_index]) {
10652 var w = width.split(" ");
10654 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10656 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10659 for(var j = 0; j < w.length; j++) {
10665 var size_cls = w[j].split("-");
10667 if(!Number.isInteger(size_cls[1] * 1)) {
10671 if(!this.colModel.config[col_index][size_cls[0]]) {
10675 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10679 h_row[0].classList.replace(
10680 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10681 "col-"+size_cls[0]+"-"+size_cls[1]
10684 for(var i = 0; i < rows.length; i++) {
10686 var size_cls = w[j].split("-");
10688 if(!Number.isInteger(size_cls[1] * 1)) {
10692 if(!this.colModel.config[col_index][size_cls[0]]) {
10696 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10700 rows[i].classList.replace(
10701 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10702 "col-"+size_cls[0]+"-"+size_cls[1]
10706 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10711 // currently only used to find the split on drag..
10712 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10717 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10718 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10727 * @class Roo.bootstrap.TableCell
10728 * @extends Roo.bootstrap.Component
10729 * @children Roo.bootstrap.Component
10730 * @parent Roo.bootstrap.TableRow
10731 * Bootstrap TableCell class
10733 * @cfg {String} html cell contain text
10734 * @cfg {String} cls cell class
10735 * @cfg {String} tag cell tag (td|th) default td
10736 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10737 * @cfg {String} align Aligns the content in a cell
10738 * @cfg {String} axis Categorizes cells
10739 * @cfg {String} bgcolor Specifies the background color of a cell
10740 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10741 * @cfg {Number} colspan Specifies the number of columns a cell should span
10742 * @cfg {String} headers Specifies one or more header cells a cell is related to
10743 * @cfg {Number} height Sets the height of a cell
10744 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10745 * @cfg {Number} rowspan Sets the number of rows a cell should span
10746 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10747 * @cfg {String} valign Vertical aligns the content in a cell
10748 * @cfg {Number} width Specifies the width of a cell
10751 * Create a new TableCell
10752 * @param {Object} config The config object
10755 Roo.bootstrap.TableCell = function(config){
10756 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10759 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10779 getAutoCreate : function(){
10780 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10787 cfg.tag = this.tag;
10800 cfg.align=this.align
10805 if (this.bgcolor) {
10806 cfg.bgcolor=this.bgcolor
10808 if (this.charoff) {
10809 cfg.charoff=this.charoff
10811 if (this.colspan) {
10812 cfg.colspan=this.colspan
10814 if (this.headers) {
10815 cfg.headers=this.headers
10818 cfg.height=this.height
10821 cfg.nowrap=this.nowrap
10823 if (this.rowspan) {
10824 cfg.rowspan=this.rowspan
10827 cfg.scope=this.scope
10830 cfg.valign=this.valign
10833 cfg.width=this.width
10852 * @class Roo.bootstrap.TableRow
10853 * @extends Roo.bootstrap.Component
10854 * @children Roo.bootstrap.TableCell
10855 * @parent Roo.bootstrap.TableBody
10856 * Bootstrap TableRow class
10857 * @cfg {String} cls row class
10858 * @cfg {String} align Aligns the content in a table row
10859 * @cfg {String} bgcolor Specifies a background color for a table row
10860 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10861 * @cfg {String} valign Vertical aligns the content in a table row
10864 * Create a new TableRow
10865 * @param {Object} config The config object
10868 Roo.bootstrap.TableRow = function(config){
10869 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10872 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10880 getAutoCreate : function(){
10881 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10888 cfg.cls = this.cls;
10891 cfg.align = this.align;
10894 cfg.bgcolor = this.bgcolor;
10897 cfg.charoff = this.charoff;
10900 cfg.valign = this.valign;
10918 * @class Roo.bootstrap.TableBody
10919 * @extends Roo.bootstrap.Component
10920 * @children Roo.bootstrap.TableRow
10921 * @parent Roo.bootstrap.Table
10922 * Bootstrap TableBody class
10923 * @cfg {String} cls element class
10924 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10925 * @cfg {String} align Aligns the content inside the element
10926 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10927 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10930 * Create a new TableBody
10931 * @param {Object} config The config object
10934 Roo.bootstrap.TableBody = function(config){
10935 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10938 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10946 getAutoCreate : function(){
10947 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10957 cfg.tag = this.tag;
10961 cfg.align = this.align;
10964 cfg.charoff = this.charoff;
10967 cfg.valign = this.valign;
10974 // initEvents : function()
10977 // if(!this.store){
10981 // this.store = Roo.factory(this.store, Roo.data);
10982 // this.store.on('load', this.onLoad, this);
10984 // this.store.load();
10988 // onLoad: function ()
10990 // this.fireEvent('load', this);
11000 * Ext JS Library 1.1.1
11001 * Copyright(c) 2006-2007, Ext JS, LLC.
11003 * Originally Released Under LGPL - original licence link has changed is not relivant.
11006 * <script type="text/javascript">
11009 // as we use this in bootstrap.
11010 Roo.namespace('Roo.form');
11012 * @class Roo.form.Action
11013 * Internal Class used to handle form actions
11015 * @param {Roo.form.BasicForm} el The form element or its id
11016 * @param {Object} config Configuration options
11021 // define the action interface
11022 Roo.form.Action = function(form, options){
11024 this.options = options || {};
11027 * Client Validation Failed
11030 Roo.form.Action.CLIENT_INVALID = 'client';
11032 * Server Validation Failed
11035 Roo.form.Action.SERVER_INVALID = 'server';
11037 * Connect to Server Failed
11040 Roo.form.Action.CONNECT_FAILURE = 'connect';
11042 * Reading Data from Server Failed
11045 Roo.form.Action.LOAD_FAILURE = 'load';
11047 Roo.form.Action.prototype = {
11049 failureType : undefined,
11050 response : undefined,
11051 result : undefined,
11053 // interface method
11054 run : function(options){
11058 // interface method
11059 success : function(response){
11063 // interface method
11064 handleResponse : function(response){
11068 // default connection failure
11069 failure : function(response){
11071 this.response = response;
11072 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11073 this.form.afterAction(this, false);
11076 processResponse : function(response){
11077 this.response = response;
11078 if(!response.responseText){
11081 this.result = this.handleResponse(response);
11082 return this.result;
11085 // utility functions used internally
11086 getUrl : function(appendParams){
11087 var url = this.options.url || this.form.url || this.form.el.dom.action;
11089 var p = this.getParams();
11091 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11097 getMethod : function(){
11098 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11101 getParams : function(){
11102 var bp = this.form.baseParams;
11103 var p = this.options.params;
11105 if(typeof p == "object"){
11106 p = Roo.urlEncode(Roo.applyIf(p, bp));
11107 }else if(typeof p == 'string' && bp){
11108 p += '&' + Roo.urlEncode(bp);
11111 p = Roo.urlEncode(bp);
11116 createCallback : function(){
11118 success: this.success,
11119 failure: this.failure,
11121 timeout: (this.form.timeout*1000),
11122 upload: this.form.fileUpload ? this.success : undefined
11127 Roo.form.Action.Submit = function(form, options){
11128 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11131 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11134 haveProgress : false,
11135 uploadComplete : false,
11137 // uploadProgress indicator.
11138 uploadProgress : function()
11140 if (!this.form.progressUrl) {
11144 if (!this.haveProgress) {
11145 Roo.MessageBox.progress("Uploading", "Uploading");
11147 if (this.uploadComplete) {
11148 Roo.MessageBox.hide();
11152 this.haveProgress = true;
11154 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11156 var c = new Roo.data.Connection();
11158 url : this.form.progressUrl,
11163 success : function(req){
11164 //console.log(data);
11168 rdata = Roo.decode(req.responseText)
11170 Roo.log("Invalid data from server..");
11174 if (!rdata || !rdata.success) {
11176 Roo.MessageBox.alert(Roo.encode(rdata));
11179 var data = rdata.data;
11181 if (this.uploadComplete) {
11182 Roo.MessageBox.hide();
11187 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11188 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11191 this.uploadProgress.defer(2000,this);
11194 failure: function(data) {
11195 Roo.log('progress url failed ');
11206 // run get Values on the form, so it syncs any secondary forms.
11207 this.form.getValues();
11209 var o = this.options;
11210 var method = this.getMethod();
11211 var isPost = method == 'POST';
11212 if(o.clientValidation === false || this.form.isValid()){
11214 if (this.form.progressUrl) {
11215 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11216 (new Date() * 1) + '' + Math.random());
11221 Roo.Ajax.request(Roo.apply(this.createCallback(), {
11222 form:this.form.el.dom,
11223 url:this.getUrl(!isPost),
11225 params:isPost ? this.getParams() : null,
11226 isUpload: this.form.fileUpload,
11227 formData : this.form.formData
11230 this.uploadProgress();
11232 }else if (o.clientValidation !== false){ // client validation failed
11233 this.failureType = Roo.form.Action.CLIENT_INVALID;
11234 this.form.afterAction(this, false);
11238 success : function(response)
11240 this.uploadComplete= true;
11241 if (this.haveProgress) {
11242 Roo.MessageBox.hide();
11246 var result = this.processResponse(response);
11247 if(result === true || result.success){
11248 this.form.afterAction(this, true);
11252 this.form.markInvalid(result.errors);
11253 this.failureType = Roo.form.Action.SERVER_INVALID;
11255 this.form.afterAction(this, false);
11257 failure : function(response)
11259 this.uploadComplete= true;
11260 if (this.haveProgress) {
11261 Roo.MessageBox.hide();
11264 this.response = response;
11265 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11266 this.form.afterAction(this, false);
11269 handleResponse : function(response){
11270 if(this.form.errorReader){
11271 var rs = this.form.errorReader.read(response);
11274 for(var i = 0, len = rs.records.length; i < len; i++) {
11275 var r = rs.records[i];
11276 errors[i] = r.data;
11279 if(errors.length < 1){
11283 success : rs.success,
11289 ret = Roo.decode(response.responseText);
11293 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11303 Roo.form.Action.Load = function(form, options){
11304 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11305 this.reader = this.form.reader;
11308 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11313 Roo.Ajax.request(Roo.apply(
11314 this.createCallback(), {
11315 method:this.getMethod(),
11316 url:this.getUrl(false),
11317 params:this.getParams()
11321 success : function(response){
11323 var result = this.processResponse(response);
11324 if(result === true || !result.success || !result.data){
11325 this.failureType = Roo.form.Action.LOAD_FAILURE;
11326 this.form.afterAction(this, false);
11329 this.form.clearInvalid();
11330 this.form.setValues(result.data);
11331 this.form.afterAction(this, true);
11334 handleResponse : function(response){
11335 if(this.form.reader){
11336 var rs = this.form.reader.read(response);
11337 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11339 success : rs.success,
11343 return Roo.decode(response.responseText);
11347 Roo.form.Action.ACTION_TYPES = {
11348 'load' : Roo.form.Action.Load,
11349 'submit' : Roo.form.Action.Submit
11358 * @class Roo.bootstrap.form.Form
11359 * @extends Roo.bootstrap.Component
11360 * @children Roo.bootstrap.Component
11361 * Bootstrap Form class
11362 * @cfg {String} method GET | POST (default POST)
11363 * @cfg {String} labelAlign top | left (default top)
11364 * @cfg {String} align left | right - for navbars
11365 * @cfg {Boolean} loadMask load mask when submit (default true)
11369 * Create a new Form
11370 * @param {Object} config The config object
11374 Roo.bootstrap.form.Form = function(config){
11376 Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11378 Roo.bootstrap.form.Form.popover.apply();
11382 * @event clientvalidation
11383 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11384 * @param {Form} this
11385 * @param {Boolean} valid true if the form has passed client-side validation
11387 clientvalidation: true,
11389 * @event beforeaction
11390 * Fires before any action is performed. Return false to cancel the action.
11391 * @param {Form} this
11392 * @param {Action} action The action to be performed
11394 beforeaction: true,
11396 * @event actionfailed
11397 * Fires when an action fails.
11398 * @param {Form} this
11399 * @param {Action} action The action that failed
11401 actionfailed : true,
11403 * @event actioncomplete
11404 * Fires when an action is completed.
11405 * @param {Form} this
11406 * @param {Action} action The action that completed
11408 actioncomplete : true
11412 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component, {
11415 * @cfg {String} method
11416 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11420 * @cfg {String} url
11421 * The URL to use for form actions if one isn't supplied in the action options.
11424 * @cfg {Boolean} fileUpload
11425 * Set to true if this form is a file upload.
11429 * @cfg {Object} baseParams
11430 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11434 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11438 * @cfg {Sting} align (left|right) for navbar forms
11443 activeAction : null,
11446 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11447 * element by passing it or its id or mask the form itself by passing in true.
11450 waitMsgTarget : false,
11455 * @cfg {Boolean} errorMask (true|false) default false
11460 * @cfg {Number} maskOffset Default 100
11465 * @cfg {Boolean} maskBody
11469 getAutoCreate : function(){
11473 method : this.method || 'POST',
11474 id : this.id || Roo.id(),
11477 if (this.parent().xtype.match(/^Nav/)) {
11478 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11482 if (this.labelAlign == 'left' ) {
11483 cfg.cls += ' form-horizontal';
11489 initEvents : function()
11491 this.el.on('submit', this.onSubmit, this);
11492 // this was added as random key presses on the form where triggering form submit.
11493 this.el.on('keypress', function(e) {
11494 if (e.getCharCode() != 13) {
11497 // we might need to allow it for textareas.. and some other items.
11498 // check e.getTarget().
11500 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11504 Roo.log("keypress blocked");
11506 e.preventDefault();
11512 onSubmit : function(e){
11517 * Returns true if client-side validation on the form is successful.
11520 isValid : function(){
11521 var items = this.getItems();
11523 var target = false;
11525 items.each(function(f){
11531 Roo.log('invalid field: ' + f.name);
11535 if(!target && f.el.isVisible(true)){
11541 if(this.errorMask && !valid){
11542 Roo.bootstrap.form.Form.popover.mask(this, target);
11549 * Returns true if any fields in this form have changed since their original load.
11552 isDirty : function(){
11554 var items = this.getItems();
11555 items.each(function(f){
11565 * Performs a predefined action (submit or load) or custom actions you define on this form.
11566 * @param {String} actionName The name of the action type
11567 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11568 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11569 * accept other config options):
11571 Property Type Description
11572 ---------------- --------------- ----------------------------------------------------------------------------------
11573 url String The url for the action (defaults to the form's url)
11574 method String The form method to use (defaults to the form's method, or POST if not defined)
11575 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11576 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11577 validate the form on the client (defaults to false)
11579 * @return {BasicForm} this
11581 doAction : function(action, options){
11582 if(typeof action == 'string'){
11583 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11585 if(this.fireEvent('beforeaction', this, action) !== false){
11586 this.beforeAction(action);
11587 action.run.defer(100, action);
11593 beforeAction : function(action){
11594 var o = action.options;
11599 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11601 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11604 // not really supported yet.. ??
11606 //if(this.waitMsgTarget === true){
11607 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11608 //}else if(this.waitMsgTarget){
11609 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11610 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11612 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11618 afterAction : function(action, success){
11619 this.activeAction = null;
11620 var o = action.options;
11625 Roo.get(document.body).unmask();
11631 //if(this.waitMsgTarget === true){
11632 // this.el.unmask();
11633 //}else if(this.waitMsgTarget){
11634 // this.waitMsgTarget.unmask();
11636 // Roo.MessageBox.updateProgress(1);
11637 // Roo.MessageBox.hide();
11644 Roo.callback(o.success, o.scope, [this, action]);
11645 this.fireEvent('actioncomplete', this, action);
11649 // failure condition..
11650 // we have a scenario where updates need confirming.
11651 // eg. if a locking scenario exists..
11652 // we look for { errors : { needs_confirm : true }} in the response.
11654 (typeof(action.result) != 'undefined') &&
11655 (typeof(action.result.errors) != 'undefined') &&
11656 (typeof(action.result.errors.needs_confirm) != 'undefined')
11659 Roo.log("not supported yet");
11662 Roo.MessageBox.confirm(
11663 "Change requires confirmation",
11664 action.result.errorMsg,
11669 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11679 Roo.callback(o.failure, o.scope, [this, action]);
11680 // show an error message if no failed handler is set..
11681 if (!this.hasListener('actionfailed')) {
11682 Roo.log("need to add dialog support");
11684 Roo.MessageBox.alert("Error",
11685 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11686 action.result.errorMsg :
11687 "Saving Failed, please check your entries or try again"
11692 this.fireEvent('actionfailed', this, action);
11697 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11698 * @param {String} id The value to search for
11701 findField : function(id){
11702 var items = this.getItems();
11703 var field = items.get(id);
11705 items.each(function(f){
11706 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11713 return field || null;
11716 * Mark fields in this form invalid in bulk.
11717 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11718 * @return {BasicForm} this
11720 markInvalid : function(errors){
11721 if(errors instanceof Array){
11722 for(var i = 0, len = errors.length; i < len; i++){
11723 var fieldError = errors[i];
11724 var f = this.findField(fieldError.id);
11726 f.markInvalid(fieldError.msg);
11732 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11733 field.markInvalid(errors[id]);
11737 //Roo.each(this.childForms || [], function (f) {
11738 // f.markInvalid(errors);
11745 * Set values for fields in this form in bulk.
11746 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11747 * @return {BasicForm} this
11749 setValues : function(values){
11750 if(values instanceof Array){ // array of objects
11751 for(var i = 0, len = values.length; i < len; i++){
11753 var f = this.findField(v.id);
11755 f.setValue(v.value);
11756 if(this.trackResetOnLoad){
11757 f.originalValue = f.getValue();
11761 }else{ // object hash
11764 if(typeof values[id] != 'function' && (field = this.findField(id))){
11766 if (field.setFromData &&
11767 field.valueField &&
11768 field.displayField &&
11769 // combos' with local stores can
11770 // be queried via setValue()
11771 // to set their value..
11772 (field.store && !field.store.isLocal)
11776 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11777 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11778 field.setFromData(sd);
11780 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11782 field.setFromData(values);
11785 field.setValue(values[id]);
11789 if(this.trackResetOnLoad){
11790 field.originalValue = field.getValue();
11796 //Roo.each(this.childForms || [], function (f) {
11797 // f.setValues(values);
11804 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11805 * they are returned as an array.
11806 * @param {Boolean} asString
11809 getValues : function(asString){
11810 //if (this.childForms) {
11811 // copy values from the child forms
11812 // Roo.each(this.childForms, function (f) {
11813 // this.setValues(f.getValues());
11819 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11820 if(asString === true){
11823 return Roo.urlDecode(fs);
11827 * Returns the fields in this form as an object with key/value pairs.
11828 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11831 getFieldValues : function(with_hidden)
11833 var items = this.getItems();
11835 items.each(function(f){
11837 if (!f.getName()) {
11841 var v = f.getValue();
11843 if (f.inputType =='radio') {
11844 if (typeof(ret[f.getName()]) == 'undefined') {
11845 ret[f.getName()] = ''; // empty..
11848 if (!f.el.dom.checked) {
11852 v = f.el.dom.value;
11856 if(f.xtype == 'MoneyField'){
11857 ret[f.currencyName] = f.getCurrency();
11860 // not sure if this supported any more..
11861 if ((typeof(v) == 'object') && f.getRawValue) {
11862 v = f.getRawValue() ; // dates..
11864 // combo boxes where name != hiddenName...
11865 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11866 ret[f.name] = f.getRawValue();
11868 ret[f.getName()] = v;
11875 * Clears all invalid messages in this form.
11876 * @return {BasicForm} this
11878 clearInvalid : function(){
11879 var items = this.getItems();
11881 items.each(function(f){
11889 * Resets this form.
11890 * @return {BasicForm} this
11892 reset : function(){
11893 var items = this.getItems();
11894 items.each(function(f){
11898 Roo.each(this.childForms || [], function (f) {
11906 getItems : function()
11908 var r=new Roo.util.MixedCollection(false, function(o){
11909 return o.id || (o.id = Roo.id());
11911 var iter = function(el) {
11918 Roo.each(el.items,function(e) {
11927 hideFields : function(items)
11929 Roo.each(items, function(i){
11931 var f = this.findField(i);
11942 showFields : function(items)
11944 Roo.each(items, function(i){
11946 var f = this.findField(i);
11959 Roo.apply(Roo.bootstrap.form.Form, {
11975 intervalID : false,
11981 if(this.isApplied){
11986 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11987 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11988 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11989 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11992 this.maskEl.top.enableDisplayMode("block");
11993 this.maskEl.left.enableDisplayMode("block");
11994 this.maskEl.bottom.enableDisplayMode("block");
11995 this.maskEl.right.enableDisplayMode("block");
11997 this.toolTip = new Roo.bootstrap.Tooltip({
11998 cls : 'roo-form-error-popover',
12000 'left' : ['r-l', [-2,0], 'right'],
12001 'right' : ['l-r', [2,0], 'left'],
12002 'bottom' : ['tl-bl', [0,2], 'top'],
12003 'top' : [ 'bl-tl', [0,-2], 'bottom']
12007 this.toolTip.render(Roo.get(document.body));
12009 this.toolTip.el.enableDisplayMode("block");
12011 Roo.get(document.body).on('click', function(){
12015 Roo.get(document.body).on('touchstart', function(){
12019 this.isApplied = true
12022 mask : function(form, target)
12026 this.target = target;
12028 if(!this.form.errorMask || !target.el){
12032 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12034 Roo.log(scrollable);
12036 var ot = this.target.el.calcOffsetsTo(scrollable);
12038 var scrollTo = ot[1] - this.form.maskOffset;
12040 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12042 scrollable.scrollTo('top', scrollTo);
12044 var box = this.target.el.getBox();
12046 var zIndex = Roo.bootstrap.Modal.zIndex++;
12049 this.maskEl.top.setStyle('position', 'absolute');
12050 this.maskEl.top.setStyle('z-index', zIndex);
12051 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12052 this.maskEl.top.setLeft(0);
12053 this.maskEl.top.setTop(0);
12054 this.maskEl.top.show();
12056 this.maskEl.left.setStyle('position', 'absolute');
12057 this.maskEl.left.setStyle('z-index', zIndex);
12058 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12059 this.maskEl.left.setLeft(0);
12060 this.maskEl.left.setTop(box.y - this.padding);
12061 this.maskEl.left.show();
12063 this.maskEl.bottom.setStyle('position', 'absolute');
12064 this.maskEl.bottom.setStyle('z-index', zIndex);
12065 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12066 this.maskEl.bottom.setLeft(0);
12067 this.maskEl.bottom.setTop(box.bottom + this.padding);
12068 this.maskEl.bottom.show();
12070 this.maskEl.right.setStyle('position', 'absolute');
12071 this.maskEl.right.setStyle('z-index', zIndex);
12072 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12073 this.maskEl.right.setLeft(box.right + this.padding);
12074 this.maskEl.right.setTop(box.y - this.padding);
12075 this.maskEl.right.show();
12077 this.toolTip.bindEl = this.target.el;
12079 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12081 var tip = this.target.blankText;
12083 if(this.target.getValue() !== '' ) {
12085 if (this.target.invalidText.length) {
12086 tip = this.target.invalidText;
12087 } else if (this.target.regexText.length){
12088 tip = this.target.regexText;
12092 this.toolTip.show(tip);
12094 this.intervalID = window.setInterval(function() {
12095 Roo.bootstrap.form.Form.popover.unmask();
12098 window.onwheel = function(){ return false;};
12100 (function(){ this.isMasked = true; }).defer(500, this);
12104 unmask : function()
12106 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12110 this.maskEl.top.setStyle('position', 'absolute');
12111 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12112 this.maskEl.top.hide();
12114 this.maskEl.left.setStyle('position', 'absolute');
12115 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12116 this.maskEl.left.hide();
12118 this.maskEl.bottom.setStyle('position', 'absolute');
12119 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12120 this.maskEl.bottom.hide();
12122 this.maskEl.right.setStyle('position', 'absolute');
12123 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12124 this.maskEl.right.hide();
12126 this.toolTip.hide();
12128 this.toolTip.el.hide();
12130 window.onwheel = function(){ return true;};
12132 if(this.intervalID){
12133 window.clearInterval(this.intervalID);
12134 this.intervalID = false;
12137 this.isMasked = false;
12147 * Ext JS Library 1.1.1
12148 * Copyright(c) 2006-2007, Ext JS, LLC.
12150 * Originally Released Under LGPL - original licence link has changed is not relivant.
12153 * <script type="text/javascript">
12156 * @class Roo.form.VTypes
12157 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12160 Roo.form.VTypes = function(){
12161 // closure these in so they are only created once.
12162 var alpha = /^[a-zA-Z_]+$/;
12163 var alphanum = /^[a-zA-Z0-9_]+$/;
12164 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12165 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12167 // All these messages and functions are configurable
12170 * The function used to validate email addresses
12171 * @param {String} value The email address
12173 'email' : function(v){
12174 return email.test(v);
12177 * The error text to display when the email validation function returns false
12180 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12182 * The keystroke filter mask to be applied on email input
12185 'emailMask' : /[a-z0-9_\.\-@]/i,
12188 * The function used to validate URLs
12189 * @param {String} value The URL
12191 'url' : function(v){
12192 return url.test(v);
12195 * The error text to display when the url validation function returns false
12198 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12201 * The function used to validate alpha values
12202 * @param {String} value The value
12204 'alpha' : function(v){
12205 return alpha.test(v);
12208 * The error text to display when the alpha validation function returns false
12211 'alphaText' : 'This field should only contain letters and _',
12213 * The keystroke filter mask to be applied on alpha input
12216 'alphaMask' : /[a-z_]/i,
12219 * The function used to validate alphanumeric values
12220 * @param {String} value The value
12222 'alphanum' : function(v){
12223 return alphanum.test(v);
12226 * The error text to display when the alphanumeric validation function returns false
12229 'alphanumText' : 'This field should only contain letters, numbers and _',
12231 * The keystroke filter mask to be applied on alphanumeric input
12234 'alphanumMask' : /[a-z0-9_]/i
12244 * @class Roo.bootstrap.form.Input
12245 * @extends Roo.bootstrap.Component
12246 * Bootstrap Input class
12247 * @cfg {Boolean} disabled is it disabled
12248 * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)
12249 * @cfg {String} name name of the input
12250 * @cfg {string} fieldLabel - the label associated
12251 * @cfg {string} placeholder - placeholder to put in text.
12252 * @cfg {string} before - input group add on before
12253 * @cfg {string} after - input group add on after
12254 * @cfg {string} size - (lg|sm) or leave empty..
12255 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12256 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12257 * @cfg {Number} md colspan out of 12 for computer-sized screens
12258 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12259 * @cfg {string} value default value of the input
12260 * @cfg {Number} labelWidth set the width of label
12261 * @cfg {Number} labellg set the width of label (1-12)
12262 * @cfg {Number} labelmd set the width of label (1-12)
12263 * @cfg {Number} labelsm set the width of label (1-12)
12264 * @cfg {Number} labelxs set the width of label (1-12)
12265 * @cfg {String} labelAlign (top|left)
12266 * @cfg {Boolean} readOnly Specifies that the field should be read-only
12267 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12268 * @cfg {String} indicatorpos (left|right) default left
12269 * @cfg {String} capture (user|camera) use for file input only. (default empty)
12270 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12271 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12272 * @cfg {Roo.bootstrap.Button} before Button to show before
12273 * @cfg {Roo.bootstrap.Button} afterButton to show before
12274 * @cfg {String} align (left|center|right) Default left
12275 * @cfg {Boolean} forceFeedback (true|false) Default false
12278 * Create a new Input
12279 * @param {Object} config The config object
12282 Roo.bootstrap.form.Input = function(config){
12284 Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12289 * Fires when this field receives input focus.
12290 * @param {Roo.form.Field} this
12295 * Fires when this field loses input focus.
12296 * @param {Roo.form.Field} this
12300 * @event specialkey
12301 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
12302 * {@link Roo.EventObject#getKey} to determine which key was pressed.
12303 * @param {Roo.form.Field} this
12304 * @param {Roo.EventObject} e The event object
12309 * Fires just before the field blurs if the field value has changed.
12310 * @param {Roo.form.Field} this
12311 * @param {Mixed} newValue The new value
12312 * @param {Mixed} oldValue The original value
12317 * Fires after the field has been marked as invalid.
12318 * @param {Roo.form.Field} this
12319 * @param {String} msg The validation message
12324 * Fires after the field has been validated with no errors.
12325 * @param {Roo.form.Field} this
12330 * Fires after the key up
12331 * @param {Roo.form.Field} this
12332 * @param {Roo.EventObject} e The event Object
12337 * Fires after the user pastes into input
12338 * @param {Roo.form.Field} this
12339 * @param {Roo.EventObject} e The event Object
12345 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component, {
12347 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12348 automatic validation (defaults to "keyup").
12350 validationEvent : "keyup",
12352 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12354 validateOnBlur : true,
12356 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12358 validationDelay : 250,
12360 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12362 focusClass : "x-form-focus", // not needed???
12366 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12368 invalidClass : "has-warning",
12371 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12373 validClass : "has-success",
12376 * @cfg {Boolean} hasFeedback (true|false) default true
12378 hasFeedback : true,
12381 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12383 invalidFeedbackClass : "glyphicon-warning-sign",
12386 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12388 validFeedbackClass : "glyphicon-ok",
12391 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12393 selectOnFocus : false,
12396 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12400 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12405 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12407 disableKeyFilter : false,
12410 * @cfg {Boolean} disabled True to disable the field (defaults to false).
12414 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12418 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12420 blankText : "Please complete this mandatory field",
12423 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12427 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12429 maxLength : Number.MAX_VALUE,
12431 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12433 minLengthText : "The minimum length for this field is {0}",
12435 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12437 maxLengthText : "The maximum length for this field is {0}",
12441 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12442 * If available, this function will be called only after the basic validators all return true, and will be passed the
12443 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12447 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12448 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12449 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
12453 * @cfg {String} regexText -- Depricated - use Invalid Text
12458 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12464 autocomplete: false,
12468 inputType : 'text',
12471 placeholder: false,
12476 preventMark: false,
12477 isFormField : true,
12480 labelAlign : false,
12483 formatedValue : false,
12484 forceFeedback : false,
12486 indicatorpos : 'left',
12496 parentLabelAlign : function()
12499 while (parent.parent()) {
12500 parent = parent.parent();
12501 if (typeof(parent.labelAlign) !='undefined') {
12502 return parent.labelAlign;
12509 getAutoCreate : function()
12511 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12517 if(this.inputType != 'hidden'){
12518 cfg.cls = 'form-group' //input-group
12524 type : this.inputType,
12525 value : this.value,
12526 cls : 'form-control',
12527 placeholder : this.placeholder || '',
12528 autocomplete : this.autocomplete || 'new-password'
12530 if (this.inputType == 'file') {
12531 input.style = 'overflow:hidden'; // why not in CSS?
12534 if(this.capture.length){
12535 input.capture = this.capture;
12538 if(this.accept.length){
12539 input.accept = this.accept + "/*";
12543 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12546 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12547 input.maxLength = this.maxLength;
12550 if (this.disabled) {
12551 input.disabled=true;
12554 if (this.readOnly) {
12555 input.readonly=true;
12559 input.name = this.name;
12563 input.cls += ' input-' + this.size;
12567 ['xs','sm','md','lg'].map(function(size){
12568 if (settings[size]) {
12569 cfg.cls += ' col-' + size + '-' + settings[size];
12573 var inputblock = input;
12577 cls: 'glyphicon form-control-feedback'
12580 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12583 cls : 'has-feedback',
12591 if (this.before || this.after) {
12594 cls : 'input-group',
12598 if (this.before && typeof(this.before) == 'string') {
12600 inputblock.cn.push({
12602 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12606 if (this.before && typeof(this.before) == 'object') {
12607 this.before = Roo.factory(this.before);
12609 inputblock.cn.push({
12611 cls : 'roo-input-before input-group-prepend input-group-' +
12612 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12616 inputblock.cn.push(input);
12618 if (this.after && typeof(this.after) == 'string') {
12619 inputblock.cn.push({
12621 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12625 if (this.after && typeof(this.after) == 'object') {
12626 this.after = Roo.factory(this.after);
12628 inputblock.cn.push({
12630 cls : 'roo-input-after input-group-append input-group-' +
12631 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12635 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12636 inputblock.cls += ' has-feedback';
12637 inputblock.cn.push(feedback);
12642 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12643 tooltip : 'This field is required'
12645 if (this.allowBlank ) {
12646 indicator.style = this.allowBlank ? ' display:none' : '';
12648 if (align ==='left' && this.fieldLabel.length) {
12650 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12657 cls : 'control-label col-form-label',
12658 html : this.fieldLabel
12669 var labelCfg = cfg.cn[1];
12670 var contentCfg = cfg.cn[2];
12672 if(this.indicatorpos == 'right'){
12677 cls : 'control-label col-form-label',
12681 html : this.fieldLabel
12695 labelCfg = cfg.cn[0];
12696 contentCfg = cfg.cn[1];
12700 if(this.labelWidth > 12){
12701 labelCfg.style = "width: " + this.labelWidth + 'px';
12704 if(this.labelWidth < 13 && this.labelmd == 0){
12705 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12708 if(this.labellg > 0){
12709 labelCfg.cls += ' col-lg-' + this.labellg;
12710 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12713 if(this.labelmd > 0){
12714 labelCfg.cls += ' col-md-' + this.labelmd;
12715 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12718 if(this.labelsm > 0){
12719 labelCfg.cls += ' col-sm-' + this.labelsm;
12720 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12723 if(this.labelxs > 0){
12724 labelCfg.cls += ' col-xs-' + this.labelxs;
12725 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12729 } else if ( this.fieldLabel.length) {
12736 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12737 tooltip : 'This field is required',
12738 style : this.allowBlank ? ' display:none' : ''
12742 //cls : 'input-group-addon',
12743 html : this.fieldLabel
12751 if(this.indicatorpos == 'right'){
12756 //cls : 'input-group-addon',
12757 html : this.fieldLabel
12762 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12763 tooltip : 'This field is required',
12764 style : this.allowBlank ? ' display:none' : ''
12784 if (this.parentType === 'Navbar' && this.parent().bar) {
12785 cfg.cls += ' navbar-form';
12788 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12789 // on BS4 we do this only if not form
12790 cfg.cls += ' navbar-form';
12798 * return the real input element.
12800 inputEl: function ()
12802 return this.el.select('input.form-control',true).first();
12805 tooltipEl : function()
12807 return this.inputEl();
12810 indicatorEl : function()
12812 if (Roo.bootstrap.version == 4) {
12813 return false; // not enabled in v4 yet.
12816 var indicator = this.el.select('i.roo-required-indicator',true).first();
12826 setDisabled : function(v)
12828 var i = this.inputEl().dom;
12830 i.removeAttribute('disabled');
12834 i.setAttribute('disabled','true');
12836 initEvents : function()
12839 this.inputEl().on("keydown" , this.fireKey, this);
12840 this.inputEl().on("focus", this.onFocus, this);
12841 this.inputEl().on("blur", this.onBlur, this);
12843 this.inputEl().relayEvent('keyup', this);
12844 this.inputEl().relayEvent('paste', this);
12846 this.indicator = this.indicatorEl();
12848 if(this.indicator){
12849 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12852 // reference to original value for reset
12853 this.originalValue = this.getValue();
12854 //Roo.form.TextField.superclass.initEvents.call(this);
12855 if(this.validationEvent == 'keyup'){
12856 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12857 this.inputEl().on('keyup', this.filterValidation, this);
12859 else if(this.validationEvent !== false){
12860 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12863 if(this.selectOnFocus){
12864 this.on("focus", this.preFocus, this);
12867 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12868 this.inputEl().on("keypress", this.filterKeys, this);
12870 this.inputEl().relayEvent('keypress', this);
12873 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12874 this.el.on("click", this.autoSize, this);
12877 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12878 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12881 if (typeof(this.before) == 'object') {
12882 this.before.render(this.el.select('.roo-input-before',true).first());
12884 if (typeof(this.after) == 'object') {
12885 this.after.render(this.el.select('.roo-input-after',true).first());
12888 this.inputEl().on('change', this.onChange, this);
12891 filterValidation : function(e){
12892 if(!e.isNavKeyPress()){
12893 this.validationTask.delay(this.validationDelay);
12897 * Validates the field value
12898 * @return {Boolean} True if the value is valid, else false
12900 validate : function(){
12901 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12902 if(this.disabled || this.validateValue(this.getRawValue())){
12907 this.markInvalid();
12913 * Validates a value according to the field's validation rules and marks the field as invalid
12914 * if the validation fails
12915 * @param {Mixed} value The value to validate
12916 * @return {Boolean} True if the value is valid, else false
12918 validateValue : function(value)
12920 if(this.getVisibilityEl().hasClass('hidden')){
12924 if(value.length < 1) { // if it's blank
12925 if(this.allowBlank){
12931 if(value.length < this.minLength){
12934 if(value.length > this.maxLength){
12938 var vt = Roo.form.VTypes;
12939 if(!vt[this.vtype](value, this)){
12943 if(typeof this.validator == "function"){
12944 var msg = this.validator(value);
12948 if (typeof(msg) == 'string') {
12949 this.invalidText = msg;
12953 if(this.regex && !this.regex.test(value)){
12961 fireKey : function(e){
12962 //Roo.log('field ' + e.getKey());
12963 if(e.isNavKeyPress()){
12964 this.fireEvent("specialkey", this, e);
12967 focus : function (selectText){
12969 this.inputEl().focus();
12970 if(selectText === true){
12971 this.inputEl().dom.select();
12977 onFocus : function(){
12978 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12979 // this.el.addClass(this.focusClass);
12981 if(!this.hasFocus){
12982 this.hasFocus = true;
12983 this.startValue = this.getValue();
12984 this.fireEvent("focus", this);
12988 beforeBlur : Roo.emptyFn,
12992 onBlur : function(){
12994 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12995 //this.el.removeClass(this.focusClass);
12997 this.hasFocus = false;
12998 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
13001 var v = this.getValue();
13002 if(String(v) !== String(this.startValue)){
13003 this.fireEvent('change', this, v, this.startValue);
13005 this.fireEvent("blur", this);
13008 onChange : function(e)
13010 var v = this.getValue();
13011 if(String(v) !== String(this.startValue)){
13012 this.fireEvent('change', this, v, this.startValue);
13018 * Resets the current field value to the originally loaded value and clears any validation messages
13020 reset : function(){
13021 this.setValue(this.originalValue);
13025 * Returns the name of the field
13026 * @return {Mixed} name The name field
13028 getName: function(){
13032 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
13033 * @return {Mixed} value The field value
13035 getValue : function(){
13037 var v = this.inputEl().getValue();
13042 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
13043 * @return {Mixed} value The field value
13045 getRawValue : function(){
13046 var v = this.inputEl().getValue();
13052 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
13053 * @param {Mixed} value The value to set
13055 setRawValue : function(v){
13056 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13059 selectText : function(start, end){
13060 var v = this.getRawValue();
13062 start = start === undefined ? 0 : start;
13063 end = end === undefined ? v.length : end;
13064 var d = this.inputEl().dom;
13065 if(d.setSelectionRange){
13066 d.setSelectionRange(start, end);
13067 }else if(d.createTextRange){
13068 var range = d.createTextRange();
13069 range.moveStart("character", start);
13070 range.moveEnd("character", v.length-end);
13077 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
13078 * @param {Mixed} value The value to set
13080 setValue : function(v){
13083 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13089 processValue : function(value){
13090 if(this.stripCharsRe){
13091 var newValue = value.replace(this.stripCharsRe, '');
13092 if(newValue !== value){
13093 this.setRawValue(newValue);
13100 preFocus : function(){
13102 if(this.selectOnFocus){
13103 this.inputEl().dom.select();
13106 filterKeys : function(e){
13107 var k = e.getKey();
13108 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13111 var c = e.getCharCode(), cc = String.fromCharCode(c);
13112 if(Roo.isIE && (e.isSpecialKey() || !cc)){
13115 if(!this.maskRe.test(cc)){
13120 * Clear any invalid styles/messages for this field
13122 clearInvalid : function(){
13124 if(!this.el || this.preventMark){ // not rendered
13129 this.el.removeClass([this.invalidClass, 'is-invalid']);
13131 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13133 var feedback = this.el.select('.form-control-feedback', true).first();
13136 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13141 if(this.indicator){
13142 this.indicator.removeClass('visible');
13143 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13146 this.fireEvent('valid', this);
13150 * Mark this field as valid
13152 markValid : function()
13154 if(!this.el || this.preventMark){ // not rendered...
13158 this.el.removeClass([this.invalidClass, this.validClass]);
13159 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13161 var feedback = this.el.select('.form-control-feedback', true).first();
13164 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13167 if(this.indicator){
13168 this.indicator.removeClass('visible');
13169 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13177 if(this.allowBlank && !this.getRawValue().length){
13180 if (Roo.bootstrap.version == 3) {
13181 this.el.addClass(this.validClass);
13183 this.inputEl().addClass('is-valid');
13186 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13188 var feedback = this.el.select('.form-control-feedback', true).first();
13191 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13192 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13197 this.fireEvent('valid', this);
13201 * Mark this field as invalid
13202 * @param {String} msg The validation message
13204 markInvalid : function(msg)
13206 if(!this.el || this.preventMark){ // not rendered
13210 this.el.removeClass([this.invalidClass, this.validClass]);
13211 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13213 var feedback = this.el.select('.form-control-feedback', true).first();
13216 this.el.select('.form-control-feedback', true).first().removeClass(
13217 [this.invalidFeedbackClass, this.validFeedbackClass]);
13224 if(this.allowBlank && !this.getRawValue().length){
13228 if(this.indicator){
13229 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13230 this.indicator.addClass('visible');
13232 if (Roo.bootstrap.version == 3) {
13233 this.el.addClass(this.invalidClass);
13235 this.inputEl().addClass('is-invalid');
13240 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13242 var feedback = this.el.select('.form-control-feedback', true).first();
13245 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13247 if(this.getValue().length || this.forceFeedback){
13248 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13255 this.fireEvent('invalid', this, msg);
13258 SafariOnKeyDown : function(event)
13260 // this is a workaround for a password hang bug on chrome/ webkit.
13261 if (this.inputEl().dom.type != 'password') {
13265 var isSelectAll = false;
13267 if(this.inputEl().dom.selectionEnd > 0){
13268 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13270 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13271 event.preventDefault();
13276 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13278 event.preventDefault();
13279 // this is very hacky as keydown always get's upper case.
13281 var cc = String.fromCharCode(event.getCharCode());
13282 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
13286 adjustWidth : function(tag, w){
13287 tag = tag.toLowerCase();
13288 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13289 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13290 if(tag == 'input'){
13293 if(tag == 'textarea'){
13296 }else if(Roo.isOpera){
13297 if(tag == 'input'){
13300 if(tag == 'textarea'){
13308 setFieldLabel : function(v)
13310 if(!this.rendered){
13314 if(this.indicatorEl()){
13315 var ar = this.el.select('label > span',true);
13317 if (ar.elements.length) {
13318 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13319 this.fieldLabel = v;
13323 var br = this.el.select('label',true);
13325 if(br.elements.length) {
13326 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13327 this.fieldLabel = v;
13331 Roo.log('Cannot Found any of label > span || label in input');
13335 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13336 this.fieldLabel = v;
13351 * @class Roo.bootstrap.form.TextArea
13352 * @extends Roo.bootstrap.form.Input
13353 * Bootstrap TextArea class
13354 * @cfg {Number} cols Specifies the visible width of a text area
13355 * @cfg {Number} rows Specifies the visible number of lines in a text area
13356 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13357 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13358 * @cfg {string} html text
13361 * Create a new TextArea
13362 * @param {Object} config The config object
13365 Roo.bootstrap.form.TextArea = function(config){
13366 Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13370 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input, {
13380 getAutoCreate : function(){
13382 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13388 if(this.inputType != 'hidden'){
13389 cfg.cls = 'form-group' //input-group
13397 value : this.value || '',
13398 html: this.html || '',
13399 cls : 'form-control',
13400 placeholder : this.placeholder || ''
13404 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13405 input.maxLength = this.maxLength;
13409 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13413 input.cols = this.cols;
13416 if (this.readOnly) {
13417 input.readonly = true;
13421 input.name = this.name;
13425 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13429 ['xs','sm','md','lg'].map(function(size){
13430 if (settings[size]) {
13431 cfg.cls += ' col-' + size + '-' + settings[size];
13435 var inputblock = input;
13437 if(this.hasFeedback && !this.allowBlank){
13441 cls: 'glyphicon form-control-feedback'
13445 cls : 'has-feedback',
13454 if (this.before || this.after) {
13457 cls : 'input-group',
13461 inputblock.cn.push({
13463 cls : 'input-group-addon',
13468 inputblock.cn.push(input);
13470 if(this.hasFeedback && !this.allowBlank){
13471 inputblock.cls += ' has-feedback';
13472 inputblock.cn.push(feedback);
13476 inputblock.cn.push({
13478 cls : 'input-group-addon',
13485 if (align ==='left' && this.fieldLabel.length) {
13490 cls : 'control-label',
13491 html : this.fieldLabel
13502 if(this.labelWidth > 12){
13503 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13506 if(this.labelWidth < 13 && this.labelmd == 0){
13507 this.labelmd = this.labelWidth;
13510 if(this.labellg > 0){
13511 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13512 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13515 if(this.labelmd > 0){
13516 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13517 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13520 if(this.labelsm > 0){
13521 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13522 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13525 if(this.labelxs > 0){
13526 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13527 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13530 } else if ( this.fieldLabel.length) {
13535 //cls : 'input-group-addon',
13536 html : this.fieldLabel
13554 if (this.disabled) {
13555 input.disabled=true;
13562 * return the real textarea element.
13564 inputEl: function ()
13566 return this.el.select('textarea.form-control',true).first();
13570 * Clear any invalid styles/messages for this field
13572 clearInvalid : function()
13575 if(!this.el || this.preventMark){ // not rendered
13579 var label = this.el.select('label', true).first();
13580 var icon = this.el.select('i.fa-star', true).first();
13585 this.el.removeClass( this.validClass);
13586 this.inputEl().removeClass('is-invalid');
13588 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13590 var feedback = this.el.select('.form-control-feedback', true).first();
13593 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13598 this.fireEvent('valid', this);
13602 * Mark this field as valid
13604 markValid : function()
13606 if(!this.el || this.preventMark){ // not rendered
13610 this.el.removeClass([this.invalidClass, this.validClass]);
13611 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13613 var feedback = this.el.select('.form-control-feedback', true).first();
13616 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13619 if(this.disabled || this.allowBlank){
13623 var label = this.el.select('label', true).first();
13624 var icon = this.el.select('i.fa-star', true).first();
13629 if (Roo.bootstrap.version == 3) {
13630 this.el.addClass(this.validClass);
13632 this.inputEl().addClass('is-valid');
13636 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13638 var feedback = this.el.select('.form-control-feedback', true).first();
13641 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13642 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13647 this.fireEvent('valid', this);
13651 * Mark this field as invalid
13652 * @param {String} msg The validation message
13654 markInvalid : function(msg)
13656 if(!this.el || this.preventMark){ // not rendered
13660 this.el.removeClass([this.invalidClass, this.validClass]);
13661 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13663 var feedback = this.el.select('.form-control-feedback', true).first();
13666 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13669 if(this.disabled || this.allowBlank){
13673 var label = this.el.select('label', true).first();
13674 var icon = this.el.select('i.fa-star', true).first();
13676 if(!this.getValue().length && label && !icon){
13677 this.el.createChild({
13679 cls : 'text-danger fa fa-lg fa-star',
13680 tooltip : 'This field is required',
13681 style : 'margin-right:5px;'
13685 if (Roo.bootstrap.version == 3) {
13686 this.el.addClass(this.invalidClass);
13688 this.inputEl().addClass('is-invalid');
13691 // fixme ... this may be depricated need to test..
13692 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13694 var feedback = this.el.select('.form-control-feedback', true).first();
13697 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13699 if(this.getValue().length || this.forceFeedback){
13700 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13707 this.fireEvent('invalid', this, msg);
13715 * trigger field - base class for combo..
13720 * @class Roo.bootstrap.form.TriggerField
13721 * @extends Roo.bootstrap.form.Input
13722 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13723 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13724 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13725 * for which you can provide a custom implementation. For example:
13727 var trigger = new Roo.bootstrap.form.TriggerField();
13728 trigger.onTriggerClick = myTriggerFn;
13729 trigger.applyTo('my-field');
13732 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13733 * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13734 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13735 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13736 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13739 * Create a new TriggerField.
13740 * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13741 * to the base TextField)
13743 Roo.bootstrap.form.TriggerField = function(config){
13744 this.mimicing = false;
13745 Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13748 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input, {
13750 * @cfg {String} triggerClass A CSS class to apply to the trigger
13753 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13758 * @cfg {Boolean} removable (true|false) special filter default false
13762 /** @cfg {Boolean} grow @hide */
13763 /** @cfg {Number} growMin @hide */
13764 /** @cfg {Number} growMax @hide */
13770 autoSize: Roo.emptyFn,
13774 deferHeight : true,
13777 actionMode : 'wrap',
13782 getAutoCreate : function(){
13784 var align = this.labelAlign || this.parentLabelAlign();
13789 cls: 'form-group' //input-group
13796 type : this.inputType,
13797 cls : 'form-control',
13798 autocomplete: 'new-password',
13799 placeholder : this.placeholder || ''
13803 input.name = this.name;
13806 input.cls += ' input-' + this.size;
13809 if (this.disabled) {
13810 input.disabled=true;
13813 var inputblock = input;
13815 if(this.hasFeedback && !this.allowBlank){
13819 cls: 'glyphicon form-control-feedback'
13822 if(this.removable && !this.editable ){
13824 cls : 'has-feedback',
13830 cls : 'roo-combo-removable-btn close'
13837 cls : 'has-feedback',
13846 if(this.removable && !this.editable ){
13848 cls : 'roo-removable',
13854 cls : 'roo-combo-removable-btn close'
13861 if (this.before || this.after) {
13864 cls : 'input-group',
13868 inputblock.cn.push({
13870 cls : 'input-group-addon input-group-prepend input-group-text',
13875 inputblock.cn.push(input);
13877 if(this.hasFeedback && !this.allowBlank){
13878 inputblock.cls += ' has-feedback';
13879 inputblock.cn.push(feedback);
13883 inputblock.cn.push({
13885 cls : 'input-group-addon input-group-append input-group-text',
13894 var ibwrap = inputblock;
13899 cls: 'roo-select2-choices',
13903 cls: 'roo-select2-search-field',
13915 cls: 'roo-select2-container input-group',
13920 cls: 'form-hidden-field'
13926 if(!this.multiple && this.showToggleBtn){
13932 if (this.caret != false) {
13935 cls: 'fa fa-' + this.caret
13942 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13944 Roo.bootstrap.version == 3 ? caret : '',
13947 cls: 'combobox-clear',
13961 combobox.cls += ' roo-select2-container-multi';
13965 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13966 tooltip : 'This field is required'
13968 if (Roo.bootstrap.version == 4) {
13971 style : 'display:none'
13976 if (align ==='left' && this.fieldLabel.length) {
13978 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13985 cls : 'control-label',
13986 html : this.fieldLabel
13998 var labelCfg = cfg.cn[1];
13999 var contentCfg = cfg.cn[2];
14001 if(this.indicatorpos == 'right'){
14006 cls : 'control-label',
14010 html : this.fieldLabel
14024 labelCfg = cfg.cn[0];
14025 contentCfg = cfg.cn[1];
14028 if(this.labelWidth > 12){
14029 labelCfg.style = "width: " + this.labelWidth + 'px';
14032 if(this.labelWidth < 13 && this.labelmd == 0){
14033 this.labelmd = this.labelWidth;
14036 if(this.labellg > 0){
14037 labelCfg.cls += ' col-lg-' + this.labellg;
14038 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14041 if(this.labelmd > 0){
14042 labelCfg.cls += ' col-md-' + this.labelmd;
14043 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14046 if(this.labelsm > 0){
14047 labelCfg.cls += ' col-sm-' + this.labelsm;
14048 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14051 if(this.labelxs > 0){
14052 labelCfg.cls += ' col-xs-' + this.labelxs;
14053 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14056 } else if ( this.fieldLabel.length) {
14057 // Roo.log(" label");
14062 //cls : 'input-group-addon',
14063 html : this.fieldLabel
14071 if(this.indicatorpos == 'right'){
14079 html : this.fieldLabel
14093 // Roo.log(" no label && no align");
14100 ['xs','sm','md','lg'].map(function(size){
14101 if (settings[size]) {
14102 cfg.cls += ' col-' + size + '-' + settings[size];
14113 onResize : function(w, h){
14114 // Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14115 // if(typeof w == 'number'){
14116 // var x = w - this.trigger.getWidth();
14117 // this.inputEl().setWidth(this.adjustWidth('input', x));
14118 // this.trigger.setStyle('left', x+'px');
14123 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14126 getResizeEl : function(){
14127 return this.inputEl();
14131 getPositionEl : function(){
14132 return this.inputEl();
14136 alignErrorIcon : function(){
14137 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14141 initEvents : function(){
14145 Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14146 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14147 if(!this.multiple && this.showToggleBtn){
14148 this.trigger = this.el.select('span.dropdown-toggle',true).first();
14149 if(this.hideTrigger){
14150 this.trigger.setDisplayed(false);
14152 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14156 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14159 if(this.removable && !this.editable && !this.tickable){
14160 var close = this.closeTriggerEl();
14163 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14164 close.on('click', this.removeBtnClick, this, close);
14168 //this.trigger.addClassOnOver('x-form-trigger-over');
14169 //this.trigger.addClassOnClick('x-form-trigger-click');
14172 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14176 closeTriggerEl : function()
14178 var close = this.el.select('.roo-combo-removable-btn', true).first();
14179 return close ? close : false;
14182 removeBtnClick : function(e, h, el)
14184 e.preventDefault();
14186 if(this.fireEvent("remove", this) !== false){
14188 this.fireEvent("afterremove", this)
14192 createList : function()
14194 this.list = Roo.get(document.body).createChild({
14195 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14196 cls: 'typeahead typeahead-long dropdown-menu shadow',
14197 style: 'display:none'
14200 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14205 initTrigger : function(){
14210 onDestroy : function(){
14212 this.trigger.removeAllListeners();
14213 // this.trigger.remove();
14216 // this.wrap.remove();
14218 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14222 onFocus : function(){
14223 Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14225 if(!this.mimicing){
14226 this.wrap.addClass('x-trigger-wrap-focus');
14227 this.mimicing = true;
14228 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14229 if(this.monitorTab){
14230 this.el.on("keydown", this.checkTab, this);
14237 checkTab : function(e){
14238 if(e.getKey() == e.TAB){
14239 this.triggerBlur();
14244 onBlur : function(){
14249 mimicBlur : function(e, t){
14251 if(!this.wrap.contains(t) && this.validateBlur()){
14252 this.triggerBlur();
14258 triggerBlur : function(){
14259 this.mimicing = false;
14260 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14261 if(this.monitorTab){
14262 this.el.un("keydown", this.checkTab, this);
14264 //this.wrap.removeClass('x-trigger-wrap-focus');
14265 Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14269 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14270 validateBlur : function(e, t){
14275 onDisable : function(){
14276 this.inputEl().dom.disabled = true;
14277 //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14279 // this.wrap.addClass('x-item-disabled');
14284 onEnable : function(){
14285 this.inputEl().dom.disabled = false;
14286 //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14288 // this.el.removeClass('x-item-disabled');
14293 onShow : function(){
14294 var ae = this.getActionEl();
14297 ae.dom.style.display = '';
14298 ae.dom.style.visibility = 'visible';
14304 onHide : function(){
14305 var ae = this.getActionEl();
14306 ae.dom.style.display = 'none';
14310 * The function that should handle the trigger's click event. This method does nothing by default until overridden
14311 * by an implementing function.
14313 * @param {EventObject} e
14315 onTriggerClick : Roo.emptyFn
14323 * @class Roo.bootstrap.form.CardUploader
14324 * @extends Roo.bootstrap.Button
14325 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14326 * @cfg {Number} errorTimeout default 3000
14327 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
14328 * @cfg {Array} html The button text.
14332 * Create a new CardUploader
14333 * @param {Object} config The config object
14336 Roo.bootstrap.form.CardUploader = function(config){
14340 Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14343 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
14351 * When a image is clicked on - and needs to display a slideshow or similar..
14352 * @param {Roo.bootstrap.Card} this
14353 * @param {Object} The image information data
14359 * When a the download link is clicked
14360 * @param {Roo.bootstrap.Card} this
14361 * @param {Object} The image information data contains
14368 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input, {
14371 errorTimeout : 3000,
14375 fileCollection : false,
14378 getAutoCreate : function()
14382 cls :'form-group' ,
14387 //cls : 'input-group-addon',
14388 html : this.fieldLabel
14396 value : this.value,
14397 cls : 'd-none form-control'
14402 multiple : 'multiple',
14404 cls : 'd-none roo-card-upload-selector'
14408 cls : 'roo-card-uploader-button-container w-100 mb-2'
14411 cls : 'card-columns roo-card-uploader-container'
14421 getChildContainer : function() /// what children are added to.
14423 return this.containerEl;
14426 getButtonContainer : function() /// what children are added to.
14428 return this.el.select(".roo-card-uploader-button-container").first();
14431 initEvents : function()
14434 Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14438 xns: Roo.bootstrap,
14441 container_method : 'getButtonContainer' ,
14442 html : this.html, // fix changable?
14445 'click' : function(btn, e) {
14454 this.urlAPI = (window.createObjectURL && window) ||
14455 (window.URL && URL.revokeObjectURL && URL) ||
14456 (window.webkitURL && webkitURL);
14461 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14463 this.selectorEl.on('change', this.onFileSelected, this);
14466 this.images.forEach(function(img) {
14469 this.images = false;
14471 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14477 onClick : function(e)
14479 e.preventDefault();
14481 this.selectorEl.dom.click();
14485 onFileSelected : function(e)
14487 e.preventDefault();
14489 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14493 Roo.each(this.selectorEl.dom.files, function(file){
14494 this.addFile(file);
14503 addFile : function(file)
14506 if(typeof(file) === 'string'){
14507 throw "Add file by name?"; // should not happen
14511 if(!file || !this.urlAPI){
14521 var url = _this.urlAPI.createObjectURL( file);
14524 id : Roo.bootstrap.form.CardUploader.ID--,
14525 is_uploaded : false,
14529 mimetype : file.type,
14537 * addCard - add an Attachment to the uploader
14538 * @param data - the data about the image to upload
14542 title : "Title of file",
14543 is_uploaded : false,
14544 src : "http://.....",
14545 srcfile : { the File upload object },
14546 mimetype : file.type,
14549 .. any other data...
14555 addCard : function (data)
14557 // hidden input element?
14558 // if the file is not an image...
14559 //then we need to use something other that and header_image
14564 xns : Roo.bootstrap,
14565 xtype : 'CardFooter',
14568 xns : Roo.bootstrap,
14574 xns : Roo.bootstrap,
14576 html : String.format("<small>{0}</small>", data.title),
14577 cls : 'col-10 text-left',
14582 click : function() {
14584 t.fireEvent( "download", t, data );
14590 xns : Roo.bootstrap,
14592 style: 'max-height: 28px; ',
14598 click : function() {
14599 t.removeCard(data.id)
14611 var cn = this.addxtype(
14614 xns : Roo.bootstrap,
14617 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14618 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14619 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14624 initEvents : function() {
14625 Roo.bootstrap.Card.prototype.initEvents.call(this);
14627 this.imgEl = this.el.select('.card-img-top').first();
14629 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14630 this.imgEl.set({ 'pointer' : 'cursor' });
14633 this.getCardFooter().addClass('p-1');
14640 // dont' really need ot update items.
14641 // this.items.push(cn);
14642 this.fileCollection.add(cn);
14644 if (!data.srcfile) {
14645 this.updateInput();
14650 var reader = new FileReader();
14651 reader.addEventListener("load", function() {
14652 data.srcdata = reader.result;
14655 reader.readAsDataURL(data.srcfile);
14660 removeCard : function(id)
14663 var card = this.fileCollection.get(id);
14664 card.data.is_deleted = 1;
14665 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14666 //this.fileCollection.remove(card);
14667 //this.items = this.items.filter(function(e) { return e != card });
14668 // dont' really need ot update items.
14669 card.el.dom.parentNode.removeChild(card.el.dom);
14670 this.updateInput();
14676 this.fileCollection.each(function(card) {
14677 if (card.el.dom && card.el.dom.parentNode) {
14678 card.el.dom.parentNode.removeChild(card.el.dom);
14681 this.fileCollection.clear();
14682 this.updateInput();
14685 updateInput : function()
14688 this.fileCollection.each(function(e) {
14692 this.inputEl().dom.value = JSON.stringify(data);
14702 Roo.bootstrap.form.CardUploader.ID = -1;/*
14704 * Ext JS Library 1.1.1
14705 * Copyright(c) 2006-2007, Ext JS, LLC.
14707 * Originally Released Under LGPL - original licence link has changed is not relivant.
14710 * <script type="text/javascript">
14715 * @class Roo.data.SortTypes
14717 * Defines the default sorting (casting?) comparison functions used when sorting data.
14719 Roo.data.SortTypes = {
14721 * Default sort that does nothing
14722 * @param {Mixed} s The value being converted
14723 * @return {Mixed} The comparison value
14725 none : function(s){
14730 * The regular expression used to strip tags
14734 stripTagsRE : /<\/?[^>]+>/gi,
14737 * Strips all HTML tags to sort on text only
14738 * @param {Mixed} s The value being converted
14739 * @return {String} The comparison value
14741 asText : function(s){
14742 return String(s).replace(this.stripTagsRE, "");
14746 * Strips all HTML tags to sort on text only - Case insensitive
14747 * @param {Mixed} s The value being converted
14748 * @return {String} The comparison value
14750 asUCText : function(s){
14751 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14755 * Case insensitive string
14756 * @param {Mixed} s The value being converted
14757 * @return {String} The comparison value
14759 asUCString : function(s) {
14760 return String(s).toUpperCase();
14765 * @param {Mixed} s The value being converted
14766 * @return {Number} The comparison value
14768 asDate : function(s) {
14772 if(s instanceof Date){
14773 return s.getTime();
14775 return Date.parse(String(s));
14780 * @param {Mixed} s The value being converted
14781 * @return {Float} The comparison value
14783 asFloat : function(s) {
14784 var val = parseFloat(String(s).replace(/,/g, ""));
14793 * @param {Mixed} s The value being converted
14794 * @return {Number} The comparison value
14796 asInt : function(s) {
14797 var val = parseInt(String(s).replace(/,/g, ""));
14805 * Ext JS Library 1.1.1
14806 * Copyright(c) 2006-2007, Ext JS, LLC.
14808 * Originally Released Under LGPL - original licence link has changed is not relivant.
14811 * <script type="text/javascript">
14815 * @class Roo.data.Record
14816 * Instances of this class encapsulate both record <em>definition</em> information, and record
14817 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14818 * to access Records cached in an {@link Roo.data.Store} object.<br>
14820 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14821 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14824 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14826 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14827 * {@link #create}. The parameters are the same.
14828 * @param {Array} data An associative Array of data values keyed by the field name.
14829 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14830 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14831 * not specified an integer id is generated.
14833 Roo.data.Record = function(data, id){
14834 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14839 * Generate a constructor for a specific record layout.
14840 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14841 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14842 * Each field definition object may contain the following properties: <ul>
14843 * <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,
14844 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14845 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14846 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14847 * is being used, then this is a string containing the javascript expression to reference the data relative to
14848 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14849 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14850 * this may be omitted.</p></li>
14851 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14852 * <ul><li>auto (Default, implies no conversion)</li>
14857 * <li>date</li></ul></p></li>
14858 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14859 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14860 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14861 * by the Reader into an object that will be stored in the Record. It is passed the
14862 * following parameters:<ul>
14863 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14865 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14867 * <br>usage:<br><pre><code>
14868 var TopicRecord = Roo.data.Record.create(
14869 {name: 'title', mapping: 'topic_title'},
14870 {name: 'author', mapping: 'username'},
14871 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14872 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14873 {name: 'lastPoster', mapping: 'user2'},
14874 {name: 'excerpt', mapping: 'post_text'}
14877 var myNewRecord = new TopicRecord({
14878 title: 'Do my job please',
14881 lastPost: new Date(),
14882 lastPoster: 'Animal',
14883 excerpt: 'No way dude!'
14885 myStore.add(myNewRecord);
14890 Roo.data.Record.create = function(o){
14891 var f = function(){
14892 f.superclass.constructor.apply(this, arguments);
14894 Roo.extend(f, Roo.data.Record);
14895 var p = f.prototype;
14896 p.fields = new Roo.util.MixedCollection(false, function(field){
14899 for(var i = 0, len = o.length; i < len; i++){
14900 p.fields.add(new Roo.data.Field(o[i]));
14902 f.getField = function(name){
14903 return p.fields.get(name);
14908 Roo.data.Record.AUTO_ID = 1000;
14909 Roo.data.Record.EDIT = 'edit';
14910 Roo.data.Record.REJECT = 'reject';
14911 Roo.data.Record.COMMIT = 'commit';
14913 Roo.data.Record.prototype = {
14915 * Readonly flag - true if this record has been modified.
14924 join : function(store){
14925 this.store = store;
14929 * Set the named field to the specified value.
14930 * @param {String} name The name of the field to set.
14931 * @param {Object} value The value to set the field to.
14933 set : function(name, value){
14934 if(this.data[name] == value){
14938 if(!this.modified){
14939 this.modified = {};
14941 if(typeof this.modified[name] == 'undefined'){
14942 this.modified[name] = this.data[name];
14944 this.data[name] = value;
14945 if(!this.editing && this.store){
14946 this.store.afterEdit(this);
14951 * Get the value of the named field.
14952 * @param {String} name The name of the field to get the value of.
14953 * @return {Object} The value of the field.
14955 get : function(name){
14956 return this.data[name];
14960 beginEdit : function(){
14961 this.editing = true;
14962 this.modified = {};
14966 cancelEdit : function(){
14967 this.editing = false;
14968 delete this.modified;
14972 endEdit : function(){
14973 this.editing = false;
14974 if(this.dirty && this.store){
14975 this.store.afterEdit(this);
14980 * Usually called by the {@link Roo.data.Store} which owns the Record.
14981 * Rejects all changes made to the Record since either creation, or the last commit operation.
14982 * Modified fields are reverted to their original values.
14984 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14985 * of reject operations.
14987 reject : function(){
14988 var m = this.modified;
14990 if(typeof m[n] != "function"){
14991 this.data[n] = m[n];
14994 this.dirty = false;
14995 delete this.modified;
14996 this.editing = false;
14998 this.store.afterReject(this);
15003 * Usually called by the {@link Roo.data.Store} which owns the Record.
15004 * Commits all changes made to the Record since either creation, or the last commit operation.
15006 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15007 * of commit operations.
15009 commit : function(){
15010 this.dirty = false;
15011 delete this.modified;
15012 this.editing = false;
15014 this.store.afterCommit(this);
15019 hasError : function(){
15020 return this.error != null;
15024 clearError : function(){
15029 * Creates a copy of this record.
15030 * @param {String} id (optional) A new record id if you don't want to use this record's id
15033 copy : function(newId) {
15034 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15038 * Ext JS Library 1.1.1
15039 * Copyright(c) 2006-2007, Ext JS, LLC.
15041 * Originally Released Under LGPL - original licence link has changed is not relivant.
15044 * <script type="text/javascript">
15050 * @class Roo.data.Store
15051 * @extends Roo.util.Observable
15052 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15053 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15055 * 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
15056 * has no knowledge of the format of the data returned by the Proxy.<br>
15058 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15059 * instances from the data object. These records are cached and made available through accessor functions.
15061 * Creates a new Store.
15062 * @param {Object} config A config object containing the objects needed for the Store to access data,
15063 * and read the data into Records.
15065 Roo.data.Store = function(config){
15066 this.data = new Roo.util.MixedCollection(false);
15067 this.data.getKey = function(o){
15070 this.baseParams = {};
15072 this.paramNames = {
15077 "multisort" : "_multisort"
15080 if(config && config.data){
15081 this.inlineData = config.data;
15082 delete config.data;
15085 Roo.apply(this, config);
15087 if(this.reader){ // reader passed
15088 this.reader = Roo.factory(this.reader, Roo.data);
15089 this.reader.xmodule = this.xmodule || false;
15090 if(!this.recordType){
15091 this.recordType = this.reader.recordType;
15093 if(this.reader.onMetaChange){
15094 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15098 if(this.recordType){
15099 this.fields = this.recordType.prototype.fields;
15101 this.modified = [];
15105 * @event datachanged
15106 * Fires when the data cache has changed, and a widget which is using this Store
15107 * as a Record cache should refresh its view.
15108 * @param {Store} this
15110 datachanged : true,
15112 * @event metachange
15113 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15114 * @param {Store} this
15115 * @param {Object} meta The JSON metadata
15120 * Fires when Records have been added to the Store
15121 * @param {Store} this
15122 * @param {Roo.data.Record[]} records The array of Records added
15123 * @param {Number} index The index at which the record(s) were added
15128 * Fires when a Record has been removed from the Store
15129 * @param {Store} this
15130 * @param {Roo.data.Record} record The Record that was removed
15131 * @param {Number} index The index at which the record was removed
15136 * Fires when a Record has been updated
15137 * @param {Store} this
15138 * @param {Roo.data.Record} record The Record that was updated
15139 * @param {String} operation The update operation being performed. Value may be one of:
15141 Roo.data.Record.EDIT
15142 Roo.data.Record.REJECT
15143 Roo.data.Record.COMMIT
15149 * Fires when the data cache has been cleared.
15150 * @param {Store} this
15154 * @event beforeload
15155 * Fires before a request is made for a new data object. If the beforeload handler returns false
15156 * the load action will be canceled.
15157 * @param {Store} this
15158 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15162 * @event beforeloadadd
15163 * Fires after a new set of Records has been loaded.
15164 * @param {Store} this
15165 * @param {Roo.data.Record[]} records The Records that were loaded
15166 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15168 beforeloadadd : true,
15171 * Fires after a new set of Records has been loaded, before they are added to the store.
15172 * @param {Store} this
15173 * @param {Roo.data.Record[]} records The Records that were loaded
15174 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15175 * @params {Object} return from reader
15179 * @event loadexception
15180 * Fires if an exception occurs in the Proxy during loading.
15181 * Called with the signature of the Proxy's "loadexception" event.
15182 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15185 * @param {Object} return from JsonData.reader() - success, totalRecords, records
15186 * @param {Object} load options
15187 * @param {Object} jsonData from your request (normally this contains the Exception)
15189 loadexception : true
15193 this.proxy = Roo.factory(this.proxy, Roo.data);
15194 this.proxy.xmodule = this.xmodule || false;
15195 this.relayEvents(this.proxy, ["loadexception"]);
15197 this.sortToggle = {};
15198 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15200 Roo.data.Store.superclass.constructor.call(this);
15202 if(this.inlineData){
15203 this.loadData(this.inlineData);
15204 delete this.inlineData;
15208 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15210 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
15211 * without a remote query - used by combo/forms at present.
15215 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15218 * @cfg {Array} data Inline data to be loaded when the store is initialized.
15221 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
15222 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15225 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15226 * on any HTTP request
15229 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15232 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15236 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15237 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15239 remoteSort : false,
15242 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15243 * loaded or when a record is removed. (defaults to false).
15245 pruneModifiedRecords : false,
15248 lastOptions : null,
15251 * Add Records to the Store and fires the add event.
15252 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15254 add : function(records){
15255 records = [].concat(records);
15256 for(var i = 0, len = records.length; i < len; i++){
15257 records[i].join(this);
15259 var index = this.data.length;
15260 this.data.addAll(records);
15261 this.fireEvent("add", this, records, index);
15265 * Remove a Record from the Store and fires the remove event.
15266 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15268 remove : function(record){
15269 var index = this.data.indexOf(record);
15270 this.data.removeAt(index);
15272 if(this.pruneModifiedRecords){
15273 this.modified.remove(record);
15275 this.fireEvent("remove", this, record, index);
15279 * Remove all Records from the Store and fires the clear event.
15281 removeAll : function(){
15283 if(this.pruneModifiedRecords){
15284 this.modified = [];
15286 this.fireEvent("clear", this);
15290 * Inserts Records to the Store at the given index and fires the add event.
15291 * @param {Number} index The start index at which to insert the passed Records.
15292 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15294 insert : function(index, records){
15295 records = [].concat(records);
15296 for(var i = 0, len = records.length; i < len; i++){
15297 this.data.insert(index, records[i]);
15298 records[i].join(this);
15300 this.fireEvent("add", this, records, index);
15304 * Get the index within the cache of the passed Record.
15305 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15306 * @return {Number} The index of the passed Record. Returns -1 if not found.
15308 indexOf : function(record){
15309 return this.data.indexOf(record);
15313 * Get the index within the cache of the Record with the passed id.
15314 * @param {String} id The id of the Record to find.
15315 * @return {Number} The index of the Record. Returns -1 if not found.
15317 indexOfId : function(id){
15318 return this.data.indexOfKey(id);
15322 * Get the Record with the specified id.
15323 * @param {String} id The id of the Record to find.
15324 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15326 getById : function(id){
15327 return this.data.key(id);
15331 * Get the Record at the specified index.
15332 * @param {Number} index The index of the Record to find.
15333 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15335 getAt : function(index){
15336 return this.data.itemAt(index);
15340 * Returns a range of Records between specified indices.
15341 * @param {Number} startIndex (optional) The starting index (defaults to 0)
15342 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15343 * @return {Roo.data.Record[]} An array of Records
15345 getRange : function(start, end){
15346 return this.data.getRange(start, end);
15350 storeOptions : function(o){
15351 o = Roo.apply({}, o);
15354 this.lastOptions = o;
15358 * Loads the Record cache from the configured Proxy using the configured Reader.
15360 * If using remote paging, then the first load call must specify the <em>start</em>
15361 * and <em>limit</em> properties in the options.params property to establish the initial
15362 * position within the dataset, and the number of Records to cache on each read from the Proxy.
15364 * <strong>It is important to note that for remote data sources, loading is asynchronous,
15365 * and this call will return before the new data has been loaded. Perform any post-processing
15366 * in a callback function, or in a "load" event handler.</strong>
15368 * @param {Object} options An object containing properties which control loading options:<ul>
15369 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15370 * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15373 data : data, // array of key=>value data like JsonReader
15374 total : data.length,
15380 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15381 * passed the following arguments:<ul>
15382 * <li>r : Roo.data.Record[]</li>
15383 * <li>options: Options object from the load call</li>
15384 * <li>success: Boolean success indicator</li></ul></li>
15385 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15386 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15389 load : function(options){
15390 options = options || {};
15391 if(this.fireEvent("beforeload", this, options) !== false){
15392 this.storeOptions(options);
15393 var p = Roo.apply(options.params || {}, this.baseParams);
15394 // if meta was not loaded from remote source.. try requesting it.
15395 if (!this.reader.metaFromRemote) {
15396 p._requestMeta = 1;
15398 if(this.sortInfo && this.remoteSort){
15399 var pn = this.paramNames;
15400 p[pn["sort"]] = this.sortInfo.field;
15401 p[pn["dir"]] = this.sortInfo.direction;
15403 if (this.multiSort) {
15404 var pn = this.paramNames;
15405 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15408 this.proxy.load(p, this.reader, this.loadRecords, this, options);
15413 * Reloads the Record cache from the configured Proxy using the configured Reader and
15414 * the options from the last load operation performed.
15415 * @param {Object} options (optional) An object containing properties which may override the options
15416 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15417 * the most recently used options are reused).
15419 reload : function(options){
15420 this.load(Roo.applyIf(options||{}, this.lastOptions));
15424 // Called as a callback by the Reader during a load operation.
15425 loadRecords : function(o, options, success){
15428 if(success !== false){
15429 this.fireEvent("load", this, [], options, o);
15431 if(options.callback){
15432 options.callback.call(options.scope || this, [], options, false);
15436 // if data returned failure - throw an exception.
15437 if (o.success === false) {
15438 // show a message if no listener is registered.
15439 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15440 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15442 // loadmask wil be hooked into this..
15443 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15446 var r = o.records, t = o.totalRecords || r.length;
15448 this.fireEvent("beforeloadadd", this, r, options, o);
15450 if(!options || options.add !== true){
15451 if(this.pruneModifiedRecords){
15452 this.modified = [];
15454 for(var i = 0, len = r.length; i < len; i++){
15458 this.data = this.snapshot;
15459 delete this.snapshot;
15462 this.data.addAll(r);
15463 this.totalLength = t;
15465 this.fireEvent("datachanged", this);
15467 this.totalLength = Math.max(t, this.data.length+r.length);
15471 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15473 var e = new Roo.data.Record({});
15475 e.set(this.parent.displayField, this.parent.emptyTitle);
15476 e.set(this.parent.valueField, '');
15481 this.fireEvent("load", this, r, options, o);
15482 if(options.callback){
15483 options.callback.call(options.scope || this, r, options, true);
15489 * Loads data from a passed data block. A Reader which understands the format of the data
15490 * must have been configured in the constructor.
15491 * @param {Object} data The data block from which to read the Records. The format of the data expected
15492 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15493 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15495 loadData : function(o, append){
15496 var r = this.reader.readRecords(o);
15497 this.loadRecords(r, {add: append}, true);
15501 * using 'cn' the nested child reader read the child array into it's child stores.
15502 * @param {Object} rec The record with a 'children array
15504 loadDataFromChildren : function(rec)
15506 this.loadData(this.reader.toLoadData(rec));
15511 * Gets the number of cached records.
15513 * <em>If using paging, this may not be the total size of the dataset. If the data object
15514 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15515 * the data set size</em>
15517 getCount : function(){
15518 return this.data.length || 0;
15522 * Gets the total number of records in the dataset as returned by the server.
15524 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15525 * the dataset size</em>
15527 getTotalCount : function(){
15528 return this.totalLength || 0;
15532 * Returns the sort state of the Store as an object with two properties:
15534 field {String} The name of the field by which the Records are sorted
15535 direction {String} The sort order, "ASC" or "DESC"
15538 getSortState : function(){
15539 return this.sortInfo;
15543 applySort : function(){
15544 if(this.sortInfo && !this.remoteSort){
15545 var s = this.sortInfo, f = s.field;
15546 var st = this.fields.get(f).sortType;
15547 var fn = function(r1, r2){
15548 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15549 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15551 this.data.sort(s.direction, fn);
15552 if(this.snapshot && this.snapshot != this.data){
15553 this.snapshot.sort(s.direction, fn);
15559 * Sets the default sort column and order to be used by the next load operation.
15560 * @param {String} fieldName The name of the field to sort by.
15561 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15563 setDefaultSort : function(field, dir){
15564 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15568 * Sort the Records.
15569 * If remote sorting is used, the sort is performed on the server, and the cache is
15570 * reloaded. If local sorting is used, the cache is sorted internally.
15571 * @param {String} fieldName The name of the field to sort by.
15572 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15574 sort : function(fieldName, dir){
15575 var f = this.fields.get(fieldName);
15577 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15579 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15580 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15585 this.sortToggle[f.name] = dir;
15586 this.sortInfo = {field: f.name, direction: dir};
15587 if(!this.remoteSort){
15589 this.fireEvent("datachanged", this);
15591 this.load(this.lastOptions);
15596 * Calls the specified function for each of the Records in the cache.
15597 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15598 * Returning <em>false</em> aborts and exits the iteration.
15599 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15601 each : function(fn, scope){
15602 this.data.each(fn, scope);
15606 * Gets all records modified since the last commit. Modified records are persisted across load operations
15607 * (e.g., during paging).
15608 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15610 getModifiedRecords : function(){
15611 return this.modified;
15615 createFilterFn : function(property, value, anyMatch){
15616 if(!value.exec){ // not a regex
15617 value = String(value);
15618 if(value.length == 0){
15621 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15623 return function(r){
15624 return value.test(r.data[property]);
15629 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15630 * @param {String} property A field on your records
15631 * @param {Number} start The record index to start at (defaults to 0)
15632 * @param {Number} end The last record index to include (defaults to length - 1)
15633 * @return {Number} The sum
15635 sum : function(property, start, end){
15636 var rs = this.data.items, v = 0;
15637 start = start || 0;
15638 end = (end || end === 0) ? end : rs.length-1;
15640 for(var i = start; i <= end; i++){
15641 v += (rs[i].data[property] || 0);
15647 * Filter the records by a specified property.
15648 * @param {String} field A field on your records
15649 * @param {String/RegExp} value Either a string that the field
15650 * should start with or a RegExp to test against the field
15651 * @param {Boolean} anyMatch True to match any part not just the beginning
15653 filter : function(property, value, anyMatch){
15654 var fn = this.createFilterFn(property, value, anyMatch);
15655 return fn ? this.filterBy(fn) : this.clearFilter();
15659 * Filter by a function. The specified function will be called with each
15660 * record in this data source. If the function returns true the record is included,
15661 * otherwise it is filtered.
15662 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15663 * @param {Object} scope (optional) The scope of the function (defaults to this)
15665 filterBy : function(fn, scope){
15666 this.snapshot = this.snapshot || this.data;
15667 this.data = this.queryBy(fn, scope||this);
15668 this.fireEvent("datachanged", this);
15672 * Query the records by a specified property.
15673 * @param {String} field A field on your records
15674 * @param {String/RegExp} value Either a string that the field
15675 * should start with or a RegExp to test against the field
15676 * @param {Boolean} anyMatch True to match any part not just the beginning
15677 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15679 query : function(property, value, anyMatch){
15680 var fn = this.createFilterFn(property, value, anyMatch);
15681 return fn ? this.queryBy(fn) : this.data.clone();
15685 * Query by a function. The specified function will be called with each
15686 * record in this data source. If the function returns true the record is included
15688 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15689 * @param {Object} scope (optional) The scope of the function (defaults to this)
15690 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15692 queryBy : function(fn, scope){
15693 var data = this.snapshot || this.data;
15694 return data.filterBy(fn, scope||this);
15698 * Collects unique values for a particular dataIndex from this store.
15699 * @param {String} dataIndex The property to collect
15700 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15701 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15702 * @return {Array} An array of the unique values
15704 collect : function(dataIndex, allowNull, bypassFilter){
15705 var d = (bypassFilter === true && this.snapshot) ?
15706 this.snapshot.items : this.data.items;
15707 var v, sv, r = [], l = {};
15708 for(var i = 0, len = d.length; i < len; i++){
15709 v = d[i].data[dataIndex];
15711 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15720 * Revert to a view of the Record cache with no filtering applied.
15721 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15723 clearFilter : function(suppressEvent){
15724 if(this.snapshot && this.snapshot != this.data){
15725 this.data = this.snapshot;
15726 delete this.snapshot;
15727 if(suppressEvent !== true){
15728 this.fireEvent("datachanged", this);
15734 afterEdit : function(record){
15735 if(this.modified.indexOf(record) == -1){
15736 this.modified.push(record);
15738 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15742 afterReject : function(record){
15743 this.modified.remove(record);
15744 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15748 afterCommit : function(record){
15749 this.modified.remove(record);
15750 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15754 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15755 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15757 commitChanges : function(){
15758 var m = this.modified.slice(0);
15759 this.modified = [];
15760 for(var i = 0, len = m.length; i < len; i++){
15766 * Cancel outstanding changes on all changed records.
15768 rejectChanges : function(){
15769 var m = this.modified.slice(0);
15770 this.modified = [];
15771 for(var i = 0, len = m.length; i < len; i++){
15776 onMetaChange : function(meta, rtype, o){
15777 this.recordType = rtype;
15778 this.fields = rtype.prototype.fields;
15779 delete this.snapshot;
15780 this.sortInfo = meta.sortInfo || this.sortInfo;
15781 this.modified = [];
15782 this.fireEvent('metachange', this, this.reader.meta);
15785 moveIndex : function(data, type)
15787 var index = this.indexOf(data);
15789 var newIndex = index + type;
15793 this.insert(newIndex, data);
15798 * Ext JS Library 1.1.1
15799 * Copyright(c) 2006-2007, Ext JS, LLC.
15801 * Originally Released Under LGPL - original licence link has changed is not relivant.
15804 * <script type="text/javascript">
15808 * @class Roo.data.SimpleStore
15809 * @extends Roo.data.Store
15810 * Small helper class to make creating Stores from Array data easier.
15811 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15812 * @cfg {Array} fields An array of field definition objects, or field name strings.
15813 * @cfg {Object} an existing reader (eg. copied from another store)
15814 * @cfg {Array} data The multi-dimensional array of data
15815 * @cfg {Roo.data.DataProxy} proxy [not-required]
15816 * @cfg {Roo.data.Reader} reader [not-required]
15818 * @param {Object} config
15820 Roo.data.SimpleStore = function(config)
15822 Roo.data.SimpleStore.superclass.constructor.call(this, {
15824 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15827 Roo.data.Record.create(config.fields)
15829 proxy : new Roo.data.MemoryProxy(config.data)
15833 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15835 * Ext JS Library 1.1.1
15836 * Copyright(c) 2006-2007, Ext JS, LLC.
15838 * Originally Released Under LGPL - original licence link has changed is not relivant.
15841 * <script type="text/javascript">
15846 * @extends Roo.data.Store
15847 * @class Roo.data.JsonStore
15848 * Small helper class to make creating Stores for JSON data easier. <br/>
15850 var store = new Roo.data.JsonStore({
15851 url: 'get-images.php',
15853 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15856 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15857 * JsonReader and HttpProxy (unless inline data is provided).</b>
15858 * @cfg {Array} fields An array of field definition objects, or field name strings.
15860 * @param {Object} config
15862 Roo.data.JsonStore = function(c){
15863 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15864 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15865 reader: new Roo.data.JsonReader(c, c.fields)
15868 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15870 * Ext JS Library 1.1.1
15871 * Copyright(c) 2006-2007, Ext JS, LLC.
15873 * Originally Released Under LGPL - original licence link has changed is not relivant.
15876 * <script type="text/javascript">
15880 Roo.data.Field = function(config){
15881 if(typeof config == "string"){
15882 config = {name: config};
15884 Roo.apply(this, config);
15887 this.type = "auto";
15890 var st = Roo.data.SortTypes;
15891 // named sortTypes are supported, here we look them up
15892 if(typeof this.sortType == "string"){
15893 this.sortType = st[this.sortType];
15896 // set default sortType for strings and dates
15897 if(!this.sortType){
15900 this.sortType = st.asUCString;
15903 this.sortType = st.asDate;
15906 this.sortType = st.none;
15911 var stripRe = /[\$,%]/g;
15913 // prebuilt conversion function for this field, instead of
15914 // switching every time we're reading a value
15916 var cv, dateFormat = this.dateFormat;
15921 cv = function(v){ return v; };
15924 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15928 return v !== undefined && v !== null && v !== '' ?
15929 parseInt(String(v).replace(stripRe, ""), 10) : '';
15934 return v !== undefined && v !== null && v !== '' ?
15935 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15940 cv = function(v){ return v === true || v === "true" || v == 1; };
15947 if(v instanceof Date){
15951 if(dateFormat == "timestamp"){
15952 return new Date(v*1000);
15954 return Date.parseDate(v, dateFormat);
15956 var parsed = Date.parse(v);
15957 return parsed ? new Date(parsed) : null;
15966 Roo.data.Field.prototype = {
15974 * Ext JS Library 1.1.1
15975 * Copyright(c) 2006-2007, Ext JS, LLC.
15977 * Originally Released Under LGPL - original licence link has changed is not relivant.
15980 * <script type="text/javascript">
15983 // Base class for reading structured data from a data source. This class is intended to be
15984 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15987 * @class Roo.data.DataReader
15989 * Base class for reading structured data from a data source. This class is intended to be
15990 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15993 Roo.data.DataReader = function(meta, recordType){
15997 this.recordType = recordType instanceof Array ?
15998 Roo.data.Record.create(recordType) : recordType;
16001 Roo.data.DataReader.prototype = {
16004 readerType : 'Data',
16006 * Create an empty record
16007 * @param {Object} data (optional) - overlay some values
16008 * @return {Roo.data.Record} record created.
16010 newRow : function(d) {
16012 this.recordType.prototype.fields.each(function(c) {
16014 case 'int' : da[c.name] = 0; break;
16015 case 'date' : da[c.name] = new Date(); break;
16016 case 'float' : da[c.name] = 0.0; break;
16017 case 'boolean' : da[c.name] = false; break;
16018 default : da[c.name] = ""; break;
16022 return new this.recordType(Roo.apply(da, d));
16028 * Ext JS Library 1.1.1
16029 * Copyright(c) 2006-2007, Ext JS, LLC.
16031 * Originally Released Under LGPL - original licence link has changed is not relivant.
16034 * <script type="text/javascript">
16038 * @class Roo.data.DataProxy
16039 * @extends Roo.util.Observable
16041 * This class is an abstract base class for implementations which provide retrieval of
16042 * unformatted data objects.<br>
16044 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16045 * (of the appropriate type which knows how to parse the data object) to provide a block of
16046 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16048 * Custom implementations must implement the load method as described in
16049 * {@link Roo.data.HttpProxy#load}.
16051 Roo.data.DataProxy = function(){
16054 * @event beforeload
16055 * Fires before a network request is made to retrieve a data object.
16056 * @param {Object} This DataProxy object.
16057 * @param {Object} params The params parameter to the load function.
16062 * Fires before the load method's callback is called.
16063 * @param {Object} This DataProxy object.
16064 * @param {Object} o The data object.
16065 * @param {Object} arg The callback argument object passed to the load function.
16069 * @event loadexception
16070 * Fires if an Exception occurs during data retrieval.
16071 * @param {Object} This DataProxy object.
16072 * @param {Object} o The data object.
16073 * @param {Object} arg The callback argument object passed to the load function.
16074 * @param {Object} e The Exception.
16076 loadexception : true
16078 Roo.data.DataProxy.superclass.constructor.call(this);
16081 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16084 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16088 * Ext JS Library 1.1.1
16089 * Copyright(c) 2006-2007, Ext JS, LLC.
16091 * Originally Released Under LGPL - original licence link has changed is not relivant.
16094 * <script type="text/javascript">
16097 * @class Roo.data.MemoryProxy
16098 * @extends Roo.data.DataProxy
16099 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16100 * to the Reader when its load method is called.
16102 * @param {Object} config A config object containing the objects needed for the Store to access data,
16104 Roo.data.MemoryProxy = function(config){
16106 if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16107 data = config.data;
16109 Roo.data.MemoryProxy.superclass.constructor.call(this);
16113 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16116 * @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16119 * Load data from the requested source (in this case an in-memory
16120 * data object passed to the constructor), read the data object into
16121 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16122 * process that block using the passed callback.
16123 * @param {Object} params This parameter is not used by the MemoryProxy class.
16124 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16125 * object into a block of Roo.data.Records.
16126 * @param {Function} callback The function into which to pass the block of Roo.data.records.
16127 * The function must be passed <ul>
16128 * <li>The Record block object</li>
16129 * <li>The "arg" argument from the load function</li>
16130 * <li>A boolean success indicator</li>
16132 * @param {Object} scope The scope in which to call the callback
16133 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16135 load : function(params, reader, callback, scope, arg){
16136 params = params || {};
16139 result = reader.readRecords(params.data ? params.data :this.data);
16141 this.fireEvent("loadexception", this, arg, null, e);
16142 callback.call(scope, null, arg, false);
16145 callback.call(scope, result, arg, true);
16149 update : function(params, records){
16154 * Ext JS Library 1.1.1
16155 * Copyright(c) 2006-2007, Ext JS, LLC.
16157 * Originally Released Under LGPL - original licence link has changed is not relivant.
16160 * <script type="text/javascript">
16163 * @class Roo.data.HttpProxy
16164 * @extends Roo.data.DataProxy
16165 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16166 * configured to reference a certain URL.<br><br>
16168 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16169 * from which the running page was served.<br><br>
16171 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16173 * Be aware that to enable the browser to parse an XML document, the server must set
16174 * the Content-Type header in the HTTP response to "text/xml".
16176 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16177 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
16178 * will be used to make the request.
16180 Roo.data.HttpProxy = function(conn){
16181 Roo.data.HttpProxy.superclass.constructor.call(this);
16182 // is conn a conn config or a real conn?
16184 this.useAjax = !conn || !conn.events;
16188 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16189 // thse are take from connection...
16192 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16195 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16196 * extra parameters to each request made by this object. (defaults to undefined)
16199 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16200 * to each request made by this object. (defaults to undefined)
16203 * @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)
16206 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16209 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16215 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16219 * Return the {@link Roo.data.Connection} object being used by this Proxy.
16220 * @return {Connection} The Connection object. This object may be used to subscribe to events on
16221 * a finer-grained basis than the DataProxy events.
16223 getConnection : function(){
16224 return this.useAjax ? Roo.Ajax : this.conn;
16228 * Load data from the configured {@link Roo.data.Connection}, read the data object into
16229 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16230 * process that block using the passed callback.
16231 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16232 * for the request to the remote server.
16233 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16234 * object into a block of Roo.data.Records.
16235 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16236 * The function must be passed <ul>
16237 * <li>The Record block object</li>
16238 * <li>The "arg" argument from the load function</li>
16239 * <li>A boolean success indicator</li>
16241 * @param {Object} scope The scope in which to call the callback
16242 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16244 load : function(params, reader, callback, scope, arg){
16245 if(this.fireEvent("beforeload", this, params) !== false){
16247 params : params || {},
16249 callback : callback,
16254 callback : this.loadResponse,
16258 Roo.applyIf(o, this.conn);
16259 if(this.activeRequest){
16260 Roo.Ajax.abort(this.activeRequest);
16262 this.activeRequest = Roo.Ajax.request(o);
16264 this.conn.request(o);
16267 callback.call(scope||this, null, arg, false);
16272 loadResponse : function(o, success, response){
16273 delete this.activeRequest;
16275 this.fireEvent("loadexception", this, o, response);
16276 o.request.callback.call(o.request.scope, null, o.request.arg, false);
16281 result = o.reader.read(response);
16284 o.raw = { errorMsg : response.responseText };
16285 this.fireEvent("loadexception", this, o, response, e);
16286 o.request.callback.call(o.request.scope, o, o.request.arg, false);
16290 this.fireEvent("load", this, o, o.request.arg);
16291 o.request.callback.call(o.request.scope, result, o.request.arg, true);
16295 update : function(dataSet){
16300 updateResponse : function(dataSet){
16305 * Ext JS Library 1.1.1
16306 * Copyright(c) 2006-2007, Ext JS, LLC.
16308 * Originally Released Under LGPL - original licence link has changed is not relivant.
16311 * <script type="text/javascript">
16315 * @class Roo.data.ScriptTagProxy
16316 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16317 * other than the originating domain of the running page.<br><br>
16319 * <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
16320 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16322 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16323 * source code that is used as the source inside a <script> tag.<br><br>
16325 * In order for the browser to process the returned data, the server must wrap the data object
16326 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16327 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16328 * depending on whether the callback name was passed:
16331 boolean scriptTag = false;
16332 String cb = request.getParameter("callback");
16335 response.setContentType("text/javascript");
16337 response.setContentType("application/x-json");
16339 Writer out = response.getWriter();
16341 out.write(cb + "(");
16343 out.print(dataBlock.toJsonString());
16350 * @param {Object} config A configuration object.
16352 Roo.data.ScriptTagProxy = function(config){
16353 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16354 Roo.apply(this, config);
16355 this.head = document.getElementsByTagName("head")[0];
16358 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16360 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16362 * @cfg {String} url The URL from which to request the data object.
16365 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16369 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16370 * the server the name of the callback function set up by the load call to process the returned data object.
16371 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16372 * javascript output which calls this named function passing the data object as its only parameter.
16374 callbackParam : "callback",
16376 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16377 * name to the request.
16382 * Load data from the configured URL, read the data object into
16383 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16384 * process that block using the passed callback.
16385 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16386 * for the request to the remote server.
16387 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16388 * object into a block of Roo.data.Records.
16389 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16390 * The function must be passed <ul>
16391 * <li>The Record block object</li>
16392 * <li>The "arg" argument from the load function</li>
16393 * <li>A boolean success indicator</li>
16395 * @param {Object} scope The scope in which to call the callback
16396 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16398 load : function(params, reader, callback, scope, arg){
16399 if(this.fireEvent("beforeload", this, params) !== false){
16401 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16403 var url = this.url;
16404 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16406 url += "&_dc=" + (new Date().getTime());
16408 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16411 cb : "stcCallback"+transId,
16412 scriptId : "stcScript"+transId,
16416 callback : callback,
16422 window[trans.cb] = function(o){
16423 conn.handleResponse(o, trans);
16426 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16428 if(this.autoAbort !== false){
16432 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16434 var script = document.createElement("script");
16435 script.setAttribute("src", url);
16436 script.setAttribute("type", "text/javascript");
16437 script.setAttribute("id", trans.scriptId);
16438 this.head.appendChild(script);
16440 this.trans = trans;
16442 callback.call(scope||this, null, arg, false);
16447 isLoading : function(){
16448 return this.trans ? true : false;
16452 * Abort the current server request.
16454 abort : function(){
16455 if(this.isLoading()){
16456 this.destroyTrans(this.trans);
16461 destroyTrans : function(trans, isLoaded){
16462 this.head.removeChild(document.getElementById(trans.scriptId));
16463 clearTimeout(trans.timeoutId);
16465 window[trans.cb] = undefined;
16467 delete window[trans.cb];
16470 // if hasn't been loaded, wait for load to remove it to prevent script error
16471 window[trans.cb] = function(){
16472 window[trans.cb] = undefined;
16474 delete window[trans.cb];
16481 handleResponse : function(o, trans){
16482 this.trans = false;
16483 this.destroyTrans(trans, true);
16486 result = trans.reader.readRecords(o);
16488 this.fireEvent("loadexception", this, o, trans.arg, e);
16489 trans.callback.call(trans.scope||window, null, trans.arg, false);
16492 this.fireEvent("load", this, o, trans.arg);
16493 trans.callback.call(trans.scope||window, result, trans.arg, true);
16497 handleFailure : function(trans){
16498 this.trans = false;
16499 this.destroyTrans(trans, false);
16500 this.fireEvent("loadexception", this, null, trans.arg);
16501 trans.callback.call(trans.scope||window, null, trans.arg, false);
16505 * Ext JS Library 1.1.1
16506 * Copyright(c) 2006-2007, Ext JS, LLC.
16508 * Originally Released Under LGPL - original licence link has changed is not relivant.
16511 * <script type="text/javascript">
16515 * @class Roo.data.JsonReader
16516 * @extends Roo.data.DataReader
16517 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16518 * based on mappings in a provided Roo.data.Record constructor.
16520 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16521 * in the reply previously.
16526 var RecordDef = Roo.data.Record.create([
16527 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16528 {name: 'occupation'} // This field will use "occupation" as the mapping.
16530 var myReader = new Roo.data.JsonReader({
16531 totalProperty: "results", // The property which contains the total dataset size (optional)
16532 root: "rows", // The property which contains an Array of row objects
16533 id: "id" // The property within each row object that provides an ID for the record (optional)
16537 * This would consume a JSON file like this:
16539 { 'results': 2, 'rows': [
16540 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16541 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16544 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16545 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16546 * paged from the remote server.
16547 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16548 * @cfg {String} root name of the property which contains the Array of row objects.
16549 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16550 * @cfg {Array} fields Array of field definition objects
16552 * Create a new JsonReader
16553 * @param {Object} meta Metadata configuration options
16554 * @param {Object} recordType Either an Array of field definition objects,
16555 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16557 Roo.data.JsonReader = function(meta, recordType){
16560 // set some defaults:
16561 Roo.applyIf(meta, {
16562 totalProperty: 'total',
16563 successProperty : 'success',
16568 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16570 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16572 readerType : 'Json',
16575 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16576 * Used by Store query builder to append _requestMeta to params.
16579 metaFromRemote : false,
16581 * This method is only used by a DataProxy which has retrieved data from a remote server.
16582 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16583 * @return {Object} data A data block which is used by an Roo.data.Store object as
16584 * a cache of Roo.data.Records.
16586 read : function(response){
16587 var json = response.responseText;
16589 var o = /* eval:var:o */ eval("("+json+")");
16591 throw {message: "JsonReader.read: Json object not found"};
16597 this.metaFromRemote = true;
16598 this.meta = o.metaData;
16599 this.recordType = Roo.data.Record.create(o.metaData.fields);
16600 this.onMetaChange(this.meta, this.recordType, o);
16602 return this.readRecords(o);
16605 // private function a store will implement
16606 onMetaChange : function(meta, recordType, o){
16613 simpleAccess: function(obj, subsc) {
16620 getJsonAccessor: function(){
16622 return function(expr) {
16624 return(re.test(expr))
16625 ? new Function("obj", "return obj." + expr)
16630 return Roo.emptyFn;
16635 * Create a data block containing Roo.data.Records from an XML document.
16636 * @param {Object} o An object which contains an Array of row objects in the property specified
16637 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16638 * which contains the total size of the dataset.
16639 * @return {Object} data A data block which is used by an Roo.data.Store object as
16640 * a cache of Roo.data.Records.
16642 readRecords : function(o){
16644 * After any data loads, the raw JSON data is available for further custom processing.
16648 var s = this.meta, Record = this.recordType,
16649 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16651 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16653 if(s.totalProperty) {
16654 this.getTotal = this.getJsonAccessor(s.totalProperty);
16656 if(s.successProperty) {
16657 this.getSuccess = this.getJsonAccessor(s.successProperty);
16659 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16661 var g = this.getJsonAccessor(s.id);
16662 this.getId = function(rec) {
16664 return (r === undefined || r === "") ? null : r;
16667 this.getId = function(){return null;};
16670 for(var jj = 0; jj < fl; jj++){
16672 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16673 this.ef[jj] = this.getJsonAccessor(map);
16677 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16678 if(s.totalProperty){
16679 var vt = parseInt(this.getTotal(o), 10);
16684 if(s.successProperty){
16685 var vs = this.getSuccess(o);
16686 if(vs === false || vs === 'false'){
16691 for(var i = 0; i < c; i++){
16694 var id = this.getId(n);
16695 for(var j = 0; j < fl; j++){
16697 var v = this.ef[j](n);
16699 Roo.log('missing convert for ' + f.name);
16703 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16707 raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16713 var record = new Record(values, id);
16715 records[i] = record;
16721 totalRecords : totalRecords
16724 // used when loading children.. @see loadDataFromChildren
16725 toLoadData: function(rec)
16727 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16728 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16729 return { data : data, total : data.length };
16734 * Ext JS Library 1.1.1
16735 * Copyright(c) 2006-2007, Ext JS, LLC.
16737 * Originally Released Under LGPL - original licence link has changed is not relivant.
16740 * <script type="text/javascript">
16744 * @class Roo.data.ArrayReader
16745 * @extends Roo.data.DataReader
16746 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16747 * Each element of that Array represents a row of data fields. The
16748 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16749 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16753 var RecordDef = Roo.data.Record.create([
16754 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16755 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16757 var myReader = new Roo.data.ArrayReader({
16758 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16762 * This would consume an Array like this:
16764 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16768 * Create a new JsonReader
16769 * @param {Object} meta Metadata configuration options.
16770 * @param {Object|Array} recordType Either an Array of field definition objects
16772 * @cfg {Array} fields Array of field definition objects
16773 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16774 * as specified to {@link Roo.data.Record#create},
16775 * or an {@link Roo.data.Record} object
16778 * created using {@link Roo.data.Record#create}.
16780 Roo.data.ArrayReader = function(meta, recordType)
16782 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16785 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16788 * Create a data block containing Roo.data.Records from an XML document.
16789 * @param {Object} o An Array of row objects which represents the dataset.
16790 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16791 * a cache of Roo.data.Records.
16793 readRecords : function(o)
16795 var sid = this.meta ? this.meta.id : null;
16796 var recordType = this.recordType, fields = recordType.prototype.fields;
16799 for(var i = 0; i < root.length; i++){
16802 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16803 for(var j = 0, jlen = fields.length; j < jlen; j++){
16804 var f = fields.items[j];
16805 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16806 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16808 values[f.name] = v;
16810 var record = new recordType(values, id);
16812 records[records.length] = record;
16816 totalRecords : records.length
16819 // used when loading children.. @see loadDataFromChildren
16820 toLoadData: function(rec)
16822 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16823 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16834 * @class Roo.bootstrap.form.ComboBox
16835 * @extends Roo.bootstrap.form.TriggerField
16836 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16837 * @cfg {Boolean} append (true|false) default false
16838 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16839 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16840 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16841 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16842 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16843 * @cfg {Boolean} animate default true
16844 * @cfg {Boolean} emptyResultText only for touch device
16845 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16846 * @cfg {String} emptyTitle default ''
16847 * @cfg {Number} width fixed with? experimental
16849 * Create a new ComboBox.
16850 * @param {Object} config Configuration options
16852 Roo.bootstrap.form.ComboBox = function(config){
16853 Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16857 * Fires when the dropdown list is expanded
16858 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16863 * Fires when the dropdown list is collapsed
16864 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16868 * @event beforeselect
16869 * Fires before a list item is selected. Return false to cancel the selection.
16870 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16871 * @param {Roo.data.Record} record The data record returned from the underlying store
16872 * @param {Number} index The index of the selected item in the dropdown list
16874 'beforeselect' : true,
16877 * Fires when a list item is selected
16878 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16879 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16880 * @param {Number} index The index of the selected item in the dropdown list
16884 * @event beforequery
16885 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16886 * The event object passed has these properties:
16887 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16888 * @param {String} query The query
16889 * @param {Boolean} forceAll true to force "all" query
16890 * @param {Boolean} cancel true to cancel the query
16891 * @param {Object} e The query event object
16893 'beforequery': true,
16896 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16897 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16902 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16903 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16904 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16909 * Fires when the remove value from the combobox array
16910 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16914 * @event afterremove
16915 * Fires when the remove value from the combobox array
16916 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16918 'afterremove' : true,
16920 * @event specialfilter
16921 * Fires when specialfilter
16922 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16924 'specialfilter' : true,
16927 * Fires when tick the element
16928 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16932 * @event touchviewdisplay
16933 * Fires when touch view require special display (default is using displayField)
16934 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16935 * @param {Object} cfg set html .
16937 'touchviewdisplay' : true
16942 this.tickItems = [];
16944 this.selectedIndex = -1;
16945 if(this.mode == 'local'){
16946 if(config.queryDelay === undefined){
16947 this.queryDelay = 10;
16949 if(config.minChars === undefined){
16955 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16958 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16959 * rendering into an Roo.Editor, defaults to false)
16962 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16963 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16966 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16969 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16970 * the dropdown list (defaults to undefined, with no header element)
16974 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16978 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16980 listWidth: undefined,
16982 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16983 * mode = 'remote' or 'text' if mode = 'local')
16985 displayField: undefined,
16988 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16989 * mode = 'remote' or 'value' if mode = 'local').
16990 * Note: use of a valueField requires the user make a selection
16991 * in order for a value to be mapped.
16993 valueField: undefined,
16995 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
17000 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
17001 * field's data value (defaults to the underlying DOM element's name)
17003 hiddenName: undefined,
17005 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17009 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17011 selectedClass: 'active',
17014 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17018 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17019 * anchor positions (defaults to 'tl-bl')
17021 listAlign: 'tl-bl?',
17023 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17027 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
17028 * query specified by the allQuery config option (defaults to 'query')
17030 triggerAction: 'query',
17032 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17033 * (defaults to 4, does not apply if editable = false)
17037 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17038 * delay (typeAheadDelay) if it matches a known value (defaults to false)
17042 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17043 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17047 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17048 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
17052 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
17053 * when editable = true (defaults to false)
17055 selectOnFocus:false,
17057 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17059 queryParam: 'query',
17061 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
17062 * when mode = 'remote' (defaults to 'Loading...')
17064 loadingText: 'Loading...',
17066 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17070 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17074 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17075 * traditional select (defaults to true)
17079 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17083 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17087 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17088 * listWidth has a higher value)
17092 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17093 * allow the user to set arbitrary text into the field (defaults to false)
17095 forceSelection:false,
17097 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17098 * if typeAhead = true (defaults to 250)
17100 typeAheadDelay : 250,
17102 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17103 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17105 valueNotFoundText : undefined,
17107 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17109 blockFocus : false,
17112 * @cfg {Boolean} disableClear Disable showing of clear button.
17114 disableClear : false,
17116 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
17118 alwaysQuery : false,
17121 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
17126 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17128 invalidClass : "has-warning",
17131 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17133 validClass : "has-success",
17136 * @cfg {Boolean} specialFilter (true|false) special filter default false
17138 specialFilter : false,
17141 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17143 mobileTouchView : true,
17146 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17148 useNativeIOS : false,
17151 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17153 mobile_restrict_height : false,
17155 ios_options : false,
17167 btnPosition : 'right',
17168 triggerList : true,
17169 showToggleBtn : true,
17171 emptyResultText: 'Empty',
17172 triggerText : 'Select',
17176 // element that contains real text value.. (when hidden is used..)
17178 getAutoCreate : function()
17183 * Render classic select for iso
17186 if(Roo.isIOS && this.useNativeIOS){
17187 cfg = this.getAutoCreateNativeIOS();
17195 if(Roo.isTouch && this.mobileTouchView){
17196 cfg = this.getAutoCreateTouchView();
17203 if(!this.tickable){
17204 cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17209 * ComboBox with tickable selections
17212 var align = this.labelAlign || this.parentLabelAlign();
17215 cls : 'form-group roo-combobox-tickable' //input-group
17218 var btn_text_select = '';
17219 var btn_text_done = '';
17220 var btn_text_cancel = '';
17222 if (this.btn_text_show) {
17223 btn_text_select = 'Select';
17224 btn_text_done = 'Done';
17225 btn_text_cancel = 'Cancel';
17230 cls : 'tickable-buttons',
17235 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17236 //html : this.triggerText
17237 html: btn_text_select
17243 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17245 html: btn_text_done
17251 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17253 html: btn_text_cancel
17259 buttons.cn.unshift({
17261 cls: 'roo-select2-search-field-input'
17267 Roo.each(buttons.cn, function(c){
17269 c.cls += ' btn-' + _this.size;
17272 if (_this.disabled) {
17279 style : 'display: contents',
17284 cls: 'form-hidden-field'
17288 cls: 'roo-select2-choices',
17292 cls: 'roo-select2-search-field',
17303 cls: 'roo-select2-container input-group roo-select2-container-multi',
17309 // cls: 'typeahead typeahead-long dropdown-menu',
17310 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
17315 if(this.hasFeedback && !this.allowBlank){
17319 cls: 'glyphicon form-control-feedback'
17322 combobox.cn.push(feedback);
17329 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17330 tooltip : 'This field is required'
17332 if (Roo.bootstrap.version == 4) {
17335 style : 'display:none'
17338 if (align ==='left' && this.fieldLabel.length) {
17340 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
17347 cls : 'control-label col-form-label',
17348 html : this.fieldLabel
17360 var labelCfg = cfg.cn[1];
17361 var contentCfg = cfg.cn[2];
17364 if(this.indicatorpos == 'right'){
17370 cls : 'control-label col-form-label',
17374 html : this.fieldLabel
17390 labelCfg = cfg.cn[0];
17391 contentCfg = cfg.cn[1];
17395 if(this.labelWidth > 12){
17396 labelCfg.style = "width: " + this.labelWidth + 'px';
17398 if(this.width * 1 > 0){
17399 contentCfg.style = "width: " + this.width + 'px';
17401 if(this.labelWidth < 13 && this.labelmd == 0){
17402 this.labelmd = this.labelWidth;
17405 if(this.labellg > 0){
17406 labelCfg.cls += ' col-lg-' + this.labellg;
17407 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17410 if(this.labelmd > 0){
17411 labelCfg.cls += ' col-md-' + this.labelmd;
17412 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17415 if(this.labelsm > 0){
17416 labelCfg.cls += ' col-sm-' + this.labelsm;
17417 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17420 if(this.labelxs > 0){
17421 labelCfg.cls += ' col-xs-' + this.labelxs;
17422 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17426 } else if ( this.fieldLabel.length) {
17427 // Roo.log(" label");
17432 //cls : 'input-group-addon',
17433 html : this.fieldLabel
17438 if(this.indicatorpos == 'right'){
17442 //cls : 'input-group-addon',
17443 html : this.fieldLabel
17453 // Roo.log(" no label && no align");
17460 ['xs','sm','md','lg'].map(function(size){
17461 if (settings[size]) {
17462 cfg.cls += ' col-' + size + '-' + settings[size];
17470 _initEventsCalled : false,
17473 initEvents: function()
17475 if (this._initEventsCalled) { // as we call render... prevent looping...
17478 this._initEventsCalled = true;
17481 throw "can not find store for combo";
17484 this.indicator = this.indicatorEl();
17486 this.store = Roo.factory(this.store, Roo.data);
17487 this.store.parent = this;
17489 // if we are building from html. then this element is so complex, that we can not really
17490 // use the rendered HTML.
17491 // so we have to trash and replace the previous code.
17492 if (Roo.XComponent.build_from_html) {
17493 // remove this element....
17494 var e = this.el.dom, k=0;
17495 while (e ) { e = e.previousSibling; ++k;}
17500 this.rendered = false;
17502 this.render(this.parent().getChildContainer(true), k);
17505 if(Roo.isIOS && this.useNativeIOS){
17506 this.initIOSView();
17514 if(Roo.isTouch && this.mobileTouchView){
17515 this.initTouchView();
17520 this.initTickableEvents();
17524 Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17526 if(this.hiddenName){
17528 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17530 this.hiddenField.dom.value =
17531 this.hiddenValue !== undefined ? this.hiddenValue :
17532 this.value !== undefined ? this.value : '';
17534 // prevent input submission
17535 this.el.dom.removeAttribute('name');
17536 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17541 // this.el.dom.setAttribute('autocomplete', 'off');
17544 var cls = 'x-combo-list';
17546 //this.list = new Roo.Layer({
17547 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17553 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17554 _this.list.setWidth(lw);
17557 this.list.on('mouseover', this.onViewOver, this);
17558 this.list.on('mousemove', this.onViewMove, this);
17559 this.list.on('scroll', this.onViewScroll, this);
17562 this.list.swallowEvent('mousewheel');
17563 this.assetHeight = 0;
17566 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17567 this.assetHeight += this.header.getHeight();
17570 this.innerList = this.list.createChild({cls:cls+'-inner'});
17571 this.innerList.on('mouseover', this.onViewOver, this);
17572 this.innerList.on('mousemove', this.onViewMove, this);
17573 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17575 if(this.allowBlank && !this.pageSize && !this.disableClear){
17576 this.footer = this.list.createChild({cls:cls+'-ft'});
17577 this.pageTb = new Roo.Toolbar(this.footer);
17581 this.footer = this.list.createChild({cls:cls+'-ft'});
17582 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17583 {pageSize: this.pageSize});
17587 if (this.pageTb && this.allowBlank && !this.disableClear) {
17589 this.pageTb.add(new Roo.Toolbar.Fill(), {
17590 cls: 'x-btn-icon x-btn-clear',
17592 handler: function()
17595 _this.clearValue();
17596 _this.onSelect(false, -1);
17601 this.assetHeight += this.footer.getHeight();
17606 this.tpl = Roo.bootstrap.version == 4 ?
17607 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17608 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17611 this.view = new Roo.View(this.list, this.tpl, {
17612 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17614 //this.view.wrapEl.setDisplayed(false);
17615 this.view.on('click', this.onViewClick, this);
17618 this.store.on('beforeload', this.onBeforeLoad, this);
17619 this.store.on('load', this.onLoad, this);
17620 this.store.on('loadexception', this.onLoadException, this);
17622 if(this.resizable){
17623 this.resizer = new Roo.Resizable(this.list, {
17624 pinned:true, handles:'se'
17626 this.resizer.on('resize', function(r, w, h){
17627 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17628 this.listWidth = w;
17629 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17630 this.restrictHeight();
17632 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17635 if(!this.editable){
17636 this.editable = true;
17637 this.setEditable(false);
17642 if (typeof(this.events.add.listeners) != 'undefined') {
17644 this.addicon = this.wrap.createChild(
17645 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17647 this.addicon.on('click', function(e) {
17648 this.fireEvent('add', this);
17651 if (typeof(this.events.edit.listeners) != 'undefined') {
17653 this.editicon = this.wrap.createChild(
17654 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17655 if (this.addicon) {
17656 this.editicon.setStyle('margin-left', '40px');
17658 this.editicon.on('click', function(e) {
17660 // we fire even if inothing is selected..
17661 this.fireEvent('edit', this, this.lastData );
17667 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17668 "up" : function(e){
17669 this.inKeyMode = true;
17673 "down" : function(e){
17674 if(!this.isExpanded()){
17675 this.onTriggerClick();
17677 this.inKeyMode = true;
17682 "enter" : function(e){
17683 // this.onViewClick();
17687 if(this.fireEvent("specialkey", this, e)){
17688 this.onViewClick(false);
17694 "esc" : function(e){
17698 "tab" : function(e){
17701 if(this.fireEvent("specialkey", this, e)){
17702 this.onViewClick(false);
17710 doRelay : function(foo, bar, hname){
17711 if(hname == 'down' || this.scope.isExpanded()){
17712 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17721 this.queryDelay = Math.max(this.queryDelay || 10,
17722 this.mode == 'local' ? 10 : 250);
17725 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17727 if(this.typeAhead){
17728 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17730 if(this.editable !== false){
17731 this.inputEl().on("keyup", this.onKeyUp, this);
17733 if(this.forceSelection){
17734 this.inputEl().on('blur', this.doForce, this);
17738 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17739 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17743 initTickableEvents: function()
17747 if(this.hiddenName){
17749 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17751 this.hiddenField.dom.value =
17752 this.hiddenValue !== undefined ? this.hiddenValue :
17753 this.value !== undefined ? this.value : '';
17755 // prevent input submission
17756 this.el.dom.removeAttribute('name');
17757 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17762 // this.list = this.el.select('ul.dropdown-menu',true).first();
17764 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17765 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17766 if(this.triggerList){
17767 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17770 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17771 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17773 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17774 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17776 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17777 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17779 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17780 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17781 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17784 this.cancelBtn.hide();
17789 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17790 _this.list.setWidth(lw);
17793 this.list.on('mouseover', this.onViewOver, this);
17794 this.list.on('mousemove', this.onViewMove, this);
17796 this.list.on('scroll', this.onViewScroll, this);
17799 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17800 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17803 this.view = new Roo.View(this.list, this.tpl, {
17808 selectedClass: this.selectedClass
17811 //this.view.wrapEl.setDisplayed(false);
17812 this.view.on('click', this.onViewClick, this);
17816 this.store.on('beforeload', this.onBeforeLoad, this);
17817 this.store.on('load', this.onLoad, this);
17818 this.store.on('loadexception', this.onLoadException, this);
17821 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17822 "up" : function(e){
17823 this.inKeyMode = true;
17827 "down" : function(e){
17828 this.inKeyMode = true;
17832 "enter" : function(e){
17833 if(this.fireEvent("specialkey", this, e)){
17834 this.onViewClick(false);
17840 "esc" : function(e){
17841 this.onTickableFooterButtonClick(e, false, false);
17844 "tab" : function(e){
17845 this.fireEvent("specialkey", this, e);
17847 this.onTickableFooterButtonClick(e, false, false);
17854 doRelay : function(e, fn, key){
17855 if(this.scope.isExpanded()){
17856 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17865 this.queryDelay = Math.max(this.queryDelay || 10,
17866 this.mode == 'local' ? 10 : 250);
17869 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17871 if(this.typeAhead){
17872 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17875 if(this.editable !== false){
17876 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17879 this.indicator = this.indicatorEl();
17881 if(this.indicator){
17882 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17883 this.indicator.hide();
17888 onDestroy : function(){
17890 this.view.setStore(null);
17891 this.view.el.removeAllListeners();
17892 this.view.el.remove();
17893 this.view.purgeListeners();
17896 this.list.dom.innerHTML = '';
17900 this.store.un('beforeload', this.onBeforeLoad, this);
17901 this.store.un('load', this.onLoad, this);
17902 this.store.un('loadexception', this.onLoadException, this);
17904 Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17908 fireKey : function(e){
17909 if(e.isNavKeyPress() && !this.list.isVisible()){
17910 this.fireEvent("specialkey", this, e);
17915 onResize: function(w, h)
17919 // Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17921 // if(typeof w != 'number'){
17922 // // we do not handle it!?!?
17925 // var tw = this.trigger.getWidth();
17926 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17927 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17929 // this.inputEl().setWidth( this.adjustWidth('input', x));
17931 // //this.trigger.setStyle('left', x+'px');
17933 // if(this.list && this.listWidth === undefined){
17934 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17935 // this.list.setWidth(lw);
17936 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17944 * Allow or prevent the user from directly editing the field text. If false is passed,
17945 * the user will only be able to select from the items defined in the dropdown list. This method
17946 * is the runtime equivalent of setting the 'editable' config option at config time.
17947 * @param {Boolean} value True to allow the user to directly edit the field text
17949 setEditable : function(value){
17950 if(value == this.editable){
17953 this.editable = value;
17955 this.inputEl().dom.setAttribute('readOnly', true);
17956 this.inputEl().on('mousedown', this.onTriggerClick, this);
17957 this.inputEl().addClass('x-combo-noedit');
17959 this.inputEl().dom.removeAttribute('readOnly');
17960 this.inputEl().un('mousedown', this.onTriggerClick, this);
17961 this.inputEl().removeClass('x-combo-noedit');
17967 onBeforeLoad : function(combo,opts){
17968 if(!this.hasFocus){
17972 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17974 this.restrictHeight();
17975 this.selectedIndex = -1;
17979 onLoad : function(){
17981 this.hasQuery = false;
17983 if(!this.hasFocus){
17987 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17988 this.loading.hide();
17991 if(this.store.getCount() > 0){
17994 this.restrictHeight();
17995 if(this.lastQuery == this.allQuery){
17996 if(this.editable && !this.tickable){
17997 this.inputEl().dom.select();
18001 !this.selectByValue(this.value, true) &&
18004 !this.store.lastOptions ||
18005 typeof(this.store.lastOptions.add) == 'undefined' ||
18006 this.store.lastOptions.add != true
18009 this.select(0, true);
18012 if(this.autoFocus){
18015 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18016 this.taTask.delay(this.typeAheadDelay);
18020 this.onEmptyResults();
18026 onLoadException : function()
18028 this.hasQuery = false;
18030 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18031 this.loading.hide();
18034 if(this.tickable && this.editable){
18039 // only causes errors at present
18040 //Roo.log(this.store.reader.jsonData);
18041 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18043 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18049 onTypeAhead : function(){
18050 if(this.store.getCount() > 0){
18051 var r = this.store.getAt(0);
18052 var newValue = r.data[this.displayField];
18053 var len = newValue.length;
18054 var selStart = this.getRawValue().length;
18056 if(selStart != len){
18057 this.setRawValue(newValue);
18058 this.selectText(selStart, newValue.length);
18064 onSelect : function(record, index){
18066 if(this.fireEvent('beforeselect', this, record, index) !== false){
18068 this.setFromData(index > -1 ? record.data : false);
18071 this.fireEvent('select', this, record, index);
18076 * Returns the currently selected field value or empty string if no value is set.
18077 * @return {String} value The selected value
18079 getValue : function()
18081 if(Roo.isIOS && this.useNativeIOS){
18082 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18086 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18089 if(this.valueField){
18090 return typeof this.value != 'undefined' ? this.value : '';
18092 return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18096 getRawValue : function()
18098 if(Roo.isIOS && this.useNativeIOS){
18099 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18102 var v = this.inputEl().getValue();
18108 * Clears any text/value currently set in the field
18110 clearValue : function(){
18112 if(this.hiddenField){
18113 this.hiddenField.dom.value = '';
18116 this.setRawValue('');
18117 this.lastSelectionText = '';
18118 this.lastData = false;
18120 var close = this.closeTriggerEl();
18131 * Sets the specified value into the field. If the value finds a match, the corresponding record text
18132 * will be displayed in the field. If the value does not match the data value of an existing item,
18133 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18134 * Otherwise the field will be blank (although the value will still be set).
18135 * @param {String} value The value to match
18137 setValue : function(v)
18139 if(Roo.isIOS && this.useNativeIOS){
18140 this.setIOSValue(v);
18150 if(this.valueField){
18151 var r = this.findRecord(this.valueField, v);
18153 text = r.data[this.displayField];
18154 }else if(this.valueNotFoundText !== undefined){
18155 text = this.valueNotFoundText;
18158 this.lastSelectionText = text;
18159 if(this.hiddenField){
18160 this.hiddenField.dom.value = v;
18162 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18165 var close = this.closeTriggerEl();
18168 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18174 * @property {Object} the last set data for the element
18179 * Sets the value of the field based on a object which is related to the record format for the store.
18180 * @param {Object} value the value to set as. or false on reset?
18182 setFromData : function(o){
18189 var dv = ''; // display value
18190 var vv = ''; // value value..
18192 if (this.displayField) {
18193 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18195 // this is an error condition!!!
18196 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18199 if(this.valueField){
18200 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18203 var close = this.closeTriggerEl();
18206 if(dv.length || vv * 1 > 0){
18208 this.blockFocus=true;
18214 if(this.hiddenField){
18215 this.hiddenField.dom.value = vv;
18217 this.lastSelectionText = dv;
18218 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18222 // no hidden field.. - we store the value in 'value', but still display
18223 // display field!!!!
18224 this.lastSelectionText = dv;
18225 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18232 reset : function(){
18233 // overridden so that last data is reset..
18240 this.setValue(this.originalValue);
18241 //this.clearInvalid();
18242 this.lastData = false;
18244 this.view.clearSelections();
18250 findRecord : function(prop, value){
18252 if(this.store.getCount() > 0){
18253 this.store.each(function(r){
18254 if(r.data[prop] == value){
18264 getName: function()
18266 // returns hidden if it's set..
18267 if (!this.rendered) {return ''};
18268 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
18272 onViewMove : function(e, t){
18273 this.inKeyMode = false;
18277 onViewOver : function(e, t){
18278 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18281 var item = this.view.findItemFromChild(t);
18284 var index = this.view.indexOf(item);
18285 this.select(index, false);
18290 onViewClick : function(view, doFocus, el, e)
18292 var index = this.view.getSelectedIndexes()[0];
18294 var r = this.store.getAt(index);
18298 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18305 Roo.each(this.tickItems, function(v,k){
18307 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18309 _this.tickItems.splice(k, 1);
18311 if(typeof(e) == 'undefined' && view == false){
18312 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18324 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18325 this.tickItems.push(r.data);
18328 if(typeof(e) == 'undefined' && view == false){
18329 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18336 this.onSelect(r, index);
18338 if(doFocus !== false && !this.blockFocus){
18339 this.inputEl().focus();
18344 restrictHeight : function(){
18345 //this.innerList.dom.style.height = '';
18346 //var inner = this.innerList.dom;
18347 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18348 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18349 //this.list.beginUpdate();
18350 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18351 this.list.alignTo(this.inputEl(), this.listAlign);
18352 this.list.alignTo(this.inputEl(), this.listAlign);
18353 //this.list.endUpdate();
18357 onEmptyResults : function(){
18359 if(this.tickable && this.editable){
18360 this.hasFocus = false;
18361 this.restrictHeight();
18369 * Returns true if the dropdown list is expanded, else false.
18371 isExpanded : function(){
18372 return this.list.isVisible();
18376 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18377 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18378 * @param {String} value The data value of the item to select
18379 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18380 * selected item if it is not currently in view (defaults to true)
18381 * @return {Boolean} True if the value matched an item in the list, else false
18383 selectByValue : function(v, scrollIntoView){
18384 if(v !== undefined && v !== null){
18385 var r = this.findRecord(this.valueField || this.displayField, v);
18387 this.select(this.store.indexOf(r), scrollIntoView);
18395 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18396 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18397 * @param {Number} index The zero-based index of the list item to select
18398 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18399 * selected item if it is not currently in view (defaults to true)
18401 select : function(index, scrollIntoView){
18402 this.selectedIndex = index;
18403 this.view.select(index);
18404 if(scrollIntoView !== false){
18405 var el = this.view.getNode(index);
18407 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18410 this.list.scrollChildIntoView(el, false);
18416 selectNext : function(){
18417 var ct = this.store.getCount();
18419 if(this.selectedIndex == -1){
18421 }else if(this.selectedIndex < ct-1){
18422 this.select(this.selectedIndex+1);
18428 selectPrev : function(){
18429 var ct = this.store.getCount();
18431 if(this.selectedIndex == -1){
18433 }else if(this.selectedIndex != 0){
18434 this.select(this.selectedIndex-1);
18440 onKeyUp : function(e){
18441 if(this.editable !== false && !e.isSpecialKey()){
18442 this.lastKey = e.getKey();
18443 this.dqTask.delay(this.queryDelay);
18448 validateBlur : function(){
18449 return !this.list || !this.list.isVisible();
18453 initQuery : function(){
18455 var v = this.getRawValue();
18457 if(this.tickable && this.editable){
18458 v = this.tickableInputEl().getValue();
18465 doForce : function(){
18466 if(this.inputEl().dom.value.length > 0){
18467 this.inputEl().dom.value =
18468 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18474 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
18475 * query allowing the query action to be canceled if needed.
18476 * @param {String} query The SQL query to execute
18477 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18478 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
18479 * saved in the current store (defaults to false)
18481 doQuery : function(q, forceAll){
18483 if(q === undefined || q === null){
18488 forceAll: forceAll,
18492 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18497 forceAll = qe.forceAll;
18498 if(forceAll === true || (q.length >= this.minChars)){
18500 this.hasQuery = true;
18502 if(this.lastQuery != q || this.alwaysQuery){
18503 this.lastQuery = q;
18504 if(this.mode == 'local'){
18505 this.selectedIndex = -1;
18507 this.store.clearFilter();
18510 if(this.specialFilter){
18511 this.fireEvent('specialfilter', this);
18516 this.store.filter(this.displayField, q);
18519 this.store.fireEvent("datachanged", this.store);
18526 this.store.baseParams[this.queryParam] = q;
18528 var options = {params : this.getParams(q)};
18531 options.add = true;
18532 options.params.start = this.page * this.pageSize;
18535 this.store.load(options);
18538 * this code will make the page width larger, at the beginning, the list not align correctly,
18539 * we should expand the list on onLoad
18540 * so command out it
18545 this.selectedIndex = -1;
18550 this.loadNext = false;
18554 getParams : function(q){
18556 //p[this.queryParam] = q;
18560 p.limit = this.pageSize;
18566 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18568 collapse : function(){
18569 if(!this.isExpanded()){
18575 this.hasFocus = false;
18579 this.cancelBtn.hide();
18580 this.trigger.show();
18583 this.tickableInputEl().dom.value = '';
18584 this.tickableInputEl().blur();
18589 Roo.get(document).un('mousedown', this.collapseIf, this);
18590 Roo.get(document).un('mousewheel', this.collapseIf, this);
18591 if (!this.editable) {
18592 Roo.get(document).un('keydown', this.listKeyPress, this);
18594 this.fireEvent('collapse', this);
18600 collapseIf : function(e){
18601 var in_combo = e.within(this.el);
18602 var in_list = e.within(this.list);
18603 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18605 if (in_combo || in_list || is_list) {
18606 //e.stopPropagation();
18611 this.onTickableFooterButtonClick(e, false, false);
18619 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18621 expand : function(){
18623 if(this.isExpanded() || !this.hasFocus){
18627 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18628 this.list.setWidth(lw);
18634 this.restrictHeight();
18638 this.tickItems = Roo.apply([], this.item);
18641 this.cancelBtn.show();
18642 this.trigger.hide();
18645 this.tickableInputEl().focus();
18650 Roo.get(document).on('mousedown', this.collapseIf, this);
18651 Roo.get(document).on('mousewheel', this.collapseIf, this);
18652 if (!this.editable) {
18653 Roo.get(document).on('keydown', this.listKeyPress, this);
18656 this.fireEvent('expand', this);
18660 // Implements the default empty TriggerField.onTriggerClick function
18661 onTriggerClick : function(e)
18663 Roo.log('trigger click');
18665 if(this.disabled || !this.triggerList){
18670 this.loadNext = false;
18672 if(this.isExpanded()){
18674 if (!this.blockFocus) {
18675 this.inputEl().focus();
18679 this.hasFocus = true;
18680 if(this.triggerAction == 'all') {
18681 this.doQuery(this.allQuery, true);
18683 this.doQuery(this.getRawValue());
18685 if (!this.blockFocus) {
18686 this.inputEl().focus();
18691 onTickableTriggerClick : function(e)
18698 this.loadNext = false;
18699 this.hasFocus = true;
18701 if(this.triggerAction == 'all') {
18702 this.doQuery(this.allQuery, true);
18704 this.doQuery(this.getRawValue());
18708 onSearchFieldClick : function(e)
18710 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18711 this.onTickableFooterButtonClick(e, false, false);
18715 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18720 this.loadNext = false;
18721 this.hasFocus = true;
18723 if(this.triggerAction == 'all') {
18724 this.doQuery(this.allQuery, true);
18726 this.doQuery(this.getRawValue());
18730 listKeyPress : function(e)
18732 //Roo.log('listkeypress');
18733 // scroll to first matching element based on key pres..
18734 if (e.isSpecialKey()) {
18737 var k = String.fromCharCode(e.getKey()).toUpperCase();
18740 var csel = this.view.getSelectedNodes();
18741 var cselitem = false;
18743 var ix = this.view.indexOf(csel[0]);
18744 cselitem = this.store.getAt(ix);
18745 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18751 this.store.each(function(v) {
18753 // start at existing selection.
18754 if (cselitem.id == v.id) {
18760 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18761 match = this.store.indexOf(v);
18767 if (match === false) {
18768 return true; // no more action?
18771 this.view.select(match);
18772 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18773 sn.scrollIntoView(sn.dom.parentNode, false);
18776 onViewScroll : function(e, t){
18778 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){
18782 this.hasQuery = true;
18784 this.loading = this.list.select('.loading', true).first();
18786 if(this.loading === null){
18787 this.list.createChild({
18789 cls: 'loading roo-select2-more-results roo-select2-active',
18790 html: 'Loading more results...'
18793 this.loading = this.list.select('.loading', true).first();
18795 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18797 this.loading.hide();
18800 this.loading.show();
18805 this.loadNext = true;
18807 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18812 addItem : function(o)
18814 var dv = ''; // display value
18816 if (this.displayField) {
18817 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18819 // this is an error condition!!!
18820 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18827 var choice = this.choices.createChild({
18829 cls: 'roo-select2-search-choice',
18838 cls: 'roo-select2-search-choice-close fa fa-times',
18843 }, this.searchField);
18845 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18847 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18855 this.inputEl().dom.value = '';
18860 onRemoveItem : function(e, _self, o)
18862 e.preventDefault();
18864 this.lastItem = Roo.apply([], this.item);
18866 var index = this.item.indexOf(o.data) * 1;
18869 Roo.log('not this item?!');
18873 this.item.splice(index, 1);
18878 this.fireEvent('remove', this, e);
18884 syncValue : function()
18886 if(!this.item.length){
18893 Roo.each(this.item, function(i){
18894 if(_this.valueField){
18895 value.push(i[_this.valueField]);
18902 this.value = value.join(',');
18904 if(this.hiddenField){
18905 this.hiddenField.dom.value = this.value;
18908 this.store.fireEvent("datachanged", this.store);
18913 clearItem : function()
18915 if(!this.multiple){
18921 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18929 if(this.tickable && !Roo.isTouch){
18930 this.view.refresh();
18934 inputEl: function ()
18936 if(Roo.isIOS && this.useNativeIOS){
18937 return this.el.select('select.roo-ios-select', true).first();
18940 if(Roo.isTouch && this.mobileTouchView){
18941 return this.el.select('input.form-control',true).first();
18945 return this.searchField;
18948 return this.el.select('input.form-control',true).first();
18951 onTickableFooterButtonClick : function(e, btn, el)
18953 e.preventDefault();
18955 this.lastItem = Roo.apply([], this.item);
18957 if(btn && btn.name == 'cancel'){
18958 this.tickItems = Roo.apply([], this.item);
18967 Roo.each(this.tickItems, function(o){
18975 validate : function()
18977 if(this.getVisibilityEl().hasClass('hidden')){
18981 var v = this.getRawValue();
18984 v = this.getValue();
18987 if(this.disabled || this.allowBlank || v.length){
18992 this.markInvalid();
18996 tickableInputEl : function()
18998 if(!this.tickable || !this.editable){
18999 return this.inputEl();
19002 return this.inputEl().select('.roo-select2-search-field-input', true).first();
19006 getAutoCreateTouchView : function()
19011 cls: 'form-group' //input-group
19017 type : this.inputType,
19018 cls : 'form-control x-combo-noedit',
19019 autocomplete: 'new-password',
19020 placeholder : this.placeholder || '',
19025 input.name = this.name;
19029 input.cls += ' input-' + this.size;
19032 if (this.disabled) {
19033 input.disabled = true;
19037 cls : 'roo-combobox-wrap',
19044 inputblock.cls += ' input-group';
19046 inputblock.cn.unshift({
19048 cls : 'input-group-addon input-group-prepend input-group-text',
19053 if(this.removable && !this.multiple){
19054 inputblock.cls += ' roo-removable';
19056 inputblock.cn.push({
19059 cls : 'roo-combo-removable-btn close'
19063 if(this.hasFeedback && !this.allowBlank){
19065 inputblock.cls += ' has-feedback';
19067 inputblock.cn.push({
19069 cls: 'glyphicon form-control-feedback'
19076 inputblock.cls += (this.before) ? '' : ' input-group';
19078 inputblock.cn.push({
19080 cls : 'input-group-addon input-group-append input-group-text',
19086 var ibwrap = inputblock;
19091 cls: 'roo-select2-choices',
19095 cls: 'roo-select2-search-field',
19108 cls: 'roo-select2-container input-group roo-touchview-combobox ',
19113 cls: 'form-hidden-field'
19119 if(!this.multiple && this.showToggleBtn){
19125 if (this.caret != false) {
19128 cls: 'fa fa-' + this.caret
19135 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19137 Roo.bootstrap.version == 3 ? caret : '',
19140 cls: 'combobox-clear',
19154 combobox.cls += ' roo-select2-container-multi';
19157 var required = this.allowBlank ? {
19159 style: 'display: none'
19162 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19163 tooltip : 'This field is required'
19166 var align = this.labelAlign || this.parentLabelAlign();
19168 if (align ==='left' && this.fieldLabel.length) {
19174 cls : 'control-label col-form-label',
19175 html : this.fieldLabel
19179 cls : 'roo-combobox-wrap ',
19186 var labelCfg = cfg.cn[1];
19187 var contentCfg = cfg.cn[2];
19190 if(this.indicatorpos == 'right'){
19195 cls : 'control-label col-form-label',
19199 html : this.fieldLabel
19205 cls : "roo-combobox-wrap ",
19213 labelCfg = cfg.cn[0];
19214 contentCfg = cfg.cn[1];
19219 if(this.labelWidth > 12){
19220 labelCfg.style = "width: " + this.labelWidth + 'px';
19223 if(this.labelWidth < 13 && this.labelmd == 0){
19224 this.labelmd = this.labelWidth;
19227 if(this.labellg > 0){
19228 labelCfg.cls += ' col-lg-' + this.labellg;
19229 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19232 if(this.labelmd > 0){
19233 labelCfg.cls += ' col-md-' + this.labelmd;
19234 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19237 if(this.labelsm > 0){
19238 labelCfg.cls += ' col-sm-' + this.labelsm;
19239 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19242 if(this.labelxs > 0){
19243 labelCfg.cls += ' col-xs-' + this.labelxs;
19244 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19248 } else if ( this.fieldLabel.length) {
19253 cls : 'control-label',
19254 html : this.fieldLabel
19265 if(this.indicatorpos == 'right'){
19269 cls : 'control-label',
19270 html : this.fieldLabel,
19288 var settings = this;
19290 ['xs','sm','md','lg'].map(function(size){
19291 if (settings[size]) {
19292 cfg.cls += ' col-' + size + '-' + settings[size];
19299 initTouchView : function()
19301 this.renderTouchView();
19303 this.touchViewEl.on('scroll', function(){
19304 this.el.dom.scrollTop = 0;
19307 this.originalValue = this.getValue();
19309 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19311 this.inputEl().on("click", this.showTouchView, this);
19312 if (this.triggerEl) {
19313 this.triggerEl.on("click", this.showTouchView, this);
19317 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19318 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19320 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19322 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19323 this.store.on('load', this.onTouchViewLoad, this);
19324 this.store.on('loadexception', this.onTouchViewLoadException, this);
19326 if(this.hiddenName){
19328 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19330 this.hiddenField.dom.value =
19331 this.hiddenValue !== undefined ? this.hiddenValue :
19332 this.value !== undefined ? this.value : '';
19334 this.el.dom.removeAttribute('name');
19335 this.hiddenField.dom.setAttribute('name', this.hiddenName);
19339 this.choices = this.el.select('ul.roo-select2-choices', true).first();
19340 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19343 if(this.removable && !this.multiple){
19344 var close = this.closeTriggerEl();
19346 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19347 close.on('click', this.removeBtnClick, this, close);
19351 * fix the bug in Safari iOS8
19353 this.inputEl().on("focus", function(e){
19354 document.activeElement.blur();
19357 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19364 renderTouchView : function()
19366 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19367 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19369 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19370 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19372 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19373 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19374 this.touchViewBodyEl.setStyle('overflow', 'auto');
19376 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19377 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19379 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19380 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19384 showTouchView : function()
19390 this.touchViewHeaderEl.hide();
19392 if(this.modalTitle.length){
19393 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19394 this.touchViewHeaderEl.show();
19397 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19398 this.touchViewEl.show();
19400 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19402 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19403 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19405 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19407 if(this.modalTitle.length){
19408 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19411 this.touchViewBodyEl.setHeight(bodyHeight);
19415 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19417 this.touchViewEl.addClass(['in','show']);
19420 if(this._touchViewMask){
19421 Roo.get(document.body).addClass("x-body-masked");
19422 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19423 this._touchViewMask.setStyle('z-index', 10000);
19424 this._touchViewMask.addClass('show');
19427 this.doTouchViewQuery();
19431 hideTouchView : function()
19433 this.touchViewEl.removeClass(['in','show']);
19437 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19439 this.touchViewEl.setStyle('display', 'none');
19442 if(this._touchViewMask){
19443 this._touchViewMask.removeClass('show');
19444 Roo.get(document.body).removeClass("x-body-masked");
19448 setTouchViewValue : function()
19455 Roo.each(this.tickItems, function(o){
19460 this.hideTouchView();
19463 doTouchViewQuery : function()
19472 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19476 if(!this.alwaysQuery || this.mode == 'local'){
19477 this.onTouchViewLoad();
19484 onTouchViewBeforeLoad : function(combo,opts)
19490 onTouchViewLoad : function()
19492 if(this.store.getCount() < 1){
19493 this.onTouchViewEmptyResults();
19497 this.clearTouchView();
19499 var rawValue = this.getRawValue();
19501 var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19503 this.tickItems = [];
19505 this.store.data.each(function(d, rowIndex){
19506 var row = this.touchViewListGroup.createChild(template);
19508 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19509 row.addClass(d.data.cls);
19512 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19515 html : d.data[this.displayField]
19518 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19519 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19522 row.removeClass('selected');
19523 if(!this.multiple && this.valueField &&
19524 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19527 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19528 row.addClass('selected');
19531 if(this.multiple && this.valueField &&
19532 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19536 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19537 this.tickItems.push(d.data);
19540 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19544 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19546 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19548 if(this.modalTitle.length){
19549 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19552 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19554 if(this.mobile_restrict_height && listHeight < bodyHeight){
19555 this.touchViewBodyEl.setHeight(listHeight);
19560 if(firstChecked && listHeight > bodyHeight){
19561 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19566 onTouchViewLoadException : function()
19568 this.hideTouchView();
19571 onTouchViewEmptyResults : function()
19573 this.clearTouchView();
19575 this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19577 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19581 clearTouchView : function()
19583 this.touchViewListGroup.dom.innerHTML = '';
19586 onTouchViewClick : function(e, el, o)
19588 e.preventDefault();
19591 var rowIndex = o.rowIndex;
19593 var r = this.store.getAt(rowIndex);
19595 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19597 if(!this.multiple){
19598 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19599 c.dom.removeAttribute('checked');
19602 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19604 this.setFromData(r.data);
19606 var close = this.closeTriggerEl();
19612 this.hideTouchView();
19614 this.fireEvent('select', this, r, rowIndex);
19619 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19620 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19621 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19625 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19626 this.addItem(r.data);
19627 this.tickItems.push(r.data);
19631 getAutoCreateNativeIOS : function()
19634 cls: 'form-group' //input-group,
19639 cls : 'roo-ios-select'
19643 combobox.name = this.name;
19646 if (this.disabled) {
19647 combobox.disabled = true;
19650 var settings = this;
19652 ['xs','sm','md','lg'].map(function(size){
19653 if (settings[size]) {
19654 cfg.cls += ' col-' + size + '-' + settings[size];
19664 initIOSView : function()
19666 this.store.on('load', this.onIOSViewLoad, this);
19671 onIOSViewLoad : function()
19673 if(this.store.getCount() < 1){
19677 this.clearIOSView();
19679 if(this.allowBlank) {
19681 var default_text = '-- SELECT --';
19683 if(this.placeholder.length){
19684 default_text = this.placeholder;
19687 if(this.emptyTitle.length){
19688 default_text += ' - ' + this.emptyTitle + ' -';
19691 var opt = this.inputEl().createChild({
19694 html : default_text
19698 o[this.valueField] = 0;
19699 o[this.displayField] = default_text;
19701 this.ios_options.push({
19708 this.store.data.each(function(d, rowIndex){
19712 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19713 html = d.data[this.displayField];
19718 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19719 value = d.data[this.valueField];
19728 if(this.value == d.data[this.valueField]){
19729 option['selected'] = true;
19732 var opt = this.inputEl().createChild(option);
19734 this.ios_options.push({
19741 this.inputEl().on('change', function(){
19742 this.fireEvent('select', this);
19747 clearIOSView: function()
19749 this.inputEl().dom.innerHTML = '';
19751 this.ios_options = [];
19754 setIOSValue: function(v)
19758 if(!this.ios_options){
19762 Roo.each(this.ios_options, function(opts){
19764 opts.el.dom.removeAttribute('selected');
19766 if(opts.data[this.valueField] != v){
19770 opts.el.dom.setAttribute('selected', true);
19776 * @cfg {Boolean} grow
19780 * @cfg {Number} growMin
19784 * @cfg {Number} growMax
19793 Roo.apply(Roo.bootstrap.form.ComboBox, {
19797 cls: 'modal-header',
19819 cls: 'list-group-item',
19823 cls: 'roo-combobox-list-group-item-value'
19827 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19841 listItemCheckbox : {
19843 cls: 'list-group-item',
19847 cls: 'roo-combobox-list-group-item-value'
19851 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19867 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19872 cls: 'modal-footer',
19880 cls: 'col-xs-6 text-left',
19883 cls: 'btn btn-danger roo-touch-view-cancel',
19889 cls: 'col-xs-6 text-right',
19892 cls: 'btn btn-success roo-touch-view-ok',
19903 Roo.apply(Roo.bootstrap.form.ComboBox, {
19905 touchViewTemplate : {
19907 cls: 'modal fade roo-combobox-touch-view',
19911 cls: 'modal-dialog',
19912 style : 'position:fixed', // we have to fix position....
19916 cls: 'modal-content',
19918 Roo.bootstrap.form.ComboBox.header,
19919 Roo.bootstrap.form.ComboBox.body,
19920 Roo.bootstrap.form.ComboBox.footer
19929 * Ext JS Library 1.1.1
19930 * Copyright(c) 2006-2007, Ext JS, LLC.
19932 * Originally Released Under LGPL - original licence link has changed is not relivant.
19935 * <script type="text/javascript">
19940 * @extends Roo.util.Observable
19941 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19942 * This class also supports single and multi selection modes. <br>
19943 * Create a data model bound view:
19945 var store = new Roo.data.Store(...);
19947 var view = new Roo.View({
19949 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19951 singleSelect: true,
19952 selectedClass: "ydataview-selected",
19956 // listen for node click?
19957 view.on("click", function(vw, index, node, e){
19958 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19962 dataModel.load("foobar.xml");
19964 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19966 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19967 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19969 * Note: old style constructor is still suported (container, template, config)
19972 * Create a new View
19973 * @param {Object} config The config object
19976 Roo.View = function(config, depreciated_tpl, depreciated_config){
19978 this.parent = false;
19980 if (typeof(depreciated_tpl) == 'undefined') {
19981 // new way.. - universal constructor.
19982 Roo.apply(this, config);
19983 this.el = Roo.get(this.el);
19986 this.el = Roo.get(config);
19987 this.tpl = depreciated_tpl;
19988 Roo.apply(this, depreciated_config);
19990 this.wrapEl = this.el.wrap().wrap();
19991 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19994 if(typeof(this.tpl) == "string"){
19995 this.tpl = new Roo.Template(this.tpl);
19997 // support xtype ctors..
19998 this.tpl = new Roo.factory(this.tpl, Roo);
20002 this.tpl.compile();
20007 * @event beforeclick
20008 * Fires before a click is processed. Returns false to cancel the default action.
20009 * @param {Roo.View} this
20010 * @param {Number} index The index of the target node
20011 * @param {HTMLElement} node The target node
20012 * @param {Roo.EventObject} e The raw event object
20014 "beforeclick" : true,
20017 * Fires when a template node is clicked.
20018 * @param {Roo.View} this
20019 * @param {Number} index The index of the target node
20020 * @param {HTMLElement} node The target node
20021 * @param {Roo.EventObject} e The raw event object
20026 * Fires when a template node is double clicked.
20027 * @param {Roo.View} this
20028 * @param {Number} index The index of the target node
20029 * @param {HTMLElement} node The target node
20030 * @param {Roo.EventObject} e The raw event object
20034 * @event contextmenu
20035 * Fires when a template node is right clicked.
20036 * @param {Roo.View} this
20037 * @param {Number} index The index of the target node
20038 * @param {HTMLElement} node The target node
20039 * @param {Roo.EventObject} e The raw event object
20041 "contextmenu" : true,
20043 * @event selectionchange
20044 * Fires when the selected nodes change.
20045 * @param {Roo.View} this
20046 * @param {Array} selections Array of the selected nodes
20048 "selectionchange" : true,
20051 * @event beforeselect
20052 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20053 * @param {Roo.View} this
20054 * @param {HTMLElement} node The node to be selected
20055 * @param {Array} selections Array of currently selected nodes
20057 "beforeselect" : true,
20059 * @event preparedata
20060 * Fires on every row to render, to allow you to change the data.
20061 * @param {Roo.View} this
20062 * @param {Object} data to be rendered (change this)
20064 "preparedata" : true
20072 "click": this.onClick,
20073 "dblclick": this.onDblClick,
20074 "contextmenu": this.onContextMenu,
20078 this.selections = [];
20080 this.cmp = new Roo.CompositeElementLite([]);
20082 this.store = Roo.factory(this.store, Roo.data);
20083 this.setStore(this.store, true);
20086 if ( this.footer && this.footer.xtype) {
20088 var fctr = this.wrapEl.appendChild(document.createElement("div"));
20090 this.footer.dataSource = this.store;
20091 this.footer.container = fctr;
20092 this.footer = Roo.factory(this.footer, Roo);
20093 fctr.insertFirst(this.el);
20095 // this is a bit insane - as the paging toolbar seems to detach the el..
20096 // dom.parentNode.parentNode.parentNode
20097 // they get detached?
20101 Roo.View.superclass.constructor.call(this);
20106 Roo.extend(Roo.View, Roo.util.Observable, {
20109 * @cfg {Roo.data.Store} store Data store to load data from.
20114 * @cfg {String|Roo.Element} el The container element.
20119 * @cfg {String|Roo.Template} tpl The template used by this View
20123 * @cfg {String} dataName the named area of the template to use as the data area
20124 * Works with domtemplates roo-name="name"
20128 * @cfg {String} selectedClass The css class to add to selected nodes
20130 selectedClass : "x-view-selected",
20132 * @cfg {String} emptyText The empty text to show when nothing is loaded.
20137 * @cfg {String} text to display on mask (default Loading)
20141 * @cfg {Boolean} multiSelect Allow multiple selection
20143 multiSelect : false,
20145 * @cfg {Boolean} singleSelect Allow single selection
20147 singleSelect: false,
20150 * @cfg {Boolean} toggleSelect - selecting
20152 toggleSelect : false,
20155 * @cfg {Boolean} tickable - selecting
20160 * Returns the element this view is bound to.
20161 * @return {Roo.Element}
20163 getEl : function(){
20164 return this.wrapEl;
20170 * Refreshes the view. - called by datachanged on the store. - do not call directly.
20172 refresh : function(){
20173 //Roo.log('refresh');
20176 // if we are using something like 'domtemplate', then
20177 // the what gets used is:
20178 // t.applySubtemplate(NAME, data, wrapping data..)
20179 // the outer template then get' applied with
20180 // the store 'extra data'
20181 // and the body get's added to the
20182 // roo-name="data" node?
20183 // <span class='roo-tpl-{name}'></span> ?????
20187 this.clearSelections();
20188 this.el.update("");
20190 var records = this.store.getRange();
20191 if(records.length < 1) {
20193 // is this valid?? = should it render a template??
20195 this.el.update(this.emptyText);
20199 if (this.dataName) {
20200 this.el.update(t.apply(this.store.meta)); //????
20201 el = this.el.child('.roo-tpl-' + this.dataName);
20204 for(var i = 0, len = records.length; i < len; i++){
20205 var data = this.prepareData(records[i].data, i, records[i]);
20206 this.fireEvent("preparedata", this, data, i, records[i]);
20208 var d = Roo.apply({}, data);
20211 Roo.apply(d, {'roo-id' : Roo.id()});
20215 Roo.each(this.parent.item, function(item){
20216 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20219 Roo.apply(d, {'roo-data-checked' : 'checked'});
20223 html[html.length] = Roo.util.Format.trim(
20225 t.applySubtemplate(this.dataName, d, this.store.meta) :
20232 el.update(html.join(""));
20233 this.nodes = el.dom.childNodes;
20234 this.updateIndexes(0);
20239 * Function to override to reformat the data that is sent to
20240 * the template for each node.
20241 * DEPRICATED - use the preparedata event handler.
20242 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20243 * a JSON object for an UpdateManager bound view).
20245 prepareData : function(data, index, record)
20247 this.fireEvent("preparedata", this, data, index, record);
20251 onUpdate : function(ds, record){
20252 // Roo.log('on update');
20253 this.clearSelections();
20254 var index = this.store.indexOf(record);
20255 var n = this.nodes[index];
20256 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20257 n.parentNode.removeChild(n);
20258 this.updateIndexes(index, index);
20264 onAdd : function(ds, records, index)
20266 //Roo.log(['on Add', ds, records, index] );
20267 this.clearSelections();
20268 if(this.nodes.length == 0){
20272 var n = this.nodes[index];
20273 for(var i = 0, len = records.length; i < len; i++){
20274 var d = this.prepareData(records[i].data, i, records[i]);
20276 this.tpl.insertBefore(n, d);
20279 this.tpl.append(this.el, d);
20282 this.updateIndexes(index);
20285 onRemove : function(ds, record, index){
20286 // Roo.log('onRemove');
20287 this.clearSelections();
20288 var el = this.dataName ?
20289 this.el.child('.roo-tpl-' + this.dataName) :
20292 el.dom.removeChild(this.nodes[index]);
20293 this.updateIndexes(index);
20297 * Refresh an individual node.
20298 * @param {Number} index
20300 refreshNode : function(index){
20301 this.onUpdate(this.store, this.store.getAt(index));
20304 updateIndexes : function(startIndex, endIndex){
20305 var ns = this.nodes;
20306 startIndex = startIndex || 0;
20307 endIndex = endIndex || ns.length - 1;
20308 for(var i = startIndex; i <= endIndex; i++){
20309 ns[i].nodeIndex = i;
20314 * Changes the data store this view uses and refresh the view.
20315 * @param {Store} store
20317 setStore : function(store, initial){
20318 if(!initial && this.store){
20319 this.store.un("datachanged", this.refresh);
20320 this.store.un("add", this.onAdd);
20321 this.store.un("remove", this.onRemove);
20322 this.store.un("update", this.onUpdate);
20323 this.store.un("clear", this.refresh);
20324 this.store.un("beforeload", this.onBeforeLoad);
20325 this.store.un("load", this.onLoad);
20326 this.store.un("loadexception", this.onLoad);
20330 store.on("datachanged", this.refresh, this);
20331 store.on("add", this.onAdd, this);
20332 store.on("remove", this.onRemove, this);
20333 store.on("update", this.onUpdate, this);
20334 store.on("clear", this.refresh, this);
20335 store.on("beforeload", this.onBeforeLoad, this);
20336 store.on("load", this.onLoad, this);
20337 store.on("loadexception", this.onLoad, this);
20345 * onbeforeLoad - masks the loading area.
20348 onBeforeLoad : function(store,opts)
20350 //Roo.log('onBeforeLoad');
20352 this.el.update("");
20354 this.el.mask(this.mask ? this.mask : "Loading" );
20356 onLoad : function ()
20363 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20364 * @param {HTMLElement} node
20365 * @return {HTMLElement} The template node
20367 findItemFromChild : function(node){
20368 var el = this.dataName ?
20369 this.el.child('.roo-tpl-' + this.dataName,true) :
20372 if(!node || node.parentNode == el){
20375 var p = node.parentNode;
20376 while(p && p != el){
20377 if(p.parentNode == el){
20386 onClick : function(e){
20387 var item = this.findItemFromChild(e.getTarget());
20389 var index = this.indexOf(item);
20390 if(this.onItemClick(item, index, e) !== false){
20391 this.fireEvent("click", this, index, item, e);
20394 this.clearSelections();
20399 onContextMenu : function(e){
20400 var item = this.findItemFromChild(e.getTarget());
20402 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20407 onDblClick : function(e){
20408 var item = this.findItemFromChild(e.getTarget());
20410 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20414 onItemClick : function(item, index, e)
20416 if(this.fireEvent("beforeclick", this, index, item, e) === false){
20419 if (this.toggleSelect) {
20420 var m = this.isSelected(item) ? 'unselect' : 'select';
20423 _t[m](item, true, false);
20426 if(this.multiSelect || this.singleSelect){
20427 if(this.multiSelect && e.shiftKey && this.lastSelection){
20428 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20430 this.select(item, this.multiSelect && e.ctrlKey);
20431 this.lastSelection = item;
20434 if(!this.tickable){
20435 e.preventDefault();
20443 * Get the number of selected nodes.
20446 getSelectionCount : function(){
20447 return this.selections.length;
20451 * Get the currently selected nodes.
20452 * @return {Array} An array of HTMLElements
20454 getSelectedNodes : function(){
20455 return this.selections;
20459 * Get the indexes of the selected nodes.
20462 getSelectedIndexes : function(){
20463 var indexes = [], s = this.selections;
20464 for(var i = 0, len = s.length; i < len; i++){
20465 indexes.push(s[i].nodeIndex);
20471 * Clear all selections
20472 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20474 clearSelections : function(suppressEvent){
20475 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20476 this.cmp.elements = this.selections;
20477 this.cmp.removeClass(this.selectedClass);
20478 this.selections = [];
20479 if(!suppressEvent){
20480 this.fireEvent("selectionchange", this, this.selections);
20486 * Returns true if the passed node is selected
20487 * @param {HTMLElement/Number} node The node or node index
20488 * @return {Boolean}
20490 isSelected : function(node){
20491 var s = this.selections;
20495 node = this.getNode(node);
20496 return s.indexOf(node) !== -1;
20501 * @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
20502 * @param {Boolean} keepExisting (optional) true to keep existing selections
20503 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20505 select : function(nodeInfo, keepExisting, suppressEvent){
20506 if(nodeInfo instanceof Array){
20508 this.clearSelections(true);
20510 for(var i = 0, len = nodeInfo.length; i < len; i++){
20511 this.select(nodeInfo[i], true, true);
20515 var node = this.getNode(nodeInfo);
20516 if(!node || this.isSelected(node)){
20517 return; // already selected.
20520 this.clearSelections(true);
20523 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20524 Roo.fly(node).addClass(this.selectedClass);
20525 this.selections.push(node);
20526 if(!suppressEvent){
20527 this.fireEvent("selectionchange", this, this.selections);
20535 * @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
20536 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20537 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20539 unselect : function(nodeInfo, keepExisting, suppressEvent)
20541 if(nodeInfo instanceof Array){
20542 Roo.each(this.selections, function(s) {
20543 this.unselect(s, nodeInfo);
20547 var node = this.getNode(nodeInfo);
20548 if(!node || !this.isSelected(node)){
20549 //Roo.log("not selected");
20550 return; // not selected.
20554 Roo.each(this.selections, function(s) {
20556 Roo.fly(node).removeClass(this.selectedClass);
20563 this.selections= ns;
20564 this.fireEvent("selectionchange", this, this.selections);
20568 * Gets a template node.
20569 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20570 * @return {HTMLElement} The node or null if it wasn't found
20572 getNode : function(nodeInfo){
20573 if(typeof nodeInfo == "string"){
20574 return document.getElementById(nodeInfo);
20575 }else if(typeof nodeInfo == "number"){
20576 return this.nodes[nodeInfo];
20582 * Gets a range template nodes.
20583 * @param {Number} startIndex
20584 * @param {Number} endIndex
20585 * @return {Array} An array of nodes
20587 getNodes : function(start, end){
20588 var ns = this.nodes;
20589 start = start || 0;
20590 end = typeof end == "undefined" ? ns.length - 1 : end;
20593 for(var i = start; i <= end; i++){
20597 for(var i = start; i >= end; i--){
20605 * Finds the index of the passed node
20606 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20607 * @return {Number} The index of the node or -1
20609 indexOf : function(node){
20610 node = this.getNode(node);
20611 if(typeof node.nodeIndex == "number"){
20612 return node.nodeIndex;
20614 var ns = this.nodes;
20615 for(var i = 0, len = ns.length; i < len; i++){
20626 * based on jquery fullcalendar
20630 Roo.bootstrap = Roo.bootstrap || {};
20632 * @class Roo.bootstrap.Calendar
20633 * @extends Roo.bootstrap.Component
20634 * Bootstrap Calendar class
20635 * @cfg {Boolean} loadMask (true|false) default false
20636 * @cfg {Object} header generate the user specific header of the calendar, default false
20639 * Create a new Container
20640 * @param {Object} config The config object
20645 Roo.bootstrap.Calendar = function(config){
20646 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20650 * Fires when a date is selected
20651 * @param {DatePicker} this
20652 * @param {Date} date The selected date
20656 * @event monthchange
20657 * Fires when the displayed month changes
20658 * @param {DatePicker} this
20659 * @param {Date} date The selected month
20661 'monthchange': true,
20663 * @event evententer
20664 * Fires when mouse over an event
20665 * @param {Calendar} this
20666 * @param {event} Event
20668 'evententer': true,
20670 * @event eventleave
20671 * Fires when the mouse leaves an
20672 * @param {Calendar} this
20675 'eventleave': true,
20677 * @event eventclick
20678 * Fires when the mouse click an
20679 * @param {Calendar} this
20688 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20691 * @cfg {Roo.data.Store} store
20692 * The data source for the calendar
20696 * @cfg {Number} startDay
20697 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20705 getAutoCreate : function(){
20708 var fc_button = function(name, corner, style, content ) {
20709 return Roo.apply({},{
20711 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20713 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20716 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20727 style : 'width:100%',
20734 cls : 'fc-header-left',
20736 fc_button('prev', 'left', 'arrow', '‹' ),
20737 fc_button('next', 'right', 'arrow', '›' ),
20738 { tag: 'span', cls: 'fc-header-space' },
20739 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20747 cls : 'fc-header-center',
20751 cls: 'fc-header-title',
20754 html : 'month / year'
20762 cls : 'fc-header-right',
20764 /* fc_button('month', 'left', '', 'month' ),
20765 fc_button('week', '', '', 'week' ),
20766 fc_button('day', 'right', '', 'day' )
20778 header = this.header;
20781 var cal_heads = function() {
20783 // fixme - handle this.
20785 for (var i =0; i < Date.dayNames.length; i++) {
20786 var d = Date.dayNames[i];
20789 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20790 html : d.substring(0,3)
20794 ret[0].cls += ' fc-first';
20795 ret[6].cls += ' fc-last';
20798 var cal_cell = function(n) {
20801 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20806 cls: 'fc-day-number',
20810 cls: 'fc-day-content',
20814 style: 'position: relative;' // height: 17px;
20826 var cal_rows = function() {
20829 for (var r = 0; r < 6; r++) {
20836 for (var i =0; i < Date.dayNames.length; i++) {
20837 var d = Date.dayNames[i];
20838 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20841 row.cn[0].cls+=' fc-first';
20842 row.cn[0].cn[0].style = 'min-height:90px';
20843 row.cn[6].cls+=' fc-last';
20847 ret[0].cls += ' fc-first';
20848 ret[4].cls += ' fc-prev-last';
20849 ret[5].cls += ' fc-last';
20856 cls: 'fc-border-separate',
20857 style : 'width:100%',
20865 cls : 'fc-first fc-last',
20883 cls : 'fc-content',
20884 style : "position: relative;",
20887 cls : 'fc-view fc-view-month fc-grid',
20888 style : 'position: relative',
20889 unselectable : 'on',
20892 cls : 'fc-event-container',
20893 style : 'position:absolute;z-index:8;top:0;left:0;'
20911 initEvents : function()
20914 throw "can not find store for calendar";
20920 style: "text-align:center",
20924 style: "background-color:white;width:50%;margin:250 auto",
20928 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20939 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20941 var size = this.el.select('.fc-content', true).first().getSize();
20942 this.maskEl.setSize(size.width, size.height);
20943 this.maskEl.enableDisplayMode("block");
20944 if(!this.loadMask){
20945 this.maskEl.hide();
20948 this.store = Roo.factory(this.store, Roo.data);
20949 this.store.on('load', this.onLoad, this);
20950 this.store.on('beforeload', this.onBeforeLoad, this);
20954 this.cells = this.el.select('.fc-day',true);
20955 //Roo.log(this.cells);
20956 this.textNodes = this.el.query('.fc-day-number');
20957 this.cells.addClassOnOver('fc-state-hover');
20959 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20960 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20961 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20962 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20964 this.on('monthchange', this.onMonthChange, this);
20966 this.update(new Date().clearTime());
20969 resize : function() {
20970 var sz = this.el.getSize();
20972 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20973 this.el.select('.fc-day-content div',true).setHeight(34);
20978 showPrevMonth : function(e){
20979 this.update(this.activeDate.add("mo", -1));
20981 showToday : function(e){
20982 this.update(new Date().clearTime());
20985 showNextMonth : function(e){
20986 this.update(this.activeDate.add("mo", 1));
20990 showPrevYear : function(){
20991 this.update(this.activeDate.add("y", -1));
20995 showNextYear : function(){
20996 this.update(this.activeDate.add("y", 1));
21001 update : function(date)
21003 var vd = this.activeDate;
21004 this.activeDate = date;
21005 // if(vd && this.el){
21006 // var t = date.getTime();
21007 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21008 // Roo.log('using add remove');
21010 // this.fireEvent('monthchange', this, date);
21012 // this.cells.removeClass("fc-state-highlight");
21013 // this.cells.each(function(c){
21014 // if(c.dateValue == t){
21015 // c.addClass("fc-state-highlight");
21016 // setTimeout(function(){
21017 // try{c.dom.firstChild.focus();}catch(e){}
21027 var days = date.getDaysInMonth();
21029 var firstOfMonth = date.getFirstDateOfMonth();
21030 var startingPos = firstOfMonth.getDay()-this.startDay;
21032 if(startingPos < this.startDay){
21036 var pm = date.add(Date.MONTH, -1);
21037 var prevStart = pm.getDaysInMonth()-startingPos;
21039 this.cells = this.el.select('.fc-day',true);
21040 this.textNodes = this.el.query('.fc-day-number');
21041 this.cells.addClassOnOver('fc-state-hover');
21043 var cells = this.cells.elements;
21044 var textEls = this.textNodes;
21046 Roo.each(cells, function(cell){
21047 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21050 days += startingPos;
21052 // convert everything to numbers so it's fast
21053 var day = 86400000;
21054 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21057 //Roo.log(prevStart);
21059 var today = new Date().clearTime().getTime();
21060 var sel = date.clearTime().getTime();
21061 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21062 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21063 var ddMatch = this.disabledDatesRE;
21064 var ddText = this.disabledDatesText;
21065 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21066 var ddaysText = this.disabledDaysText;
21067 var format = this.format;
21069 var setCellClass = function(cal, cell){
21073 //Roo.log('set Cell Class');
21075 var t = d.getTime();
21079 cell.dateValue = t;
21081 cell.className += " fc-today";
21082 cell.className += " fc-state-highlight";
21083 cell.title = cal.todayText;
21086 // disable highlight in other month..
21087 //cell.className += " fc-state-highlight";
21092 cell.className = " fc-state-disabled";
21093 cell.title = cal.minText;
21097 cell.className = " fc-state-disabled";
21098 cell.title = cal.maxText;
21102 if(ddays.indexOf(d.getDay()) != -1){
21103 cell.title = ddaysText;
21104 cell.className = " fc-state-disabled";
21107 if(ddMatch && format){
21108 var fvalue = d.dateFormat(format);
21109 if(ddMatch.test(fvalue)){
21110 cell.title = ddText.replace("%0", fvalue);
21111 cell.className = " fc-state-disabled";
21115 if (!cell.initialClassName) {
21116 cell.initialClassName = cell.dom.className;
21119 cell.dom.className = cell.initialClassName + ' ' + cell.className;
21124 for(; i < startingPos; i++) {
21125 textEls[i].innerHTML = (++prevStart);
21126 d.setDate(d.getDate()+1);
21128 cells[i].className = "fc-past fc-other-month";
21129 setCellClass(this, cells[i]);
21134 for(; i < days; i++){
21135 intDay = i - startingPos + 1;
21136 textEls[i].innerHTML = (intDay);
21137 d.setDate(d.getDate()+1);
21139 cells[i].className = ''; // "x-date-active";
21140 setCellClass(this, cells[i]);
21144 for(; i < 42; i++) {
21145 textEls[i].innerHTML = (++extraDays);
21146 d.setDate(d.getDate()+1);
21148 cells[i].className = "fc-future fc-other-month";
21149 setCellClass(this, cells[i]);
21152 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21154 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21156 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21157 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21159 if(totalRows != 6){
21160 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21161 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21164 this.fireEvent('monthchange', this, date);
21168 if(!this.internalRender){
21169 var main = this.el.dom.firstChild;
21170 var w = main.offsetWidth;
21171 this.el.setWidth(w + this.el.getBorderWidth("lr"));
21172 Roo.fly(main).setWidth(w);
21173 this.internalRender = true;
21174 // opera does not respect the auto grow header center column
21175 // then, after it gets a width opera refuses to recalculate
21176 // without a second pass
21177 if(Roo.isOpera && !this.secondPass){
21178 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21179 this.secondPass = true;
21180 this.update.defer(10, this, [date]);
21187 findCell : function(dt) {
21188 dt = dt.clearTime().getTime();
21190 this.cells.each(function(c){
21191 //Roo.log("check " +c.dateValue + '?=' + dt);
21192 if(c.dateValue == dt){
21202 findCells : function(ev) {
21203 var s = ev.start.clone().clearTime().getTime();
21205 var e= ev.end.clone().clearTime().getTime();
21208 this.cells.each(function(c){
21209 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21211 if(c.dateValue > e){
21214 if(c.dateValue < s){
21223 // findBestRow: function(cells)
21227 // for (var i =0 ; i < cells.length;i++) {
21228 // ret = Math.max(cells[i].rows || 0,ret);
21235 addItem : function(ev)
21237 // look for vertical location slot in
21238 var cells = this.findCells(ev);
21240 // ev.row = this.findBestRow(cells);
21242 // work out the location.
21246 for(var i =0; i < cells.length; i++) {
21248 cells[i].row = cells[0].row;
21251 cells[i].row = cells[i].row + 1;
21261 if (crow.start.getY() == cells[i].getY()) {
21263 crow.end = cells[i];
21280 cells[0].events.push(ev);
21282 this.calevents.push(ev);
21285 clearEvents: function() {
21287 if(!this.calevents){
21291 Roo.each(this.cells.elements, function(c){
21297 Roo.each(this.calevents, function(e) {
21298 Roo.each(e.els, function(el) {
21299 el.un('mouseenter' ,this.onEventEnter, this);
21300 el.un('mouseleave' ,this.onEventLeave, this);
21305 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21311 renderEvents: function()
21315 this.cells.each(function(c) {
21324 if(c.row != c.events.length){
21325 r = 4 - (4 - (c.row - c.events.length));
21328 c.events = ev.slice(0, r);
21329 c.more = ev.slice(r);
21331 if(c.more.length && c.more.length == 1){
21332 c.events.push(c.more.pop());
21335 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21339 this.cells.each(function(c) {
21341 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21344 for (var e = 0; e < c.events.length; e++){
21345 var ev = c.events[e];
21346 var rows = ev.rows;
21348 for(var i = 0; i < rows.length; i++) {
21350 // how many rows should it span..
21353 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21354 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21356 unselectable : "on",
21359 cls: 'fc-event-inner',
21363 // cls: 'fc-event-time',
21364 // html : cells.length > 1 ? '' : ev.time
21368 cls: 'fc-event-title',
21369 html : String.format('{0}', ev.title)
21376 cls: 'ui-resizable-handle ui-resizable-e',
21377 html : '  '
21384 cfg.cls += ' fc-event-start';
21386 if ((i+1) == rows.length) {
21387 cfg.cls += ' fc-event-end';
21390 var ctr = _this.el.select('.fc-event-container',true).first();
21391 var cg = ctr.createChild(cfg);
21393 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21394 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21396 var r = (c.more.length) ? 1 : 0;
21397 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
21398 cg.setWidth(ebox.right - sbox.x -2);
21400 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21401 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21402 cg.on('click', _this.onEventClick, _this, ev);
21413 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21414 style : 'position: absolute',
21415 unselectable : "on",
21418 cls: 'fc-event-inner',
21422 cls: 'fc-event-title',
21430 cls: 'ui-resizable-handle ui-resizable-e',
21431 html : '  '
21437 var ctr = _this.el.select('.fc-event-container',true).first();
21438 var cg = ctr.createChild(cfg);
21440 var sbox = c.select('.fc-day-content',true).first().getBox();
21441 var ebox = c.select('.fc-day-content',true).first().getBox();
21443 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
21444 cg.setWidth(ebox.right - sbox.x -2);
21446 cg.on('click', _this.onMoreEventClick, _this, c.more);
21456 onEventEnter: function (e, el,event,d) {
21457 this.fireEvent('evententer', this, el, event);
21460 onEventLeave: function (e, el,event,d) {
21461 this.fireEvent('eventleave', this, el, event);
21464 onEventClick: function (e, el,event,d) {
21465 this.fireEvent('eventclick', this, el, event);
21468 onMonthChange: function () {
21472 onMoreEventClick: function(e, el, more)
21476 this.calpopover.placement = 'right';
21477 this.calpopover.setTitle('More');
21479 this.calpopover.setContent('');
21481 var ctr = this.calpopover.el.select('.popover-content', true).first();
21483 Roo.each(more, function(m){
21485 cls : 'fc-event-hori fc-event-draggable',
21488 var cg = ctr.createChild(cfg);
21490 cg.on('click', _this.onEventClick, _this, m);
21493 this.calpopover.show(el);
21498 onLoad: function ()
21500 this.calevents = [];
21503 if(this.store.getCount() > 0){
21504 this.store.data.each(function(d){
21507 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21508 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21509 time : d.data.start_time,
21510 title : d.data.title,
21511 description : d.data.description,
21512 venue : d.data.venue
21517 this.renderEvents();
21519 if(this.calevents.length && this.loadMask){
21520 this.maskEl.hide();
21524 onBeforeLoad: function()
21526 this.clearEvents();
21528 this.maskEl.show();
21542 * @class Roo.bootstrap.Popover
21543 * @extends Roo.bootstrap.Component
21544 * @parent none builder
21545 * @children Roo.bootstrap.Component
21546 * Bootstrap Popover class
21547 * @cfg {String} html contents of the popover (or false to use children..)
21548 * @cfg {String} title of popover (or false to hide)
21549 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21550 * @cfg {String} trigger click || hover (or false to trigger manually)
21551 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21552 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21553 * - if false and it has a 'parent' then it will be automatically added to that element
21554 * - if string - Roo.get will be called
21555 * @cfg {Number} delay - delay before showing
21558 * Create a new Popover
21559 * @param {Object} config The config object
21562 Roo.bootstrap.Popover = function(config){
21563 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21569 * After the popover show
21571 * @param {Roo.bootstrap.Popover} this
21576 * After the popover hide
21578 * @param {Roo.bootstrap.Popover} this
21584 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21589 placement : 'right',
21590 trigger : 'hover', // hover
21596 can_build_overlaid : false,
21598 maskEl : false, // the mask element
21601 alignEl : false, // when show is called with an element - this get's stored.
21603 getChildContainer : function()
21605 return this.contentEl;
21608 getPopoverHeader : function()
21610 this.title = true; // flag not to hide it..
21611 this.headerEl.addClass('p-0');
21612 return this.headerEl
21616 getAutoCreate : function(){
21619 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21620 style: 'display:block',
21626 cls : 'popover-inner ',
21630 cls: 'popover-title popover-header',
21631 html : this.title === false ? '' : this.title
21634 cls : 'popover-content popover-body ' + (this.cls || ''),
21635 html : this.html || ''
21646 * @param {string} the title
21648 setTitle: function(str)
21652 this.headerEl.dom.innerHTML = str;
21657 * @param {string} the body content
21659 setContent: function(str)
21662 if (this.contentEl) {
21663 this.contentEl.dom.innerHTML = str;
21667 // as it get's added to the bottom of the page.
21668 onRender : function(ct, position)
21670 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21675 var cfg = Roo.apply({}, this.getAutoCreate());
21679 cfg.cls += ' ' + this.cls;
21682 cfg.style = this.style;
21684 //Roo.log("adding to ");
21685 this.el = Roo.get(document.body).createChild(cfg, position);
21686 // Roo.log(this.el);
21689 this.contentEl = this.el.select('.popover-content',true).first();
21690 this.headerEl = this.el.select('.popover-title',true).first();
21693 if(typeof(this.items) != 'undefined'){
21694 var items = this.items;
21697 for(var i =0;i < items.length;i++) {
21698 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21702 this.items = nitems;
21704 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21705 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21712 resizeMask : function()
21714 this.maskEl.setSize(
21715 Roo.lib.Dom.getViewWidth(true),
21716 Roo.lib.Dom.getViewHeight(true)
21720 initEvents : function()
21724 Roo.bootstrap.Popover.register(this);
21727 this.arrowEl = this.el.select('.arrow',true).first();
21728 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21729 this.el.enableDisplayMode('block');
21733 if (this.over === false && !this.parent()) {
21736 if (this.triggers === false) {
21741 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21742 var triggers = this.trigger ? this.trigger.split(' ') : [];
21743 Roo.each(triggers, function(trigger) {
21745 if (trigger == 'click') {
21746 on_el.on('click', this.toggle, this);
21747 } else if (trigger != 'manual') {
21748 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21749 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21751 on_el.on(eventIn ,this.enter, this);
21752 on_el.on(eventOut, this.leave, this);
21762 toggle : function () {
21763 this.hoverState == 'in' ? this.leave() : this.enter();
21766 enter : function () {
21768 clearTimeout(this.timeout);
21770 this.hoverState = 'in';
21772 if (!this.delay || !this.delay.show) {
21777 this.timeout = setTimeout(function () {
21778 if (_t.hoverState == 'in') {
21781 }, this.delay.show)
21784 leave : function() {
21785 clearTimeout(this.timeout);
21787 this.hoverState = 'out';
21789 if (!this.delay || !this.delay.hide) {
21794 this.timeout = setTimeout(function () {
21795 if (_t.hoverState == 'out') {
21798 }, this.delay.hide)
21802 * update the position of the dialog
21803 * normally this is needed if the popover get's bigger - due to a Table reload etc..
21808 doAlign : function()
21811 if (this.alignEl) {
21812 this.updatePosition(this.placement, true);
21815 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21816 var es = this.el.getSize();
21817 var x = Roo.lib.Dom.getViewWidth()/2;
21818 var y = Roo.lib.Dom.getViewHeight()/2;
21819 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21831 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21832 * @param {string} (left|right|top|bottom) position
21834 show : function (on_el, placement)
21836 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21837 on_el = on_el || false; // default to false
21840 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21841 on_el = this.parent().el;
21842 } else if (this.over) {
21843 on_el = Roo.get(this.over);
21848 this.alignEl = Roo.get( on_el );
21851 this.render(document.body);
21857 if (this.title === false) {
21858 this.headerEl.hide();
21863 this.el.dom.style.display = 'block';
21867 //var arrow = this.el.select('.arrow',true).first();
21868 //arrow.set(align[2],
21870 this.el.addClass('in');
21874 this.hoverState = 'in';
21877 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21878 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21879 this.maskEl.dom.style.display = 'block';
21880 this.maskEl.addClass('show');
21882 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21884 this.fireEvent('show', this);
21888 * fire this manually after loading a grid in the table for example
21889 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21890 * @param {Boolean} try and move it if we cant get right position.
21892 updatePosition : function(placement, try_move)
21894 // allow for calling with no parameters
21895 placement = placement ? placement : this.placement;
21896 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21898 this.el.removeClass([
21899 'fade','top','bottom', 'left', 'right','in',
21900 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21902 this.el.addClass(placement + ' bs-popover-' + placement);
21904 if (!this.alignEl ) {
21908 switch (placement) {
21910 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21911 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21912 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21913 //normal display... or moved up/down.
21914 this.el.setXY(offset);
21915 var xy = this.alignEl.getAnchorXY('tr', false);
21917 this.arrowEl.setXY(xy);
21920 // continue through...
21921 return this.updatePosition('left', false);
21925 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21926 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21927 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21928 //normal display... or moved up/down.
21929 this.el.setXY(offset);
21930 var xy = this.alignEl.getAnchorXY('tl', false);
21931 xy[0]-=10;xy[1]+=5; // << fix me
21932 this.arrowEl.setXY(xy);
21936 return this.updatePosition('right', false);
21939 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21940 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21941 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21942 //normal display... or moved up/down.
21943 this.el.setXY(offset);
21944 var xy = this.alignEl.getAnchorXY('t', false);
21945 xy[1]-=10; // << fix me
21946 this.arrowEl.setXY(xy);
21950 return this.updatePosition('bottom', false);
21953 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21954 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21955 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21956 //normal display... or moved up/down.
21957 this.el.setXY(offset);
21958 var xy = this.alignEl.getAnchorXY('b', false);
21959 xy[1]+=2; // << fix me
21960 this.arrowEl.setXY(xy);
21964 return this.updatePosition('top', false);
21975 this.el.setXY([0,0]);
21976 this.el.removeClass('in');
21978 this.hoverState = null;
21979 this.maskEl.hide(); // always..
21980 this.fireEvent('hide', this);
21986 Roo.apply(Roo.bootstrap.Popover, {
21989 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21990 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21991 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21992 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21997 clickHander : false,
22001 onMouseDown : function(e)
22003 if (this.popups.length && !e.getTarget(".roo-popover")) {
22004 /// what is nothing is showing..
22013 register : function(popup)
22015 if (!Roo.bootstrap.Popover.clickHandler) {
22016 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22018 // hide other popups.
22019 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
22020 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
22021 this.hideAll(); //<< why?
22022 //this.popups.push(popup);
22024 hideAll : function()
22026 this.popups.forEach(function(p) {
22030 onShow : function() {
22031 Roo.bootstrap.Popover.popups.push(this);
22033 onHide : function() {
22034 Roo.bootstrap.Popover.popups.remove(this);
22039 * @class Roo.bootstrap.PopoverNav
22040 * @extends Roo.bootstrap.nav.Simplebar
22041 * @parent Roo.bootstrap.Popover
22042 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22044 * Bootstrap Popover header navigation class
22045 * FIXME? should this go under nav?
22049 * Create a new Popover Header Navigation
22050 * @param {Object} config The config object
22053 Roo.bootstrap.PopoverNav = function(config){
22054 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22057 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar, {
22060 container_method : 'getPopoverHeader'
22078 * @class Roo.bootstrap.Progress
22079 * @extends Roo.bootstrap.Component
22080 * @children Roo.bootstrap.ProgressBar
22081 * Bootstrap Progress class
22082 * @cfg {Boolean} striped striped of the progress bar
22083 * @cfg {Boolean} active animated of the progress bar
22087 * Create a new Progress
22088 * @param {Object} config The config object
22091 Roo.bootstrap.Progress = function(config){
22092 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22095 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
22100 getAutoCreate : function(){
22108 cfg.cls += ' progress-striped';
22112 cfg.cls += ' active';
22131 * @class Roo.bootstrap.ProgressBar
22132 * @extends Roo.bootstrap.Component
22133 * Bootstrap ProgressBar class
22134 * @cfg {Number} aria_valuenow aria-value now
22135 * @cfg {Number} aria_valuemin aria-value min
22136 * @cfg {Number} aria_valuemax aria-value max
22137 * @cfg {String} label label for the progress bar
22138 * @cfg {String} panel (success | info | warning | danger )
22139 * @cfg {String} role role of the progress bar
22140 * @cfg {String} sr_only text
22144 * Create a new ProgressBar
22145 * @param {Object} config The config object
22148 Roo.bootstrap.ProgressBar = function(config){
22149 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22152 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
22156 aria_valuemax : 100,
22162 getAutoCreate : function()
22167 cls: 'progress-bar',
22168 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22180 cfg.role = this.role;
22183 if(this.aria_valuenow){
22184 cfg['aria-valuenow'] = this.aria_valuenow;
22187 if(this.aria_valuemin){
22188 cfg['aria-valuemin'] = this.aria_valuemin;
22191 if(this.aria_valuemax){
22192 cfg['aria-valuemax'] = this.aria_valuemax;
22195 if(this.label && !this.sr_only){
22196 cfg.html = this.label;
22200 cfg.cls += ' progress-bar-' + this.panel;
22206 update : function(aria_valuenow)
22208 this.aria_valuenow = aria_valuenow;
22210 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22218 * @class Roo.bootstrap.TabGroup
22219 * @extends Roo.bootstrap.Column
22220 * @children Roo.bootstrap.TabPanel
22221 * Bootstrap Column class
22222 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22223 * @cfg {Boolean} carousel true to make the group behave like a carousel
22224 * @cfg {Boolean} bullets show bullets for the panels
22225 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22226 * @cfg {Number} timer auto slide timer .. default 0 millisecond
22227 * @cfg {Boolean} showarrow (true|false) show arrow default true
22230 * Create a new TabGroup
22231 * @param {Object} config The config object
22234 Roo.bootstrap.TabGroup = function(config){
22235 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22237 this.navId = Roo.id();
22240 Roo.bootstrap.TabGroup.register(this);
22244 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
22247 transition : false,
22252 slideOnTouch : false,
22255 getAutoCreate : function()
22257 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22259 cfg.cls += ' tab-content';
22261 if (this.carousel) {
22262 cfg.cls += ' carousel slide';
22265 cls : 'carousel-inner',
22269 if(this.bullets && !Roo.isTouch){
22272 cls : 'carousel-bullets',
22276 if(this.bullets_cls){
22277 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22284 cfg.cn[0].cn.push(bullets);
22287 if(this.showarrow){
22288 cfg.cn[0].cn.push({
22290 class : 'carousel-arrow',
22294 class : 'carousel-prev',
22298 class : 'fa fa-chevron-left'
22304 class : 'carousel-next',
22308 class : 'fa fa-chevron-right'
22321 initEvents: function()
22323 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22324 // this.el.on("touchstart", this.onTouchStart, this);
22327 if(this.autoslide){
22330 this.slideFn = window.setInterval(function() {
22331 _this.showPanelNext();
22335 if(this.showarrow){
22336 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22337 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22343 // onTouchStart : function(e, el, o)
22345 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22349 // this.showPanelNext();
22353 getChildContainer : function()
22355 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22359 * register a Navigation item
22360 * @param {Roo.bootstrap.nav.Item} the navitem to add
22362 register : function(item)
22364 this.tabs.push( item);
22365 item.navId = this.navId; // not really needed..
22370 getActivePanel : function()
22373 Roo.each(this.tabs, function(t) {
22383 getPanelByName : function(n)
22386 Roo.each(this.tabs, function(t) {
22387 if (t.tabId == n) {
22395 indexOfPanel : function(p)
22398 Roo.each(this.tabs, function(t,i) {
22399 if (t.tabId == p.tabId) {
22408 * show a specific panel
22409 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22410 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22412 showPanel : function (pan)
22414 if(this.transition || typeof(pan) == 'undefined'){
22415 Roo.log("waiting for the transitionend");
22419 if (typeof(pan) == 'number') {
22420 pan = this.tabs[pan];
22423 if (typeof(pan) == 'string') {
22424 pan = this.getPanelByName(pan);
22427 var cur = this.getActivePanel();
22430 Roo.log('pan or acitve pan is undefined');
22434 if (pan.tabId == this.getActivePanel().tabId) {
22438 if (false === cur.fireEvent('beforedeactivate')) {
22442 if(this.bullets > 0 && !Roo.isTouch){
22443 this.setActiveBullet(this.indexOfPanel(pan));
22446 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22448 //class="carousel-item carousel-item-next carousel-item-left"
22450 this.transition = true;
22451 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
22452 var lr = dir == 'next' ? 'left' : 'right';
22453 pan.el.addClass(dir); // or prev
22454 pan.el.addClass('carousel-item-' + dir); // or prev
22455 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22456 cur.el.addClass(lr); // or right
22457 pan.el.addClass(lr);
22458 cur.el.addClass('carousel-item-' +lr); // or right
22459 pan.el.addClass('carousel-item-' +lr);
22463 cur.el.on('transitionend', function() {
22464 Roo.log("trans end?");
22466 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22467 pan.setActive(true);
22469 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22470 cur.setActive(false);
22472 _this.transition = false;
22474 }, this, { single: true } );
22479 cur.setActive(false);
22480 pan.setActive(true);
22485 showPanelNext : function()
22487 var i = this.indexOfPanel(this.getActivePanel());
22489 if (i >= this.tabs.length - 1 && !this.autoslide) {
22493 if (i >= this.tabs.length - 1 && this.autoslide) {
22497 this.showPanel(this.tabs[i+1]);
22500 showPanelPrev : function()
22502 var i = this.indexOfPanel(this.getActivePanel());
22504 if (i < 1 && !this.autoslide) {
22508 if (i < 1 && this.autoslide) {
22509 i = this.tabs.length;
22512 this.showPanel(this.tabs[i-1]);
22516 addBullet: function()
22518 if(!this.bullets || Roo.isTouch){
22521 var ctr = this.el.select('.carousel-bullets',true).first();
22522 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22523 var bullet = ctr.createChild({
22524 cls : 'bullet bullet-' + i
22525 },ctr.dom.lastChild);
22530 bullet.on('click', (function(e, el, o, ii, t){
22532 e.preventDefault();
22534 this.showPanel(ii);
22536 if(this.autoslide && this.slideFn){
22537 clearInterval(this.slideFn);
22538 this.slideFn = window.setInterval(function() {
22539 _this.showPanelNext();
22543 }).createDelegate(this, [i, bullet], true));
22548 setActiveBullet : function(i)
22554 Roo.each(this.el.select('.bullet', true).elements, function(el){
22555 el.removeClass('selected');
22558 var bullet = this.el.select('.bullet-' + i, true).first();
22564 bullet.addClass('selected');
22575 Roo.apply(Roo.bootstrap.TabGroup, {
22579 * register a Navigation Group
22580 * @param {Roo.bootstrap.nav.Group} the navgroup to add
22582 register : function(navgrp)
22584 this.groups[navgrp.navId] = navgrp;
22588 * fetch a Navigation Group based on the navigation ID
22589 * if one does not exist , it will get created.
22590 * @param {string} the navgroup to add
22591 * @returns {Roo.bootstrap.nav.Group} the navgroup
22593 get: function(navId) {
22594 if (typeof(this.groups[navId]) == 'undefined') {
22595 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22597 return this.groups[navId] ;
22612 * @class Roo.bootstrap.TabPanel
22613 * @extends Roo.bootstrap.Component
22614 * @children Roo.bootstrap.Component
22615 * Bootstrap TabPanel class
22616 * @cfg {Boolean} active panel active
22617 * @cfg {String} html panel content
22618 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22619 * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22620 * @cfg {String} href click to link..
22621 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22625 * Create a new TabPanel
22626 * @param {Object} config The config object
22629 Roo.bootstrap.TabPanel = function(config){
22630 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22634 * Fires when the active status changes
22635 * @param {Roo.bootstrap.TabPanel} this
22636 * @param {Boolean} state the new state
22641 * @event beforedeactivate
22642 * Fires before a tab is de-activated - can be used to do validation on a form.
22643 * @param {Roo.bootstrap.TabPanel} this
22644 * @return {Boolean} false if there is an error
22647 'beforedeactivate': true
22650 this.tabId = this.tabId || Roo.id();
22654 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22661 touchSlide : false,
22662 getAutoCreate : function(){
22667 // item is needed for carousel - not sure if it has any effect otherwise
22668 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22669 html: this.html || ''
22673 cfg.cls += ' active';
22677 cfg.tabId = this.tabId;
22685 initEvents: function()
22687 var p = this.parent();
22689 this.navId = this.navId || p.navId;
22691 if (typeof(this.navId) != 'undefined') {
22692 // not really needed.. but just in case.. parent should be a NavGroup.
22693 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22697 var i = tg.tabs.length - 1;
22699 if(this.active && tg.bullets > 0 && i < tg.bullets){
22700 tg.setActiveBullet(i);
22704 this.el.on('click', this.onClick, this);
22706 if(Roo.isTouch && this.touchSlide){
22707 this.el.on("touchstart", this.onTouchStart, this);
22708 this.el.on("touchmove", this.onTouchMove, this);
22709 this.el.on("touchend", this.onTouchEnd, this);
22714 onRender : function(ct, position)
22716 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22719 setActive : function(state)
22721 Roo.log("panel - set active " + this.tabId + "=" + state);
22723 this.active = state;
22725 this.el.removeClass('active');
22727 } else if (!this.el.hasClass('active')) {
22728 this.el.addClass('active');
22731 this.fireEvent('changed', this, state);
22734 onClick : function(e)
22736 e.preventDefault();
22738 if(!this.href.length){
22742 window.location.href = this.href;
22751 onTouchStart : function(e)
22753 this.swiping = false;
22755 this.startX = e.browserEvent.touches[0].clientX;
22756 this.startY = e.browserEvent.touches[0].clientY;
22759 onTouchMove : function(e)
22761 this.swiping = true;
22763 this.endX = e.browserEvent.touches[0].clientX;
22764 this.endY = e.browserEvent.touches[0].clientY;
22767 onTouchEnd : function(e)
22774 var tabGroup = this.parent();
22776 if(this.endX > this.startX){ // swiping right
22777 tabGroup.showPanelPrev();
22781 if(this.startX > this.endX){ // swiping left
22782 tabGroup.showPanelNext();
22801 * @class Roo.bootstrap.form.DateField
22802 * @extends Roo.bootstrap.form.Input
22803 * Bootstrap DateField class
22804 * @cfg {Number} weekStart default 0
22805 * @cfg {String} viewMode default empty, (months|years)
22806 * @cfg {String} minViewMode default empty, (months|years)
22807 * @cfg {Number} startDate default -Infinity
22808 * @cfg {Number} endDate default Infinity
22809 * @cfg {Boolean} todayHighlight default false
22810 * @cfg {Boolean} todayBtn default false
22811 * @cfg {Boolean} calendarWeeks default false
22812 * @cfg {Object} daysOfWeekDisabled default empty
22813 * @cfg {Boolean} singleMode default false (true | false)
22815 * @cfg {Boolean} keyboardNavigation default true
22816 * @cfg {String} language default en
22819 * Create a new DateField
22820 * @param {Object} config The config object
22823 Roo.bootstrap.form.DateField = function(config){
22824 Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22828 * Fires when this field show.
22829 * @param {Roo.bootstrap.form.DateField} this
22830 * @param {Mixed} date The date value
22835 * Fires when this field hide.
22836 * @param {Roo.bootstrap.form.DateField} this
22837 * @param {Mixed} date The date value
22842 * Fires when select a date.
22843 * @param {Roo.bootstrap.form.DateField} this
22844 * @param {Mixed} date The date value
22848 * @event beforeselect
22849 * Fires when before select a date.
22850 * @param {Roo.bootstrap.form.DateField} this
22851 * @param {Mixed} date The date value
22853 beforeselect : true
22857 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input, {
22860 * @cfg {String} format
22861 * The default date format string which can be overriden for localization support. The format must be
22862 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22866 * @cfg {String} altFormats
22867 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22868 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22870 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22878 todayHighlight : false,
22884 keyboardNavigation: true,
22886 calendarWeeks: false,
22888 startDate: -Infinity,
22892 daysOfWeekDisabled: [],
22896 singleMode : false,
22898 UTCDate: function()
22900 return new Date(Date.UTC.apply(Date, arguments));
22903 UTCToday: function()
22905 var today = new Date();
22906 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22909 getDate: function() {
22910 var d = this.getUTCDate();
22911 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22914 getUTCDate: function() {
22918 setDate: function(d) {
22919 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22922 setUTCDate: function(d) {
22924 this.setValue(this.formatDate(this.date));
22927 onRender: function(ct, position)
22930 Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22932 this.language = this.language || 'en';
22933 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22934 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22936 this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22937 this.format = this.format || 'm/d/y';
22938 this.isInline = false;
22939 this.isInput = true;
22940 this.component = this.el.select('.add-on', true).first() || false;
22941 this.component = (this.component && this.component.length === 0) ? false : this.component;
22942 this.hasInput = this.component && this.inputEl().length;
22944 if (typeof(this.minViewMode === 'string')) {
22945 switch (this.minViewMode) {
22947 this.minViewMode = 1;
22950 this.minViewMode = 2;
22953 this.minViewMode = 0;
22958 if (typeof(this.viewMode === 'string')) {
22959 switch (this.viewMode) {
22972 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22974 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22976 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22978 this.picker().on('mousedown', this.onMousedown, this);
22979 this.picker().on('click', this.onClick, this);
22981 this.picker().addClass('datepicker-dropdown');
22983 this.startViewMode = this.viewMode;
22985 if(this.singleMode){
22986 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22987 v.setVisibilityMode(Roo.Element.DISPLAY);
22991 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22992 v.setStyle('width', '189px');
22996 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22997 if(!this.calendarWeeks){
23002 v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23003 v.attr('colspan', function(i, val){
23004 return parseInt(val) + 1;
23009 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23011 this.setStartDate(this.startDate);
23012 this.setEndDate(this.endDate);
23014 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23021 if(this.isInline) {
23026 picker : function()
23028 return this.pickerEl;
23029 // return this.el.select('.datepicker', true).first();
23032 fillDow: function()
23034 var dowCnt = this.weekStart;
23043 if(this.calendarWeeks){
23051 while (dowCnt < this.weekStart + 7) {
23055 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23059 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23062 fillMonths: function()
23065 var months = this.picker().select('>.datepicker-months td', true).first();
23067 months.dom.innerHTML = '';
23073 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23076 months.createChild(month);
23083 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;
23085 if (this.date < this.startDate) {
23086 this.viewDate = new Date(this.startDate);
23087 } else if (this.date > this.endDate) {
23088 this.viewDate = new Date(this.endDate);
23090 this.viewDate = new Date(this.date);
23098 var d = new Date(this.viewDate),
23099 year = d.getUTCFullYear(),
23100 month = d.getUTCMonth(),
23101 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23102 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23103 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23104 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23105 currentDate = this.date && this.date.valueOf(),
23106 today = this.UTCToday();
23108 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23110 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23112 // this.picker.select('>tfoot th.today').
23113 // .text(dates[this.language].today)
23114 // .toggle(this.todayBtn !== false);
23116 this.updateNavArrows();
23119 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23121 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23123 prevMonth.setUTCDate(day);
23125 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23127 var nextMonth = new Date(prevMonth);
23129 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23131 nextMonth = nextMonth.valueOf();
23133 var fillMonths = false;
23135 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23137 while(prevMonth.valueOf() <= nextMonth) {
23140 if (prevMonth.getUTCDay() === this.weekStart) {
23142 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23150 if(this.calendarWeeks){
23151 // ISO 8601: First week contains first thursday.
23152 // ISO also states week starts on Monday, but we can be more abstract here.
23154 // Start of current week: based on weekstart/current date
23155 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23156 // Thursday of this week
23157 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23158 // First Thursday of year, year from thursday
23159 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23160 // Calendar week: ms between thursdays, div ms per day, div 7 days
23161 calWeek = (th - yth) / 864e5 / 7 + 1;
23163 fillMonths.cn.push({
23171 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23173 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23176 if (this.todayHighlight &&
23177 prevMonth.getUTCFullYear() == today.getFullYear() &&
23178 prevMonth.getUTCMonth() == today.getMonth() &&
23179 prevMonth.getUTCDate() == today.getDate()) {
23180 clsName += ' today';
23183 if (currentDate && prevMonth.valueOf() === currentDate) {
23184 clsName += ' active';
23187 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23188 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23189 clsName += ' disabled';
23192 fillMonths.cn.push({
23194 cls: 'day ' + clsName,
23195 html: prevMonth.getDate()
23198 prevMonth.setDate(prevMonth.getDate()+1);
23201 var currentYear = this.date && this.date.getUTCFullYear();
23202 var currentMonth = this.date && this.date.getUTCMonth();
23204 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23206 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23207 v.removeClass('active');
23209 if(currentYear === year && k === currentMonth){
23210 v.addClass('active');
23213 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23214 v.addClass('disabled');
23220 year = parseInt(year/10, 10) * 10;
23222 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23224 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23227 for (var i = -1; i < 11; i++) {
23228 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23230 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23238 showMode: function(dir)
23241 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23244 Roo.each(this.picker().select('>div',true).elements, function(v){
23245 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23248 this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23253 if(this.isInline) {
23257 this.picker().removeClass(['bottom', 'top']);
23259 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23261 * place to the top of element!
23265 this.picker().addClass('top');
23266 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23271 this.picker().addClass('bottom');
23273 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23276 parseDate : function(value)
23278 if(!value || value instanceof Date){
23281 var v = Date.parseDate(value, this.format);
23282 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23283 v = Date.parseDate(value, 'Y-m-d');
23285 if(!v && this.altFormats){
23286 if(!this.altFormatsArray){
23287 this.altFormatsArray = this.altFormats.split("|");
23289 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23290 v = Date.parseDate(value, this.altFormatsArray[i]);
23296 formatDate : function(date, fmt)
23298 return (!date || !(date instanceof Date)) ?
23299 date : date.dateFormat(fmt || this.format);
23302 onFocus : function()
23304 Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23308 onBlur : function()
23310 Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23312 var d = this.inputEl().getValue();
23319 showPopup : function()
23321 this.picker().show();
23325 this.fireEvent('showpopup', this, this.date);
23328 hidePopup : function()
23330 if(this.isInline) {
23333 this.picker().hide();
23334 this.viewMode = this.startViewMode;
23337 this.fireEvent('hidepopup', this, this.date);
23341 onMousedown: function(e)
23343 e.stopPropagation();
23344 e.preventDefault();
23349 Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23353 setValue: function(v)
23355 if(this.fireEvent('beforeselect', this, v) !== false){
23356 var d = new Date(this.parseDate(v) ).clearTime();
23358 if(isNaN(d.getTime())){
23359 this.date = this.viewDate = '';
23360 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23364 v = this.formatDate(d);
23366 Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23368 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23372 this.fireEvent('select', this, this.date);
23376 getValue: function()
23378 return this.formatDate(this.date);
23381 fireKey: function(e)
23383 if (!this.picker().isVisible()){
23384 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23390 var dateChanged = false,
23392 newDate, newViewDate;
23397 e.preventDefault();
23401 if (!this.keyboardNavigation) {
23404 dir = e.keyCode == 37 ? -1 : 1;
23407 newDate = this.moveYear(this.date, dir);
23408 newViewDate = this.moveYear(this.viewDate, dir);
23409 } else if (e.shiftKey){
23410 newDate = this.moveMonth(this.date, dir);
23411 newViewDate = this.moveMonth(this.viewDate, dir);
23413 newDate = new Date(this.date);
23414 newDate.setUTCDate(this.date.getUTCDate() + dir);
23415 newViewDate = new Date(this.viewDate);
23416 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23418 if (this.dateWithinRange(newDate)){
23419 this.date = newDate;
23420 this.viewDate = newViewDate;
23421 this.setValue(this.formatDate(this.date));
23423 e.preventDefault();
23424 dateChanged = true;
23429 if (!this.keyboardNavigation) {
23432 dir = e.keyCode == 38 ? -1 : 1;
23434 newDate = this.moveYear(this.date, dir);
23435 newViewDate = this.moveYear(this.viewDate, dir);
23436 } else if (e.shiftKey){
23437 newDate = this.moveMonth(this.date, dir);
23438 newViewDate = this.moveMonth(this.viewDate, dir);
23440 newDate = new Date(this.date);
23441 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23442 newViewDate = new Date(this.viewDate);
23443 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23445 if (this.dateWithinRange(newDate)){
23446 this.date = newDate;
23447 this.viewDate = newViewDate;
23448 this.setValue(this.formatDate(this.date));
23450 e.preventDefault();
23451 dateChanged = true;
23455 this.setValue(this.formatDate(this.date));
23457 e.preventDefault();
23460 this.setValue(this.formatDate(this.date));
23474 onClick: function(e)
23476 e.stopPropagation();
23477 e.preventDefault();
23479 var target = e.getTarget();
23481 if(target.nodeName.toLowerCase() === 'i'){
23482 target = Roo.get(target).dom.parentNode;
23485 var nodeName = target.nodeName;
23486 var className = target.className;
23487 var html = target.innerHTML;
23488 //Roo.log(nodeName);
23490 switch(nodeName.toLowerCase()) {
23492 switch(className) {
23498 var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23499 switch(this.viewMode){
23501 this.viewDate = this.moveMonth(this.viewDate, dir);
23505 this.viewDate = this.moveYear(this.viewDate, dir);
23511 var date = new Date();
23512 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23514 this.setValue(this.formatDate(this.date));
23521 if (className.indexOf('disabled') < 0) {
23522 if (!this.viewDate) {
23523 this.viewDate = new Date();
23525 this.viewDate.setUTCDate(1);
23526 if (className.indexOf('month') > -1) {
23527 this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23529 var year = parseInt(html, 10) || 0;
23530 this.viewDate.setUTCFullYear(year);
23534 if(this.singleMode){
23535 this.setValue(this.formatDate(this.viewDate));
23546 //Roo.log(className);
23547 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23548 var day = parseInt(html, 10) || 1;
23549 var year = (this.viewDate || new Date()).getUTCFullYear(),
23550 month = (this.viewDate || new Date()).getUTCMonth();
23552 if (className.indexOf('old') > -1) {
23559 } else if (className.indexOf('new') > -1) {
23567 //Roo.log([year,month,day]);
23568 this.date = this.UTCDate(year, month, day,0,0,0,0);
23569 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23571 //Roo.log(this.formatDate(this.date));
23572 this.setValue(this.formatDate(this.date));
23579 setStartDate: function(startDate)
23581 this.startDate = startDate || -Infinity;
23582 if (this.startDate !== -Infinity) {
23583 this.startDate = this.parseDate(this.startDate);
23586 this.updateNavArrows();
23589 setEndDate: function(endDate)
23591 this.endDate = endDate || Infinity;
23592 if (this.endDate !== Infinity) {
23593 this.endDate = this.parseDate(this.endDate);
23596 this.updateNavArrows();
23599 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23601 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23602 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23603 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23605 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23606 return parseInt(d, 10);
23609 this.updateNavArrows();
23612 updateNavArrows: function()
23614 if(this.singleMode){
23618 var d = new Date(this.viewDate),
23619 year = d.getUTCFullYear(),
23620 month = d.getUTCMonth();
23622 Roo.each(this.picker().select('.prev', true).elements, function(v){
23624 switch (this.viewMode) {
23627 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23633 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23640 Roo.each(this.picker().select('.next', true).elements, function(v){
23642 switch (this.viewMode) {
23645 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23651 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23659 moveMonth: function(date, dir)
23664 var new_date = new Date(date.valueOf()),
23665 day = new_date.getUTCDate(),
23666 month = new_date.getUTCMonth(),
23667 mag = Math.abs(dir),
23669 dir = dir > 0 ? 1 : -1;
23672 // If going back one month, make sure month is not current month
23673 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23675 return new_date.getUTCMonth() == month;
23677 // If going forward one month, make sure month is as expected
23678 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23680 return new_date.getUTCMonth() != new_month;
23682 new_month = month + dir;
23683 new_date.setUTCMonth(new_month);
23684 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23685 if (new_month < 0 || new_month > 11) {
23686 new_month = (new_month + 12) % 12;
23689 // For magnitudes >1, move one month at a time...
23690 for (var i=0; i<mag; i++) {
23691 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23692 new_date = this.moveMonth(new_date, dir);
23694 // ...then reset the day, keeping it in the new month
23695 new_month = new_date.getUTCMonth();
23696 new_date.setUTCDate(day);
23698 return new_month != new_date.getUTCMonth();
23701 // Common date-resetting loop -- if date is beyond end of month, make it
23704 new_date.setUTCDate(--day);
23705 new_date.setUTCMonth(new_month);
23710 moveYear: function(date, dir)
23712 return this.moveMonth(date, dir*12);
23715 dateWithinRange: function(date)
23717 return date >= this.startDate && date <= this.endDate;
23723 this.picker().remove();
23726 validateValue : function(value)
23728 if(this.getVisibilityEl().hasClass('hidden')){
23732 if(value.length < 1) {
23733 if(this.allowBlank){
23739 if(value.length < this.minLength){
23742 if(value.length > this.maxLength){
23746 var vt = Roo.form.VTypes;
23747 if(!vt[this.vtype](value, this)){
23751 if(typeof this.validator == "function"){
23752 var msg = this.validator(value);
23758 if(this.regex && !this.regex.test(value)){
23762 if(typeof(this.parseDate(value)) == 'undefined'){
23766 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23770 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23780 this.date = this.viewDate = '';
23782 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23787 Roo.apply(Roo.bootstrap.form.DateField, {
23798 html: '<i class="fa fa-arrow-left"/>'
23808 html: '<i class="fa fa-arrow-right"/>'
23850 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23851 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23852 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23853 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23854 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23867 navFnc: 'FullYear',
23872 navFnc: 'FullYear',
23877 Roo.apply(Roo.bootstrap.form.DateField, {
23881 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23885 cls: 'datepicker-days',
23889 cls: 'table-condensed',
23891 Roo.bootstrap.form.DateField.head,
23895 Roo.bootstrap.form.DateField.footer
23902 cls: 'datepicker-months',
23906 cls: 'table-condensed',
23908 Roo.bootstrap.form.DateField.head,
23909 Roo.bootstrap.form.DateField.content,
23910 Roo.bootstrap.form.DateField.footer
23917 cls: 'datepicker-years',
23921 cls: 'table-condensed',
23923 Roo.bootstrap.form.DateField.head,
23924 Roo.bootstrap.form.DateField.content,
23925 Roo.bootstrap.form.DateField.footer
23944 * @class Roo.bootstrap.form.TimeField
23945 * @extends Roo.bootstrap.form.Input
23946 * Bootstrap DateField class
23950 * Create a new TimeField
23951 * @param {Object} config The config object
23954 Roo.bootstrap.form.TimeField = function(config){
23955 Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23959 * Fires when this field show.
23960 * @param {Roo.bootstrap.form.DateField} thisthis
23961 * @param {Mixed} date The date value
23966 * Fires when this field hide.
23967 * @param {Roo.bootstrap.form.DateField} this
23968 * @param {Mixed} date The date value
23973 * Fires when select a date.
23974 * @param {Roo.bootstrap.form.DateField} this
23975 * @param {Mixed} date The date value
23981 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input, {
23984 * @cfg {String} format
23985 * The default time format string which can be overriden for localization support. The format must be
23986 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23990 getAutoCreate : function()
23992 this.after = '<i class="fa far fa-clock"></i>';
23993 return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23997 onRender: function(ct, position)
24000 Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
24002 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
24004 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24006 this.pop = this.picker().select('>.datepicker-time',true).first();
24007 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24009 this.picker().on('mousedown', this.onMousedown, this);
24010 this.picker().on('click', this.onClick, this);
24012 this.picker().addClass('datepicker-dropdown');
24017 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24018 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24019 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24020 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24021 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24022 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24026 fireKey: function(e){
24027 if (!this.picker().isVisible()){
24028 if (e.keyCode == 27) { // allow escape to hide and re-show picker
24034 e.preventDefault();
24042 this.onTogglePeriod();
24045 this.onIncrementMinutes();
24048 this.onDecrementMinutes();
24057 onClick: function(e) {
24058 e.stopPropagation();
24059 e.preventDefault();
24062 picker : function()
24064 return this.pickerEl;
24067 fillTime: function()
24069 var time = this.pop.select('tbody', true).first();
24071 time.dom.innerHTML = '';
24086 cls: 'hours-up fa fas fa-chevron-up'
24106 cls: 'minutes-up fa fas fa-chevron-up'
24127 cls: 'timepicker-hour',
24142 cls: 'timepicker-minute',
24157 cls: 'btn btn-primary period',
24179 cls: 'hours-down fa fas fa-chevron-down'
24199 cls: 'minutes-down fa fas fa-chevron-down'
24217 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24224 var hours = this.time.getHours();
24225 var minutes = this.time.getMinutes();
24238 hours = hours - 12;
24242 hours = '0' + hours;
24246 minutes = '0' + minutes;
24249 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24250 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24251 this.pop.select('button', true).first().dom.innerHTML = period;
24257 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24259 var cls = ['bottom'];
24261 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24268 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24272 //this.picker().setXY(20000,20000);
24273 this.picker().addClass(cls.join('-'));
24277 Roo.each(cls, function(c){
24282 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
24283 //_this.picker().setTop(_this.inputEl().getHeight());
24287 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
24289 //_this.picker().setTop(0 - _this.picker().getHeight());
24294 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24298 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24306 onFocus : function()
24308 Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24312 onBlur : function()
24314 Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24320 this.picker().show();
24325 this.fireEvent('show', this, this.date);
24330 this.picker().hide();
24333 this.fireEvent('hide', this, this.date);
24336 setTime : function()
24339 this.setValue(this.time.format(this.format));
24341 this.fireEvent('select', this, this.date);
24346 onMousedown: function(e){
24347 e.stopPropagation();
24348 e.preventDefault();
24351 onIncrementHours: function()
24353 Roo.log('onIncrementHours');
24354 this.time = this.time.add(Date.HOUR, 1);
24359 onDecrementHours: function()
24361 Roo.log('onDecrementHours');
24362 this.time = this.time.add(Date.HOUR, -1);
24366 onIncrementMinutes: function()
24368 Roo.log('onIncrementMinutes');
24369 this.time = this.time.add(Date.MINUTE, 1);
24373 onDecrementMinutes: function()
24375 Roo.log('onDecrementMinutes');
24376 this.time = this.time.add(Date.MINUTE, -1);
24380 onTogglePeriod: function()
24382 Roo.log('onTogglePeriod');
24383 this.time = this.time.add(Date.HOUR, 12);
24391 Roo.apply(Roo.bootstrap.form.TimeField, {
24395 cls: 'datepicker dropdown-menu',
24399 cls: 'datepicker-time',
24403 cls: 'table-condensed',
24432 cls: 'btn btn-info ok',
24460 * @class Roo.bootstrap.form.MonthField
24461 * @extends Roo.bootstrap.form.Input
24462 * Bootstrap MonthField class
24464 * @cfg {String} language default en
24467 * Create a new MonthField
24468 * @param {Object} config The config object
24471 Roo.bootstrap.form.MonthField = function(config){
24472 Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24477 * Fires when this field show.
24478 * @param {Roo.bootstrap.form.MonthField} this
24479 * @param {Mixed} date The date value
24484 * Fires when this field hide.
24485 * @param {Roo.bootstrap.form.MonthField} this
24486 * @param {Mixed} date The date value
24491 * Fires when select a date.
24492 * @param {Roo.bootstrap.form.MonthField} this
24493 * @param {String} oldvalue The old value
24494 * @param {String} newvalue The new value
24500 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input, {
24502 onRender: function(ct, position)
24505 Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24507 this.language = this.language || 'en';
24508 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24509 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24511 this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24512 this.isInline = false;
24513 this.isInput = true;
24514 this.component = this.el.select('.add-on', true).first() || false;
24515 this.component = (this.component && this.component.length === 0) ? false : this.component;
24516 this.hasInput = this.component && this.inputEL().length;
24518 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24520 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24522 this.picker().on('mousedown', this.onMousedown, this);
24523 this.picker().on('click', this.onClick, this);
24525 this.picker().addClass('datepicker-dropdown');
24527 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24528 v.setStyle('width', '189px');
24535 if(this.isInline) {
24541 setValue: function(v, suppressEvent)
24543 var o = this.getValue();
24545 Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24549 if(suppressEvent !== true){
24550 this.fireEvent('select', this, o, v);
24555 getValue: function()
24560 onClick: function(e)
24562 e.stopPropagation();
24563 e.preventDefault();
24565 var target = e.getTarget();
24567 if(target.nodeName.toLowerCase() === 'i'){
24568 target = Roo.get(target).dom.parentNode;
24571 var nodeName = target.nodeName;
24572 var className = target.className;
24573 var html = target.innerHTML;
24575 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24579 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24581 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24587 picker : function()
24589 return this.pickerEl;
24592 fillMonths: function()
24595 var months = this.picker().select('>.datepicker-months td', true).first();
24597 months.dom.innerHTML = '';
24603 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24606 months.createChild(month);
24615 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24616 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24619 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24620 e.removeClass('active');
24622 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24623 e.addClass('active');
24630 if(this.isInline) {
24634 this.picker().removeClass(['bottom', 'top']);
24636 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24638 * place to the top of element!
24642 this.picker().addClass('top');
24643 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24648 this.picker().addClass('bottom');
24650 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24653 onFocus : function()
24655 Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24659 onBlur : function()
24661 Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24663 var d = this.inputEl().getValue();
24672 this.picker().show();
24673 this.picker().select('>.datepicker-months', true).first().show();
24677 this.fireEvent('show', this, this.date);
24682 if(this.isInline) {
24685 this.picker().hide();
24686 this.fireEvent('hide', this, this.date);
24690 onMousedown: function(e)
24692 e.stopPropagation();
24693 e.preventDefault();
24698 Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24702 fireKey: function(e)
24704 if (!this.picker().isVisible()){
24705 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24716 e.preventDefault();
24720 dir = e.keyCode == 37 ? -1 : 1;
24722 this.vIndex = this.vIndex + dir;
24724 if(this.vIndex < 0){
24728 if(this.vIndex > 11){
24732 if(isNaN(this.vIndex)){
24736 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24742 dir = e.keyCode == 38 ? -1 : 1;
24744 this.vIndex = this.vIndex + dir * 4;
24746 if(this.vIndex < 0){
24750 if(this.vIndex > 11){
24754 if(isNaN(this.vIndex)){
24758 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24763 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24764 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24768 e.preventDefault();
24771 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24772 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24788 this.picker().remove();
24793 Roo.apply(Roo.bootstrap.form.MonthField, {
24812 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24813 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24818 Roo.apply(Roo.bootstrap.form.MonthField, {
24822 cls: 'datepicker dropdown-menu roo-dynamic',
24826 cls: 'datepicker-months',
24830 cls: 'table-condensed',
24832 Roo.bootstrap.form.DateField.content
24852 * @class Roo.bootstrap.form.CheckBox
24853 * @extends Roo.bootstrap.form.Input
24854 * Bootstrap CheckBox class
24856 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24857 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24858 * @cfg {String} boxLabel The text that appears beside the checkbox
24859 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24860 * @cfg {Boolean} checked initnal the element
24861 * @cfg {Boolean} inline inline the element (default false)
24862 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24863 * @cfg {String} tooltip label tooltip
24866 * Create a new CheckBox
24867 * @param {Object} config The config object
24870 Roo.bootstrap.form.CheckBox = function(config){
24871 Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24876 * Fires when the element is checked or unchecked.
24877 * @param {Roo.bootstrap.form.CheckBox} this This input
24878 * @param {Boolean} checked The new checked value
24883 * Fires when the element is click.
24884 * @param {Roo.bootstrap.form.CheckBox} this This input
24891 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input, {
24893 inputType: 'checkbox',
24902 // checkbox success does not make any sense really..
24907 getAutoCreate : function()
24909 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24915 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24918 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24924 type : this.inputType,
24925 value : this.inputValue,
24926 cls : 'roo-' + this.inputType, //'form-box',
24927 placeholder : this.placeholder || ''
24931 if(this.inputType != 'radio'){
24935 cls : 'roo-hidden-value',
24936 value : this.checked ? this.inputValue : this.valueOff
24941 if (this.weight) { // Validity check?
24942 cfg.cls += " " + this.inputType + "-" + this.weight;
24945 if (this.disabled) {
24946 input.disabled=true;
24950 input.checked = this.checked;
24955 input.name = this.name;
24957 if(this.inputType != 'radio'){
24958 hidden.name = this.name;
24959 input.name = '_hidden_' + this.name;
24964 input.cls += ' input-' + this.size;
24969 ['xs','sm','md','lg'].map(function(size){
24970 if (settings[size]) {
24971 cfg.cls += ' col-' + size + '-' + settings[size];
24975 var inputblock = input;
24977 if (this.before || this.after) {
24980 cls : 'input-group',
24985 inputblock.cn.push({
24987 cls : 'input-group-addon',
24992 inputblock.cn.push(input);
24994 if(this.inputType != 'radio'){
24995 inputblock.cn.push(hidden);
24999 inputblock.cn.push({
25001 cls : 'input-group-addon',
25007 var boxLabelCfg = false;
25013 //'for': id, // box label is handled by onclick - so no for...
25015 html: this.boxLabel
25018 boxLabelCfg.tooltip = this.tooltip;
25024 if (align ==='left' && this.fieldLabel.length) {
25025 // Roo.log("left and has label");
25030 cls : 'control-label',
25031 html : this.fieldLabel
25042 cfg.cn[1].cn.push(boxLabelCfg);
25045 if(this.labelWidth > 12){
25046 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25049 if(this.labelWidth < 13 && this.labelmd == 0){
25050 this.labelmd = this.labelWidth;
25053 if(this.labellg > 0){
25054 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25055 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25058 if(this.labelmd > 0){
25059 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25060 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25063 if(this.labelsm > 0){
25064 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25065 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25068 if(this.labelxs > 0){
25069 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25070 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25073 } else if ( this.fieldLabel.length) {
25074 // Roo.log(" label");
25078 tag: this.boxLabel ? 'span' : 'label',
25080 cls: 'control-label box-input-label',
25081 //cls : 'input-group-addon',
25082 html : this.fieldLabel
25089 cfg.cn.push(boxLabelCfg);
25094 // Roo.log(" no label && no align");
25095 cfg.cn = [ inputblock ] ;
25097 cfg.cn.push(boxLabelCfg);
25105 if(this.inputType != 'radio'){
25106 cfg.cn.push(hidden);
25114 * return the real input element.
25116 inputEl: function ()
25118 return this.el.select('input.roo-' + this.inputType,true).first();
25120 hiddenEl: function ()
25122 return this.el.select('input.roo-hidden-value',true).first();
25125 labelEl: function()
25127 return this.el.select('label.control-label',true).first();
25129 /* depricated... */
25133 return this.labelEl();
25136 boxLabelEl: function()
25138 return this.el.select('label.box-label',true).first();
25141 initEvents : function()
25143 // Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25145 this.inputEl().on('click', this.onClick, this);
25147 if (this.boxLabel) {
25148 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
25151 this.startValue = this.getValue();
25154 Roo.bootstrap.form.CheckBox.register(this);
25158 onClick : function(e)
25160 if(this.fireEvent('click', this, e) !== false){
25161 this.setChecked(!this.checked);
25166 setChecked : function(state,suppressEvent)
25168 this.startValue = this.getValue();
25170 if(this.inputType == 'radio'){
25172 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25173 e.dom.checked = false;
25176 this.inputEl().dom.checked = true;
25178 this.inputEl().dom.value = this.inputValue;
25180 if(suppressEvent !== true){
25181 this.fireEvent('check', this, true);
25189 this.checked = state;
25191 this.inputEl().dom.checked = state;
25194 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25196 if(suppressEvent !== true){
25197 this.fireEvent('check', this, state);
25203 getValue : function()
25205 if(this.inputType == 'radio'){
25206 return this.getGroupValue();
25209 return this.hiddenEl().dom.value;
25213 getGroupValue : function()
25215 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25219 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25222 setValue : function(v,suppressEvent)
25224 if(this.inputType == 'radio'){
25225 this.setGroupValue(v, suppressEvent);
25229 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25234 setGroupValue : function(v, suppressEvent)
25236 this.startValue = this.getValue();
25238 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25239 e.dom.checked = false;
25241 if(e.dom.value == v){
25242 e.dom.checked = true;
25246 if(suppressEvent !== true){
25247 this.fireEvent('check', this, true);
25255 validate : function()
25257 if(this.getVisibilityEl().hasClass('hidden')){
25263 (this.inputType == 'radio' && this.validateRadio()) ||
25264 (this.inputType == 'checkbox' && this.validateCheckbox())
25270 this.markInvalid();
25274 validateRadio : function()
25276 if(this.getVisibilityEl().hasClass('hidden')){
25280 if(this.allowBlank){
25286 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25287 if(!e.dom.checked){
25299 validateCheckbox : function()
25302 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25303 //return (this.getValue() == this.inputValue) ? true : false;
25306 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25314 for(var i in group){
25315 if(group[i].el.isVisible(true)){
25323 for(var i in group){
25328 r = (group[i].getValue() == group[i].inputValue) ? true : false;
25335 * Mark this field as valid
25337 markValid : function()
25341 this.fireEvent('valid', this);
25343 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25346 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25353 if(this.inputType == 'radio'){
25354 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25355 var fg = e.findParent('.form-group', false, true);
25356 if (Roo.bootstrap.version == 3) {
25357 fg.removeClass([_this.invalidClass, _this.validClass]);
25358 fg.addClass(_this.validClass);
25360 fg.removeClass(['is-valid', 'is-invalid']);
25361 fg.addClass('is-valid');
25369 var fg = this.el.findParent('.form-group', false, true);
25370 if (Roo.bootstrap.version == 3) {
25371 fg.removeClass([this.invalidClass, this.validClass]);
25372 fg.addClass(this.validClass);
25374 fg.removeClass(['is-valid', 'is-invalid']);
25375 fg.addClass('is-valid');
25380 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25386 for(var i in group){
25387 var fg = group[i].el.findParent('.form-group', false, true);
25388 if (Roo.bootstrap.version == 3) {
25389 fg.removeClass([this.invalidClass, this.validClass]);
25390 fg.addClass(this.validClass);
25392 fg.removeClass(['is-valid', 'is-invalid']);
25393 fg.addClass('is-valid');
25399 * Mark this field as invalid
25400 * @param {String} msg The validation message
25402 markInvalid : function(msg)
25404 if(this.allowBlank){
25410 this.fireEvent('invalid', this, msg);
25412 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25415 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25419 label.markInvalid();
25422 if(this.inputType == 'radio'){
25424 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25425 var fg = e.findParent('.form-group', false, true);
25426 if (Roo.bootstrap.version == 3) {
25427 fg.removeClass([_this.invalidClass, _this.validClass]);
25428 fg.addClass(_this.invalidClass);
25430 fg.removeClass(['is-invalid', 'is-valid']);
25431 fg.addClass('is-invalid');
25439 var fg = this.el.findParent('.form-group', false, true);
25440 if (Roo.bootstrap.version == 3) {
25441 fg.removeClass([_this.invalidClass, _this.validClass]);
25442 fg.addClass(_this.invalidClass);
25444 fg.removeClass(['is-invalid', 'is-valid']);
25445 fg.addClass('is-invalid');
25450 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25456 for(var i in group){
25457 var fg = group[i].el.findParent('.form-group', false, true);
25458 if (Roo.bootstrap.version == 3) {
25459 fg.removeClass([_this.invalidClass, _this.validClass]);
25460 fg.addClass(_this.invalidClass);
25462 fg.removeClass(['is-invalid', 'is-valid']);
25463 fg.addClass('is-invalid');
25469 clearInvalid : function()
25471 Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25473 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25475 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25477 if (label && label.iconEl) {
25478 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25479 label.iconEl.removeClass(['is-invalid', 'is-valid']);
25483 disable : function()
25485 if(this.inputType != 'radio'){
25486 Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25493 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25494 _this.getActionEl().addClass(this.disabledClass);
25495 e.dom.disabled = true;
25499 this.disabled = true;
25500 this.fireEvent("disable", this);
25504 enable : function()
25506 if(this.inputType != 'radio'){
25507 Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25514 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25515 _this.getActionEl().removeClass(this.disabledClass);
25516 e.dom.disabled = false;
25520 this.disabled = false;
25521 this.fireEvent("enable", this);
25525 setBoxLabel : function(v)
25530 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25536 Roo.apply(Roo.bootstrap.form.CheckBox, {
25541 * register a CheckBox Group
25542 * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25544 register : function(checkbox)
25546 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25547 this.groups[checkbox.groupId] = {};
25550 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25554 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25558 * fetch a CheckBox Group based on the group ID
25559 * @param {string} the group ID
25560 * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25562 get: function(groupId) {
25563 if (typeof(this.groups[groupId]) == 'undefined') {
25567 return this.groups[groupId] ;
25580 * @class Roo.bootstrap.form.Radio
25581 * @extends Roo.bootstrap.Component
25582 * Bootstrap Radio class
25583 * @cfg {String} boxLabel - the label associated
25584 * @cfg {String} value - the value of radio
25587 * Create a new Radio
25588 * @param {Object} config The config object
25590 Roo.bootstrap.form.Radio = function(config){
25591 Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25595 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25601 getAutoCreate : function()
25605 cls : 'form-group radio',
25610 html : this.boxLabel
25618 initEvents : function()
25620 this.parent().register(this);
25622 this.el.on('click', this.onClick, this);
25626 onClick : function(e)
25628 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25629 this.setChecked(true);
25633 setChecked : function(state, suppressEvent)
25635 this.parent().setValue(this.value, suppressEvent);
25639 setBoxLabel : function(v)
25644 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25659 * @class Roo.bootstrap.form.SecurePass
25660 * @extends Roo.bootstrap.form.Input
25661 * Bootstrap SecurePass class
25665 * Create a new SecurePass
25666 * @param {Object} config The config object
25669 Roo.bootstrap.form.SecurePass = function (config) {
25670 // these go here, so the translation tool can replace them..
25672 PwdEmpty: "Please type a password, and then retype it to confirm.",
25673 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25674 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25675 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25676 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25677 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25678 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25679 TooWeak: "Your password is Too Weak."
25681 this.meterLabel = "Password strength:";
25682 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25683 this.meterClass = [
25684 "roo-password-meter-tooweak",
25685 "roo-password-meter-weak",
25686 "roo-password-meter-medium",
25687 "roo-password-meter-strong",
25688 "roo-password-meter-grey"
25693 Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25696 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25698 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25700 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25701 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25702 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25703 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25704 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25705 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25706 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25716 * @cfg {String/Object} Label for the strength meter (defaults to
25717 * 'Password strength:')
25722 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25723 * ['Weak', 'Medium', 'Strong'])
25726 pwdStrengths: false,
25739 initEvents: function ()
25741 Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25743 if (this.el.is('input[type=password]') && Roo.isSafari) {
25744 this.el.on('keydown', this.SafariOnKeyDown, this);
25747 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25750 onRender: function (ct, position)
25752 Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25753 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25754 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25756 this.trigger.createChild({
25761 cls: 'roo-password-meter-grey col-xs-12',
25764 //width: this.meterWidth + 'px'
25768 cls: 'roo-password-meter-text'
25774 if (this.hideTrigger) {
25775 this.trigger.setDisplayed(false);
25777 this.setSize(this.width || '', this.height || '');
25780 onDestroy: function ()
25782 if (this.trigger) {
25783 this.trigger.removeAllListeners();
25784 this.trigger.remove();
25787 this.wrap.remove();
25789 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25792 checkStrength: function ()
25794 var pwd = this.inputEl().getValue();
25795 if (pwd == this._lastPwd) {
25800 if (this.ClientSideStrongPassword(pwd)) {
25802 } else if (this.ClientSideMediumPassword(pwd)) {
25804 } else if (this.ClientSideWeakPassword(pwd)) {
25810 Roo.log('strength1: ' + strength);
25812 //var pm = this.trigger.child('div/div/div').dom;
25813 var pm = this.trigger.child('div/div');
25814 pm.removeClass(this.meterClass);
25815 pm.addClass(this.meterClass[strength]);
25818 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25820 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25822 this._lastPwd = pwd;
25826 Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25828 this._lastPwd = '';
25830 var pm = this.trigger.child('div/div');
25831 pm.removeClass(this.meterClass);
25832 pm.addClass('roo-password-meter-grey');
25835 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25838 this.inputEl().dom.type='password';
25841 validateValue: function (value)
25843 if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25846 if (value.length == 0) {
25847 if (this.allowBlank) {
25848 this.clearInvalid();
25852 this.markInvalid(this.errors.PwdEmpty);
25853 this.errorMsg = this.errors.PwdEmpty;
25861 if (!value.match(/[\x21-\x7e]+/)) {
25862 this.markInvalid(this.errors.PwdBadChar);
25863 this.errorMsg = this.errors.PwdBadChar;
25866 if (value.length < 6) {
25867 this.markInvalid(this.errors.PwdShort);
25868 this.errorMsg = this.errors.PwdShort;
25871 if (value.length > 16) {
25872 this.markInvalid(this.errors.PwdLong);
25873 this.errorMsg = this.errors.PwdLong;
25877 if (this.ClientSideStrongPassword(value)) {
25879 } else if (this.ClientSideMediumPassword(value)) {
25881 } else if (this.ClientSideWeakPassword(value)) {
25888 if (strength < 2) {
25889 //this.markInvalid(this.errors.TooWeak);
25890 this.errorMsg = this.errors.TooWeak;
25895 console.log('strength2: ' + strength);
25897 //var pm = this.trigger.child('div/div/div').dom;
25899 var pm = this.trigger.child('div/div');
25900 pm.removeClass(this.meterClass);
25901 pm.addClass(this.meterClass[strength]);
25903 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25905 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25907 this.errorMsg = '';
25911 CharacterSetChecks: function (type)
25914 this.fResult = false;
25917 isctype: function (character, type)
25920 case this.kCapitalLetter:
25921 if (character >= 'A' && character <= 'Z') {
25926 case this.kSmallLetter:
25927 if (character >= 'a' && character <= 'z') {
25933 if (character >= '0' && character <= '9') {
25938 case this.kPunctuation:
25939 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25950 IsLongEnough: function (pwd, size)
25952 return !(pwd == null || isNaN(size) || pwd.length < size);
25955 SpansEnoughCharacterSets: function (word, nb)
25957 if (!this.IsLongEnough(word, nb))
25962 var characterSetChecks = new Array(
25963 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25964 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25967 for (var index = 0; index < word.length; ++index) {
25968 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25969 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25970 characterSetChecks[nCharSet].fResult = true;
25977 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25978 if (characterSetChecks[nCharSet].fResult) {
25983 if (nCharSets < nb) {
25989 ClientSideStrongPassword: function (pwd)
25991 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25994 ClientSideMediumPassword: function (pwd)
25996 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25999 ClientSideWeakPassword: function (pwd)
26001 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26005 Roo.htmleditor = {};
26008 * @class Roo.htmleditor.Filter
26009 * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26010 * @cfg {DomElement} node The node to iterate and filter
26011 * @cfg {boolean|String|Array} tag Tags to replace
26013 * Create a new Filter.
26014 * @param {Object} config Configuration options
26019 Roo.htmleditor.Filter = function(cfg) {
26020 Roo.apply(this.cfg);
26021 // this does not actually call walk as it's really just a abstract class
26025 Roo.htmleditor.Filter.prototype = {
26031 // overrride to do replace comments.
26032 replaceComment : false,
26034 // overrride to do replace or do stuff with tags..
26035 replaceTag : false,
26037 walk : function(dom)
26039 Roo.each( Array.from(dom.childNodes), function( e ) {
26042 case e.nodeType == 8 && this.replaceComment !== false: // comment
26043 this.replaceComment(e);
26046 case e.nodeType != 1: //not a node.
26049 case this.tag === true: // everything
26050 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26051 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26052 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26053 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26054 if (this.replaceTag && false === this.replaceTag(e)) {
26057 if (e.hasChildNodes()) {
26062 default: // tags .. that do not match.
26063 if (e.hasChildNodes()) {
26073 removeNodeKeepChildren : function( node)
26076 ar = Array.from(node.childNodes);
26077 for (var i = 0; i < ar.length; i++) {
26079 node.removeChild(ar[i]);
26080 // what if we need to walk these???
26081 node.parentNode.insertBefore(ar[i], node);
26084 node.parentNode.removeChild(node);
26089 * @class Roo.htmleditor.FilterAttributes
26090 * clean attributes and styles including http:// etc.. in attribute
26092 * Run a new Attribute Filter
26093 * @param {Object} config Configuration options
26095 Roo.htmleditor.FilterAttributes = function(cfg)
26097 Roo.apply(this, cfg);
26098 this.attrib_black = this.attrib_black || [];
26099 this.attrib_white = this.attrib_white || [];
26101 this.attrib_clean = this.attrib_clean || [];
26102 this.style_white = this.style_white || [];
26103 this.style_black = this.style_black || [];
26104 this.walk(cfg.node);
26107 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26109 tag: true, // all tags
26111 attrib_black : false, // array
26112 attrib_clean : false,
26113 attrib_white : false,
26115 style_white : false,
26116 style_black : false,
26119 replaceTag : function(node)
26121 if (!node.attributes || !node.attributes.length) {
26125 for (var i = node.attributes.length-1; i > -1 ; i--) {
26126 var a = node.attributes[i];
26128 if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26129 node.removeAttribute(a.name);
26135 if (a.name.toLowerCase().substr(0,2)=='on') {
26136 node.removeAttribute(a.name);
26141 if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26142 node.removeAttribute(a.name);
26145 if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26146 this.cleanAttr(node,a.name,a.value); // fixme..
26149 if (a.name == 'style') {
26150 this.cleanStyle(node,a.name,a.value);
26153 /// clean up MS crap..
26154 // tecnically this should be a list of valid class'es..
26157 if (a.name == 'class') {
26158 if (a.value.match(/^Mso/)) {
26159 node.removeAttribute('class');
26162 if (a.value.match(/^body$/)) {
26163 node.removeAttribute('class');
26173 return true; // clean children
26176 cleanAttr: function(node, n,v)
26179 if (v.match(/^\./) || v.match(/^\//)) {
26182 if (v.match(/^(http|https):\/\//)
26183 || v.match(/^mailto:/)
26184 || v.match(/^ftp:/)
26185 || v.match(/^data:/)
26189 if (v.match(/^#/)) {
26192 if (v.match(/^\{/)) { // allow template editing.
26195 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26196 node.removeAttribute(n);
26199 cleanStyle : function(node, n,v)
26201 if (v.match(/expression/)) { //XSS?? should we even bother..
26202 node.removeAttribute(n);
26206 var parts = v.split(/;/);
26209 Roo.each(parts, function(p) {
26210 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26214 var l = p.split(':').shift().replace(/\s+/g,'');
26215 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26217 if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26221 // only allow 'c whitelisted system attributes'
26222 if ( this.style_white.length && style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26230 if (clean.length) {
26231 node.setAttribute(n, clean.join(';'));
26233 node.removeAttribute(n);
26242 * @class Roo.htmleditor.FilterBlack
26243 * remove blacklisted elements.
26245 * Run a new Blacklisted Filter
26246 * @param {Object} config Configuration options
26249 Roo.htmleditor.FilterBlack = function(cfg)
26251 Roo.apply(this, cfg);
26252 this.walk(cfg.node);
26255 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26257 tag : true, // all elements.
26259 replaceTag : function(n)
26261 n.parentNode.removeChild(n);
26265 * @class Roo.htmleditor.FilterComment
26268 * Run a new Comments Filter
26269 * @param {Object} config Configuration options
26271 Roo.htmleditor.FilterComment = function(cfg)
26273 this.walk(cfg.node);
26276 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26279 replaceComment : function(n)
26281 n.parentNode.removeChild(n);
26284 * @class Roo.htmleditor.FilterKeepChildren
26285 * remove tags but keep children
26287 * Run a new Keep Children Filter
26288 * @param {Object} config Configuration options
26291 Roo.htmleditor.FilterKeepChildren = function(cfg)
26293 Roo.apply(this, cfg);
26294 if (this.tag === false) {
26295 return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26298 if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26299 this.cleanNamespace = true;
26302 this.walk(cfg.node);
26305 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26307 cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26309 replaceTag : function(node)
26311 // walk children...
26312 //Roo.log(node.tagName);
26313 var ar = Array.from(node.childNodes);
26316 for (var i = 0; i < ar.length; i++) {
26318 if (e.nodeType == 1) {
26320 (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26321 || // array and it matches
26322 (typeof(this.tag) == 'string' && this.tag == e.tagName)
26324 (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26326 (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26328 this.replaceTag(ar[i]); // child is blacklisted as well...
26333 ar = Array.from(node.childNodes);
26334 for (var i = 0; i < ar.length; i++) {
26336 node.removeChild(ar[i]);
26337 // what if we need to walk these???
26338 node.parentNode.insertBefore(ar[i], node);
26339 if (this.tag !== false) {
26344 //Roo.log("REMOVE:" + node.tagName);
26345 node.parentNode.removeChild(node);
26346 return false; // don't walk children
26351 * @class Roo.htmleditor.FilterParagraph
26352 * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26353 * like on 'push' to remove the <p> tags and replace them with line breaks.
26355 * Run a new Paragraph Filter
26356 * @param {Object} config Configuration options
26359 Roo.htmleditor.FilterParagraph = function(cfg)
26361 // no need to apply config.
26362 this.walk(cfg.node);
26365 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26372 replaceTag : function(node)
26375 if (node.childNodes.length == 1 &&
26376 node.childNodes[0].nodeType == 3 &&
26377 node.childNodes[0].textContent.trim().length < 1
26379 // remove and replace with '<BR>';
26380 node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26381 return false; // no need to walk..
26383 var ar = Array.from(node.childNodes);
26384 for (var i = 0; i < ar.length; i++) {
26385 node.removeChild(ar[i]);
26386 // what if we need to walk these???
26387 node.parentNode.insertBefore(ar[i], node);
26389 // now what about this?
26393 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26394 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26395 node.parentNode.removeChild(node);
26402 * @class Roo.htmleditor.FilterSpan
26403 * filter span's with no attributes out..
26405 * Run a new Span Filter
26406 * @param {Object} config Configuration options
26409 Roo.htmleditor.FilterSpan = function(cfg)
26411 // no need to apply config.
26412 this.walk(cfg.node);
26415 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26421 replaceTag : function(node)
26423 if (node.attributes && node.attributes.length > 0) {
26424 return true; // walk if there are any.
26426 Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26432 * @class Roo.htmleditor.FilterTableWidth
26433 try and remove table width data - as that frequently messes up other stuff.
26435 * was cleanTableWidths.
26437 * Quite often pasting from word etc.. results in tables with column and widths.
26438 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26441 * Run a new Table Filter
26442 * @param {Object} config Configuration options
26445 Roo.htmleditor.FilterTableWidth = function(cfg)
26447 // no need to apply config.
26448 this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26449 this.walk(cfg.node);
26452 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26457 replaceTag: function(node) {
26461 if (node.hasAttribute('width')) {
26462 node.removeAttribute('width');
26466 if (node.hasAttribute("style")) {
26469 var styles = node.getAttribute("style").split(";");
26471 Roo.each(styles, function(s) {
26472 if (!s.match(/:/)) {
26475 var kv = s.split(":");
26476 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26479 // what ever is left... we allow.
26482 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26483 if (!nstyle.length) {
26484 node.removeAttribute('style');
26488 return true; // continue doing children..
26491 * @class Roo.htmleditor.FilterWord
26492 * try and clean up all the mess that Word generates.
26494 * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters
26497 * Run a new Span Filter
26498 * @param {Object} config Configuration options
26501 Roo.htmleditor.FilterWord = function(cfg)
26503 // no need to apply config.
26504 this.replaceDocBullets(cfg.node);
26506 this.replaceAname(cfg.node);
26507 // this is disabled as the removal is done by other filters;
26508 // this.walk(cfg.node);
26513 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
26519 * Clean up MS wordisms...
26521 replaceTag : function(node)
26524 // no idea what this does - span with text, replaceds with just text.
26526 node.nodeName == 'SPAN' &&
26527 !node.hasAttributes() &&
26528 node.childNodes.length == 1 &&
26529 node.firstChild.nodeName == "#text"
26531 var textNode = node.firstChild;
26532 node.removeChild(textNode);
26533 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26534 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26536 node.parentNode.insertBefore(textNode, node);
26537 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26538 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26541 node.parentNode.removeChild(node);
26542 return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
26547 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26548 node.parentNode.removeChild(node);
26549 return false; // dont do chidlren
26551 //Roo.log(node.tagName);
26552 // remove - but keep children..
26553 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26554 //Roo.log('-- removed');
26555 while (node.childNodes.length) {
26556 var cn = node.childNodes[0];
26557 node.removeChild(cn);
26558 node.parentNode.insertBefore(cn, node);
26559 // move node to parent - and clean it..
26560 if (cn.nodeType == 1) {
26561 this.replaceTag(cn);
26565 node.parentNode.removeChild(node);
26566 /// no need to iterate chidlren = it's got none..
26567 //this.iterateChildren(node, this.cleanWord);
26568 return false; // no need to iterate children.
26571 if (node.className.length) {
26573 var cn = node.className.split(/\W+/);
26575 Roo.each(cn, function(cls) {
26576 if (cls.match(/Mso[a-zA-Z]+/)) {
26581 node.className = cna.length ? cna.join(' ') : '';
26583 node.removeAttribute("class");
26587 if (node.hasAttribute("lang")) {
26588 node.removeAttribute("lang");
26591 if (node.hasAttribute("style")) {
26593 var styles = node.getAttribute("style").split(";");
26595 Roo.each(styles, function(s) {
26596 if (!s.match(/:/)) {
26599 var kv = s.split(":");
26600 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26603 // what ever is left... we allow.
26606 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26607 if (!nstyle.length) {
26608 node.removeAttribute('style');
26611 return true; // do children
26617 styleToObject: function(node)
26619 var styles = (node.getAttribute("style") || '').split(";");
26621 Roo.each(styles, function(s) {
26622 if (!s.match(/:/)) {
26625 var kv = s.split(":");
26627 // what ever is left... we allow.
26628 ret[kv[0].trim()] = kv[1];
26634 replaceAname : function (doc)
26636 // replace all the a/name without..
26637 var aa = Array.from(doc.getElementsByTagName('a'));
26638 for (var i = 0; i < aa.length; i++) {
26640 if (a.hasAttribute("name")) {
26641 a.removeAttribute("name");
26643 if (a.hasAttribute("href")) {
26646 // reparent children.
26647 this.removeNodeKeepChildren(a);
26657 replaceDocBullets : function(doc)
26659 // this is a bit odd - but it appears some indents use ql-indent-1
26660 //Roo.log(doc.innerHTML);
26662 var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
26663 for( var i = 0; i < listpara.length; i ++) {
26664 listpara[i].className = "MsoListParagraph";
26667 listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
26668 for( var i = 0; i < listpara.length; i ++) {
26669 listpara[i].className = "MsoListParagraph";
26671 listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
26672 for( var i = 0; i < listpara.length; i ++) {
26673 listpara[i].className = "MsoListParagraph";
26675 listpara = Array.from(doc.getElementsByClassName('ql-indent-1'));
26676 for( var i = 0; i < listpara.length; i ++) {
26677 listpara[i].className = "MsoListParagraph";
26680 // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
26681 var htwo = Array.from(doc.getElementsByTagName('h2'));
26682 for( var i = 0; i < htwo.length; i ++) {
26683 if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
26684 htwo[i].className = "MsoListParagraph";
26687 listpara = Array.from(doc.getElementsByClassName('MsoNormal'));
26688 for( var i = 0; i < listpara.length; i ++) {
26689 if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
26690 listpara[i].className = "MsoListParagraph";
26692 listpara[i].className = "MsoNormalx";
26696 listpara = doc.getElementsByClassName('MsoListParagraph');
26697 // Roo.log(doc.innerHTML);
26701 while(listpara.length) {
26703 this.replaceDocBullet(listpara.item(0));
26710 replaceDocBullet : function(p)
26712 // gather all the siblings.
26714 parent = p.parentNode,
26715 doc = parent.ownerDocument,
26718 var listtype = 'ul';
26720 if (ns.nodeType != 1) {
26721 ns = ns.nextSibling;
26724 if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
26727 var spans = ns.getElementsByTagName('span');
26728 if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
26730 ns = ns.nextSibling;
26732 if (spans.length && spans[0].hasAttribute('style')) {
26733 var style = this.styleToObject(spans[0]);
26734 if (typeof(style['font-family']) != 'undefined' && !style['font-family'].match(/Symbol/)) {
26741 var spans = ns.getElementsByTagName('span');
26742 if (!spans.length) {
26745 var has_list = false;
26746 for(var i = 0; i < spans.length; i++) {
26747 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
26756 ns = ns.nextSibling;
26760 if (!items.length) {
26765 var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
26766 parent.insertBefore(ul, p);
26768 var stack = [ ul ];
26769 var last_li = false;
26771 var margin_to_depth = {};
26774 items.forEach(function(n, ipos) {
26775 //Roo.log("got innertHMLT=" + n.innerHTML);
26777 var spans = n.getElementsByTagName('span');
26778 if (!spans.length) {
26779 //Roo.log("No spans found");
26781 parent.removeChild(n);
26784 return; // skip it...
26790 for(var i = 0; i < spans.length; i++) {
26792 style = this.styleToObject(spans[i]);
26793 if (typeof(style['mso-list']) == 'undefined') {
26796 if (listtype == 'ol') {
26797 num = spans[i].innerText.replace(/[^0-9]+]/g,'') * 1;
26799 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
26802 //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
26803 style = this.styleToObject(n); // mo-list is from the parent node.
26804 if (typeof(style['mso-list']) == 'undefined') {
26805 //Roo.log("parent is missing level");
26807 parent.removeChild(n);
26812 var margin = style['margin-left'];
26813 if (typeof(margin_to_depth[margin]) == 'undefined') {
26815 margin_to_depth[margin] = max_margins;
26817 nlvl = margin_to_depth[margin] ;
26821 var nul = doc.createElement(listtype); // what about number lists...
26823 last_li = doc.createElement('li');
26824 stack[lvl].appendChild(last_li);
26826 last_li.appendChild(nul);
26832 // not starting at 1..
26833 if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
26834 stack[nlvl].setAttribute("start", num);
26837 var nli = stack[nlvl].appendChild(doc.createElement('li'));
26839 nli.innerHTML = n.innerHTML;
26840 //Roo.log("innerHTML = " + n.innerHTML);
26841 parent.removeChild(n);
26857 * @class Roo.htmleditor.FilterStyleToTag
26858 * part of the word stuff... - certain 'styles' should be converted to tags.
26860 * font-weight: bold -> bold
26861 * ?? super / subscrit etc..
26864 * Run a new style to tag filter.
26865 * @param {Object} config Configuration options
26867 Roo.htmleditor.FilterStyleToTag = function(cfg)
26871 B : [ 'fontWeight' , 'bold'],
26872 I : [ 'fontStyle' , 'italic'],
26873 //pre : [ 'font-style' , 'italic'],
26874 // h1.. h6 ?? font-size?
26875 SUP : [ 'verticalAlign' , 'super' ],
26876 SUB : [ 'verticalAlign' , 'sub' ]
26881 Roo.apply(this, cfg);
26884 this.walk(cfg.node);
26891 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
26893 tag: true, // all tags
26898 replaceTag : function(node)
26902 if (node.getAttribute("style") === null) {
26906 for (var k in this.tags) {
26907 if (node.style[this.tags[k][0]] == this.tags[k][1]) {
26909 node.style.removeProperty(this.tags[k][0]);
26912 if (!inject.length) {
26915 var cn = Array.from(node.childNodes);
26917 Roo.each(inject, function(t) {
26918 var nc = node.ownerDocument.createElement(t);
26919 nn.appendChild(nc);
26922 for(var i = 0;i < cn.length;cn++) {
26923 node.removeChild(cn[i]);
26924 nn.appendChild(cn[i]);
26926 return true /// iterate thru
26930 * @class Roo.htmleditor.FilterLongBr
26931 * BR/BR/BR - keep a maximum of 2...
26933 * Run a new Long BR Filter
26934 * @param {Object} config Configuration options
26937 Roo.htmleditor.FilterLongBr = function(cfg)
26939 // no need to apply config.
26940 this.walk(cfg.node);
26943 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
26950 replaceTag : function(node)
26953 var ps = node.nextSibling;
26954 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26955 ps = ps.nextSibling;
26958 if (!ps && [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) {
26959 node.parentNode.removeChild(node); // remove last BR inside one fo these tags
26963 if (!ps || ps.nodeType != 1) {
26967 if (!ps || ps.tagName != 'BR') {
26976 if (!node.previousSibling) {
26979 var ps = node.previousSibling;
26981 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26982 ps = ps.previousSibling;
26984 if (!ps || ps.nodeType != 1) {
26987 // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
26988 if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
26992 node.parentNode.removeChild(node); // remove me...
26994 return false; // no need to do children
27001 * @class Roo.htmleditor.FilterBlock
27002 * removes id / data-block and contenteditable that are associated with blocks
27003 * usage should be done on a cloned copy of the dom
27005 * Run a new Attribute Filter { node : xxxx }}
27006 * @param {Object} config Configuration options
27008 Roo.htmleditor.FilterBlock = function(cfg)
27010 Roo.apply(this, cfg);
27011 var qa = cfg.node.querySelectorAll;
27012 this.removeAttributes('data-block');
27013 this.removeAttributes('contenteditable');
27014 this.removeAttributes('id');
27018 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27020 node: true, // all tags
27023 removeAttributes : function(attr)
27025 var ar = this.node.querySelectorAll('*[' + attr + ']');
27026 for (var i =0;i<ar.length;i++) {
27027 ar[i].removeAttribute(attr);
27036 * @class Roo.htmleditor.KeyEnter
27037 * Handle Enter press..
27038 * @cfg {Roo.HtmlEditorCore} core the editor.
27040 * Create a new Filter.
27041 * @param {Object} config Configuration options
27048 Roo.htmleditor.KeyEnter = function(cfg) {
27049 Roo.apply(this, cfg);
27050 // this does not actually call walk as it's really just a abstract class
27052 Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
27055 //Roo.htmleditor.KeyEnter.i = 0;
27058 Roo.htmleditor.KeyEnter.prototype = {
27062 keypress : function(e)
27064 if (e.charCode != 13 && e.charCode != 10) {
27065 Roo.log([e.charCode,e]);
27068 e.preventDefault();
27069 // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
27070 var doc = this.core.doc;
27074 var sel = this.core.getSelection();
27075 var range = sel.getRangeAt(0);
27076 var n = range.commonAncestorContainer;
27077 var pc = range.closest([ 'ol', 'ul']);
27078 var pli = range.closest('li');
27079 if (!pc || e.ctrlKey) {
27080 // on it list, or ctrl pressed.
27082 sel.insertNode('br', 'after');
27084 // only do this if we have ctrl key..
27085 var br = doc.createElement('br');
27086 br.className = 'clear';
27087 br.setAttribute('style', 'clear: both');
27088 sel.insertNode(br, 'after');
27092 this.core.undoManager.addEvent();
27093 this.core.fireEditorEvent(e);
27097 // deal with <li> insetion
27098 if (pli.innerText.trim() == '' &&
27099 pli.previousSibling &&
27100 pli.previousSibling.nodeName == 'LI' &&
27101 pli.previousSibling.innerText.trim() == '') {
27102 pli.parentNode.removeChild(pli.previousSibling);
27103 sel.cursorAfter(pc);
27104 this.core.undoManager.addEvent();
27105 this.core.fireEditorEvent(e);
27109 var li = doc.createElement('LI');
27110 li.innerHTML = ' ';
27111 if (!pli || !pli.firstSibling) {
27112 pc.appendChild(li);
27114 pli.parentNode.insertBefore(li, pli.firstSibling);
27116 sel.cursorText (li.firstChild);
27118 this.core.undoManager.addEvent();
27119 this.core.fireEditorEvent(e);
27131 * @class Roo.htmleditor.Block
27132 * Base class for html editor blocks - do not use it directly .. extend it..
27133 * @cfg {DomElement} node The node to apply stuff to.
27134 * @cfg {String} friendly_name the name that appears in the context bar about this block
27135 * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
27138 * Create a new Filter.
27139 * @param {Object} config Configuration options
27142 Roo.htmleditor.Block = function(cfg)
27144 // do nothing .. should not be called really.
27147 * factory method to get the block from an element (using cache if necessary)
27149 * @param {HtmlElement} the dom element
27151 Roo.htmleditor.Block.factory = function(node)
27153 var cc = Roo.htmleditor.Block.cache;
27154 var id = Roo.get(node).id;
27155 if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
27156 Roo.htmleditor.Block.cache[id].readElement(node);
27157 return Roo.htmleditor.Block.cache[id];
27159 var db = node.getAttribute('data-block');
27161 db = node.nodeName.toLowerCase().toUpperCaseFirst();
27163 var cls = Roo.htmleditor['Block' + db];
27164 if (typeof(cls) == 'undefined') {
27165 //Roo.log(node.getAttribute('data-block'));
27166 Roo.log("OOps missing block : " + 'Block' + db);
27169 Roo.htmleditor.Block.cache[id] = new cls({ node: node });
27170 return Roo.htmleditor.Block.cache[id]; /// should trigger update element
27174 * initalize all Elements from content that are 'blockable'
27176 * @param the body element
27178 Roo.htmleditor.Block.initAll = function(body, type)
27180 if (typeof(type) == 'undefined') {
27181 var ia = Roo.htmleditor.Block.initAll;
27187 Roo.each(Roo.get(body).query(type), function(e) {
27188 Roo.htmleditor.Block.factory(e);
27191 // question goes here... do we need to clear out this cache sometimes?
27192 // or show we make it relivant to the htmleditor.
27193 Roo.htmleditor.Block.cache = {};
27195 Roo.htmleditor.Block.prototype = {
27199 // used by context menu
27200 friendly_name : 'Based Block',
27202 // text for button to delete this element
27203 deleteTitle : false,
27207 * Update a node with values from this object
27208 * @param {DomElement} node
27210 updateElement : function(node)
27212 Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
27215 * convert to plain HTML for calling insertAtCursor..
27217 toHTML : function()
27219 return Roo.DomHelper.markup(this.toObject());
27222 * used by readEleemnt to extract data from a node
27223 * may need improving as it's pretty basic
27225 * @param {DomElement} node
27226 * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
27227 * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
27228 * @param {String} style the style property - eg. text-align
27230 getVal : function(node, tag, attr, style)
27233 if (tag !== true && n.tagName != tag.toUpperCase()) {
27234 // in theory we could do figure[3] << 3rd figure? or some more complex search..?
27235 // but kiss for now.
27236 n = node.getElementsByTagName(tag).item(0);
27241 if (attr === false) {
27244 if (attr == 'html') {
27245 return n.innerHTML;
27247 if (attr == 'style') {
27248 return n.style[style];
27251 return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
27255 * create a DomHelper friendly object - for use with
27256 * Roo.DomHelper.markup / overwrite / etc..
27259 toObject : function()
27264 * Read a node that has a 'data-block' property - and extract the values from it.
27265 * @param {DomElement} node - the node
27267 readElement : function(node)
27278 * @class Roo.htmleditor.BlockFigure
27279 * Block that has an image and a figcaption
27280 * @cfg {String} image_src the url for the image
27281 * @cfg {String} align (left|right) alignment for the block default left
27282 * @cfg {String} caption the text to appear below (and in the alt tag)
27283 * @cfg {String} caption_display (block|none) display or not the caption
27284 * @cfg {String|number} image_width the width of the image number or %?
27285 * @cfg {String|number} image_height the height of the image number or %?
27288 * Create a new Filter.
27289 * @param {Object} config Configuration options
27292 Roo.htmleditor.BlockFigure = function(cfg)
27295 this.readElement(cfg.node);
27296 this.updateElement(cfg.node);
27298 Roo.apply(this, cfg);
27300 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
27307 caption_display : 'block',
27313 // margin: '2%', not used
27315 text_align: 'left', // (left|right) alignment for the text caption default left. - not used at present
27318 // used by context menu
27319 friendly_name : 'Image with caption',
27320 deleteTitle : "Delete Image and Caption",
27322 contextMenu : function(toolbar)
27325 var block = function() {
27326 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
27330 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
27332 var syncValue = toolbar.editorcore.syncValue;
27338 xtype : 'TextItem',
27340 xns : rooui.Toolbar //Boostrap?
27344 text: 'Change Image URL',
27347 click: function (btn, state)
27351 Roo.MessageBox.show({
27352 title : "Image Source URL",
27353 msg : "Enter the url for the image",
27354 buttons: Roo.MessageBox.OKCANCEL,
27355 fn: function(btn, val){
27362 toolbar.editorcore.onEditorEvent();
27366 //multiline: multiline,
27368 value : b.image_src
27372 xns : rooui.Toolbar
27377 text: 'Change Link URL',
27380 click: function (btn, state)
27384 Roo.MessageBox.show({
27385 title : "Link URL",
27386 msg : "Enter the url for the link - leave blank to have no link",
27387 buttons: Roo.MessageBox.OKCANCEL,
27388 fn: function(btn, val){
27395 toolbar.editorcore.onEditorEvent();
27399 //multiline: multiline,
27405 xns : rooui.Toolbar
27409 text: 'Show Video URL',
27412 click: function (btn, state)
27414 Roo.MessageBox.alert("Video URL",
27415 block().video_url == '' ? 'This image is not linked ot a video' :
27416 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
27419 xns : rooui.Toolbar
27424 xtype : 'TextItem',
27426 xns : rooui.Toolbar //Boostrap?
27429 xtype : 'ComboBox',
27430 allowBlank : false,
27431 displayField : 'val',
27434 triggerAction : 'all',
27436 valueField : 'val',
27440 select : function (combo, r, index)
27442 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27444 b.width = r.get('val');
27447 toolbar.editorcore.onEditorEvent();
27452 xtype : 'SimpleStore',
27465 xtype : 'TextItem',
27467 xns : rooui.Toolbar //Boostrap?
27470 xtype : 'ComboBox',
27471 allowBlank : false,
27472 displayField : 'val',
27475 triggerAction : 'all',
27477 valueField : 'val',
27481 select : function (combo, r, index)
27483 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27485 b.align = r.get('val');
27488 toolbar.editorcore.onEditorEvent();
27493 xtype : 'SimpleStore',
27507 text: 'Hide Caption',
27508 name : 'caption_display',
27510 enableToggle : true,
27511 setValue : function(v) {
27512 // this trigger toggle.
27514 this.setText(v ? "Hide Caption" : "Show Caption");
27515 this.setPressed(v != 'block');
27518 toggle: function (btn, state)
27521 b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
27522 this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
27525 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27526 toolbar.editorcore.onEditorEvent();
27529 xns : rooui.Toolbar
27535 * create a DomHelper friendly object - for use with
27536 * Roo.DomHelper.markup / overwrite / etc..
27538 toObject : function()
27540 var d = document.createElement('div');
27541 d.innerHTML = this.caption;
27543 var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0;
27545 var iw = this.align == 'center' ? this.width : '100%';
27548 contenteditable : 'false',
27549 src : this.image_src,
27550 alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
27553 maxWidth : iw + ' !important', // this is not getting rendered?
27559 '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
27561 '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' +
27566 if (this.href.length > 0) {
27570 contenteditable : 'true',
27578 if (this.video_url.length > 0) {
27583 allowfullscreen : true,
27584 width : 420, // these are for video tricks - that we replace the outer
27586 src : this.video_url,
27592 // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
27593 var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
27598 'data-block' : 'Figure',
27599 'data-width' : this.width,
27600 contenteditable : 'false',
27604 float : this.align ,
27605 maxWidth : this.align == 'center' ? '100% !important' : (this.width + ' !important'),
27606 width : this.align == 'center' ? '100%' : this.width,
27608 padding: this.align == 'center' ? '0' : '0 10px' ,
27609 textAlign : this.align // seems to work for email..
27614 align : this.align,
27620 'data-display' : this.caption_display,
27622 textAlign : 'left',
27624 lineHeight : '24px',
27625 display : this.caption_display,
27626 maxWidth : (this.align == 'center' ? this.width : '100%' ) + ' !important',
27628 width: this.align == 'center' ? this.width : '100%'
27632 cls : this.cls.length > 0 ? (this.cls + '-thumbnail' ) : '',
27637 marginTop : '16px',
27643 // we can not rely on yahoo syndication to use CSS elements - so have to use '<i>' to encase stuff.
27645 contenteditable : true,
27661 readElement : function(node)
27663 // this should not really come from the link...
27664 this.video_url = this.getVal(node, 'div', 'src');
27665 this.cls = this.getVal(node, 'div', 'class');
27666 this.href = this.getVal(node, 'a', 'href');
27669 this.image_src = this.getVal(node, 'img', 'src');
27671 this.align = this.getVal(node, 'figure', 'align');
27672 var figcaption = this.getVal(node, 'figcaption', false);
27673 if (figcaption !== '') {
27674 this.caption = this.getVal(figcaption, 'i', 'html');
27678 this.caption_display = this.getVal(node, 'figcaption', 'data-display');
27679 //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
27680 this.width = this.getVal(node, true, 'data-width');
27681 //this.margin = this.getVal(node, 'figure', 'style', 'margin');
27684 removeNode : function()
27701 * @class Roo.htmleditor.BlockTable
27702 * Block that manages a table
27705 * Create a new Filter.
27706 * @param {Object} config Configuration options
27709 Roo.htmleditor.BlockTable = function(cfg)
27712 this.readElement(cfg.node);
27713 this.updateElement(cfg.node);
27715 Roo.apply(this, cfg);
27718 for(var r = 0; r < this.no_row; r++) {
27720 for(var c = 0; c < this.no_col; c++) {
27721 this.rows[r][c] = this.emptyCell();
27728 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
27737 // used by context menu
27738 friendly_name : 'Table',
27739 deleteTitle : 'Delete Table',
27740 // context menu is drawn once..
27742 contextMenu : function(toolbar)
27745 var block = function() {
27746 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
27750 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
27752 var syncValue = toolbar.editorcore.syncValue;
27758 xtype : 'TextItem',
27760 xns : rooui.Toolbar //Boostrap?
27763 xtype : 'ComboBox',
27764 allowBlank : false,
27765 displayField : 'val',
27768 triggerAction : 'all',
27770 valueField : 'val',
27774 select : function (combo, r, index)
27776 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27778 b.width = r.get('val');
27781 toolbar.editorcore.onEditorEvent();
27786 xtype : 'SimpleStore',
27798 xtype : 'TextItem',
27799 text : "Columns: ",
27800 xns : rooui.Toolbar //Boostrap?
27807 click : function (_self, e)
27809 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27810 block().removeColumn();
27812 toolbar.editorcore.onEditorEvent();
27815 xns : rooui.Toolbar
27821 click : function (_self, e)
27823 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27824 block().addColumn();
27826 toolbar.editorcore.onEditorEvent();
27829 xns : rooui.Toolbar
27833 xtype : 'TextItem',
27835 xns : rooui.Toolbar //Boostrap?
27842 click : function (_self, e)
27844 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27845 block().removeRow();
27847 toolbar.editorcore.onEditorEvent();
27850 xns : rooui.Toolbar
27856 click : function (_self, e)
27860 toolbar.editorcore.onEditorEvent();
27863 xns : rooui.Toolbar
27868 text: 'Reset Column Widths',
27871 click : function (_self, e)
27873 block().resetWidths();
27875 toolbar.editorcore.onEditorEvent();
27878 xns : rooui.Toolbar
27889 * create a DomHelper friendly object - for use with
27890 * Roo.DomHelper.markup / overwrite / etc..
27891 * ?? should it be called with option to hide all editing features?
27893 toObject : function()
27898 contenteditable : 'false', // this stops cell selection from picking the table.
27899 'data-block' : 'Table',
27902 border : 'solid 1px #000', // ??? hard coded?
27903 'border-collapse' : 'collapse'
27906 { tag : 'tbody' , cn : [] }
27910 // do we have a head = not really
27912 Roo.each(this.rows, function( row ) {
27917 border : 'solid 1px #000',
27923 ret.cn[0].cn.push(tr);
27924 // does the row have any properties? ?? height?
27926 Roo.each(row, function( cell ) {
27930 contenteditable : 'true',
27931 'data-block' : 'Td',
27935 if (cell.colspan > 1) {
27936 td.colspan = cell.colspan ;
27937 nc += cell.colspan;
27941 if (cell.rowspan > 1) {
27942 td.rowspan = cell.rowspan ;
27951 ncols = Math.max(nc, ncols);
27955 // add the header row..
27964 readElement : function(node)
27966 node = node ? node : this.node ;
27967 this.width = this.getVal(node, true, 'style', 'width') || '100%';
27971 var trs = Array.from(node.rows);
27972 trs.forEach(function(tr) {
27974 this.rows.push(row);
27978 Array.from(tr.cells).forEach(function(td) {
27981 colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
27982 rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
27983 style : td.hasAttribute('style') ? td.getAttribute('style') : '',
27984 html : td.innerHTML
27986 no_column += add.colspan;
27993 this.no_col = Math.max(this.no_col, no_column);
28000 normalizeRows: function()
28004 this.rows.forEach(function(row) {
28007 row = this.normalizeRow(row);
28009 row.forEach(function(c) {
28010 while (typeof(ret[rid][cid]) != 'undefined') {
28013 if (typeof(ret[rid]) == 'undefined') {
28019 if (c.rowspan < 2) {
28023 for(var i = 1 ;i < c.rowspan; i++) {
28024 if (typeof(ret[rid+i]) == 'undefined') {
28027 ret[rid+i][cid] = c;
28035 normalizeRow: function(row)
28038 row.forEach(function(c) {
28039 if (c.colspan < 2) {
28043 for(var i =0 ;i < c.colspan; i++) {
28051 deleteColumn : function(sel)
28053 if (!sel || sel.type != 'col') {
28056 if (this.no_col < 2) {
28060 this.rows.forEach(function(row) {
28061 var cols = this.normalizeRow(row);
28062 var col = cols[sel.col];
28063 if (col.colspan > 1) {
28073 removeColumn : function()
28075 this.deleteColumn({
28077 col : this.no_col-1
28079 this.updateElement();
28083 addColumn : function()
28086 this.rows.forEach(function(row) {
28087 row.push(this.emptyCell());
28090 this.updateElement();
28093 deleteRow : function(sel)
28095 if (!sel || sel.type != 'row') {
28099 if (this.no_row < 2) {
28103 var rows = this.normalizeRows();
28106 rows[sel.row].forEach(function(col) {
28107 if (col.rowspan > 1) {
28110 col.remove = 1; // flage it as removed.
28115 this.rows.forEach(function(row) {
28117 row.forEach(function(c) {
28118 if (typeof(c.remove) == 'undefined') {
28123 if (newrow.length > 0) {
28127 this.rows = newrows;
28132 this.updateElement();
28135 removeRow : function()
28139 row : this.no_row-1
28145 addRow : function()
28149 for (var i = 0; i < this.no_col; i++ ) {
28151 row.push(this.emptyCell());
28154 this.rows.push(row);
28155 this.updateElement();
28159 // the default cell object... at present...
28160 emptyCell : function() {
28161 return (new Roo.htmleditor.BlockTd({})).toObject();
28166 removeNode : function()
28173 resetWidths : function()
28175 Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
28176 var nn = Roo.htmleditor.Block.factory(n);
28178 nn.updateElement(n);
28191 * since selections really work on the table cell, then editing really should work from there
28193 * The original plan was to support merging etc... - but that may not be needed yet..
28195 * So this simple version will support:
28197 * adjust the width +/-
28198 * reset the width...
28207 * @class Roo.htmleditor.BlockTable
28208 * Block that manages a table
28211 * Create a new Filter.
28212 * @param {Object} config Configuration options
28215 Roo.htmleditor.BlockTd = function(cfg)
28218 this.readElement(cfg.node);
28219 this.updateElement(cfg.node);
28221 Roo.apply(this, cfg);
28226 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
28231 textAlign : 'left',
28238 // used by context menu
28239 friendly_name : 'Table Cell',
28240 deleteTitle : false, // use our customer delete
28242 // context menu is drawn once..
28244 contextMenu : function(toolbar)
28247 var cell = function() {
28248 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
28251 var table = function() {
28252 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
28256 var saveSel = function()
28258 lr = toolbar.editorcore.getSelection().getRangeAt(0);
28260 var restoreSel = function()
28264 toolbar.editorcore.focus();
28265 var cr = toolbar.editorcore.getSelection();
28266 cr.removeAllRanges();
28268 toolbar.editorcore.onEditorEvent();
28269 }).defer(10, this);
28275 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
28277 var syncValue = toolbar.editorcore.syncValue;
28284 text : 'Edit Table',
28286 click : function() {
28287 var t = toolbar.tb.selectedNode.closest('table');
28288 toolbar.editorcore.selectNode(t);
28289 toolbar.editorcore.onEditorEvent();
28298 xtype : 'TextItem',
28299 text : "Column Width: ",
28300 xns : rooui.Toolbar
28307 click : function (_self, e)
28309 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28310 cell().shrinkColumn();
28312 toolbar.editorcore.onEditorEvent();
28315 xns : rooui.Toolbar
28321 click : function (_self, e)
28323 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28324 cell().growColumn();
28326 toolbar.editorcore.onEditorEvent();
28329 xns : rooui.Toolbar
28333 xtype : 'TextItem',
28334 text : "Vertical Align: ",
28335 xns : rooui.Toolbar //Boostrap?
28338 xtype : 'ComboBox',
28339 allowBlank : false,
28340 displayField : 'val',
28343 triggerAction : 'all',
28345 valueField : 'val',
28349 select : function (combo, r, index)
28351 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28353 b.valign = r.get('val');
28356 toolbar.editorcore.onEditorEvent();
28361 xtype : 'SimpleStore',
28365 ['bottom'] // there are afew more...
28373 xtype : 'TextItem',
28374 text : "Merge Cells: ",
28375 xns : rooui.Toolbar
28384 click : function (_self, e)
28386 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28387 cell().mergeRight();
28388 //block().growColumn();
28390 toolbar.editorcore.onEditorEvent();
28393 xns : rooui.Toolbar
28400 click : function (_self, e)
28402 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28403 cell().mergeBelow();
28404 //block().growColumn();
28406 toolbar.editorcore.onEditorEvent();
28409 xns : rooui.Toolbar
28412 xtype : 'TextItem',
28414 xns : rooui.Toolbar
28422 click : function (_self, e)
28424 //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28427 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28428 toolbar.editorcore.onEditorEvent();
28432 xns : rooui.Toolbar
28436 xns : rooui.Toolbar
28445 xns : rooui.Toolbar,
28454 click : function (_self, e)
28458 cell().deleteColumn();
28460 toolbar.editorcore.selectNode(t.node);
28461 toolbar.editorcore.onEditorEvent();
28470 click : function (_self, e)
28473 cell().deleteRow();
28476 toolbar.editorcore.selectNode(t.node);
28477 toolbar.editorcore.onEditorEvent();
28484 xtype : 'Separator',
28491 click : function (_self, e)
28494 var nn = t.node.nextSibling || t.node.previousSibling;
28495 t.node.parentNode.removeChild(t.node);
28497 toolbar.editorcore.selectNode(nn, true);
28499 toolbar.editorcore.onEditorEvent();
28509 // align... << fixme
28517 * create a DomHelper friendly object - for use with
28518 * Roo.DomHelper.markup / overwrite / etc..
28519 * ?? should it be called with option to hide all editing features?
28522 * create a DomHelper friendly object - for use with
28523 * Roo.DomHelper.markup / overwrite / etc..
28524 * ?? should it be called with option to hide all editing features?
28526 toObject : function()
28530 contenteditable : 'true', // this stops cell selection from picking the table.
28531 'data-block' : 'Td',
28532 valign : this.valign,
28534 'text-align' : this.textAlign,
28535 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
28536 'border-collapse' : 'collapse',
28537 padding : '6px', // 8 for desktop / 4 for mobile
28538 'vertical-align': this.valign
28542 if (this.width != '') {
28543 ret.width = this.width;
28544 ret.style.width = this.width;
28548 if (this.colspan > 1) {
28549 ret.colspan = this.colspan ;
28551 if (this.rowspan > 1) {
28552 ret.rowspan = this.rowspan ;
28561 readElement : function(node)
28563 node = node ? node : this.node ;
28564 this.width = node.style.width;
28565 this.colspan = Math.max(1,1*node.getAttribute('colspan'));
28566 this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
28567 this.html = node.innerHTML;
28568 if (node.style.textAlign != '') {
28569 this.textAlign = node.style.textAlign;
28575 // the default cell object... at present...
28576 emptyCell : function() {
28580 textAlign : 'left',
28581 html : " " // is this going to be editable now?
28586 removeNode : function()
28588 return this.node.closest('table');
28596 toTableArray : function()
28599 var tab = this.node.closest('tr').closest('table');
28600 Array.from(tab.rows).forEach(function(r, ri){
28604 this.colWidths = [];
28605 var all_auto = true;
28606 Array.from(tab.rows).forEach(function(r, ri){
28609 Array.from(r.cells).forEach(function(ce, ci){
28614 colspan : ce.colSpan,
28615 rowspan : ce.rowSpan
28617 if (ce.isEqualNode(this.node)) {
28620 // if we have been filled up by a row?
28621 if (typeof(ret[rn][cn]) != 'undefined') {
28622 while(typeof(ret[rn][cn]) != 'undefined') {
28628 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
28629 this.colWidths[cn] = ce.style.width;
28630 if (this.colWidths[cn] != '') {
28636 if (c.colspan < 2 && c.rowspan < 2 ) {
28641 for(var j = 0; j < c.rowspan; j++) {
28642 if (typeof(ret[rn+j]) == 'undefined') {
28643 continue; // we have a problem..
28646 for(var i = 0; i < c.colspan; i++) {
28647 ret[rn+j][cn+i] = c;
28656 // initalize widths.?
28657 // either all widths or no widths..
28659 this.colWidths[0] = false; // no widths flag.
28670 mergeRight: function()
28673 // get the contents of the next cell along..
28674 var tr = this.node.closest('tr');
28675 var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
28676 if (i >= tr.childNodes.length - 1) {
28677 return; // no cells on right to merge with.
28679 var table = this.toTableArray();
28681 if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
28682 return; // nothing right?
28684 var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
28685 // right cell - must be same rowspan and on the same row.
28686 if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
28687 return; // right hand side is not same rowspan.
28692 this.node.innerHTML += ' ' + rc.cell.innerHTML;
28693 tr.removeChild(rc.cell);
28694 this.colspan += rc.colspan;
28695 this.node.setAttribute('colspan', this.colspan);
28697 var table = this.toTableArray();
28698 this.normalizeWidths(table);
28699 this.updateWidths(table);
28703 mergeBelow : function()
28705 var table = this.toTableArray();
28706 if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
28707 return; // no row below
28709 if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
28710 return; // nothing right?
28712 var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
28714 if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
28715 return; // right hand side is not same rowspan.
28717 this.node.innerHTML = this.node.innerHTML + rc.cell.innerHTML ;
28718 rc.cell.parentNode.removeChild(rc.cell);
28719 this.rowspan += rc.rowspan;
28720 this.node.setAttribute('rowspan', this.rowspan);
28725 if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
28728 var table = this.toTableArray();
28729 var cd = this.cellData;
28733 for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
28736 for(var c = cd.col; c < cd.col + cd.colspan; c++) {
28737 if (r == cd.row && c == cd.col) {
28738 this.node.removeAttribute('rowspan');
28739 this.node.removeAttribute('colspan');
28742 var ntd = this.node.cloneNode(); // which col/row should be 0..
28743 ntd.removeAttribute('id');
28744 ntd.style.width = this.colWidths[c];
28745 ntd.innerHTML = '';
28746 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1 };
28750 this.redrawAllCells(table);
28756 redrawAllCells: function(table)
28760 var tab = this.node.closest('tr').closest('table');
28761 var ctr = tab.rows[0].parentNode;
28762 Array.from(tab.rows).forEach(function(r, ri){
28764 Array.from(r.cells).forEach(function(ce, ci){
28765 ce.parentNode.removeChild(ce);
28767 r.parentNode.removeChild(r);
28769 for(var r = 0 ; r < table.length; r++) {
28770 var re = tab.rows[r];
28772 var re = tab.ownerDocument.createElement('tr');
28773 ctr.appendChild(re);
28774 for(var c = 0 ; c < table[r].length; c++) {
28775 if (table[r][c].cell === false) {
28779 re.appendChild(table[r][c].cell);
28781 table[r][c].cell = false;
28786 updateWidths : function(table)
28788 for(var r = 0 ; r < table.length; r++) {
28790 for(var c = 0 ; c < table[r].length; c++) {
28791 if (table[r][c].cell === false) {
28795 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
28796 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
28797 el.width = Math.floor(this.colWidths[c]) +'%';
28798 el.updateElement(el.node);
28800 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
28801 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
28803 for(var i = 0; i < table[r][c].colspan; i ++) {
28804 width += Math.floor(this.colWidths[c + i]);
28806 el.width = width +'%';
28807 el.updateElement(el.node);
28809 table[r][c].cell = false; // done
28813 normalizeWidths : function(table)
28815 if (this.colWidths[0] === false) {
28816 var nw = 100.0 / this.colWidths.length;
28817 this.colWidths.forEach(function(w,i) {
28818 this.colWidths[i] = nw;
28823 var t = 0, missing = [];
28825 this.colWidths.forEach(function(w,i) {
28827 this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
28828 var add = this.colWidths[i];
28837 var nc = this.colWidths.length;
28838 if (missing.length) {
28839 var mult = (nc - missing.length) / (1.0 * nc);
28841 var ew = (100 -t) / (1.0 * missing.length);
28842 this.colWidths.forEach(function(w,i) {
28844 this.colWidths[i] = w * mult;
28848 this.colWidths[i] = ew;
28850 // have to make up numbers..
28853 // now we should have all the widths..
28858 shrinkColumn : function()
28860 var table = this.toTableArray();
28861 this.normalizeWidths(table);
28862 var col = this.cellData.col;
28863 var nw = this.colWidths[col] * 0.8;
28867 var otherAdd = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
28868 this.colWidths.forEach(function(w,i) {
28870 this.colWidths[i] = nw;
28873 this.colWidths[i] += otherAdd
28875 this.updateWidths(table);
28878 growColumn : function()
28880 var table = this.toTableArray();
28881 this.normalizeWidths(table);
28882 var col = this.cellData.col;
28883 var nw = this.colWidths[col] * 1.2;
28887 var otherSub = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
28888 this.colWidths.forEach(function(w,i) {
28890 this.colWidths[i] = nw;
28893 this.colWidths[i] -= otherSub
28895 this.updateWidths(table);
28898 deleteRow : function()
28900 // delete this rows 'tr'
28901 // if any of the cells in this row have a rowspan > 1 && row!= this row..
28902 // then reduce the rowspan.
28903 var table = this.toTableArray();
28904 // this.cellData.row;
28905 for (var i =0;i< table[this.cellData.row].length ; i++) {
28906 var c = table[this.cellData.row][i];
28907 if (c.row != this.cellData.row) {
28910 c.cell.setAttribute('rowspan', c.rowspan);
28913 if (c.rowspan > 1) {
28915 c.cell.setAttribute('rowspan', c.rowspan);
28918 table.splice(this.cellData.row,1);
28919 this.redrawAllCells(table);
28922 deleteColumn : function()
28924 var table = this.toTableArray();
28926 for (var i =0;i< table.length ; i++) {
28927 var c = table[i][this.cellData.col];
28928 if (c.col != this.cellData.col) {
28929 table[i][this.cellData.col].colspan--;
28930 } else if (c.colspan > 1) {
28932 c.cell.setAttribute('colspan', c.colspan);
28934 table[i].splice(this.cellData.col,1);
28937 this.redrawAllCells(table);
28945 //<script type="text/javascript">
28948 * Based Ext JS Library 1.1.1
28949 * Copyright(c) 2006-2007, Ext JS, LLC.
28955 * @class Roo.HtmlEditorCore
28956 * @extends Roo.Component
28957 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
28959 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
28962 Roo.HtmlEditorCore = function(config){
28965 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
28970 * @event initialize
28971 * Fires when the editor is fully initialized (including the iframe)
28972 * @param {Roo.HtmlEditorCore} this
28977 * Fires when the editor is first receives the focus. Any insertion must wait
28978 * until after this event.
28979 * @param {Roo.HtmlEditorCore} this
28983 * @event beforesync
28984 * Fires before the textarea is updated with content from the editor iframe. Return false
28985 * to cancel the sync.
28986 * @param {Roo.HtmlEditorCore} this
28987 * @param {String} html
28991 * @event beforepush
28992 * Fires before the iframe editor is updated with content from the textarea. Return false
28993 * to cancel the push.
28994 * @param {Roo.HtmlEditorCore} this
28995 * @param {String} html
29000 * Fires when the textarea is updated with content from the editor iframe.
29001 * @param {Roo.HtmlEditorCore} this
29002 * @param {String} html
29007 * Fires when the iframe editor is updated with content from the textarea.
29008 * @param {Roo.HtmlEditorCore} this
29009 * @param {String} html
29014 * @event editorevent
29015 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
29016 * @param {Roo.HtmlEditorCore} this
29023 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
29025 // defaults : white / black...
29026 this.applyBlacklists();
29033 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
29037 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
29043 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
29048 * @cfg {Number} height (in pixels)
29052 * @cfg {Number} width (in pixels)
29056 * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
29057 * if you are doing an email editor, this probably needs disabling, it's designed
29062 * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
29064 enableBlocks : true,
29066 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
29069 stylesheets: false,
29071 * @cfg {String} language default en - language of text (usefull for rtl languages)
29077 * @cfg {boolean} allowComments - default false - allow comments in HTML source
29078 * - by default they are stripped - if you are editing email you may need this.
29080 allowComments: false,
29084 // private properties
29085 validationEvent : false,
29087 initialized : false,
29089 sourceEditMode : false,
29090 onFocus : Roo.emptyFn,
29092 hideMode:'offsets',
29096 // blacklist + whitelisted elements..
29103 undoManager : false,
29105 * Protected method that will not generally be called directly. It
29106 * is called when the editor initializes the iframe with HTML contents. Override this method if you
29107 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
29109 getDocMarkup : function(){
29113 // inherit styels from page...??
29114 if (this.stylesheets === false) {
29116 Roo.get(document.head).select('style').each(function(node) {
29117 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
29120 Roo.get(document.head).select('link').each(function(node) {
29121 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
29124 } else if (!this.stylesheets.length) {
29126 st = '<style type="text/css">' +
29127 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
29130 for (var i in this.stylesheets) {
29131 if (typeof(this.stylesheets[i]) != 'string') {
29134 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
29139 st += '<style type="text/css">' +
29140 'IMG { cursor: pointer } ' +
29143 st += '<meta name="google" content="notranslate">';
29145 var cls = 'notranslate roo-htmleditor-body';
29147 if(this.bodyCls.length){
29148 cls += ' ' + this.bodyCls;
29151 return '<html class="notranslate" translate="no"><head>' + st +
29152 //<style type="text/css">' +
29153 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
29155 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
29159 onRender : function(ct, position)
29162 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
29163 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
29166 this.el.dom.style.border = '0 none';
29167 this.el.dom.setAttribute('tabIndex', -1);
29168 this.el.addClass('x-hidden hide');
29172 if(Roo.isIE){ // fix IE 1px bogus margin
29173 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
29177 this.frameId = Roo.id();
29181 var iframe = this.owner.wrap.createChild({
29183 cls: 'form-control', // bootstrap..
29185 name: this.frameId,
29186 frameBorder : 'no',
29187 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
29192 this.iframe = iframe.dom;
29194 this.assignDocWin();
29196 this.doc.designMode = 'on';
29199 this.doc.write(this.getDocMarkup());
29203 var task = { // must defer to wait for browser to be ready
29205 //console.log("run task?" + this.doc.readyState);
29206 this.assignDocWin();
29207 if(this.doc.body || this.doc.readyState == 'complete'){
29209 this.doc.designMode="on";
29214 Roo.TaskMgr.stop(task);
29215 this.initEditor.defer(10, this);
29222 Roo.TaskMgr.start(task);
29227 onResize : function(w, h)
29229 Roo.log('resize: ' +w + ',' + h );
29230 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
29234 if(typeof w == 'number'){
29236 this.iframe.style.width = w + 'px';
29238 if(typeof h == 'number'){
29240 this.iframe.style.height = h + 'px';
29242 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
29249 * Toggles the editor between standard and source edit mode.
29250 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
29252 toggleSourceEdit : function(sourceEditMode){
29254 this.sourceEditMode = sourceEditMode === true;
29256 if(this.sourceEditMode){
29258 Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']); //FIXME - what's the BS styles for these
29261 Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
29262 //this.iframe.className = '';
29265 //this.setSize(this.owner.wrap.getSize());
29266 //this.fireEvent('editmodechange', this, this.sourceEditMode);
29273 * Protected method that will not generally be called directly. If you need/want
29274 * custom HTML cleanup, this is the method you should override.
29275 * @param {String} html The HTML to be cleaned
29276 * return {String} The cleaned HTML
29278 cleanHtml : function(html)
29280 html = String(html);
29281 if(html.length > 5){
29282 if(Roo.isSafari){ // strip safari nonsense
29283 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
29286 if(html == ' '){
29293 * HTML Editor -> Textarea
29294 * Protected method that will not generally be called directly. Syncs the contents
29295 * of the editor iframe with the textarea.
29297 syncValue : function()
29299 //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
29300 if(this.initialized){
29302 if (this.undoManager) {
29303 this.undoManager.addEvent();
29307 var bd = (this.doc.body || this.doc.documentElement);
29310 var sel = this.win.getSelection();
29312 var div = document.createElement('div');
29313 div.innerHTML = bd.innerHTML;
29314 var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
29315 if (gtx.length > 0) {
29316 var rm = gtx.item(0).parentNode;
29317 rm.parentNode.removeChild(rm);
29321 if (this.enableBlocks) {
29322 new Roo.htmleditor.FilterBlock({ node : div });
29325 var html = div.innerHTML;
29328 if (this.autoClean) {
29330 new Roo.htmleditor.FilterAttributes({
29332 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
29333 attrib_clean : ['href', 'src' ]
29336 var tidy = new Roo.htmleditor.TidySerializer({
29339 html = tidy.serialize(div);
29345 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
29346 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
29348 html = '<div style="'+m[0]+'">' + html + '</div>';
29351 html = this.cleanHtml(html);
29352 // fix up the special chars.. normaly like back quotes in word...
29353 // however we do not want to do this with chinese..
29354 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
29356 var cc = match.charCodeAt();
29358 // Get the character value, handling surrogate pairs
29359 if (match.length == 2) {
29360 // It's a surrogate pair, calculate the Unicode code point
29361 var high = match.charCodeAt(0) - 0xD800;
29362 var low = match.charCodeAt(1) - 0xDC00;
29363 cc = (high * 0x400) + low + 0x10000;
29365 (cc >= 0x4E00 && cc < 0xA000 ) ||
29366 (cc >= 0x3400 && cc < 0x4E00 ) ||
29367 (cc >= 0xf900 && cc < 0xfb00 )
29372 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
29373 return "&#" + cc + ";";
29380 if(this.owner.fireEvent('beforesync', this, html) !== false){
29381 this.el.dom.value = html;
29382 this.owner.fireEvent('sync', this, html);
29388 * TEXTAREA -> EDITABLE
29389 * Protected method that will not generally be called directly. Pushes the value of the textarea
29390 * into the iframe editor.
29392 pushValue : function()
29394 //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
29395 if(this.initialized){
29396 var v = this.el.dom.value.trim();
29399 if(this.owner.fireEvent('beforepush', this, v) !== false){
29400 var d = (this.doc.body || this.doc.documentElement);
29403 this.el.dom.value = d.innerHTML;
29404 this.owner.fireEvent('push', this, v);
29406 if (this.autoClean) {
29407 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
29408 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
29410 if (this.enableBlocks) {
29411 Roo.htmleditor.Block.initAll(this.doc.body);
29414 this.updateLanguage();
29416 var lc = this.doc.body.lastChild;
29417 if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
29418 // add an extra line at the end.
29419 this.doc.body.appendChild(this.doc.createElement('br'));
29427 deferFocus : function(){
29428 this.focus.defer(10, this);
29432 focus : function(){
29433 if(this.win && !this.sourceEditMode){
29440 assignDocWin: function()
29442 var iframe = this.iframe;
29445 this.doc = iframe.contentWindow.document;
29446 this.win = iframe.contentWindow;
29448 // if (!Roo.get(this.frameId)) {
29451 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
29452 // this.win = Roo.get(this.frameId).dom.contentWindow;
29454 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
29458 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
29459 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
29464 initEditor : function(){
29465 //console.log("INIT EDITOR");
29466 this.assignDocWin();
29470 this.doc.designMode="on";
29472 this.doc.write(this.getDocMarkup());
29475 var dbody = (this.doc.body || this.doc.documentElement);
29476 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
29477 // this copies styles from the containing element into thsi one..
29478 // not sure why we need all of this..
29479 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
29481 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
29482 //ss['background-attachment'] = 'fixed'; // w3c
29483 dbody.bgProperties = 'fixed'; // ie
29484 dbody.setAttribute("translate", "no");
29486 //Roo.DomHelper.applyStyles(dbody, ss);
29487 Roo.EventManager.on(this.doc, {
29489 'mouseup': this.onEditorEvent,
29490 'dblclick': this.onEditorEvent,
29491 'click': this.onEditorEvent,
29492 'keyup': this.onEditorEvent,
29497 Roo.EventManager.on(this.doc, {
29498 'paste': this.onPasteEvent,
29502 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
29505 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
29506 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
29508 this.initialized = true;
29511 // initialize special key events - enter
29512 new Roo.htmleditor.KeyEnter({core : this});
29516 this.owner.fireEvent('initialize', this);
29519 // this is to prevent a href clicks resulting in a redirect?
29521 onPasteEvent : function(e,v)
29523 // I think we better assume paste is going to be a dirty load of rubish from word..
29525 // even pasting into a 'email version' of this widget will have to clean up that mess.
29526 var cd = (e.browserEvent.clipboardData || window.clipboardData);
29528 // check what type of paste - if it's an image, then handle it differently.
29529 if (cd.files && cd.files.length > 0) {
29531 var urlAPI = (window.createObjectURL && window) ||
29532 (window.URL && URL.revokeObjectURL && URL) ||
29533 (window.webkitURL && webkitURL);
29535 var url = urlAPI.createObjectURL( cd.files[0]);
29536 this.insertAtCursor('<img src=" + url + ">');
29539 if (cd.types.indexOf('text/html') < 0 ) {
29543 var html = cd.getData('text/html'); // clipboard event
29544 if (cd.types.indexOf('text/rtf') > -1) {
29545 var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
29546 images = parser.doc ? parser.doc.getElementsByType('pict') : [];
29551 images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
29552 .map(function(g) { return g.toDataURL(); })
29553 .filter(function(g) { return g != 'about:blank'; });
29556 html = this.cleanWordChars(html);
29558 var d = (new DOMParser().parseFromString(html, 'text/html')).body;
29561 var sn = this.getParentElement();
29562 // check if d contains a table, and prevent nesting??
29563 //Roo.log(d.getElementsByTagName('table'));
29565 //Roo.log(sn.closest('table'));
29566 if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
29567 e.preventDefault();
29568 this.insertAtCursor("You can not nest tables");
29569 //Roo.log("prevent?"); // fixme -
29575 if (images.length > 0) {
29576 // replace all v:imagedata - with img.
29577 var ar = Array.from(d.getElementsByTagName('v:imagedata'));
29578 Roo.each(ar, function(node) {
29579 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
29580 node.parentNode.removeChild(node);
29584 Roo.each(d.getElementsByTagName('img'), function(img, i) {
29585 img.setAttribute('src', images[i]);
29588 if (this.autoClean) {
29589 new Roo.htmleditor.FilterWord({ node : d });
29591 new Roo.htmleditor.FilterStyleToTag({ node : d });
29592 new Roo.htmleditor.FilterAttributes({
29594 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
29595 attrib_clean : ['href', 'src' ]
29597 new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
29598 // should be fonts..
29599 new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
29600 new Roo.htmleditor.FilterParagraph({ node : d });
29601 new Roo.htmleditor.FilterSpan({ node : d });
29602 new Roo.htmleditor.FilterLongBr({ node : d });
29603 new Roo.htmleditor.FilterComment({ node : d });
29607 if (this.enableBlocks) {
29609 Array.from(d.getElementsByTagName('img')).forEach(function(img) {
29610 if (img.closest('figure')) { // assume!! that it's aready
29613 var fig = new Roo.htmleditor.BlockFigure({
29614 image_src : img.src
29616 fig.updateElement(img); // replace it..
29622 this.insertAtCursor(d.innerHTML.replace(/ /g,' '));
29623 if (this.enableBlocks) {
29624 Roo.htmleditor.Block.initAll(this.doc.body);
29628 e.preventDefault();
29630 // default behaveiour should be our local cleanup paste? (optional?)
29631 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
29632 //this.owner.fireEvent('paste', e, v);
29635 onDestroy : function(){
29641 //for (var i =0; i < this.toolbars.length;i++) {
29642 // // fixme - ask toolbars for heights?
29643 // this.toolbars[i].onDestroy();
29646 //this.wrap.dom.innerHTML = '';
29647 //this.wrap.remove();
29652 onFirstFocus : function(){
29654 this.assignDocWin();
29655 this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
29657 this.activated = true;
29660 if(Roo.isGecko){ // prevent silly gecko errors
29662 var s = this.win.getSelection();
29663 if(!s.focusNode || s.focusNode.nodeType != 3){
29664 var r = s.getRangeAt(0);
29665 r.selectNodeContents((this.doc.body || this.doc.documentElement));
29670 this.execCmd('useCSS', true);
29671 this.execCmd('styleWithCSS', false);
29674 this.owner.fireEvent('activate', this);
29678 adjustFont: function(btn){
29679 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
29680 //if(Roo.isSafari){ // safari
29683 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
29684 if(Roo.isSafari){ // safari
29685 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
29686 v = (v < 10) ? 10 : v;
29687 v = (v > 48) ? 48 : v;
29688 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
29693 v = Math.max(1, v+adjust);
29695 this.execCmd('FontSize', v );
29698 onEditorEvent : function(e)
29702 if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
29703 return; // we do not handle this.. (undo manager does..)
29705 // in theory this detects if the last element is not a br, then we try and do that.
29706 // its so clicking in space at bottom triggers adding a br and moving the cursor.
29708 e.target.nodeName == 'BODY' &&
29709 e.type == "mouseup" &&
29710 this.doc.body.lastChild
29712 var lc = this.doc.body.lastChild;
29713 // gtx-trans is google translate plugin adding crap.
29714 while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
29715 lc = lc.previousSibling;
29717 if (lc.nodeType == 1 && lc.nodeName != 'BR') {
29718 // if last element is <BR> - then dont do anything.
29720 var ns = this.doc.createElement('br');
29721 this.doc.body.appendChild(ns);
29722 range = this.doc.createRange();
29723 range.setStartAfter(ns);
29724 range.collapse(true);
29725 var sel = this.win.getSelection();
29726 sel.removeAllRanges();
29727 sel.addRange(range);
29733 this.fireEditorEvent(e);
29734 // this.updateToolbar();
29735 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
29738 fireEditorEvent: function(e)
29740 this.owner.fireEvent('editorevent', this, e);
29743 insertTag : function(tg)
29745 // could be a bit smarter... -> wrap the current selected tRoo..
29746 if (tg.toLowerCase() == 'span' ||
29747 tg.toLowerCase() == 'code' ||
29748 tg.toLowerCase() == 'sup' ||
29749 tg.toLowerCase() == 'sub'
29752 range = this.createRange(this.getSelection());
29753 var wrappingNode = this.doc.createElement(tg.toLowerCase());
29754 wrappingNode.appendChild(range.extractContents());
29755 range.insertNode(wrappingNode);
29762 this.execCmd("formatblock", tg);
29763 this.undoManager.addEvent();
29766 insertText : function(txt)
29770 var range = this.createRange();
29771 range.deleteContents();
29772 //alert(Sender.getAttribute('label'));
29774 range.insertNode(this.doc.createTextNode(txt));
29775 this.undoManager.addEvent();
29781 * Executes a Midas editor command on the editor document and performs necessary focus and
29782 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
29783 * @param {String} cmd The Midas command
29784 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
29786 relayCmd : function(cmd, value)
29790 case 'justifyleft':
29791 case 'justifyright':
29792 case 'justifycenter':
29793 // if we are in a cell, then we will adjust the
29794 var n = this.getParentElement();
29795 var td = n.closest('td');
29797 var bl = Roo.htmleditor.Block.factory(td);
29798 bl.textAlign = cmd.replace('justify','');
29799 bl.updateElement();
29800 this.owner.fireEvent('editorevent', this);
29803 this.execCmd('styleWithCSS', true); //
29807 // if there is no selection, then we insert, and set the curson inside it..
29808 this.execCmd('styleWithCSS', false);
29818 this.execCmd(cmd, value);
29819 this.owner.fireEvent('editorevent', this);
29820 //this.updateToolbar();
29821 this.owner.deferFocus();
29825 * Executes a Midas editor command directly on the editor document.
29826 * For visual commands, you should use {@link #relayCmd} instead.
29827 * <b>This should only be called after the editor is initialized.</b>
29828 * @param {String} cmd The Midas command
29829 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
29831 execCmd : function(cmd, value){
29832 this.doc.execCommand(cmd, false, value === undefined ? null : value);
29839 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
29841 * @param {String} text | dom node..
29843 insertAtCursor : function(text)
29846 if(!this.activated){
29850 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
29854 // from jquery ui (MIT licenced)
29856 var win = this.win;
29858 if (win.getSelection && win.getSelection().getRangeAt) {
29860 // delete the existing?
29862 this.createRange(this.getSelection()).deleteContents();
29863 range = win.getSelection().getRangeAt(0);
29864 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
29865 range.insertNode(node);
29866 range = range.cloneRange();
29867 range.collapse(false);
29869 win.getSelection().removeAllRanges();
29870 win.getSelection().addRange(range);
29874 } else if (win.document.selection && win.document.selection.createRange) {
29875 // no firefox support
29876 var txt = typeof(text) == 'string' ? text : text.outerHTML;
29877 win.document.selection.createRange().pasteHTML(txt);
29880 // no firefox support
29881 var txt = typeof(text) == 'string' ? text : text.outerHTML;
29882 this.execCmd('InsertHTML', txt);
29890 mozKeyPress : function(e){
29892 var c = e.getCharCode(), cmd;
29895 c = String.fromCharCode(c).toLowerCase();
29909 // this.cleanUpPaste.defer(100, this);
29915 this.relayCmd(cmd);
29916 //this.win.focus();
29917 //this.execCmd(cmd);
29918 //this.deferFocus();
29919 e.preventDefault();
29927 fixKeys : function(){ // load time branching for fastest keydown performance
29931 return function(e){
29932 var k = e.getKey(), r;
29935 r = this.doc.selection.createRange();
29938 r.pasteHTML('    ');
29943 /// this is handled by Roo.htmleditor.KeyEnter
29946 r = this.doc.selection.createRange();
29948 var target = r.parentElement();
29949 if(!target || target.tagName.toLowerCase() != 'li'){
29951 r.pasteHTML('<br/>');
29958 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
29959 // this.cleanUpPaste.defer(100, this);
29965 }else if(Roo.isOpera){
29966 return function(e){
29967 var k = e.getKey();
29971 this.execCmd('InsertHTML','    ');
29975 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
29976 // this.cleanUpPaste.defer(100, this);
29981 }else if(Roo.isSafari){
29982 return function(e){
29983 var k = e.getKey();
29987 this.execCmd('InsertText','\t');
29991 this.mozKeyPress(e);
29993 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
29994 // this.cleanUpPaste.defer(100, this);
30002 getAllAncestors: function()
30004 var p = this.getSelectedNode();
30007 a.push(p); // push blank onto stack..
30008 p = this.getParentElement();
30012 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
30016 a.push(this.doc.body);
30020 lastSelNode : false,
30023 getSelection : function()
30025 this.assignDocWin();
30026 return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
30029 * Select a dom node
30030 * @param {DomElement} node the node to select
30032 selectNode : function(node, collapse)
30034 var nodeRange = node.ownerDocument.createRange();
30036 nodeRange.selectNode(node);
30038 nodeRange.selectNodeContents(node);
30040 if (collapse === true) {
30041 nodeRange.collapse(true);
30044 var s = this.win.getSelection();
30045 s.removeAllRanges();
30046 s.addRange(nodeRange);
30049 getSelectedNode: function()
30051 // this may only work on Gecko!!!
30053 // should we cache this!!!!
30057 var range = this.createRange(this.getSelection()).cloneRange();
30060 var parent = range.parentElement();
30062 var testRange = range.duplicate();
30063 testRange.moveToElementText(parent);
30064 if (testRange.inRange(range)) {
30067 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
30070 parent = parent.parentElement;
30075 // is ancestor a text element.
30076 var ac = range.commonAncestorContainer;
30077 if (ac.nodeType == 3) {
30078 ac = ac.parentNode;
30081 var ar = ac.childNodes;
30084 var other_nodes = [];
30085 var has_other_nodes = false;
30086 for (var i=0;i<ar.length;i++) {
30087 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
30090 // fullly contained node.
30092 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
30097 // probably selected..
30098 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
30099 other_nodes.push(ar[i]);
30103 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
30108 has_other_nodes = true;
30110 if (!nodes.length && other_nodes.length) {
30111 nodes= other_nodes;
30113 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
30121 createRange: function(sel)
30123 // this has strange effects when using with
30124 // top toolbar - not sure if it's a great idea.
30125 //this.editor.contentWindow.focus();
30126 if (typeof sel != "undefined") {
30128 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
30130 return this.doc.createRange();
30133 return this.doc.createRange();
30136 getParentElement: function()
30139 this.assignDocWin();
30140 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
30142 var range = this.createRange(sel);
30145 var p = range.commonAncestorContainer;
30146 while (p.nodeType == 3) { // text node
30157 * Range intersection.. the hard stuff...
30161 * [ -- selected range --- ]
30165 * if end is before start or hits it. fail.
30166 * if start is after end or hits it fail.
30168 * if either hits (but other is outside. - then it's not
30174 // @see http://www.thismuchiknow.co.uk/?p=64.
30175 rangeIntersectsNode : function(range, node)
30177 var nodeRange = node.ownerDocument.createRange();
30179 nodeRange.selectNode(node);
30181 nodeRange.selectNodeContents(node);
30184 var rangeStartRange = range.cloneRange();
30185 rangeStartRange.collapse(true);
30187 var rangeEndRange = range.cloneRange();
30188 rangeEndRange.collapse(false);
30190 var nodeStartRange = nodeRange.cloneRange();
30191 nodeStartRange.collapse(true);
30193 var nodeEndRange = nodeRange.cloneRange();
30194 nodeEndRange.collapse(false);
30196 return rangeStartRange.compareBoundaryPoints(
30197 Range.START_TO_START, nodeEndRange) == -1 &&
30198 rangeEndRange.compareBoundaryPoints(
30199 Range.START_TO_START, nodeStartRange) == 1;
30203 rangeCompareNode : function(range, node)
30205 var nodeRange = node.ownerDocument.createRange();
30207 nodeRange.selectNode(node);
30209 nodeRange.selectNodeContents(node);
30213 range.collapse(true);
30215 nodeRange.collapse(true);
30217 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
30218 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
30220 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
30222 var nodeIsBefore = ss == 1;
30223 var nodeIsAfter = ee == -1;
30225 if (nodeIsBefore && nodeIsAfter) {
30228 if (!nodeIsBefore && nodeIsAfter) {
30229 return 1; //right trailed.
30232 if (nodeIsBefore && !nodeIsAfter) {
30233 return 2; // left trailed.
30239 cleanWordChars : function(input) {// change the chars to hex code
30242 [ 8211, "–" ],
30243 [ 8212, "—" ],
30251 var output = input;
30252 Roo.each(swapCodes, function(sw) {
30253 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
30255 output = output.replace(swapper, sw[1]);
30265 cleanUpChild : function (node)
30268 new Roo.htmleditor.FilterComment({node : node});
30269 new Roo.htmleditor.FilterAttributes({
30271 attrib_black : this.ablack,
30272 attrib_clean : this.aclean,
30273 style_white : this.cwhite,
30274 style_black : this.cblack
30276 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
30277 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
30283 * Clean up MS wordisms...
30284 * @deprecated - use filter directly
30286 cleanWord : function(node)
30288 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
30289 new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
30296 * @deprecated - use filters
30298 cleanTableWidths : function(node)
30300 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
30307 applyBlacklists : function()
30309 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
30310 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
30312 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
30313 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
30314 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
30318 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
30319 if (b.indexOf(tag) > -1) {
30322 this.white.push(tag);
30326 Roo.each(w, function(tag) {
30327 if (b.indexOf(tag) > -1) {
30330 if (this.white.indexOf(tag) > -1) {
30333 this.white.push(tag);
30338 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
30339 if (w.indexOf(tag) > -1) {
30342 this.black.push(tag);
30346 Roo.each(b, function(tag) {
30347 if (w.indexOf(tag) > -1) {
30350 if (this.black.indexOf(tag) > -1) {
30353 this.black.push(tag);
30358 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
30359 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
30363 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
30364 if (b.indexOf(tag) > -1) {
30367 this.cwhite.push(tag);
30371 Roo.each(w, function(tag) {
30372 if (b.indexOf(tag) > -1) {
30375 if (this.cwhite.indexOf(tag) > -1) {
30378 this.cwhite.push(tag);
30383 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
30384 if (w.indexOf(tag) > -1) {
30387 this.cblack.push(tag);
30391 Roo.each(b, function(tag) {
30392 if (w.indexOf(tag) > -1) {
30395 if (this.cblack.indexOf(tag) > -1) {
30398 this.cblack.push(tag);
30403 setStylesheets : function(stylesheets)
30405 if(typeof(stylesheets) == 'string'){
30406 Roo.get(this.iframe.contentDocument.head).createChild({
30408 rel : 'stylesheet',
30417 Roo.each(stylesheets, function(s) {
30422 Roo.get(_this.iframe.contentDocument.head).createChild({
30424 rel : 'stylesheet',
30434 updateLanguage : function()
30436 if (!this.iframe || !this.iframe.contentDocument) {
30439 Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
30443 removeStylesheets : function()
30447 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
30452 setStyle : function(style)
30454 Roo.get(this.iframe.contentDocument.head).createChild({
30463 // hide stuff that is not compatible
30477 * @event specialkey
30481 * @cfg {String} fieldClass @hide
30484 * @cfg {String} focusClass @hide
30487 * @cfg {String} autoCreate @hide
30490 * @cfg {String} inputType @hide
30493 * @cfg {String} invalidClass @hide
30496 * @cfg {String} invalidText @hide
30499 * @cfg {String} msgFx @hide
30502 * @cfg {String} validateOnBlur @hide
30506 Roo.HtmlEditorCore.white = [
30507 'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
30509 'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD', 'DIR', 'DIV',
30510 'DL', 'DT', 'H1', 'H2', 'H3', 'H4',
30511 'H5', 'H6', 'HR', 'ISINDEX', 'LISTING', 'MARQUEE',
30512 'MENU', 'MULTICOL', 'OL', 'P', 'PLAINTEXT', 'PRE',
30513 'TABLE', 'UL', 'XMP',
30515 'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH',
30518 'DIR', 'MENU', 'OL', 'UL', 'DL',
30524 Roo.HtmlEditorCore.black = [
30525 // 'embed', 'object', // enable - backend responsiblity to clean thiese
30527 'BASE', 'BASEFONT', 'BGSOUND', 'BLINK', 'BODY',
30528 'FRAME', 'FRAMESET', 'HEAD', 'HTML', 'ILAYER',
30529 'IFRAME', 'LAYER', 'LINK', 'META', 'OBJECT',
30530 'SCRIPT', 'STYLE' ,'TITLE', 'XML',
30531 //'FONT' // CLEAN LATER..
30532 'COLGROUP', 'COL' // messy tables.
30536 Roo.HtmlEditorCore.clean = [ // ?? needed???
30537 'SCRIPT', 'STYLE', 'TITLE', 'XML'
30539 Roo.HtmlEditorCore.tag_remove = [
30544 Roo.HtmlEditorCore.ablack = [
30548 Roo.HtmlEditorCore.aclean = [
30549 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
30553 Roo.HtmlEditorCore.pwhite= [
30554 'http', 'https', 'mailto'
30557 // white listed style attributes.
30558 Roo.HtmlEditorCore.cwhite= [
30559 // 'text-align', /// default is to allow most things..
30565 // black listed style attributes.
30566 Roo.HtmlEditorCore.cblack= [
30567 // 'font-size' -- this can be set by the project
30581 * @class Roo.bootstrap.form.HtmlEditor
30582 * @extends Roo.bootstrap.form.TextArea
30583 * Bootstrap HtmlEditor class
30586 * Create a new HtmlEditor
30587 * @param {Object} config The config object
30590 Roo.bootstrap.form.HtmlEditor = function(config){
30591 Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
30592 if (!this.toolbars) {
30593 this.toolbars = [];
30596 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
30599 * @event initialize
30600 * Fires when the editor is fully initialized (including the iframe)
30601 * @param {HtmlEditor} this
30606 * Fires when the editor is first receives the focus. Any insertion must wait
30607 * until after this event.
30608 * @param {HtmlEditor} this
30612 * @event beforesync
30613 * Fires before the textarea is updated with content from the editor iframe. Return false
30614 * to cancel the sync.
30615 * @param {HtmlEditor} this
30616 * @param {String} html
30620 * @event beforepush
30621 * Fires before the iframe editor is updated with content from the textarea. Return false
30622 * to cancel the push.
30623 * @param {HtmlEditor} this
30624 * @param {String} html
30629 * Fires when the textarea is updated with content from the editor iframe.
30630 * @param {HtmlEditor} this
30631 * @param {String} html
30636 * Fires when the iframe editor is updated with content from the textarea.
30637 * @param {HtmlEditor} this
30638 * @param {String} html
30642 * @event editmodechange
30643 * Fires when the editor switches edit modes
30644 * @param {HtmlEditor} this
30645 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
30647 editmodechange: true,
30649 * @event editorevent
30650 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30651 * @param {HtmlEditor} this
30655 * @event firstfocus
30656 * Fires when on first focus - needed by toolbars..
30657 * @param {HtmlEditor} this
30662 * Auto save the htmlEditor value as a file into Events
30663 * @param {HtmlEditor} this
30667 * @event savedpreview
30668 * preview the saved version of htmlEditor
30669 * @param {HtmlEditor} this
30676 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea, {
30680 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
30685 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
30690 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
30695 * @cfg {Number} height (in pixels)
30699 * @cfg {Number} width (in pixels)
30704 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30707 stylesheets: false,
30712 // private properties
30713 validationEvent : false,
30715 initialized : false,
30718 onFocus : Roo.emptyFn,
30720 hideMode:'offsets',
30722 tbContainer : false,
30726 toolbarContainer :function() {
30727 return this.wrap.select('.x-html-editor-tb',true).first();
30731 * Protected method that will not generally be called directly. It
30732 * is called when the editor creates its toolbar. Override this method if you need to
30733 * add custom toolbar buttons.
30734 * @param {HtmlEditor} editor
30736 createToolbar : function(){
30737 Roo.log('renewing');
30738 Roo.log("create toolbars");
30740 this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
30741 this.toolbars[0].render(this.toolbarContainer());
30745 // if (!editor.toolbars || !editor.toolbars.length) {
30746 // editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
30749 // for (var i =0 ; i < editor.toolbars.length;i++) {
30750 // editor.toolbars[i] = Roo.factory(
30751 // typeof(editor.toolbars[i]) == 'string' ?
30752 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
30753 // Roo.bootstrap.form.HtmlEditor);
30754 // editor.toolbars[i].init(editor);
30760 onRender : function(ct, position)
30762 // Roo.log("Call onRender: " + this.xtype);
30764 Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
30766 this.wrap = this.inputEl().wrap({
30767 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
30770 this.editorcore.onRender(ct, position);
30772 if (this.resizable) {
30773 this.resizeEl = new Roo.Resizable(this.wrap, {
30777 minHeight : this.height,
30778 height: this.height,
30779 handles : this.resizable,
30782 resize : function(r, w, h) {
30783 _t.onResize(w,h); // -something
30789 this.createToolbar(this);
30792 if(!this.width && this.resizable){
30793 this.setSize(this.wrap.getSize());
30795 if (this.resizeEl) {
30796 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
30797 // should trigger onReize..
30803 onResize : function(w, h)
30805 Roo.log('resize: ' +w + ',' + h );
30806 Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
30810 if(this.inputEl() ){
30811 if(typeof w == 'number'){
30812 var aw = w - this.wrap.getFrameWidth('lr');
30813 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
30816 if(typeof h == 'number'){
30817 var tbh = -11; // fixme it needs to tool bar size!
30818 for (var i =0; i < this.toolbars.length;i++) {
30819 // fixme - ask toolbars for heights?
30820 tbh += this.toolbars[i].el.getHeight();
30821 //if (this.toolbars[i].footer) {
30822 // tbh += this.toolbars[i].footer.el.getHeight();
30830 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
30831 ah -= 5; // knock a few pixes off for look..
30832 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
30836 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
30837 this.editorcore.onResize(ew,eh);
30842 * Toggles the editor between standard and source edit mode.
30843 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
30845 toggleSourceEdit : function(sourceEditMode)
30847 this.editorcore.toggleSourceEdit(sourceEditMode);
30849 if(this.editorcore.sourceEditMode){
30850 Roo.log('editor - showing textarea');
30853 // Roo.log(this.syncValue());
30855 this.inputEl().removeClass(['hide', 'x-hidden']);
30856 this.inputEl().dom.removeAttribute('tabIndex');
30857 this.inputEl().focus();
30859 Roo.log('editor - hiding textarea');
30861 // Roo.log(this.pushValue());
30864 this.inputEl().addClass(['hide', 'x-hidden']);
30865 this.inputEl().dom.setAttribute('tabIndex', -1);
30866 //this.deferFocus();
30869 if(this.resizable){
30870 this.setSize(this.wrap.getSize());
30873 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
30876 // private (for BoxComponent)
30877 adjustSize : Roo.BoxComponent.prototype.adjustSize,
30879 // private (for BoxComponent)
30880 getResizeEl : function(){
30884 // private (for BoxComponent)
30885 getPositionEl : function(){
30890 initEvents : function(){
30891 this.originalValue = this.getValue();
30895 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
30898 // markInvalid : Roo.emptyFn,
30900 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
30903 // clearInvalid : Roo.emptyFn,
30905 setValue : function(v){
30906 Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
30907 this.editorcore.pushValue();
30912 deferFocus : function(){
30913 this.focus.defer(10, this);
30917 focus : function(){
30918 this.editorcore.focus();
30924 onDestroy : function(){
30930 for (var i =0; i < this.toolbars.length;i++) {
30931 // fixme - ask toolbars for heights?
30932 this.toolbars[i].onDestroy();
30935 this.wrap.dom.innerHTML = '';
30936 this.wrap.remove();
30941 onFirstFocus : function(){
30942 //Roo.log("onFirstFocus");
30943 this.editorcore.onFirstFocus();
30944 for (var i =0; i < this.toolbars.length;i++) {
30945 this.toolbars[i].onFirstFocus();
30951 syncValue : function()
30953 this.editorcore.syncValue();
30956 pushValue : function()
30958 this.editorcore.pushValue();
30962 // hide stuff that is not compatible
30976 * @event specialkey
30980 * @cfg {String} fieldClass @hide
30983 * @cfg {String} focusClass @hide
30986 * @cfg {String} autoCreate @hide
30989 * @cfg {String} inputType @hide
30993 * @cfg {String} invalidText @hide
30996 * @cfg {String} msgFx @hide
30999 * @cfg {String} validateOnBlur @hide
31008 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
31010 * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
31011 * @parent Roo.bootstrap.form.HtmlEditor
31012 * @extends Roo.bootstrap.nav.Simplebar
31018 new Roo.bootstrap.form.HtmlEditor({
31021 new Roo.bootstrap.form.HtmlEditorToolbarStandard({
31022 disable : { fonts: 1 , format: 1, ..., ... , ...],
31028 * @cfg {Object} disable List of elements to disable..
31029 * @cfg {Array} btns List of additional buttons.
31033 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
31036 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
31039 Roo.apply(this, config);
31041 // default disabled, based on 'good practice'..
31042 this.disable = this.disable || {};
31043 Roo.applyIf(this.disable, {
31046 specialElements : true
31048 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
31050 this.editor = config.editor;
31051 this.editorcore = config.editor.editorcore;
31053 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
31055 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
31056 // dont call parent... till later.
31058 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar, {
31063 editorcore : false,
31068 "h1","h2","h3","h4","h5","h6",
31070 "abbr", "acronym", "address", "cite", "samp", "var",
31074 onRender : function(ct, position)
31076 // Roo.log("Call onRender: " + this.xtype);
31078 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
31080 this.el.dom.style.marginBottom = '0';
31082 var editorcore = this.editorcore;
31083 var editor= this.editor;
31086 var btn = function(id,cmd , toggle, handler, html){
31088 var event = toggle ? 'toggle' : 'click';
31093 xns: Roo.bootstrap,
31097 enableToggle:toggle !== false,
31099 pressed : toggle ? false : null,
31102 a.listeners[toggle ? 'toggle' : 'click'] = function() {
31103 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
31109 // var cb_box = function...
31114 xns: Roo.bootstrap,
31119 xns: Roo.bootstrap,
31123 Roo.each(this.formats, function(f) {
31124 style.menu.items.push({
31126 xns: Roo.bootstrap,
31127 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
31132 editorcore.insertTag(this.tagname);
31139 children.push(style);
31141 btn('bold',false,true);
31142 btn('italic',false,true);
31143 btn('align-left', 'justifyleft',true);
31144 btn('align-center', 'justifycenter',true);
31145 btn('align-right' , 'justifyright',true);
31146 btn('link', false, false, function(btn) {
31147 //Roo.log("create link?");
31148 var url = prompt(this.createLinkText, this.defaultLinkValue);
31149 if(url && url != 'http:/'+'/'){
31150 this.editorcore.relayCmd('createlink', url);
31153 btn('list','insertunorderedlist',true);
31154 btn('pencil', false,true, function(btn){
31156 this.toggleSourceEdit(btn.pressed);
31159 if (this.editor.btns.length > 0) {
31160 for (var i = 0; i<this.editor.btns.length; i++) {
31161 children.push(this.editor.btns[i]);
31169 xns: Roo.bootstrap,
31174 xns: Roo.bootstrap,
31179 cog.menu.items.push({
31181 xns: Roo.bootstrap,
31182 html : Clean styles,
31187 editorcore.insertTag(this.tagname);
31196 this.xtype = 'NavSimplebar';
31198 for(var i=0;i< children.length;i++) {
31200 this.buttons.add(this.addxtypeChild(children[i]));
31204 editor.on('editorevent', this.updateToolbar, this);
31206 onBtnClick : function(id)
31208 this.editorcore.relayCmd(id);
31209 this.editorcore.focus();
31213 * Protected method that will not generally be called directly. It triggers
31214 * a toolbar update by reading the markup state of the current selection in the editor.
31216 updateToolbar: function(){
31218 if(!this.editorcore.activated){
31219 this.editor.onFirstFocus(); // is this neeed?
31223 var btns = this.buttons;
31224 var doc = this.editorcore.doc;
31225 btns.get('bold').setActive(doc.queryCommandState('bold'));
31226 btns.get('italic').setActive(doc.queryCommandState('italic'));
31227 //btns.get('underline').setActive(doc.queryCommandState('underline'));
31229 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
31230 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
31231 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
31233 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
31234 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
31237 var ans = this.editorcore.getAllAncestors();
31238 if (this.formatCombo) {
31241 var store = this.formatCombo.store;
31242 this.formatCombo.setValue("");
31243 for (var i =0; i < ans.length;i++) {
31244 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
31246 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
31254 // hides menus... - so this cant be on a menu...
31255 Roo.bootstrap.MenuMgr.hideAll();
31257 Roo.bootstrap.menu.Manager.hideAll();
31258 //this.editorsyncValue();
31260 onFirstFocus: function() {
31261 this.buttons.each(function(item){
31265 toggleSourceEdit : function(sourceEditMode){
31268 if(sourceEditMode){
31269 Roo.log("disabling buttons");
31270 this.buttons.each( function(item){
31271 if(item.cmd != 'pencil'){
31277 Roo.log("enabling buttons");
31278 if(this.editorcore.initialized){
31279 this.buttons.each( function(item){
31285 Roo.log("calling toggole on editor");
31286 // tell the editor that it's been pressed..
31287 this.editor.toggleSourceEdit(sourceEditMode);
31301 * @class Roo.bootstrap.form.Markdown
31302 * @extends Roo.bootstrap.form.TextArea
31303 * Bootstrap Showdown editable area
31304 * @cfg {string} content
31307 * Create a new Showdown
31310 Roo.bootstrap.form.Markdown = function(config){
31311 Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
31315 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea, {
31319 initEvents : function()
31322 Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
31323 this.markdownEl = this.el.createChild({
31324 cls : 'roo-markdown-area'
31326 this.inputEl().addClass('d-none');
31327 if (this.getValue() == '') {
31328 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
31331 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
31333 this.markdownEl.on('click', this.toggleTextEdit, this);
31334 this.on('blur', this.toggleTextEdit, this);
31335 this.on('specialkey', this.resizeTextArea, this);
31338 toggleTextEdit : function()
31340 var sh = this.markdownEl.getHeight();
31341 this.inputEl().addClass('d-none');
31342 this.markdownEl.addClass('d-none');
31343 if (!this.editing) {
31345 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
31346 this.inputEl().removeClass('d-none');
31347 this.inputEl().focus();
31348 this.editing = true;
31351 // show showdown...
31352 this.updateMarkdown();
31353 this.markdownEl.removeClass('d-none');
31354 this.editing = false;
31357 updateMarkdown : function()
31359 if (this.getValue() == '') {
31360 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
31364 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
31367 resizeTextArea: function () {
31370 Roo.log([sh, this.getValue().split("\n").length * 30]);
31371 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
31373 setValue : function(val)
31375 Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
31376 if (!this.editing) {
31377 this.updateMarkdown();
31383 if (!this.editing) {
31384 this.toggleTextEdit();
31392 * Ext JS Library 1.1.1
31393 * Copyright(c) 2006-2007, Ext JS, LLC.
31395 * Originally Released Under LGPL - original licence link has changed is not relivant.
31398 * <script type="text/javascript">
31402 * @class Roo.bootstrap.PagingToolbar
31403 * @extends Roo.bootstrap.nav.Simplebar
31404 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31406 * Create a new PagingToolbar
31407 * @param {Object} config The config object
31408 * @param {Roo.data.Store} store
31410 Roo.bootstrap.PagingToolbar = function(config)
31412 // old args format still supported... - xtype is prefered..
31413 // created from xtype...
31415 this.ds = config.dataSource;
31417 if (config.store && !this.ds) {
31418 this.store= Roo.factory(config.store, Roo.data);
31419 this.ds = this.store;
31420 this.ds.xmodule = this.xmodule || false;
31423 this.toolbarItems = [];
31424 if (config.items) {
31425 this.toolbarItems = config.items;
31428 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
31433 this.bind(this.ds);
31436 if (Roo.bootstrap.version == 4) {
31437 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
31439 this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
31444 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
31446 * @cfg {Roo.bootstrap.Button} buttons[]
31447 * Buttons for the toolbar
31450 * @cfg {Roo.data.Store} store
31451 * The underlying data store providing the paged data
31454 * @cfg {String/HTMLElement/Element} container
31455 * container The id or element that will contain the toolbar
31458 * @cfg {Boolean} displayInfo
31459 * True to display the displayMsg (defaults to false)
31462 * @cfg {Number} pageSize
31463 * The number of records to display per page (defaults to 20)
31467 * @cfg {String} displayMsg
31468 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
31470 displayMsg : 'Displaying {0} - {1} of {2}',
31472 * @cfg {String} emptyMsg
31473 * The message to display when no records are found (defaults to "No data to display")
31475 emptyMsg : 'No data to display',
31477 * Customizable piece of the default paging text (defaults to "Page")
31480 beforePageText : "Page",
31482 * Customizable piece of the default paging text (defaults to "of %0")
31485 afterPageText : "of {0}",
31487 * Customizable piece of the default paging text (defaults to "First Page")
31490 firstText : "First Page",
31492 * Customizable piece of the default paging text (defaults to "Previous Page")
31495 prevText : "Previous Page",
31497 * Customizable piece of the default paging text (defaults to "Next Page")
31500 nextText : "Next Page",
31502 * Customizable piece of the default paging text (defaults to "Last Page")
31505 lastText : "Last Page",
31507 * Customizable piece of the default paging text (defaults to "Refresh")
31510 refreshText : "Refresh",
31514 onRender : function(ct, position)
31516 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
31517 this.navgroup.parentId = this.id;
31518 this.navgroup.onRender(this.el, null);
31519 // add the buttons to the navgroup
31521 if(this.displayInfo){
31522 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
31523 this.displayEl = this.el.select('.x-paging-info', true).first();
31524 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
31525 // this.displayEl = navel.el.select('span',true).first();
31531 Roo.each(_this.buttons, function(e){ // this might need to use render????
31532 Roo.factory(e).render(_this.el);
31536 Roo.each(_this.toolbarItems, function(e) {
31537 _this.navgroup.addItem(e);
31541 this.first = this.navgroup.addItem({
31542 tooltip: this.firstText,
31543 cls: "prev btn-outline-secondary",
31544 html : ' <i class="fa fa-step-backward"></i>',
31546 preventDefault: true,
31547 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
31550 this.prev = this.navgroup.addItem({
31551 tooltip: this.prevText,
31552 cls: "prev btn-outline-secondary",
31553 html : ' <i class="fa fa-backward"></i>',
31555 preventDefault: true,
31556 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
31558 //this.addSeparator();
31561 var field = this.navgroup.addItem( {
31563 cls : 'x-paging-position btn-outline-secondary',
31565 html : this.beforePageText +
31566 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
31567 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
31570 this.field = field.el.select('input', true).first();
31571 this.field.on("keydown", this.onPagingKeydown, this);
31572 this.field.on("focus", function(){this.dom.select();});
31575 this.afterTextEl = field.el.select('.x-paging-after',true).first();
31576 //this.field.setHeight(18);
31577 //this.addSeparator();
31578 this.next = this.navgroup.addItem({
31579 tooltip: this.nextText,
31580 cls: "next btn-outline-secondary",
31581 html : ' <i class="fa fa-forward"></i>',
31583 preventDefault: true,
31584 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
31586 this.last = this.navgroup.addItem({
31587 tooltip: this.lastText,
31588 html : ' <i class="fa fa-step-forward"></i>',
31589 cls: "next btn-outline-secondary",
31591 preventDefault: true,
31592 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
31594 //this.addSeparator();
31595 this.loading = this.navgroup.addItem({
31596 tooltip: this.refreshText,
31597 cls: "btn-outline-secondary",
31598 html : ' <i class="fa fa-refresh"></i>',
31599 preventDefault: true,
31600 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
31606 updateInfo : function(){
31607 if(this.displayEl){
31608 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
31609 var msg = count == 0 ?
31613 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
31615 this.displayEl.update(msg);
31620 onLoad : function(ds, r, o)
31622 this.cursor = o.params && o.params.start ? o.params.start : 0;
31624 var d = this.getPageData(),
31629 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
31630 this.field.dom.value = ap;
31631 this.first.setDisabled(ap == 1);
31632 this.prev.setDisabled(ap == 1);
31633 this.next.setDisabled(ap == ps);
31634 this.last.setDisabled(ap == ps);
31635 this.loading.enable();
31640 getPageData : function(){
31641 var total = this.ds.getTotalCount();
31644 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
31645 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
31650 onLoadError : function(proxy, o){
31651 this.loading.enable();
31652 if (this.ds.events.loadexception.listeners.length < 2) {
31653 // nothing has been assigned to loadexception except this...
31655 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
31661 onPagingKeydown : function(e){
31662 var k = e.getKey();
31663 var d = this.getPageData();
31665 var v = this.field.dom.value, pageNum;
31666 if(!v || isNaN(pageNum = parseInt(v, 10))){
31667 this.field.dom.value = d.activePage;
31670 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
31671 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31674 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))
31676 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
31677 this.field.dom.value = pageNum;
31678 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
31681 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
31683 var v = this.field.dom.value, pageNum;
31684 var increment = (e.shiftKey) ? 10 : 1;
31685 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31688 if(!v || isNaN(pageNum = parseInt(v, 10))) {
31689 this.field.dom.value = d.activePage;
31692 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31694 this.field.dom.value = parseInt(v, 10) + increment;
31695 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31696 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31703 beforeLoad : function(){
31705 this.loading.disable();
31710 onClick : function(which){
31719 ds.load({params:{start: 0, limit: this.pageSize}});
31722 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31725 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31728 var total = ds.getTotalCount();
31729 var extra = total % this.pageSize;
31730 var lastStart = extra ? (total - extra) : total-this.pageSize;
31731 ds.load({params:{start: lastStart, limit: this.pageSize}});
31734 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31740 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31741 * @param {Roo.data.Store} store The data store to unbind
31743 unbind : function(ds){
31744 ds.un("beforeload", this.beforeLoad, this);
31745 ds.un("load", this.onLoad, this);
31746 ds.un("loadexception", this.onLoadError, this);
31747 ds.un("remove", this.updateInfo, this);
31748 ds.un("add", this.updateInfo, this);
31749 this.ds = undefined;
31753 * Binds the paging toolbar to the specified {@link Roo.data.Store}
31754 * @param {Roo.data.Store} store The data store to bind
31756 bind : function(ds){
31757 ds.on("beforeload", this.beforeLoad, this);
31758 ds.on("load", this.onLoad, this);
31759 ds.on("loadexception", this.onLoadError, this);
31760 ds.on("remove", this.updateInfo, this);
31761 ds.on("add", this.updateInfo, this);
31772 * @class Roo.bootstrap.MessageBar
31773 * @extends Roo.bootstrap.Component
31774 * Bootstrap MessageBar class
31775 * @cfg {String} html contents of the MessageBar
31776 * @cfg {String} weight (info | success | warning | danger) default info
31777 * @cfg {String} beforeClass insert the bar before the given class
31778 * @cfg {Boolean} closable (true | false) default false
31779 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
31782 * Create a new Element
31783 * @param {Object} config The config object
31786 Roo.bootstrap.MessageBar = function(config){
31787 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
31790 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
31796 beforeClass: 'bootstrap-sticky-wrap',
31798 getAutoCreate : function(){
31802 cls: 'alert alert-dismissable alert-' + this.weight,
31807 html: this.html || ''
31813 cfg.cls += ' alert-messages-fixed';
31827 onRender : function(ct, position)
31829 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
31832 var cfg = Roo.apply({}, this.getAutoCreate());
31836 cfg.cls += ' ' + this.cls;
31839 cfg.style = this.style;
31841 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
31843 this.el.setVisibilityMode(Roo.Element.DISPLAY);
31846 this.el.select('>button.close').on('click', this.hide, this);
31852 if (!this.rendered) {
31858 this.fireEvent('show', this);
31864 if (!this.rendered) {
31870 this.fireEvent('hide', this);
31873 update : function()
31875 // var e = this.el.dom.firstChild;
31877 // if(this.closable){
31878 // e = e.nextSibling;
31881 // e.data = this.html || '';
31883 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
31899 * @class Roo.bootstrap.Graph
31900 * @extends Roo.bootstrap.Component
31901 * Bootstrap Graph class
31905 @cfg {String} graphtype bar | vbar | pie
31906 @cfg {number} g_x coodinator | centre x (pie)
31907 @cfg {number} g_y coodinator | centre y (pie)
31908 @cfg {number} g_r radius (pie)
31909 @cfg {number} g_height height of the chart (respected by all elements in the set)
31910 @cfg {number} g_width width of the chart (respected by all elements in the set)
31911 @cfg {Object} title The title of the chart
31914 -opts (object) options for the chart
31916 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
31917 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
31919 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.
31920 o stacked (boolean) whether or not to tread values as in a stacked bar chart
31922 o stretch (boolean)
31924 -opts (object) options for the pie
31927 o startAngle (number)
31928 o endAngle (number)
31932 * Create a new Input
31933 * @param {Object} config The config object
31936 Roo.bootstrap.Graph = function(config){
31937 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
31943 * The img click event for the img.
31944 * @param {Roo.EventObject} e
31950 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
31961 //g_colors: this.colors,
31968 getAutoCreate : function(){
31979 onRender : function(ct,position){
31982 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
31984 if (typeof(Raphael) == 'undefined') {
31985 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
31989 this.raphael = Raphael(this.el.dom);
31991 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
31992 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
31993 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
31994 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
31996 r.text(160, 10, "Single Series Chart").attr(txtattr);
31997 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
31998 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
31999 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
32001 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
32002 r.barchart(330, 10, 300, 220, data1);
32003 r.barchart(10, 250, 300, 220, data2, {stacked: true});
32004 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
32007 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
32008 // r.barchart(30, 30, 560, 250, xdata, {
32009 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
32010 // axis : "0 0 1 1",
32011 // axisxlabels : xdata
32012 // //yvalues : cols,
32015 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
32017 // this.load(null,xdata,{
32018 // axis : "0 0 1 1",
32019 // axisxlabels : xdata
32024 load : function(graphtype,xdata,opts)
32026 this.raphael.clear();
32028 graphtype = this.graphtype;
32033 var r = this.raphael,
32034 fin = function () {
32035 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
32037 fout = function () {
32038 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
32040 pfin = function() {
32041 this.sector.stop();
32042 this.sector.scale(1.1, 1.1, this.cx, this.cy);
32045 this.label[0].stop();
32046 this.label[0].attr({ r: 7.5 });
32047 this.label[1].attr({ "font-weight": 800 });
32050 pfout = function() {
32051 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
32054 this.label[0].animate({ r: 5 }, 500, "bounce");
32055 this.label[1].attr({ "font-weight": 400 });
32061 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
32064 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
32067 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
32068 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
32070 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
32077 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
32082 setTitle: function(o)
32087 initEvents: function() {
32090 this.el.on('click', this.onClick, this);
32094 onClick : function(e)
32096 Roo.log('img onclick');
32097 this.fireEvent('click', this, e);
32103 Roo.bootstrap.dash = {};/*
32109 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
32112 * @class Roo.bootstrap.dash.NumberBox
32113 * @extends Roo.bootstrap.Component
32114 * Bootstrap NumberBox class
32115 * @cfg {String} headline Box headline
32116 * @cfg {String} content Box content
32117 * @cfg {String} icon Box icon
32118 * @cfg {String} footer Footer text
32119 * @cfg {String} fhref Footer href
32122 * Create a new NumberBox
32123 * @param {Object} config The config object
32127 Roo.bootstrap.dash.NumberBox = function(config){
32128 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
32132 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
32141 getAutoCreate : function(){
32145 cls : 'small-box ',
32153 cls : 'roo-headline',
32154 html : this.headline
32158 cls : 'roo-content',
32159 html : this.content
32173 cls : 'ion ' + this.icon
32182 cls : 'small-box-footer',
32183 href : this.fhref || '#',
32187 cfg.cn.push(footer);
32194 onRender : function(ct,position){
32195 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
32202 setHeadline: function (value)
32204 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
32207 setFooter: function (value, href)
32209 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
32212 this.el.select('a.small-box-footer',true).first().attr('href', href);
32217 setContent: function (value)
32219 this.el.select('.roo-content',true).first().dom.innerHTML = value;
32222 initEvents: function()
32236 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
32239 * @class Roo.bootstrap.dash.TabBox
32240 * @extends Roo.bootstrap.Component
32241 * @children Roo.bootstrap.dash.TabPane
32242 * Bootstrap TabBox class
32243 * @cfg {String} title Title of the TabBox
32244 * @cfg {String} icon Icon of the TabBox
32245 * @cfg {Boolean} showtabs (true|false) show the tabs default true
32246 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
32249 * Create a new TabBox
32250 * @param {Object} config The config object
32254 Roo.bootstrap.dash.TabBox = function(config){
32255 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
32260 * When a pane is added
32261 * @param {Roo.bootstrap.dash.TabPane} pane
32265 * @event activatepane
32266 * When a pane is activated
32267 * @param {Roo.bootstrap.dash.TabPane} pane
32269 "activatepane" : true
32277 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
32282 tabScrollable : false,
32284 getChildContainer : function()
32286 return this.el.select('.tab-content', true).first();
32289 getAutoCreate : function(){
32293 cls: 'pull-left header',
32301 cls: 'fa ' + this.icon
32307 cls: 'nav nav-tabs pull-right',
32313 if(this.tabScrollable){
32320 cls: 'nav nav-tabs pull-right',
32331 cls: 'nav-tabs-custom',
32336 cls: 'tab-content no-padding',
32344 initEvents : function()
32346 //Roo.log('add add pane handler');
32347 this.on('addpane', this.onAddPane, this);
32350 * Updates the box title
32351 * @param {String} html to set the title to.
32353 setTitle : function(value)
32355 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
32357 onAddPane : function(pane)
32359 this.panes.push(pane);
32360 //Roo.log('addpane');
32362 // tabs are rendere left to right..
32363 if(!this.showtabs){
32367 var ctr = this.el.select('.nav-tabs', true).first();
32370 var existing = ctr.select('.nav-tab',true);
32371 var qty = existing.getCount();;
32374 var tab = ctr.createChild({
32376 cls : 'nav-tab' + (qty ? '' : ' active'),
32384 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
32387 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
32389 pane.el.addClass('active');
32394 onTabClick : function(ev,un,ob,pane)
32396 //Roo.log('tab - prev default');
32397 ev.preventDefault();
32400 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
32401 pane.tab.addClass('active');
32402 //Roo.log(pane.title);
32403 this.getChildContainer().select('.tab-pane',true).removeClass('active');
32404 // technically we should have a deactivate event.. but maybe add later.
32405 // and it should not de-activate the selected tab...
32406 this.fireEvent('activatepane', pane);
32407 pane.el.addClass('active');
32408 pane.fireEvent('activate');
32413 getActivePane : function()
32416 Roo.each(this.panes, function(p) {
32417 if(p.el.hasClass('active')){
32438 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
32440 * @class Roo.bootstrap.TabPane
32441 * @extends Roo.bootstrap.Component
32442 * @children Roo.bootstrap.Graph Roo.bootstrap.Column
32443 * Bootstrap TabPane class
32444 * @cfg {Boolean} active (false | true) Default false
32445 * @cfg {String} title title of panel
32449 * Create a new TabPane
32450 * @param {Object} config The config object
32453 Roo.bootstrap.dash.TabPane = function(config){
32454 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
32460 * When a pane is activated
32461 * @param {Roo.bootstrap.dash.TabPane} pane
32468 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
32473 // the tabBox that this is attached to.
32476 getAutoCreate : function()
32484 cfg.cls += ' active';
32489 initEvents : function()
32491 //Roo.log('trigger add pane handler');
32492 this.parent().fireEvent('addpane', this)
32496 * Updates the tab title
32497 * @param {String} html to set the title to.
32499 setTitle: function(str)
32505 this.tab.select('a', true).first().dom.innerHTML = str;
32524 * @class Roo.bootstrap.Tooltip
32525 * Bootstrap Tooltip class
32526 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
32527 * to determine which dom element triggers the tooltip.
32529 * It needs to add support for additional attributes like tooltip-position
32532 * Create a new Toolti
32533 * @param {Object} config The config object
32536 Roo.bootstrap.Tooltip = function(config){
32537 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
32539 this.alignment = Roo.bootstrap.Tooltip.alignment;
32541 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
32542 this.alignment = config.alignment;
32547 Roo.apply(Roo.bootstrap.Tooltip, {
32549 * @function init initialize tooltip monitoring.
32553 currentTip : false,
32554 currentRegion : false,
32560 Roo.get(document).on('mouseover', this.enter ,this);
32561 Roo.get(document).on('mouseout', this.leave, this);
32564 this.currentTip = new Roo.bootstrap.Tooltip();
32567 enter : function(ev)
32569 var dom = ev.getTarget();
32571 //Roo.log(['enter',dom]);
32572 var el = Roo.fly(dom);
32573 if (this.currentEl) {
32575 //Roo.log(this.currentEl);
32576 //Roo.log(this.currentEl.contains(dom));
32577 if (this.currentEl == el) {
32580 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
32586 if (this.currentTip.el) {
32587 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
32591 if(!el || el.dom == document){
32597 if (!el.attr('tooltip')) {
32598 pel = el.findParent("[tooltip]");
32600 bindEl = Roo.get(pel);
32606 // you can not look for children, as if el is the body.. then everythign is the child..
32607 if (!pel && !el.attr('tooltip')) { //
32608 if (!el.select("[tooltip]").elements.length) {
32611 // is the mouse over this child...?
32612 bindEl = el.select("[tooltip]").first();
32613 var xy = ev.getXY();
32614 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
32615 //Roo.log("not in region.");
32618 //Roo.log("child element over..");
32621 this.currentEl = el;
32622 this.currentTip.bind(bindEl);
32623 this.currentRegion = Roo.lib.Region.getRegion(dom);
32624 this.currentTip.enter();
32627 leave : function(ev)
32629 var dom = ev.getTarget();
32630 //Roo.log(['leave',dom]);
32631 if (!this.currentEl) {
32636 if (dom != this.currentEl.dom) {
32639 var xy = ev.getXY();
32640 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
32643 // only activate leave if mouse cursor is outside... bounding box..
32648 if (this.currentTip) {
32649 this.currentTip.leave();
32651 //Roo.log('clear currentEl');
32652 this.currentEl = false;
32657 'left' : ['r-l', [-2,0], 'right'],
32658 'right' : ['l-r', [2,0], 'left'],
32659 'bottom' : ['t-b', [0,2], 'top'],
32660 'top' : [ 'b-t', [0,-2], 'bottom']
32666 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
32671 delay : null, // can be { show : 300 , hide: 500}
32675 hoverState : null, //???
32677 placement : 'bottom',
32681 getAutoCreate : function(){
32688 cls : 'tooltip-arrow arrow'
32691 cls : 'tooltip-inner'
32698 bind : function(el)
32703 initEvents : function()
32705 this.arrowEl = this.el.select('.arrow', true).first();
32706 this.innerEl = this.el.select('.tooltip-inner', true).first();
32709 enter : function () {
32711 if (this.timeout != null) {
32712 clearTimeout(this.timeout);
32715 this.hoverState = 'in';
32716 //Roo.log("enter - show");
32717 if (!this.delay || !this.delay.show) {
32722 this.timeout = setTimeout(function () {
32723 if (_t.hoverState == 'in') {
32726 }, this.delay.show);
32730 clearTimeout(this.timeout);
32732 this.hoverState = 'out';
32733 if (!this.delay || !this.delay.hide) {
32739 this.timeout = setTimeout(function () {
32740 //Roo.log("leave - timeout");
32742 if (_t.hoverState == 'out') {
32744 Roo.bootstrap.Tooltip.currentEl = false;
32749 show : function (msg)
32752 this.render(document.body);
32755 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
32757 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
32759 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
32761 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
32762 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
32764 var placement = typeof this.placement == 'function' ?
32765 this.placement.call(this, this.el, on_el) :
32768 var autoToken = /\s?auto?\s?/i;
32769 var autoPlace = autoToken.test(placement);
32771 placement = placement.replace(autoToken, '') || 'top';
32775 //this.el.setXY([0,0]);
32777 //this.el.dom.style.display='block';
32779 //this.el.appendTo(on_el);
32781 var p = this.getPosition();
32782 var box = this.el.getBox();
32788 var align = this.alignment[placement];
32790 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
32792 if(placement == 'top' || placement == 'bottom'){
32794 placement = 'right';
32797 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
32798 placement = 'left';
32801 var scroll = Roo.select('body', true).first().getScroll();
32803 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
32807 align = this.alignment[placement];
32809 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
32813 var elems = document.getElementsByTagName('div');
32814 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
32815 for (var i = 0; i < elems.length; i++) {
32816 var zindex = Number.parseInt(
32817 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
32820 if (zindex > highest) {
32827 this.el.dom.style.zIndex = highest;
32829 this.el.alignTo(this.bindEl, align[0],align[1]);
32830 //var arrow = this.el.select('.arrow',true).first();
32831 //arrow.set(align[2],
32833 this.el.addClass(placement);
32834 this.el.addClass("bs-tooltip-"+ placement);
32836 this.el.addClass('in fade show');
32838 this.hoverState = null;
32840 if (this.el.hasClass('fade')) {
32855 //this.el.setXY([0,0]);
32856 this.el.removeClass(['show', 'in']);
32872 * @class Roo.bootstrap.LocationPicker
32873 * @extends Roo.bootstrap.Component
32874 * Bootstrap LocationPicker class
32875 * @cfg {Number} latitude Position when init default 0
32876 * @cfg {Number} longitude Position when init default 0
32877 * @cfg {Number} zoom default 15
32878 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
32879 * @cfg {Boolean} mapTypeControl default false
32880 * @cfg {Boolean} disableDoubleClickZoom default false
32881 * @cfg {Boolean} scrollwheel default true
32882 * @cfg {Boolean} streetViewControl default false
32883 * @cfg {Number} radius default 0
32884 * @cfg {String} locationName
32885 * @cfg {Boolean} draggable default true
32886 * @cfg {Boolean} enableAutocomplete default false
32887 * @cfg {Boolean} enableReverseGeocode default true
32888 * @cfg {String} markerTitle
32891 * Create a new LocationPicker
32892 * @param {Object} config The config object
32896 Roo.bootstrap.LocationPicker = function(config){
32898 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
32903 * Fires when the picker initialized.
32904 * @param {Roo.bootstrap.LocationPicker} this
32905 * @param {Google Location} location
32909 * @event positionchanged
32910 * Fires when the picker position changed.
32911 * @param {Roo.bootstrap.LocationPicker} this
32912 * @param {Google Location} location
32914 positionchanged : true,
32917 * Fires when the map resize.
32918 * @param {Roo.bootstrap.LocationPicker} this
32923 * Fires when the map show.
32924 * @param {Roo.bootstrap.LocationPicker} this
32929 * Fires when the map hide.
32930 * @param {Roo.bootstrap.LocationPicker} this
32935 * Fires when click the map.
32936 * @param {Roo.bootstrap.LocationPicker} this
32937 * @param {Map event} e
32941 * @event mapRightClick
32942 * Fires when right click the map.
32943 * @param {Roo.bootstrap.LocationPicker} this
32944 * @param {Map event} e
32946 mapRightClick : true,
32948 * @event markerClick
32949 * Fires when click the marker.
32950 * @param {Roo.bootstrap.LocationPicker} this
32951 * @param {Map event} e
32953 markerClick : true,
32955 * @event markerRightClick
32956 * Fires when right click the marker.
32957 * @param {Roo.bootstrap.LocationPicker} this
32958 * @param {Map event} e
32960 markerRightClick : true,
32962 * @event OverlayViewDraw
32963 * Fires when OverlayView Draw
32964 * @param {Roo.bootstrap.LocationPicker} this
32966 OverlayViewDraw : true,
32968 * @event OverlayViewOnAdd
32969 * Fires when OverlayView Draw
32970 * @param {Roo.bootstrap.LocationPicker} this
32972 OverlayViewOnAdd : true,
32974 * @event OverlayViewOnRemove
32975 * Fires when OverlayView Draw
32976 * @param {Roo.bootstrap.LocationPicker} this
32978 OverlayViewOnRemove : true,
32980 * @event OverlayViewShow
32981 * Fires when OverlayView Draw
32982 * @param {Roo.bootstrap.LocationPicker} this
32983 * @param {Pixel} cpx
32985 OverlayViewShow : true,
32987 * @event OverlayViewHide
32988 * Fires when OverlayView Draw
32989 * @param {Roo.bootstrap.LocationPicker} this
32991 OverlayViewHide : true,
32993 * @event loadexception
32994 * Fires when load google lib failed.
32995 * @param {Roo.bootstrap.LocationPicker} this
32997 loadexception : true
33002 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
33004 gMapContext: false,
33010 mapTypeControl: false,
33011 disableDoubleClickZoom: false,
33013 streetViewControl: false,
33017 enableAutocomplete: false,
33018 enableReverseGeocode: true,
33021 getAutoCreate: function()
33026 cls: 'roo-location-picker'
33032 initEvents: function(ct, position)
33034 if(!this.el.getWidth() || this.isApplied()){
33038 this.el.setVisibilityMode(Roo.Element.DISPLAY);
33043 initial: function()
33045 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
33046 this.fireEvent('loadexception', this);
33050 if(!this.mapTypeId){
33051 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
33054 this.gMapContext = this.GMapContext();
33056 this.initOverlayView();
33058 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
33062 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
33063 _this.setPosition(_this.gMapContext.marker.position);
33066 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
33067 _this.fireEvent('mapClick', this, event);
33071 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
33072 _this.fireEvent('mapRightClick', this, event);
33076 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
33077 _this.fireEvent('markerClick', this, event);
33081 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
33082 _this.fireEvent('markerRightClick', this, event);
33086 this.setPosition(this.gMapContext.location);
33088 this.fireEvent('initial', this, this.gMapContext.location);
33091 initOverlayView: function()
33095 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
33099 _this.fireEvent('OverlayViewDraw', _this);
33104 _this.fireEvent('OverlayViewOnAdd', _this);
33107 onRemove: function()
33109 _this.fireEvent('OverlayViewOnRemove', _this);
33112 show: function(cpx)
33114 _this.fireEvent('OverlayViewShow', _this, cpx);
33119 _this.fireEvent('OverlayViewHide', _this);
33125 fromLatLngToContainerPixel: function(event)
33127 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
33130 isApplied: function()
33132 return this.getGmapContext() == false ? false : true;
33135 getGmapContext: function()
33137 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
33140 GMapContext: function()
33142 var position = new google.maps.LatLng(this.latitude, this.longitude);
33144 var _map = new google.maps.Map(this.el.dom, {
33147 mapTypeId: this.mapTypeId,
33148 mapTypeControl: this.mapTypeControl,
33149 disableDoubleClickZoom: this.disableDoubleClickZoom,
33150 scrollwheel: this.scrollwheel,
33151 streetViewControl: this.streetViewControl,
33152 locationName: this.locationName,
33153 draggable: this.draggable,
33154 enableAutocomplete: this.enableAutocomplete,
33155 enableReverseGeocode: this.enableReverseGeocode
33158 var _marker = new google.maps.Marker({
33159 position: position,
33161 title: this.markerTitle,
33162 draggable: this.draggable
33169 location: position,
33170 radius: this.radius,
33171 locationName: this.locationName,
33172 addressComponents: {
33173 formatted_address: null,
33174 addressLine1: null,
33175 addressLine2: null,
33177 streetNumber: null,
33181 stateOrProvince: null
33184 domContainer: this.el.dom,
33185 geodecoder: new google.maps.Geocoder()
33189 drawCircle: function(center, radius, options)
33191 if (this.gMapContext.circle != null) {
33192 this.gMapContext.circle.setMap(null);
33196 options = Roo.apply({}, options, {
33197 strokeColor: "#0000FF",
33198 strokeOpacity: .35,
33200 fillColor: "#0000FF",
33204 options.map = this.gMapContext.map;
33205 options.radius = radius;
33206 options.center = center;
33207 this.gMapContext.circle = new google.maps.Circle(options);
33208 return this.gMapContext.circle;
33214 setPosition: function(location)
33216 this.gMapContext.location = location;
33217 this.gMapContext.marker.setPosition(location);
33218 this.gMapContext.map.panTo(location);
33219 this.drawCircle(location, this.gMapContext.radius, {});
33223 if (this.gMapContext.settings.enableReverseGeocode) {
33224 this.gMapContext.geodecoder.geocode({
33225 latLng: this.gMapContext.location
33226 }, function(results, status) {
33228 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
33229 _this.gMapContext.locationName = results[0].formatted_address;
33230 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
33232 _this.fireEvent('positionchanged', this, location);
33239 this.fireEvent('positionchanged', this, location);
33244 google.maps.event.trigger(this.gMapContext.map, "resize");
33246 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
33248 this.fireEvent('resize', this);
33251 setPositionByLatLng: function(latitude, longitude)
33253 this.setPosition(new google.maps.LatLng(latitude, longitude));
33256 getCurrentPosition: function()
33259 latitude: this.gMapContext.location.lat(),
33260 longitude: this.gMapContext.location.lng()
33264 getAddressName: function()
33266 return this.gMapContext.locationName;
33269 getAddressComponents: function()
33271 return this.gMapContext.addressComponents;
33274 address_component_from_google_geocode: function(address_components)
33278 for (var i = 0; i < address_components.length; i++) {
33279 var component = address_components[i];
33280 if (component.types.indexOf("postal_code") >= 0) {
33281 result.postalCode = component.short_name;
33282 } else if (component.types.indexOf("street_number") >= 0) {
33283 result.streetNumber = component.short_name;
33284 } else if (component.types.indexOf("route") >= 0) {
33285 result.streetName = component.short_name;
33286 } else if (component.types.indexOf("neighborhood") >= 0) {
33287 result.city = component.short_name;
33288 } else if (component.types.indexOf("locality") >= 0) {
33289 result.city = component.short_name;
33290 } else if (component.types.indexOf("sublocality") >= 0) {
33291 result.district = component.short_name;
33292 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
33293 result.stateOrProvince = component.short_name;
33294 } else if (component.types.indexOf("country") >= 0) {
33295 result.country = component.short_name;
33299 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
33300 result.addressLine2 = "";
33304 setZoomLevel: function(zoom)
33306 this.gMapContext.map.setZoom(zoom);
33319 this.fireEvent('show', this);
33330 this.fireEvent('hide', this);
33335 Roo.apply(Roo.bootstrap.LocationPicker, {
33337 OverlayView : function(map, options)
33339 options = options || {};
33346 * @class Roo.bootstrap.Alert
33347 * @extends Roo.bootstrap.Component
33348 * Bootstrap Alert class - shows an alert area box
33350 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
33351 Enter a valid email address
33354 * @cfg {String} title The title of alert
33355 * @cfg {String} html The content of alert
33356 * @cfg {String} weight (success|info|warning|danger) Weight of the message
33357 * @cfg {String} fa font-awesomeicon
33358 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
33359 * @cfg {Boolean} close true to show a x closer
33363 * Create a new alert
33364 * @param {Object} config The config object
33368 Roo.bootstrap.Alert = function(config){
33369 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
33373 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
33379 faicon: false, // BC
33383 getAutoCreate : function()
33395 style : this.close ? '' : 'display:none'
33399 cls : 'roo-alert-icon'
33404 cls : 'roo-alert-title',
33409 cls : 'roo-alert-text',
33416 cfg.cn[0].cls += ' fa ' + this.faicon;
33419 cfg.cn[0].cls += ' fa ' + this.fa;
33423 cfg.cls += ' alert-' + this.weight;
33429 initEvents: function()
33431 this.el.setVisibilityMode(Roo.Element.DISPLAY);
33432 this.titleEl = this.el.select('.roo-alert-title',true).first();
33433 this.iconEl = this.el.select('.roo-alert-icon',true).first();
33434 this.htmlEl = this.el.select('.roo-alert-text',true).first();
33435 if (this.seconds > 0) {
33436 this.hide.defer(this.seconds, this);
33440 * Set the Title Message HTML
33441 * @param {String} html
33443 setTitle : function(str)
33445 this.titleEl.dom.innerHTML = str;
33449 * Set the Body Message HTML
33450 * @param {String} html
33452 setHtml : function(str)
33454 this.htmlEl.dom.innerHTML = str;
33457 * Set the Weight of the alert
33458 * @param {String} (success|info|warning|danger) weight
33461 setWeight : function(weight)
33464 this.el.removeClass('alert-' + this.weight);
33467 this.weight = weight;
33469 this.el.addClass('alert-' + this.weight);
33472 * Set the Icon of the alert
33473 * @param {String} see fontawsome names (name without the 'fa-' bit)
33475 setIcon : function(icon)
33478 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
33481 this.faicon = icon;
33483 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
33508 * @class Roo.bootstrap.UploadCropbox
33509 * @extends Roo.bootstrap.Component
33510 * Bootstrap UploadCropbox class
33511 * @cfg {String} emptyText show when image has been loaded
33512 * @cfg {String} rotateNotify show when image too small to rotate
33513 * @cfg {Number} errorTimeout default 3000
33514 * @cfg {Number} minWidth default 300
33515 * @cfg {Number} minHeight default 300
33516 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
33517 * @cfg {Boolean} isDocument (true|false) default false
33518 * @cfg {String} url action url
33519 * @cfg {String} paramName default 'imageUpload'
33520 * @cfg {String} method default POST
33521 * @cfg {Boolean} loadMask (true|false) default true
33522 * @cfg {Boolean} loadingText default 'Loading...'
33525 * Create a new UploadCropbox
33526 * @param {Object} config The config object
33529 Roo.bootstrap.UploadCropbox = function(config){
33530 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
33534 * @event beforeselectfile
33535 * Fire before select file
33536 * @param {Roo.bootstrap.UploadCropbox} this
33538 "beforeselectfile" : true,
33541 * Fire after initEvent
33542 * @param {Roo.bootstrap.UploadCropbox} this
33547 * Fire after initEvent
33548 * @param {Roo.bootstrap.UploadCropbox} this
33549 * @param {String} data
33554 * Fire when preparing the file data
33555 * @param {Roo.bootstrap.UploadCropbox} this
33556 * @param {Object} file
33561 * Fire when get exception
33562 * @param {Roo.bootstrap.UploadCropbox} this
33563 * @param {XMLHttpRequest} xhr
33565 "exception" : true,
33567 * @event beforeloadcanvas
33568 * Fire before load the canvas
33569 * @param {Roo.bootstrap.UploadCropbox} this
33570 * @param {String} src
33572 "beforeloadcanvas" : true,
33575 * Fire when trash image
33576 * @param {Roo.bootstrap.UploadCropbox} this
33581 * Fire when download the image
33582 * @param {Roo.bootstrap.UploadCropbox} this
33586 * @event footerbuttonclick
33587 * Fire when footerbuttonclick
33588 * @param {Roo.bootstrap.UploadCropbox} this
33589 * @param {String} type
33591 "footerbuttonclick" : true,
33595 * @param {Roo.bootstrap.UploadCropbox} this
33600 * Fire when rotate the image
33601 * @param {Roo.bootstrap.UploadCropbox} this
33602 * @param {String} pos
33607 * Fire when inspect the file
33608 * @param {Roo.bootstrap.UploadCropbox} this
33609 * @param {Object} file
33614 * Fire when xhr upload the file
33615 * @param {Roo.bootstrap.UploadCropbox} this
33616 * @param {Object} data
33621 * Fire when arrange the file data
33622 * @param {Roo.bootstrap.UploadCropbox} this
33623 * @param {Object} formData
33628 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
33631 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
33633 emptyText : 'Click to upload image',
33634 rotateNotify : 'Image is too small to rotate',
33635 errorTimeout : 3000,
33649 cropType : 'image/jpeg',
33651 canvasLoaded : false,
33652 isDocument : false,
33654 paramName : 'imageUpload',
33656 loadingText : 'Loading...',
33659 getAutoCreate : function()
33663 cls : 'roo-upload-cropbox',
33667 cls : 'roo-upload-cropbox-selector',
33672 cls : 'roo-upload-cropbox-body',
33673 style : 'cursor:pointer',
33677 cls : 'roo-upload-cropbox-preview'
33681 cls : 'roo-upload-cropbox-thumb'
33685 cls : 'roo-upload-cropbox-empty-notify',
33686 html : this.emptyText
33690 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
33691 html : this.rotateNotify
33697 cls : 'roo-upload-cropbox-footer',
33700 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
33710 onRender : function(ct, position)
33712 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
33714 if (this.buttons.length) {
33716 Roo.each(this.buttons, function(bb) {
33718 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
33720 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
33726 this.maskEl = this.el;
33730 initEvents : function()
33732 this.urlAPI = (window.createObjectURL && window) ||
33733 (window.URL && URL.revokeObjectURL && URL) ||
33734 (window.webkitURL && webkitURL);
33736 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
33737 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33739 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
33740 this.selectorEl.hide();
33742 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
33743 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33745 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
33746 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33747 this.thumbEl.hide();
33749 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
33750 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33752 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
33753 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33754 this.errorEl.hide();
33756 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
33757 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33758 this.footerEl.hide();
33760 this.setThumbBoxSize();
33766 this.fireEvent('initial', this);
33773 window.addEventListener("resize", function() { _this.resize(); } );
33775 this.bodyEl.on('click', this.beforeSelectFile, this);
33778 this.bodyEl.on('touchstart', this.onTouchStart, this);
33779 this.bodyEl.on('touchmove', this.onTouchMove, this);
33780 this.bodyEl.on('touchend', this.onTouchEnd, this);
33784 this.bodyEl.on('mousedown', this.onMouseDown, this);
33785 this.bodyEl.on('mousemove', this.onMouseMove, this);
33786 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
33787 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
33788 Roo.get(document).on('mouseup', this.onMouseUp, this);
33791 this.selectorEl.on('change', this.onFileSelected, this);
33797 this.baseScale = 1;
33799 this.baseRotate = 1;
33800 this.dragable = false;
33801 this.pinching = false;
33804 this.cropData = false;
33805 this.notifyEl.dom.innerHTML = this.emptyText;
33807 this.selectorEl.dom.value = '';
33811 resize : function()
33813 if(this.fireEvent('resize', this) != false){
33814 this.setThumbBoxPosition();
33815 this.setCanvasPosition();
33819 onFooterButtonClick : function(e, el, o, type)
33822 case 'rotate-left' :
33823 this.onRotateLeft(e);
33825 case 'rotate-right' :
33826 this.onRotateRight(e);
33829 this.beforeSelectFile(e);
33844 this.fireEvent('footerbuttonclick', this, type);
33847 beforeSelectFile : function(e)
33849 e.preventDefault();
33851 if(this.fireEvent('beforeselectfile', this) != false){
33852 this.selectorEl.dom.click();
33856 onFileSelected : function(e)
33858 e.preventDefault();
33860 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
33864 var file = this.selectorEl.dom.files[0];
33866 if(this.fireEvent('inspect', this, file) != false){
33867 this.prepare(file);
33872 trash : function(e)
33874 this.fireEvent('trash', this);
33877 download : function(e)
33879 this.fireEvent('download', this);
33882 loadCanvas : function(src)
33884 if(this.fireEvent('beforeloadcanvas', this, src) != false){
33888 this.imageEl = document.createElement('img');
33892 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
33894 this.imageEl.src = src;
33898 onLoadCanvas : function()
33900 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
33901 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
33903 this.bodyEl.un('click', this.beforeSelectFile, this);
33905 this.notifyEl.hide();
33906 this.thumbEl.show();
33907 this.footerEl.show();
33909 this.baseRotateLevel();
33911 if(this.isDocument){
33912 this.setThumbBoxSize();
33915 this.setThumbBoxPosition();
33917 this.baseScaleLevel();
33923 this.canvasLoaded = true;
33926 this.maskEl.unmask();
33931 setCanvasPosition : function()
33933 if(!this.canvasEl){
33937 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
33938 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
33940 this.previewEl.setLeft(pw);
33941 this.previewEl.setTop(ph);
33945 onMouseDown : function(e)
33949 this.dragable = true;
33950 this.pinching = false;
33952 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
33953 this.dragable = false;
33957 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33958 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33962 onMouseMove : function(e)
33966 if(!this.canvasLoaded){
33970 if (!this.dragable){
33974 var minX = Math.ceil(this.thumbEl.getLeft(true));
33975 var minY = Math.ceil(this.thumbEl.getTop(true));
33977 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
33978 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
33980 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33981 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33983 x = x - this.mouseX;
33984 y = y - this.mouseY;
33986 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
33987 var bgY = Math.ceil(y + this.previewEl.getTop(true));
33989 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
33990 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
33992 this.previewEl.setLeft(bgX);
33993 this.previewEl.setTop(bgY);
33995 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33996 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33999 onMouseUp : function(e)
34003 this.dragable = false;
34006 onMouseWheel : function(e)
34010 this.startScale = this.scale;
34012 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
34014 if(!this.zoomable()){
34015 this.scale = this.startScale;
34024 zoomable : function()
34026 var minScale = this.thumbEl.getWidth() / this.minWidth;
34028 if(this.minWidth < this.minHeight){
34029 minScale = this.thumbEl.getHeight() / this.minHeight;
34032 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
34033 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
34037 (this.rotate == 0 || this.rotate == 180) &&
34039 width > this.imageEl.OriginWidth ||
34040 height > this.imageEl.OriginHeight ||
34041 (width < this.minWidth && height < this.minHeight)
34049 (this.rotate == 90 || this.rotate == 270) &&
34051 width > this.imageEl.OriginWidth ||
34052 height > this.imageEl.OriginHeight ||
34053 (width < this.minHeight && height < this.minWidth)
34060 !this.isDocument &&
34061 (this.rotate == 0 || this.rotate == 180) &&
34063 width < this.minWidth ||
34064 width > this.imageEl.OriginWidth ||
34065 height < this.minHeight ||
34066 height > this.imageEl.OriginHeight
34073 !this.isDocument &&
34074 (this.rotate == 90 || this.rotate == 270) &&
34076 width < this.minHeight ||
34077 width > this.imageEl.OriginWidth ||
34078 height < this.minWidth ||
34079 height > this.imageEl.OriginHeight
34089 onRotateLeft : function(e)
34091 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
34093 var minScale = this.thumbEl.getWidth() / this.minWidth;
34095 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
34096 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
34098 this.startScale = this.scale;
34100 while (this.getScaleLevel() < minScale){
34102 this.scale = this.scale + 1;
34104 if(!this.zoomable()){
34109 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
34110 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
34115 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
34122 this.scale = this.startScale;
34124 this.onRotateFail();
34129 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
34131 if(this.isDocument){
34132 this.setThumbBoxSize();
34133 this.setThumbBoxPosition();
34134 this.setCanvasPosition();
34139 this.fireEvent('rotate', this, 'left');
34143 onRotateRight : function(e)
34145 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
34147 var minScale = this.thumbEl.getWidth() / this.minWidth;
34149 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
34150 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
34152 this.startScale = this.scale;
34154 while (this.getScaleLevel() < minScale){
34156 this.scale = this.scale + 1;
34158 if(!this.zoomable()){
34163 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
34164 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
34169 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
34176 this.scale = this.startScale;
34178 this.onRotateFail();
34183 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
34185 if(this.isDocument){
34186 this.setThumbBoxSize();
34187 this.setThumbBoxPosition();
34188 this.setCanvasPosition();
34193 this.fireEvent('rotate', this, 'right');
34196 onRotateFail : function()
34198 this.errorEl.show(true);
34202 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
34207 this.previewEl.dom.innerHTML = '';
34209 var canvasEl = document.createElement("canvas");
34211 var contextEl = canvasEl.getContext("2d");
34213 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34214 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34215 var center = this.imageEl.OriginWidth / 2;
34217 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
34218 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34219 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34220 center = this.imageEl.OriginHeight / 2;
34223 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
34225 contextEl.translate(center, center);
34226 contextEl.rotate(this.rotate * Math.PI / 180);
34228 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34230 this.canvasEl = document.createElement("canvas");
34232 this.contextEl = this.canvasEl.getContext("2d");
34234 switch (this.rotate) {
34237 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34238 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34240 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34245 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34246 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34248 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34249 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);
34253 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34258 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34259 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34261 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34262 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);
34266 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);
34271 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34272 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34274 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34275 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34279 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);
34286 this.previewEl.appendChild(this.canvasEl);
34288 this.setCanvasPosition();
34293 if(!this.canvasLoaded){
34297 var imageCanvas = document.createElement("canvas");
34299 var imageContext = imageCanvas.getContext("2d");
34301 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34302 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34304 var center = imageCanvas.width / 2;
34306 imageContext.translate(center, center);
34308 imageContext.rotate(this.rotate * Math.PI / 180);
34310 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34312 var canvas = document.createElement("canvas");
34314 var context = canvas.getContext("2d");
34316 canvas.width = this.minWidth;
34317 canvas.height = this.minHeight;
34319 switch (this.rotate) {
34322 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34323 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34325 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34326 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34328 var targetWidth = this.minWidth - 2 * x;
34329 var targetHeight = this.minHeight - 2 * y;
34333 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34334 scale = targetWidth / width;
34337 if(x > 0 && y == 0){
34338 scale = targetHeight / height;
34341 if(x > 0 && y > 0){
34342 scale = targetWidth / width;
34344 if(width < height){
34345 scale = targetHeight / height;
34349 context.scale(scale, scale);
34351 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34352 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34354 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34355 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34357 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34362 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34363 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34365 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34366 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34368 var targetWidth = this.minWidth - 2 * x;
34369 var targetHeight = this.minHeight - 2 * y;
34373 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34374 scale = targetWidth / width;
34377 if(x > 0 && y == 0){
34378 scale = targetHeight / height;
34381 if(x > 0 && y > 0){
34382 scale = targetWidth / width;
34384 if(width < height){
34385 scale = targetHeight / height;
34389 context.scale(scale, scale);
34391 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34392 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34394 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34395 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34397 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34399 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34404 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34405 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34407 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34408 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34410 var targetWidth = this.minWidth - 2 * x;
34411 var targetHeight = this.minHeight - 2 * y;
34415 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34416 scale = targetWidth / width;
34419 if(x > 0 && y == 0){
34420 scale = targetHeight / height;
34423 if(x > 0 && y > 0){
34424 scale = targetWidth / width;
34426 if(width < height){
34427 scale = targetHeight / height;
34431 context.scale(scale, scale);
34433 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34434 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34436 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34437 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34439 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34440 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34442 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34447 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34448 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34450 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34451 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34453 var targetWidth = this.minWidth - 2 * x;
34454 var targetHeight = this.minHeight - 2 * y;
34458 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34459 scale = targetWidth / width;
34462 if(x > 0 && y == 0){
34463 scale = targetHeight / height;
34466 if(x > 0 && y > 0){
34467 scale = targetWidth / width;
34469 if(width < height){
34470 scale = targetHeight / height;
34474 context.scale(scale, scale);
34476 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34477 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34479 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34480 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34482 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34484 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34491 this.cropData = canvas.toDataURL(this.cropType);
34493 if(this.fireEvent('crop', this, this.cropData) !== false){
34494 this.process(this.file, this.cropData);
34501 setThumbBoxSize : function()
34505 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
34506 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
34507 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
34509 this.minWidth = width;
34510 this.minHeight = height;
34512 if(this.rotate == 90 || this.rotate == 270){
34513 this.minWidth = height;
34514 this.minHeight = width;
34519 width = Math.ceil(this.minWidth * height / this.minHeight);
34521 if(this.minWidth > this.minHeight){
34523 height = Math.ceil(this.minHeight * width / this.minWidth);
34526 this.thumbEl.setStyle({
34527 width : width + 'px',
34528 height : height + 'px'
34535 setThumbBoxPosition : function()
34537 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
34538 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
34540 this.thumbEl.setLeft(x);
34541 this.thumbEl.setTop(y);
34545 baseRotateLevel : function()
34547 this.baseRotate = 1;
34550 typeof(this.exif) != 'undefined' &&
34551 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
34552 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
34554 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
34557 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
34561 baseScaleLevel : function()
34565 if(this.isDocument){
34567 if(this.baseRotate == 6 || this.baseRotate == 8){
34569 height = this.thumbEl.getHeight();
34570 this.baseScale = height / this.imageEl.OriginWidth;
34572 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
34573 width = this.thumbEl.getWidth();
34574 this.baseScale = width / this.imageEl.OriginHeight;
34580 height = this.thumbEl.getHeight();
34581 this.baseScale = height / this.imageEl.OriginHeight;
34583 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
34584 width = this.thumbEl.getWidth();
34585 this.baseScale = width / this.imageEl.OriginWidth;
34591 if(this.baseRotate == 6 || this.baseRotate == 8){
34593 width = this.thumbEl.getHeight();
34594 this.baseScale = width / this.imageEl.OriginHeight;
34596 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
34597 height = this.thumbEl.getWidth();
34598 this.baseScale = height / this.imageEl.OriginHeight;
34601 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34602 height = this.thumbEl.getWidth();
34603 this.baseScale = height / this.imageEl.OriginHeight;
34605 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
34606 width = this.thumbEl.getHeight();
34607 this.baseScale = width / this.imageEl.OriginWidth;
34614 width = this.thumbEl.getWidth();
34615 this.baseScale = width / this.imageEl.OriginWidth;
34617 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
34618 height = this.thumbEl.getHeight();
34619 this.baseScale = height / this.imageEl.OriginHeight;
34622 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34624 height = this.thumbEl.getHeight();
34625 this.baseScale = height / this.imageEl.OriginHeight;
34627 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
34628 width = this.thumbEl.getWidth();
34629 this.baseScale = width / this.imageEl.OriginWidth;
34637 getScaleLevel : function()
34639 return this.baseScale * Math.pow(1.1, this.scale);
34642 onTouchStart : function(e)
34644 if(!this.canvasLoaded){
34645 this.beforeSelectFile(e);
34649 var touches = e.browserEvent.touches;
34655 if(touches.length == 1){
34656 this.onMouseDown(e);
34660 if(touches.length != 2){
34666 for(var i = 0, finger; finger = touches[i]; i++){
34667 coords.push(finger.pageX, finger.pageY);
34670 var x = Math.pow(coords[0] - coords[2], 2);
34671 var y = Math.pow(coords[1] - coords[3], 2);
34673 this.startDistance = Math.sqrt(x + y);
34675 this.startScale = this.scale;
34677 this.pinching = true;
34678 this.dragable = false;
34682 onTouchMove : function(e)
34684 if(!this.pinching && !this.dragable){
34688 var touches = e.browserEvent.touches;
34695 this.onMouseMove(e);
34701 for(var i = 0, finger; finger = touches[i]; i++){
34702 coords.push(finger.pageX, finger.pageY);
34705 var x = Math.pow(coords[0] - coords[2], 2);
34706 var y = Math.pow(coords[1] - coords[3], 2);
34708 this.endDistance = Math.sqrt(x + y);
34710 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
34712 if(!this.zoomable()){
34713 this.scale = this.startScale;
34721 onTouchEnd : function(e)
34723 this.pinching = false;
34724 this.dragable = false;
34728 process : function(file, crop)
34731 this.maskEl.mask(this.loadingText);
34734 this.xhr = new XMLHttpRequest();
34736 file.xhr = this.xhr;
34738 this.xhr.open(this.method, this.url, true);
34741 "Accept": "application/json",
34742 "Cache-Control": "no-cache",
34743 "X-Requested-With": "XMLHttpRequest"
34746 for (var headerName in headers) {
34747 var headerValue = headers[headerName];
34749 this.xhr.setRequestHeader(headerName, headerValue);
34755 this.xhr.onload = function()
34757 _this.xhrOnLoad(_this.xhr);
34760 this.xhr.onerror = function()
34762 _this.xhrOnError(_this.xhr);
34765 var formData = new FormData();
34767 formData.append('returnHTML', 'NO');
34770 formData.append('crop', crop);
34773 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
34774 formData.append(this.paramName, file, file.name);
34777 if(typeof(file.filename) != 'undefined'){
34778 formData.append('filename', file.filename);
34781 if(typeof(file.mimetype) != 'undefined'){
34782 formData.append('mimetype', file.mimetype);
34785 if(this.fireEvent('arrange', this, formData) != false){
34786 this.xhr.send(formData);
34790 xhrOnLoad : function(xhr)
34793 this.maskEl.unmask();
34796 if (xhr.readyState !== 4) {
34797 this.fireEvent('exception', this, xhr);
34801 var response = Roo.decode(xhr.responseText);
34803 if(!response.success){
34804 this.fireEvent('exception', this, xhr);
34808 var response = Roo.decode(xhr.responseText);
34810 this.fireEvent('upload', this, response);
34814 xhrOnError : function()
34817 this.maskEl.unmask();
34820 Roo.log('xhr on error');
34822 var response = Roo.decode(xhr.responseText);
34828 prepare : function(file)
34831 this.maskEl.mask(this.loadingText);
34837 if(typeof(file) === 'string'){
34838 this.loadCanvas(file);
34842 if(!file || !this.urlAPI){
34847 this.cropType = file.type;
34851 if(this.fireEvent('prepare', this, this.file) != false){
34853 var reader = new FileReader();
34855 reader.onload = function (e) {
34856 if (e.target.error) {
34857 Roo.log(e.target.error);
34861 var buffer = e.target.result,
34862 dataView = new DataView(buffer),
34864 maxOffset = dataView.byteLength - 4,
34868 if (dataView.getUint16(0) === 0xffd8) {
34869 while (offset < maxOffset) {
34870 markerBytes = dataView.getUint16(offset);
34872 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
34873 markerLength = dataView.getUint16(offset + 2) + 2;
34874 if (offset + markerLength > dataView.byteLength) {
34875 Roo.log('Invalid meta data: Invalid segment size.');
34879 if(markerBytes == 0xffe1){
34880 _this.parseExifData(
34887 offset += markerLength;
34897 var url = _this.urlAPI.createObjectURL(_this.file);
34899 _this.loadCanvas(url);
34904 reader.readAsArrayBuffer(this.file);
34910 parseExifData : function(dataView, offset, length)
34912 var tiffOffset = offset + 10,
34916 if (dataView.getUint32(offset + 4) !== 0x45786966) {
34917 // No Exif data, might be XMP data instead
34921 // Check for the ASCII code for "Exif" (0x45786966):
34922 if (dataView.getUint32(offset + 4) !== 0x45786966) {
34923 // No Exif data, might be XMP data instead
34926 if (tiffOffset + 8 > dataView.byteLength) {
34927 Roo.log('Invalid Exif data: Invalid segment size.');
34930 // Check for the two null bytes:
34931 if (dataView.getUint16(offset + 8) !== 0x0000) {
34932 Roo.log('Invalid Exif data: Missing byte alignment offset.');
34935 // Check the byte alignment:
34936 switch (dataView.getUint16(tiffOffset)) {
34938 littleEndian = true;
34941 littleEndian = false;
34944 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
34947 // Check for the TIFF tag marker (0x002A):
34948 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
34949 Roo.log('Invalid Exif data: Missing TIFF marker.');
34952 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
34953 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
34955 this.parseExifTags(
34958 tiffOffset + dirOffset,
34963 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
34968 if (dirOffset + 6 > dataView.byteLength) {
34969 Roo.log('Invalid Exif data: Invalid directory offset.');
34972 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
34973 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
34974 if (dirEndOffset + 4 > dataView.byteLength) {
34975 Roo.log('Invalid Exif data: Invalid directory size.');
34978 for (i = 0; i < tagsNumber; i += 1) {
34982 dirOffset + 2 + 12 * i, // tag offset
34986 // Return the offset to the next directory:
34987 return dataView.getUint32(dirEndOffset, littleEndian);
34990 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
34992 var tag = dataView.getUint16(offset, littleEndian);
34994 this.exif[tag] = this.getExifValue(
34998 dataView.getUint16(offset + 2, littleEndian), // tag type
34999 dataView.getUint32(offset + 4, littleEndian), // tag length
35004 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
35006 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
35015 Roo.log('Invalid Exif data: Invalid tag type.');
35019 tagSize = tagType.size * length;
35020 // Determine if the value is contained in the dataOffset bytes,
35021 // or if the value at the dataOffset is a pointer to the actual data:
35022 dataOffset = tagSize > 4 ?
35023 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
35024 if (dataOffset + tagSize > dataView.byteLength) {
35025 Roo.log('Invalid Exif data: Invalid data offset.');
35028 if (length === 1) {
35029 return tagType.getValue(dataView, dataOffset, littleEndian);
35032 for (i = 0; i < length; i += 1) {
35033 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
35036 if (tagType.ascii) {
35038 // Concatenate the chars:
35039 for (i = 0; i < values.length; i += 1) {
35041 // Ignore the terminating NULL byte(s):
35042 if (c === '\u0000') {
35054 Roo.apply(Roo.bootstrap.UploadCropbox, {
35056 'Orientation': 0x0112
35060 1: 0, //'top-left',
35062 3: 180, //'bottom-right',
35063 // 4: 'bottom-left',
35065 6: 90, //'right-top',
35066 // 7: 'right-bottom',
35067 8: 270 //'left-bottom'
35071 // byte, 8-bit unsigned int:
35073 getValue: function (dataView, dataOffset) {
35074 return dataView.getUint8(dataOffset);
35078 // ascii, 8-bit byte:
35080 getValue: function (dataView, dataOffset) {
35081 return String.fromCharCode(dataView.getUint8(dataOffset));
35086 // short, 16 bit int:
35088 getValue: function (dataView, dataOffset, littleEndian) {
35089 return dataView.getUint16(dataOffset, littleEndian);
35093 // long, 32 bit int:
35095 getValue: function (dataView, dataOffset, littleEndian) {
35096 return dataView.getUint32(dataOffset, littleEndian);
35100 // rational = two long values, first is numerator, second is denominator:
35102 getValue: function (dataView, dataOffset, littleEndian) {
35103 return dataView.getUint32(dataOffset, littleEndian) /
35104 dataView.getUint32(dataOffset + 4, littleEndian);
35108 // slong, 32 bit signed int:
35110 getValue: function (dataView, dataOffset, littleEndian) {
35111 return dataView.getInt32(dataOffset, littleEndian);
35115 // srational, two slongs, first is numerator, second is denominator:
35117 getValue: function (dataView, dataOffset, littleEndian) {
35118 return dataView.getInt32(dataOffset, littleEndian) /
35119 dataView.getInt32(dataOffset + 4, littleEndian);
35129 cls : 'btn-group roo-upload-cropbox-rotate-left',
35130 action : 'rotate-left',
35134 cls : 'btn btn-default',
35135 html : '<i class="fa fa-undo"></i>'
35141 cls : 'btn-group roo-upload-cropbox-picture',
35142 action : 'picture',
35146 cls : 'btn btn-default',
35147 html : '<i class="fa fa-picture-o"></i>'
35153 cls : 'btn-group roo-upload-cropbox-rotate-right',
35154 action : 'rotate-right',
35158 cls : 'btn btn-default',
35159 html : '<i class="fa fa-repeat"></i>'
35167 cls : 'btn-group roo-upload-cropbox-rotate-left',
35168 action : 'rotate-left',
35172 cls : 'btn btn-default',
35173 html : '<i class="fa fa-undo"></i>'
35179 cls : 'btn-group roo-upload-cropbox-download',
35180 action : 'download',
35184 cls : 'btn btn-default',
35185 html : '<i class="fa fa-download"></i>'
35191 cls : 'btn-group roo-upload-cropbox-crop',
35196 cls : 'btn btn-default',
35197 html : '<i class="fa fa-crop"></i>'
35203 cls : 'btn-group roo-upload-cropbox-trash',
35208 cls : 'btn btn-default',
35209 html : '<i class="fa fa-trash"></i>'
35215 cls : 'btn-group roo-upload-cropbox-rotate-right',
35216 action : 'rotate-right',
35220 cls : 'btn btn-default',
35221 html : '<i class="fa fa-repeat"></i>'
35229 cls : 'btn-group roo-upload-cropbox-rotate-left',
35230 action : 'rotate-left',
35234 cls : 'btn btn-default',
35235 html : '<i class="fa fa-undo"></i>'
35241 cls : 'btn-group roo-upload-cropbox-rotate-right',
35242 action : 'rotate-right',
35246 cls : 'btn btn-default',
35247 html : '<i class="fa fa-repeat"></i>'
35260 * @class Roo.bootstrap.DocumentManager
35261 * @extends Roo.bootstrap.Component
35262 * Bootstrap DocumentManager class
35263 * @cfg {String} paramName default 'imageUpload'
35264 * @cfg {String} toolTipName default 'filename'
35265 * @cfg {String} method default POST
35266 * @cfg {String} url action url
35267 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
35268 * @cfg {Boolean} multiple multiple upload default true
35269 * @cfg {Number} thumbSize default 300
35270 * @cfg {String} fieldLabel
35271 * @cfg {Number} labelWidth default 4
35272 * @cfg {String} labelAlign (left|top) default left
35273 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
35274 * @cfg {Number} labellg set the width of label (1-12)
35275 * @cfg {Number} labelmd set the width of label (1-12)
35276 * @cfg {Number} labelsm set the width of label (1-12)
35277 * @cfg {Number} labelxs set the width of label (1-12)
35280 * Create a new DocumentManager
35281 * @param {Object} config The config object
35284 Roo.bootstrap.DocumentManager = function(config){
35285 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
35288 this.delegates = [];
35293 * Fire when initial the DocumentManager
35294 * @param {Roo.bootstrap.DocumentManager} this
35299 * inspect selected file
35300 * @param {Roo.bootstrap.DocumentManager} this
35301 * @param {File} file
35306 * Fire when xhr load exception
35307 * @param {Roo.bootstrap.DocumentManager} this
35308 * @param {XMLHttpRequest} xhr
35310 "exception" : true,
35312 * @event afterupload
35313 * Fire when xhr load exception
35314 * @param {Roo.bootstrap.DocumentManager} this
35315 * @param {XMLHttpRequest} xhr
35317 "afterupload" : true,
35320 * prepare the form data
35321 * @param {Roo.bootstrap.DocumentManager} this
35322 * @param {Object} formData
35327 * Fire when remove the file
35328 * @param {Roo.bootstrap.DocumentManager} this
35329 * @param {Object} file
35334 * Fire after refresh the file
35335 * @param {Roo.bootstrap.DocumentManager} this
35340 * Fire after click the image
35341 * @param {Roo.bootstrap.DocumentManager} this
35342 * @param {Object} file
35347 * Fire when upload a image and editable set to true
35348 * @param {Roo.bootstrap.DocumentManager} this
35349 * @param {Object} file
35353 * @event beforeselectfile
35354 * Fire before select file
35355 * @param {Roo.bootstrap.DocumentManager} this
35357 "beforeselectfile" : true,
35360 * Fire before process file
35361 * @param {Roo.bootstrap.DocumentManager} this
35362 * @param {Object} file
35366 * @event previewrendered
35367 * Fire when preview rendered
35368 * @param {Roo.bootstrap.DocumentManager} this
35369 * @param {Object} file
35371 "previewrendered" : true,
35374 "previewResize" : true
35379 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
35388 paramName : 'imageUpload',
35389 toolTipName : 'filename',
35392 labelAlign : 'left',
35402 getAutoCreate : function()
35404 var managerWidget = {
35406 cls : 'roo-document-manager',
35410 cls : 'roo-document-manager-selector',
35415 cls : 'roo-document-manager-uploader',
35419 cls : 'roo-document-manager-upload-btn',
35420 html : '<i class="fa fa-plus"></i>'
35431 cls : 'column col-md-12',
35436 if(this.fieldLabel.length){
35441 cls : 'column col-md-12',
35442 html : this.fieldLabel
35446 cls : 'column col-md-12',
35451 if(this.labelAlign == 'left'){
35456 html : this.fieldLabel
35465 if(this.labelWidth > 12){
35466 content[0].style = "width: " + this.labelWidth + 'px';
35469 if(this.labelWidth < 13 && this.labelmd == 0){
35470 this.labelmd = this.labelWidth;
35473 if(this.labellg > 0){
35474 content[0].cls += ' col-lg-' + this.labellg;
35475 content[1].cls += ' col-lg-' + (12 - this.labellg);
35478 if(this.labelmd > 0){
35479 content[0].cls += ' col-md-' + this.labelmd;
35480 content[1].cls += ' col-md-' + (12 - this.labelmd);
35483 if(this.labelsm > 0){
35484 content[0].cls += ' col-sm-' + this.labelsm;
35485 content[1].cls += ' col-sm-' + (12 - this.labelsm);
35488 if(this.labelxs > 0){
35489 content[0].cls += ' col-xs-' + this.labelxs;
35490 content[1].cls += ' col-xs-' + (12 - this.labelxs);
35498 cls : 'row clearfix',
35506 initEvents : function()
35508 this.managerEl = this.el.select('.roo-document-manager', true).first();
35509 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35511 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
35512 this.selectorEl.hide();
35515 this.selectorEl.attr('multiple', 'multiple');
35518 this.selectorEl.on('change', this.onFileSelected, this);
35520 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
35521 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35523 this.uploader.on('click', this.onUploaderClick, this);
35525 this.renderProgressDialog();
35529 window.addEventListener("resize", function() { _this.refresh(); } );
35531 this.fireEvent('initial', this);
35534 renderProgressDialog : function()
35538 this.progressDialog = new Roo.bootstrap.Modal({
35539 cls : 'roo-document-manager-progress-dialog',
35540 allow_close : false,
35551 btnclick : function() {
35552 _this.uploadCancel();
35558 this.progressDialog.render(Roo.get(document.body));
35560 this.progress = new Roo.bootstrap.Progress({
35561 cls : 'roo-document-manager-progress',
35566 this.progress.render(this.progressDialog.getChildContainer());
35568 this.progressBar = new Roo.bootstrap.ProgressBar({
35569 cls : 'roo-document-manager-progress-bar',
35572 aria_valuemax : 12,
35576 this.progressBar.render(this.progress.getChildContainer());
35579 onUploaderClick : function(e)
35581 e.preventDefault();
35583 if(this.fireEvent('beforeselectfile', this) != false){
35584 this.selectorEl.dom.click();
35589 onFileSelected : function(e)
35591 e.preventDefault();
35593 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35597 Roo.each(this.selectorEl.dom.files, function(file){
35598 if(this.fireEvent('inspect', this, file) != false){
35599 this.files.push(file);
35609 this.selectorEl.dom.value = '';
35611 if(!this.files || !this.files.length){
35615 if(this.boxes > 0 && this.files.length > this.boxes){
35616 this.files = this.files.slice(0, this.boxes);
35619 this.uploader.show();
35621 if(this.boxes > 0 && this.files.length > this.boxes - 1){
35622 this.uploader.hide();
35631 Roo.each(this.files, function(file){
35633 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
35634 var f = this.renderPreview(file);
35639 if(file.type.indexOf('image') != -1){
35640 this.delegates.push(
35642 _this.process(file);
35643 }).createDelegate(this)
35651 _this.process(file);
35652 }).createDelegate(this)
35657 this.files = files;
35659 this.delegates = this.delegates.concat(docs);
35661 if(!this.delegates.length){
35666 this.progressBar.aria_valuemax = this.delegates.length;
35673 arrange : function()
35675 if(!this.delegates.length){
35676 this.progressDialog.hide();
35681 var delegate = this.delegates.shift();
35683 this.progressDialog.show();
35685 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
35687 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
35692 refresh : function()
35694 this.uploader.show();
35696 if(this.boxes > 0 && this.files.length > this.boxes - 1){
35697 this.uploader.hide();
35700 Roo.isTouch ? this.closable(false) : this.closable(true);
35702 this.fireEvent('refresh', this);
35705 onRemove : function(e, el, o)
35707 e.preventDefault();
35709 this.fireEvent('remove', this, o);
35713 remove : function(o)
35717 Roo.each(this.files, function(file){
35718 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
35727 this.files = files;
35734 Roo.each(this.files, function(file){
35739 file.target.remove();
35748 onClick : function(e, el, o)
35750 e.preventDefault();
35752 this.fireEvent('click', this, o);
35756 closable : function(closable)
35758 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
35760 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35772 xhrOnLoad : function(xhr)
35774 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
35778 if (xhr.readyState !== 4) {
35780 this.fireEvent('exception', this, xhr);
35784 var response = Roo.decode(xhr.responseText);
35786 if(!response.success){
35788 this.fireEvent('exception', this, xhr);
35792 var file = this.renderPreview(response.data);
35794 this.files.push(file);
35798 this.fireEvent('afterupload', this, xhr);
35802 xhrOnError : function(xhr)
35804 Roo.log('xhr on error');
35806 var response = Roo.decode(xhr.responseText);
35813 process : function(file)
35815 if(this.fireEvent('process', this, file) !== false){
35816 if(this.editable && file.type.indexOf('image') != -1){
35817 this.fireEvent('edit', this, file);
35821 this.uploadStart(file, false);
35828 uploadStart : function(file, crop)
35830 this.xhr = new XMLHttpRequest();
35832 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
35837 file.xhr = this.xhr;
35839 this.managerEl.createChild({
35841 cls : 'roo-document-manager-loading',
35845 tooltip : file.name,
35846 cls : 'roo-document-manager-thumb',
35847 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
35853 this.xhr.open(this.method, this.url, true);
35856 "Accept": "application/json",
35857 "Cache-Control": "no-cache",
35858 "X-Requested-With": "XMLHttpRequest"
35861 for (var headerName in headers) {
35862 var headerValue = headers[headerName];
35864 this.xhr.setRequestHeader(headerName, headerValue);
35870 this.xhr.onload = function()
35872 _this.xhrOnLoad(_this.xhr);
35875 this.xhr.onerror = function()
35877 _this.xhrOnError(_this.xhr);
35880 var formData = new FormData();
35882 formData.append('returnHTML', 'NO');
35885 formData.append('crop', crop);
35888 formData.append(this.paramName, file, file.name);
35895 if(this.fireEvent('prepare', this, formData, options) != false){
35897 if(options.manually){
35901 this.xhr.send(formData);
35905 this.uploadCancel();
35908 uploadCancel : function()
35914 this.delegates = [];
35916 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
35923 renderPreview : function(file)
35925 if(typeof(file.target) != 'undefined' && file.target){
35929 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
35931 var previewEl = this.managerEl.createChild({
35933 cls : 'roo-document-manager-preview',
35937 tooltip : file[this.toolTipName],
35938 cls : 'roo-document-manager-thumb',
35939 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
35944 html : '<i class="fa fa-times-circle"></i>'
35949 var close = previewEl.select('button.close', true).first();
35951 close.on('click', this.onRemove, this, file);
35953 file.target = previewEl;
35955 var image = previewEl.select('img', true).first();
35959 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
35961 image.on('click', this.onClick, this, file);
35963 this.fireEvent('previewrendered', this, file);
35969 onPreviewLoad : function(file, image)
35971 if(typeof(file.target) == 'undefined' || !file.target){
35975 var width = image.dom.naturalWidth || image.dom.width;
35976 var height = image.dom.naturalHeight || image.dom.height;
35978 if(!this.previewResize) {
35982 if(width > height){
35983 file.target.addClass('wide');
35987 file.target.addClass('tall');
35992 uploadFromSource : function(file, crop)
35994 this.xhr = new XMLHttpRequest();
35996 this.managerEl.createChild({
35998 cls : 'roo-document-manager-loading',
36002 tooltip : file.name,
36003 cls : 'roo-document-manager-thumb',
36004 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
36010 this.xhr.open(this.method, this.url, true);
36013 "Accept": "application/json",
36014 "Cache-Control": "no-cache",
36015 "X-Requested-With": "XMLHttpRequest"
36018 for (var headerName in headers) {
36019 var headerValue = headers[headerName];
36021 this.xhr.setRequestHeader(headerName, headerValue);
36027 this.xhr.onload = function()
36029 _this.xhrOnLoad(_this.xhr);
36032 this.xhr.onerror = function()
36034 _this.xhrOnError(_this.xhr);
36037 var formData = new FormData();
36039 formData.append('returnHTML', 'NO');
36041 formData.append('crop', crop);
36043 if(typeof(file.filename) != 'undefined'){
36044 formData.append('filename', file.filename);
36047 if(typeof(file.mimetype) != 'undefined'){
36048 formData.append('mimetype', file.mimetype);
36053 if(this.fireEvent('prepare', this, formData) != false){
36054 this.xhr.send(formData);
36064 * @class Roo.bootstrap.DocumentViewer
36065 * @extends Roo.bootstrap.Component
36066 * Bootstrap DocumentViewer class
36067 * @cfg {Boolean} showDownload (true|false) show download button (default true)
36068 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
36071 * Create a new DocumentViewer
36072 * @param {Object} config The config object
36075 Roo.bootstrap.DocumentViewer = function(config){
36076 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
36081 * Fire after initEvent
36082 * @param {Roo.bootstrap.DocumentViewer} this
36088 * @param {Roo.bootstrap.DocumentViewer} this
36093 * Fire after download button
36094 * @param {Roo.bootstrap.DocumentViewer} this
36099 * Fire after trash button
36100 * @param {Roo.bootstrap.DocumentViewer} this
36107 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
36109 showDownload : true,
36113 getAutoCreate : function()
36117 cls : 'roo-document-viewer',
36121 cls : 'roo-document-viewer-body',
36125 cls : 'roo-document-viewer-thumb',
36129 cls : 'roo-document-viewer-image'
36137 cls : 'roo-document-viewer-footer',
36140 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
36144 cls : 'btn-group roo-document-viewer-download',
36148 cls : 'btn btn-default',
36149 html : '<i class="fa fa-download"></i>'
36155 cls : 'btn-group roo-document-viewer-trash',
36159 cls : 'btn btn-default',
36160 html : '<i class="fa fa-trash"></i>'
36173 initEvents : function()
36175 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
36176 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36178 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
36179 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36181 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
36182 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36184 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
36185 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
36187 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
36188 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
36190 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
36191 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
36193 this.bodyEl.on('click', this.onClick, this);
36194 this.downloadBtn.on('click', this.onDownload, this);
36195 this.trashBtn.on('click', this.onTrash, this);
36197 this.downloadBtn.hide();
36198 this.trashBtn.hide();
36200 if(this.showDownload){
36201 this.downloadBtn.show();
36204 if(this.showTrash){
36205 this.trashBtn.show();
36208 if(!this.showDownload && !this.showTrash) {
36209 this.footerEl.hide();
36214 initial : function()
36216 this.fireEvent('initial', this);
36220 onClick : function(e)
36222 e.preventDefault();
36224 this.fireEvent('click', this);
36227 onDownload : function(e)
36229 e.preventDefault();
36231 this.fireEvent('download', this);
36234 onTrash : function(e)
36236 e.preventDefault();
36238 this.fireEvent('trash', this);
36250 * @class Roo.bootstrap.form.FieldLabel
36251 * @extends Roo.bootstrap.Component
36252 * Bootstrap FieldLabel class
36253 * @cfg {String} html contents of the element
36254 * @cfg {String} tag tag of the element default label
36255 * @cfg {String} cls class of the element
36256 * @cfg {String} target label target
36257 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
36258 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
36259 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
36260 * @cfg {String} iconTooltip default "This field is required"
36261 * @cfg {String} indicatorpos (left|right) default left
36264 * Create a new FieldLabel
36265 * @param {Object} config The config object
36268 Roo.bootstrap.form.FieldLabel = function(config){
36269 Roo.bootstrap.Element.superclass.constructor.call(this, config);
36274 * Fires after the field has been marked as invalid.
36275 * @param {Roo.form.FieldLabel} this
36276 * @param {String} msg The validation message
36281 * Fires after the field has been validated with no errors.
36282 * @param {Roo.form.FieldLabel} this
36288 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component, {
36295 invalidClass : 'has-warning',
36296 validClass : 'has-success',
36297 iconTooltip : 'This field is required',
36298 indicatorpos : 'left',
36300 getAutoCreate : function(){
36303 if (!this.allowBlank) {
36309 cls : 'roo-bootstrap-field-label ' + this.cls,
36314 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
36315 tooltip : this.iconTooltip
36324 if(this.indicatorpos == 'right'){
36327 cls : 'roo-bootstrap-field-label ' + this.cls,
36336 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
36337 tooltip : this.iconTooltip
36346 initEvents: function()
36348 Roo.bootstrap.Element.superclass.initEvents.call(this);
36350 this.indicator = this.indicatorEl();
36352 if(this.indicator){
36353 this.indicator.removeClass('visible');
36354 this.indicator.addClass('invisible');
36357 Roo.bootstrap.form.FieldLabel.register(this);
36360 indicatorEl : function()
36362 var indicator = this.el.select('i.roo-required-indicator',true).first();
36373 * Mark this field as valid
36375 markValid : function()
36377 if(this.indicator){
36378 this.indicator.removeClass('visible');
36379 this.indicator.addClass('invisible');
36381 if (Roo.bootstrap.version == 3) {
36382 this.el.removeClass(this.invalidClass);
36383 this.el.addClass(this.validClass);
36385 this.el.removeClass('is-invalid');
36386 this.el.addClass('is-valid');
36390 this.fireEvent('valid', this);
36394 * Mark this field as invalid
36395 * @param {String} msg The validation message
36397 markInvalid : function(msg)
36399 if(this.indicator){
36400 this.indicator.removeClass('invisible');
36401 this.indicator.addClass('visible');
36403 if (Roo.bootstrap.version == 3) {
36404 this.el.removeClass(this.validClass);
36405 this.el.addClass(this.invalidClass);
36407 this.el.removeClass('is-valid');
36408 this.el.addClass('is-invalid');
36412 this.fireEvent('invalid', this, msg);
36418 Roo.apply(Roo.bootstrap.form.FieldLabel, {
36423 * register a FieldLabel Group
36424 * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
36426 register : function(label)
36428 if(this.groups.hasOwnProperty(label.target)){
36432 this.groups[label.target] = label;
36436 * fetch a FieldLabel Group based on the target
36437 * @param {string} target
36438 * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
36440 get: function(target) {
36441 if (typeof(this.groups[target]) == 'undefined') {
36445 return this.groups[target] ;
36454 * page DateSplitField.
36460 * @class Roo.bootstrap.form.DateSplitField
36461 * @extends Roo.bootstrap.Component
36462 * Bootstrap DateSplitField class
36463 * @cfg {string} fieldLabel - the label associated
36464 * @cfg {Number} labelWidth set the width of label (0-12)
36465 * @cfg {String} labelAlign (top|left)
36466 * @cfg {Boolean} dayAllowBlank (true|false) default false
36467 * @cfg {Boolean} monthAllowBlank (true|false) default false
36468 * @cfg {Boolean} yearAllowBlank (true|false) default false
36469 * @cfg {string} dayPlaceholder
36470 * @cfg {string} monthPlaceholder
36471 * @cfg {string} yearPlaceholder
36472 * @cfg {string} dayFormat default 'd'
36473 * @cfg {string} monthFormat default 'm'
36474 * @cfg {string} yearFormat default 'Y'
36475 * @cfg {Number} labellg set the width of label (1-12)
36476 * @cfg {Number} labelmd set the width of label (1-12)
36477 * @cfg {Number} labelsm set the width of label (1-12)
36478 * @cfg {Number} labelxs set the width of label (1-12)
36482 * Create a new DateSplitField
36483 * @param {Object} config The config object
36486 Roo.bootstrap.form.DateSplitField = function(config){
36487 Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
36493 * getting the data of years
36494 * @param {Roo.bootstrap.form.DateSplitField} this
36495 * @param {Object} years
36500 * getting the data of days
36501 * @param {Roo.bootstrap.form.DateSplitField} this
36502 * @param {Object} days
36507 * Fires after the field has been marked as invalid.
36508 * @param {Roo.form.Field} this
36509 * @param {String} msg The validation message
36514 * Fires after the field has been validated with no errors.
36515 * @param {Roo.form.Field} this
36521 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component, {
36524 labelAlign : 'top',
36526 dayAllowBlank : false,
36527 monthAllowBlank : false,
36528 yearAllowBlank : false,
36529 dayPlaceholder : '',
36530 monthPlaceholder : '',
36531 yearPlaceholder : '',
36535 isFormField : true,
36541 getAutoCreate : function()
36545 cls : 'row roo-date-split-field-group',
36550 cls : 'form-hidden-field roo-date-split-field-group-value',
36556 var labelCls = 'col-md-12';
36557 var contentCls = 'col-md-4';
36559 if(this.fieldLabel){
36563 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
36567 html : this.fieldLabel
36572 if(this.labelAlign == 'left'){
36574 if(this.labelWidth > 12){
36575 label.style = "width: " + this.labelWidth + 'px';
36578 if(this.labelWidth < 13 && this.labelmd == 0){
36579 this.labelmd = this.labelWidth;
36582 if(this.labellg > 0){
36583 labelCls = ' col-lg-' + this.labellg;
36584 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
36587 if(this.labelmd > 0){
36588 labelCls = ' col-md-' + this.labelmd;
36589 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
36592 if(this.labelsm > 0){
36593 labelCls = ' col-sm-' + this.labelsm;
36594 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
36597 if(this.labelxs > 0){
36598 labelCls = ' col-xs-' + this.labelxs;
36599 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
36603 label.cls += ' ' + labelCls;
36605 cfg.cn.push(label);
36608 Roo.each(['day', 'month', 'year'], function(t){
36611 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
36618 inputEl: function ()
36620 return this.el.select('.roo-date-split-field-group-value', true).first();
36623 onRender : function(ct, position)
36627 Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
36629 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
36631 this.dayField = new Roo.bootstrap.form.ComboBox({
36632 allowBlank : this.dayAllowBlank,
36633 alwaysQuery : true,
36634 displayField : 'value',
36637 forceSelection : true,
36639 placeholder : this.dayPlaceholder,
36640 selectOnFocus : true,
36641 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
36642 triggerAction : 'all',
36644 valueField : 'value',
36645 store : new Roo.data.SimpleStore({
36646 data : (function() {
36648 _this.fireEvent('days', _this, days);
36651 fields : [ 'value' ]
36654 select : function (_self, record, index)
36656 _this.setValue(_this.getValue());
36661 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
36663 this.monthField = new Roo.bootstrap.form.MonthField({
36664 after : '<i class=\"fa fa-calendar\"></i>',
36665 allowBlank : this.monthAllowBlank,
36666 placeholder : this.monthPlaceholder,
36669 render : function (_self)
36671 this.el.select('span.input-group-addon', true).first().on('click', function(e){
36672 e.preventDefault();
36676 select : function (_self, oldvalue, newvalue)
36678 _this.setValue(_this.getValue());
36683 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
36685 this.yearField = new Roo.bootstrap.form.ComboBox({
36686 allowBlank : this.yearAllowBlank,
36687 alwaysQuery : true,
36688 displayField : 'value',
36691 forceSelection : true,
36693 placeholder : this.yearPlaceholder,
36694 selectOnFocus : true,
36695 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
36696 triggerAction : 'all',
36698 valueField : 'value',
36699 store : new Roo.data.SimpleStore({
36700 data : (function() {
36702 _this.fireEvent('years', _this, years);
36705 fields : [ 'value' ]
36708 select : function (_self, record, index)
36710 _this.setValue(_this.getValue());
36715 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
36718 setValue : function(v, format)
36720 this.inputEl.dom.value = v;
36722 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
36724 var d = Date.parseDate(v, f);
36731 this.setDay(d.format(this.dayFormat));
36732 this.setMonth(d.format(this.monthFormat));
36733 this.setYear(d.format(this.yearFormat));
36740 setDay : function(v)
36742 this.dayField.setValue(v);
36743 this.inputEl.dom.value = this.getValue();
36748 setMonth : function(v)
36750 this.monthField.setValue(v, true);
36751 this.inputEl.dom.value = this.getValue();
36756 setYear : function(v)
36758 this.yearField.setValue(v);
36759 this.inputEl.dom.value = this.getValue();
36764 getDay : function()
36766 return this.dayField.getValue();
36769 getMonth : function()
36771 return this.monthField.getValue();
36774 getYear : function()
36776 return this.yearField.getValue();
36779 getValue : function()
36781 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
36783 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
36793 this.inputEl.dom.value = '';
36798 validate : function()
36800 var d = this.dayField.validate();
36801 var m = this.monthField.validate();
36802 var y = this.yearField.validate();
36807 (!this.dayAllowBlank && !d) ||
36808 (!this.monthAllowBlank && !m) ||
36809 (!this.yearAllowBlank && !y)
36814 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
36823 this.markInvalid();
36828 markValid : function()
36831 var label = this.el.select('label', true).first();
36832 var icon = this.el.select('i.fa-star', true).first();
36838 this.fireEvent('valid', this);
36842 * Mark this field as invalid
36843 * @param {String} msg The validation message
36845 markInvalid : function(msg)
36848 var label = this.el.select('label', true).first();
36849 var icon = this.el.select('i.fa-star', true).first();
36851 if(label && !icon){
36852 this.el.select('.roo-date-split-field-label', true).createChild({
36854 cls : 'text-danger fa fa-lg fa-star',
36855 tooltip : 'This field is required',
36856 style : 'margin-right:5px;'
36860 this.fireEvent('invalid', this, msg);
36863 clearInvalid : function()
36865 var label = this.el.select('label', true).first();
36866 var icon = this.el.select('i.fa-star', true).first();
36872 this.fireEvent('valid', this);
36875 getName: function()
36885 * @class Roo.bootstrap.LayoutMasonry
36886 * @extends Roo.bootstrap.Component
36887 * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
36888 * Bootstrap Layout Masonry class
36891 * http://masonry.desandro.com
36893 * The idea is to render all the bricks based on vertical width...
36895 * The original code extends 'outlayer' - we might need to use that....
36898 * Create a new Element
36899 * @param {Object} config The config object
36902 Roo.bootstrap.LayoutMasonry = function(config){
36904 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
36908 Roo.bootstrap.LayoutMasonry.register(this);
36914 * Fire after layout the items
36915 * @param {Roo.bootstrap.LayoutMasonry} this
36916 * @param {Roo.EventObject} e
36923 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
36926 * @cfg {Boolean} isLayoutInstant = no animation?
36928 isLayoutInstant : false, // needed?
36931 * @cfg {Number} boxWidth width of the columns
36936 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
36941 * @cfg {Number} padWidth padding below box..
36946 * @cfg {Number} gutter gutter width..
36951 * @cfg {Number} maxCols maximum number of columns
36957 * @cfg {Boolean} isAutoInitial defalut true
36959 isAutoInitial : true,
36964 * @cfg {Boolean} isHorizontal defalut false
36966 isHorizontal : false,
36968 currentSize : null,
36974 bricks: null, //CompositeElement
36978 _isLayoutInited : false,
36980 // isAlternative : false, // only use for vertical layout...
36983 * @cfg {Number} alternativePadWidth padding below box..
36985 alternativePadWidth : 50,
36987 selectedBrick : [],
36989 getAutoCreate : function(){
36991 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
36995 cls: 'blog-masonary-wrapper ' + this.cls,
36997 cls : 'mas-boxes masonary'
37004 getChildContainer: function( )
37006 if (this.boxesEl) {
37007 return this.boxesEl;
37010 this.boxesEl = this.el.select('.mas-boxes').first();
37012 return this.boxesEl;
37016 initEvents : function()
37020 if(this.isAutoInitial){
37021 Roo.log('hook children rendered');
37022 this.on('childrenrendered', function() {
37023 Roo.log('children rendered');
37029 initial : function()
37031 this.selectedBrick = [];
37033 this.currentSize = this.el.getBox(true);
37035 Roo.EventManager.onWindowResize(this.resize, this);
37037 if(!this.isAutoInitial){
37045 //this.layout.defer(500,this);
37049 resize : function()
37051 var cs = this.el.getBox(true);
37054 this.currentSize.width == cs.width &&
37055 this.currentSize.x == cs.x &&
37056 this.currentSize.height == cs.height &&
37057 this.currentSize.y == cs.y
37059 Roo.log("no change in with or X or Y");
37063 this.currentSize = cs;
37069 layout : function()
37071 this._resetLayout();
37073 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
37075 this.layoutItems( isInstant );
37077 this._isLayoutInited = true;
37079 this.fireEvent('layout', this);
37083 _resetLayout : function()
37085 if(this.isHorizontal){
37086 this.horizontalMeasureColumns();
37090 this.verticalMeasureColumns();
37094 verticalMeasureColumns : function()
37096 this.getContainerWidth();
37098 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
37099 // this.colWidth = Math.floor(this.containerWidth * 0.8);
37103 var boxWidth = this.boxWidth + this.padWidth;
37105 if(this.containerWidth < this.boxWidth){
37106 boxWidth = this.containerWidth
37109 var containerWidth = this.containerWidth;
37111 var cols = Math.floor(containerWidth / boxWidth);
37113 this.cols = Math.max( cols, 1 );
37115 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
37117 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
37119 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
37121 this.colWidth = boxWidth + avail - this.padWidth;
37123 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
37124 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
37127 horizontalMeasureColumns : function()
37129 this.getContainerWidth();
37131 var boxWidth = this.boxWidth;
37133 if(this.containerWidth < boxWidth){
37134 boxWidth = this.containerWidth;
37137 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
37139 this.el.setHeight(boxWidth);
37143 getContainerWidth : function()
37145 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
37148 layoutItems : function( isInstant )
37150 Roo.log(this.bricks);
37152 var items = Roo.apply([], this.bricks);
37154 if(this.isHorizontal){
37155 this._horizontalLayoutItems( items , isInstant );
37159 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
37160 // this._verticalAlternativeLayoutItems( items , isInstant );
37164 this._verticalLayoutItems( items , isInstant );
37168 _verticalLayoutItems : function ( items , isInstant)
37170 if ( !items || !items.length ) {
37175 ['xs', 'xs', 'xs', 'tall'],
37176 ['xs', 'xs', 'tall'],
37177 ['xs', 'xs', 'sm'],
37178 ['xs', 'xs', 'xs'],
37184 ['sm', 'xs', 'xs'],
37188 ['tall', 'xs', 'xs', 'xs'],
37189 ['tall', 'xs', 'xs'],
37201 Roo.each(items, function(item, k){
37203 switch (item.size) {
37204 // these layouts take up a full box,
37215 boxes.push([item]);
37238 var filterPattern = function(box, length)
37246 var pattern = box.slice(0, length);
37250 Roo.each(pattern, function(i){
37251 format.push(i.size);
37254 Roo.each(standard, function(s){
37256 if(String(s) != String(format)){
37265 if(!match && length == 1){
37270 filterPattern(box, length - 1);
37274 queue.push(pattern);
37276 box = box.slice(length, box.length);
37278 filterPattern(box, 4);
37284 Roo.each(boxes, function(box, k){
37290 if(box.length == 1){
37295 filterPattern(box, 4);
37299 this._processVerticalLayoutQueue( queue, isInstant );
37303 // _verticalAlternativeLayoutItems : function( items , isInstant )
37305 // if ( !items || !items.length ) {
37309 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
37313 _horizontalLayoutItems : function ( items , isInstant)
37315 if ( !items || !items.length || items.length < 3) {
37321 var eItems = items.slice(0, 3);
37323 items = items.slice(3, items.length);
37326 ['xs', 'xs', 'xs', 'wide'],
37327 ['xs', 'xs', 'wide'],
37328 ['xs', 'xs', 'sm'],
37329 ['xs', 'xs', 'xs'],
37335 ['sm', 'xs', 'xs'],
37339 ['wide', 'xs', 'xs', 'xs'],
37340 ['wide', 'xs', 'xs'],
37353 Roo.each(items, function(item, k){
37355 switch (item.size) {
37366 boxes.push([item]);
37390 var filterPattern = function(box, length)
37398 var pattern = box.slice(0, length);
37402 Roo.each(pattern, function(i){
37403 format.push(i.size);
37406 Roo.each(standard, function(s){
37408 if(String(s) != String(format)){
37417 if(!match && length == 1){
37422 filterPattern(box, length - 1);
37426 queue.push(pattern);
37428 box = box.slice(length, box.length);
37430 filterPattern(box, 4);
37436 Roo.each(boxes, function(box, k){
37442 if(box.length == 1){
37447 filterPattern(box, 4);
37454 var pos = this.el.getBox(true);
37458 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37460 var hit_end = false;
37462 Roo.each(queue, function(box){
37466 Roo.each(box, function(b){
37468 b.el.setVisibilityMode(Roo.Element.DISPLAY);
37478 Roo.each(box, function(b){
37480 b.el.setVisibilityMode(Roo.Element.DISPLAY);
37483 mx = Math.max(mx, b.x);
37487 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
37491 Roo.each(box, function(b){
37493 b.el.setVisibilityMode(Roo.Element.DISPLAY);
37507 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
37510 /** Sets position of item in DOM
37511 * @param {Element} item
37512 * @param {Number} x - horizontal position
37513 * @param {Number} y - vertical position
37514 * @param {Boolean} isInstant - disables transitions
37516 _processVerticalLayoutQueue : function( queue, isInstant )
37518 var pos = this.el.getBox(true);
37523 for (var i = 0; i < this.cols; i++){
37527 Roo.each(queue, function(box, k){
37529 var col = k % this.cols;
37531 Roo.each(box, function(b,kk){
37533 b.el.position('absolute');
37535 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37536 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37538 if(b.size == 'md-left' || b.size == 'md-right'){
37539 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
37540 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
37543 b.el.setWidth(width);
37544 b.el.setHeight(height);
37546 b.el.select('iframe',true).setSize(width,height);
37550 for (var i = 0; i < this.cols; i++){
37552 if(maxY[i] < maxY[col]){
37557 col = Math.min(col, i);
37561 x = pos.x + col * (this.colWidth + this.padWidth);
37565 var positions = [];
37567 switch (box.length){
37569 positions = this.getVerticalOneBoxColPositions(x, y, box);
37572 positions = this.getVerticalTwoBoxColPositions(x, y, box);
37575 positions = this.getVerticalThreeBoxColPositions(x, y, box);
37578 positions = this.getVerticalFourBoxColPositions(x, y, box);
37584 Roo.each(box, function(b,kk){
37586 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
37588 var sz = b.el.getSize();
37590 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
37598 for (var i = 0; i < this.cols; i++){
37599 mY = Math.max(mY, maxY[i]);
37602 this.el.setHeight(mY - pos.y);
37606 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
37608 // var pos = this.el.getBox(true);
37611 // var maxX = pos.right;
37613 // var maxHeight = 0;
37615 // Roo.each(items, function(item, k){
37619 // item.el.position('absolute');
37621 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
37623 // item.el.setWidth(width);
37625 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
37627 // item.el.setHeight(height);
37630 // item.el.setXY([x, y], isInstant ? false : true);
37632 // item.el.setXY([maxX - width, y], isInstant ? false : true);
37635 // y = y + height + this.alternativePadWidth;
37637 // maxHeight = maxHeight + height + this.alternativePadWidth;
37641 // this.el.setHeight(maxHeight);
37645 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
37647 var pos = this.el.getBox(true);
37652 var maxX = pos.right;
37654 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
37656 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37658 Roo.each(queue, function(box, k){
37660 Roo.each(box, function(b, kk){
37662 b.el.position('absolute');
37664 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37665 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37667 if(b.size == 'md-left' || b.size == 'md-right'){
37668 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
37669 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
37672 b.el.setWidth(width);
37673 b.el.setHeight(height);
37681 var positions = [];
37683 switch (box.length){
37685 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
37688 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
37691 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
37694 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
37700 Roo.each(box, function(b,kk){
37702 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
37704 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
37712 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
37714 Roo.each(eItems, function(b,k){
37716 b.size = (k == 0) ? 'sm' : 'xs';
37717 b.x = (k == 0) ? 2 : 1;
37718 b.y = (k == 0) ? 2 : 1;
37720 b.el.position('absolute');
37722 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37724 b.el.setWidth(width);
37726 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37728 b.el.setHeight(height);
37732 var positions = [];
37735 x : maxX - this.unitWidth * 2 - this.gutter,
37740 x : maxX - this.unitWidth,
37741 y : minY + (this.unitWidth + this.gutter) * 2
37745 x : maxX - this.unitWidth * 3 - this.gutter * 2,
37749 Roo.each(eItems, function(b,k){
37751 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
37757 getVerticalOneBoxColPositions : function(x, y, box)
37761 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
37763 if(box[0].size == 'md-left'){
37767 if(box[0].size == 'md-right'){
37772 x : x + (this.unitWidth + this.gutter) * rand,
37779 getVerticalTwoBoxColPositions : function(x, y, box)
37783 if(box[0].size == 'xs'){
37787 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
37791 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
37805 x : x + (this.unitWidth + this.gutter) * 2,
37806 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
37813 getVerticalThreeBoxColPositions : function(x, y, box)
37817 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
37825 x : x + (this.unitWidth + this.gutter) * 1,
37830 x : x + (this.unitWidth + this.gutter) * 2,
37838 if(box[0].size == 'xs' && box[1].size == 'xs'){
37847 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
37851 x : x + (this.unitWidth + this.gutter) * 1,
37865 x : x + (this.unitWidth + this.gutter) * 2,
37870 x : x + (this.unitWidth + this.gutter) * 2,
37871 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
37878 getVerticalFourBoxColPositions : function(x, y, box)
37882 if(box[0].size == 'xs'){
37891 y : y + (this.unitHeight + this.gutter) * 1
37896 y : y + (this.unitHeight + this.gutter) * 2
37900 x : x + (this.unitWidth + this.gutter) * 1,
37914 x : x + (this.unitWidth + this.gutter) * 2,
37919 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
37920 y : y + (this.unitHeight + this.gutter) * 1
37924 x : x + (this.unitWidth + this.gutter) * 2,
37925 y : y + (this.unitWidth + this.gutter) * 2
37932 getHorizontalOneBoxColPositions : function(maxX, minY, box)
37936 if(box[0].size == 'md-left'){
37938 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
37945 if(box[0].size == 'md-right'){
37947 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
37948 y : minY + (this.unitWidth + this.gutter) * 1
37954 var rand = Math.floor(Math.random() * (4 - box[0].y));
37957 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37958 y : minY + (this.unitWidth + this.gutter) * rand
37965 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
37969 if(box[0].size == 'xs'){
37972 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37977 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37978 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
37986 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37991 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37992 y : minY + (this.unitWidth + this.gutter) * 2
37999 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
38003 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
38006 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38011 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38012 y : minY + (this.unitWidth + this.gutter) * 1
38016 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38017 y : minY + (this.unitWidth + this.gutter) * 2
38024 if(box[0].size == 'xs' && box[1].size == 'xs'){
38027 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38032 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38037 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38038 y : minY + (this.unitWidth + this.gutter) * 1
38046 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38051 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38052 y : minY + (this.unitWidth + this.gutter) * 2
38056 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38057 y : minY + (this.unitWidth + this.gutter) * 2
38064 getHorizontalFourBoxColPositions : function(maxX, minY, box)
38068 if(box[0].size == 'xs'){
38071 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38076 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38081 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),
38086 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
38087 y : minY + (this.unitWidth + this.gutter) * 1
38095 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38100 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38101 y : minY + (this.unitWidth + this.gutter) * 2
38105 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38106 y : minY + (this.unitWidth + this.gutter) * 2
38110 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),
38111 y : minY + (this.unitWidth + this.gutter) * 2
38119 * remove a Masonry Brick
38120 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
38122 removeBrick : function(brick_id)
38128 for (var i = 0; i<this.bricks.length; i++) {
38129 if (this.bricks[i].id == brick_id) {
38130 this.bricks.splice(i,1);
38131 this.el.dom.removeChild(Roo.get(brick_id).dom);
38138 * adds a Masonry Brick
38139 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
38141 addBrick : function(cfg)
38143 var cn = new Roo.bootstrap.MasonryBrick(cfg);
38144 //this.register(cn);
38145 cn.parentId = this.id;
38146 cn.render(this.el);
38151 * register a Masonry Brick
38152 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
38155 register : function(brick)
38157 this.bricks.push(brick);
38158 brick.masonryId = this.id;
38162 * clear all the Masonry Brick
38164 clearAll : function()
38167 //this.getChildContainer().dom.innerHTML = "";
38168 this.el.dom.innerHTML = '';
38171 getSelected : function()
38173 if (!this.selectedBrick) {
38177 return this.selectedBrick;
38181 Roo.apply(Roo.bootstrap.LayoutMasonry, {
38185 * register a Masonry Layout
38186 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
38189 register : function(layout)
38191 this.groups[layout.id] = layout;
38194 * fetch a Masonry Layout based on the masonry layout ID
38195 * @param {string} the masonry layout to add
38196 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
38199 get: function(layout_id) {
38200 if (typeof(this.groups[layout_id]) == 'undefined') {
38203 return this.groups[layout_id] ;
38215 * http://masonry.desandro.com
38217 * The idea is to render all the bricks based on vertical width...
38219 * The original code extends 'outlayer' - we might need to use that....
38225 * @class Roo.bootstrap.LayoutMasonryAuto
38226 * @extends Roo.bootstrap.Component
38227 * Bootstrap Layout Masonry class
38230 * Create a new Element
38231 * @param {Object} config The config object
38234 Roo.bootstrap.LayoutMasonryAuto = function(config){
38235 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
38238 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
38241 * @cfg {Boolean} isFitWidth - resize the width..
38243 isFitWidth : false, // options..
38245 * @cfg {Boolean} isOriginLeft = left align?
38247 isOriginLeft : true,
38249 * @cfg {Boolean} isOriginTop = top align?
38251 isOriginTop : false,
38253 * @cfg {Boolean} isLayoutInstant = no animation?
38255 isLayoutInstant : false, // needed?
38257 * @cfg {Boolean} isResizingContainer = not sure if this is used..
38259 isResizingContainer : true,
38261 * @cfg {Number} columnWidth width of the columns
38267 * @cfg {Number} maxCols maximum number of columns
38272 * @cfg {Number} padHeight padding below box..
38278 * @cfg {Boolean} isAutoInitial defalut true
38281 isAutoInitial : true,
38287 initialColumnWidth : 0,
38288 currentSize : null,
38290 colYs : null, // array.
38297 bricks: null, //CompositeElement
38298 cols : 0, // array?
38299 // element : null, // wrapped now this.el
38300 _isLayoutInited : null,
38303 getAutoCreate : function(){
38307 cls: 'blog-masonary-wrapper ' + this.cls,
38309 cls : 'mas-boxes masonary'
38316 getChildContainer: function( )
38318 if (this.boxesEl) {
38319 return this.boxesEl;
38322 this.boxesEl = this.el.select('.mas-boxes').first();
38324 return this.boxesEl;
38328 initEvents : function()
38332 if(this.isAutoInitial){
38333 Roo.log('hook children rendered');
38334 this.on('childrenrendered', function() {
38335 Roo.log('children rendered');
38342 initial : function()
38344 this.reloadItems();
38346 this.currentSize = this.el.getBox(true);
38348 /// was window resize... - let's see if this works..
38349 Roo.EventManager.onWindowResize(this.resize, this);
38351 if(!this.isAutoInitial){
38356 this.layout.defer(500,this);
38359 reloadItems: function()
38361 this.bricks = this.el.select('.masonry-brick', true);
38363 this.bricks.each(function(b) {
38364 //Roo.log(b.getSize());
38365 if (!b.attr('originalwidth')) {
38366 b.attr('originalwidth', b.getSize().width);
38371 Roo.log(this.bricks.elements.length);
38374 resize : function()
38377 var cs = this.el.getBox(true);
38379 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
38380 Roo.log("no change in with or X");
38383 this.currentSize = cs;
38387 layout : function()
38390 this._resetLayout();
38391 //this._manageStamps();
38393 // don't animate first layout
38394 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38395 this.layoutItems( isInstant );
38397 // flag for initalized
38398 this._isLayoutInited = true;
38401 layoutItems : function( isInstant )
38403 //var items = this._getItemsForLayout( this.items );
38404 // original code supports filtering layout items.. we just ignore it..
38406 this._layoutItems( this.bricks , isInstant );
38408 this._postLayout();
38410 _layoutItems : function ( items , isInstant)
38412 //this.fireEvent( 'layout', this, items );
38415 if ( !items || !items.elements.length ) {
38416 // no items, emit event with empty array
38421 items.each(function(item) {
38422 Roo.log("layout item");
38424 // get x/y object from method
38425 var position = this._getItemLayoutPosition( item );
38427 position.item = item;
38428 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
38429 queue.push( position );
38432 this._processLayoutQueue( queue );
38434 /** Sets position of item in DOM
38435 * @param {Element} item
38436 * @param {Number} x - horizontal position
38437 * @param {Number} y - vertical position
38438 * @param {Boolean} isInstant - disables transitions
38440 _processLayoutQueue : function( queue )
38442 for ( var i=0, len = queue.length; i < len; i++ ) {
38443 var obj = queue[i];
38444 obj.item.position('absolute');
38445 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
38451 * Any logic you want to do after each layout,
38452 * i.e. size the container
38454 _postLayout : function()
38456 this.resizeContainer();
38459 resizeContainer : function()
38461 if ( !this.isResizingContainer ) {
38464 var size = this._getContainerSize();
38466 this.el.setSize(size.width,size.height);
38467 this.boxesEl.setSize(size.width,size.height);
38473 _resetLayout : function()
38475 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
38476 this.colWidth = this.el.getWidth();
38477 //this.gutter = this.el.getWidth();
38479 this.measureColumns();
38485 this.colYs.push( 0 );
38491 measureColumns : function()
38493 this.getContainerWidth();
38494 // if columnWidth is 0, default to outerWidth of first item
38495 if ( !this.columnWidth ) {
38496 var firstItem = this.bricks.first();
38497 Roo.log(firstItem);
38498 this.columnWidth = this.containerWidth;
38499 if (firstItem && firstItem.attr('originalwidth') ) {
38500 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
38502 // columnWidth fall back to item of first element
38503 Roo.log("set column width?");
38504 this.initialColumnWidth = this.columnWidth ;
38506 // if first elem has no width, default to size of container
38511 if (this.initialColumnWidth) {
38512 this.columnWidth = this.initialColumnWidth;
38517 // column width is fixed at the top - however if container width get's smaller we should
38520 // this bit calcs how man columns..
38522 var columnWidth = this.columnWidth += this.gutter;
38524 // calculate columns
38525 var containerWidth = this.containerWidth + this.gutter;
38527 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
38528 // fix rounding errors, typically with gutters
38529 var excess = columnWidth - containerWidth % columnWidth;
38532 // if overshoot is less than a pixel, round up, otherwise floor it
38533 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
38534 cols = Math[ mathMethod ]( cols );
38535 this.cols = Math.max( cols, 1 );
38536 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38538 // padding positioning..
38539 var totalColWidth = this.cols * this.columnWidth;
38540 var padavail = this.containerWidth - totalColWidth;
38541 // so for 2 columns - we need 3 'pads'
38543 var padNeeded = (1+this.cols) * this.padWidth;
38545 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
38547 this.columnWidth += padExtra
38548 //this.padWidth = Math.floor(padavail / ( this.cols));
38550 // adjust colum width so that padding is fixed??
38552 // we have 3 columns ... total = width * 3
38553 // we have X left over... that should be used by
38555 //if (this.expandC) {
38563 getContainerWidth : function()
38565 /* // container is parent if fit width
38566 var container = this.isFitWidth ? this.element.parentNode : this.element;
38567 // check that this.size and size are there
38568 // IE8 triggers resize on body size change, so they might not be
38570 var size = getSize( container ); //FIXME
38571 this.containerWidth = size && size.innerWidth; //FIXME
38574 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
38578 _getItemLayoutPosition : function( item ) // what is item?
38580 // we resize the item to our columnWidth..
38582 item.setWidth(this.columnWidth);
38583 item.autoBoxAdjust = false;
38585 var sz = item.getSize();
38587 // how many columns does this brick span
38588 var remainder = this.containerWidth % this.columnWidth;
38590 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
38591 // round if off by 1 pixel, otherwise use ceil
38592 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
38593 colSpan = Math.min( colSpan, this.cols );
38595 // normally this should be '1' as we dont' currently allow multi width columns..
38597 var colGroup = this._getColGroup( colSpan );
38598 // get the minimum Y value from the columns
38599 var minimumY = Math.min.apply( Math, colGroup );
38600 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
38602 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
38604 // position the brick
38606 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
38607 y: this.currentSize.y + minimumY + this.padHeight
38611 // apply setHeight to necessary columns
38612 var setHeight = minimumY + sz.height + this.padHeight;
38613 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
38615 var setSpan = this.cols + 1 - colGroup.length;
38616 for ( var i = 0; i < setSpan; i++ ) {
38617 this.colYs[ shortColIndex + i ] = setHeight ;
38624 * @param {Number} colSpan - number of columns the element spans
38625 * @returns {Array} colGroup
38627 _getColGroup : function( colSpan )
38629 if ( colSpan < 2 ) {
38630 // if brick spans only one column, use all the column Ys
38635 // how many different places could this brick fit horizontally
38636 var groupCount = this.cols + 1 - colSpan;
38637 // for each group potential horizontal position
38638 for ( var i = 0; i < groupCount; i++ ) {
38639 // make an array of colY values for that one group
38640 var groupColYs = this.colYs.slice( i, i + colSpan );
38641 // and get the max value of the array
38642 colGroup[i] = Math.max.apply( Math, groupColYs );
38647 _manageStamp : function( stamp )
38649 var stampSize = stamp.getSize();
38650 var offset = stamp.getBox();
38651 // get the columns that this stamp affects
38652 var firstX = this.isOriginLeft ? offset.x : offset.right;
38653 var lastX = firstX + stampSize.width;
38654 var firstCol = Math.floor( firstX / this.columnWidth );
38655 firstCol = Math.max( 0, firstCol );
38657 var lastCol = Math.floor( lastX / this.columnWidth );
38658 // lastCol should not go over if multiple of columnWidth #425
38659 lastCol -= lastX % this.columnWidth ? 0 : 1;
38660 lastCol = Math.min( this.cols - 1, lastCol );
38662 // set colYs to bottom of the stamp
38663 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
38666 for ( var i = firstCol; i <= lastCol; i++ ) {
38667 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
38672 _getContainerSize : function()
38674 this.maxY = Math.max.apply( Math, this.colYs );
38679 if ( this.isFitWidth ) {
38680 size.width = this._getContainerFitWidth();
38686 _getContainerFitWidth : function()
38688 var unusedCols = 0;
38689 // count unused columns
38692 if ( this.colYs[i] !== 0 ) {
38697 // fit container to columns that have been used
38698 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
38701 needsResizeLayout : function()
38703 var previousWidth = this.containerWidth;
38704 this.getContainerWidth();
38705 return previousWidth !== this.containerWidth;
38720 * @class Roo.bootstrap.MasonryBrick
38721 * @extends Roo.bootstrap.Component
38722 * Bootstrap MasonryBrick class
38725 * Create a new MasonryBrick
38726 * @param {Object} config The config object
38729 Roo.bootstrap.MasonryBrick = function(config){
38731 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
38733 Roo.bootstrap.MasonryBrick.register(this);
38739 * When a MasonryBrick is clcik
38740 * @param {Roo.bootstrap.MasonryBrick} this
38741 * @param {Roo.EventObject} e
38747 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
38750 * @cfg {String} title
38754 * @cfg {String} html
38758 * @cfg {String} bgimage
38762 * @cfg {String} videourl
38766 * @cfg {String} cls
38770 * @cfg {String} href
38774 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
38779 * @cfg {String} placetitle (center|bottom)
38784 * @cfg {Boolean} isFitContainer defalut true
38786 isFitContainer : true,
38789 * @cfg {Boolean} preventDefault defalut false
38791 preventDefault : false,
38794 * @cfg {Boolean} inverse defalut false
38796 maskInverse : false,
38798 getAutoCreate : function()
38800 if(!this.isFitContainer){
38801 return this.getSplitAutoCreate();
38804 var cls = 'masonry-brick masonry-brick-full';
38806 if(this.href.length){
38807 cls += ' masonry-brick-link';
38810 if(this.bgimage.length){
38811 cls += ' masonry-brick-image';
38814 if(this.maskInverse){
38815 cls += ' mask-inverse';
38818 if(!this.html.length && !this.maskInverse && !this.videourl.length){
38819 cls += ' enable-mask';
38823 cls += ' masonry-' + this.size + '-brick';
38826 if(this.placetitle.length){
38828 switch (this.placetitle) {
38830 cls += ' masonry-center-title';
38833 cls += ' masonry-bottom-title';
38840 if(!this.html.length && !this.bgimage.length){
38841 cls += ' masonry-center-title';
38844 if(!this.html.length && this.bgimage.length){
38845 cls += ' masonry-bottom-title';
38850 cls += ' ' + this.cls;
38854 tag: (this.href.length) ? 'a' : 'div',
38859 cls: 'masonry-brick-mask'
38863 cls: 'masonry-brick-paragraph',
38869 if(this.href.length){
38870 cfg.href = this.href;
38873 var cn = cfg.cn[1].cn;
38875 if(this.title.length){
38878 cls: 'masonry-brick-title',
38883 if(this.html.length){
38886 cls: 'masonry-brick-text',
38891 if (!this.title.length && !this.html.length) {
38892 cfg.cn[1].cls += ' hide';
38895 if(this.bgimage.length){
38898 cls: 'masonry-brick-image-view',
38903 if(this.videourl.length){
38904 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
38905 // youtube support only?
38908 cls: 'masonry-brick-image-view',
38911 allowfullscreen : true
38919 getSplitAutoCreate : function()
38921 var cls = 'masonry-brick masonry-brick-split';
38923 if(this.href.length){
38924 cls += ' masonry-brick-link';
38927 if(this.bgimage.length){
38928 cls += ' masonry-brick-image';
38932 cls += ' masonry-' + this.size + '-brick';
38935 switch (this.placetitle) {
38937 cls += ' masonry-center-title';
38940 cls += ' masonry-bottom-title';
38943 if(!this.bgimage.length){
38944 cls += ' masonry-center-title';
38947 if(this.bgimage.length){
38948 cls += ' masonry-bottom-title';
38954 cls += ' ' + this.cls;
38958 tag: (this.href.length) ? 'a' : 'div',
38963 cls: 'masonry-brick-split-head',
38967 cls: 'masonry-brick-paragraph',
38974 cls: 'masonry-brick-split-body',
38980 if(this.href.length){
38981 cfg.href = this.href;
38984 if(this.title.length){
38985 cfg.cn[0].cn[0].cn.push({
38987 cls: 'masonry-brick-title',
38992 if(this.html.length){
38993 cfg.cn[1].cn.push({
38995 cls: 'masonry-brick-text',
39000 if(this.bgimage.length){
39001 cfg.cn[0].cn.push({
39003 cls: 'masonry-brick-image-view',
39008 if(this.videourl.length){
39009 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
39010 // youtube support only?
39011 cfg.cn[0].cn.cn.push({
39013 cls: 'masonry-brick-image-view',
39016 allowfullscreen : true
39023 initEvents: function()
39025 switch (this.size) {
39058 this.el.on('touchstart', this.onTouchStart, this);
39059 this.el.on('touchmove', this.onTouchMove, this);
39060 this.el.on('touchend', this.onTouchEnd, this);
39061 this.el.on('contextmenu', this.onContextMenu, this);
39063 this.el.on('mouseenter' ,this.enter, this);
39064 this.el.on('mouseleave', this.leave, this);
39065 this.el.on('click', this.onClick, this);
39068 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
39069 this.parent().bricks.push(this);
39074 onClick: function(e, el)
39076 var time = this.endTimer - this.startTimer;
39077 // Roo.log(e.preventDefault());
39080 e.preventDefault();
39085 if(!this.preventDefault){
39089 e.preventDefault();
39091 if (this.activeClass != '') {
39092 this.selectBrick();
39095 this.fireEvent('click', this, e);
39098 enter: function(e, el)
39100 e.preventDefault();
39102 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
39106 if(this.bgimage.length && this.html.length){
39107 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
39111 leave: function(e, el)
39113 e.preventDefault();
39115 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
39119 if(this.bgimage.length && this.html.length){
39120 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
39124 onTouchStart: function(e, el)
39126 // e.preventDefault();
39128 this.touchmoved = false;
39130 if(!this.isFitContainer){
39134 if(!this.bgimage.length || !this.html.length){
39138 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
39140 this.timer = new Date().getTime();
39144 onTouchMove: function(e, el)
39146 this.touchmoved = true;
39149 onContextMenu : function(e,el)
39151 e.preventDefault();
39152 e.stopPropagation();
39156 onTouchEnd: function(e, el)
39158 // e.preventDefault();
39160 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
39167 if(!this.bgimage.length || !this.html.length){
39169 if(this.href.length){
39170 window.location.href = this.href;
39176 if(!this.isFitContainer){
39180 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
39182 window.location.href = this.href;
39185 //selection on single brick only
39186 selectBrick : function() {
39188 if (!this.parentId) {
39192 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
39193 var index = m.selectedBrick.indexOf(this.id);
39196 m.selectedBrick.splice(index,1);
39197 this.el.removeClass(this.activeClass);
39201 for(var i = 0; i < m.selectedBrick.length; i++) {
39202 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
39203 b.el.removeClass(b.activeClass);
39206 m.selectedBrick = [];
39208 m.selectedBrick.push(this.id);
39209 this.el.addClass(this.activeClass);
39213 isSelected : function(){
39214 return this.el.hasClass(this.activeClass);
39219 Roo.apply(Roo.bootstrap.MasonryBrick, {
39222 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
39224 * register a Masonry Brick
39225 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39228 register : function(brick)
39230 //this.groups[brick.id] = brick;
39231 this.groups.add(brick.id, brick);
39234 * fetch a masonry brick based on the masonry brick ID
39235 * @param {string} the masonry brick to add
39236 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
39239 get: function(brick_id)
39241 // if (typeof(this.groups[brick_id]) == 'undefined') {
39244 // return this.groups[brick_id] ;
39246 if(this.groups.key(brick_id)) {
39247 return this.groups.key(brick_id);
39265 * @class Roo.bootstrap.Brick
39266 * @extends Roo.bootstrap.Component
39267 * Bootstrap Brick class
39270 * Create a new Brick
39271 * @param {Object} config The config object
39274 Roo.bootstrap.Brick = function(config){
39275 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
39281 * When a Brick is click
39282 * @param {Roo.bootstrap.Brick} this
39283 * @param {Roo.EventObject} e
39289 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
39292 * @cfg {String} title
39296 * @cfg {String} html
39300 * @cfg {String} bgimage
39304 * @cfg {String} cls
39308 * @cfg {String} href
39312 * @cfg {String} video
39316 * @cfg {Boolean} square
39320 getAutoCreate : function()
39322 var cls = 'roo-brick';
39324 if(this.href.length){
39325 cls += ' roo-brick-link';
39328 if(this.bgimage.length){
39329 cls += ' roo-brick-image';
39332 if(!this.html.length && !this.bgimage.length){
39333 cls += ' roo-brick-center-title';
39336 if(!this.html.length && this.bgimage.length){
39337 cls += ' roo-brick-bottom-title';
39341 cls += ' ' + this.cls;
39345 tag: (this.href.length) ? 'a' : 'div',
39350 cls: 'roo-brick-paragraph',
39356 if(this.href.length){
39357 cfg.href = this.href;
39360 var cn = cfg.cn[0].cn;
39362 if(this.title.length){
39365 cls: 'roo-brick-title',
39370 if(this.html.length){
39373 cls: 'roo-brick-text',
39380 if(this.bgimage.length){
39383 cls: 'roo-brick-image-view',
39391 initEvents: function()
39393 if(this.title.length || this.html.length){
39394 this.el.on('mouseenter' ,this.enter, this);
39395 this.el.on('mouseleave', this.leave, this);
39398 Roo.EventManager.onWindowResize(this.resize, this);
39400 if(this.bgimage.length){
39401 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
39402 this.imageEl.on('load', this.onImageLoad, this);
39409 onImageLoad : function()
39414 resize : function()
39416 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
39418 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
39420 if(this.bgimage.length){
39421 var image = this.el.select('.roo-brick-image-view', true).first();
39423 image.setWidth(paragraph.getWidth());
39426 image.setHeight(paragraph.getWidth());
39429 this.el.setHeight(image.getHeight());
39430 paragraph.setHeight(image.getHeight());
39436 enter: function(e, el)
39438 e.preventDefault();
39440 if(this.bgimage.length){
39441 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
39442 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
39446 leave: function(e, el)
39448 e.preventDefault();
39450 if(this.bgimage.length){
39451 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
39452 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
39467 * @class Roo.bootstrap.form.NumberField
39468 * @extends Roo.bootstrap.form.Input
39469 * Bootstrap NumberField class
39475 * Create a new NumberField
39476 * @param {Object} config The config object
39479 Roo.bootstrap.form.NumberField = function(config){
39480 Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
39483 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
39486 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39488 allowDecimals : true,
39490 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39492 decimalSeparator : ".",
39494 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39496 decimalPrecision : 2,
39498 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39500 allowNegative : true,
39503 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
39507 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39509 minValue : Number.NEGATIVE_INFINITY,
39511 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39513 maxValue : Number.MAX_VALUE,
39515 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39517 minText : "The minimum value for this field is {0}",
39519 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39521 maxText : "The maximum value for this field is {0}",
39523 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
39524 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39526 nanText : "{0} is not a valid number",
39528 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
39530 thousandsDelimiter : false,
39532 * @cfg {String} valueAlign alignment of value
39534 valueAlign : "left",
39536 getAutoCreate : function()
39538 var hiddenInput = {
39542 cls: 'hidden-number-input'
39546 hiddenInput.name = this.name;
39551 var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
39553 this.name = hiddenInput.name;
39555 if(cfg.cn.length > 0) {
39556 cfg.cn.push(hiddenInput);
39563 initEvents : function()
39565 Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
39567 var allowed = "0123456789";
39569 if(this.allowDecimals){
39570 allowed += this.decimalSeparator;
39573 if(this.allowNegative){
39577 if(this.thousandsDelimiter) {
39581 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
39583 var keyPress = function(e){
39585 var k = e.getKey();
39587 var c = e.getCharCode();
39590 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39591 allowed.indexOf(String.fromCharCode(c)) === -1
39597 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39601 if(allowed.indexOf(String.fromCharCode(c)) === -1){
39606 this.el.on("keypress", keyPress, this);
39609 validateValue : function(value)
39612 if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
39616 var num = this.parseValue(value);
39619 this.markInvalid(String.format(this.nanText, value));
39623 if(num < this.minValue){
39624 this.markInvalid(String.format(this.minText, this.minValue));
39628 if(num > this.maxValue){
39629 this.markInvalid(String.format(this.maxText, this.maxValue));
39636 getValue : function()
39638 var v = this.hiddenEl().getValue();
39640 return this.fixPrecision(this.parseValue(v));
39643 parseValue : function(value)
39645 if(this.thousandsDelimiter) {
39647 r = new RegExp(",", "g");
39648 value = value.replace(r, "");
39651 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
39652 return isNaN(value) ? '' : value;
39655 fixPrecision : function(value)
39657 if(this.thousandsDelimiter) {
39659 r = new RegExp(",", "g");
39660 value = value.replace(r, "");
39663 var nan = isNaN(value);
39665 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
39666 return nan ? '' : value;
39668 return parseFloat(value).toFixed(this.decimalPrecision);
39671 setValue : function(v)
39673 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
39679 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39681 this.inputEl().dom.value = (v == '') ? '' :
39682 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
39684 if(!this.allowZero && v === '0') {
39685 this.hiddenEl().dom.value = '';
39686 this.inputEl().dom.value = '';
39693 decimalPrecisionFcn : function(v)
39695 return Math.floor(v);
39698 beforeBlur : function()
39700 var v = this.parseValue(this.getRawValue());
39702 if(v || v === 0 || v === ''){
39707 hiddenEl : function()
39709 return this.el.select('input.hidden-number-input',true).first();
39721 * @class Roo.bootstrap.DocumentSlider
39722 * @extends Roo.bootstrap.Component
39723 * Bootstrap DocumentSlider class
39726 * Create a new DocumentViewer
39727 * @param {Object} config The config object
39730 Roo.bootstrap.DocumentSlider = function(config){
39731 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
39738 * Fire after initEvent
39739 * @param {Roo.bootstrap.DocumentSlider} this
39744 * Fire after update
39745 * @param {Roo.bootstrap.DocumentSlider} this
39751 * @param {Roo.bootstrap.DocumentSlider} this
39757 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
39763 getAutoCreate : function()
39767 cls : 'roo-document-slider',
39771 cls : 'roo-document-slider-header',
39775 cls : 'roo-document-slider-header-title'
39781 cls : 'roo-document-slider-body',
39785 cls : 'roo-document-slider-prev',
39789 cls : 'fa fa-chevron-left'
39795 cls : 'roo-document-slider-thumb',
39799 cls : 'roo-document-slider-image'
39805 cls : 'roo-document-slider-next',
39809 cls : 'fa fa-chevron-right'
39821 initEvents : function()
39823 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
39824 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
39826 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
39827 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
39829 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
39830 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
39832 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
39833 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
39835 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
39836 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
39838 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
39839 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
39841 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
39842 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
39844 this.thumbEl.on('click', this.onClick, this);
39846 this.prevIndicator.on('click', this.prev, this);
39848 this.nextIndicator.on('click', this.next, this);
39852 initial : function()
39854 if(this.files.length){
39855 this.indicator = 1;
39859 this.fireEvent('initial', this);
39862 update : function()
39864 this.imageEl.attr('src', this.files[this.indicator - 1]);
39866 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
39868 this.prevIndicator.show();
39870 if(this.indicator == 1){
39871 this.prevIndicator.hide();
39874 this.nextIndicator.show();
39876 if(this.indicator == this.files.length){
39877 this.nextIndicator.hide();
39880 this.thumbEl.scrollTo('top');
39882 this.fireEvent('update', this);
39885 onClick : function(e)
39887 e.preventDefault();
39889 this.fireEvent('click', this);
39894 e.preventDefault();
39896 this.indicator = Math.max(1, this.indicator - 1);
39903 e.preventDefault();
39905 this.indicator = Math.min(this.files.length, this.indicator + 1);
39919 * @class Roo.bootstrap.form.RadioSet
39920 * @extends Roo.bootstrap.form.Input
39921 * @children Roo.bootstrap.form.Radio
39922 * Bootstrap RadioSet class
39923 * @cfg {String} indicatorpos (left|right) default left
39924 * @cfg {Boolean} inline (true|false) inline the element (default true)
39925 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
39927 * Create a new RadioSet
39928 * @param {Object} config The config object
39931 Roo.bootstrap.form.RadioSet = function(config){
39933 Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
39937 Roo.bootstrap.form.RadioSet.register(this);
39942 * Fires when the element is checked or unchecked.
39943 * @param {Roo.bootstrap.form.RadioSet} this This radio
39944 * @param {Roo.bootstrap.form.Radio} item The checked item
39949 * Fires when the element is click.
39950 * @param {Roo.bootstrap.form.RadioSet} this This radio set
39951 * @param {Roo.bootstrap.form.Radio} item The checked item
39952 * @param {Roo.EventObject} e The event object
39959 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input, {
39967 indicatorpos : 'left',
39969 getAutoCreate : function()
39973 cls : 'roo-radio-set-label',
39977 html : this.fieldLabel
39981 if (Roo.bootstrap.version == 3) {
39984 if(this.indicatorpos == 'left'){
39987 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
39988 tooltip : 'This field is required'
39993 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
39994 tooltip : 'This field is required'
40000 cls : 'roo-radio-set-items'
40003 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
40005 if (align === 'left' && this.fieldLabel.length) {
40008 cls : "roo-radio-set-right",
40014 if(this.labelWidth > 12){
40015 label.style = "width: " + this.labelWidth + 'px';
40018 if(this.labelWidth < 13 && this.labelmd == 0){
40019 this.labelmd = this.labelWidth;
40022 if(this.labellg > 0){
40023 label.cls += ' col-lg-' + this.labellg;
40024 items.cls += ' col-lg-' + (12 - this.labellg);
40027 if(this.labelmd > 0){
40028 label.cls += ' col-md-' + this.labelmd;
40029 items.cls += ' col-md-' + (12 - this.labelmd);
40032 if(this.labelsm > 0){
40033 label.cls += ' col-sm-' + this.labelsm;
40034 items.cls += ' col-sm-' + (12 - this.labelsm);
40037 if(this.labelxs > 0){
40038 label.cls += ' col-xs-' + this.labelxs;
40039 items.cls += ' col-xs-' + (12 - this.labelxs);
40045 cls : 'roo-radio-set',
40049 cls : 'roo-radio-set-input',
40052 value : this.value ? this.value : ''
40059 if(this.weight.length){
40060 cfg.cls += ' roo-radio-' + this.weight;
40064 cfg.cls += ' roo-radio-set-inline';
40068 ['xs','sm','md','lg'].map(function(size){
40069 if (settings[size]) {
40070 cfg.cls += ' col-' + size + '-' + settings[size];
40078 initEvents : function()
40080 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
40081 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
40083 if(!this.fieldLabel.length){
40084 this.labelEl.hide();
40087 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
40088 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
40090 this.indicator = this.indicatorEl();
40092 if(this.indicator){
40093 this.indicator.addClass('invisible');
40096 this.originalValue = this.getValue();
40100 inputEl: function ()
40102 return this.el.select('.roo-radio-set-input', true).first();
40105 getChildContainer : function()
40107 return this.itemsEl;
40110 register : function(item)
40112 this.radioes.push(item);
40116 validate : function()
40118 if(this.getVisibilityEl().hasClass('hidden')){
40124 Roo.each(this.radioes, function(i){
40133 if(this.allowBlank) {
40137 if(this.disabled || valid){
40142 this.markInvalid();
40147 markValid : function()
40149 if(this.labelEl.isVisible(true) && this.indicatorEl()){
40150 this.indicatorEl().removeClass('visible');
40151 this.indicatorEl().addClass('invisible');
40155 if (Roo.bootstrap.version == 3) {
40156 this.el.removeClass([this.invalidClass, this.validClass]);
40157 this.el.addClass(this.validClass);
40159 this.el.removeClass(['is-invalid','is-valid']);
40160 this.el.addClass(['is-valid']);
40162 this.fireEvent('valid', this);
40165 markInvalid : function(msg)
40167 if(this.allowBlank || this.disabled){
40171 if(this.labelEl.isVisible(true) && this.indicatorEl()){
40172 this.indicatorEl().removeClass('invisible');
40173 this.indicatorEl().addClass('visible');
40175 if (Roo.bootstrap.version == 3) {
40176 this.el.removeClass([this.invalidClass, this.validClass]);
40177 this.el.addClass(this.invalidClass);
40179 this.el.removeClass(['is-invalid','is-valid']);
40180 this.el.addClass(['is-invalid']);
40183 this.fireEvent('invalid', this, msg);
40187 setValue : function(v, suppressEvent)
40189 if(this.value === v){
40196 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40199 Roo.each(this.radioes, function(i){
40201 i.el.removeClass('checked');
40204 Roo.each(this.radioes, function(i){
40206 if(i.value === v || i.value.toString() === v.toString()){
40208 i.el.addClass('checked');
40210 if(suppressEvent !== true){
40211 this.fireEvent('check', this, i);
40222 clearInvalid : function(){
40224 if(!this.el || this.preventMark){
40228 this.el.removeClass([this.invalidClass]);
40230 this.fireEvent('valid', this);
40235 Roo.apply(Roo.bootstrap.form.RadioSet, {
40239 register : function(set)
40241 this.groups[set.name] = set;
40244 get: function(name)
40246 if (typeof(this.groups[name]) == 'undefined') {
40250 return this.groups[name] ;
40256 * Ext JS Library 1.1.1
40257 * Copyright(c) 2006-2007, Ext JS, LLC.
40259 * Originally Released Under LGPL - original licence link has changed is not relivant.
40262 * <script type="text/javascript">
40267 * @class Roo.bootstrap.SplitBar
40268 * @extends Roo.util.Observable
40269 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
40273 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
40274 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
40275 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
40276 split.minSize = 100;
40277 split.maxSize = 600;
40278 split.animate = true;
40279 split.on('moved', splitterMoved);
40282 * Create a new SplitBar
40283 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
40284 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
40285 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40286 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
40287 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
40288 position of the SplitBar).
40290 Roo.bootstrap.SplitBar = function(cfg){
40295 // dragElement : elm
40296 // resizingElement: el,
40298 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
40299 // placement : Roo.bootstrap.SplitBar.LEFT ,
40300 // existingProxy ???
40303 this.el = Roo.get(cfg.dragElement, true);
40304 this.el.dom.unselectable = "on";
40306 this.resizingEl = Roo.get(cfg.resizingElement, true);
40310 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40311 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
40314 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
40317 * The minimum size of the resizing element. (Defaults to 0)
40323 * The maximum size of the resizing element. (Defaults to 2000)
40326 this.maxSize = 2000;
40329 * Whether to animate the transition to the new size
40332 this.animate = false;
40335 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
40338 this.useShim = false;
40343 if(!cfg.existingProxy){
40345 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
40347 this.proxy = Roo.get(cfg.existingProxy).dom;
40350 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
40353 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
40356 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
40359 this.dragSpecs = {};
40362 * @private The adapter to use to positon and resize elements
40364 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40365 this.adapter.init(this);
40367 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40369 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
40370 this.el.addClass("roo-splitbar-h");
40373 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
40374 this.el.addClass("roo-splitbar-v");
40380 * Fires when the splitter is moved (alias for {@link #event-moved})
40381 * @param {Roo.bootstrap.SplitBar} this
40382 * @param {Number} newSize the new width or height
40387 * Fires when the splitter is moved
40388 * @param {Roo.bootstrap.SplitBar} this
40389 * @param {Number} newSize the new width or height
40393 * @event beforeresize
40394 * Fires before the splitter is dragged
40395 * @param {Roo.bootstrap.SplitBar} this
40397 "beforeresize" : true,
40399 "beforeapply" : true
40402 Roo.util.Observable.call(this);
40405 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
40406 onStartProxyDrag : function(x, y){
40407 this.fireEvent("beforeresize", this);
40409 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
40411 o.enableDisplayMode("block");
40412 // all splitbars share the same overlay
40413 Roo.bootstrap.SplitBar.prototype.overlay = o;
40415 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
40416 this.overlay.show();
40417 Roo.get(this.proxy).setDisplayed("block");
40418 var size = this.adapter.getElementSize(this);
40419 this.activeMinSize = this.getMinimumSize();;
40420 this.activeMaxSize = this.getMaximumSize();;
40421 var c1 = size - this.activeMinSize;
40422 var c2 = Math.max(this.activeMaxSize - size, 0);
40423 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40424 this.dd.resetConstraints();
40425 this.dd.setXConstraint(
40426 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
40427 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
40429 this.dd.setYConstraint(0, 0);
40431 this.dd.resetConstraints();
40432 this.dd.setXConstraint(0, 0);
40433 this.dd.setYConstraint(
40434 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
40435 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
40438 this.dragSpecs.startSize = size;
40439 this.dragSpecs.startPoint = [x, y];
40440 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
40444 * @private Called after the drag operation by the DDProxy
40446 onEndProxyDrag : function(e){
40447 Roo.get(this.proxy).setDisplayed(false);
40448 var endPoint = Roo.lib.Event.getXY(e);
40450 this.overlay.hide();
40453 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40454 newSize = this.dragSpecs.startSize +
40455 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
40456 endPoint[0] - this.dragSpecs.startPoint[0] :
40457 this.dragSpecs.startPoint[0] - endPoint[0]
40460 newSize = this.dragSpecs.startSize +
40461 (this.placement == Roo.bootstrap.SplitBar.TOP ?
40462 endPoint[1] - this.dragSpecs.startPoint[1] :
40463 this.dragSpecs.startPoint[1] - endPoint[1]
40466 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
40467 if(newSize != this.dragSpecs.startSize){
40468 if(this.fireEvent('beforeapply', this, newSize) !== false){
40469 this.adapter.setElementSize(this, newSize);
40470 this.fireEvent("moved", this, newSize);
40471 this.fireEvent("resize", this, newSize);
40477 * Get the adapter this SplitBar uses
40478 * @return The adapter object
40480 getAdapter : function(){
40481 return this.adapter;
40485 * Set the adapter this SplitBar uses
40486 * @param {Object} adapter A SplitBar adapter object
40488 setAdapter : function(adapter){
40489 this.adapter = adapter;
40490 this.adapter.init(this);
40494 * Gets the minimum size for the resizing element
40495 * @return {Number} The minimum size
40497 getMinimumSize : function(){
40498 return this.minSize;
40502 * Sets the minimum size for the resizing element
40503 * @param {Number} minSize The minimum size
40505 setMinimumSize : function(minSize){
40506 this.minSize = minSize;
40510 * Gets the maximum size for the resizing element
40511 * @return {Number} The maximum size
40513 getMaximumSize : function(){
40514 return this.maxSize;
40518 * Sets the maximum size for the resizing element
40519 * @param {Number} maxSize The maximum size
40521 setMaximumSize : function(maxSize){
40522 this.maxSize = maxSize;
40526 * Sets the initialize size for the resizing element
40527 * @param {Number} size The initial size
40529 setCurrentSize : function(size){
40530 var oldAnimate = this.animate;
40531 this.animate = false;
40532 this.adapter.setElementSize(this, size);
40533 this.animate = oldAnimate;
40537 * Destroy this splitbar.
40538 * @param {Boolean} removeEl True to remove the element
40540 destroy : function(removeEl){
40542 this.shim.remove();
40545 this.proxy.parentNode.removeChild(this.proxy);
40553 * @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.
40555 Roo.bootstrap.SplitBar.createProxy = function(dir){
40556 var proxy = new Roo.Element(document.createElement("div"));
40557 proxy.unselectable();
40558 var cls = 'roo-splitbar-proxy';
40559 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
40560 document.body.appendChild(proxy.dom);
40565 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
40566 * Default Adapter. It assumes the splitter and resizing element are not positioned
40567 * elements and only gets/sets the width of the element. Generally used for table based layouts.
40569 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
40572 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
40573 // do nothing for now
40574 init : function(s){
40578 * Called before drag operations to get the current size of the resizing element.
40579 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
40581 getElementSize : function(s){
40582 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40583 return s.resizingEl.getWidth();
40585 return s.resizingEl.getHeight();
40590 * Called after drag operations to set the size of the resizing element.
40591 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
40592 * @param {Number} newSize The new size to set
40593 * @param {Function} onComplete A function to be invoked when resizing is complete
40595 setElementSize : function(s, newSize, onComplete){
40596 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40598 s.resizingEl.setWidth(newSize);
40600 onComplete(s, newSize);
40603 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
40608 s.resizingEl.setHeight(newSize);
40610 onComplete(s, newSize);
40613 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
40620 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
40621 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
40622 * Adapter that moves the splitter element to align with the resized sizing element.
40623 * Used with an absolute positioned SplitBar.
40624 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
40625 * document.body, make sure you assign an id to the body element.
40627 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
40628 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40629 this.container = Roo.get(container);
40632 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
40633 init : function(s){
40634 this.basic.init(s);
40637 getElementSize : function(s){
40638 return this.basic.getElementSize(s);
40641 setElementSize : function(s, newSize, onComplete){
40642 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
40645 moveSplitter : function(s){
40646 var yes = Roo.bootstrap.SplitBar;
40647 switch(s.placement){
40649 s.el.setX(s.resizingEl.getRight());
40652 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
40655 s.el.setY(s.resizingEl.getBottom());
40658 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
40665 * Orientation constant - Create a vertical SplitBar
40669 Roo.bootstrap.SplitBar.VERTICAL = 1;
40672 * Orientation constant - Create a horizontal SplitBar
40676 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
40679 * Placement constant - The resizing element is to the left of the splitter element
40683 Roo.bootstrap.SplitBar.LEFT = 1;
40686 * Placement constant - The resizing element is to the right of the splitter element
40690 Roo.bootstrap.SplitBar.RIGHT = 2;
40693 * Placement constant - The resizing element is positioned above the splitter element
40697 Roo.bootstrap.SplitBar.TOP = 3;
40700 * Placement constant - The resizing element is positioned under splitter element
40704 Roo.bootstrap.SplitBar.BOTTOM = 4;
40707 * Ext JS Library 1.1.1
40708 * Copyright(c) 2006-2007, Ext JS, LLC.
40710 * Originally Released Under LGPL - original licence link has changed is not relivant.
40713 * <script type="text/javascript">
40717 * @class Roo.bootstrap.layout.Manager
40718 * @extends Roo.bootstrap.Component
40720 * Base class for layout managers.
40722 Roo.bootstrap.layout.Manager = function(config)
40724 this.monitorWindowResize = true; // do this before we apply configuration.
40726 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
40732 /** false to disable window resize monitoring @type Boolean */
40738 * Fires when a layout is performed.
40739 * @param {Roo.LayoutManager} this
40743 * @event regionresized
40744 * Fires when the user resizes a region.
40745 * @param {Roo.LayoutRegion} region The resized region
40746 * @param {Number} newSize The new size (width for east/west, height for north/south)
40748 "regionresized" : true,
40750 * @event regioncollapsed
40751 * Fires when a region is collapsed.
40752 * @param {Roo.LayoutRegion} region The collapsed region
40754 "regioncollapsed" : true,
40756 * @event regionexpanded
40757 * Fires when a region is expanded.
40758 * @param {Roo.LayoutRegion} region The expanded region
40760 "regionexpanded" : true
40762 this.updating = false;
40765 this.el = Roo.get(config.el);
40771 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
40776 monitorWindowResize : true,
40782 onRender : function(ct, position)
40785 this.el = Roo.get(ct);
40788 //this.fireEvent('render',this);
40792 initEvents: function()
40796 // ie scrollbar fix
40797 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
40798 document.body.scroll = "no";
40799 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
40800 this.el.position('relative');
40802 this.id = this.el.id;
40803 this.el.addClass("roo-layout-container");
40804 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
40805 if(this.el.dom != document.body ) {
40806 this.el.on('resize', this.layout,this);
40807 this.el.on('show', this.layout,this);
40813 * Returns true if this layout is currently being updated
40814 * @return {Boolean}
40816 isUpdating : function(){
40817 return this.updating;
40821 * Suspend the LayoutManager from doing auto-layouts while
40822 * making multiple add or remove calls
40824 beginUpdate : function(){
40825 this.updating = true;
40829 * Restore auto-layouts and optionally disable the manager from performing a layout
40830 * @param {Boolean} noLayout true to disable a layout update
40832 endUpdate : function(noLayout){
40833 this.updating = false;
40839 layout: function(){
40843 onRegionResized : function(region, newSize){
40844 this.fireEvent("regionresized", region, newSize);
40848 onRegionCollapsed : function(region){
40849 this.fireEvent("regioncollapsed", region);
40852 onRegionExpanded : function(region){
40853 this.fireEvent("regionexpanded", region);
40857 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
40858 * performs box-model adjustments.
40859 * @return {Object} The size as an object {width: (the width), height: (the height)}
40861 getViewSize : function()
40864 if(this.el.dom != document.body){
40865 size = this.el.getSize();
40867 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
40869 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
40870 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40875 * Returns the Element this layout is bound to.
40876 * @return {Roo.Element}
40878 getEl : function(){
40883 * Returns the specified region.
40884 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
40885 * @return {Roo.LayoutRegion}
40887 getRegion : function(target){
40888 return this.regions[target.toLowerCase()];
40891 onWindowResize : function(){
40892 if(this.monitorWindowResize){
40899 * Ext JS Library 1.1.1
40900 * Copyright(c) 2006-2007, Ext JS, LLC.
40902 * Originally Released Under LGPL - original licence link has changed is not relivant.
40905 * <script type="text/javascript">
40908 * @class Roo.bootstrap.layout.Border
40909 * @extends Roo.bootstrap.layout.Manager
40910 * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
40911 * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
40912 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
40913 * please see: examples/bootstrap/nested.html<br><br>
40915 <b>The container the layout is rendered into can be either the body element or any other element.
40916 If it is not the body element, the container needs to either be an absolute positioned element,
40917 or you will need to add "position:relative" to the css of the container. You will also need to specify
40918 the container size if it is not the body element.</b>
40921 * Create a new Border
40922 * @param {Object} config Configuration options
40924 Roo.bootstrap.layout.Border = function(config){
40925 config = config || {};
40926 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
40930 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
40931 if(config[region]){
40932 config[region].region = region;
40933 this.addRegion(config[region]);
40939 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
40941 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
40944 * @cfg {Roo.bootstrap.layout.Region} center region to go in center
40947 * @cfg {Roo.bootstrap.layout.Region} west region to go in west
40950 * @cfg {Roo.bootstrap.layout.Region} east region to go in east
40953 * @cfg {Roo.bootstrap.layout.Region} south region to go in south
40956 * @cfg {Roo.bootstrap.layout.Region} north region to go in north
40962 parent : false, // this might point to a 'nest' or a ???
40965 * Creates and adds a new region if it doesn't already exist.
40966 * @param {String} target The target region key (north, south, east, west or center).
40967 * @param {Object} config The regions config object
40968 * @return {BorderLayoutRegion} The new region
40970 addRegion : function(config)
40972 if(!this.regions[config.region]){
40973 var r = this.factory(config);
40974 this.bindRegion(r);
40976 return this.regions[config.region];
40980 bindRegion : function(r){
40981 this.regions[r.config.region] = r;
40983 r.on("visibilitychange", this.layout, this);
40984 r.on("paneladded", this.layout, this);
40985 r.on("panelremoved", this.layout, this);
40986 r.on("invalidated", this.layout, this);
40987 r.on("resized", this.onRegionResized, this);
40988 r.on("collapsed", this.onRegionCollapsed, this);
40989 r.on("expanded", this.onRegionExpanded, this);
40993 * Performs a layout update.
40995 layout : function()
40997 if(this.updating) {
41001 // render all the rebions if they have not been done alreayd?
41002 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
41003 if(this.regions[region] && !this.regions[region].bodyEl){
41004 this.regions[region].onRender(this.el)
41008 var size = this.getViewSize();
41009 var w = size.width;
41010 var h = size.height;
41015 //var x = 0, y = 0;
41017 var rs = this.regions;
41018 var north = rs["north"];
41019 var south = rs["south"];
41020 var west = rs["west"];
41021 var east = rs["east"];
41022 var center = rs["center"];
41023 //if(this.hideOnLayout){ // not supported anymore
41024 //c.el.setStyle("display", "none");
41026 if(north && north.isVisible()){
41027 var b = north.getBox();
41028 var m = north.getMargins();
41029 b.width = w - (m.left+m.right);
41032 centerY = b.height + b.y + m.bottom;
41033 centerH -= centerY;
41034 north.updateBox(this.safeBox(b));
41036 if(south && south.isVisible()){
41037 var b = south.getBox();
41038 var m = south.getMargins();
41039 b.width = w - (m.left+m.right);
41041 var totalHeight = (b.height + m.top + m.bottom);
41042 b.y = h - totalHeight + m.top;
41043 centerH -= totalHeight;
41044 south.updateBox(this.safeBox(b));
41046 if(west && west.isVisible()){
41047 var b = west.getBox();
41048 var m = west.getMargins();
41049 b.height = centerH - (m.top+m.bottom);
41051 b.y = centerY + m.top;
41052 var totalWidth = (b.width + m.left + m.right);
41053 centerX += totalWidth;
41054 centerW -= totalWidth;
41055 west.updateBox(this.safeBox(b));
41057 if(east && east.isVisible()){
41058 var b = east.getBox();
41059 var m = east.getMargins();
41060 b.height = centerH - (m.top+m.bottom);
41061 var totalWidth = (b.width + m.left + m.right);
41062 b.x = w - totalWidth + m.left;
41063 b.y = centerY + m.top;
41064 centerW -= totalWidth;
41065 east.updateBox(this.safeBox(b));
41068 var m = center.getMargins();
41070 x: centerX + m.left,
41071 y: centerY + m.top,
41072 width: centerW - (m.left+m.right),
41073 height: centerH - (m.top+m.bottom)
41075 //if(this.hideOnLayout){
41076 //center.el.setStyle("display", "block");
41078 center.updateBox(this.safeBox(centerBox));
41081 this.fireEvent("layout", this);
41085 safeBox : function(box){
41086 box.width = Math.max(0, box.width);
41087 box.height = Math.max(0, box.height);
41092 * Adds a ContentPanel (or subclass) to this layout.
41093 * @param {String} target The target region key (north, south, east, west or center).
41094 * @param {Roo.ContentPanel} panel The panel to add
41095 * @return {Roo.ContentPanel} The added panel
41097 add : function(target, panel){
41099 target = target.toLowerCase();
41100 return this.regions[target].add(panel);
41104 * Remove a ContentPanel (or subclass) to this layout.
41105 * @param {String} target The target region key (north, south, east, west or center).
41106 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
41107 * @return {Roo.ContentPanel} The removed panel
41109 remove : function(target, panel){
41110 target = target.toLowerCase();
41111 return this.regions[target].remove(panel);
41115 * Searches all regions for a panel with the specified id
41116 * @param {String} panelId
41117 * @return {Roo.ContentPanel} The panel or null if it wasn't found
41119 findPanel : function(panelId){
41120 var rs = this.regions;
41121 for(var target in rs){
41122 if(typeof rs[target] != "function"){
41123 var p = rs[target].getPanel(panelId);
41133 * Searches all regions for a panel with the specified id and activates (shows) it.
41134 * @param {String/ContentPanel} panelId The panels id or the panel itself
41135 * @return {Roo.ContentPanel} The shown panel or null
41137 showPanel : function(panelId) {
41138 var rs = this.regions;
41139 for(var target in rs){
41140 var r = rs[target];
41141 if(typeof r != "function"){
41142 if(r.hasPanel(panelId)){
41143 return r.showPanel(panelId);
41151 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
41152 * @param {Roo.state.Provider} provider (optional) An alternate state provider
41155 restoreState : function(provider){
41157 provider = Roo.state.Manager;
41159 var sm = new Roo.LayoutStateManager();
41160 sm.init(this, provider);
41166 * Adds a xtype elements to the layout.
41170 xtype : 'ContentPanel',
41177 xtype : 'NestedLayoutPanel',
41183 items : [ ... list of content panels or nested layout panels.. ]
41187 * @param {Object} cfg Xtype definition of item to add.
41189 addxtype : function(cfg)
41191 // basically accepts a pannel...
41192 // can accept a layout region..!?!?
41193 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
41196 // theory? children can only be panels??
41198 //if (!cfg.xtype.match(/Panel$/)) {
41203 if (typeof(cfg.region) == 'undefined') {
41204 Roo.log("Failed to add Panel, region was not set");
41208 var region = cfg.region;
41214 xitems = cfg.items;
41219 if ( region == 'center') {
41220 Roo.log("Center: " + cfg.title);
41226 case 'Content': // ContentPanel (el, cfg)
41227 case 'Scroll': // ContentPanel (el, cfg)
41229 cfg.autoCreate = cfg.autoCreate || true;
41230 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41232 // var el = this.el.createChild();
41233 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
41236 this.add(region, ret);
41240 case 'TreePanel': // our new panel!
41241 cfg.el = this.el.createChild();
41242 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41243 this.add(region, ret);
41248 // create a new Layout (which is a Border Layout...
41250 var clayout = cfg.layout;
41251 clayout.el = this.el.createChild();
41252 clayout.items = clayout.items || [];
41256 // replace this exitems with the clayout ones..
41257 xitems = clayout.items;
41259 // force background off if it's in center...
41260 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
41261 cfg.background = false;
41263 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
41266 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41267 //console.log('adding nested layout panel ' + cfg.toSource());
41268 this.add(region, ret);
41269 nb = {}; /// find first...
41274 // needs grid and region
41276 //var el = this.getRegion(region).el.createChild();
41278 *var el = this.el.createChild();
41279 // create the grid first...
41280 cfg.grid.container = el;
41281 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
41284 if (region == 'center' && this.active ) {
41285 cfg.background = false;
41288 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41290 this.add(region, ret);
41292 if (cfg.background) {
41293 // render grid on panel activation (if panel background)
41294 ret.on('activate', function(gp) {
41295 if (!gp.grid.rendered) {
41296 // gp.grid.render(el);
41300 // cfg.grid.render(el);
41306 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
41307 // it was the old xcomponent building that caused this before.
41308 // espeically if border is the top element in the tree.
41318 if (typeof(Roo[cfg.xtype]) != 'undefined') {
41320 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41321 this.add(region, ret);
41325 throw "Can not add '" + cfg.xtype + "' to Border";
41331 this.beginUpdate();
41335 Roo.each(xitems, function(i) {
41336 region = nb && i.region ? i.region : false;
41338 var add = ret.addxtype(i);
41341 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
41342 if (!i.background) {
41343 abn[region] = nb[region] ;
41350 // make the last non-background panel active..
41351 //if (nb) { Roo.log(abn); }
41354 for(var r in abn) {
41355 region = this.getRegion(r);
41357 // tried using nb[r], but it does not work..
41359 region.showPanel(abn[r]);
41370 factory : function(cfg)
41373 var validRegions = Roo.bootstrap.layout.Border.regions;
41375 var target = cfg.region;
41378 var r = Roo.bootstrap.layout;
41382 return new r.North(cfg);
41384 return new r.South(cfg);
41386 return new r.East(cfg);
41388 return new r.West(cfg);
41390 return new r.Center(cfg);
41392 throw 'Layout region "'+target+'" not supported.';
41399 * Ext JS Library 1.1.1
41400 * Copyright(c) 2006-2007, Ext JS, LLC.
41402 * Originally Released Under LGPL - original licence link has changed is not relivant.
41405 * <script type="text/javascript">
41409 * @class Roo.bootstrap.layout.Basic
41410 * @extends Roo.util.Observable
41411 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
41412 * and does not have a titlebar, tabs or any other features. All it does is size and position
41413 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
41414 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
41415 * @cfg {string} region the region that it inhabits..
41416 * @cfg {bool} skipConfig skip config?
41420 Roo.bootstrap.layout.Basic = function(config){
41422 this.mgr = config.mgr;
41424 this.position = config.region;
41426 var skipConfig = config.skipConfig;
41430 * @scope Roo.BasicLayoutRegion
41434 * @event beforeremove
41435 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
41436 * @param {Roo.LayoutRegion} this
41437 * @param {Roo.ContentPanel} panel The panel
41438 * @param {Object} e The cancel event object
41440 "beforeremove" : true,
41442 * @event invalidated
41443 * Fires when the layout for this region is changed.
41444 * @param {Roo.LayoutRegion} this
41446 "invalidated" : true,
41448 * @event visibilitychange
41449 * Fires when this region is shown or hidden
41450 * @param {Roo.LayoutRegion} this
41451 * @param {Boolean} visibility true or false
41453 "visibilitychange" : true,
41455 * @event paneladded
41456 * Fires when a panel is added.
41457 * @param {Roo.LayoutRegion} this
41458 * @param {Roo.ContentPanel} panel The panel
41460 "paneladded" : true,
41462 * @event panelremoved
41463 * Fires when a panel is removed.
41464 * @param {Roo.LayoutRegion} this
41465 * @param {Roo.ContentPanel} panel The panel
41467 "panelremoved" : true,
41469 * @event beforecollapse
41470 * Fires when this region before collapse.
41471 * @param {Roo.LayoutRegion} this
41473 "beforecollapse" : true,
41476 * Fires when this region is collapsed.
41477 * @param {Roo.LayoutRegion} this
41479 "collapsed" : true,
41482 * Fires when this region is expanded.
41483 * @param {Roo.LayoutRegion} this
41488 * Fires when this region is slid into view.
41489 * @param {Roo.LayoutRegion} this
41491 "slideshow" : true,
41494 * Fires when this region slides out of view.
41495 * @param {Roo.LayoutRegion} this
41497 "slidehide" : true,
41499 * @event panelactivated
41500 * Fires when a panel is activated.
41501 * @param {Roo.LayoutRegion} this
41502 * @param {Roo.ContentPanel} panel The activated panel
41504 "panelactivated" : true,
41507 * Fires when the user resizes this region.
41508 * @param {Roo.LayoutRegion} this
41509 * @param {Number} newSize The new size (width for east/west, height for north/south)
41513 /** A collection of panels in this region. @type Roo.util.MixedCollection */
41514 this.panels = new Roo.util.MixedCollection();
41515 this.panels.getKey = this.getPanelId.createDelegate(this);
41517 this.activePanel = null;
41518 // ensure listeners are added...
41520 if (config.listeners || config.events) {
41521 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
41522 listeners : config.listeners || {},
41523 events : config.events || {}
41527 if(skipConfig !== true){
41528 this.applyConfig(config);
41532 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
41534 getPanelId : function(p){
41538 applyConfig : function(config){
41539 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
41540 this.config = config;
41545 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
41546 * the width, for horizontal (north, south) the height.
41547 * @param {Number} newSize The new width or height
41549 resizeTo : function(newSize){
41550 var el = this.el ? this.el :
41551 (this.activePanel ? this.activePanel.getEl() : null);
41553 switch(this.position){
41556 el.setWidth(newSize);
41557 this.fireEvent("resized", this, newSize);
41561 el.setHeight(newSize);
41562 this.fireEvent("resized", this, newSize);
41568 getBox : function(){
41569 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
41572 getMargins : function(){
41573 return this.margins;
41576 updateBox : function(box){
41578 var el = this.activePanel.getEl();
41579 el.dom.style.left = box.x + "px";
41580 el.dom.style.top = box.y + "px";
41581 this.activePanel.setSize(box.width, box.height);
41585 * Returns the container element for this region.
41586 * @return {Roo.Element}
41588 getEl : function(){
41589 return this.activePanel;
41593 * Returns true if this region is currently visible.
41594 * @return {Boolean}
41596 isVisible : function(){
41597 return this.activePanel ? true : false;
41600 setActivePanel : function(panel){
41601 panel = this.getPanel(panel);
41602 if(this.activePanel && this.activePanel != panel){
41603 this.activePanel.setActiveState(false);
41604 this.activePanel.getEl().setLeftTop(-10000,-10000);
41606 this.activePanel = panel;
41607 panel.setActiveState(true);
41609 panel.setSize(this.box.width, this.box.height);
41611 this.fireEvent("panelactivated", this, panel);
41612 this.fireEvent("invalidated");
41616 * Show the specified panel.
41617 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
41618 * @return {Roo.ContentPanel} The shown panel or null
41620 showPanel : function(panel){
41621 panel = this.getPanel(panel);
41623 this.setActivePanel(panel);
41629 * Get the active panel for this region.
41630 * @return {Roo.ContentPanel} The active panel or null
41632 getActivePanel : function(){
41633 return this.activePanel;
41637 * Add the passed ContentPanel(s)
41638 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
41639 * @return {Roo.ContentPanel} The panel added (if only one was added)
41641 add : function(panel){
41642 if(arguments.length > 1){
41643 for(var i = 0, len = arguments.length; i < len; i++) {
41644 this.add(arguments[i]);
41648 if(this.hasPanel(panel)){
41649 this.showPanel(panel);
41652 var el = panel.getEl();
41653 if(el.dom.parentNode != this.mgr.el.dom){
41654 this.mgr.el.dom.appendChild(el.dom);
41656 if(panel.setRegion){
41657 panel.setRegion(this);
41659 this.panels.add(panel);
41660 el.setStyle("position", "absolute");
41661 if(!panel.background){
41662 this.setActivePanel(panel);
41663 if(this.config.initialSize && this.panels.getCount()==1){
41664 this.resizeTo(this.config.initialSize);
41667 this.fireEvent("paneladded", this, panel);
41672 * Returns true if the panel is in this region.
41673 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41674 * @return {Boolean}
41676 hasPanel : function(panel){
41677 if(typeof panel == "object"){ // must be panel obj
41678 panel = panel.getId();
41680 return this.getPanel(panel) ? true : false;
41684 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
41685 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41686 * @param {Boolean} preservePanel Overrides the config preservePanel option
41687 * @return {Roo.ContentPanel} The panel that was removed
41689 remove : function(panel, preservePanel){
41690 panel = this.getPanel(panel);
41695 this.fireEvent("beforeremove", this, panel, e);
41696 if(e.cancel === true){
41699 var panelId = panel.getId();
41700 this.panels.removeKey(panelId);
41705 * Returns the panel specified or null if it's not in this region.
41706 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41707 * @return {Roo.ContentPanel}
41709 getPanel : function(id){
41710 if(typeof id == "object"){ // must be panel obj
41713 return this.panels.get(id);
41717 * Returns this regions position (north/south/east/west/center).
41720 getPosition: function(){
41721 return this.position;
41725 * Ext JS Library 1.1.1
41726 * Copyright(c) 2006-2007, Ext JS, LLC.
41728 * Originally Released Under LGPL - original licence link has changed is not relivant.
41731 * <script type="text/javascript">
41735 * @class Roo.bootstrap.layout.Region
41736 * @extends Roo.bootstrap.layout.Basic
41737 * This class represents a region in a layout manager.
41739 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
41740 * @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})
41741 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
41742 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
41743 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
41744 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
41745 * @cfg {String} title The title for the region (overrides panel titles)
41746 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
41747 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
41748 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
41749 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
41750 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
41751 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
41752 * the space available, similar to FireFox 1.5 tabs (defaults to false)
41753 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
41754 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
41755 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
41757 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
41758 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
41759 * @cfg {Boolean} disableTabTips True to disable tab tooltips
41760 * @cfg {Number} width For East/West panels
41761 * @cfg {Number} height For North/South panels
41762 * @cfg {Boolean} split To show the splitter
41763 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
41765 * @cfg {string} cls Extra CSS classes to add to region
41767 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
41768 * @cfg {string} region the region that it inhabits..
41771 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
41772 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
41774 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
41775 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
41776 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
41778 Roo.bootstrap.layout.Region = function(config)
41780 this.applyConfig(config);
41782 var mgr = config.mgr;
41783 var pos = config.region;
41784 config.skipConfig = true;
41785 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
41788 this.onRender(mgr.el);
41791 this.visible = true;
41792 this.collapsed = false;
41793 this.unrendered_panels = [];
41796 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
41798 position: '', // set by wrapper (eg. north/south etc..)
41799 unrendered_panels : null, // unrendered panels.
41801 tabPosition : false,
41803 mgr: false, // points to 'Border'
41806 createBody : function(){
41807 /** This region's body element
41808 * @type Roo.Element */
41809 this.bodyEl = this.el.createChild({
41811 cls: "roo-layout-panel-body tab-content" // bootstrap added...
41815 onRender: function(ctr, pos)
41817 var dh = Roo.DomHelper;
41818 /** This region's container element
41819 * @type Roo.Element */
41820 this.el = dh.append(ctr.dom, {
41822 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
41824 /** This region's title element
41825 * @type Roo.Element */
41827 this.titleEl = dh.append(this.el.dom, {
41829 unselectable: "on",
41830 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
41832 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
41833 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
41837 this.titleEl.enableDisplayMode();
41838 /** This region's title text element
41839 * @type HTMLElement */
41840 this.titleTextEl = this.titleEl.dom.firstChild;
41841 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
41843 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
41844 this.closeBtn.enableDisplayMode();
41845 this.closeBtn.on("click", this.closeClicked, this);
41846 this.closeBtn.hide();
41848 this.createBody(this.config);
41849 if(this.config.hideWhenEmpty){
41851 this.on("paneladded", this.validateVisibility, this);
41852 this.on("panelremoved", this.validateVisibility, this);
41854 if(this.autoScroll){
41855 this.bodyEl.setStyle("overflow", "auto");
41857 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
41859 //if(c.titlebar !== false){
41860 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
41861 this.titleEl.hide();
41863 this.titleEl.show();
41864 if(this.config.title){
41865 this.titleTextEl.innerHTML = this.config.title;
41869 if(this.config.collapsed){
41870 this.collapse(true);
41872 if(this.config.hidden){
41876 if (this.unrendered_panels && this.unrendered_panels.length) {
41877 for (var i =0;i< this.unrendered_panels.length; i++) {
41878 this.add(this.unrendered_panels[i]);
41880 this.unrendered_panels = null;
41886 applyConfig : function(c)
41889 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
41890 var dh = Roo.DomHelper;
41891 if(c.titlebar !== false){
41892 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
41893 this.collapseBtn.on("click", this.collapse, this);
41894 this.collapseBtn.enableDisplayMode();
41896 if(c.showPin === true || this.showPin){
41897 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
41898 this.stickBtn.enableDisplayMode();
41899 this.stickBtn.on("click", this.expand, this);
41900 this.stickBtn.hide();
41905 /** This region's collapsed element
41906 * @type Roo.Element */
41909 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
41910 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
41913 if(c.floatable !== false){
41914 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
41915 this.collapsedEl.on("click", this.collapseClick, this);
41918 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
41919 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
41920 id: "message", unselectable: "on", style:{"float":"left"}});
41921 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
41923 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
41924 this.expandBtn.on("click", this.expand, this);
41928 if(this.collapseBtn){
41929 this.collapseBtn.setVisible(c.collapsible == true);
41932 this.cmargins = c.cmargins || this.cmargins ||
41933 (this.position == "west" || this.position == "east" ?
41934 {top: 0, left: 2, right:2, bottom: 0} :
41935 {top: 2, left: 0, right:0, bottom: 2});
41937 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
41940 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
41942 this.autoScroll = c.autoScroll || false;
41947 this.duration = c.duration || .30;
41948 this.slideDuration = c.slideDuration || .45;
41953 * Returns true if this region is currently visible.
41954 * @return {Boolean}
41956 isVisible : function(){
41957 return this.visible;
41961 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
41962 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
41964 //setCollapsedTitle : function(title){
41965 // title = title || " ";
41966 // if(this.collapsedTitleTextEl){
41967 // this.collapsedTitleTextEl.innerHTML = title;
41971 getBox : function(){
41973 // if(!this.collapsed){
41974 b = this.el.getBox(false, true);
41976 // b = this.collapsedEl.getBox(false, true);
41981 getMargins : function(){
41982 return this.margins;
41983 //return this.collapsed ? this.cmargins : this.margins;
41986 highlight : function(){
41987 this.el.addClass("x-layout-panel-dragover");
41990 unhighlight : function(){
41991 this.el.removeClass("x-layout-panel-dragover");
41994 updateBox : function(box)
41996 if (!this.bodyEl) {
41997 return; // not rendered yet..
42001 if(!this.collapsed){
42002 this.el.dom.style.left = box.x + "px";
42003 this.el.dom.style.top = box.y + "px";
42004 this.updateBody(box.width, box.height);
42006 this.collapsedEl.dom.style.left = box.x + "px";
42007 this.collapsedEl.dom.style.top = box.y + "px";
42008 this.collapsedEl.setSize(box.width, box.height);
42011 this.tabs.autoSizeTabs();
42015 updateBody : function(w, h)
42018 this.el.setWidth(w);
42019 w -= this.el.getBorderWidth("rl");
42020 if(this.config.adjustments){
42021 w += this.config.adjustments[0];
42024 if(h !== null && h > 0){
42025 this.el.setHeight(h);
42026 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
42027 h -= this.el.getBorderWidth("tb");
42028 if(this.config.adjustments){
42029 h += this.config.adjustments[1];
42031 this.bodyEl.setHeight(h);
42033 h = this.tabs.syncHeight(h);
42036 if(this.panelSize){
42037 w = w !== null ? w : this.panelSize.width;
42038 h = h !== null ? h : this.panelSize.height;
42040 if(this.activePanel){
42041 var el = this.activePanel.getEl();
42042 w = w !== null ? w : el.getWidth();
42043 h = h !== null ? h : el.getHeight();
42044 this.panelSize = {width: w, height: h};
42045 this.activePanel.setSize(w, h);
42047 if(Roo.isIE && this.tabs){
42048 this.tabs.el.repaint();
42053 * Returns the container element for this region.
42054 * @return {Roo.Element}
42056 getEl : function(){
42061 * Hides this region.
42064 //if(!this.collapsed){
42065 this.el.dom.style.left = "-2000px";
42068 // this.collapsedEl.dom.style.left = "-2000px";
42069 // this.collapsedEl.hide();
42071 this.visible = false;
42072 this.fireEvent("visibilitychange", this, false);
42076 * Shows this region if it was previously hidden.
42079 //if(!this.collapsed){
42082 // this.collapsedEl.show();
42084 this.visible = true;
42085 this.fireEvent("visibilitychange", this, true);
42088 closeClicked : function(){
42089 if(this.activePanel){
42090 this.remove(this.activePanel);
42094 collapseClick : function(e){
42096 e.stopPropagation();
42099 e.stopPropagation();
42105 * Collapses this region.
42106 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
42109 collapse : function(skipAnim, skipCheck = false){
42110 if(this.collapsed) {
42114 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
42116 this.collapsed = true;
42118 this.split.el.hide();
42120 if(this.config.animate && skipAnim !== true){
42121 this.fireEvent("invalidated", this);
42122 this.animateCollapse();
42124 this.el.setLocation(-20000,-20000);
42126 this.collapsedEl.show();
42127 this.fireEvent("collapsed", this);
42128 this.fireEvent("invalidated", this);
42134 animateCollapse : function(){
42139 * Expands this region if it was previously collapsed.
42140 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
42141 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
42144 expand : function(e, skipAnim){
42146 e.stopPropagation();
42148 if(!this.collapsed || this.el.hasActiveFx()) {
42152 this.afterSlideIn();
42155 this.collapsed = false;
42156 if(this.config.animate && skipAnim !== true){
42157 this.animateExpand();
42161 this.split.el.show();
42163 this.collapsedEl.setLocation(-2000,-2000);
42164 this.collapsedEl.hide();
42165 this.fireEvent("invalidated", this);
42166 this.fireEvent("expanded", this);
42170 animateExpand : function(){
42174 initTabs : function()
42176 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
42178 var ts = new Roo.bootstrap.panel.Tabs({
42179 el: this.bodyEl.dom,
42181 tabPosition: this.tabPosition ? this.tabPosition : 'top',
42182 disableTooltips: this.config.disableTabTips,
42183 toolbar : this.config.toolbar
42186 if(this.config.hideTabs){
42187 ts.stripWrap.setDisplayed(false);
42190 ts.resizeTabs = this.config.resizeTabs === true;
42191 ts.minTabWidth = this.config.minTabWidth || 40;
42192 ts.maxTabWidth = this.config.maxTabWidth || 250;
42193 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
42194 ts.monitorResize = false;
42195 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
42196 ts.bodyEl.addClass('roo-layout-tabs-body');
42197 this.panels.each(this.initPanelAsTab, this);
42200 initPanelAsTab : function(panel){
42201 var ti = this.tabs.addTab(
42205 this.config.closeOnTab && panel.isClosable(),
42208 if(panel.tabTip !== undefined){
42209 ti.setTooltip(panel.tabTip);
42211 ti.on("activate", function(){
42212 this.setActivePanel(panel);
42215 if(this.config.closeOnTab){
42216 ti.on("beforeclose", function(t, e){
42218 this.remove(panel);
42222 panel.tabItem = ti;
42227 updatePanelTitle : function(panel, title)
42229 if(this.activePanel == panel){
42230 this.updateTitle(title);
42233 var ti = this.tabs.getTab(panel.getEl().id);
42235 if(panel.tabTip !== undefined){
42236 ti.setTooltip(panel.tabTip);
42241 updateTitle : function(title){
42242 if(this.titleTextEl && !this.config.title){
42243 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
42247 setActivePanel : function(panel)
42249 panel = this.getPanel(panel);
42250 if(this.activePanel && this.activePanel != panel){
42251 if(this.activePanel.setActiveState(false) === false){
42255 this.activePanel = panel;
42256 panel.setActiveState(true);
42257 if(this.panelSize){
42258 panel.setSize(this.panelSize.width, this.panelSize.height);
42261 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
42263 this.updateTitle(panel.getTitle());
42265 this.fireEvent("invalidated", this);
42267 this.fireEvent("panelactivated", this, panel);
42271 * Shows the specified panel.
42272 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
42273 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
42275 showPanel : function(panel)
42277 panel = this.getPanel(panel);
42280 var tab = this.tabs.getTab(panel.getEl().id);
42281 if(tab.isHidden()){
42282 this.tabs.unhideTab(tab.id);
42286 this.setActivePanel(panel);
42293 * Get the active panel for this region.
42294 * @return {Roo.ContentPanel} The active panel or null
42296 getActivePanel : function(){
42297 return this.activePanel;
42300 validateVisibility : function(){
42301 if(this.panels.getCount() < 1){
42302 this.updateTitle(" ");
42303 this.closeBtn.hide();
42306 if(!this.isVisible()){
42313 * Adds the passed ContentPanel(s) to this region.
42314 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42315 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
42317 add : function(panel)
42319 if(arguments.length > 1){
42320 for(var i = 0, len = arguments.length; i < len; i++) {
42321 this.add(arguments[i]);
42326 // if we have not been rendered yet, then we can not really do much of this..
42327 if (!this.bodyEl) {
42328 this.unrendered_panels.push(panel);
42335 if(this.hasPanel(panel)){
42336 this.showPanel(panel);
42339 panel.setRegion(this);
42340 this.panels.add(panel);
42341 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
42342 // sinle panel - no tab...?? would it not be better to render it with the tabs,
42343 // and hide them... ???
42344 this.bodyEl.dom.appendChild(panel.getEl().dom);
42345 if(panel.background !== true){
42346 this.setActivePanel(panel);
42348 this.fireEvent("paneladded", this, panel);
42355 this.initPanelAsTab(panel);
42359 if(panel.background !== true){
42360 this.tabs.activate(panel.getEl().id);
42362 this.fireEvent("paneladded", this, panel);
42367 * Hides the tab for the specified panel.
42368 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42370 hidePanel : function(panel){
42371 if(this.tabs && (panel = this.getPanel(panel))){
42372 this.tabs.hideTab(panel.getEl().id);
42377 * Unhides the tab for a previously hidden panel.
42378 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42380 unhidePanel : function(panel){
42381 if(this.tabs && (panel = this.getPanel(panel))){
42382 this.tabs.unhideTab(panel.getEl().id);
42386 clearPanels : function(){
42387 while(this.panels.getCount() > 0){
42388 this.remove(this.panels.first());
42393 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
42394 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42395 * @param {Boolean} preservePanel Overrides the config preservePanel option
42396 * @return {Roo.ContentPanel} The panel that was removed
42398 remove : function(panel, preservePanel)
42400 panel = this.getPanel(panel);
42405 this.fireEvent("beforeremove", this, panel, e);
42406 if(e.cancel === true){
42409 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
42410 var panelId = panel.getId();
42411 this.panels.removeKey(panelId);
42413 document.body.appendChild(panel.getEl().dom);
42416 this.tabs.removeTab(panel.getEl().id);
42417 }else if (!preservePanel){
42418 this.bodyEl.dom.removeChild(panel.getEl().dom);
42420 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
42421 var p = this.panels.first();
42422 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
42423 tempEl.appendChild(p.getEl().dom);
42424 this.bodyEl.update("");
42425 this.bodyEl.dom.appendChild(p.getEl().dom);
42427 this.updateTitle(p.getTitle());
42429 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
42430 this.setActivePanel(p);
42432 panel.setRegion(null);
42433 if(this.activePanel == panel){
42434 this.activePanel = null;
42436 if(this.config.autoDestroy !== false && preservePanel !== true){
42437 try{panel.destroy();}catch(e){}
42439 this.fireEvent("panelremoved", this, panel);
42444 * Returns the TabPanel component used by this region
42445 * @return {Roo.TabPanel}
42447 getTabs : function(){
42451 createTool : function(parentEl, className){
42452 var btn = Roo.DomHelper.append(parentEl, {
42454 cls: "x-layout-tools-button",
42457 cls: "roo-layout-tools-button-inner " + className,
42461 btn.addClassOnOver("roo-layout-tools-button-over");
42466 * Ext JS Library 1.1.1
42467 * Copyright(c) 2006-2007, Ext JS, LLC.
42469 * Originally Released Under LGPL - original licence link has changed is not relivant.
42472 * <script type="text/javascript">
42478 * @class Roo.SplitLayoutRegion
42479 * @extends Roo.LayoutRegion
42480 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
42482 Roo.bootstrap.layout.Split = function(config){
42483 this.cursor = config.cursor;
42484 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
42487 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
42489 splitTip : "Drag to resize.",
42490 collapsibleSplitTip : "Drag to resize. Double click to hide.",
42491 useSplitTips : false,
42493 applyConfig : function(config){
42494 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
42497 onRender : function(ctr,pos) {
42499 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
42500 if(!this.config.split){
42505 var splitEl = Roo.DomHelper.append(ctr.dom, {
42507 id: this.el.id + "-split",
42508 cls: "roo-layout-split roo-layout-split-"+this.position,
42511 /** The SplitBar for this region
42512 * @type Roo.SplitBar */
42513 // does not exist yet...
42514 Roo.log([this.position, this.orientation]);
42516 this.split = new Roo.bootstrap.SplitBar({
42517 dragElement : splitEl,
42518 resizingElement: this.el,
42519 orientation : this.orientation
42522 this.split.on("moved", this.onSplitMove, this);
42523 this.split.useShim = this.config.useShim === true;
42524 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
42525 if(this.useSplitTips){
42526 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
42528 //if(config.collapsible){
42529 // this.split.el.on("dblclick", this.collapse, this);
42532 if(typeof this.config.minSize != "undefined"){
42533 this.split.minSize = this.config.minSize;
42535 if(typeof this.config.maxSize != "undefined"){
42536 this.split.maxSize = this.config.maxSize;
42538 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
42539 this.hideSplitter();
42544 getHMaxSize : function(){
42545 var cmax = this.config.maxSize || 10000;
42546 var center = this.mgr.getRegion("center");
42547 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
42550 getVMaxSize : function(){
42551 var cmax = this.config.maxSize || 10000;
42552 var center = this.mgr.getRegion("center");
42553 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
42556 onSplitMove : function(split, newSize){
42557 this.fireEvent("resized", this, newSize);
42561 * Returns the {@link Roo.SplitBar} for this region.
42562 * @return {Roo.SplitBar}
42564 getSplitBar : function(){
42569 this.hideSplitter();
42570 Roo.bootstrap.layout.Split.superclass.hide.call(this);
42573 hideSplitter : function(){
42575 this.split.el.setLocation(-2000,-2000);
42576 this.split.el.hide();
42582 this.split.el.show();
42584 Roo.bootstrap.layout.Split.superclass.show.call(this);
42587 beforeSlide: function(){
42588 if(Roo.isGecko){// firefox overflow auto bug workaround
42589 this.bodyEl.clip();
42591 this.tabs.bodyEl.clip();
42593 if(this.activePanel){
42594 this.activePanel.getEl().clip();
42596 if(this.activePanel.beforeSlide){
42597 this.activePanel.beforeSlide();
42603 afterSlide : function(){
42604 if(Roo.isGecko){// firefox overflow auto bug workaround
42605 this.bodyEl.unclip();
42607 this.tabs.bodyEl.unclip();
42609 if(this.activePanel){
42610 this.activePanel.getEl().unclip();
42611 if(this.activePanel.afterSlide){
42612 this.activePanel.afterSlide();
42618 initAutoHide : function(){
42619 if(this.autoHide !== false){
42620 if(!this.autoHideHd){
42621 var st = new Roo.util.DelayedTask(this.slideIn, this);
42622 this.autoHideHd = {
42623 "mouseout": function(e){
42624 if(!e.within(this.el, true)){
42628 "mouseover" : function(e){
42634 this.el.on(this.autoHideHd);
42638 clearAutoHide : function(){
42639 if(this.autoHide !== false){
42640 this.el.un("mouseout", this.autoHideHd.mouseout);
42641 this.el.un("mouseover", this.autoHideHd.mouseover);
42645 clearMonitor : function(){
42646 Roo.get(document).un("click", this.slideInIf, this);
42649 // these names are backwards but not changed for compat
42650 slideOut : function(){
42651 if(this.isSlid || this.el.hasActiveFx()){
42654 this.isSlid = true;
42655 if(this.collapseBtn){
42656 this.collapseBtn.hide();
42658 this.closeBtnState = this.closeBtn.getStyle('display');
42659 this.closeBtn.hide();
42661 this.stickBtn.show();
42664 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
42665 this.beforeSlide();
42666 this.el.setStyle("z-index", 10001);
42667 this.el.slideIn(this.getSlideAnchor(), {
42668 callback: function(){
42670 this.initAutoHide();
42671 Roo.get(document).on("click", this.slideInIf, this);
42672 this.fireEvent("slideshow", this);
42679 afterSlideIn : function(){
42680 this.clearAutoHide();
42681 this.isSlid = false;
42682 this.clearMonitor();
42683 this.el.setStyle("z-index", "");
42684 if(this.collapseBtn){
42685 this.collapseBtn.show();
42687 this.closeBtn.setStyle('display', this.closeBtnState);
42689 this.stickBtn.hide();
42691 this.fireEvent("slidehide", this);
42694 slideIn : function(cb){
42695 if(!this.isSlid || this.el.hasActiveFx()){
42699 this.isSlid = false;
42700 this.beforeSlide();
42701 this.el.slideOut(this.getSlideAnchor(), {
42702 callback: function(){
42703 this.el.setLeftTop(-10000, -10000);
42705 this.afterSlideIn();
42713 slideInIf : function(e){
42714 if(!e.within(this.el)){
42719 animateCollapse : function(){
42720 this.beforeSlide();
42721 this.el.setStyle("z-index", 20000);
42722 var anchor = this.getSlideAnchor();
42723 this.el.slideOut(anchor, {
42724 callback : function(){
42725 this.el.setStyle("z-index", "");
42726 this.collapsedEl.slideIn(anchor, {duration:.3});
42728 this.el.setLocation(-10000,-10000);
42730 this.fireEvent("collapsed", this);
42737 animateExpand : function(){
42738 this.beforeSlide();
42739 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
42740 this.el.setStyle("z-index", 20000);
42741 this.collapsedEl.hide({
42744 this.el.slideIn(this.getSlideAnchor(), {
42745 callback : function(){
42746 this.el.setStyle("z-index", "");
42749 this.split.el.show();
42751 this.fireEvent("invalidated", this);
42752 this.fireEvent("expanded", this);
42780 getAnchor : function(){
42781 return this.anchors[this.position];
42784 getCollapseAnchor : function(){
42785 return this.canchors[this.position];
42788 getSlideAnchor : function(){
42789 return this.sanchors[this.position];
42792 getAlignAdj : function(){
42793 var cm = this.cmargins;
42794 switch(this.position){
42810 getExpandAdj : function(){
42811 var c = this.collapsedEl, cm = this.cmargins;
42812 switch(this.position){
42814 return [-(cm.right+c.getWidth()+cm.left), 0];
42817 return [cm.right+c.getWidth()+cm.left, 0];
42820 return [0, -(cm.top+cm.bottom+c.getHeight())];
42823 return [0, cm.top+cm.bottom+c.getHeight()];
42829 * Ext JS Library 1.1.1
42830 * Copyright(c) 2006-2007, Ext JS, LLC.
42832 * Originally Released Under LGPL - original licence link has changed is not relivant.
42835 * <script type="text/javascript">
42838 * These classes are private internal classes
42840 Roo.bootstrap.layout.Center = function(config){
42841 config.region = "center";
42842 Roo.bootstrap.layout.Region.call(this, config);
42843 this.visible = true;
42844 this.minWidth = config.minWidth || 20;
42845 this.minHeight = config.minHeight || 20;
42848 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
42850 // center panel can't be hidden
42854 // center panel can't be hidden
42857 getMinWidth: function(){
42858 return this.minWidth;
42861 getMinHeight: function(){
42862 return this.minHeight;
42876 Roo.bootstrap.layout.North = function(config)
42878 config.region = 'north';
42879 config.cursor = 'n-resize';
42881 Roo.bootstrap.layout.Split.call(this, config);
42885 this.split.placement = Roo.bootstrap.SplitBar.TOP;
42886 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
42887 this.split.el.addClass("roo-layout-split-v");
42889 //var size = config.initialSize || config.height;
42890 //if(this.el && typeof size != "undefined"){
42891 // this.el.setHeight(size);
42894 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
42896 orientation: Roo.bootstrap.SplitBar.VERTICAL,
42899 onRender : function(ctr, pos)
42901 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42902 var size = this.config.initialSize || this.config.height;
42903 if(this.el && typeof size != "undefined"){
42904 this.el.setHeight(size);
42909 getBox : function(){
42910 if(this.collapsed){
42911 return this.collapsedEl.getBox();
42913 var box = this.el.getBox();
42915 box.height += this.split.el.getHeight();
42920 updateBox : function(box){
42921 if(this.split && !this.collapsed){
42922 box.height -= this.split.el.getHeight();
42923 this.split.el.setLeft(box.x);
42924 this.split.el.setTop(box.y+box.height);
42925 this.split.el.setWidth(box.width);
42927 if(this.collapsed){
42928 this.updateBody(box.width, null);
42930 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42938 Roo.bootstrap.layout.South = function(config){
42939 config.region = 'south';
42940 config.cursor = 's-resize';
42941 Roo.bootstrap.layout.Split.call(this, config);
42943 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
42944 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
42945 this.split.el.addClass("roo-layout-split-v");
42950 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
42951 orientation: Roo.bootstrap.SplitBar.VERTICAL,
42953 onRender : function(ctr, pos)
42955 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42956 var size = this.config.initialSize || this.config.height;
42957 if(this.el && typeof size != "undefined"){
42958 this.el.setHeight(size);
42963 getBox : function(){
42964 if(this.collapsed){
42965 return this.collapsedEl.getBox();
42967 var box = this.el.getBox();
42969 var sh = this.split.el.getHeight();
42976 updateBox : function(box){
42977 if(this.split && !this.collapsed){
42978 var sh = this.split.el.getHeight();
42981 this.split.el.setLeft(box.x);
42982 this.split.el.setTop(box.y-sh);
42983 this.split.el.setWidth(box.width);
42985 if(this.collapsed){
42986 this.updateBody(box.width, null);
42988 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42992 Roo.bootstrap.layout.East = function(config){
42993 config.region = "east";
42994 config.cursor = "e-resize";
42995 Roo.bootstrap.layout.Split.call(this, config);
42997 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
42998 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
42999 this.split.el.addClass("roo-layout-split-h");
43003 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
43004 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
43006 onRender : function(ctr, pos)
43008 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
43009 var size = this.config.initialSize || this.config.width;
43010 if(this.el && typeof size != "undefined"){
43011 this.el.setWidth(size);
43016 getBox : function(){
43017 if(this.collapsed){
43018 return this.collapsedEl.getBox();
43020 var box = this.el.getBox();
43022 var sw = this.split.el.getWidth();
43029 updateBox : function(box){
43030 if(this.split && !this.collapsed){
43031 var sw = this.split.el.getWidth();
43033 this.split.el.setLeft(box.x);
43034 this.split.el.setTop(box.y);
43035 this.split.el.setHeight(box.height);
43038 if(this.collapsed){
43039 this.updateBody(null, box.height);
43041 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
43045 Roo.bootstrap.layout.West = function(config){
43046 config.region = "west";
43047 config.cursor = "w-resize";
43049 Roo.bootstrap.layout.Split.call(this, config);
43051 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
43052 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
43053 this.split.el.addClass("roo-layout-split-h");
43057 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
43058 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
43060 onRender: function(ctr, pos)
43062 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
43063 var size = this.config.initialSize || this.config.width;
43064 if(typeof size != "undefined"){
43065 this.el.setWidth(size);
43069 getBox : function(){
43070 if(this.collapsed){
43071 return this.collapsedEl.getBox();
43073 var box = this.el.getBox();
43074 if (box.width == 0) {
43075 box.width = this.config.width; // kludge?
43078 box.width += this.split.el.getWidth();
43083 updateBox : function(box){
43084 if(this.split && !this.collapsed){
43085 var sw = this.split.el.getWidth();
43087 this.split.el.setLeft(box.x+box.width);
43088 this.split.el.setTop(box.y);
43089 this.split.el.setHeight(box.height);
43091 if(this.collapsed){
43092 this.updateBody(null, box.height);
43094 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
43098 * Ext JS Library 1.1.1
43099 * Copyright(c) 2006-2007, Ext JS, LLC.
43101 * Originally Released Under LGPL - original licence link has changed is not relivant.
43104 * <script type="text/javascript">
43107 * @class Roo.bootstrap.paenl.Content
43108 * @extends Roo.util.Observable
43109 * @children Roo.bootstrap.Component
43110 * @parent builder Roo.bootstrap.layout.Border
43111 * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
43112 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
43113 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
43114 * @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
43115 * @cfg {Boolean} closable True if the panel can be closed/removed
43116 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
43117 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
43118 * @cfg {Toolbar} toolbar A toolbar for this panel
43119 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
43120 * @cfg {String} title The title for this panel
43121 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
43122 * @cfg {String} url Calls {@link #setUrl} with this value
43123 * @cfg {String} region [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
43124 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
43125 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
43126 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
43127 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
43128 * @cfg {Boolean} badges render the badges
43129 * @cfg {String} cls extra classes to use
43130 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
43133 * Create a new ContentPanel.
43134 * @param {String/Object} config A string to set only the title or a config object
43137 Roo.bootstrap.panel.Content = function( config){
43139 this.tpl = config.tpl || false;
43141 var el = config.el;
43142 var content = config.content;
43144 if(config.autoCreate){ // xtype is available if this is called from factory
43147 this.el = Roo.get(el);
43148 if(!this.el && config && config.autoCreate){
43149 if(typeof config.autoCreate == "object"){
43150 if(!config.autoCreate.id){
43151 config.autoCreate.id = config.id||el;
43153 this.el = Roo.DomHelper.append(document.body,
43154 config.autoCreate, true);
43158 cls: (config.cls || '') +
43159 (config.background ? ' bg-' + config.background : '') +
43160 " roo-layout-inactive-content",
43163 if (config.iframe) {
43167 style : 'border: 0px',
43168 src : 'about:blank'
43174 elcfg.html = config.html;
43178 this.el = Roo.DomHelper.append(document.body, elcfg , true);
43179 if (config.iframe) {
43180 this.iframeEl = this.el.select('iframe',true).first();
43185 this.closable = false;
43186 this.loaded = false;
43187 this.active = false;
43190 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
43192 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
43194 this.wrapEl = this.el; //this.el.wrap();
43196 if (config.toolbar.items) {
43197 ti = config.toolbar.items ;
43198 delete config.toolbar.items ;
43202 this.toolbar.render(this.wrapEl, 'before');
43203 for(var i =0;i < ti.length;i++) {
43204 // Roo.log(['add child', items[i]]);
43205 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43207 this.toolbar.items = nitems;
43208 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
43209 delete config.toolbar;
43213 // xtype created footer. - not sure if will work as we normally have to render first..
43214 if (this.footer && !this.footer.el && this.footer.xtype) {
43215 if (!this.wrapEl) {
43216 this.wrapEl = this.el.wrap();
43219 this.footer.container = this.wrapEl.createChild();
43221 this.footer = Roo.factory(this.footer, Roo);
43226 if(typeof config == "string"){
43227 this.title = config;
43229 Roo.apply(this, config);
43233 this.resizeEl = Roo.get(this.resizeEl, true);
43235 this.resizeEl = this.el;
43237 // handle view.xtype
43245 * Fires when this panel is activated.
43246 * @param {Roo.ContentPanel} this
43250 * @event deactivate
43251 * Fires when this panel is activated.
43252 * @param {Roo.ContentPanel} this
43254 "deactivate" : true,
43258 * Fires when this panel is resized if fitToFrame is true.
43259 * @param {Roo.ContentPanel} this
43260 * @param {Number} width The width after any component adjustments
43261 * @param {Number} height The height after any component adjustments
43267 * Fires when this tab is created
43268 * @param {Roo.ContentPanel} this
43274 * Fires when this content is scrolled
43275 * @param {Roo.ContentPanel} this
43276 * @param {Event} scrollEvent
43287 if(this.autoScroll && !this.iframe){
43288 this.resizeEl.setStyle("overflow", "auto");
43289 this.resizeEl.on('scroll', this.onScroll, this);
43291 // fix randome scrolling
43292 //this.el.on('scroll', function() {
43293 // Roo.log('fix random scolling');
43294 // this.scrollTo('top',0);
43297 content = content || this.content;
43299 this.setContent(content);
43301 if(config && config.url){
43302 this.setUrl(this.url, this.params, this.loadOnce);
43307 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
43309 if (this.view && typeof(this.view.xtype) != 'undefined') {
43310 this.view.el = this.el.appendChild(document.createElement("div"));
43311 this.view = Roo.factory(this.view);
43312 this.view.render && this.view.render(false, '');
43316 this.fireEvent('render', this);
43319 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
43329 /* Resize Element - use this to work out scroll etc. */
43332 setRegion : function(region){
43333 this.region = region;
43334 this.setActiveClass(region && !this.background);
43338 setActiveClass: function(state)
43341 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
43342 this.el.setStyle('position','relative');
43344 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
43345 this.el.setStyle('position', 'absolute');
43350 * Returns the toolbar for this Panel if one was configured.
43351 * @return {Roo.Toolbar}
43353 getToolbar : function(){
43354 return this.toolbar;
43357 setActiveState : function(active)
43359 this.active = active;
43360 this.setActiveClass(active);
43362 if(this.fireEvent("deactivate", this) === false){
43367 this.fireEvent("activate", this);
43371 * Updates this panel's element (not for iframe)
43372 * @param {String} content The new content
43373 * @param {Boolean} loadScripts (optional) true to look for and process scripts
43375 setContent : function(content, loadScripts){
43380 this.el.update(content, loadScripts);
43383 ignoreResize : function(w, h)
43385 //return false; // always resize?
43386 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
43389 this.lastSize = {width: w, height: h};
43394 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
43395 * @return {Roo.UpdateManager} The UpdateManager
43397 getUpdateManager : function(){
43401 return this.el.getUpdateManager();
43404 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
43405 * Does not work with IFRAME contents
43406 * @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:
43409 url: "your-url.php",
43410 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
43411 callback: yourFunction,
43412 scope: yourObject, //(optional scope)
43415 text: "Loading...",
43421 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
43422 * 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.
43423 * @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}
43424 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
43425 * @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.
43426 * @return {Roo.ContentPanel} this
43434 var um = this.el.getUpdateManager();
43435 um.update.apply(um, arguments);
43441 * 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.
43442 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
43443 * @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)
43444 * @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)
43445 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
43447 setUrl : function(url, params, loadOnce){
43449 this.iframeEl.dom.src = url;
43453 if(this.refreshDelegate){
43454 this.removeListener("activate", this.refreshDelegate);
43456 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
43457 this.on("activate", this.refreshDelegate);
43458 return this.el.getUpdateManager();
43461 _handleRefresh : function(url, params, loadOnce){
43462 if(!loadOnce || !this.loaded){
43463 var updater = this.el.getUpdateManager();
43464 updater.update(url, params, this._setLoaded.createDelegate(this));
43468 _setLoaded : function(){
43469 this.loaded = true;
43473 * Returns this panel's id
43476 getId : function(){
43481 * Returns this panel's element - used by regiosn to add.
43482 * @return {Roo.Element}
43484 getEl : function(){
43485 return this.wrapEl || this.el;
43490 adjustForComponents : function(width, height)
43492 //Roo.log('adjustForComponents ');
43493 if(this.resizeEl != this.el){
43494 width -= this.el.getFrameWidth('lr');
43495 height -= this.el.getFrameWidth('tb');
43498 var te = this.toolbar.getEl();
43499 te.setWidth(width);
43500 height -= te.getHeight();
43503 var te = this.footer.getEl();
43504 te.setWidth(width);
43505 height -= te.getHeight();
43509 if(this.adjustments){
43510 width += this.adjustments[0];
43511 height += this.adjustments[1];
43513 return {"width": width, "height": height};
43516 setSize : function(width, height){
43517 if(this.fitToFrame && !this.ignoreResize(width, height)){
43518 if(this.fitContainer && this.resizeEl != this.el){
43519 this.el.setSize(width, height);
43521 var size = this.adjustForComponents(width, height);
43523 this.iframeEl.setSize(width,height);
43526 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
43527 this.fireEvent('resize', this, size.width, size.height);
43534 * Returns this panel's title
43537 getTitle : function(){
43539 if (typeof(this.title) != 'object') {
43544 for (var k in this.title) {
43545 if (!this.title.hasOwnProperty(k)) {
43549 if (k.indexOf('-') >= 0) {
43550 var s = k.split('-');
43551 for (var i = 0; i<s.length; i++) {
43552 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
43555 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
43562 * Set this panel's title
43563 * @param {String} title
43565 setTitle : function(title){
43566 this.title = title;
43568 this.region.updatePanelTitle(this, title);
43573 * Returns true is this panel was configured to be closable
43574 * @return {Boolean}
43576 isClosable : function(){
43577 return this.closable;
43580 beforeSlide : function(){
43582 this.resizeEl.clip();
43585 afterSlide : function(){
43587 this.resizeEl.unclip();
43591 * Force a content refresh from the URL specified in the {@link #setUrl} method.
43592 * Will fail silently if the {@link #setUrl} method has not been called.
43593 * This does not activate the panel, just updates its content.
43595 refresh : function(){
43596 if(this.refreshDelegate){
43597 this.loaded = false;
43598 this.refreshDelegate();
43603 * Destroys this panel
43605 destroy : function(){
43606 this.el.removeAllListeners();
43607 var tempEl = document.createElement("span");
43608 tempEl.appendChild(this.el.dom);
43609 tempEl.innerHTML = "";
43615 * form - if the content panel contains a form - this is a reference to it.
43616 * @type {Roo.form.Form}
43620 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
43621 * This contains a reference to it.
43627 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
43637 * @param {Object} cfg Xtype definition of item to add.
43641 getChildContainer: function () {
43642 return this.getEl();
43646 onScroll : function(e)
43648 this.fireEvent('scroll', this, e);
43653 var ret = new Roo.factory(cfg);
43658 if (cfg.xtype.match(/^Form$/)) {
43661 //if (this.footer) {
43662 // el = this.footer.container.insertSibling(false, 'before');
43664 el = this.el.createChild();
43667 this.form = new Roo.form.Form(cfg);
43670 if ( this.form.allItems.length) {
43671 this.form.render(el.dom);
43675 // should only have one of theses..
43676 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
43677 // views.. should not be just added - used named prop 'view''
43679 cfg.el = this.el.appendChild(document.createElement("div"));
43682 var ret = new Roo.factory(cfg);
43684 ret.render && ret.render(false, ''); // render blank..
43694 * @class Roo.bootstrap.panel.Grid
43695 * @extends Roo.bootstrap.panel.Content
43697 * Create a new GridPanel.
43698 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
43699 * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
43700 * @param {Object} config A the config object
43706 Roo.bootstrap.panel.Grid = function(config)
43710 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
43711 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
43713 config.el = this.wrapper;
43714 //this.el = this.wrapper;
43716 if (config.container) {
43717 // ctor'ed from a Border/panel.grid
43720 this.wrapper.setStyle("overflow", "hidden");
43721 this.wrapper.addClass('roo-grid-container');
43726 if(config.toolbar){
43727 var tool_el = this.wrapper.createChild();
43728 this.toolbar = Roo.factory(config.toolbar);
43730 if (config.toolbar.items) {
43731 ti = config.toolbar.items ;
43732 delete config.toolbar.items ;
43736 this.toolbar.render(tool_el);
43737 for(var i =0;i < ti.length;i++) {
43738 // Roo.log(['add child', items[i]]);
43739 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43741 this.toolbar.items = nitems;
43743 delete config.toolbar;
43746 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
43747 config.grid.scrollBody = true;;
43748 config.grid.monitorWindowResize = false; // turn off autosizing
43749 config.grid.autoHeight = false;
43750 config.grid.autoWidth = false;
43752 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
43754 if (config.background) {
43755 // render grid on panel activation (if panel background)
43756 this.on('activate', function(gp) {
43757 if (!gp.grid.rendered) {
43758 gp.grid.render(this.wrapper);
43759 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
43764 this.grid.render(this.wrapper);
43765 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
43768 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
43769 // ??? needed ??? config.el = this.wrapper;
43774 // xtype created footer. - not sure if will work as we normally have to render first..
43775 if (this.footer && !this.footer.el && this.footer.xtype) {
43777 var ctr = this.grid.getView().getFooterPanel(true);
43778 this.footer.dataSource = this.grid.dataSource;
43779 this.footer = Roo.factory(this.footer, Roo);
43780 this.footer.render(ctr);
43790 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
43793 getId : function(){
43794 return this.grid.id;
43798 * Returns the grid for this panel
43799 * @return {Roo.bootstrap.Table}
43801 getGrid : function(){
43805 setSize : function(width, height)
43808 //if(!this.ignoreResize(width, height)){
43809 var grid = this.grid;
43810 var size = this.adjustForComponents(width, height);
43811 // tfoot is not a footer?
43814 var gridel = grid.getGridEl();
43815 gridel.setSize(size.width, size.height);
43817 var tbd = grid.getGridEl().select('tbody', true).first();
43818 var thd = grid.getGridEl().select('thead',true).first();
43819 var tbf= grid.getGridEl().select('tfoot', true).first();
43822 size.height -= tbf.getHeight();
43825 size.height -= thd.getHeight();
43828 tbd.setSize(size.width, size.height );
43829 // this is for the account management tab -seems to work there.
43830 var thd = grid.getGridEl().select('thead',true).first();
43832 // tbd.setSize(size.width, size.height - thd.getHeight());
43842 beforeSlide : function(){
43843 this.grid.getView().scroller.clip();
43846 afterSlide : function(){
43847 this.grid.getView().scroller.unclip();
43850 destroy : function(){
43851 this.grid.destroy();
43853 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
43858 * @class Roo.bootstrap.panel.Nest
43859 * @extends Roo.bootstrap.panel.Content
43861 * Create a new Panel, that can contain a layout.Border.
43864 * @param {String/Object} config A string to set only the title or a config object
43866 Roo.bootstrap.panel.Nest = function(config)
43868 // construct with only one argument..
43869 /* FIXME - implement nicer consturctors
43870 if (layout.layout) {
43872 layout = config.layout;
43873 delete config.layout;
43875 if (layout.xtype && !layout.getEl) {
43876 // then layout needs constructing..
43877 layout = Roo.factory(layout, Roo);
43881 config.el = config.layout.getEl();
43883 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
43885 config.layout.monitorWindowResize = false; // turn off autosizing
43886 this.layout = config.layout;
43887 this.layout.getEl().addClass("roo-layout-nested-layout");
43888 this.layout.parent = this;
43895 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
43897 * @cfg {Roo.BorderLayout} layout The layout for this panel
43901 setSize : function(width, height){
43902 if(!this.ignoreResize(width, height)){
43903 var size = this.adjustForComponents(width, height);
43904 var el = this.layout.getEl();
43905 if (size.height < 1) {
43906 el.setWidth(size.width);
43908 el.setSize(size.width, size.height);
43910 var touch = el.dom.offsetWidth;
43911 this.layout.layout();
43912 // ie requires a double layout on the first pass
43913 if(Roo.isIE && !this.initialized){
43914 this.initialized = true;
43915 this.layout.layout();
43920 // activate all subpanels if not currently active..
43922 setActiveState : function(active){
43923 this.active = active;
43924 this.setActiveClass(active);
43927 this.fireEvent("deactivate", this);
43931 this.fireEvent("activate", this);
43932 // not sure if this should happen before or after..
43933 if (!this.layout) {
43934 return; // should not happen..
43937 for (var r in this.layout.regions) {
43938 reg = this.layout.getRegion(r);
43939 if (reg.getActivePanel()) {
43940 //reg.showPanel(reg.getActivePanel()); // force it to activate..
43941 reg.setActivePanel(reg.getActivePanel());
43944 if (!reg.panels.length) {
43947 reg.showPanel(reg.getPanel(0));
43956 * Returns the nested BorderLayout for this panel
43957 * @return {Roo.BorderLayout}
43959 getLayout : function(){
43960 return this.layout;
43964 * Adds a xtype elements to the layout of the nested panel
43968 xtype : 'ContentPanel',
43975 xtype : 'NestedLayoutPanel',
43981 items : [ ... list of content panels or nested layout panels.. ]
43985 * @param {Object} cfg Xtype definition of item to add.
43987 addxtype : function(cfg) {
43988 return this.layout.addxtype(cfg);
43993 * Ext JS Library 1.1.1
43994 * Copyright(c) 2006-2007, Ext JS, LLC.
43996 * Originally Released Under LGPL - original licence link has changed is not relivant.
43999 * <script type="text/javascript">
44002 * @class Roo.TabPanel
44003 * @extends Roo.util.Observable
44004 * A lightweight tab container.
44008 // basic tabs 1, built from existing content
44009 var tabs = new Roo.TabPanel("tabs1");
44010 tabs.addTab("script", "View Script");
44011 tabs.addTab("markup", "View Markup");
44012 tabs.activate("script");
44014 // more advanced tabs, built from javascript
44015 var jtabs = new Roo.TabPanel("jtabs");
44016 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
44018 // set up the UpdateManager
44019 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
44020 var updater = tab2.getUpdateManager();
44021 updater.setDefaultUrl("ajax1.htm");
44022 tab2.on('activate', updater.refresh, updater, true);
44024 // Use setUrl for Ajax loading
44025 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
44026 tab3.setUrl("ajax2.htm", null, true);
44029 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
44032 jtabs.activate("jtabs-1");
44035 * Create a new TabPanel.
44036 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
44037 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
44039 Roo.bootstrap.panel.Tabs = function(config){
44041 * The container element for this TabPanel.
44042 * @type Roo.Element
44044 this.el = Roo.get(config.el);
44047 if(typeof config == "boolean"){
44048 this.tabPosition = config ? "bottom" : "top";
44050 Roo.apply(this, config);
44054 if(this.tabPosition == "bottom"){
44055 // if tabs are at the bottom = create the body first.
44056 this.bodyEl = Roo.get(this.createBody(this.el.dom));
44057 this.el.addClass("roo-tabs-bottom");
44059 // next create the tabs holders
44061 if (this.tabPosition == "west"){
44063 var reg = this.region; // fake it..
44065 if (!reg.mgr.parent) {
44068 reg = reg.mgr.parent.region;
44070 Roo.log("got nest?");
44072 if (reg.mgr.getRegion('west')) {
44073 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
44074 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
44075 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
44076 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
44077 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
44085 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
44086 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
44087 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
44088 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
44093 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
44096 // finally - if tabs are at the top, then create the body last..
44097 if(this.tabPosition != "bottom"){
44098 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
44099 * @type Roo.Element
44101 this.bodyEl = Roo.get(this.createBody(this.el.dom));
44102 this.el.addClass("roo-tabs-top");
44106 this.bodyEl.setStyle("position", "relative");
44108 this.active = null;
44109 this.activateDelegate = this.activate.createDelegate(this);
44114 * Fires when the active tab changes
44115 * @param {Roo.TabPanel} this
44116 * @param {Roo.TabPanelItem} activePanel The new active tab
44120 * @event beforetabchange
44121 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
44122 * @param {Roo.TabPanel} this
44123 * @param {Object} e Set cancel to true on this object to cancel the tab change
44124 * @param {Roo.TabPanelItem} tab The tab being changed to
44126 "beforetabchange" : true
44129 Roo.EventManager.onWindowResize(this.onResize, this);
44130 this.cpad = this.el.getPadding("lr");
44131 this.hiddenCount = 0;
44134 // toolbar on the tabbar support...
44135 if (this.toolbar) {
44136 alert("no toolbar support yet");
44137 this.toolbar = false;
44139 var tcfg = this.toolbar;
44140 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
44141 this.toolbar = new Roo.Toolbar(tcfg);
44142 if (Roo.isSafari) {
44143 var tbl = tcfg.container.child('table', true);
44144 tbl.setAttribute('width', '100%');
44152 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
44155 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
44157 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
44159 tabPosition : "top",
44161 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
44163 currentTabWidth : 0,
44165 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
44169 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
44173 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
44175 preferredTabWidth : 175,
44177 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
44179 resizeTabs : false,
44181 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
44183 monitorResize : true,
44185 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
44187 toolbar : false, // set by caller..
44189 region : false, /// set by caller
44191 disableTooltips : true, // not used yet...
44194 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
44195 * @param {String} id The id of the div to use <b>or create</b>
44196 * @param {String} text The text for the tab
44197 * @param {String} content (optional) Content to put in the TabPanelItem body
44198 * @param {Boolean} closable (optional) True to create a close icon on the tab
44199 * @return {Roo.TabPanelItem} The created TabPanelItem
44201 addTab : function(id, text, content, closable, tpl)
44203 var item = new Roo.bootstrap.panel.TabItem({
44207 closable : closable,
44210 this.addTabItem(item);
44212 item.setContent(content);
44218 * Returns the {@link Roo.TabPanelItem} with the specified id/index
44219 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
44220 * @return {Roo.TabPanelItem}
44222 getTab : function(id){
44223 return this.items[id];
44227 * Hides the {@link Roo.TabPanelItem} with the specified id/index
44228 * @param {String/Number} id The id or index of the TabPanelItem to hide.
44230 hideTab : function(id){
44231 var t = this.items[id];
44234 this.hiddenCount++;
44235 this.autoSizeTabs();
44240 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
44241 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
44243 unhideTab : function(id){
44244 var t = this.items[id];
44246 t.setHidden(false);
44247 this.hiddenCount--;
44248 this.autoSizeTabs();
44253 * Adds an existing {@link Roo.TabPanelItem}.
44254 * @param {Roo.TabPanelItem} item The TabPanelItem to add
44256 addTabItem : function(item)
44258 this.items[item.id] = item;
44259 this.items.push(item);
44260 this.autoSizeTabs();
44261 // if(this.resizeTabs){
44262 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
44263 // this.autoSizeTabs();
44265 // item.autoSize();
44270 * Removes a {@link Roo.TabPanelItem}.
44271 * @param {String/Number} id The id or index of the TabPanelItem to remove.
44273 removeTab : function(id){
44274 var items = this.items;
44275 var tab = items[id];
44276 if(!tab) { return; }
44277 var index = items.indexOf(tab);
44278 if(this.active == tab && items.length > 1){
44279 var newTab = this.getNextAvailable(index);
44284 this.stripEl.dom.removeChild(tab.pnode.dom);
44285 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
44286 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
44288 items.splice(index, 1);
44289 delete this.items[tab.id];
44290 tab.fireEvent("close", tab);
44291 tab.purgeListeners();
44292 this.autoSizeTabs();
44295 getNextAvailable : function(start){
44296 var items = this.items;
44298 // look for a next tab that will slide over to
44299 // replace the one being removed
44300 while(index < items.length){
44301 var item = items[++index];
44302 if(item && !item.isHidden()){
44306 // if one isn't found select the previous tab (on the left)
44309 var item = items[--index];
44310 if(item && !item.isHidden()){
44318 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
44319 * @param {String/Number} id The id or index of the TabPanelItem to disable.
44321 disableTab : function(id){
44322 var tab = this.items[id];
44323 if(tab && this.active != tab){
44329 * Enables a {@link Roo.TabPanelItem} that is disabled.
44330 * @param {String/Number} id The id or index of the TabPanelItem to enable.
44332 enableTab : function(id){
44333 var tab = this.items[id];
44338 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
44339 * @param {String/Number} id The id or index of the TabPanelItem to activate.
44340 * @return {Roo.TabPanelItem} The TabPanelItem.
44342 activate : function(id)
44344 //Roo.log('activite:' + id);
44346 var tab = this.items[id];
44350 if(tab == this.active || tab.disabled){
44354 this.fireEvent("beforetabchange", this, e, tab);
44355 if(e.cancel !== true && !tab.disabled){
44357 this.active.hide();
44359 this.active = this.items[id];
44360 this.active.show();
44361 this.fireEvent("tabchange", this, this.active);
44367 * Gets the active {@link Roo.TabPanelItem}.
44368 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
44370 getActiveTab : function(){
44371 return this.active;
44375 * Updates the tab body element to fit the height of the container element
44376 * for overflow scrolling
44377 * @param {Number} targetHeight (optional) Override the starting height from the elements height
44379 syncHeight : function(targetHeight){
44380 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44381 var bm = this.bodyEl.getMargins();
44382 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
44383 this.bodyEl.setHeight(newHeight);
44387 onResize : function(){
44388 if(this.monitorResize){
44389 this.autoSizeTabs();
44394 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
44396 beginUpdate : function(){
44397 this.updating = true;
44401 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
44403 endUpdate : function(){
44404 this.updating = false;
44405 this.autoSizeTabs();
44409 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
44411 autoSizeTabs : function()
44413 var count = this.items.length;
44414 var vcount = count - this.hiddenCount;
44417 this.stripEl.hide();
44419 this.stripEl.show();
44422 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
44427 var w = Math.max(this.el.getWidth() - this.cpad, 10);
44428 var availWidth = Math.floor(w / vcount);
44429 var b = this.stripBody;
44430 if(b.getWidth() > w){
44431 var tabs = this.items;
44432 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
44433 if(availWidth < this.minTabWidth){
44434 /*if(!this.sleft){ // incomplete scrolling code
44435 this.createScrollButtons();
44438 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
44441 if(this.currentTabWidth < this.preferredTabWidth){
44442 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
44448 * Returns the number of tabs in this TabPanel.
44451 getCount : function(){
44452 return this.items.length;
44456 * Resizes all the tabs to the passed width
44457 * @param {Number} The new width
44459 setTabWidth : function(width){
44460 this.currentTabWidth = width;
44461 for(var i = 0, len = this.items.length; i < len; i++) {
44462 if(!this.items[i].isHidden()) {
44463 this.items[i].setWidth(width);
44469 * Destroys this TabPanel
44470 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
44472 destroy : function(removeEl){
44473 Roo.EventManager.removeResizeListener(this.onResize, this);
44474 for(var i = 0, len = this.items.length; i < len; i++){
44475 this.items[i].purgeListeners();
44477 if(removeEl === true){
44478 this.el.update("");
44483 createStrip : function(container)
44485 var strip = document.createElement("nav");
44486 strip.className = Roo.bootstrap.version == 4 ?
44487 "navbar-light bg-light" :
44488 "navbar navbar-default"; //"x-tabs-wrap";
44489 container.appendChild(strip);
44493 createStripList : function(strip)
44495 // div wrapper for retard IE
44496 // returns the "tr" element.
44497 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
44498 //'<div class="x-tabs-strip-wrap">'+
44499 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
44500 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
44501 return strip.firstChild; //.firstChild.firstChild.firstChild;
44503 createBody : function(container)
44505 var body = document.createElement("div");
44506 Roo.id(body, "tab-body");
44507 //Roo.fly(body).addClass("x-tabs-body");
44508 Roo.fly(body).addClass("tab-content");
44509 container.appendChild(body);
44512 createItemBody :function(bodyEl, id){
44513 var body = Roo.getDom(id);
44515 body = document.createElement("div");
44518 //Roo.fly(body).addClass("x-tabs-item-body");
44519 Roo.fly(body).addClass("tab-pane");
44520 bodyEl.insertBefore(body, bodyEl.firstChild);
44524 createStripElements : function(stripEl, text, closable, tpl)
44526 var td = document.createElement("li"); // was td..
44527 td.className = 'nav-item';
44529 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
44532 stripEl.appendChild(td);
44534 td.className = "x-tabs-closable";
44535 if(!this.closeTpl){
44536 this.closeTpl = new Roo.Template(
44537 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
44538 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
44539 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
44542 var el = this.closeTpl.overwrite(td, {"text": text});
44543 var close = el.getElementsByTagName("div")[0];
44544 var inner = el.getElementsByTagName("em")[0];
44545 return {"el": el, "close": close, "inner": inner};
44548 // not sure what this is..
44549 // if(!this.tabTpl){
44550 //this.tabTpl = new Roo.Template(
44551 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
44552 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
44554 // this.tabTpl = new Roo.Template(
44555 // '<a href="#">' +
44556 // '<span unselectable="on"' +
44557 // (this.disableTooltips ? '' : ' title="{text}"') +
44558 // ' >{text}</span></a>'
44564 var template = tpl || this.tabTpl || false;
44567 template = new Roo.Template(
44568 Roo.bootstrap.version == 4 ?
44570 '<a class="nav-link" href="#" unselectable="on"' +
44571 (this.disableTooltips ? '' : ' title="{text}"') +
44574 '<a class="nav-link" href="#">' +
44575 '<span unselectable="on"' +
44576 (this.disableTooltips ? '' : ' title="{text}"') +
44577 ' >{text}</span></a>'
44582 switch (typeof(template)) {
44586 template = new Roo.Template(template);
44592 var el = template.overwrite(td, {"text": text});
44594 var inner = el.getElementsByTagName("span")[0];
44596 return {"el": el, "inner": inner};
44604 * @class Roo.TabPanelItem
44605 * @extends Roo.util.Observable
44606 * Represents an individual item (tab plus body) in a TabPanel.
44607 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
44608 * @param {String} id The id of this TabPanelItem
44609 * @param {String} text The text for the tab of this TabPanelItem
44610 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
44612 Roo.bootstrap.panel.TabItem = function(config){
44614 * The {@link Roo.TabPanel} this TabPanelItem belongs to
44615 * @type Roo.TabPanel
44617 this.tabPanel = config.panel;
44619 * The id for this TabPanelItem
44622 this.id = config.id;
44624 this.disabled = false;
44626 this.text = config.text;
44628 this.loaded = false;
44629 this.closable = config.closable;
44632 * The body element for this TabPanelItem.
44633 * @type Roo.Element
44635 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
44636 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
44637 this.bodyEl.setStyle("display", "block");
44638 this.bodyEl.setStyle("zoom", "1");
44639 //this.hideAction();
44641 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
44643 this.el = Roo.get(els.el);
44644 this.inner = Roo.get(els.inner, true);
44645 this.textEl = Roo.bootstrap.version == 4 ?
44646 this.el : Roo.get(this.el.dom.firstChild, true);
44648 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
44649 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
44652 // this.el.on("mousedown", this.onTabMouseDown, this);
44653 this.el.on("click", this.onTabClick, this);
44655 if(config.closable){
44656 var c = Roo.get(els.close, true);
44657 c.dom.title = this.closeText;
44658 c.addClassOnOver("close-over");
44659 c.on("click", this.closeClick, this);
44665 * Fires when this tab becomes the active tab.
44666 * @param {Roo.TabPanel} tabPanel The parent TabPanel
44667 * @param {Roo.TabPanelItem} this
44671 * @event beforeclose
44672 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
44673 * @param {Roo.TabPanelItem} this
44674 * @param {Object} e Set cancel to true on this object to cancel the close.
44676 "beforeclose": true,
44679 * Fires when this tab is closed.
44680 * @param {Roo.TabPanelItem} this
44684 * @event deactivate
44685 * Fires when this tab is no longer the active tab.
44686 * @param {Roo.TabPanel} tabPanel The parent TabPanel
44687 * @param {Roo.TabPanelItem} this
44689 "deactivate" : true
44691 this.hidden = false;
44693 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
44696 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
44698 purgeListeners : function(){
44699 Roo.util.Observable.prototype.purgeListeners.call(this);
44700 this.el.removeAllListeners();
44703 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
44706 this.status_node.addClass("active");
44709 this.tabPanel.stripWrap.repaint();
44711 this.fireEvent("activate", this.tabPanel, this);
44715 * Returns true if this tab is the active tab.
44716 * @return {Boolean}
44718 isActive : function(){
44719 return this.tabPanel.getActiveTab() == this;
44723 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
44726 this.status_node.removeClass("active");
44728 this.fireEvent("deactivate", this.tabPanel, this);
44731 hideAction : function(){
44732 this.bodyEl.hide();
44733 this.bodyEl.setStyle("position", "absolute");
44734 this.bodyEl.setLeft("-20000px");
44735 this.bodyEl.setTop("-20000px");
44738 showAction : function(){
44739 this.bodyEl.setStyle("position", "relative");
44740 this.bodyEl.setTop("");
44741 this.bodyEl.setLeft("");
44742 this.bodyEl.show();
44746 * Set the tooltip for the tab.
44747 * @param {String} tooltip The tab's tooltip
44749 setTooltip : function(text){
44750 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
44751 this.textEl.dom.qtip = text;
44752 this.textEl.dom.removeAttribute('title');
44754 this.textEl.dom.title = text;
44758 onTabClick : function(e){
44759 e.preventDefault();
44760 this.tabPanel.activate(this.id);
44763 onTabMouseDown : function(e){
44764 e.preventDefault();
44765 this.tabPanel.activate(this.id);
44768 getWidth : function(){
44769 return this.inner.getWidth();
44772 setWidth : function(width){
44773 var iwidth = width - this.linode.getPadding("lr");
44774 this.inner.setWidth(iwidth);
44775 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
44776 this.linode.setWidth(width);
44780 * Show or hide the tab
44781 * @param {Boolean} hidden True to hide or false to show.
44783 setHidden : function(hidden){
44784 this.hidden = hidden;
44785 this.linode.setStyle("display", hidden ? "none" : "");
44789 * Returns true if this tab is "hidden"
44790 * @return {Boolean}
44792 isHidden : function(){
44793 return this.hidden;
44797 * Returns the text for this tab
44800 getText : function(){
44804 autoSize : function(){
44805 //this.el.beginMeasure();
44806 this.textEl.setWidth(1);
44808 * #2804 [new] Tabs in Roojs
44809 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
44811 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
44812 //this.el.endMeasure();
44816 * Sets the text for the tab (Note: this also sets the tooltip text)
44817 * @param {String} text The tab's text and tooltip
44819 setText : function(text){
44821 this.textEl.update(text);
44822 this.setTooltip(text);
44823 //if(!this.tabPanel.resizeTabs){
44824 // this.autoSize();
44828 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
44830 activate : function(){
44831 this.tabPanel.activate(this.id);
44835 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
44837 disable : function(){
44838 if(this.tabPanel.active != this){
44839 this.disabled = true;
44840 this.status_node.addClass("disabled");
44845 * Enables this TabPanelItem if it was previously disabled.
44847 enable : function(){
44848 this.disabled = false;
44849 this.status_node.removeClass("disabled");
44853 * Sets the content for this TabPanelItem.
44854 * @param {String} content The content
44855 * @param {Boolean} loadScripts true to look for and load scripts
44857 setContent : function(content, loadScripts){
44858 this.bodyEl.update(content, loadScripts);
44862 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
44863 * @return {Roo.UpdateManager} The UpdateManager
44865 getUpdateManager : function(){
44866 return this.bodyEl.getUpdateManager();
44870 * Set a URL to be used to load the content for this TabPanelItem.
44871 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
44872 * @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)
44873 * @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)
44874 * @return {Roo.UpdateManager} The UpdateManager
44876 setUrl : function(url, params, loadOnce){
44877 if(this.refreshDelegate){
44878 this.un('activate', this.refreshDelegate);
44880 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44881 this.on("activate", this.refreshDelegate);
44882 return this.bodyEl.getUpdateManager();
44886 _handleRefresh : function(url, params, loadOnce){
44887 if(!loadOnce || !this.loaded){
44888 var updater = this.bodyEl.getUpdateManager();
44889 updater.update(url, params, this._setLoaded.createDelegate(this));
44894 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
44895 * Will fail silently if the setUrl method has not been called.
44896 * This does not activate the panel, just updates its content.
44898 refresh : function(){
44899 if(this.refreshDelegate){
44900 this.loaded = false;
44901 this.refreshDelegate();
44906 _setLoaded : function(){
44907 this.loaded = true;
44911 closeClick : function(e){
44914 this.fireEvent("beforeclose", this, o);
44915 if(o.cancel !== true){
44916 this.tabPanel.removeTab(this.id);
44920 * The text displayed in the tooltip for the close icon.
44923 closeText : "Close this tab"
44926 * This script refer to:
44927 * Title: International Telephone Input
44928 * Author: Jack O'Connor
44929 * Code version: v12.1.12
44930 * Availability: https://github.com/jackocnr/intl-tel-input.git
44933 Roo.bootstrap.form.PhoneInputData = function() {
44936 "Afghanistan (افغانستان)",
44941 "Albania (Shqipëri)",
44946 "Algeria (الجزائر)",
44971 "Antigua and Barbuda",
44981 "Armenia (Հայաստան)",
44997 "Austria (Österreich)",
45002 "Azerbaijan (Azərbaycan)",
45012 "Bahrain (البحرين)",
45017 "Bangladesh (বাংলাদেশ)",
45027 "Belarus (Беларусь)",
45032 "Belgium (België)",
45062 "Bosnia and Herzegovina (Босна и Херцеговина)",
45077 "British Indian Ocean Territory",
45082 "British Virgin Islands",
45092 "Bulgaria (България)",
45102 "Burundi (Uburundi)",
45107 "Cambodia (កម្ពុជា)",
45112 "Cameroon (Cameroun)",
45121 ["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"]
45124 "Cape Verde (Kabu Verdi)",
45129 "Caribbean Netherlands",
45140 "Central African Republic (République centrafricaine)",
45160 "Christmas Island",
45166 "Cocos (Keeling) Islands",
45177 "Comoros (جزر القمر)",
45182 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
45187 "Congo (Republic) (Congo-Brazzaville)",
45207 "Croatia (Hrvatska)",
45228 "Czech Republic (Česká republika)",
45233 "Denmark (Danmark)",
45248 "Dominican Republic (República Dominicana)",
45252 ["809", "829", "849"]
45270 "Equatorial Guinea (Guinea Ecuatorial)",
45290 "Falkland Islands (Islas Malvinas)",
45295 "Faroe Islands (Føroyar)",
45316 "French Guiana (Guyane française)",
45321 "French Polynesia (Polynésie française)",
45336 "Georgia (საქართველო)",
45341 "Germany (Deutschland)",
45361 "Greenland (Kalaallit Nunaat)",
45398 "Guinea-Bissau (Guiné Bissau)",
45423 "Hungary (Magyarország)",
45428 "Iceland (Ísland)",
45448 "Iraq (العراق)",
45464 "Israel (ישראל)",
45491 "Jordan (الأردن)",
45496 "Kazakhstan (Казахстан)",
45517 "Kuwait (الكويت)",
45522 "Kyrgyzstan (Кыргызстан)",
45532 "Latvia (Latvija)",
45537 "Lebanon (لبنان)",
45552 "Libya (ليبيا)",
45562 "Lithuania (Lietuva)",
45577 "Macedonia (FYROM) (Македонија)",
45582 "Madagascar (Madagasikara)",
45612 "Marshall Islands",
45622 "Mauritania (موريتانيا)",
45627 "Mauritius (Moris)",
45648 "Moldova (Republica Moldova)",
45658 "Mongolia (Монгол)",
45663 "Montenegro (Crna Gora)",
45673 "Morocco (المغرب)",
45679 "Mozambique (Moçambique)",
45684 "Myanmar (Burma) (မြန်မာ)",
45689 "Namibia (Namibië)",
45704 "Netherlands (Nederland)",
45709 "New Caledonia (Nouvelle-Calédonie)",
45744 "North Korea (조선 민주주의 인민 공화국)",
45749 "Northern Mariana Islands",
45765 "Pakistan (پاکستان)",
45775 "Palestine (فلسطين)",
45785 "Papua New Guinea",
45827 "Réunion (La Réunion)",
45833 "Romania (România)",
45849 "Saint Barthélemy",
45860 "Saint Kitts and Nevis",
45870 "Saint Martin (Saint-Martin (partie française))",
45876 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
45881 "Saint Vincent and the Grenadines",
45896 "São Tomé and Príncipe (São Tomé e Príncipe)",
45901 "Saudi Arabia (المملكة العربية السعودية)",
45906 "Senegal (Sénégal)",
45936 "Slovakia (Slovensko)",
45941 "Slovenia (Slovenija)",
45951 "Somalia (Soomaaliya)",
45961 "South Korea (대한민국)",
45966 "South Sudan (جنوب السودان)",
45976 "Sri Lanka (ශ්රී ලංකාව)",
45981 "Sudan (السودان)",
45991 "Svalbard and Jan Mayen",
46002 "Sweden (Sverige)",
46007 "Switzerland (Schweiz)",
46012 "Syria (سوريا)",
46057 "Trinidad and Tobago",
46062 "Tunisia (تونس)",
46067 "Turkey (Türkiye)",
46077 "Turks and Caicos Islands",
46087 "U.S. Virgin Islands",
46097 "Ukraine (Україна)",
46102 "United Arab Emirates (الإمارات العربية المتحدة)",
46124 "Uzbekistan (Oʻzbekiston)",
46134 "Vatican City (Città del Vaticano)",
46145 "Vietnam (Việt Nam)",
46150 "Wallis and Futuna (Wallis-et-Futuna)",
46155 "Western Sahara (الصحراء الغربية)",
46161 "Yemen (اليمن)",
46185 * This script refer to:
46186 * Title: International Telephone Input
46187 * Author: Jack O'Connor
46188 * Code version: v12.1.12
46189 * Availability: https://github.com/jackocnr/intl-tel-input.git
46193 * @class Roo.bootstrap.form.PhoneInput
46194 * @extends Roo.bootstrap.form.TriggerField
46195 * An input with International dial-code selection
46197 * @cfg {String} defaultDialCode default '+852'
46198 * @cfg {Array} preferedCountries default []
46201 * Create a new PhoneInput.
46202 * @param {Object} config Configuration options
46205 Roo.bootstrap.form.PhoneInput = function(config) {
46206 Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
46209 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
46211 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
46213 listWidth: undefined,
46215 selectedClass: 'active',
46217 invalidClass : "has-warning",
46219 validClass: 'has-success',
46221 allowed: '0123456789',
46226 * @cfg {String} defaultDialCode The default dial code when initializing the input
46228 defaultDialCode: '+852',
46231 * @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
46233 preferedCountries: false,
46235 getAutoCreate : function()
46237 var data = Roo.bootstrap.form.PhoneInputData();
46238 var align = this.labelAlign || this.parentLabelAlign();
46241 this.allCountries = [];
46242 this.dialCodeMapping = [];
46244 for (var i = 0; i < data.length; i++) {
46246 this.allCountries[i] = {
46250 priority: c[3] || 0,
46251 areaCodes: c[4] || null
46253 this.dialCodeMapping[c[2]] = {
46256 priority: c[3] || 0,
46257 areaCodes: c[4] || null
46269 // type: 'number', -- do not use number - we get the flaky up/down arrows.
46270 maxlength: this.max_length,
46271 cls : 'form-control tel-input',
46272 autocomplete: 'new-password'
46275 var hiddenInput = {
46278 cls: 'hidden-tel-input'
46282 hiddenInput.name = this.name;
46285 if (this.disabled) {
46286 input.disabled = true;
46289 var flag_container = {
46306 cls: this.hasFeedback ? 'has-feedback' : '',
46312 cls: 'dial-code-holder',
46319 cls: 'roo-select2-container input-group',
46326 if (this.fieldLabel.length) {
46329 tooltip: 'This field is required'
46335 cls: 'control-label',
46341 html: this.fieldLabel
46344 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46350 if(this.indicatorpos == 'right') {
46351 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46358 if(align == 'left') {
46366 if(this.labelWidth > 12){
46367 label.style = "width: " + this.labelWidth + 'px';
46369 if(this.labelWidth < 13 && this.labelmd == 0){
46370 this.labelmd = this.labelWidth;
46372 if(this.labellg > 0){
46373 label.cls += ' col-lg-' + this.labellg;
46374 input.cls += ' col-lg-' + (12 - this.labellg);
46376 if(this.labelmd > 0){
46377 label.cls += ' col-md-' + this.labelmd;
46378 container.cls += ' col-md-' + (12 - this.labelmd);
46380 if(this.labelsm > 0){
46381 label.cls += ' col-sm-' + this.labelsm;
46382 container.cls += ' col-sm-' + (12 - this.labelsm);
46384 if(this.labelxs > 0){
46385 label.cls += ' col-xs-' + this.labelxs;
46386 container.cls += ' col-xs-' + (12 - this.labelxs);
46396 var settings = this;
46398 ['xs','sm','md','lg'].map(function(size){
46399 if (settings[size]) {
46400 cfg.cls += ' col-' + size + '-' + settings[size];
46404 this.store = new Roo.data.Store({
46405 proxy : new Roo.data.MemoryProxy({}),
46406 reader : new Roo.data.JsonReader({
46417 'name' : 'dialCode',
46421 'name' : 'priority',
46425 'name' : 'areaCodes',
46432 if(!this.preferedCountries) {
46433 this.preferedCountries = [
46440 var p = this.preferedCountries.reverse();
46443 for (var i = 0; i < p.length; i++) {
46444 for (var j = 0; j < this.allCountries.length; j++) {
46445 if(this.allCountries[j].iso2 == p[i]) {
46446 var t = this.allCountries[j];
46447 this.allCountries.splice(j,1);
46448 this.allCountries.unshift(t);
46454 this.store.proxy.data = {
46456 data: this.allCountries
46462 initEvents : function()
46465 Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
46467 this.indicator = this.indicatorEl();
46468 this.flag = this.flagEl();
46469 this.dialCodeHolder = this.dialCodeHolderEl();
46471 this.trigger = this.el.select('div.flag-box',true).first();
46472 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
46477 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
46478 _this.list.setWidth(lw);
46481 this.list.on('mouseover', this.onViewOver, this);
46482 this.list.on('mousemove', this.onViewMove, this);
46483 this.inputEl().on("keyup", this.onKeyUp, this);
46484 this.inputEl().on("keypress", this.onKeyPress, this);
46486 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
46488 this.view = new Roo.View(this.list, this.tpl, {
46489 singleSelect:true, store: this.store, selectedClass: this.selectedClass
46492 this.view.on('click', this.onViewClick, this);
46493 this.setValue(this.defaultDialCode);
46496 onTriggerClick : function(e)
46498 Roo.log('trigger click');
46503 if(this.isExpanded()){
46505 this.hasFocus = false;
46507 this.store.load({});
46508 this.hasFocus = true;
46513 isExpanded : function()
46515 return this.list.isVisible();
46518 collapse : function()
46520 if(!this.isExpanded()){
46524 Roo.get(document).un('mousedown', this.collapseIf, this);
46525 Roo.get(document).un('mousewheel', this.collapseIf, this);
46526 this.fireEvent('collapse', this);
46530 expand : function()
46534 if(this.isExpanded() || !this.hasFocus){
46538 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
46539 this.list.setWidth(lw);
46542 this.restrictHeight();
46544 Roo.get(document).on('mousedown', this.collapseIf, this);
46545 Roo.get(document).on('mousewheel', this.collapseIf, this);
46547 this.fireEvent('expand', this);
46550 restrictHeight : function()
46552 this.list.alignTo(this.inputEl(), this.listAlign);
46553 this.list.alignTo(this.inputEl(), this.listAlign);
46556 onViewOver : function(e, t)
46558 if(this.inKeyMode){
46561 var item = this.view.findItemFromChild(t);
46564 var index = this.view.indexOf(item);
46565 this.select(index, false);
46570 onViewClick : function(view, doFocus, el, e)
46572 var index = this.view.getSelectedIndexes()[0];
46574 var r = this.store.getAt(index);
46577 this.onSelect(r, index);
46579 if(doFocus !== false && !this.blockFocus){
46580 this.inputEl().focus();
46584 onViewMove : function(e, t)
46586 this.inKeyMode = false;
46589 select : function(index, scrollIntoView)
46591 this.selectedIndex = index;
46592 this.view.select(index);
46593 if(scrollIntoView !== false){
46594 var el = this.view.getNode(index);
46596 this.list.scrollChildIntoView(el, false);
46601 createList : function()
46603 this.list = Roo.get(document.body).createChild({
46605 cls: 'typeahead typeahead-long dropdown-menu tel-list',
46606 style: 'display:none'
46609 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
46612 collapseIf : function(e)
46614 var in_combo = e.within(this.el);
46615 var in_list = e.within(this.list);
46616 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
46618 if (in_combo || in_list || is_list) {
46624 onSelect : function(record, index)
46626 if(this.fireEvent('beforeselect', this, record, index) !== false){
46628 this.setFlagClass(record.data.iso2);
46629 this.setDialCode(record.data.dialCode);
46630 this.hasFocus = false;
46632 this.fireEvent('select', this, record, index);
46636 flagEl : function()
46638 var flag = this.el.select('div.flag',true).first();
46645 dialCodeHolderEl : function()
46647 var d = this.el.select('input.dial-code-holder',true).first();
46654 setDialCode : function(v)
46656 this.dialCodeHolder.dom.value = '+'+v;
46659 setFlagClass : function(n)
46661 this.flag.dom.className = 'flag '+n;
46664 getValue : function()
46666 var v = this.inputEl().getValue();
46667 if(this.dialCodeHolder) {
46668 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
46673 setValue : function(v)
46675 var d = this.getDialCode(v);
46677 //invalid dial code
46678 if(v.length == 0 || !d || d.length == 0) {
46680 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
46681 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
46687 this.setFlagClass(this.dialCodeMapping[d].iso2);
46688 this.setDialCode(d);
46689 this.inputEl().dom.value = v.replace('+'+d,'');
46690 this.hiddenEl().dom.value = this.getValue();
46695 getDialCode : function(v)
46699 if (v.length == 0) {
46700 return this.dialCodeHolder.dom.value;
46704 if (v.charAt(0) != "+") {
46707 var numericChars = "";
46708 for (var i = 1; i < v.length; i++) {
46709 var c = v.charAt(i);
46712 if (this.dialCodeMapping[numericChars]) {
46713 dialCode = v.substr(1, i);
46715 if (numericChars.length == 4) {
46725 this.setValue(this.defaultDialCode);
46729 hiddenEl : function()
46731 return this.el.select('input.hidden-tel-input',true).first();
46734 // after setting val
46735 onKeyUp : function(e){
46736 this.setValue(this.getValue());
46739 onKeyPress : function(e){
46740 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
46747 * @class Roo.bootstrap.form.MoneyField
46748 * @extends Roo.bootstrap.form.ComboBox
46749 * Bootstrap MoneyField class
46752 * Create a new MoneyField.
46753 * @param {Object} config Configuration options
46756 Roo.bootstrap.form.MoneyField = function(config) {
46758 Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
46762 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
46765 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
46767 allowDecimals : true,
46769 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
46771 decimalSeparator : ".",
46773 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
46775 decimalPrecision : 0,
46777 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
46779 allowNegative : true,
46781 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
46785 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
46787 minValue : Number.NEGATIVE_INFINITY,
46789 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
46791 maxValue : Number.MAX_VALUE,
46793 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
46795 minText : "The minimum value for this field is {0}",
46797 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
46799 maxText : "The maximum value for this field is {0}",
46801 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
46802 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
46804 nanText : "{0} is not a valid number",
46806 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
46810 * @cfg {String} defaults currency of the MoneyField
46811 * value should be in lkey
46813 defaultCurrency : false,
46815 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
46817 thousandsDelimiter : false,
46819 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
46828 * @cfg {Roo.data.Store} store Store to lookup currency??
46832 getAutoCreate : function()
46834 var align = this.labelAlign || this.parentLabelAlign();
46846 cls : 'form-control roo-money-amount-input',
46847 autocomplete: 'new-password'
46850 var hiddenInput = {
46854 cls: 'hidden-number-input'
46857 if(this.max_length) {
46858 input.maxlength = this.max_length;
46862 hiddenInput.name = this.name;
46865 if (this.disabled) {
46866 input.disabled = true;
46869 var clg = 12 - this.inputlg;
46870 var cmd = 12 - this.inputmd;
46871 var csm = 12 - this.inputsm;
46872 var cxs = 12 - this.inputxs;
46876 cls : 'row roo-money-field',
46880 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
46884 cls: 'roo-select2-container input-group',
46888 cls : 'form-control roo-money-currency-input',
46889 autocomplete: 'new-password',
46891 name : this.currencyName
46895 cls : 'input-group-addon',
46909 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
46913 cls: this.hasFeedback ? 'has-feedback' : '',
46924 if (this.fieldLabel.length) {
46927 tooltip: 'This field is required'
46933 cls: 'control-label',
46939 html: this.fieldLabel
46942 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46948 if(this.indicatorpos == 'right') {
46949 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46956 if(align == 'left') {
46964 if(this.labelWidth > 12){
46965 label.style = "width: " + this.labelWidth + 'px';
46967 if(this.labelWidth < 13 && this.labelmd == 0){
46968 this.labelmd = this.labelWidth;
46970 if(this.labellg > 0){
46971 label.cls += ' col-lg-' + this.labellg;
46972 input.cls += ' col-lg-' + (12 - this.labellg);
46974 if(this.labelmd > 0){
46975 label.cls += ' col-md-' + this.labelmd;
46976 container.cls += ' col-md-' + (12 - this.labelmd);
46978 if(this.labelsm > 0){
46979 label.cls += ' col-sm-' + this.labelsm;
46980 container.cls += ' col-sm-' + (12 - this.labelsm);
46982 if(this.labelxs > 0){
46983 label.cls += ' col-xs-' + this.labelxs;
46984 container.cls += ' col-xs-' + (12 - this.labelxs);
46995 var settings = this;
46997 ['xs','sm','md','lg'].map(function(size){
46998 if (settings[size]) {
46999 cfg.cls += ' col-' + size + '-' + settings[size];
47006 initEvents : function()
47008 this.indicator = this.indicatorEl();
47010 this.initCurrencyEvent();
47012 this.initNumberEvent();
47015 initCurrencyEvent : function()
47018 throw "can not find store for combo";
47021 this.store = Roo.factory(this.store, Roo.data);
47022 this.store.parent = this;
47026 this.triggerEl = this.el.select('.input-group-addon', true).first();
47028 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
47033 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
47034 _this.list.setWidth(lw);
47037 this.list.on('mouseover', this.onViewOver, this);
47038 this.list.on('mousemove', this.onViewMove, this);
47039 this.list.on('scroll', this.onViewScroll, this);
47042 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
47045 this.view = new Roo.View(this.list, this.tpl, {
47046 singleSelect:true, store: this.store, selectedClass: this.selectedClass
47049 this.view.on('click', this.onViewClick, this);
47051 this.store.on('beforeload', this.onBeforeLoad, this);
47052 this.store.on('load', this.onLoad, this);
47053 this.store.on('loadexception', this.onLoadException, this);
47055 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
47056 "up" : function(e){
47057 this.inKeyMode = true;
47061 "down" : function(e){
47062 if(!this.isExpanded()){
47063 this.onTriggerClick();
47065 this.inKeyMode = true;
47070 "enter" : function(e){
47073 if(this.fireEvent("specialkey", this, e)){
47074 this.onViewClick(false);
47080 "esc" : function(e){
47084 "tab" : function(e){
47087 if(this.fireEvent("specialkey", this, e)){
47088 this.onViewClick(false);
47096 doRelay : function(foo, bar, hname){
47097 if(hname == 'down' || this.scope.isExpanded()){
47098 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
47106 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
47110 initNumberEvent : function(e)
47112 this.inputEl().on("keydown" , this.fireKey, this);
47113 this.inputEl().on("focus", this.onFocus, this);
47114 this.inputEl().on("blur", this.onBlur, this);
47116 this.inputEl().relayEvent('keyup', this);
47118 if(this.indicator){
47119 this.indicator.addClass('invisible');
47122 this.originalValue = this.getValue();
47124 if(this.validationEvent == 'keyup'){
47125 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
47126 this.inputEl().on('keyup', this.filterValidation, this);
47128 else if(this.validationEvent !== false){
47129 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
47132 if(this.selectOnFocus){
47133 this.on("focus", this.preFocus, this);
47136 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
47137 this.inputEl().on("keypress", this.filterKeys, this);
47139 this.inputEl().relayEvent('keypress', this);
47142 var allowed = "0123456789";
47144 if(this.allowDecimals){
47145 allowed += this.decimalSeparator;
47148 if(this.allowNegative){
47152 if(this.thousandsDelimiter) {
47156 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
47158 var keyPress = function(e){
47160 var k = e.getKey();
47162 var c = e.getCharCode();
47165 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
47166 allowed.indexOf(String.fromCharCode(c)) === -1
47172 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
47176 if(allowed.indexOf(String.fromCharCode(c)) === -1){
47181 this.inputEl().on("keypress", keyPress, this);
47185 onTriggerClick : function(e)
47192 this.loadNext = false;
47194 if(this.isExpanded()){
47199 this.hasFocus = true;
47201 if(this.triggerAction == 'all') {
47202 this.doQuery(this.allQuery, true);
47206 this.doQuery(this.getRawValue());
47209 getCurrency : function()
47211 var v = this.currencyEl().getValue();
47216 restrictHeight : function()
47218 this.list.alignTo(this.currencyEl(), this.listAlign);
47219 this.list.alignTo(this.currencyEl(), this.listAlign);
47222 onViewClick : function(view, doFocus, el, e)
47224 var index = this.view.getSelectedIndexes()[0];
47226 var r = this.store.getAt(index);
47229 this.onSelect(r, index);
47233 onSelect : function(record, index){
47235 if(this.fireEvent('beforeselect', this, record, index) !== false){
47237 this.setFromCurrencyData(index > -1 ? record.data : false);
47241 this.fireEvent('select', this, record, index);
47245 setFromCurrencyData : function(o)
47249 this.lastCurrency = o;
47251 if (this.currencyField) {
47252 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
47254 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
47257 this.lastSelectionText = currency;
47259 //setting default currency
47260 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
47261 this.setCurrency(this.defaultCurrency);
47265 this.setCurrency(currency);
47268 setFromData : function(o)
47272 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
47274 this.setFromCurrencyData(c);
47279 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
47281 Roo.log('no value set for '+ (this.name ? this.name : this.id));
47284 this.setValue(value);
47288 setCurrency : function(v)
47290 this.currencyValue = v;
47293 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
47298 setValue : function(v)
47300 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
47306 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
47308 this.inputEl().dom.value = (v == '') ? '' :
47309 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
47311 if(!this.allowZero && v === '0') {
47312 this.hiddenEl().dom.value = '';
47313 this.inputEl().dom.value = '';
47320 getRawValue : function()
47322 var v = this.inputEl().getValue();
47327 getValue : function()
47329 return this.fixPrecision(this.parseValue(this.getRawValue()));
47332 parseValue : function(value)
47334 if(this.thousandsDelimiter) {
47336 r = new RegExp(",", "g");
47337 value = value.replace(r, "");
47340 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
47341 return isNaN(value) ? '' : value;
47345 fixPrecision : function(value)
47347 if(this.thousandsDelimiter) {
47349 r = new RegExp(",", "g");
47350 value = value.replace(r, "");
47353 var nan = isNaN(value);
47355 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
47356 return nan ? '' : value;
47358 return parseFloat(value).toFixed(this.decimalPrecision);
47361 decimalPrecisionFcn : function(v)
47363 return Math.floor(v);
47366 validateValue : function(value)
47368 if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
47372 var num = this.parseValue(value);
47375 this.markInvalid(String.format(this.nanText, value));
47379 if(num < this.minValue){
47380 this.markInvalid(String.format(this.minText, this.minValue));
47384 if(num > this.maxValue){
47385 this.markInvalid(String.format(this.maxText, this.maxValue));
47392 validate : function()
47394 if(this.disabled || this.allowBlank){
47399 var currency = this.getCurrency();
47401 if(this.validateValue(this.getRawValue()) && currency.length){
47406 this.markInvalid();
47410 getName: function()
47415 beforeBlur : function()
47421 var v = this.parseValue(this.getRawValue());
47428 onBlur : function()
47432 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
47433 //this.el.removeClass(this.focusClass);
47436 this.hasFocus = false;
47438 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
47442 var v = this.getValue();
47444 if(String(v) !== String(this.startValue)){
47445 this.fireEvent('change', this, v, this.startValue);
47448 this.fireEvent("blur", this);
47451 inputEl : function()
47453 return this.el.select('.roo-money-amount-input', true).first();
47456 currencyEl : function()
47458 return this.el.select('.roo-money-currency-input', true).first();
47461 hiddenEl : function()
47463 return this.el.select('input.hidden-number-input',true).first();
47467 * @class Roo.bootstrap.BezierSignature
47468 * @extends Roo.bootstrap.Component
47469 * Bootstrap BezierSignature class
47470 * This script refer to:
47471 * Title: Signature Pad
47473 * Availability: https://github.com/szimek/signature_pad
47476 * Create a new BezierSignature
47477 * @param {Object} config The config object
47480 Roo.bootstrap.BezierSignature = function(config){
47481 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
47487 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
47494 mouse_btn_down: true,
47497 * @cfg {int} canvas height
47499 canvas_height: '200px',
47502 * @cfg {float|function} Radius of a single dot.
47507 * @cfg {float} Minimum width of a line. Defaults to 0.5.
47512 * @cfg {float} Maximum width of a line. Defaults to 2.5.
47517 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
47522 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
47527 * @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.
47529 bg_color: 'rgba(0, 0, 0, 0)',
47532 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
47534 dot_color: 'black',
47537 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
47539 velocity_filter_weight: 0.7,
47542 * @cfg {function} Callback when stroke begin.
47547 * @cfg {function} Callback when stroke end.
47551 getAutoCreate : function()
47553 var cls = 'roo-signature column';
47556 cls += ' ' + this.cls;
47566 for(var i = 0; i < col_sizes.length; i++) {
47567 if(this[col_sizes[i]]) {
47568 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
47578 cls: 'roo-signature-body',
47582 cls: 'roo-signature-body-canvas',
47583 height: this.canvas_height,
47584 width: this.canvas_width
47591 style: 'display: none'
47599 initEvents: function()
47601 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
47603 var canvas = this.canvasEl();
47605 // mouse && touch event swapping...
47606 canvas.dom.style.touchAction = 'none';
47607 canvas.dom.style.msTouchAction = 'none';
47609 this.mouse_btn_down = false;
47610 canvas.on('mousedown', this._handleMouseDown, this);
47611 canvas.on('mousemove', this._handleMouseMove, this);
47612 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
47614 if (window.PointerEvent) {
47615 canvas.on('pointerdown', this._handleMouseDown, this);
47616 canvas.on('pointermove', this._handleMouseMove, this);
47617 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
47620 if ('ontouchstart' in window) {
47621 canvas.on('touchstart', this._handleTouchStart, this);
47622 canvas.on('touchmove', this._handleTouchMove, this);
47623 canvas.on('touchend', this._handleTouchEnd, this);
47626 Roo.EventManager.onWindowResize(this.resize, this, true);
47628 // file input event
47629 this.fileEl().on('change', this.uploadImage, this);
47636 resize: function(){
47638 var canvas = this.canvasEl().dom;
47639 var ctx = this.canvasElCtx();
47640 var img_data = false;
47642 if(canvas.width > 0) {
47643 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
47645 // setting canvas width will clean img data
47648 var style = window.getComputedStyle ?
47649 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
47651 var padding_left = parseInt(style.paddingLeft) || 0;
47652 var padding_right = parseInt(style.paddingRight) || 0;
47654 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
47657 ctx.putImageData(img_data, 0, 0);
47661 _handleMouseDown: function(e)
47663 if (e.browserEvent.which === 1) {
47664 this.mouse_btn_down = true;
47665 this.strokeBegin(e);
47669 _handleMouseMove: function (e)
47671 if (this.mouse_btn_down) {
47672 this.strokeMoveUpdate(e);
47676 _handleMouseUp: function (e)
47678 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
47679 this.mouse_btn_down = false;
47684 _handleTouchStart: function (e) {
47686 e.preventDefault();
47687 if (e.browserEvent.targetTouches.length === 1) {
47688 // var touch = e.browserEvent.changedTouches[0];
47689 // this.strokeBegin(touch);
47691 this.strokeBegin(e); // assume e catching the correct xy...
47695 _handleTouchMove: function (e) {
47696 e.preventDefault();
47697 // var touch = event.targetTouches[0];
47698 // _this._strokeMoveUpdate(touch);
47699 this.strokeMoveUpdate(e);
47702 _handleTouchEnd: function (e) {
47703 var wasCanvasTouched = e.target === this.canvasEl().dom;
47704 if (wasCanvasTouched) {
47705 e.preventDefault();
47706 // var touch = event.changedTouches[0];
47707 // _this._strokeEnd(touch);
47712 reset: function () {
47713 this._lastPoints = [];
47714 this._lastVelocity = 0;
47715 this._lastWidth = (this.min_width + this.max_width) / 2;
47716 this.canvasElCtx().fillStyle = this.dot_color;
47719 strokeMoveUpdate: function(e)
47721 this.strokeUpdate(e);
47723 if (this.throttle) {
47724 this.throttleStroke(this.strokeUpdate, this.throttle);
47727 this.strokeUpdate(e);
47731 strokeBegin: function(e)
47733 var newPointGroup = {
47734 color: this.dot_color,
47738 if (typeof this.onBegin === 'function') {
47742 this.curve_data.push(newPointGroup);
47744 this.strokeUpdate(e);
47747 strokeUpdate: function(e)
47749 var rect = this.canvasEl().dom.getBoundingClientRect();
47750 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
47751 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
47752 var lastPoints = lastPointGroup.points;
47753 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
47754 var isLastPointTooClose = lastPoint
47755 ? point.distanceTo(lastPoint) <= this.min_distance
47757 var color = lastPointGroup.color;
47758 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
47759 var curve = this.addPoint(point);
47761 this.drawDot({color: color, point: point});
47764 this.drawCurve({color: color, curve: curve});
47774 strokeEnd: function(e)
47776 this.strokeUpdate(e);
47777 if (typeof this.onEnd === 'function') {
47782 addPoint: function (point) {
47783 var _lastPoints = this._lastPoints;
47784 _lastPoints.push(point);
47785 if (_lastPoints.length > 2) {
47786 if (_lastPoints.length === 3) {
47787 _lastPoints.unshift(_lastPoints[0]);
47789 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
47790 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
47791 _lastPoints.shift();
47797 calculateCurveWidths: function (startPoint, endPoint) {
47798 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
47799 (1 - this.velocity_filter_weight) * this._lastVelocity;
47801 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
47804 start: this._lastWidth
47807 this._lastVelocity = velocity;
47808 this._lastWidth = newWidth;
47812 drawDot: function (_a) {
47813 var color = _a.color, point = _a.point;
47814 var ctx = this.canvasElCtx();
47815 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
47817 this.drawCurveSegment(point.x, point.y, width);
47819 ctx.fillStyle = color;
47823 drawCurve: function (_a) {
47824 var color = _a.color, curve = _a.curve;
47825 var ctx = this.canvasElCtx();
47826 var widthDelta = curve.endWidth - curve.startWidth;
47827 var drawSteps = Math.floor(curve.length()) * 2;
47829 ctx.fillStyle = color;
47830 for (var i = 0; i < drawSteps; i += 1) {
47831 var t = i / drawSteps;
47837 var x = uuu * curve.startPoint.x;
47838 x += 3 * uu * t * curve.control1.x;
47839 x += 3 * u * tt * curve.control2.x;
47840 x += ttt * curve.endPoint.x;
47841 var y = uuu * curve.startPoint.y;
47842 y += 3 * uu * t * curve.control1.y;
47843 y += 3 * u * tt * curve.control2.y;
47844 y += ttt * curve.endPoint.y;
47845 var width = curve.startWidth + ttt * widthDelta;
47846 this.drawCurveSegment(x, y, width);
47852 drawCurveSegment: function (x, y, width) {
47853 var ctx = this.canvasElCtx();
47855 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
47856 this.is_empty = false;
47861 var ctx = this.canvasElCtx();
47862 var canvas = this.canvasEl().dom;
47863 ctx.fillStyle = this.bg_color;
47864 ctx.clearRect(0, 0, canvas.width, canvas.height);
47865 ctx.fillRect(0, 0, canvas.width, canvas.height);
47866 this.curve_data = [];
47868 this.is_empty = true;
47873 return this.el.select('input',true).first();
47876 canvasEl: function()
47878 return this.el.select('canvas',true).first();
47881 canvasElCtx: function()
47883 return this.el.select('canvas',true).first().dom.getContext('2d');
47886 getImage: function(type)
47888 if(this.is_empty) {
47893 return this.canvasEl().dom.toDataURL('image/'+type, 1);
47896 drawFromImage: function(img_src)
47898 var img = new Image();
47900 img.onload = function(){
47901 this.canvasElCtx().drawImage(img, 0, 0);
47906 this.is_empty = false;
47909 selectImage: function()
47911 this.fileEl().dom.click();
47914 uploadImage: function(e)
47916 var reader = new FileReader();
47918 reader.onload = function(e){
47919 var img = new Image();
47920 img.onload = function(){
47922 this.canvasElCtx().drawImage(img, 0, 0);
47924 img.src = e.target.result;
47927 reader.readAsDataURL(e.target.files[0]);
47930 // Bezier Point Constructor
47931 Point: (function () {
47932 function Point(x, y, time) {
47935 this.time = time || Date.now();
47937 Point.prototype.distanceTo = function (start) {
47938 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
47940 Point.prototype.equals = function (other) {
47941 return this.x === other.x && this.y === other.y && this.time === other.time;
47943 Point.prototype.velocityFrom = function (start) {
47944 return this.time !== start.time
47945 ? this.distanceTo(start) / (this.time - start.time)
47952 // Bezier Constructor
47953 Bezier: (function () {
47954 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
47955 this.startPoint = startPoint;
47956 this.control2 = control2;
47957 this.control1 = control1;
47958 this.endPoint = endPoint;
47959 this.startWidth = startWidth;
47960 this.endWidth = endWidth;
47962 Bezier.fromPoints = function (points, widths, scope) {
47963 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
47964 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
47965 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
47967 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
47968 var dx1 = s1.x - s2.x;
47969 var dy1 = s1.y - s2.y;
47970 var dx2 = s2.x - s3.x;
47971 var dy2 = s2.y - s3.y;
47972 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
47973 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
47974 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
47975 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
47976 var dxm = m1.x - m2.x;
47977 var dym = m1.y - m2.y;
47978 var k = l2 / (l1 + l2);
47979 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
47980 var tx = s2.x - cm.x;
47981 var ty = s2.y - cm.y;
47983 c1: new scope.Point(m1.x + tx, m1.y + ty),
47984 c2: new scope.Point(m2.x + tx, m2.y + ty)
47987 Bezier.prototype.length = function () {
47992 for (var i = 0; i <= steps; i += 1) {
47994 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
47995 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
47997 var xdiff = cx - px;
47998 var ydiff = cy - py;
47999 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
48006 Bezier.prototype.point = function (t, start, c1, c2, end) {
48007 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
48008 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
48009 + (3.0 * c2 * (1.0 - t) * t * t)
48010 + (end * t * t * t);
48015 throttleStroke: function(fn, wait) {
48016 if (wait === void 0) { wait = 250; }
48018 var timeout = null;
48022 var later = function () {
48023 previous = Date.now();
48025 result = fn.apply(storedContext, storedArgs);
48027 storedContext = null;
48031 return function wrapper() {
48033 for (var _i = 0; _i < arguments.length; _i++) {
48034 args[_i] = arguments[_i];
48036 var now = Date.now();
48037 var remaining = wait - (now - previous);
48038 storedContext = this;
48040 if (remaining <= 0 || remaining > wait) {
48042 clearTimeout(timeout);
48046 result = fn.apply(storedContext, storedArgs);
48048 storedContext = null;
48052 else if (!timeout) {
48053 timeout = window.setTimeout(later, remaining);
48063 // old names for form elements
48064 Roo.bootstrap.Form = Roo.bootstrap.form.Form;
48065 Roo.bootstrap.Input = Roo.bootstrap.form.Input;
48066 Roo.bootstrap.TextArea = Roo.bootstrap.form.TextArea;
48067 Roo.bootstrap.TriggerField = Roo.bootstrap.form.TriggerField;
48068 Roo.bootstrap.ComboBox = Roo.bootstrap.form.ComboBox;
48069 Roo.bootstrap.DateField = Roo.bootstrap.form.DateField;
48070 Roo.bootstrap.TimeField = Roo.bootstrap.form.TimeField;
48071 Roo.bootstrap.MonthField = Roo.bootstrap.form.MonthField;
48072 Roo.bootstrap.CheckBox = Roo.bootstrap.form.CheckBox;
48073 Roo.bootstrap.Radio = Roo.bootstrap.form.Radio;
48074 Roo.bootstrap.RadioSet = Roo.bootstrap.form.RadioSet;
48075 Roo.bootstrap.SecurePass = Roo.bootstrap.form.SecurePass;
48076 Roo.bootstrap.FieldLabel = Roo.bootstrap.form.FieldLabel;
48077 Roo.bootstrap.DateSplitField= Roo.bootstrap.form.DateSplitField;
48078 Roo.bootstrap.NumberField = Roo.bootstrap.form.NumberField;
48079 Roo.bootstrap.PhoneInput = Roo.bootstrap.form.PhoneInput;
48080 Roo.bootstrap.PhoneInputData= Roo.bootstrap.form.PhoneInputData;
48081 Roo.bootstrap.MoneyField = Roo.bootstrap.form.MoneyField;
48082 Roo.bootstrap.HtmlEditor = Roo.bootstrap.form.HtmlEditor;
48083 Roo.bootstrap.HtmlEditor.ToolbarStandard = Roo.bootstrap.form.HtmlEditorToolbarStandard;
48084 Roo.bootstrap.Markdown = Roo.bootstrap.form.Markdown;
48085 Roo.bootstrap.CardUploader = Roo.bootstrap.form.CardUploader;// depricated.
48086 Roo.bootstrap.Navbar = Roo.bootstrap.nav.Bar;
48087 Roo.bootstrap.NavGroup = Roo.bootstrap.nav.Group;
48088 Roo.bootstrap.NavHeaderbar = Roo.bootstrap.nav.Headerbar;
48089 Roo.bootstrap.NavItem = Roo.bootstrap.nav.Item;
48091 Roo.bootstrap.NavProgressBar = Roo.bootstrap.nav.ProgressBar;
48092 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
48094 Roo.bootstrap.NavSidebar = Roo.bootstrap.nav.Sidebar;
48095 Roo.bootstrap.NavSidebarItem = Roo.bootstrap.nav.SidebarItem;
48097 Roo.bootstrap.NavSimplebar = Roo.bootstrap.nav.Simplebar;// deprciated
48098 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
48099 Roo.bootstrap.MenuItem = Roo.bootstrap.menu.Item;
48100 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator